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 |