From dc390d0e1f1dd43a74130534e857b629c8506c0b Mon Sep 17 00:00:00 2001 From: "lrn@chromium.org" Date: Tue, 23 Nov 2010 11:46:36 +0000 Subject: [PATCH] Untemplated preparser.h and made it depend on virtual types. Extracted preparse-data specification and logging classes. Review URL: http://codereview.chromium.org/5166006 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5877 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/SConscript | 2 + src/parser.cc | 193 ++---- src/parser.h | 151 +---- src/preparse-data.cc | 180 ++++++ src/preparse-data.h | 223 +++++++ src/preparser.cc | 1184 +++++++++++++++++++++++++++++++++++ src/preparser.h | 1188 +----------------------------------- test/cctest/test-api.cc | 2 +- test/cctest/test-parsing.cc | 3 +- tools/visual_studio/v8_base.vcproj | 16 + 10 files changed, 1656 insertions(+), 1486 deletions(-) create mode 100644 src/preparse-data.cc create mode 100644 src/preparse-data.h create mode 100644 src/preparser.cc diff --git a/src/SConscript b/src/SConscript index a83f4d5..8953698 100755 --- a/src/SConscript +++ b/src/SConscript @@ -89,6 +89,8 @@ SOURCES = { objects-visiting.cc oprofile-agent.cc parser.cc + preparser.cc + preparse-data.cc profile-generator.cc property.cc regexp-macro-assembler-irregexp.cc diff --git a/src/parser.cc b/src/parser.cc index 7e4a51e..186d102 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -356,65 +356,6 @@ Handle Parser::LookupCachedSymbol(int symbol_id, } -Vector PartialParserRecorder::ExtractData() { - int function_size = function_store_.size(); - int total_size = ScriptDataImpl::kHeaderSize + function_size; - Vector data = Vector::New(total_size); - preamble_[ScriptDataImpl::kFunctionsSizeOffset] = function_size; - preamble_[ScriptDataImpl::kSymbolCountOffset] = 0; - memcpy(data.start(), preamble_, sizeof(preamble_)); - int symbol_start = ScriptDataImpl::kHeaderSize + function_size; - if (function_size > 0) { - function_store_.WriteTo(data.SubVector(ScriptDataImpl::kHeaderSize, - symbol_start)); - } - return data; -} - - -void CompleteParserRecorder::LogSymbol(int start, Vector literal) { - if (!is_recording_) return; - - int hash = vector_hash(literal); - HashMap::Entry* entry = symbol_table_.Lookup(&literal, hash, true); - int id = static_cast(reinterpret_cast(entry->value)); - if (id == 0) { - // Put (symbol_id_ + 1) into entry and increment it. - id = ++symbol_id_; - entry->value = reinterpret_cast(id); - Vector > symbol = symbol_entries_.AddBlock(1, literal); - entry->key = &symbol[0]; - } - WriteNumber(id - 1); -} - - -Vector CompleteParserRecorder::ExtractData() { - int function_size = function_store_.size(); - // Add terminator to symbols, then pad to unsigned size. - int symbol_size = symbol_store_.size(); - int padding = sizeof(unsigned) - (symbol_size % sizeof(unsigned)); - symbol_store_.AddBlock(padding, ScriptDataImpl::kNumberTerminator); - symbol_size += padding; - int total_size = ScriptDataImpl::kHeaderSize + function_size - + (symbol_size / sizeof(unsigned)); - Vector data = Vector::New(total_size); - preamble_[ScriptDataImpl::kFunctionsSizeOffset] = function_size; - preamble_[ScriptDataImpl::kSymbolCountOffset] = symbol_id_; - memcpy(data.start(), preamble_, sizeof(preamble_)); - int symbol_start = ScriptDataImpl::kHeaderSize + function_size; - if (function_size > 0) { - function_store_.WriteTo(data.SubVector(ScriptDataImpl::kHeaderSize, - symbol_start)); - } - if (!has_error()) { - symbol_store_.WriteTo( - Vector::cast(data.SubVector(symbol_start, total_size))); - } - return data; -} - - FunctionEntry ScriptDataImpl::GetFunctionEntry(int start) { // The current pre-data entry must be a FunctionEntry with the given // start position. @@ -437,92 +378,52 @@ int ScriptDataImpl::GetSymbolIdentifier() { bool ScriptDataImpl::SanityCheck() { // Check that the header data is valid and doesn't specify // point to positions outside the store. - if (store_.length() < ScriptDataImpl::kHeaderSize) return false; - if (magic() != ScriptDataImpl::kMagicNumber) return false; - if (version() != ScriptDataImpl::kCurrentVersion) return false; + if (store_.length() < PreparseDataConstants::kHeaderSize) return false; + if (magic() != PreparseDataConstants::kMagicNumber) return false; + if (version() != PreparseDataConstants::kCurrentVersion) return false; if (has_error()) { // Extra sane sanity check for error message encoding. - if (store_.length() <= kHeaderSize + kMessageTextPos) return false; - if (Read(kMessageStartPos) > Read(kMessageEndPos)) return false; - unsigned arg_count = Read(kMessageArgCountPos); - int pos = kMessageTextPos; + if (store_.length() <= PreparseDataConstants::kHeaderSize + + PreparseDataConstants::kMessageTextPos) { + return false; + } + if (Read(PreparseDataConstants::kMessageStartPos) > + Read(PreparseDataConstants::kMessageEndPos)) { + return false; + } + unsigned arg_count = Read(PreparseDataConstants::kMessageArgCountPos); + int pos = PreparseDataConstants::kMessageTextPos; for (unsigned int i = 0; i <= arg_count; i++) { - if (store_.length() <= kHeaderSize + pos) return false; + if (store_.length() <= PreparseDataConstants::kHeaderSize + pos) { + return false; + } int length = static_cast(Read(pos)); if (length < 0) return false; pos += 1 + length; } - if (store_.length() < kHeaderSize + pos) return false; + if (store_.length() < PreparseDataConstants::kHeaderSize + pos) { + return false; + } return true; } // Check that the space allocated for function entries is sane. int functions_size = - static_cast(store_[ScriptDataImpl::kFunctionsSizeOffset]); + static_cast(store_[PreparseDataConstants::kFunctionsSizeOffset]); if (functions_size < 0) return false; if (functions_size % FunctionEntry::kSize != 0) return false; // Check that the count of symbols is non-negative. int symbol_count = - static_cast(store_[ScriptDataImpl::kSymbolCountOffset]); + static_cast(store_[PreparseDataConstants::kSymbolCountOffset]); if (symbol_count < 0) return false; // Check that the total size has room for header and function entries. int minimum_size = - ScriptDataImpl::kHeaderSize + functions_size; + PreparseDataConstants::kHeaderSize + functions_size; if (store_.length() < minimum_size) return false; return true; } -PartialParserRecorder::PartialParserRecorder() - : function_store_(0), - is_recording_(true), - pause_count_(0) { - preamble_[ScriptDataImpl::kMagicOffset] = ScriptDataImpl::kMagicNumber; - preamble_[ScriptDataImpl::kVersionOffset] = ScriptDataImpl::kCurrentVersion; - preamble_[ScriptDataImpl::kHasErrorOffset] = false; - preamble_[ScriptDataImpl::kFunctionsSizeOffset] = 0; - preamble_[ScriptDataImpl::kSymbolCountOffset] = 0; - preamble_[ScriptDataImpl::kSizeOffset] = 0; - ASSERT_EQ(6, ScriptDataImpl::kHeaderSize); -#ifdef DEBUG - prev_start_ = -1; -#endif -} - - -CompleteParserRecorder::CompleteParserRecorder() - : PartialParserRecorder(), - symbol_store_(0), - symbol_entries_(0), - symbol_table_(vector_compare), - symbol_id_(0) { -} - - -void PartialParserRecorder::WriteString(Vector str) { - function_store_.Add(str.length()); - for (int i = 0; i < str.length(); i++) { - function_store_.Add(str[i]); - } -} - - -void CompleteParserRecorder::WriteNumber(int number) { - ASSERT(number >= 0); - - int mask = (1 << 28) - 1; - for (int i = 28; i > 0; i -= 7) { - if (number > mask) { - symbol_store_.Add(static_cast(number >> i) | 0x80u); - number &= mask; - } - mask >>= 7; - } - symbol_store_.Add(static_cast(number)); -} - - - const char* ScriptDataImpl::ReadString(unsigned* start, int* chars) { int length = start[0]; char* result = NewArray(length + 1); @@ -534,47 +435,26 @@ const char* ScriptDataImpl::ReadString(unsigned* start, int* chars) { return result; } - -void PartialParserRecorder::LogMessage(Scanner::Location loc, - const char* message, - Vector args) { - if (has_error()) return; - preamble_[ScriptDataImpl::kHasErrorOffset] = true; - function_store_.Reset(); - STATIC_ASSERT(ScriptDataImpl::kMessageStartPos == 0); - function_store_.Add(loc.beg_pos); - STATIC_ASSERT(ScriptDataImpl::kMessageEndPos == 1); - function_store_.Add(loc.end_pos); - STATIC_ASSERT(ScriptDataImpl::kMessageArgCountPos == 2); - function_store_.Add(args.length()); - STATIC_ASSERT(ScriptDataImpl::kMessageTextPos == 3); - WriteString(CStrVector(message)); - for (int i = 0; i < args.length(); i++) { - WriteString(CStrVector(args[i])); - } - is_recording_ = false; -} - - Scanner::Location ScriptDataImpl::MessageLocation() { - int beg_pos = Read(kMessageStartPos); - int end_pos = Read(kMessageEndPos); + int beg_pos = Read(PreparseDataConstants::kMessageStartPos); + int end_pos = Read(PreparseDataConstants::kMessageEndPos); return Scanner::Location(beg_pos, end_pos); } const char* ScriptDataImpl::BuildMessage() { - unsigned* start = ReadAddress(kMessageTextPos); + unsigned* start = ReadAddress(PreparseDataConstants::kMessageTextPos); return ReadString(start, NULL); } Vector ScriptDataImpl::BuildArgs() { - int arg_count = Read(kMessageArgCountPos); + int arg_count = Read(PreparseDataConstants::kMessageArgCountPos); const char** array = NewArray(arg_count); // Position after text found by skipping past length field and // length field content words. - int pos = kMessageTextPos + 1 + Read(kMessageTextPos); + int pos = PreparseDataConstants::kMessageTextPos + 1 + + Read(PreparseDataConstants::kMessageTextPos); for (int i = 0; i < arg_count; i++) { int count = 0; array[i] = ReadString(ReadAddress(pos), &count); @@ -585,12 +465,12 @@ Vector ScriptDataImpl::BuildArgs() { unsigned ScriptDataImpl::Read(int position) { - return store_[ScriptDataImpl::kHeaderSize + position]; + return store_[PreparseDataConstants::kHeaderSize + position]; } unsigned* ScriptDataImpl::ReadAddress(int position) { - return &store_[ScriptDataImpl::kHeaderSize + position]; + return &store_[PreparseDataConstants::kHeaderSize + position]; } @@ -4601,9 +4481,10 @@ bool ScriptDataImpl::HasError() { void ScriptDataImpl::Initialize() { // Prepares state for use. - if (store_.length() >= kHeaderSize) { - function_index_ = kHeaderSize; - int symbol_data_offset = kHeaderSize + store_[kFunctionsSizeOffset]; + if (store_.length() >= PreparseDataConstants::kHeaderSize) { + function_index_ = PreparseDataConstants::kHeaderSize; + int symbol_data_offset = PreparseDataConstants::kHeaderSize + + store_[PreparseDataConstants::kFunctionsSizeOffset]; if (store_.length() > symbol_data_offset) { symbol_data_ = reinterpret_cast(&store_[symbol_data_offset]); } else { @@ -4625,7 +4506,7 @@ int ScriptDataImpl::ReadNumber(byte** source) { byte* data = *source; if (data >= symbol_data_end_) return -1; byte input = *data; - if (input == kNumberTerminator) { + if (input == PreparseDataConstants::kNumberTerminator) { // End of stream marker. return -1; } @@ -4646,11 +4527,11 @@ int ScriptDataImpl::ReadNumber(byte** source) { static ScriptDataImpl* DoPreParse(Handle source, unibrow::CharacterStream* stream, bool allow_lazy, - PartialParserRecorder* recorder, + ParserRecorder* recorder, int literal_flags) { V8JavaScriptScanner scanner; scanner.Initialize(source, stream, literal_flags); - preparser::PreParser preparser; + preparser::PreParser preparser; if (!preparser.PreParseProgram(&scanner, recorder, allow_lazy)) { Top::StackOverflow(); return NULL; diff --git a/src/parser.h b/src/parser.h index 9a84ab9..a067bd7 100644 --- a/src/parser.h +++ b/src/parser.h @@ -32,6 +32,7 @@ #include "ast.h" #include "scanner.h" #include "scopes.h" +#include "preparse-data.h" namespace v8 { namespace internal { @@ -123,32 +124,15 @@ class ScriptDataImpl : public ScriptData { Vector BuildArgs(); int symbol_count() { - return (store_.length() > kHeaderSize) ? store_[kSymbolCountOffset] : 0; + return (store_.length() > PreparseDataConstants::kHeaderSize) + ? store_[PreparseDataConstants::kSymbolCountOffset] + : 0; } // The following functions should only be called if SanityCheck has // returned true. - bool has_error() { return store_[kHasErrorOffset]; } - unsigned magic() { return store_[kMagicOffset]; } - unsigned version() { return store_[kVersionOffset]; } - - static const unsigned kMagicNumber = 0xBadDead; - static const unsigned kCurrentVersion = 5; - - static const int kMagicOffset = 0; - static const int kVersionOffset = 1; - static const int kHasErrorOffset = 2; - static const int kFunctionsSizeOffset = 3; - static const int kSymbolCountOffset = 4; - static const int kSizeOffset = 5; - static const int kHeaderSize = 6; - - // If encoding a message, the following positions are fixed. - static const int kMessageStartPos = 0; - static const int kMessageEndPos = 1; - static const int kMessageArgCountPos = 2; - static const int kMessageTextPos = 3; - - static const byte kNumberTerminator = 0x80u; + bool has_error() { return store_[PreparseDataConstants::kHasErrorOffset]; } + unsigned magic() { return store_[PreparseDataConstants::kMagicOffset]; } + unsigned version() { return store_[PreparseDataConstants::kVersionOffset]; } private: Vector store_; @@ -177,127 +161,6 @@ class ScriptDataImpl : public ScriptData { }; -// Record only functions. -class PartialParserRecorder { - public: - PartialParserRecorder(); - virtual ~PartialParserRecorder() {} - - void LogFunction(int start, int end, int literals, int properties) { - function_store_.Add(start); - function_store_.Add(end); - function_store_.Add(literals); - function_store_.Add(properties); - } - - virtual void LogSymbol(int start, const char* symbol, int length) { } - - // Logs an error message and marks the log as containing an error. - // Further logging will be ignored, and ExtractData will return a vector - // representing the error only. - void LogMessage(int start, - int end, - const char* message, - const char* argument_opt) { - Scanner::Location location(start, end); - Vector arguments; - if (argument_opt != NULL) { - arguments = Vector(&argument_opt, 1); - } - this->LogMessage(location, message, arguments); - } - - int function_position() { return function_store_.size(); } - - void LogMessage(Scanner::Location loc, - const char* message, - Vector args); - - virtual Vector ExtractData(); - - void PauseRecording() { - pause_count_++; - is_recording_ = false; - } - - void ResumeRecording() { - ASSERT(pause_count_ > 0); - if (--pause_count_ == 0) is_recording_ = !has_error(); - } - - int symbol_position() { return 0; } - int symbol_ids() { return 0; } - - protected: - bool has_error() { - return static_cast(preamble_[ScriptDataImpl::kHasErrorOffset]); - } - - bool is_recording() { - return is_recording_; - } - - void WriteString(Vector str); - - Collector function_store_; - unsigned preamble_[ScriptDataImpl::kHeaderSize]; - bool is_recording_; - int pause_count_; - -#ifdef DEBUG - int prev_start_; -#endif -}; - - -// Record both functions and symbols. -class CompleteParserRecorder: public PartialParserRecorder { - public: - CompleteParserRecorder(); - virtual ~CompleteParserRecorder() { } - - void LogSymbol(int start, Vector literal); - - virtual void LogSymbol(int start, const char* symbol, int length) { - LogSymbol(start, Vector(symbol, length)); - } - - virtual Vector ExtractData(); - - int symbol_position() { return symbol_store_.size(); } - int symbol_ids() { return symbol_id_; } - - private: - static int vector_hash(Vector string) { - int hash = 0; - for (int i = 0; i < string.length(); i++) { - int c = string[i]; - hash += c; - hash += (hash << 10); - hash ^= (hash >> 6); - } - return hash; - } - - static bool vector_compare(void* a, void* b) { - Vector* string1 = reinterpret_cast* >(a); - Vector* string2 = reinterpret_cast* >(b); - int length = string1->length(); - if (string2->length() != length) return false; - return memcmp(string1->start(), string2->start(), length) == 0; - } - - // Write a non-negative number to the symbol store. - void WriteNumber(int number); - - Collector symbol_store_; - Collector > symbol_entries_; - HashMap symbol_table_; - int symbol_id_; -}; - - - class ParserApi { public: // Parses the source code represented by the compilation info and sets its diff --git a/src/preparse-data.cc b/src/preparse-data.cc new file mode 100644 index 0000000..9a36771 --- /dev/null +++ b/src/preparse-data.cc @@ -0,0 +1,180 @@ +// Copyright 2010 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. + +#include "../include/v8stdint.h" +#include "globals.h" +#include "checks.h" +#include "allocation.h" +#include "utils.h" +#include "list-inl.h" +#include "hashmap.h" +#include "preparse-data.h" + +namespace v8 { +namespace internal { + +// ---------------------------------------------------------------------------- +// FunctionLoggingParserRecorder + +FunctionLoggingParserRecorder::FunctionLoggingParserRecorder() + : function_store_(0), + is_recording_(true), + pause_count_(0) { + preamble_[PreparseDataConstants::kMagicOffset] = + PreparseDataConstants::kMagicNumber; + preamble_[PreparseDataConstants::kVersionOffset] = + PreparseDataConstants::kCurrentVersion; + preamble_[PreparseDataConstants::kHasErrorOffset] = false; + preamble_[PreparseDataConstants::kFunctionsSizeOffset] = 0; + preamble_[PreparseDataConstants::kSymbolCountOffset] = 0; + preamble_[PreparseDataConstants::kSizeOffset] = 0; + ASSERT_EQ(6, PreparseDataConstants::kHeaderSize); +#ifdef DEBUG + prev_start_ = -1; +#endif +} + + +void FunctionLoggingParserRecorder::LogMessage(int start_pos, + int end_pos, + const char* message, + const char* arg_opt) { + if (has_error()) return; + preamble_[PreparseDataConstants::kHasErrorOffset] = true; + function_store_.Reset(); + STATIC_ASSERT(PreparseDataConstants::kMessageStartPos == 0); + function_store_.Add(start_pos); + STATIC_ASSERT(PreparseDataConstants::kMessageEndPos == 1); + function_store_.Add(end_pos); + STATIC_ASSERT(PreparseDataConstants::kMessageArgCountPos == 2); + function_store_.Add((arg_opt == NULL) ? 0 : 1); + STATIC_ASSERT(PreparseDataConstants::kMessageTextPos == 3); + WriteString(CStrVector(message)); + if (arg_opt) WriteString(CStrVector(arg_opt)); + is_recording_ = false; +} + + +void FunctionLoggingParserRecorder::WriteString(Vector str) { + function_store_.Add(str.length()); + for (int i = 0; i < str.length(); i++) { + function_store_.Add(str[i]); + } +} + +// ---------------------------------------------------------------------------- +// PartialParserRecorder - Record both function entries and symbols. + +Vector PartialParserRecorder::ExtractData() { + int function_size = function_store_.size(); + int total_size = PreparseDataConstants::kHeaderSize + function_size; + Vector data = Vector::New(total_size); + preamble_[PreparseDataConstants::kFunctionsSizeOffset] = function_size; + preamble_[PreparseDataConstants::kSymbolCountOffset] = 0; + memcpy(data.start(), preamble_, sizeof(preamble_)); + int symbol_start = PreparseDataConstants::kHeaderSize + function_size; + if (function_size > 0) { + function_store_.WriteTo(data.SubVector(PreparseDataConstants::kHeaderSize, + symbol_start)); + } + return data; +} + + +// ---------------------------------------------------------------------------- +// CompleteParserRecorder - Record both function entries and symbols. + +CompleteParserRecorder::CompleteParserRecorder() + : FunctionLoggingParserRecorder(), + symbol_store_(0), + symbol_entries_(0), + symbol_table_(vector_compare), + symbol_id_(0) { +} + + +void CompleteParserRecorder::LogSymbol( + int start, const char* literal_chars, int length) { + if (!is_recording_) return; + + Vector literal(literal_chars, length); + int hash = vector_hash(literal); + HashMap::Entry* entry = symbol_table_.Lookup(&literal, hash, true); + int id = static_cast(reinterpret_cast(entry->value)); + if (id == 0) { + // Put (symbol_id_ + 1) into entry and increment it. + id = ++symbol_id_; + entry->value = reinterpret_cast(id); + Vector > symbol = symbol_entries_.AddBlock(1, literal); + entry->key = &symbol[0]; + } + WriteNumber(id - 1); +} + + +Vector CompleteParserRecorder::ExtractData() { + int function_size = function_store_.size(); + // Add terminator to symbols, then pad to unsigned size. + int symbol_size = symbol_store_.size(); + int padding = sizeof(unsigned) - (symbol_size % sizeof(unsigned)); + symbol_store_.AddBlock(padding, PreparseDataConstants::kNumberTerminator); + symbol_size += padding; + int total_size = PreparseDataConstants::kHeaderSize + function_size + + (symbol_size / sizeof(unsigned)); + Vector data = Vector::New(total_size); + preamble_[PreparseDataConstants::kFunctionsSizeOffset] = function_size; + preamble_[PreparseDataConstants::kSymbolCountOffset] = symbol_id_; + memcpy(data.start(), preamble_, sizeof(preamble_)); + int symbol_start = PreparseDataConstants::kHeaderSize + function_size; + if (function_size > 0) { + function_store_.WriteTo(data.SubVector(PreparseDataConstants::kHeaderSize, + symbol_start)); + } + if (!has_error()) { + symbol_store_.WriteTo( + Vector::cast(data.SubVector(symbol_start, total_size))); + } + return data; +} + + +void CompleteParserRecorder::WriteNumber(int number) { + ASSERT(number >= 0); + + int mask = (1 << 28) - 1; + for (int i = 28; i > 0; i -= 7) { + if (number > mask) { + symbol_store_.Add(static_cast(number >> i) | 0x80u); + number &= mask; + } + mask >>= 7; + } + symbol_store_.Add(static_cast(number)); +} + + +} } // namespace v8::internal. diff --git a/src/preparse-data.h b/src/preparse-data.h new file mode 100644 index 0000000..a96e50f --- /dev/null +++ b/src/preparse-data.h @@ -0,0 +1,223 @@ +// Copyright 2010 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. + +#ifndef V8_PREPARSER_DATA_H_ +#define V8_PREPARSER_DATA_H_ + +#include "hashmap.h" + +namespace v8 { +namespace internal { + +// Generic and general data used by preparse data recorders and readers. + +class PreparseDataConstants : public AllStatic { + public: + // Layout and constants of the preparse data exchange format. + static const unsigned kMagicNumber = 0xBadDead; + static const unsigned kCurrentVersion = 5; + + static const int kMagicOffset = 0; + static const int kVersionOffset = 1; + static const int kHasErrorOffset = 2; + static const int kFunctionsSizeOffset = 3; + static const int kSymbolCountOffset = 4; + static const int kSizeOffset = 5; + static const int kHeaderSize = 6; + + // If encoding a message, the following positions are fixed. + static const int kMessageStartPos = 0; + static const int kMessageEndPos = 1; + static const int kMessageArgCountPos = 2; + static const int kMessageTextPos = 3; + + static const byte kNumberTerminator = 0x80u; +}; + + +// ---------------------------------------------------------------------------- +// ParserRecorder - Logging of preparser data. + +// Abstract interface for preparse data recorder. +class ParserRecorder { + public: + ParserRecorder() { } + virtual ~ParserRecorder() { } + + // Logs the scope and some details of a function literal in the source. + virtual void LogFunction(int start, + int end, + int literals, + int properties) = 0; + + // Logs a symbol creation of a literal or identifier. + virtual void LogSymbol(int start, const char* symbol, int length) = 0; + + // Logs an error message and marks the log as containing an error. + // Further logging will be ignored, and ExtractData will return a vector + // representing the error only. + virtual void LogMessage(int start, + int end, + const char* message, + const char* argument_opt) = 0; + + virtual int function_position() = 0; + + virtual int symbol_position() = 0; + + virtual int symbol_ids() = 0; + + virtual Vector ExtractData() = 0; + + virtual void PauseRecording() = 0; + + virtual void ResumeRecording() = 0; +}; + + +// ---------------------------------------------------------------------------- +// FunctionLoggingParserRecorder - Record only function entries + +class FunctionLoggingParserRecorder : public ParserRecorder { + public: + FunctionLoggingParserRecorder(); + virtual ~FunctionLoggingParserRecorder() {} + + virtual void LogFunction(int start, int end, int literals, int properties) { + function_store_.Add(start); + function_store_.Add(end); + function_store_.Add(literals); + function_store_.Add(properties); + } + + // Logs an error message and marks the log as containing an error. + // Further logging will be ignored, and ExtractData will return a vector + // representing the error only. + virtual void LogMessage(int start, + int end, + const char* message, + const char* argument_opt); + + virtual int function_position() { return function_store_.size(); } + + + virtual Vector ExtractData() = 0; + + virtual void PauseRecording() { + pause_count_++; + is_recording_ = false; + } + + virtual void ResumeRecording() { + ASSERT(pause_count_ > 0); + if (--pause_count_ == 0) is_recording_ = !has_error(); + } + + protected: + bool has_error() { + return static_cast(preamble_[PreparseDataConstants::kHasErrorOffset]); + } + + bool is_recording() { + return is_recording_; + } + + void WriteString(Vector str); + + Collector function_store_; + unsigned preamble_[PreparseDataConstants::kHeaderSize]; + bool is_recording_; + int pause_count_; + +#ifdef DEBUG + int prev_start_; +#endif +}; + + +// ---------------------------------------------------------------------------- +// PartialParserRecorder - Record only function entries + +class PartialParserRecorder : public FunctionLoggingParserRecorder { + public: + PartialParserRecorder() : FunctionLoggingParserRecorder() { } + virtual void LogSymbol(int start, const char* symbol, int length) { } + virtual ~PartialParserRecorder() { } + virtual Vector ExtractData(); + virtual int symbol_position() { return 0; } + virtual int symbol_ids() { return 0; } +}; + + +// ---------------------------------------------------------------------------- +// CompleteParserRecorder - Record both function entries and symbols. + +class CompleteParserRecorder: public FunctionLoggingParserRecorder { + public: + CompleteParserRecorder(); + virtual ~CompleteParserRecorder() { } + + virtual void LogSymbol(int start, const char* symbol, int length); + + virtual Vector ExtractData(); + + virtual int symbol_position() { return symbol_store_.size(); } + virtual int symbol_ids() { return symbol_id_; } + + private: + static int vector_hash(Vector string) { + int hash = 0; + for (int i = 0; i < string.length(); i++) { + int c = string[i]; + hash += c; + hash += (hash << 10); + hash ^= (hash >> 6); + } + return hash; + } + + static bool vector_compare(void* a, void* b) { + Vector* string1 = reinterpret_cast* >(a); + Vector* string2 = reinterpret_cast* >(b); + int length = string1->length(); + if (string2->length() != length) return false; + return memcmp(string1->start(), string2->start(), length) == 0; + } + + // Write a non-negative number to the symbol store. + void WriteNumber(int number); + + Collector symbol_store_; + Collector > symbol_entries_; + HashMap symbol_table_; + int symbol_id_; +}; + + +} } // namespace v8::internal. + +#endif // V8_PREPARSER_DATA_H_ diff --git a/src/preparser.cc b/src/preparser.cc new file mode 100644 index 0000000..9061731 --- /dev/null +++ b/src/preparser.cc @@ -0,0 +1,1184 @@ +// Copyright 2010 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. + +#include "../include/v8stdint.h" +#include "unicode.h" +#include "globals.h" +#include "checks.h" +#include "allocation.h" +#include "utils.h" +#include "list.h" +#include "scanner-base.h" +#include "preparse-data.h" +#include "preparser.h" + +namespace v8 { +namespace preparser { + +// Preparsing checks a JavaScript program and emits preparse-data that helps +// a later parsing to be faster. +// See preparser-data.h for the data. + +// The PreParser checks that the syntax follows the grammar for JavaScript, +// and collects some information about the program along the way. +// The grammar check is only performed in order to understand the program +// sufficiently to deduce some information about it, that can be used +// to speed up later parsing. Finding errors is not the goal of pre-parsing, +// rather it is to speed up properly written and correct programs. +// That means that contextual checks (like a label being declared where +// it is used) are generally omitted. + +namespace i = ::v8::internal; + +#define CHECK_OK ok); \ + if (!*ok) return -1; \ + ((void)0 +#define DUMMY ) // to make indentation work +#undef DUMMY + + +void PreParser::ReportUnexpectedToken(i::Token::Value token) { + // We don't report stack overflows here, to avoid increasing the + // stack depth even further. Instead we report it after parsing is + // over, in ParseProgram. + if (token == i::Token::ILLEGAL && scanner_->stack_overflow()) { + return; + } + i::JavaScriptScanner::Location source_location = scanner_->location(); + + // Four of the tokens are treated specially + switch (token) { + case i::Token::EOS: + return ReportMessageAt(source_location.beg_pos, source_location.end_pos, + "unexpected_eos", NULL); + case i::Token::NUMBER: + return ReportMessageAt(source_location.beg_pos, source_location.end_pos, + "unexpected_token_number", NULL); + case i::Token::STRING: + return ReportMessageAt(source_location.beg_pos, source_location.end_pos, + "unexpected_token_string", NULL); + case i::Token::IDENTIFIER: + return ReportMessageAt(source_location.beg_pos, source_location.end_pos, + "unexpected_token_identifier", NULL); + default: + const char* name = i::Token::String(token); + ReportMessageAt(source_location.beg_pos, source_location.end_pos, + "unexpected_token", name); + } +} + + +SourceElements PreParser::ParseSourceElements(int end_token, + bool* ok) { + // SourceElements :: + // (Statement)* + + while (peek() != end_token) { + ParseStatement(CHECK_OK); + } + return kUnknownSourceElements; +} + + +Statement PreParser::ParseStatement(bool* ok) { + // Statement :: + // Block + // VariableStatement + // EmptyStatement + // ExpressionStatement + // IfStatement + // IterationStatement + // ContinueStatement + // BreakStatement + // ReturnStatement + // WithStatement + // LabelledStatement + // SwitchStatement + // ThrowStatement + // TryStatement + // DebuggerStatement + + // Note: Since labels can only be used by 'break' and 'continue' + // statements, which themselves are only valid within blocks, + // iterations or 'switch' statements (i.e., BreakableStatements), + // labels can be simply ignored in all other cases; except for + // trivial labeled break statements 'label: break label' which is + // parsed into an empty statement. + + // Keep the source position of the statement + switch (peek()) { + case i::Token::LBRACE: + return ParseBlock(ok); + + case i::Token::CONST: + case i::Token::VAR: + return ParseVariableStatement(ok); + + case i::Token::SEMICOLON: + Next(); + return kUnknownStatement; + + case i::Token::IF: + return ParseIfStatement(ok); + + case i::Token::DO: + return ParseDoWhileStatement(ok); + + case i::Token::WHILE: + return ParseWhileStatement(ok); + + case i::Token::FOR: + return ParseForStatement(ok); + + case i::Token::CONTINUE: + return ParseContinueStatement(ok); + + case i::Token::BREAK: + return ParseBreakStatement(ok); + + case i::Token::RETURN: + return ParseReturnStatement(ok); + + case i::Token::WITH: + return ParseWithStatement(ok); + + case i::Token::SWITCH: + return ParseSwitchStatement(ok); + + case i::Token::THROW: + return ParseThrowStatement(ok); + + case i::Token::TRY: + return ParseTryStatement(ok); + + case i::Token::FUNCTION: + return ParseFunctionDeclaration(ok); + + case i::Token::NATIVE: + return ParseNativeDeclaration(ok); + + case i::Token::DEBUGGER: + return ParseDebuggerStatement(ok); + + default: + return ParseExpressionOrLabelledStatement(ok); + } +} + + +Statement PreParser::ParseFunctionDeclaration(bool* ok) { + // FunctionDeclaration :: + // 'function' Identifier '(' FormalParameterListopt ')' '{' FunctionBody '}' + Expect(i::Token::FUNCTION, CHECK_OK); + ParseIdentifier(CHECK_OK); + ParseFunctionLiteral(CHECK_OK); + return kUnknownStatement; +} + + +// Language extension which is only enabled for source files loaded +// through the API's extension mechanism. A native function +// declaration is resolved by looking up the function through a +// callback provided by the extension. +Statement PreParser::ParseNativeDeclaration(bool* ok) { + Expect(i::Token::NATIVE, CHECK_OK); + Expect(i::Token::FUNCTION, CHECK_OK); + ParseIdentifier(CHECK_OK); + Expect(i::Token::LPAREN, CHECK_OK); + bool done = (peek() == i::Token::RPAREN); + while (!done) { + ParseIdentifier(CHECK_OK); + done = (peek() == i::Token::RPAREN); + if (!done) { + Expect(i::Token::COMMA, CHECK_OK); + } + } + Expect(i::Token::RPAREN, CHECK_OK); + Expect(i::Token::SEMICOLON, CHECK_OK); + return kUnknownStatement; +} + + +Statement PreParser::ParseBlock(bool* ok) { + // Block :: + // '{' Statement* '}' + + // Note that a Block does not introduce a new execution scope! + // (ECMA-262, 3rd, 12.2) + // + Expect(i::Token::LBRACE, CHECK_OK); + while (peek() != i::Token::RBRACE) { + ParseStatement(CHECK_OK); + } + Expect(i::Token::RBRACE, CHECK_OK); + return kUnknownStatement; +} + + +Statement PreParser::ParseVariableStatement(bool* ok) { + // VariableStatement :: + // VariableDeclarations ';' + + Statement result = ParseVariableDeclarations(true, NULL, CHECK_OK); + ExpectSemicolon(CHECK_OK); + return result; +} + + +// If the variable declaration declares exactly one non-const +// variable, then *var is set to that variable. In all other cases, +// *var is untouched; in particular, it is the caller's responsibility +// to initialize it properly. This mechanism is also used for the parsing +// of 'for-in' loops. +Statement PreParser::ParseVariableDeclarations(bool accept_IN, + int* num_decl, + bool* ok) { + // VariableDeclarations :: + // ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[','] + + if (peek() == i::Token::VAR) { + Consume(i::Token::VAR); + } else if (peek() == i::Token::CONST) { + Consume(i::Token::CONST); + } else { + *ok = false; + return 0; + } + + // The scope of a variable/const declared anywhere inside a function + // is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). . + int nvars = 0; // the number of variables declared + do { + // Parse variable name. + if (nvars > 0) Consume(i::Token::COMMA); + ParseIdentifier(CHECK_OK); + nvars++; + if (peek() == i::Token::ASSIGN) { + Expect(i::Token::ASSIGN, CHECK_OK); + ParseAssignmentExpression(accept_IN, CHECK_OK); + } + } while (peek() == i::Token::COMMA); + + if (num_decl != NULL) *num_decl = nvars; + return kUnknownStatement; +} + + +Statement PreParser::ParseExpressionOrLabelledStatement( + bool* ok) { + // ExpressionStatement | LabelledStatement :: + // Expression ';' + // Identifier ':' Statement + + Expression expr = ParseExpression(true, CHECK_OK); + if (peek() == i::Token::COLON && expr == kIdentifierExpression) { + Consume(i::Token::COLON); + return ParseStatement(ok); + } + // Parsed expression statement. + ExpectSemicolon(CHECK_OK); + return kUnknownStatement; +} + + +Statement PreParser::ParseIfStatement(bool* ok) { + // IfStatement :: + // 'if' '(' Expression ')' Statement ('else' Statement)? + + Expect(i::Token::IF, CHECK_OK); + Expect(i::Token::LPAREN, CHECK_OK); + ParseExpression(true, CHECK_OK); + Expect(i::Token::RPAREN, CHECK_OK); + ParseStatement(CHECK_OK); + if (peek() == i::Token::ELSE) { + Next(); + ParseStatement(CHECK_OK); + } + return kUnknownStatement; +} + + +Statement PreParser::ParseContinueStatement(bool* ok) { + // ContinueStatement :: + // 'continue' [no line terminator] Identifier? ';' + + Expect(i::Token::CONTINUE, CHECK_OK); + i::Token::Value tok = peek(); + if (!scanner_->has_line_terminator_before_next() && + tok != i::Token::SEMICOLON && + tok != i::Token::RBRACE && + tok != i::Token::EOS) { + ParseIdentifier(CHECK_OK); + } + ExpectSemicolon(CHECK_OK); + return kUnknownStatement; +} + + +Statement PreParser::ParseBreakStatement(bool* ok) { + // BreakStatement :: + // 'break' [no line terminator] Identifier? ';' + + Expect(i::Token::BREAK, CHECK_OK); + i::Token::Value tok = peek(); + if (!scanner_->has_line_terminator_before_next() && + tok != i::Token::SEMICOLON && + tok != i::Token::RBRACE && + tok != i::Token::EOS) { + ParseIdentifier(CHECK_OK); + } + ExpectSemicolon(CHECK_OK); + return kUnknownStatement; +} + + +Statement PreParser::ParseReturnStatement(bool* ok) { + // ReturnStatement :: + // 'return' [no line terminator] Expression? ';' + + // Consume the return token. It is necessary to do the before + // reporting any errors on it, because of the way errors are + // reported (underlining). + Expect(i::Token::RETURN, CHECK_OK); + + // An ECMAScript program is considered syntactically incorrect if it + // contains a return statement that is not within the body of a + // function. See ECMA-262, section 12.9, page 67. + // This is not handled during preparsing. + + i::Token::Value tok = peek(); + if (!scanner_->has_line_terminator_before_next() && + tok != i::Token::SEMICOLON && + tok != i::Token::RBRACE && + tok != i::Token::EOS) { + ParseExpression(true, CHECK_OK); + } + ExpectSemicolon(CHECK_OK); + return kUnknownStatement; +} + + +Statement PreParser::ParseWithStatement(bool* ok) { + // WithStatement :: + // 'with' '(' Expression ')' Statement + Expect(i::Token::WITH, CHECK_OK); + Expect(i::Token::LPAREN, CHECK_OK); + ParseExpression(true, CHECK_OK); + Expect(i::Token::RPAREN, CHECK_OK); + + scope_->EnterWith(); + ParseStatement(CHECK_OK); + scope_->LeaveWith(); + return kUnknownStatement; +} + + +Statement PreParser::ParseSwitchStatement(bool* ok) { + // SwitchStatement :: + // 'switch' '(' Expression ')' '{' CaseClause* '}' + + Expect(i::Token::SWITCH, CHECK_OK); + Expect(i::Token::LPAREN, CHECK_OK); + ParseExpression(true, CHECK_OK); + Expect(i::Token::RPAREN, CHECK_OK); + + Expect(i::Token::LBRACE, CHECK_OK); + i::Token::Value token = peek(); + while (token != i::Token::RBRACE) { + if (token == i::Token::CASE) { + Expect(i::Token::CASE, CHECK_OK); + ParseExpression(true, CHECK_OK); + Expect(i::Token::COLON, CHECK_OK); + } else if (token == i::Token::DEFAULT) { + Expect(i::Token::DEFAULT, CHECK_OK); + Expect(i::Token::COLON, CHECK_OK); + } else { + ParseStatement(CHECK_OK); + } + token = peek(); + } + Expect(i::Token::RBRACE, CHECK_OK); + + return kUnknownStatement; +} + + +Statement PreParser::ParseDoWhileStatement(bool* ok) { + // DoStatement :: + // 'do' Statement 'while' '(' Expression ')' ';' + + Expect(i::Token::DO, CHECK_OK); + ParseStatement(CHECK_OK); + Expect(i::Token::WHILE, CHECK_OK); + Expect(i::Token::LPAREN, CHECK_OK); + ParseExpression(true, CHECK_OK); + Expect(i::Token::RPAREN, CHECK_OK); + return kUnknownStatement; +} + + +Statement PreParser::ParseWhileStatement(bool* ok) { + // WhileStatement :: + // 'while' '(' Expression ')' Statement + + Expect(i::Token::WHILE, CHECK_OK); + Expect(i::Token::LPAREN, CHECK_OK); + ParseExpression(true, CHECK_OK); + Expect(i::Token::RPAREN, CHECK_OK); + ParseStatement(CHECK_OK); + return kUnknownStatement; +} + + +Statement PreParser::ParseForStatement(bool* ok) { + // ForStatement :: + // 'for' '(' Expression? ';' Expression? ';' Expression? ')' Statement + + Expect(i::Token::FOR, CHECK_OK); + Expect(i::Token::LPAREN, CHECK_OK); + if (peek() != i::Token::SEMICOLON) { + if (peek() == i::Token::VAR || peek() == i::Token::CONST) { + int decl_count; + ParseVariableDeclarations(false, &decl_count, CHECK_OK); + if (peek() == i::Token::IN && decl_count == 1) { + Expect(i::Token::IN, CHECK_OK); + ParseExpression(true, CHECK_OK); + Expect(i::Token::RPAREN, CHECK_OK); + + ParseStatement(CHECK_OK); + return kUnknownStatement; + } + } else { + ParseExpression(false, CHECK_OK); + if (peek() == i::Token::IN) { + Expect(i::Token::IN, CHECK_OK); + ParseExpression(true, CHECK_OK); + Expect(i::Token::RPAREN, CHECK_OK); + + ParseStatement(CHECK_OK); + return kUnknownStatement; + } + } + } + + // Parsed initializer at this point. + Expect(i::Token::SEMICOLON, CHECK_OK); + + if (peek() != i::Token::SEMICOLON) { + ParseExpression(true, CHECK_OK); + } + Expect(i::Token::SEMICOLON, CHECK_OK); + + if (peek() != i::Token::RPAREN) { + ParseExpression(true, CHECK_OK); + } + Expect(i::Token::RPAREN, CHECK_OK); + + ParseStatement(CHECK_OK); + return kUnknownStatement; +} + + +Statement PreParser::ParseThrowStatement(bool* ok) { + // ThrowStatement :: + // 'throw' [no line terminator] Expression ';' + + Expect(i::Token::THROW, CHECK_OK); + if (scanner_->has_line_terminator_before_next()) { + i::JavaScriptScanner::Location pos = scanner_->location(); + ReportMessageAt(pos.beg_pos, pos.end_pos, + "newline_after_throw", NULL); + *ok = false; + return kUnknownStatement; + } + ParseExpression(true, CHECK_OK); + ExpectSemicolon(CHECK_OK); + + return kUnknownStatement; +} + + +Statement PreParser::ParseTryStatement(bool* ok) { + // TryStatement :: + // 'try' Block Catch + // 'try' Block Finally + // 'try' Block Catch Finally + // + // Catch :: + // 'catch' '(' Identifier ')' Block + // + // Finally :: + // 'finally' Block + + // In preparsing, allow any number of catch/finally blocks, including zero + // of both. + + Expect(i::Token::TRY, CHECK_OK); + + ParseBlock(CHECK_OK); + + bool catch_or_finally_seen = false; + if (peek() == i::Token::CATCH) { + Consume(i::Token::CATCH); + Expect(i::Token::LPAREN, CHECK_OK); + ParseIdentifier(CHECK_OK); + Expect(i::Token::RPAREN, CHECK_OK); + scope_->EnterWith(); + ParseBlock(ok); + scope_->LeaveWith(); + if (!*ok) return kUnknownStatement; + catch_or_finally_seen = true; + } + if (peek() == i::Token::FINALLY) { + Consume(i::Token::FINALLY); + ParseBlock(CHECK_OK); + catch_or_finally_seen = true; + } + if (!catch_or_finally_seen) { + *ok = false; + } + return kUnknownStatement; +} + + +Statement PreParser::ParseDebuggerStatement(bool* ok) { + // In ECMA-262 'debugger' is defined as a reserved keyword. In some browser + // contexts this is used as a statement which invokes the debugger as if a + // break point is present. + // DebuggerStatement :: + // 'debugger' ';' + + Expect(i::Token::DEBUGGER, CHECK_OK); + ExpectSemicolon(CHECK_OK); + return kUnknownStatement; +} + + +// Precedence = 1 +Expression PreParser::ParseExpression(bool accept_IN, bool* ok) { + // Expression :: + // AssignmentExpression + // Expression ',' AssignmentExpression + + Expression result = ParseAssignmentExpression(accept_IN, CHECK_OK); + while (peek() == i::Token::COMMA) { + Expect(i::Token::COMMA, CHECK_OK); + ParseAssignmentExpression(accept_IN, CHECK_OK); + result = kUnknownExpression; + } + return result; +} + + +// Precedence = 2 +Expression PreParser::ParseAssignmentExpression(bool accept_IN, + bool* ok) { + // AssignmentExpression :: + // ConditionalExpression + // LeftHandSideExpression AssignmentOperator AssignmentExpression + + Expression expression = ParseConditionalExpression(accept_IN, CHECK_OK); + + if (!i::Token::IsAssignmentOp(peek())) { + // Parsed conditional expression only (no assignment). + return expression; + } + + i::Token::Value op = Next(); // Get assignment operator. + ParseAssignmentExpression(accept_IN, CHECK_OK); + + if ((op == i::Token::ASSIGN) && (expression == kThisPropertyExpression)) { + scope_->AddProperty(); + } + + return kUnknownExpression; +} + + +// Precedence = 3 +Expression PreParser::ParseConditionalExpression(bool accept_IN, + bool* ok) { + // ConditionalExpression :: + // LogicalOrExpression + // LogicalOrExpression '?' AssignmentExpression ':' AssignmentExpression + + // We start using the binary expression parser for prec >= 4 only! + Expression expression = ParseBinaryExpression(4, accept_IN, CHECK_OK); + if (peek() != i::Token::CONDITIONAL) return expression; + Consume(i::Token::CONDITIONAL); + // In parsing the first assignment expression in conditional + // expressions we always accept the 'in' keyword; see ECMA-262, + // section 11.12, page 58. + ParseAssignmentExpression(true, CHECK_OK); + Expect(i::Token::COLON, CHECK_OK); + ParseAssignmentExpression(accept_IN, CHECK_OK); + return kUnknownExpression; +} + + +int PreParser::Precedence(i::Token::Value tok, bool accept_IN) { + if (tok == i::Token::IN && !accept_IN) + return 0; // 0 precedence will terminate binary expression parsing + + return i::Token::Precedence(tok); +} + + +// Precedence >= 4 +Expression PreParser::ParseBinaryExpression(int prec, + bool accept_IN, + bool* ok) { + Expression result = ParseUnaryExpression(CHECK_OK); + for (int prec1 = Precedence(peek(), accept_IN); prec1 >= prec; prec1--) { + // prec1 >= 4 + while (Precedence(peek(), accept_IN) == prec1) { + Next(); + ParseBinaryExpression(prec1 + 1, accept_IN, CHECK_OK); + result = kUnknownExpression; + } + } + return result; +} + + +Expression PreParser::ParseUnaryExpression(bool* ok) { + // UnaryExpression :: + // PostfixExpression + // 'delete' UnaryExpression + // 'void' UnaryExpression + // 'typeof' UnaryExpression + // '++' UnaryExpression + // '--' UnaryExpression + // '+' UnaryExpression + // '-' UnaryExpression + // '~' UnaryExpression + // '!' UnaryExpression + + i::Token::Value op = peek(); + if (i::Token::IsUnaryOp(op) || i::Token::IsCountOp(op)) { + op = Next(); + ParseUnaryExpression(ok); + return kUnknownExpression; + } else { + return ParsePostfixExpression(ok); + } +} + + +Expression PreParser::ParsePostfixExpression(bool* ok) { + // PostfixExpression :: + // LeftHandSideExpression ('++' | '--')? + + Expression expression = ParseLeftHandSideExpression(CHECK_OK); + if (!scanner_->has_line_terminator_before_next() && + i::Token::IsCountOp(peek())) { + Next(); + return kUnknownExpression; + } + return expression; +} + + +Expression PreParser::ParseLeftHandSideExpression(bool* ok) { + // LeftHandSideExpression :: + // (NewExpression | MemberExpression) ... + + Expression result; + if (peek() == i::Token::NEW) { + result = ParseNewExpression(CHECK_OK); + } else { + result = ParseMemberExpression(CHECK_OK); + } + + while (true) { + switch (peek()) { + case i::Token::LBRACK: { + Consume(i::Token::LBRACK); + ParseExpression(true, CHECK_OK); + Expect(i::Token::RBRACK, CHECK_OK); + if (result == kThisExpression) { + result = kThisPropertyExpression; + } else { + result = kUnknownExpression; + } + break; + } + + case i::Token::LPAREN: { + ParseArguments(CHECK_OK); + result = kUnknownExpression; + break; + } + + case i::Token::PERIOD: { + Consume(i::Token::PERIOD); + ParseIdentifierName(CHECK_OK); + if (result == kThisExpression) { + result = kThisPropertyExpression; + } else { + result = kUnknownExpression; + } + break; + } + + default: + return result; + } + } +} + + +Expression PreParser::ParseNewExpression(bool* ok) { + // NewExpression :: + // ('new')+ MemberExpression + + // The grammar for new expressions is pretty warped. The keyword + // 'new' can either be a part of the new expression (where it isn't + // followed by an argument list) or a part of the member expression, + // where it must be followed by an argument list. To accommodate + // this, we parse the 'new' keywords greedily and keep track of how + // many we have parsed. This information is then passed on to the + // member expression parser, which is only allowed to match argument + // lists as long as it has 'new' prefixes left + unsigned new_count = 0; + do { + Consume(i::Token::NEW); + new_count++; + } while (peek() == i::Token::NEW); + + return ParseMemberWithNewPrefixesExpression(new_count, ok); +} + + +Expression PreParser::ParseMemberExpression(bool* ok) { + return ParseMemberWithNewPrefixesExpression(0, ok); +} + + +Expression PreParser::ParseMemberWithNewPrefixesExpression( + unsigned new_count, bool* ok) { + // MemberExpression :: + // (PrimaryExpression | FunctionLiteral) + // ('[' Expression ']' | '.' Identifier | Arguments)* + + // Parse the initial primary or function expression. + Expression result = kUnknownExpression; + if (peek() == i::Token::FUNCTION) { + Consume(i::Token::FUNCTION); + if (peek() == i::Token::IDENTIFIER) { + ParseIdentifier(CHECK_OK); + } + result = ParseFunctionLiteral(CHECK_OK); + } else { + result = ParsePrimaryExpression(CHECK_OK); + } + + while (true) { + switch (peek()) { + case i::Token::LBRACK: { + Consume(i::Token::LBRACK); + ParseExpression(true, CHECK_OK); + Expect(i::Token::RBRACK, CHECK_OK); + if (result == kThisExpression) { + result = kThisPropertyExpression; + } else { + result = kUnknownExpression; + } + break; + } + case i::Token::PERIOD: { + Consume(i::Token::PERIOD); + ParseIdentifierName(CHECK_OK); + if (result == kThisExpression) { + result = kThisPropertyExpression; + } else { + result = kUnknownExpression; + } + break; + } + case i::Token::LPAREN: { + if (new_count == 0) return result; + // Consume one of the new prefixes (already parsed). + ParseArguments(CHECK_OK); + new_count--; + result = kUnknownExpression; + break; + } + default: + return result; + } + } +} + + +Expression PreParser::ParsePrimaryExpression(bool* ok) { + // PrimaryExpression :: + // 'this' + // 'null' + // 'true' + // 'false' + // Identifier + // Number + // String + // ArrayLiteral + // ObjectLiteral + // RegExpLiteral + // '(' Expression ')' + + Expression result = kUnknownExpression; + switch (peek()) { + case i::Token::THIS: { + Next(); + result = kThisExpression; + break; + } + + case i::Token::IDENTIFIER: { + ParseIdentifier(CHECK_OK); + result = kIdentifierExpression; + break; + } + + case i::Token::NULL_LITERAL: + case i::Token::TRUE_LITERAL: + case i::Token::FALSE_LITERAL: + case i::Token::NUMBER: { + Next(); + break; + } + case i::Token::STRING: { + Next(); + result = GetStringSymbol(); + break; + } + + case i::Token::ASSIGN_DIV: + result = ParseRegExpLiteral(true, CHECK_OK); + break; + + case i::Token::DIV: + result = ParseRegExpLiteral(false, CHECK_OK); + break; + + case i::Token::LBRACK: + result = ParseArrayLiteral(CHECK_OK); + break; + + case i::Token::LBRACE: + result = ParseObjectLiteral(CHECK_OK); + break; + + case i::Token::LPAREN: + Consume(i::Token::LPAREN); + result = ParseExpression(true, CHECK_OK); + Expect(i::Token::RPAREN, CHECK_OK); + if (result == kIdentifierExpression) result = kUnknownExpression; + break; + + case i::Token::MOD: + result = ParseV8Intrinsic(CHECK_OK); + break; + + default: { + Next(); + *ok = false; + return kUnknownExpression; + } + } + + return result; +} + + +Expression PreParser::ParseArrayLiteral(bool* ok) { + // ArrayLiteral :: + // '[' Expression? (',' Expression?)* ']' + Expect(i::Token::LBRACK, CHECK_OK); + while (peek() != i::Token::RBRACK) { + if (peek() != i::Token::COMMA) { + ParseAssignmentExpression(true, CHECK_OK); + } + if (peek() != i::Token::RBRACK) { + Expect(i::Token::COMMA, CHECK_OK); + } + } + Expect(i::Token::RBRACK, CHECK_OK); + + scope_->NextMaterializedLiteralIndex(); + return kUnknownExpression; +} + + +Expression PreParser::ParseObjectLiteral(bool* ok) { + // ObjectLiteral :: + // '{' ( + // ((IdentifierName | String | Number) ':' AssignmentExpression) + // | (('get' | 'set') (IdentifierName | String | Number) FunctionLiteral) + // )*[','] '}' + + Expect(i::Token::LBRACE, CHECK_OK); + while (peek() != i::Token::RBRACE) { + i::Token::Value next = peek(); + switch (next) { + case i::Token::IDENTIFIER: { + bool is_getter = false; + bool is_setter = false; + ParseIdentifierOrGetOrSet(&is_getter, &is_setter, CHECK_OK); + if ((is_getter || is_setter) && peek() != i::Token::COLON) { + i::Token::Value name = Next(); + if (name != i::Token::IDENTIFIER && + name != i::Token::NUMBER && + name != i::Token::STRING && + !i::Token::IsKeyword(name)) { + *ok = false; + return kUnknownExpression; + } + ParseFunctionLiteral(CHECK_OK); + if (peek() != i::Token::RBRACE) { + Expect(i::Token::COMMA, CHECK_OK); + } + continue; // restart the while + } + break; + } + case i::Token::STRING: + Consume(next); + GetStringSymbol(); + break; + case i::Token::NUMBER: + Consume(next); + break; + default: + if (i::Token::IsKeyword(next)) { + Consume(next); + } else { + // Unexpected token. + *ok = false; + return kUnknownExpression; + } + } + + Expect(i::Token::COLON, CHECK_OK); + ParseAssignmentExpression(true, CHECK_OK); + + // TODO(1240767): Consider allowing trailing comma. + if (peek() != i::Token::RBRACE) Expect(i::Token::COMMA, CHECK_OK); + } + Expect(i::Token::RBRACE, CHECK_OK); + + scope_->NextMaterializedLiteralIndex(); + return kUnknownExpression; +} + + +Expression PreParser::ParseRegExpLiteral(bool seen_equal, + bool* ok) { + if (!scanner_->ScanRegExpPattern(seen_equal)) { + Next(); + i::JavaScriptScanner::Location location = scanner_->location(); + ReportMessageAt(location.beg_pos, location.end_pos, + "unterminated_regexp", NULL); + *ok = false; + return kUnknownExpression; + } + + scope_->NextMaterializedLiteralIndex(); + + if (!scanner_->ScanRegExpFlags()) { + Next(); + i::JavaScriptScanner::Location location = scanner_->location(); + ReportMessageAt(location.beg_pos, location.end_pos, + "invalid_regexp_flags", NULL); + *ok = false; + return kUnknownExpression; + } + Next(); + return kUnknownExpression; +} + + +Arguments PreParser::ParseArguments(bool* ok) { + // Arguments :: + // '(' (AssignmentExpression)*[','] ')' + + Expect(i::Token::LPAREN, CHECK_OK); + bool done = (peek() == i::Token::RPAREN); + int argc = 0; + while (!done) { + ParseAssignmentExpression(true, CHECK_OK); + argc++; + done = (peek() == i::Token::RPAREN); + if (!done) Expect(i::Token::COMMA, CHECK_OK); + } + Expect(i::Token::RPAREN, CHECK_OK); + return argc; +} + + +Expression PreParser::ParseFunctionLiteral(bool* ok) { + // Function :: + // '(' FormalParameterList? ')' '{' FunctionBody '}' + + // Parse function body. + ScopeType outer_scope_type = scope_->type(); + bool inside_with = scope_->IsInsideWith(); + Scope function_scope(&scope_, kFunctionScope); + + // FormalParameterList :: + // '(' (Identifier)*[','] ')' + Expect(i::Token::LPAREN, CHECK_OK); + bool done = (peek() == i::Token::RPAREN); + while (!done) { + ParseIdentifier(CHECK_OK); + done = (peek() == i::Token::RPAREN); + if (!done) { + Expect(i::Token::COMMA, CHECK_OK); + } + } + Expect(i::Token::RPAREN, CHECK_OK); + + Expect(i::Token::LBRACE, CHECK_OK); + int function_block_pos = scanner_->location().beg_pos; + + // Determine if the function will be lazily compiled. + // Currently only happens to top-level functions. + // Optimistically assume that all top-level functions are lazily compiled. + bool is_lazily_compiled = + (outer_scope_type == kTopLevelScope && !inside_with && allow_lazy_); + + if (is_lazily_compiled) { + log_->PauseRecording(); + ParseSourceElements(i::Token::RBRACE, ok); + log_->ResumeRecording(); + if (!*ok) return kUnknownExpression; + + Expect(i::Token::RBRACE, CHECK_OK); + + int end_pos = scanner_->location().end_pos; + log_->LogFunction(function_block_pos, end_pos, + function_scope.materialized_literal_count(), + function_scope.expected_properties()); + } else { + ParseSourceElements(i::Token::RBRACE, CHECK_OK); + Expect(i::Token::RBRACE, CHECK_OK); + } + return kUnknownExpression; +} + + +Expression PreParser::ParseV8Intrinsic(bool* ok) { + // CallRuntime :: + // '%' Identifier Arguments + + Expect(i::Token::MOD, CHECK_OK); + ParseIdentifier(CHECK_OK); + ParseArguments(CHECK_OK); + + return kUnknownExpression; +} + + +void PreParser::ExpectSemicolon(bool* ok) { + // Check for automatic semicolon insertion according to + // the rules given in ECMA-262, section 7.9, page 21. + i::Token::Value tok = peek(); + if (tok == i::Token::SEMICOLON) { + Next(); + return; + } + if (scanner_->has_line_terminator_before_next() || + tok == i::Token::RBRACE || + tok == i::Token::EOS) { + return; + } + Expect(i::Token::SEMICOLON, ok); +} + + +Identifier PreParser::GetIdentifierSymbol() { + const char* literal_chars = scanner_->literal_string(); + int literal_length = scanner_->literal_length(); + int identifier_pos = scanner_->location().beg_pos; + + log_->LogSymbol(identifier_pos, literal_chars, literal_length); + + return kUnknownExpression; +} + + +Expression PreParser::GetStringSymbol() { + const char* literal_chars = scanner_->literal_string(); + int literal_length = scanner_->literal_length(); + + int literal_position = scanner_->location().beg_pos; + log_->LogSymbol(literal_position, literal_chars, literal_length); + + return kUnknownExpression; +} + + +Identifier PreParser::ParseIdentifier(bool* ok) { + Expect(i::Token::IDENTIFIER, ok); + if (!*ok) return kUnknownIdentifier; + return GetIdentifierSymbol(); +} + + +Identifier PreParser::ParseIdentifierName(bool* ok) { + i::Token::Value next = Next(); + if (i::Token::IsKeyword(next)) { + int pos = scanner_->location().beg_pos; + const char* keyword = i::Token::String(next); + log_->LogSymbol(pos, keyword, i::StrLength(keyword)); + return kUnknownExpression; + } + if (next == i::Token::IDENTIFIER) { + return GetIdentifierSymbol(); + } + *ok = false; + return kUnknownIdentifier; +} + + +// This function reads an identifier and determines whether or not it +// is 'get' or 'set'. The reason for not using ParseIdentifier and +// checking on the output is that this involves heap allocation which +// we can't do during preparsing. +Identifier PreParser::ParseIdentifierOrGetOrSet(bool* is_get, + bool* is_set, + bool* ok) { + Expect(i::Token::IDENTIFIER, CHECK_OK); + if (scanner_->literal_length() == 3) { + const char* token = scanner_->literal_string(); + *is_get = strncmp(token, "get", 3) == 0; + *is_set = !*is_get && strncmp(token, "set", 3) == 0; + } + return GetIdentifierSymbol(); +} + +#undef CHECK_OK +} } // v8::preparser diff --git a/src/preparser.h b/src/preparser.h index 8e8dfbe..b783d65 100644 --- a/src/preparser.h +++ b/src/preparser.h @@ -28,15 +28,12 @@ #ifndef V8_PREPARSER_H #define V8_PREPARSER_H -#include "unicode.h" -#include "utils.h" - namespace v8 { namespace preparser { // Preparsing checks a JavaScript program and emits preparse-data that helps // a later parsing to be faster. -// See preparser-data.h for the data. +// See preparse-data.h for the data. // The PreParser checks that the syntax follows the grammar for JavaScript, // and collects some information about the program along the way. @@ -76,7 +73,6 @@ typedef int Identifier; typedef int Arguments; -template class PreParser { public: PreParser() : scope_(NULL), allow_lazy_(true) { } @@ -86,8 +82,8 @@ class PreParser { // success (even if parsing failed, the pre-parse data successfully // captured the syntax error), and false if a stack-overflow happened // during parsing. - bool PreParseProgram(Scanner* scanner, - PreParserLog* log, + bool PreParseProgram(i::JavaScriptScanner* scanner, + i::ParserRecorder* log, bool allow_lazy) { allow_lazy_ = allow_lazy; scanner_ = scanner; @@ -235,1185 +231,11 @@ class PreParser { static int Precedence(i::Token::Value tok, bool accept_IN); - Scanner* scanner_; - PreParserLog* log_; + i::JavaScriptScanner* scanner_; + i::ParserRecorder* log_; Scope* scope_; bool allow_lazy_; }; - - -#define CHECK_OK ok); \ - if (!*ok) return -1; \ - ((void)0 -#define DUMMY ) // to make indentation work -#undef DUMMY - - -template -void PreParser::ReportUnexpectedToken(i::Token::Value token) { - // We don't report stack overflows here, to avoid increasing the - // stack depth even further. Instead we report it after parsing is - // over, in ParseProgram. - if (token == i::Token::ILLEGAL && scanner_->stack_overflow()) { - return; - } - typename Scanner::Location source_location = scanner_->location(); - - // Four of the tokens are treated specially - switch (token) { - case i::Token::EOS: - return ReportMessageAt(source_location.beg_pos, source_location.end_pos, - "unexpected_eos", NULL); - case i::Token::NUMBER: - return ReportMessageAt(source_location.beg_pos, source_location.end_pos, - "unexpected_token_number", NULL); - case i::Token::STRING: - return ReportMessageAt(source_location.beg_pos, source_location.end_pos, - "unexpected_token_string", NULL); - case i::Token::IDENTIFIER: - return ReportMessageAt(source_location.beg_pos, source_location.end_pos, - "unexpected_token_identifier", NULL); - default: - const char* name = i::Token::String(token); - ReportMessageAt(source_location.beg_pos, source_location.end_pos, - "unexpected_token", name); - } -} - - -template -SourceElements PreParser::ParseSourceElements(int end_token, - bool* ok) { - // SourceElements :: - // (Statement)* - - while (peek() != end_token) { - ParseStatement(CHECK_OK); - } - return kUnknownSourceElements; -} - - -template -Statement PreParser::ParseStatement(bool* ok) { - // Statement :: - // Block - // VariableStatement - // EmptyStatement - // ExpressionStatement - // IfStatement - // IterationStatement - // ContinueStatement - // BreakStatement - // ReturnStatement - // WithStatement - // LabelledStatement - // SwitchStatement - // ThrowStatement - // TryStatement - // DebuggerStatement - - // Note: Since labels can only be used by 'break' and 'continue' - // statements, which themselves are only valid within blocks, - // iterations or 'switch' statements (i.e., BreakableStatements), - // labels can be simply ignored in all other cases; except for - // trivial labeled break statements 'label: break label' which is - // parsed into an empty statement. - - // Keep the source position of the statement - switch (peek()) { - case i::Token::LBRACE: - return ParseBlock(ok); - - case i::Token::CONST: - case i::Token::VAR: - return ParseVariableStatement(ok); - - case i::Token::SEMICOLON: - Next(); - return kUnknownStatement; - - case i::Token::IF: - return ParseIfStatement(ok); - - case i::Token::DO: - return ParseDoWhileStatement(ok); - - case i::Token::WHILE: - return ParseWhileStatement(ok); - - case i::Token::FOR: - return ParseForStatement(ok); - - case i::Token::CONTINUE: - return ParseContinueStatement(ok); - - case i::Token::BREAK: - return ParseBreakStatement(ok); - - case i::Token::RETURN: - return ParseReturnStatement(ok); - - case i::Token::WITH: - return ParseWithStatement(ok); - - case i::Token::SWITCH: - return ParseSwitchStatement(ok); - - case i::Token::THROW: - return ParseThrowStatement(ok); - - case i::Token::TRY: - return ParseTryStatement(ok); - - case i::Token::FUNCTION: - return ParseFunctionDeclaration(ok); - - case i::Token::NATIVE: - return ParseNativeDeclaration(ok); - - case i::Token::DEBUGGER: - return ParseDebuggerStatement(ok); - - default: - return ParseExpressionOrLabelledStatement(ok); - } -} - - -template -Statement PreParser::ParseFunctionDeclaration(bool* ok) { - // FunctionDeclaration :: - // 'function' Identifier '(' FormalParameterListopt ')' '{' FunctionBody '}' - Expect(i::Token::FUNCTION, CHECK_OK); - ParseIdentifier(CHECK_OK); - ParseFunctionLiteral(CHECK_OK); - return kUnknownStatement; -} - - -// Language extension which is only enabled for source files loaded -// through the API's extension mechanism. A native function -// declaration is resolved by looking up the function through a -// callback provided by the extension. -template -Statement PreParser::ParseNativeDeclaration(bool* ok) { - Expect(i::Token::NATIVE, CHECK_OK); - Expect(i::Token::FUNCTION, CHECK_OK); - ParseIdentifier(CHECK_OK); - Expect(i::Token::LPAREN, CHECK_OK); - bool done = (peek() == i::Token::RPAREN); - while (!done) { - ParseIdentifier(CHECK_OK); - done = (peek() == i::Token::RPAREN); - if (!done) { - Expect(i::Token::COMMA, CHECK_OK); - } - } - Expect(i::Token::RPAREN, CHECK_OK); - Expect(i::Token::SEMICOLON, CHECK_OK); - return kUnknownStatement; -} - - -template -Statement PreParser::ParseBlock(bool* ok) { - // Block :: - // '{' Statement* '}' - - // Note that a Block does not introduce a new execution scope! - // (ECMA-262, 3rd, 12.2) - // - Expect(i::Token::LBRACE, CHECK_OK); - while (peek() != i::Token::RBRACE) { - ParseStatement(CHECK_OK); - } - Expect(i::Token::RBRACE, CHECK_OK); - return kUnknownStatement; -} - - -template -Statement PreParser::ParseVariableStatement(bool* ok) { - // VariableStatement :: - // VariableDeclarations ';' - - Statement result = ParseVariableDeclarations(true, NULL, CHECK_OK); - ExpectSemicolon(CHECK_OK); - return result; -} - - -// If the variable declaration declares exactly one non-const -// variable, then *var is set to that variable. In all other cases, -// *var is untouched; in particular, it is the caller's responsibility -// to initialize it properly. This mechanism is also used for the parsing -// of 'for-in' loops. -template -Statement PreParser::ParseVariableDeclarations(bool accept_IN, - int* num_decl, - bool* ok) { - // VariableDeclarations :: - // ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[','] - - if (peek() == i::Token::VAR) { - Consume(i::Token::VAR); - } else if (peek() == i::Token::CONST) { - Consume(i::Token::CONST); - } else { - *ok = false; - return 0; - } - - // The scope of a variable/const declared anywhere inside a function - // is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). . - int nvars = 0; // the number of variables declared - do { - // Parse variable name. - if (nvars > 0) Consume(i::Token::COMMA); - ParseIdentifier(CHECK_OK); - nvars++; - if (peek() == i::Token::ASSIGN) { - Expect(i::Token::ASSIGN, CHECK_OK); - ParseAssignmentExpression(accept_IN, CHECK_OK); - } - } while (peek() == i::Token::COMMA); - - if (num_decl != NULL) *num_decl = nvars; - return kUnknownStatement; -} - - -template -Statement PreParser::ParseExpressionOrLabelledStatement( - bool* ok) { - // ExpressionStatement | LabelledStatement :: - // Expression ';' - // Identifier ':' Statement - - Expression expr = ParseExpression(true, CHECK_OK); - if (peek() == i::Token::COLON && expr == kIdentifierExpression) { - Consume(i::Token::COLON); - return ParseStatement(ok); - } - // Parsed expression statement. - ExpectSemicolon(CHECK_OK); - return kUnknownStatement; -} - - -template -Statement PreParser::ParseIfStatement(bool* ok) { - // IfStatement :: - // 'if' '(' Expression ')' Statement ('else' Statement)? - - Expect(i::Token::IF, CHECK_OK); - Expect(i::Token::LPAREN, CHECK_OK); - ParseExpression(true, CHECK_OK); - Expect(i::Token::RPAREN, CHECK_OK); - ParseStatement(CHECK_OK); - if (peek() == i::Token::ELSE) { - Next(); - ParseStatement(CHECK_OK); - } - return kUnknownStatement; -} - - -template -Statement PreParser::ParseContinueStatement(bool* ok) { - // ContinueStatement :: - // 'continue' [no line terminator] Identifier? ';' - - Expect(i::Token::CONTINUE, CHECK_OK); - i::Token::Value tok = peek(); - if (!scanner_->has_line_terminator_before_next() && - tok != i::Token::SEMICOLON && - tok != i::Token::RBRACE && - tok != i::Token::EOS) { - ParseIdentifier(CHECK_OK); - } - ExpectSemicolon(CHECK_OK); - return kUnknownStatement; -} - - -template -Statement PreParser::ParseBreakStatement(bool* ok) { - // BreakStatement :: - // 'break' [no line terminator] Identifier? ';' - - Expect(i::Token::BREAK, CHECK_OK); - i::Token::Value tok = peek(); - if (!scanner_->has_line_terminator_before_next() && - tok != i::Token::SEMICOLON && - tok != i::Token::RBRACE && - tok != i::Token::EOS) { - ParseIdentifier(CHECK_OK); - } - ExpectSemicolon(CHECK_OK); - return kUnknownStatement; -} - - -template -Statement PreParser::ParseReturnStatement(bool* ok) { - // ReturnStatement :: - // 'return' [no line terminator] Expression? ';' - - // Consume the return token. It is necessary to do the before - // reporting any errors on it, because of the way errors are - // reported (underlining). - Expect(i::Token::RETURN, CHECK_OK); - - // An ECMAScript program is considered syntactically incorrect if it - // contains a return statement that is not within the body of a - // function. See ECMA-262, section 12.9, page 67. - // This is not handled during preparsing. - - i::Token::Value tok = peek(); - if (!scanner_->has_line_terminator_before_next() && - tok != i::Token::SEMICOLON && - tok != i::Token::RBRACE && - tok != i::Token::EOS) { - ParseExpression(true, CHECK_OK); - } - ExpectSemicolon(CHECK_OK); - return kUnknownStatement; -} - - -template -Statement PreParser::ParseWithStatement(bool* ok) { - // WithStatement :: - // 'with' '(' Expression ')' Statement - Expect(i::Token::WITH, CHECK_OK); - Expect(i::Token::LPAREN, CHECK_OK); - ParseExpression(true, CHECK_OK); - Expect(i::Token::RPAREN, CHECK_OK); - - scope_->EnterWith(); - ParseStatement(CHECK_OK); - scope_->LeaveWith(); - return kUnknownStatement; -} - - -template -Statement PreParser::ParseSwitchStatement(bool* ok) { - // SwitchStatement :: - // 'switch' '(' Expression ')' '{' CaseClause* '}' - - Expect(i::Token::SWITCH, CHECK_OK); - Expect(i::Token::LPAREN, CHECK_OK); - ParseExpression(true, CHECK_OK); - Expect(i::Token::RPAREN, CHECK_OK); - - Expect(i::Token::LBRACE, CHECK_OK); - i::Token::Value token = peek(); - while (token != i::Token::RBRACE) { - if (token == i::Token::CASE) { - Expect(i::Token::CASE, CHECK_OK); - ParseExpression(true, CHECK_OK); - Expect(i::Token::COLON, CHECK_OK); - } else if (token == i::Token::DEFAULT) { - Expect(i::Token::DEFAULT, CHECK_OK); - Expect(i::Token::COLON, CHECK_OK); - } else { - ParseStatement(CHECK_OK); - } - token = peek(); - } - Expect(i::Token::RBRACE, CHECK_OK); - - return kUnknownStatement; -} - - -template -Statement PreParser::ParseDoWhileStatement(bool* ok) { - // DoStatement :: - // 'do' Statement 'while' '(' Expression ')' ';' - - Expect(i::Token::DO, CHECK_OK); - ParseStatement(CHECK_OK); - Expect(i::Token::WHILE, CHECK_OK); - Expect(i::Token::LPAREN, CHECK_OK); - ParseExpression(true, CHECK_OK); - Expect(i::Token::RPAREN, CHECK_OK); - return kUnknownStatement; -} - - -template -Statement PreParser::ParseWhileStatement(bool* ok) { - // WhileStatement :: - // 'while' '(' Expression ')' Statement - - Expect(i::Token::WHILE, CHECK_OK); - Expect(i::Token::LPAREN, CHECK_OK); - ParseExpression(true, CHECK_OK); - Expect(i::Token::RPAREN, CHECK_OK); - ParseStatement(CHECK_OK); - return kUnknownStatement; -} - - -template -Statement PreParser::ParseForStatement(bool* ok) { - // ForStatement :: - // 'for' '(' Expression? ';' Expression? ';' Expression? ')' Statement - - Expect(i::Token::FOR, CHECK_OK); - Expect(i::Token::LPAREN, CHECK_OK); - if (peek() != i::Token::SEMICOLON) { - if (peek() == i::Token::VAR || peek() == i::Token::CONST) { - int decl_count; - ParseVariableDeclarations(false, &decl_count, CHECK_OK); - if (peek() == i::Token::IN && decl_count == 1) { - Expect(i::Token::IN, CHECK_OK); - ParseExpression(true, CHECK_OK); - Expect(i::Token::RPAREN, CHECK_OK); - - ParseStatement(CHECK_OK); - return kUnknownStatement; - } - } else { - ParseExpression(false, CHECK_OK); - if (peek() == i::Token::IN) { - Expect(i::Token::IN, CHECK_OK); - ParseExpression(true, CHECK_OK); - Expect(i::Token::RPAREN, CHECK_OK); - - ParseStatement(CHECK_OK); - return kUnknownStatement; - } - } - } - - // Parsed initializer at this point. - Expect(i::Token::SEMICOLON, CHECK_OK); - - if (peek() != i::Token::SEMICOLON) { - ParseExpression(true, CHECK_OK); - } - Expect(i::Token::SEMICOLON, CHECK_OK); - - if (peek() != i::Token::RPAREN) { - ParseExpression(true, CHECK_OK); - } - Expect(i::Token::RPAREN, CHECK_OK); - - ParseStatement(CHECK_OK); - return kUnknownStatement; -} - - -template -Statement PreParser::ParseThrowStatement(bool* ok) { - // ThrowStatement :: - // 'throw' [no line terminator] Expression ';' - - Expect(i::Token::THROW, CHECK_OK); - if (scanner_->has_line_terminator_before_next()) { - typename Scanner::Location pos = scanner_->location(); - ReportMessageAt(pos.beg_pos, pos.end_pos, - "newline_after_throw", NULL); - *ok = false; - return kUnknownStatement; - } - ParseExpression(true, CHECK_OK); - ExpectSemicolon(CHECK_OK); - - return kUnknownStatement; -} - - -template -Statement PreParser::ParseTryStatement(bool* ok) { - // TryStatement :: - // 'try' Block Catch - // 'try' Block Finally - // 'try' Block Catch Finally - // - // Catch :: - // 'catch' '(' Identifier ')' Block - // - // Finally :: - // 'finally' Block - - // In preparsing, allow any number of catch/finally blocks, including zero - // of both. - - Expect(i::Token::TRY, CHECK_OK); - - ParseBlock(CHECK_OK); - - bool catch_or_finally_seen = false; - if (peek() == i::Token::CATCH) { - Consume(i::Token::CATCH); - Expect(i::Token::LPAREN, CHECK_OK); - ParseIdentifier(CHECK_OK); - Expect(i::Token::RPAREN, CHECK_OK); - scope_->EnterWith(); - ParseBlock(ok); - scope_->LeaveWith(); - if (!*ok) return kUnknownStatement; - catch_or_finally_seen = true; - } - if (peek() == i::Token::FINALLY) { - Consume(i::Token::FINALLY); - ParseBlock(CHECK_OK); - catch_or_finally_seen = true; - } - if (!catch_or_finally_seen) { - *ok = false; - } - return kUnknownStatement; -} - - -template -Statement PreParser::ParseDebuggerStatement(bool* ok) { - // In ECMA-262 'debugger' is defined as a reserved keyword. In some browser - // contexts this is used as a statement which invokes the debugger as if a - // break point is present. - // DebuggerStatement :: - // 'debugger' ';' - - Expect(i::Token::DEBUGGER, CHECK_OK); - ExpectSemicolon(CHECK_OK); - return kUnknownStatement; -} - - -// Precedence = 1 -template -Expression PreParser::ParseExpression(bool accept_IN, bool* ok) { - // Expression :: - // AssignmentExpression - // Expression ',' AssignmentExpression - - Expression result = ParseAssignmentExpression(accept_IN, CHECK_OK); - while (peek() == i::Token::COMMA) { - Expect(i::Token::COMMA, CHECK_OK); - ParseAssignmentExpression(accept_IN, CHECK_OK); - result = kUnknownExpression; - } - return result; -} - - -// Precedence = 2 -template -Expression PreParser::ParseAssignmentExpression(bool accept_IN, - bool* ok) { - // AssignmentExpression :: - // ConditionalExpression - // LeftHandSideExpression AssignmentOperator AssignmentExpression - - Expression expression = ParseConditionalExpression(accept_IN, CHECK_OK); - - if (!i::Token::IsAssignmentOp(peek())) { - // Parsed conditional expression only (no assignment). - return expression; - } - - i::Token::Value op = Next(); // Get assignment operator. - ParseAssignmentExpression(accept_IN, CHECK_OK); - - if ((op == i::Token::ASSIGN) && (expression == kThisPropertyExpression)) { - scope_->AddProperty(); - } - - return kUnknownExpression; -} - - -// Precedence = 3 -template -Expression PreParser::ParseConditionalExpression(bool accept_IN, - bool* ok) { - // ConditionalExpression :: - // LogicalOrExpression - // LogicalOrExpression '?' AssignmentExpression ':' AssignmentExpression - - // We start using the binary expression parser for prec >= 4 only! - Expression expression = ParseBinaryExpression(4, accept_IN, CHECK_OK); - if (peek() != i::Token::CONDITIONAL) return expression; - Consume(i::Token::CONDITIONAL); - // In parsing the first assignment expression in conditional - // expressions we always accept the 'in' keyword; see ECMA-262, - // section 11.12, page 58. - ParseAssignmentExpression(true, CHECK_OK); - Expect(i::Token::COLON, CHECK_OK); - ParseAssignmentExpression(accept_IN, CHECK_OK); - return kUnknownExpression; -} - - -template -int PreParser::Precedence(i::Token::Value tok, bool accept_IN) { - if (tok == i::Token::IN && !accept_IN) - return 0; // 0 precedence will terminate binary expression parsing - - return i::Token::Precedence(tok); -} - - -// Precedence >= 4 -template -Expression PreParser::ParseBinaryExpression(int prec, - bool accept_IN, - bool* ok) { - Expression result = ParseUnaryExpression(CHECK_OK); - for (int prec1 = Precedence(peek(), accept_IN); prec1 >= prec; prec1--) { - // prec1 >= 4 - while (Precedence(peek(), accept_IN) == prec1) { - Next(); - ParseBinaryExpression(prec1 + 1, accept_IN, CHECK_OK); - result = kUnknownExpression; - } - } - return result; -} - - -template -Expression PreParser::ParseUnaryExpression(bool* ok) { - // UnaryExpression :: - // PostfixExpression - // 'delete' UnaryExpression - // 'void' UnaryExpression - // 'typeof' UnaryExpression - // '++' UnaryExpression - // '--' UnaryExpression - // '+' UnaryExpression - // '-' UnaryExpression - // '~' UnaryExpression - // '!' UnaryExpression - - i::Token::Value op = peek(); - if (i::Token::IsUnaryOp(op) || i::Token::IsCountOp(op)) { - op = Next(); - ParseUnaryExpression(ok); - return kUnknownExpression; - } else { - return ParsePostfixExpression(ok); - } -} - - -template -Expression PreParser::ParsePostfixExpression(bool* ok) { - // PostfixExpression :: - // LeftHandSideExpression ('++' | '--')? - - Expression expression = ParseLeftHandSideExpression(CHECK_OK); - if (!scanner_->has_line_terminator_before_next() && - i::Token::IsCountOp(peek())) { - Next(); - return kUnknownExpression; - } - return expression; -} - - -template -Expression PreParser::ParseLeftHandSideExpression(bool* ok) { - // LeftHandSideExpression :: - // (NewExpression | MemberExpression) ... - - Expression result; - if (peek() == i::Token::NEW) { - result = ParseNewExpression(CHECK_OK); - } else { - result = ParseMemberExpression(CHECK_OK); - } - - while (true) { - switch (peek()) { - case i::Token::LBRACK: { - Consume(i::Token::LBRACK); - ParseExpression(true, CHECK_OK); - Expect(i::Token::RBRACK, CHECK_OK); - if (result == kThisExpression) { - result = kThisPropertyExpression; - } else { - result = kUnknownExpression; - } - break; - } - - case i::Token::LPAREN: { - ParseArguments(CHECK_OK); - result = kUnknownExpression; - break; - } - - case i::Token::PERIOD: { - Consume(i::Token::PERIOD); - ParseIdentifierName(CHECK_OK); - if (result == kThisExpression) { - result = kThisPropertyExpression; - } else { - result = kUnknownExpression; - } - break; - } - - default: - return result; - } - } -} - - -template -Expression PreParser::ParseNewExpression(bool* ok) { - // NewExpression :: - // ('new')+ MemberExpression - - // The grammar for new expressions is pretty warped. The keyword - // 'new' can either be a part of the new expression (where it isn't - // followed by an argument list) or a part of the member expression, - // where it must be followed by an argument list. To accommodate - // this, we parse the 'new' keywords greedily and keep track of how - // many we have parsed. This information is then passed on to the - // member expression parser, which is only allowed to match argument - // lists as long as it has 'new' prefixes left - unsigned new_count = 0; - do { - Consume(i::Token::NEW); - new_count++; - } while (peek() == i::Token::NEW); - - return ParseMemberWithNewPrefixesExpression(new_count, ok); -} - - -template -Expression PreParser::ParseMemberExpression(bool* ok) { - return ParseMemberWithNewPrefixesExpression(0, ok); -} - - -template -Expression PreParser::ParseMemberWithNewPrefixesExpression( - unsigned new_count, bool* ok) { - // MemberExpression :: - // (PrimaryExpression | FunctionLiteral) - // ('[' Expression ']' | '.' Identifier | Arguments)* - - // Parse the initial primary or function expression. - Expression result = kUnknownExpression; - if (peek() == i::Token::FUNCTION) { - Consume(i::Token::FUNCTION); - if (peek() == i::Token::IDENTIFIER) { - ParseIdentifier(CHECK_OK); - } - result = ParseFunctionLiteral(CHECK_OK); - } else { - result = ParsePrimaryExpression(CHECK_OK); - } - - while (true) { - switch (peek()) { - case i::Token::LBRACK: { - Consume(i::Token::LBRACK); - ParseExpression(true, CHECK_OK); - Expect(i::Token::RBRACK, CHECK_OK); - if (result == kThisExpression) { - result = kThisPropertyExpression; - } else { - result = kUnknownExpression; - } - break; - } - case i::Token::PERIOD: { - Consume(i::Token::PERIOD); - ParseIdentifierName(CHECK_OK); - if (result == kThisExpression) { - result = kThisPropertyExpression; - } else { - result = kUnknownExpression; - } - break; - } - case i::Token::LPAREN: { - if (new_count == 0) return result; - // Consume one of the new prefixes (already parsed). - ParseArguments(CHECK_OK); - new_count--; - result = kUnknownExpression; - break; - } - default: - return result; - } - } -} - - -template -Expression PreParser::ParsePrimaryExpression(bool* ok) { - // PrimaryExpression :: - // 'this' - // 'null' - // 'true' - // 'false' - // Identifier - // Number - // String - // ArrayLiteral - // ObjectLiteral - // RegExpLiteral - // '(' Expression ')' - - Expression result = kUnknownExpression; - switch (peek()) { - case i::Token::THIS: { - Next(); - result = kThisExpression; - break; - } - - case i::Token::IDENTIFIER: { - ParseIdentifier(CHECK_OK); - result = kIdentifierExpression; - break; - } - - case i::Token::NULL_LITERAL: - case i::Token::TRUE_LITERAL: - case i::Token::FALSE_LITERAL: - case i::Token::NUMBER: { - Next(); - break; - } - case i::Token::STRING: { - Next(); - result = GetStringSymbol(); - break; - } - - case i::Token::ASSIGN_DIV: - result = ParseRegExpLiteral(true, CHECK_OK); - break; - - case i::Token::DIV: - result = ParseRegExpLiteral(false, CHECK_OK); - break; - - case i::Token::LBRACK: - result = ParseArrayLiteral(CHECK_OK); - break; - - case i::Token::LBRACE: - result = ParseObjectLiteral(CHECK_OK); - break; - - case i::Token::LPAREN: - Consume(i::Token::LPAREN); - result = ParseExpression(true, CHECK_OK); - Expect(i::Token::RPAREN, CHECK_OK); - if (result == kIdentifierExpression) result = kUnknownExpression; - break; - - case i::Token::MOD: - result = ParseV8Intrinsic(CHECK_OK); - break; - - default: { - Next(); - *ok = false; - return kUnknownExpression; - } - } - - return result; -} - - -template -Expression PreParser::ParseArrayLiteral(bool* ok) { - // ArrayLiteral :: - // '[' Expression? (',' Expression?)* ']' - Expect(i::Token::LBRACK, CHECK_OK); - while (peek() != i::Token::RBRACK) { - if (peek() != i::Token::COMMA) { - ParseAssignmentExpression(true, CHECK_OK); - } - if (peek() != i::Token::RBRACK) { - Expect(i::Token::COMMA, CHECK_OK); - } - } - Expect(i::Token::RBRACK, CHECK_OK); - - scope_->NextMaterializedLiteralIndex(); - return kUnknownExpression; -} - - -template -Expression PreParser::ParseObjectLiteral(bool* ok) { - // ObjectLiteral :: - // '{' ( - // ((IdentifierName | String | Number) ':' AssignmentExpression) - // | (('get' | 'set') (IdentifierName | String | Number) FunctionLiteral) - // )*[','] '}' - - Expect(i::Token::LBRACE, CHECK_OK); - while (peek() != i::Token::RBRACE) { - i::Token::Value next = peek(); - switch (next) { - case i::Token::IDENTIFIER: { - bool is_getter = false; - bool is_setter = false; - ParseIdentifierOrGetOrSet(&is_getter, &is_setter, CHECK_OK); - if ((is_getter || is_setter) && peek() != i::Token::COLON) { - i::Token::Value name = Next(); - if (name != i::Token::IDENTIFIER && - name != i::Token::NUMBER && - name != i::Token::STRING && - !i::Token::IsKeyword(name)) { - *ok = false; - return kUnknownExpression; - } - ParseFunctionLiteral(CHECK_OK); - if (peek() != i::Token::RBRACE) { - Expect(i::Token::COMMA, CHECK_OK); - } - continue; // restart the while - } - break; - } - case i::Token::STRING: - Consume(next); - GetStringSymbol(); - break; - case i::Token::NUMBER: - Consume(next); - break; - default: - if (i::Token::IsKeyword(next)) { - Consume(next); - } else { - // Unexpected token. - *ok = false; - return kUnknownExpression; - } - } - - Expect(i::Token::COLON, CHECK_OK); - ParseAssignmentExpression(true, CHECK_OK); - - // TODO(1240767): Consider allowing trailing comma. - if (peek() != i::Token::RBRACE) Expect(i::Token::COMMA, CHECK_OK); - } - Expect(i::Token::RBRACE, CHECK_OK); - - scope_->NextMaterializedLiteralIndex(); - return kUnknownExpression; -} - - -template -Expression PreParser::ParseRegExpLiteral(bool seen_equal, - bool* ok) { - if (!scanner_->ScanRegExpPattern(seen_equal)) { - Next(); - typename Scanner::Location location = scanner_->location(); - ReportMessageAt(location.beg_pos, location.end_pos, - "unterminated_regexp", NULL); - *ok = false; - return kUnknownExpression; - } - - scope_->NextMaterializedLiteralIndex(); - - if (!scanner_->ScanRegExpFlags()) { - Next(); - typename Scanner::Location location = scanner_->location(); - ReportMessageAt(location.beg_pos, location.end_pos, - "invalid_regexp_flags", NULL); - *ok = false; - return kUnknownExpression; - } - Next(); - return kUnknownExpression; -} - - -template -Arguments PreParser::ParseArguments(bool* ok) { - // Arguments :: - // '(' (AssignmentExpression)*[','] ')' - - Expect(i::Token::LPAREN, CHECK_OK); - bool done = (peek() == i::Token::RPAREN); - int argc = 0; - while (!done) { - ParseAssignmentExpression(true, CHECK_OK); - argc++; - done = (peek() == i::Token::RPAREN); - if (!done) Expect(i::Token::COMMA, CHECK_OK); - } - Expect(i::Token::RPAREN, CHECK_OK); - return argc; -} - - -template -Expression PreParser::ParseFunctionLiteral(bool* ok) { - // Function :: - // '(' FormalParameterList? ')' '{' FunctionBody '}' - - // Parse function body. - ScopeType outer_scope_type = scope_->type(); - bool inside_with = scope_->IsInsideWith(); - Scope function_scope(&scope_, kFunctionScope); - - // FormalParameterList :: - // '(' (Identifier)*[','] ')' - Expect(i::Token::LPAREN, CHECK_OK); - bool done = (peek() == i::Token::RPAREN); - while (!done) { - ParseIdentifier(CHECK_OK); - done = (peek() == i::Token::RPAREN); - if (!done) { - Expect(i::Token::COMMA, CHECK_OK); - } - } - Expect(i::Token::RPAREN, CHECK_OK); - - Expect(i::Token::LBRACE, CHECK_OK); - int function_block_pos = scanner_->location().beg_pos; - - // Determine if the function will be lazily compiled. - // Currently only happens to top-level functions. - // Optimistically assume that all top-level functions are lazily compiled. - bool is_lazily_compiled = - (outer_scope_type == kTopLevelScope && !inside_with && allow_lazy_); - - if (is_lazily_compiled) { - log_->PauseRecording(); - ParseSourceElements(i::Token::RBRACE, ok); - log_->ResumeRecording(); - if (!*ok) return kUnknownExpression; - - Expect(i::Token::RBRACE, CHECK_OK); - - int end_pos = scanner_->location().end_pos; - log_->LogFunction(function_block_pos, end_pos, - function_scope.materialized_literal_count(), - function_scope.expected_properties()); - } else { - ParseSourceElements(i::Token::RBRACE, CHECK_OK); - Expect(i::Token::RBRACE, CHECK_OK); - } - return kUnknownExpression; -} - - -template -Expression PreParser::ParseV8Intrinsic(bool* ok) { - // CallRuntime :: - // '%' Identifier Arguments - - Expect(i::Token::MOD, CHECK_OK); - ParseIdentifier(CHECK_OK); - ParseArguments(CHECK_OK); - - return kUnknownExpression; -} - - -template -void PreParser::ExpectSemicolon(bool* ok) { - // Check for automatic semicolon insertion according to - // the rules given in ECMA-262, section 7.9, page 21. - i::Token::Value tok = peek(); - if (tok == i::Token::SEMICOLON) { - Next(); - return; - } - if (scanner_->has_line_terminator_before_next() || - tok == i::Token::RBRACE || - tok == i::Token::EOS) { - return; - } - Expect(i::Token::SEMICOLON, ok); -} - - -template -Identifier PreParser::GetIdentifierSymbol() { - const char* literal_chars = scanner_->literal_string(); - int literal_length = scanner_->literal_length(); - int identifier_pos = scanner_->location().beg_pos; - - log_->LogSymbol(identifier_pos, literal_chars, literal_length); - - return kUnknownExpression; -} - - -template -Expression PreParser::GetStringSymbol() { - const char* literal_chars = scanner_->literal_string(); - int literal_length = scanner_->literal_length(); - - int literal_position = scanner_->location().beg_pos; - log_->LogSymbol(literal_position, literal_chars, literal_length); - - return kUnknownExpression; -} - - -template -Identifier PreParser::ParseIdentifier(bool* ok) { - Expect(i::Token::IDENTIFIER, ok); - if (!*ok) return kUnknownIdentifier; - return GetIdentifierSymbol(); -} - - -template -Identifier PreParser::ParseIdentifierName(bool* ok) { - i::Token::Value next = Next(); - if (i::Token::IsKeyword(next)) { - int pos = scanner_->location().beg_pos; - const char* keyword = i::Token::String(next); - log_->LogSymbol(pos, keyword, i::StrLength(keyword)); - return kUnknownExpression; - } - if (next == i::Token::IDENTIFIER) { - return GetIdentifierSymbol(); - } - *ok = false; - return kUnknownIdentifier; -} - - -// This function reads an identifier and determines whether or not it -// is 'get' or 'set'. The reason for not using ParseIdentifier and -// checking on the output is that this involves heap allocation which -// we can't do during preparsing. -template -Identifier PreParser::ParseIdentifierOrGetOrSet(bool* is_get, - bool* is_set, - bool* ok) { - Expect(i::Token::IDENTIFIER, CHECK_OK); - if (scanner_->literal_length() == 3) { - const char* token = scanner_->literal_string(); - *is_get = strncmp(token, "get", 3) == 0; - *is_set = !*is_get && strncmp(token, "set", 3) == 0; - } - return GetIdentifierSymbol(); -} - -#undef CHECK_OK } } // v8::preparser #endif // V8_PREPARSER_H diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 5322314..b864634 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -8734,7 +8734,7 @@ TEST(PreCompileInvalidPreparseDataError) { v8::ScriptData::PreCompile(script, i::StrLength(script)); CHECK(!sd->HasError()); // ScriptDataImpl private implementation details - const int kHeaderSize = i::ScriptDataImpl::kHeaderSize; + const int kHeaderSize = i::PreparseDataConstants::kHeaderSize; const int kFunctionEntrySize = i::FunctionEntry::kSize; const int kFunctionEntryStartOffset = 0; const int kFunctionEntryEndOffset = 1; diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc index 243d478..a93fc27 100755 --- a/test/cctest/test-parsing.cc +++ b/test/cctest/test-parsing.cc @@ -263,8 +263,7 @@ TEST(StandAlonePreParser) { i::CompleteParserRecorder log; i::V8JavaScriptScanner scanner; scanner.Initialize(i::Handle::null(), &stream); - v8::preparser::PreParser preparser; + v8::preparser::PreParser preparser; bool result = preparser.PreParseProgram(&scanner, &log, true); CHECK(result); i::ScriptDataImpl data(log.ExtractData()); diff --git a/tools/visual_studio/v8_base.vcproj b/tools/visual_studio/v8_base.vcproj index 95eb196..5b41750 100644 --- a/tools/visual_studio/v8_base.vcproj +++ b/tools/visual_studio/v8_base.vcproj @@ -762,6 +762,22 @@ > + + + + + + + + -- 2.7.4