| OLD | NEW |
| (Empty) |
| 1 // Copyright 2012 the V8 project authors. All rights reserved. | |
| 2 // Redistribution and use in source and binary forms, with or without | |
| 3 // modification, are permitted provided that the following conditions are | |
| 4 // met: | |
| 5 // | |
| 6 // * Redistributions of source code must retain the above copyright | |
| 7 // notice, this list of conditions and the following disclaimer. | |
| 8 // * Redistributions in binary form must reproduce the above | |
| 9 // copyright notice, this list of conditions and the following | |
| 10 // disclaimer in the documentation and/or other materials provided | |
| 11 // with the distribution. | |
| 12 // * Neither the name of Google Inc. nor the names of its | |
| 13 // contributors may be used to endorse or promote products derived | |
| 14 // from this software without specific prior written permission. | |
| 15 // | |
| 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 27 | |
| 28 #include <stdlib.h> | |
| 29 #include <stdarg.h> | |
| 30 #include <stdio.h> | |
| 31 #include <string.h> | |
| 32 | |
| 33 #include "../include/v8.h" | |
| 34 #include "../include/v8stdint.h" | |
| 35 #include "../include/v8-preparser.h" | |
| 36 | |
| 37 #include "../src/preparse-data-format.h" | |
| 38 | |
| 39 namespace i = v8::internal; | |
| 40 | |
| 41 // This file is only used for testing the preparser. | |
| 42 // The first argument must be the path of a JavaScript source file, or | |
| 43 // the flags "-e" and the next argument is then the source of a JavaScript | |
| 44 // program. | |
| 45 // Optionally this can be followed by the word "throws" (case sensitive), | |
| 46 // which signals that the parsing is expected to throw - the default is | |
| 47 // to expect the parsing to not throw. | |
| 48 // The command line can further be followed by a message text (the | |
| 49 // *type* of the exception to throw), and even more optionally, the | |
| 50 // start and end position reported with the exception. | |
| 51 // | |
| 52 // This source file is preparsed and tested against the expectations, and if | |
| 53 // successful, the resulting preparser data is written to stdout. | |
| 54 // Diagnostic output is output on stderr. | |
| 55 // The source file must contain only ASCII characters (UTF-8 isn't supported). | |
| 56 // The file is read into memory, so it should have a reasonable size. | |
| 57 | |
| 58 | |
| 59 // Adapts an ASCII string to the UnicodeInputStream interface. | |
| 60 class AsciiInputStream : public v8::UnicodeInputStream { | |
| 61 public: | |
| 62 AsciiInputStream(const uint8_t* buffer, size_t length) | |
| 63 : buffer_(buffer), | |
| 64 end_offset_(static_cast<int>(length)), | |
| 65 offset_(0) { } | |
| 66 | |
| 67 virtual ~AsciiInputStream() { } | |
| 68 | |
| 69 virtual void PushBack(int32_t ch) { | |
| 70 offset_--; | |
| 71 #ifdef DEBUG | |
| 72 if (offset_ < 0 || | |
| 73 (ch != ((offset_ >= end_offset_) ? -1 : buffer_[offset_]))) { | |
| 74 fprintf(stderr, "Invalid pushback: '%c' at offset %d.", ch, offset_); | |
| 75 exit(1); | |
| 76 } | |
| 77 #endif | |
| 78 } | |
| 79 | |
| 80 virtual int32_t Next() { | |
| 81 if (offset_ >= end_offset_) { | |
| 82 offset_++; // Increment anyway to allow symmetric pushbacks. | |
| 83 return -1; | |
| 84 } | |
| 85 uint8_t next_char = buffer_[offset_]; | |
| 86 #ifdef DEBUG | |
| 87 if (next_char > 0x7fu) { | |
| 88 fprintf(stderr, "Non-ASCII character in input: '%c'.", next_char); | |
| 89 exit(1); | |
| 90 } | |
| 91 #endif | |
| 92 offset_++; | |
| 93 return static_cast<int32_t>(next_char); | |
| 94 } | |
| 95 | |
| 96 private: | |
| 97 const uint8_t* buffer_; | |
| 98 const int end_offset_; | |
| 99 int offset_; | |
| 100 }; | |
| 101 | |
| 102 | |
| 103 bool ReadBuffer(FILE* source, void* buffer, size_t length) { | |
| 104 size_t actually_read = fread(buffer, 1, length, source); | |
| 105 return (actually_read == length); | |
| 106 } | |
| 107 | |
| 108 | |
| 109 bool WriteBuffer(FILE* dest, const void* buffer, size_t length) { | |
| 110 size_t actually_written = fwrite(buffer, 1, length, dest); | |
| 111 return (actually_written == length); | |
| 112 } | |
| 113 | |
| 114 | |
| 115 class PreparseDataInterpreter { | |
| 116 public: | |
| 117 PreparseDataInterpreter(const uint8_t* data, int length) | |
| 118 : data_(data), length_(length), message_(NULL) { } | |
| 119 | |
| 120 ~PreparseDataInterpreter() { | |
| 121 if (message_ != NULL) delete[] message_; | |
| 122 } | |
| 123 | |
| 124 bool valid() { | |
| 125 int header_length = | |
| 126 i::PreparseDataConstants::kHeaderSize * sizeof(int); // NOLINT | |
| 127 return length_ >= header_length; | |
| 128 } | |
| 129 | |
| 130 bool throws() { | |
| 131 return valid() && | |
| 132 word(i::PreparseDataConstants::kHasErrorOffset) != 0; | |
| 133 } | |
| 134 | |
| 135 const char* message() { | |
| 136 if (message_ != NULL) return message_; | |
| 137 if (!throws()) return NULL; | |
| 138 int text_pos = i::PreparseDataConstants::kHeaderSize + | |
| 139 i::PreparseDataConstants::kMessageTextPos; | |
| 140 int length = word(text_pos); | |
| 141 char* buffer = new char[length + 1]; | |
| 142 for (int i = 1; i <= length; i++) { | |
| 143 int character = word(text_pos + i); | |
| 144 buffer[i - 1] = character; | |
| 145 } | |
| 146 buffer[length] = '\0'; | |
| 147 message_ = buffer; | |
| 148 return buffer; | |
| 149 } | |
| 150 | |
| 151 int beg_pos() { | |
| 152 if (!throws()) return -1; | |
| 153 return word(i::PreparseDataConstants::kHeaderSize + | |
| 154 i::PreparseDataConstants::kMessageStartPos); | |
| 155 } | |
| 156 | |
| 157 int end_pos() { | |
| 158 if (!throws()) return -1; | |
| 159 return word(i::PreparseDataConstants::kHeaderSize + | |
| 160 i::PreparseDataConstants::kMessageEndPos); | |
| 161 } | |
| 162 | |
| 163 private: | |
| 164 int word(int offset) { | |
| 165 const int* word_data = reinterpret_cast<const int*>(data_); | |
| 166 if (word_data + offset < reinterpret_cast<const int*>(data_ + length_)) { | |
| 167 return word_data[offset]; | |
| 168 } | |
| 169 return -1; | |
| 170 } | |
| 171 | |
| 172 const uint8_t* const data_; | |
| 173 const int length_; | |
| 174 const char* message_; | |
| 175 }; | |
| 176 | |
| 177 | |
| 178 template <typename T> | |
| 179 class ScopedPointer { | |
| 180 public: | |
| 181 explicit ScopedPointer() : pointer_(NULL) {} | |
| 182 explicit ScopedPointer(T* pointer) : pointer_(pointer) {} | |
| 183 ~ScopedPointer() { if (pointer_ != NULL) delete[] pointer_; } | |
| 184 T& operator[](int index) { return pointer_[index]; } | |
| 185 T* operator*() { return pointer_ ;} | |
| 186 T* operator=(T* new_value) { | |
| 187 if (pointer_ != NULL) delete[] pointer_; | |
| 188 pointer_ = new_value; | |
| 189 return new_value; | |
| 190 } | |
| 191 private: | |
| 192 T* pointer_; | |
| 193 }; | |
| 194 | |
| 195 | |
| 196 | |
| 197 void fail(v8::PreParserData* data, const char* message, ...) { | |
| 198 va_list args; | |
| 199 va_start(args, message); | |
| 200 vfprintf(stderr, message, args); | |
| 201 va_end(args); | |
| 202 fflush(stderr); | |
| 203 if (data != NULL) { | |
| 204 // Print preparser data to stdout. | |
| 205 uint32_t size = static_cast<uint32_t>(data->size()); | |
| 206 fprintf(stderr, "LOG: data size: %u\n", size); | |
| 207 if (!WriteBuffer(stdout, data->data(), size)) { | |
| 208 perror("ERROR: Writing data"); | |
| 209 fflush(stderr); | |
| 210 } | |
| 211 } | |
| 212 exit(EXIT_FAILURE); | |
| 213 } | |
| 214 | |
| 215 | |
| 216 bool IsFlag(const char* arg) { | |
| 217 // Anything starting with '-' is considered a flag. | |
| 218 // It's summarily ignored for now. | |
| 219 return arg[0] == '-'; | |
| 220 } | |
| 221 | |
| 222 | |
| 223 struct ExceptionExpectation { | |
| 224 ExceptionExpectation() | |
| 225 : throws(false), type(NULL), beg_pos(-1), end_pos(-1) { } | |
| 226 bool throws; | |
| 227 const char* type; | |
| 228 int beg_pos; | |
| 229 int end_pos; | |
| 230 }; | |
| 231 | |
| 232 | |
| 233 void CheckException(v8::PreParserData* data, | |
| 234 ExceptionExpectation* expects) { | |
| 235 PreparseDataInterpreter reader(data->data(), static_cast<int>(data->size())); | |
| 236 if (expects->throws) { | |
| 237 if (!reader.throws()) { | |
| 238 if (expects->type == NULL) { | |
| 239 fail(data, "Didn't throw as expected\n"); | |
| 240 } else { | |
| 241 fail(data, "Didn't throw \"%s\" as expected\n", expects->type); | |
| 242 } | |
| 243 } | |
| 244 if (expects->type != NULL) { | |
| 245 const char* actual_message = reader.message(); | |
| 246 if (strcmp(expects->type, actual_message)) { | |
| 247 fail(data, "Wrong error message. Expected <%s>, found <%s> at %d..%d\n", | |
| 248 expects->type, actual_message, reader.beg_pos(), reader.end_pos()); | |
| 249 } | |
| 250 } | |
| 251 if (expects->beg_pos >= 0) { | |
| 252 if (expects->beg_pos != reader.beg_pos()) { | |
| 253 fail(data, "Wrong error start position: Expected %i, found %i\n", | |
| 254 expects->beg_pos, reader.beg_pos()); | |
| 255 } | |
| 256 } | |
| 257 if (expects->end_pos >= 0) { | |
| 258 if (expects->end_pos != reader.end_pos()) { | |
| 259 fail(data, "Wrong error end position: Expected %i, found %i\n", | |
| 260 expects->end_pos, reader.end_pos()); | |
| 261 } | |
| 262 } | |
| 263 } else if (reader.throws()) { | |
| 264 const char* message = reader.message(); | |
| 265 fail(data, "Throws unexpectedly with message: %s at location %d-%d\n", | |
| 266 message, reader.beg_pos(), reader.end_pos()); | |
| 267 } | |
| 268 } | |
| 269 | |
| 270 | |
| 271 ExceptionExpectation ParseExpectation(int argc, const char* argv[]) { | |
| 272 // Parse ["throws" [<exn-type> [<start> [<end>]]]]. | |
| 273 ExceptionExpectation expects; | |
| 274 int arg_index = 0; | |
| 275 while (argc > arg_index && strncmp("throws", argv[arg_index], 7)) { | |
| 276 arg_index++; | |
| 277 } | |
| 278 if (argc > arg_index) { | |
| 279 expects.throws = true; | |
| 280 arg_index++; | |
| 281 if (argc > arg_index && !IsFlag(argv[arg_index])) { | |
| 282 expects.type = argv[arg_index]; | |
| 283 arg_index++; | |
| 284 if (argc > arg_index && !IsFlag(argv[arg_index])) { | |
| 285 expects.beg_pos = atoi(argv[arg_index]); // NOLINT | |
| 286 arg_index++; | |
| 287 if (argc > arg_index && !IsFlag(argv[arg_index])) { | |
| 288 expects.end_pos = atoi(argv[arg_index]); // NOLINT | |
| 289 } | |
| 290 } | |
| 291 } | |
| 292 } | |
| 293 return expects; | |
| 294 } | |
| 295 | |
| 296 | |
| 297 int main(int argc, const char* argv[]) { | |
| 298 // Parse command line. | |
| 299 // Format: preparser (<scriptfile> | -e "<source>") | |
| 300 // ["throws" [<exn-type> [<start> [<end>]]]] | |
| 301 // Any flags (except an initial -e) are ignored. | |
| 302 // Flags must not separate "throws" and its arguments. | |
| 303 | |
| 304 // Check for mandatory filename argument. | |
| 305 int arg_index = 1; | |
| 306 if (argc <= arg_index) { | |
| 307 fail(NULL, "ERROR: No filename on command line.\n"); | |
| 308 } | |
| 309 const uint8_t* source = NULL; | |
| 310 const char* filename = argv[arg_index]; | |
| 311 if (!strcmp(filename, "-e")) { | |
| 312 arg_index++; | |
| 313 if (argc <= arg_index) { | |
| 314 fail(NULL, "ERROR: No source after -e on command line.\n"); | |
| 315 } | |
| 316 source = reinterpret_cast<const uint8_t*>(argv[arg_index]); | |
| 317 } | |
| 318 // Check remainder of command line for exception expectations. | |
| 319 arg_index++; | |
| 320 ExceptionExpectation expects = | |
| 321 ParseExpectation(argc - arg_index, argv + arg_index); | |
| 322 | |
| 323 v8::V8::Initialize(); | |
| 324 | |
| 325 ScopedPointer<uint8_t> buffer; | |
| 326 size_t length; | |
| 327 | |
| 328 if (source == NULL) { | |
| 329 // Open JS file. | |
| 330 FILE* input = fopen(filename, "rb"); | |
| 331 if (input == NULL) { | |
| 332 perror("ERROR: Error opening file"); | |
| 333 fflush(stderr); | |
| 334 return EXIT_FAILURE; | |
| 335 } | |
| 336 // Find length of JS file. | |
| 337 if (fseek(input, 0, SEEK_END) != 0) { | |
| 338 perror("ERROR: Error during seek"); | |
| 339 fflush(stderr); | |
| 340 return EXIT_FAILURE; | |
| 341 } | |
| 342 length = static_cast<size_t>(ftell(input)); | |
| 343 rewind(input); | |
| 344 // Read JS file into memory buffer. | |
| 345 buffer = new uint8_t[length]; | |
| 346 if (!ReadBuffer(input, *buffer, length)) { | |
| 347 perror("ERROR: Reading file"); | |
| 348 fflush(stderr); | |
| 349 return EXIT_FAILURE; | |
| 350 } | |
| 351 fclose(input); | |
| 352 source = *buffer; | |
| 353 } else { | |
| 354 length = strlen(reinterpret_cast<const char*>(source)); | |
| 355 } | |
| 356 | |
| 357 // Preparse input file. | |
| 358 AsciiInputStream input_buffer(source, length); | |
| 359 size_t kMaxStackSize = 64 * 1024 * sizeof(void*); // NOLINT | |
| 360 v8::PreParserData data = v8::Preparse(&input_buffer, kMaxStackSize); | |
| 361 | |
| 362 // Fail if stack overflow. | |
| 363 if (data.stack_overflow()) { | |
| 364 fail(&data, "ERROR: Stack overflow\n"); | |
| 365 } | |
| 366 | |
| 367 // Check that the expected exception is thrown, if an exception is | |
| 368 // expected. | |
| 369 CheckException(&data, &expects); | |
| 370 | |
| 371 return EXIT_SUCCESS; | |
| 372 } | |
| OLD | NEW |