diff options
Diffstat (limited to 'deps/v8/preparser')
-rw-r--r-- | deps/v8/preparser/SConscript | 38 | ||||
-rw-r--r-- | deps/v8/preparser/preparser-process.cc | 407 |
2 files changed, 328 insertions, 117 deletions
diff --git a/deps/v8/preparser/SConscript b/deps/v8/preparser/SConscript new file mode 100644 index 0000000000..1d51e826cc --- /dev/null +++ b/deps/v8/preparser/SConscript @@ -0,0 +1,38 @@ +# Copyright 2011 the V8 project authors. All rights reserved. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from os.path import join +Import('context') + +def ConfigureObjectFiles(): + env = Environment() + env.Replace(**context.flags['preparser']) + context.ApplyEnvOverrides(env) + return env.Object('preparser-process.cc') + +preparser_object = ConfigureObjectFiles() +Return('preparser_object') diff --git a/deps/v8/preparser/preparser-process.cc b/deps/v8/preparser/preparser-process.cc index 26dfc42b53..d7c96f0c73 100644 --- a/deps/v8/preparser/preparser-process.cc +++ b/deps/v8/preparser/preparser-process.cc @@ -1,4 +1,4 @@ -// Copyright 2010 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -27,105 +27,79 @@ #include <stdlib.h> #include <stdarg.h> +#include <stdio.h> +#include <string.h> + #include "../include/v8stdint.h" #include "../include/v8-preparser.h" -#include "unicode-inl.h" -enum ResultCode { kSuccess = 0, kErrorReading = 1, kErrorWriting = 2 }; +#include "../src/preparse-data-format.h" -namespace v8 { -namespace internal { +namespace i = v8::internal; -// THIS FILE IS PROOF-OF-CONCEPT ONLY. -// The final goal is a stand-alone preparser library. +// This file is only used for testing the stand-alone preparser +// library. +// The first argument must be the path of a JavaScript source file, or +// the flags "-e" and the next argument is then the source of a JavaScript +// program. +// Optionally this can be followed by the word "throws" (case sensitive), +// which signals that the parsing is expected to throw - the default is +// to expect the parsing to not throw. +// The command line can further be followed by a message text (the +// *type* of the exception to throw), and even more optionally, the +// start and end position reported with the exception. +// +// This source file is preparsed and tested against the expectations, and if +// successful, the resulting preparser data is written to stdout. +// Diagnostic output is output on stderr. +// The source file must contain only ASCII characters (UTF-8 isn't supported). +// The file is read into memory, so it should have a reasonable size. -class UTF8InputStream : public v8::UnicodeInputStream { +// Adapts an ASCII string to the UnicodeInputStream interface. +class AsciiInputStream : public v8::UnicodeInputStream { public: - UTF8InputStream(uint8_t* buffer, size_t length) + AsciiInputStream(const uint8_t* buffer, size_t length) : buffer_(buffer), - offset_(0), - pos_(0), - end_offset_(static_cast<int>(length)) { } + end_offset_(static_cast<int>(length)), + offset_(0) { } - virtual ~UTF8InputStream() { } + virtual ~AsciiInputStream() { } virtual void PushBack(int32_t ch) { - // Pushback assumes that the character pushed back is the - // one that was most recently read, and jumps back in the - // UTF-8 stream by the length of that character's encoding. - offset_ -= unibrow::Utf8::Length(ch); - pos_--; + offset_--; #ifdef DEBUG - if (static_cast<unsigned>(ch) <= unibrow::Utf8::kMaxOneByteChar) { - if (ch != buffer_[offset_]) { - fprintf(stderr, "Invalid pushback: '%c'.", ch); - exit(1); - } - } else { - unsigned tmp = 0; - if (static_cast<unibrow::uchar>(ch) != - unibrow::Utf8::CalculateValue(buffer_ + offset_, - end_offset_ - offset_, - &tmp)) { - fprintf(stderr, "Invalid pushback: 0x%x.", ch); - exit(1); - } + if (offset_ < 0 || + (ch != ((offset_ >= end_offset_) ? -1 : buffer_[offset_]))) { + fprintf(stderr, "Invalid pushback: '%c' at offset %d.", ch, offset_); + exit(1); } #endif } virtual int32_t Next() { - if (offset_ == end_offset_) return -1; - uint8_t first_char = buffer_[offset_]; - if (first_char <= unibrow::Utf8::kMaxOneByteChar) { - pos_++; - offset_++; - return static_cast<int32_t>(first_char); + if (offset_ >= end_offset_) { + offset_++; // Increment anyway to allow symmetric pushbacks. + return -1; + } + uint8_t next_char = buffer_[offset_]; +#ifdef DEBUG + if (next_char > 0x7fu) { + fprintf(stderr, "Non-ASCII character in input: '%c'.", next_char); + exit(1); } - unibrow::uchar codepoint = - unibrow::Utf8::CalculateValue(buffer_ + offset_, - end_offset_ - offset_, - &offset_); - pos_++; - return static_cast<int32_t>(codepoint); +#endif + offset_++; + return static_cast<int32_t>(next_char); } private: const uint8_t* buffer_; - unsigned offset_; - unsigned pos_; - unsigned end_offset_; + const int end_offset_; + int offset_; }; -// Write a number to dest in network byte order. -void WriteUInt32(FILE* dest, uint32_t value, bool* ok) { - for (int i = 3; i >= 0; i--) { - uint8_t byte = static_cast<uint8_t>(value >> (i << 3)); - int result = fputc(byte, dest); - if (result == EOF) { - *ok = false; - return; - } - } -} - -// Read number from FILE* in network byte order. -uint32_t ReadUInt32(FILE* source, bool* ok) { - uint32_t n = 0; - for (int i = 0; i < 4; i++) { - int c = fgetc(source); - if (c == EOF) { - *ok = false; - return 0; - } - n = (n << 8) + static_cast<uint32_t>(c); - } - return n; -} - - bool ReadBuffer(FILE* source, void* buffer, size_t length) { size_t actually_read = fread(buffer, 1, length, source); return (actually_read == length); @@ -138,69 +112,268 @@ bool WriteBuffer(FILE* dest, const void* buffer, size_t length) { } +class PreparseDataInterpreter { + public: + PreparseDataInterpreter(const uint8_t* data, int length) + : data_(data), length_(length), message_(NULL) { } + + ~PreparseDataInterpreter() { + if (message_ != NULL) delete[] message_; + } + + bool valid() { + int header_length = + i::PreparseDataConstants::kHeaderSize * sizeof(int); // NOLINT + return length_ >= header_length; + } + + bool throws() { + return valid() && + word(i::PreparseDataConstants::kHasErrorOffset) != 0; + } + + const char* message() { + if (message_ != NULL) return message_; + if (!throws()) return NULL; + int text_pos = i::PreparseDataConstants::kHeaderSize + + i::PreparseDataConstants::kMessageTextPos; + int length = word(text_pos); + char* buffer = new char[length + 1]; + for (int i = 1; i <= length; i++) { + int character = word(text_pos + i); + buffer[i - 1] = character; + } + buffer[length] = '\0'; + message_ = buffer; + return buffer; + } + + int beg_pos() { + if (!throws()) return -1; + return word(i::PreparseDataConstants::kHeaderSize + + i::PreparseDataConstants::kMessageStartPos); + } + + int end_pos() { + if (!throws()) return -1; + return word(i::PreparseDataConstants::kHeaderSize + + i::PreparseDataConstants::kMessageEndPos); + } + + private: + int word(int offset) { + const int* word_data = reinterpret_cast<const int*>(data_); + if (word_data + offset < reinterpret_cast<const int*>(data_ + length_)) { + return word_data[offset]; + } + return -1; + } + + const uint8_t* const data_; + const int length_; + const char* message_; +}; + + template <typename T> class ScopedPointer { public: + explicit ScopedPointer() : pointer_(NULL) {} explicit ScopedPointer(T* pointer) : pointer_(pointer) {} - ~ScopedPointer() { delete[] pointer_; } + ~ScopedPointer() { if (pointer_ != NULL) delete[] pointer_; } T& operator[](int index) { return pointer_[index]; } T* operator*() { return pointer_ ;} + T* operator=(T* new_value) { + if (pointer_ != NULL) delete[] pointer_; + pointer_ = new_value; + return new_value; + } private: T* pointer_; }; -// Preparse input and output result on stdout. -int PreParseIO(FILE* input) { - fprintf(stderr, "LOG: Enter parsing loop\n"); - bool ok = true; - uint32_t length = ReadUInt32(input, &ok); - fprintf(stderr, "LOG: Input length: %d\n", length); - if (!ok) return kErrorReading; - ScopedPointer<uint8_t> buffer(new uint8_t[length]); - if (!ReadBuffer(input, *buffer, length)) { - return kErrorReading; +void fail(v8::PreParserData* data, const char* message, ...) { + va_list args; + va_start(args, message); + vfprintf(stderr, message, args); + va_end(args); + fflush(stderr); + // Print preparser data to stdout. + uint32_t size = data->size(); + fprintf(stderr, "LOG: data size: %u\n", size); + if (!WriteBuffer(stdout, data->data(), size)) { + perror("ERROR: Writing data"); + fflush(stderr); } - UTF8InputStream input_buffer(*buffer, static_cast<size_t>(length)); + exit(EXIT_FAILURE); +}; - v8::PreParserData data = - v8::Preparse(&input_buffer, 64 * 1024 * sizeof(void*)); // NOLINT - if (data.stack_overflow()) { - fprintf(stderr, "LOG: Stack overflow\n"); - fflush(stderr); - // Report stack overflow error/no-preparser-data. - WriteUInt32(stdout, 0, &ok); - if (!ok) return kErrorWriting; - return 0; + +bool IsFlag(const char* arg) { + // Anything starting with '-' is considered a flag. + // It's summarily ignored for now. + return arg[0] == '-'; +} + + +struct ExceptionExpectation { + ExceptionExpectation() + : throws(false), type(NULL), beg_pos(-1), end_pos(-1) { } + bool throws; + const char* type; + int beg_pos; + int end_pos; +}; + + +void CheckException(v8::PreParserData* data, + ExceptionExpectation* expects) { + PreparseDataInterpreter reader(data->data(), data->size()); + if (expects->throws) { + if (!reader.throws()) { + if (expects->type == NULL) { + fail(data, "Didn't throw as expected\n"); + } else { + fail(data, "Didn't throw \"%s\" as expected\n", expects->type); + } + } + if (expects->type != NULL) { + const char* actual_message = reader.message(); + if (strcmp(expects->type, actual_message)) { + fail(data, "Wrong error message. Expected <%s>, found <%s> at %d..%d\n", + expects->type, actual_message, reader.beg_pos(), reader.end_pos()); + } + } + if (expects->beg_pos >= 0) { + if (expects->beg_pos != reader.beg_pos()) { + fail(data, "Wrong error start position: Expected %i, found %i\n", + expects->beg_pos, reader.beg_pos()); + } + } + if (expects->end_pos >= 0) { + if (expects->end_pos != reader.end_pos()) { + fail(data, "Wrong error end position: Expected %i, found %i\n", + expects->end_pos, reader.end_pos()); + } + } + } else if (reader.throws()) { + const char* message = reader.message(); + fail(data, "Throws unexpectedly with message: %s at location %d-%d\n", + message, reader.beg_pos(), reader.end_pos()); } +} - uint32_t size = data.size(); - fprintf(stderr, "LOG: Success, data size: %u\n", size); - fflush(stderr); - WriteUInt32(stdout, size, &ok); - if (!ok) return kErrorWriting; - if (!WriteBuffer(stdout, data.data(), size)) { - return kErrorWriting; + +ExceptionExpectation ParseExpectation(int argc, const char* argv[]) { + ExceptionExpectation expects; + + // Parse exception expectations from (the remainder of) the command line. + int arg_index = 0; + // Skip any flags. + while (argc > arg_index && IsFlag(argv[arg_index])) arg_index++; + if (argc > arg_index) { + if (strncmp("throws", argv[arg_index], 7)) { + // First argument after filename, if present, must be the verbatim + // "throws", marking that the preparsing should fail with an exception. + fail(NULL, "ERROR: Extra arguments not prefixed by \"throws\".\n"); + } + expects.throws = true; + do { + arg_index++; + } while (argc > arg_index && IsFlag(argv[arg_index])); + if (argc > arg_index) { + // Next argument is the exception type identifier. + expects.type = argv[arg_index]; + do { + arg_index++; + } while (argc > arg_index && IsFlag(argv[arg_index])); + if (argc > arg_index) { + expects.beg_pos = atoi(argv[arg_index]); // NOLINT + do { + arg_index++; + } while (argc > arg_index && IsFlag(argv[arg_index])); + if (argc > arg_index) { + expects.end_pos = atoi(argv[arg_index]); // NOLINT + } + } + } } - return 0; + return expects; } -} } // namespace v8::internal +int main(int argc, const char* argv[]) { + // Parse command line. + // Format: preparser (<scriptfile> | -e "<source>") + // ["throws" [<exn-type> [<start> [<end>]]]] + // Any flags (except an initial -s) are ignored. -int main(int argc, char* argv[]) { - FILE* input = stdin; - if (argc > 1) { - char* arg = argv[1]; - input = fopen(arg, "rb"); - if (input == NULL) return EXIT_FAILURE; + // Check for mandatory filename argument. + int arg_index = 1; + if (argc <= arg_index) { + fail(NULL, "ERROR: No filename on command line.\n"); } - int status = 0; - do { - status = v8::internal::PreParseIO(input); - } while (status == 0); - fprintf(stderr, "EXIT: Failure %d\n", status); - fflush(stderr); - return EXIT_FAILURE; + const uint8_t* source = NULL; + const char* filename = argv[arg_index]; + if (!strcmp(filename, "-e")) { + arg_index++; + if (argc <= arg_index) { + fail(NULL, "ERROR: No source after -e on command line.\n"); + } + source = reinterpret_cast<const uint8_t*>(argv[arg_index]); + } + // Check remainder of command line for exception expectations. + arg_index++; + ExceptionExpectation expects = + ParseExpectation(argc - arg_index, argv + arg_index); + + ScopedPointer<uint8_t> buffer; + size_t length; + + if (source == NULL) { + // Open JS file. + FILE* input = fopen(filename, "rb"); + if (input == NULL) { + perror("ERROR: Error opening file"); + fflush(stderr); + return EXIT_FAILURE; + } + // Find length of JS file. + if (fseek(input, 0, SEEK_END) != 0) { + perror("ERROR: Error during seek"); + fflush(stderr); + return EXIT_FAILURE; + } + length = static_cast<size_t>(ftell(input)); + rewind(input); + // Read JS file into memory buffer. + buffer = new uint8_t[length]; + if (!ReadBuffer(input, *buffer, length)) { + perror("ERROR: Reading file"); + fflush(stderr); + return EXIT_FAILURE; + } + fclose(input); + source = *buffer; + } else { + length = strlen(reinterpret_cast<const char*>(source)); + } + + // Preparse input file. + AsciiInputStream input_buffer(source, length); + size_t kMaxStackSize = 64 * 1024 * sizeof(void*); // NOLINT + v8::PreParserData data = v8::Preparse(&input_buffer, kMaxStackSize); + + // Fail if stack overflow. + if (data.stack_overflow()) { + fail(&data, "ERROR: Stack overflow\n"); + } + + // Check that the expected exception is thrown, if an exception is + // expected. + CheckException(&data, &expects); + + return EXIT_SUCCESS; } |