preparser-process.cc 11.2 KB
Newer Older
1
// Copyright 2012 the V8 project authors. All rights reserved.
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
// 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.

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 38 39 40
#include "../src/preparse-data-format.h"

namespace i = v8::internal;

41
// This file is only used for testing the preparser.
42 43 44
// 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.
45 46 47 48 49 50 51 52 53 54 55
// 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).
56
// The file is read into memory, so it should have a reasonable size.
57

58

59 60
// Adapts an ASCII string to the UnicodeInputStream interface.
class AsciiInputStream : public v8::UnicodeInputStream {
61
 public:
62
  AsciiInputStream(const uint8_t* buffer, size_t length)
63
      : buffer_(buffer),
64 65
        end_offset_(static_cast<int>(length)),
        offset_(0) { }
66

67
  virtual ~AsciiInputStream() { }
68 69

  virtual void PushBack(int32_t ch) {
70
    offset_--;
71
#ifdef DEBUG
72 73 74 75
    if (offset_ < 0 ||
        (ch != ((offset_ >= end_offset_) ? -1 : buffer_[offset_]))) {
      fprintf(stderr, "Invalid pushback: '%c' at offset %d.", ch, offset_);
      exit(1);
76
    }
77 78 79
#endif
  }

80
  virtual int32_t Next() {
81 82 83 84 85 86 87 88 89
    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);
90
    }
91 92 93
#endif
    offset_++;
    return static_cast<int32_t>(next_char);
94 95 96 97
  }

 private:
  const uint8_t* buffer_;
98 99
  const int end_offset_;
  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 106 107 108
  return (actually_read == length);
}


109
bool WriteBuffer(FILE* dest, const void* buffer, size_t length) {
110 111 112 113
  size_t actually_written = fwrite(buffer, 1, length, dest);
  return (actually_written == length);
}

114

115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
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_;
};


178 179 180
template <typename T>
class ScopedPointer {
 public:
181
  explicit ScopedPointer() : pointer_(NULL) {}
182
  explicit ScopedPointer(T* pointer) : pointer_(pointer) {}
183
  ~ScopedPointer() { if (pointer_ != NULL) delete[] pointer_; }
184 185
  T& operator[](int index) { return pointer_[index]; }
  T* operator*() { return pointer_ ;}
186
  T* operator=(T* new_value) {
187 188
    if (pointer_ != NULL) delete[] pointer_;
    pointer_ = new_value;
189
    return new_value;
190
  }
191 192 193 194 195
 private:
  T* pointer_;
};


196 197 198 199 200 201 202

void fail(v8::PreParserData* data, const char* message, ...) {
  va_list args;
  va_start(args, message);
  vfprintf(stderr, message, args);
  va_end(args);
  fflush(stderr);
203 204
  if (data != NULL) {
    // Print preparser data to stdout.
205
    uint32_t size = static_cast<uint32_t>(data->size());
206 207 208 209 210
    fprintf(stderr, "LOG: data size: %u\n", size);
    if (!WriteBuffer(stdout, data->data(), size)) {
      perror("ERROR: Writing data");
      fflush(stderr);
    }
211 212
  }
  exit(EXIT_FAILURE);
213
}
214 215


216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
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;
};


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 240
        fail(data, "Didn't throw as expected\n");
      } 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 248
        fail(data, "Wrong error message. Expected <%s>, found <%s> at %d..%d\n",
             expects->type, actual_message, reader.beg_pos(), reader.end_pos());
249 250
      }
    }
251 252
    if (expects->beg_pos >= 0) {
      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 258
    if (expects->end_pos >= 0) {
      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 264
      }
    }
  } else if (reader.throws()) {
    const char* message = reader.message();
265 266
    fail(data, "Throws unexpectedly with message: %s at location %d-%d\n",
         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 274
  ExceptionExpectation expects;
  int arg_index = 0;
275 276 277
  while (argc > arg_index && strncmp("throws", argv[arg_index], 7)) {
    arg_index++;
  }
278 279
  if (argc > arg_index) {
    expects.throws = true;
280 281
    arg_index++;
    if (argc > arg_index && !IsFlag(argv[arg_index])) {
282
      expects.type = argv[arg_index];
283 284
      arg_index++;
      if (argc > arg_index && !IsFlag(argv[arg_index])) {
285
        expects.beg_pos = atoi(argv[arg_index]);  // NOLINT
286 287
        arg_index++;
        if (argc > arg_index && !IsFlag(argv[arg_index])) {
288
          expects.end_pos = atoi(argv[arg_index]);  // NOLINT
289 290
        }
      }
291 292
    }
  }
293 294 295 296 297 298
  return expects;
}


int main(int argc, const char* argv[]) {
  // Parse command line.
299 300
  // Format:  preparser (<scriptfile> | -e "<source>")
  //                    ["throws" [<exn-type> [<start> [<end>]]]]
301 302
  // Any flags (except an initial -e) are ignored.
  // Flags must not separate "throws" and its arguments.
303 304 305 306 307 308

  // Check for mandatory filename argument.
  int arg_index = 1;
  if (argc <= arg_index) {
    fail(NULL, "ERROR: No filename on command line.\n");
  }
309
  const uint8_t* source = NULL;
310
  const char* filename = argv[arg_index];
311 312 313 314 315 316 317
  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]);
  }
318 319 320 321
  // Check remainder of command line for exception expectations.
  arg_index++;
  ExceptionExpectation expects =
      ParseExpectation(argc - arg_index, argv + arg_index);
322

323 324
  v8::V8::Initialize();

325 326
  ScopedPointer<uint8_t> buffer;
  size_t length;
327

328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
  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));
355
  }
356 357

  // Preparse input file.
358
  AsciiInputStream input_buffer(source, length);
359 360
  size_t kMaxStackSize = 64 * 1024 * sizeof(void*);  // NOLINT
  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 368
  // Check that the expected exception is thrown, if an exception is
  // expected.
369
  CheckException(&data, &expects);
370

371
  return EXIT_SUCCESS;
372
}