diff options
Diffstat (limited to 'src/3rdparty/v8/src/parser.cc')
-rw-r--r-- | src/3rdparty/v8/src/parser.cc | 5168 |
1 files changed, 5168 insertions, 0 deletions
diff --git a/src/3rdparty/v8/src/parser.cc b/src/3rdparty/v8/src/parser.cc new file mode 100644 index 0000000..22d4d3f --- /dev/null +++ b/src/3rdparty/v8/src/parser.cc @@ -0,0 +1,5168 @@ +// 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 "v8.h" + +#include "api.h" +#include "ast.h" +#include "bootstrapper.h" +#include "codegen.h" +#include "compiler.h" +#include "func-name-inferrer.h" +#include "messages.h" +#include "parser.h" +#include "platform.h" +#include "preparser.h" +#include "runtime.h" +#include "scopeinfo.h" +#include "string-stream.h" + +#include "ast-inl.h" +#include "jump-target-inl.h" + +namespace v8 { +namespace internal { + +// PositionStack is used for on-stack allocation of token positions for +// new expressions. Please look at ParseNewExpression. + +class PositionStack { + public: + explicit PositionStack(bool* ok) : top_(NULL), ok_(ok) {} + ~PositionStack() { ASSERT(!*ok_ || is_empty()); } + + class Element { + public: + Element(PositionStack* stack, int value) { + previous_ = stack->top(); + value_ = value; + stack->set_top(this); + } + + private: + Element* previous() { return previous_; } + int value() { return value_; } + friend class PositionStack; + Element* previous_; + int value_; + }; + + bool is_empty() { return top_ == NULL; } + int pop() { + ASSERT(!is_empty()); + int result = top_->value(); + top_ = top_->previous(); + return result; + } + + private: + Element* top() { return top_; } + void set_top(Element* value) { top_ = value; } + Element* top_; + bool* ok_; +}; + + +RegExpBuilder::RegExpBuilder() + : zone_(Isolate::Current()->zone()), + pending_empty_(false), + characters_(NULL), + terms_(), + alternatives_() +#ifdef DEBUG + , last_added_(ADD_NONE) +#endif + {} + + +void RegExpBuilder::FlushCharacters() { + pending_empty_ = false; + if (characters_ != NULL) { + RegExpTree* atom = new(zone()) RegExpAtom(characters_->ToConstVector()); + characters_ = NULL; + text_.Add(atom); + LAST(ADD_ATOM); + } +} + + +void RegExpBuilder::FlushText() { + FlushCharacters(); + int num_text = text_.length(); + if (num_text == 0) { + return; + } else if (num_text == 1) { + terms_.Add(text_.last()); + } else { + RegExpText* text = new(zone()) RegExpText(); + for (int i = 0; i < num_text; i++) + text_.Get(i)->AppendToText(text); + terms_.Add(text); + } + text_.Clear(); +} + + +void RegExpBuilder::AddCharacter(uc16 c) { + pending_empty_ = false; + if (characters_ == NULL) { + characters_ = new ZoneList<uc16>(4); + } + characters_->Add(c); + LAST(ADD_CHAR); +} + + +void RegExpBuilder::AddEmpty() { + pending_empty_ = true; +} + + +void RegExpBuilder::AddAtom(RegExpTree* term) { + if (term->IsEmpty()) { + AddEmpty(); + return; + } + if (term->IsTextElement()) { + FlushCharacters(); + text_.Add(term); + } else { + FlushText(); + terms_.Add(term); + } + LAST(ADD_ATOM); +} + + +void RegExpBuilder::AddAssertion(RegExpTree* assert) { + FlushText(); + terms_.Add(assert); + LAST(ADD_ASSERT); +} + + +void RegExpBuilder::NewAlternative() { + FlushTerms(); +} + + +void RegExpBuilder::FlushTerms() { + FlushText(); + int num_terms = terms_.length(); + RegExpTree* alternative; + if (num_terms == 0) { + alternative = RegExpEmpty::GetInstance(); + } else if (num_terms == 1) { + alternative = terms_.last(); + } else { + alternative = new(zone()) RegExpAlternative(terms_.GetList()); + } + alternatives_.Add(alternative); + terms_.Clear(); + LAST(ADD_NONE); +} + + +RegExpTree* RegExpBuilder::ToRegExp() { + FlushTerms(); + int num_alternatives = alternatives_.length(); + if (num_alternatives == 0) { + return RegExpEmpty::GetInstance(); + } + if (num_alternatives == 1) { + return alternatives_.last(); + } + return new(zone()) RegExpDisjunction(alternatives_.GetList()); +} + + +void RegExpBuilder::AddQuantifierToAtom(int min, + int max, + RegExpQuantifier::Type type) { + if (pending_empty_) { + pending_empty_ = false; + return; + } + RegExpTree* atom; + if (characters_ != NULL) { + ASSERT(last_added_ == ADD_CHAR); + // Last atom was character. + Vector<const uc16> char_vector = characters_->ToConstVector(); + int num_chars = char_vector.length(); + if (num_chars > 1) { + Vector<const uc16> prefix = char_vector.SubVector(0, num_chars - 1); + text_.Add(new(zone()) RegExpAtom(prefix)); + char_vector = char_vector.SubVector(num_chars - 1, num_chars); + } + characters_ = NULL; + atom = new(zone()) RegExpAtom(char_vector); + FlushText(); + } else if (text_.length() > 0) { + ASSERT(last_added_ == ADD_ATOM); + atom = text_.RemoveLast(); + FlushText(); + } else if (terms_.length() > 0) { + ASSERT(last_added_ == ADD_ATOM); + atom = terms_.RemoveLast(); + if (atom->max_match() == 0) { + // Guaranteed to only match an empty string. + LAST(ADD_TERM); + if (min == 0) { + return; + } + terms_.Add(atom); + return; + } + } else { + // Only call immediately after adding an atom or character! + UNREACHABLE(); + return; + } + terms_.Add(new(zone()) RegExpQuantifier(min, max, type, atom)); + LAST(ADD_TERM); +} + + +Handle<String> Parser::LookupSymbol(int symbol_id) { + // Length of symbol cache is the number of identified symbols. + // If we are larger than that, or negative, it's not a cached symbol. + // This might also happen if there is no preparser symbol data, even + // if there is some preparser data. + if (static_cast<unsigned>(symbol_id) + >= static_cast<unsigned>(symbol_cache_.length())) { + if (scanner().is_literal_ascii()) { + return isolate()->factory()->LookupAsciiSymbol( + scanner().literal_ascii_string()); + } else { + return isolate()->factory()->LookupTwoByteSymbol( + scanner().literal_uc16_string()); + } + } + return LookupCachedSymbol(symbol_id); +} + + +Handle<String> Parser::LookupCachedSymbol(int symbol_id) { + // Make sure the cache is large enough to hold the symbol identifier. + if (symbol_cache_.length() <= symbol_id) { + // Increase length to index + 1. + symbol_cache_.AddBlock(Handle<String>::null(), + symbol_id + 1 - symbol_cache_.length()); + } + Handle<String> result = symbol_cache_.at(symbol_id); + if (result.is_null()) { + if (scanner().is_literal_ascii()) { + result = isolate()->factory()->LookupAsciiSymbol( + scanner().literal_ascii_string()); + } else { + result = isolate()->factory()->LookupTwoByteSymbol( + scanner().literal_uc16_string()); + } + symbol_cache_.at(symbol_id) = result; + return result; + } + isolate()->counters()->total_preparse_symbols_skipped()->Increment(); + return result; +} + + +FunctionEntry ScriptDataImpl::GetFunctionEntry(int start) { + // The current pre-data entry must be a FunctionEntry with the given + // start position. + if ((function_index_ + FunctionEntry::kSize <= store_.length()) + && (static_cast<int>(store_[function_index_]) == start)) { + int index = function_index_; + function_index_ += FunctionEntry::kSize; + return FunctionEntry(store_.SubVector(index, + index + FunctionEntry::kSize)); + } + return FunctionEntry(); +} + + +int ScriptDataImpl::GetSymbolIdentifier() { + return ReadNumber(&symbol_data_); +} + + +bool ScriptDataImpl::SanityCheck() { + // Check that the header data is valid and doesn't specify + // point to positions outside the store. + 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() <= 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() <= PreparseDataConstants::kHeaderSize + pos) { + return false; + } + int length = static_cast<int>(Read(pos)); + if (length < 0) return false; + pos += 1 + length; + } + 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<int>(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<int>(store_[PreparseDataConstants::kSymbolCountOffset]); + if (symbol_count < 0) return false; + // Check that the total size has room for header and function entries. + int minimum_size = + PreparseDataConstants::kHeaderSize + functions_size; + if (store_.length() < minimum_size) return false; + return true; +} + + + +const char* ScriptDataImpl::ReadString(unsigned* start, int* chars) { + int length = start[0]; + char* result = NewArray<char>(length + 1); + for (int i = 0; i < length; i++) { + result[i] = start[i + 1]; + } + result[length] = '\0'; + if (chars != NULL) *chars = length; + return result; +} + +Scanner::Location ScriptDataImpl::MessageLocation() { + 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(PreparseDataConstants::kMessageTextPos); + return ReadString(start, NULL); +} + + +Vector<const char*> ScriptDataImpl::BuildArgs() { + int arg_count = Read(PreparseDataConstants::kMessageArgCountPos); + const char** array = NewArray<const char*>(arg_count); + // Position after text found by skipping past length field and + // length field content words. + 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); + pos += count + 1; + } + return Vector<const char*>(array, arg_count); +} + + +unsigned ScriptDataImpl::Read(int position) { + return store_[PreparseDataConstants::kHeaderSize + position]; +} + + +unsigned* ScriptDataImpl::ReadAddress(int position) { + return &store_[PreparseDataConstants::kHeaderSize + position]; +} + + +Scope* Parser::NewScope(Scope* parent, Scope::Type type, bool inside_with) { + Scope* result = new(zone()) Scope(parent, type); + result->Initialize(inside_with); + return result; +} + +// ---------------------------------------------------------------------------- +// Target is a support class to facilitate manipulation of the +// Parser's target_stack_ (the stack of potential 'break' and +// 'continue' statement targets). Upon construction, a new target is +// added; it is removed upon destruction. + +class Target BASE_EMBEDDED { + public: + Target(Target** variable, AstNode* node) + : variable_(variable), node_(node), previous_(*variable) { + *variable = this; + } + + ~Target() { + *variable_ = previous_; + } + + Target* previous() { return previous_; } + AstNode* node() { return node_; } + + private: + Target** variable_; + AstNode* node_; + Target* previous_; +}; + + +class TargetScope BASE_EMBEDDED { + public: + explicit TargetScope(Target** variable) + : variable_(variable), previous_(*variable) { + *variable = NULL; + } + + ~TargetScope() { + *variable_ = previous_; + } + + private: + Target** variable_; + Target* previous_; +}; + + +// ---------------------------------------------------------------------------- +// LexicalScope is a support class to facilitate manipulation of the +// Parser's scope stack. The constructor sets the parser's top scope +// to the incoming scope, and the destructor resets it. +// +// Additionlaly, it stores transient information used during parsing. +// These scopes are not kept around after parsing or referenced by syntax +// trees so they can be stack-allocated and hence used by the pre-parser. + +class LexicalScope BASE_EMBEDDED { + public: + LexicalScope(Parser* parser, Scope* scope, Isolate* isolate); + ~LexicalScope(); + + int NextMaterializedLiteralIndex() { + int next_index = + materialized_literal_count_ + JSFunction::kLiteralsPrefixSize; + materialized_literal_count_++; + return next_index; + } + int materialized_literal_count() { return materialized_literal_count_; } + + void SetThisPropertyAssignmentInfo( + bool only_simple_this_property_assignments, + Handle<FixedArray> this_property_assignments) { + only_simple_this_property_assignments_ = + only_simple_this_property_assignments; + this_property_assignments_ = this_property_assignments; + } + bool only_simple_this_property_assignments() { + return only_simple_this_property_assignments_; + } + Handle<FixedArray> this_property_assignments() { + return this_property_assignments_; + } + + void AddProperty() { expected_property_count_++; } + int expected_property_count() { return expected_property_count_; } + + void AddLoop() { loop_count_++; } + bool ContainsLoops() const { return loop_count_ > 0; } + + private: + // Captures the number of literals that need materialization in the + // function. Includes regexp literals, and boilerplate for object + // and array literals. + int materialized_literal_count_; + + // Properties count estimation. + int expected_property_count_; + + // Keeps track of assignments to properties of this. Used for + // optimizing constructors. + bool only_simple_this_property_assignments_; + Handle<FixedArray> this_property_assignments_; + + // Captures the number of loops inside the scope. + int loop_count_; + + // Bookkeeping + Parser* parser_; + // Previous values + LexicalScope* lexical_scope_parent_; + Scope* previous_scope_; + int previous_with_nesting_level_; +}; + + +LexicalScope::LexicalScope(Parser* parser, Scope* scope, Isolate* isolate) + : materialized_literal_count_(0), + expected_property_count_(0), + only_simple_this_property_assignments_(false), + this_property_assignments_(isolate->factory()->empty_fixed_array()), + loop_count_(0), + parser_(parser), + lexical_scope_parent_(parser->lexical_scope_), + previous_scope_(parser->top_scope_), + previous_with_nesting_level_(parser->with_nesting_level_) { + parser->top_scope_ = scope; + parser->lexical_scope_ = this; + parser->with_nesting_level_ = 0; +} + + +LexicalScope::~LexicalScope() { + parser_->top_scope_->Leave(); + parser_->top_scope_ = previous_scope_; + parser_->lexical_scope_ = lexical_scope_parent_; + parser_->with_nesting_level_ = previous_with_nesting_level_; +} + + +// ---------------------------------------------------------------------------- +// The CHECK_OK macro is a convenient macro to enforce error +// handling for functions that may fail (by returning !*ok). +// +// CAUTION: This macro appends extra statements after a call, +// thus it must never be used where only a single statement +// is correct (e.g. an if statement branch w/o braces)! + +#define CHECK_OK ok); \ + if (!*ok) return NULL; \ + ((void)0 +#define DUMMY ) // to make indentation work +#undef DUMMY + +#define CHECK_FAILED /**/); \ + if (failed_) return NULL; \ + ((void)0 +#define DUMMY ) // to make indentation work +#undef DUMMY + +// ---------------------------------------------------------------------------- +// Implementation of Parser + +Parser::Parser(Handle<Script> script, + bool allow_natives_syntax, + v8::Extension* extension, + ScriptDataImpl* pre_data) + : isolate_(script->GetIsolate()), + symbol_cache_(pre_data ? pre_data->symbol_count() : 0), + script_(script), + scanner_(isolate_->scanner_constants()), + top_scope_(NULL), + with_nesting_level_(0), + lexical_scope_(NULL), + target_stack_(NULL), + allow_natives_syntax_(allow_natives_syntax), + extension_(extension), + pre_data_(pre_data), + fni_(NULL), + stack_overflow_(false), + parenthesized_function_(false) { + AstNode::ResetIds(); +} + + +FunctionLiteral* Parser::ParseProgram(Handle<String> source, + bool in_global_context, + StrictModeFlag strict_mode) { + CompilationZoneScope zone_scope(DONT_DELETE_ON_EXIT); + + HistogramTimerScope timer(isolate()->counters()->parse()); + isolate()->counters()->total_parse_size()->Increment(source->length()); + fni_ = new(zone()) FuncNameInferrer(); + + // Initialize parser state. + source->TryFlatten(); + if (source->IsExternalTwoByteString()) { + // Notice that the stream is destroyed at the end of the branch block. + // The last line of the blocks can't be moved outside, even though they're + // identical calls. + ExternalTwoByteStringUC16CharacterStream stream( + Handle<ExternalTwoByteString>::cast(source), 0, source->length()); + scanner_.Initialize(&stream); + return DoParseProgram(source, in_global_context, strict_mode, &zone_scope); + } else { + GenericStringUC16CharacterStream stream(source, 0, source->length()); + scanner_.Initialize(&stream); + return DoParseProgram(source, in_global_context, strict_mode, &zone_scope); + } +} + + +FunctionLiteral* Parser::DoParseProgram(Handle<String> source, + bool in_global_context, + StrictModeFlag strict_mode, + ZoneScope* zone_scope) { + ASSERT(target_stack_ == NULL); + if (pre_data_ != NULL) pre_data_->Initialize(); + + // Compute the parsing mode. + mode_ = FLAG_lazy ? PARSE_LAZILY : PARSE_EAGERLY; + if (allow_natives_syntax_ || extension_ != NULL) mode_ = PARSE_EAGERLY; + + Scope::Type type = + in_global_context + ? Scope::GLOBAL_SCOPE + : Scope::EVAL_SCOPE; + Handle<String> no_name = isolate()->factory()->empty_symbol(); + + FunctionLiteral* result = NULL; + { Scope* scope = NewScope(top_scope_, type, inside_with()); + LexicalScope lexical_scope(this, scope, isolate()); + if (strict_mode == kStrictMode) { + top_scope_->EnableStrictMode(); + } + ZoneList<Statement*>* body = new ZoneList<Statement*>(16); + bool ok = true; + int beg_loc = scanner().location().beg_pos; + ParseSourceElements(body, Token::EOS, &ok); + if (ok && top_scope_->is_strict_mode()) { + CheckOctalLiteral(beg_loc, scanner().location().end_pos, &ok); + } + if (ok) { + result = new(zone()) FunctionLiteral( + no_name, + top_scope_, + body, + lexical_scope.materialized_literal_count(), + lexical_scope.expected_property_count(), + lexical_scope.only_simple_this_property_assignments(), + lexical_scope.this_property_assignments(), + 0, + 0, + source->length(), + false, + lexical_scope.ContainsLoops()); + } else if (stack_overflow_) { + isolate()->StackOverflow(); + } + } + + // Make sure the target stack is empty. + ASSERT(target_stack_ == NULL); + + // If there was a syntax error we have to get rid of the AST + // and it is not safe to do so before the scope has been deleted. + if (result == NULL) zone_scope->DeleteOnExit(); + return result; +} + +FunctionLiteral* Parser::ParseLazy(CompilationInfo* info) { + CompilationZoneScope zone_scope(DONT_DELETE_ON_EXIT); + HistogramTimerScope timer(isolate()->counters()->parse_lazy()); + Handle<String> source(String::cast(script_->source())); + isolate()->counters()->total_parse_size()->Increment(source->length()); + + Handle<SharedFunctionInfo> shared_info = info->shared_info(); + // Initialize parser state. + source->TryFlatten(); + if (source->IsExternalTwoByteString()) { + ExternalTwoByteStringUC16CharacterStream stream( + Handle<ExternalTwoByteString>::cast(source), + shared_info->start_position(), + shared_info->end_position()); + FunctionLiteral* result = ParseLazy(info, &stream, &zone_scope); + return result; + } else { + GenericStringUC16CharacterStream stream(source, + shared_info->start_position(), + shared_info->end_position()); + FunctionLiteral* result = ParseLazy(info, &stream, &zone_scope); + return result; + } +} + + +FunctionLiteral* Parser::ParseLazy(CompilationInfo* info, + UC16CharacterStream* source, + ZoneScope* zone_scope) { + Handle<SharedFunctionInfo> shared_info = info->shared_info(); + scanner_.Initialize(source); + ASSERT(target_stack_ == NULL); + + Handle<String> name(String::cast(shared_info->name())); + fni_ = new(zone()) FuncNameInferrer(); + fni_->PushEnclosingName(name); + + mode_ = PARSE_EAGERLY; + + // Place holder for the result. + FunctionLiteral* result = NULL; + + { + // Parse the function literal. + Handle<String> no_name = isolate()->factory()->empty_symbol(); + Scope* scope = NewScope(top_scope_, Scope::GLOBAL_SCOPE, inside_with()); + if (!info->closure().is_null()) { + scope = Scope::DeserializeScopeChain(info, scope); + } + LexicalScope lexical_scope(this, scope, isolate()); + + if (shared_info->strict_mode()) { + top_scope_->EnableStrictMode(); + } + + FunctionLiteralType type = + shared_info->is_expression() ? EXPRESSION : DECLARATION; + bool ok = true; + result = ParseFunctionLiteral(name, + false, // Strict mode name already checked. + RelocInfo::kNoPosition, type, &ok); + // Make sure the results agree. + ASSERT(ok == (result != NULL)); + } + + // Make sure the target stack is empty. + ASSERT(target_stack_ == NULL); + + // If there was a stack overflow we have to get rid of AST and it is + // not safe to do before scope has been deleted. + if (result == NULL) { + zone_scope->DeleteOnExit(); + if (stack_overflow_) isolate()->StackOverflow(); + } else { + Handle<String> inferred_name(shared_info->inferred_name()); + result->set_inferred_name(inferred_name); + } + return result; +} + + +Handle<String> Parser::GetSymbol(bool* ok) { + int symbol_id = -1; + if (pre_data() != NULL) { + symbol_id = pre_data()->GetSymbolIdentifier(); + } + return LookupSymbol(symbol_id); +} + + +void Parser::ReportMessage(const char* type, Vector<const char*> args) { + Scanner::Location source_location = scanner().location(); + ReportMessageAt(source_location, type, args); +} + + +void Parser::ReportMessageAt(Scanner::Location source_location, + const char* type, + Vector<const char*> args) { + MessageLocation location(script_, + source_location.beg_pos, + source_location.end_pos); + Factory* factory = isolate()->factory(); + Handle<FixedArray> elements = factory->NewFixedArray(args.length()); + for (int i = 0; i < args.length(); i++) { + Handle<String> arg_string = factory->NewStringFromUtf8(CStrVector(args[i])); + elements->set(i, *arg_string); + } + Handle<JSArray> array = factory->NewJSArrayWithElements(elements); + Handle<Object> result = factory->NewSyntaxError(type, array); + isolate()->Throw(*result, &location); +} + + +void Parser::ReportMessageAt(Scanner::Location source_location, + const char* type, + Vector<Handle<String> > args) { + MessageLocation location(script_, + source_location.beg_pos, + source_location.end_pos); + Factory* factory = isolate()->factory(); + Handle<FixedArray> elements = factory->NewFixedArray(args.length()); + for (int i = 0; i < args.length(); i++) { + elements->set(i, *args[i]); + } + Handle<JSArray> array = factory->NewJSArrayWithElements(elements); + Handle<Object> result = factory->NewSyntaxError(type, array); + isolate()->Throw(*result, &location); +} + + +// Base class containing common code for the different finder classes used by +// the parser. +class ParserFinder { + protected: + ParserFinder() {} + static Assignment* AsAssignment(Statement* stat) { + if (stat == NULL) return NULL; + ExpressionStatement* exp_stat = stat->AsExpressionStatement(); + if (exp_stat == NULL) return NULL; + return exp_stat->expression()->AsAssignment(); + } +}; + + +// An InitializationBlockFinder finds and marks sequences of statements of the +// form expr.a = ...; expr.b = ...; etc. +class InitializationBlockFinder : public ParserFinder { + public: + InitializationBlockFinder() + : first_in_block_(NULL), last_in_block_(NULL), block_size_(0) {} + + ~InitializationBlockFinder() { + if (InBlock()) EndBlock(); + } + + void Update(Statement* stat) { + Assignment* assignment = AsAssignment(stat); + if (InBlock()) { + if (BlockContinues(assignment)) { + UpdateBlock(assignment); + } else { + EndBlock(); + } + } + if (!InBlock() && (assignment != NULL) && + (assignment->op() == Token::ASSIGN)) { + StartBlock(assignment); + } + } + + private: + // The minimum number of contiguous assignment that will + // be treated as an initialization block. Benchmarks show that + // the overhead exceeds the savings below this limit. + static const int kMinInitializationBlock = 3; + + // Returns true if the expressions appear to denote the same object. + // In the context of initialization blocks, we only consider expressions + // of the form 'expr.x' or expr["x"]. + static bool SameObject(Expression* e1, Expression* e2) { + VariableProxy* v1 = e1->AsVariableProxy(); + VariableProxy* v2 = e2->AsVariableProxy(); + if (v1 != NULL && v2 != NULL) { + return v1->name()->Equals(*v2->name()); + } + Property* p1 = e1->AsProperty(); + Property* p2 = e2->AsProperty(); + if ((p1 == NULL) || (p2 == NULL)) return false; + Literal* key1 = p1->key()->AsLiteral(); + Literal* key2 = p2->key()->AsLiteral(); + if ((key1 == NULL) || (key2 == NULL)) return false; + if (!key1->handle()->IsString() || !key2->handle()->IsString()) { + return false; + } + String* name1 = String::cast(*key1->handle()); + String* name2 = String::cast(*key2->handle()); + if (!name1->Equals(name2)) return false; + return SameObject(p1->obj(), p2->obj()); + } + + // Returns true if the expressions appear to denote different properties + // of the same object. + static bool PropertyOfSameObject(Expression* e1, Expression* e2) { + Property* p1 = e1->AsProperty(); + Property* p2 = e2->AsProperty(); + if ((p1 == NULL) || (p2 == NULL)) return false; + return SameObject(p1->obj(), p2->obj()); + } + + bool BlockContinues(Assignment* assignment) { + if ((assignment == NULL) || (first_in_block_ == NULL)) return false; + if (assignment->op() != Token::ASSIGN) return false; + return PropertyOfSameObject(first_in_block_->target(), + assignment->target()); + } + + void StartBlock(Assignment* assignment) { + first_in_block_ = assignment; + last_in_block_ = assignment; + block_size_ = 1; + } + + void UpdateBlock(Assignment* assignment) { + last_in_block_ = assignment; + ++block_size_; + } + + void EndBlock() { + if (block_size_ >= kMinInitializationBlock) { + first_in_block_->mark_block_start(); + last_in_block_->mark_block_end(); + } + last_in_block_ = first_in_block_ = NULL; + block_size_ = 0; + } + + bool InBlock() { return first_in_block_ != NULL; } + + Assignment* first_in_block_; + Assignment* last_in_block_; + int block_size_; + + DISALLOW_COPY_AND_ASSIGN(InitializationBlockFinder); +}; + + +// A ThisNamedPropertyAssigmentFinder finds and marks statements of the form +// this.x = ...;, where x is a named property. It also determines whether a +// function contains only assignments of this type. +class ThisNamedPropertyAssigmentFinder : public ParserFinder { + public: + explicit ThisNamedPropertyAssigmentFinder(Isolate* isolate) + : isolate_(isolate), + only_simple_this_property_assignments_(true), + names_(NULL), + assigned_arguments_(NULL), + assigned_constants_(NULL) {} + + void Update(Scope* scope, Statement* stat) { + // Bail out if function already has property assignment that are + // not simple this property assignments. + if (!only_simple_this_property_assignments_) { + return; + } + + // Check whether this statement is of the form this.x = ...; + Assignment* assignment = AsAssignment(stat); + if (IsThisPropertyAssignment(assignment)) { + HandleThisPropertyAssignment(scope, assignment); + } else { + only_simple_this_property_assignments_ = false; + } + } + + // Returns whether only statements of the form this.x = y; where y is either a + // constant or a function argument was encountered. + bool only_simple_this_property_assignments() { + return only_simple_this_property_assignments_; + } + + // Returns a fixed array containing three elements for each assignment of the + // form this.x = y; + Handle<FixedArray> GetThisPropertyAssignments() { + if (names_ == NULL) { + return isolate_->factory()->empty_fixed_array(); + } + ASSERT(names_ != NULL); + ASSERT(assigned_arguments_ != NULL); + ASSERT_EQ(names_->length(), assigned_arguments_->length()); + ASSERT_EQ(names_->length(), assigned_constants_->length()); + Handle<FixedArray> assignments = + isolate_->factory()->NewFixedArray(names_->length() * 3); + for (int i = 0; i < names_->length(); i++) { + assignments->set(i * 3, *names_->at(i)); + assignments->set(i * 3 + 1, Smi::FromInt(assigned_arguments_->at(i))); + assignments->set(i * 3 + 2, *assigned_constants_->at(i)); + } + return assignments; + } + + private: + bool IsThisPropertyAssignment(Assignment* assignment) { + if (assignment != NULL) { + Property* property = assignment->target()->AsProperty(); + return assignment->op() == Token::ASSIGN + && property != NULL + && property->obj()->AsVariableProxy() != NULL + && property->obj()->AsVariableProxy()->is_this(); + } + return false; + } + + void HandleThisPropertyAssignment(Scope* scope, Assignment* assignment) { + // Check that the property assigned to is a named property, which is not + // __proto__. + Property* property = assignment->target()->AsProperty(); + ASSERT(property != NULL); + Literal* literal = property->key()->AsLiteral(); + uint32_t dummy; + if (literal != NULL && + literal->handle()->IsString() && + !String::cast(*(literal->handle()))->Equals( + isolate_->heap()->Proto_symbol()) && + !String::cast(*(literal->handle()))->AsArrayIndex(&dummy)) { + Handle<String> key = Handle<String>::cast(literal->handle()); + + // Check whether the value assigned is either a constant or matches the + // name of one of the arguments to the function. + if (assignment->value()->AsLiteral() != NULL) { + // Constant assigned. + Literal* literal = assignment->value()->AsLiteral(); + AssignmentFromConstant(key, literal->handle()); + return; + } else if (assignment->value()->AsVariableProxy() != NULL) { + // Variable assigned. + Handle<String> name = + assignment->value()->AsVariableProxy()->name(); + // Check whether the variable assigned matches an argument name. + for (int i = 0; i < scope->num_parameters(); i++) { + if (*scope->parameter(i)->name() == *name) { + // Assigned from function argument. + AssignmentFromParameter(key, i); + return; + } + } + } + } + // It is not a simple "this.x = value;" assignment with a constant + // or parameter value. + AssignmentFromSomethingElse(); + } + + void AssignmentFromParameter(Handle<String> name, int index) { + EnsureAllocation(); + names_->Add(name); + assigned_arguments_->Add(index); + assigned_constants_->Add(isolate_->factory()->undefined_value()); + } + + void AssignmentFromConstant(Handle<String> name, Handle<Object> value) { + EnsureAllocation(); + names_->Add(name); + assigned_arguments_->Add(-1); + assigned_constants_->Add(value); + } + + void AssignmentFromSomethingElse() { + // The this assignment is not a simple one. + only_simple_this_property_assignments_ = false; + } + + void EnsureAllocation() { + if (names_ == NULL) { + ASSERT(assigned_arguments_ == NULL); + ASSERT(assigned_constants_ == NULL); + names_ = new ZoneStringList(4); + assigned_arguments_ = new ZoneList<int>(4); + assigned_constants_ = new ZoneObjectList(4); + } + } + + Isolate* isolate_; + bool only_simple_this_property_assignments_; + ZoneStringList* names_; + ZoneList<int>* assigned_arguments_; + ZoneObjectList* assigned_constants_; +}; + + +void* Parser::ParseSourceElements(ZoneList<Statement*>* processor, + int end_token, + bool* ok) { + // SourceElements :: + // (Statement)* <end_token> + + // Allocate a target stack to use for this set of source + // elements. This way, all scripts and functions get their own + // target stack thus avoiding illegal breaks and continues across + // functions. + TargetScope scope(&this->target_stack_); + + ASSERT(processor != NULL); + InitializationBlockFinder block_finder; + ThisNamedPropertyAssigmentFinder this_property_assignment_finder(isolate()); + bool directive_prologue = true; // Parsing directive prologue. + + while (peek() != end_token) { + if (directive_prologue && peek() != Token::STRING) { + directive_prologue = false; + } + + Scanner::Location token_loc = scanner().peek_location(); + + Statement* stat; + if (peek() == Token::FUNCTION) { + // FunctionDeclaration is only allowed in the context of SourceElements + // (Ecma 262 5th Edition, clause 14): + // SourceElement: + // Statement + // FunctionDeclaration + // Common language extension is to allow function declaration in place + // of any statement. This language extension is disabled in strict mode. + stat = ParseFunctionDeclaration(CHECK_OK); + } else { + stat = ParseStatement(NULL, CHECK_OK); + } + + if (stat == NULL || stat->IsEmpty()) { + directive_prologue = false; // End of directive prologue. + continue; + } + + if (directive_prologue) { + // A shot at a directive. + ExpressionStatement *e_stat; + Literal *literal; + // Still processing directive prologue? + if ((e_stat = stat->AsExpressionStatement()) != NULL && + (literal = e_stat->expression()->AsLiteral()) != NULL && + literal->handle()->IsString()) { + Handle<String> directive = Handle<String>::cast(literal->handle()); + + // Check "use strict" directive (ES5 14.1). + if (!top_scope_->is_strict_mode() && + directive->Equals(isolate()->heap()->use_strict()) && + token_loc.end_pos - token_loc.beg_pos == + isolate()->heap()->use_strict()->length() + 2) { + top_scope_->EnableStrictMode(); + // "use strict" is the only directive for now. + directive_prologue = false; + } + } else { + // End of the directive prologue. + directive_prologue = false; + } + } + + // We find and mark the initialization blocks on top level code only. + // This is because the optimization prevents reuse of the map transitions, + // so it should be used only for code that will only be run once. + if (top_scope_->is_global_scope()) { + block_finder.Update(stat); + } + // Find and mark all assignments to named properties in this (this.x =) + if (top_scope_->is_function_scope()) { + this_property_assignment_finder.Update(top_scope_, stat); + } + processor->Add(stat); + } + + // Propagate the collected information on this property assignments. + if (top_scope_->is_function_scope()) { + bool only_simple_this_property_assignments = + this_property_assignment_finder.only_simple_this_property_assignments() + && top_scope_->declarations()->length() == 0; + if (only_simple_this_property_assignments) { + lexical_scope_->SetThisPropertyAssignmentInfo( + only_simple_this_property_assignments, + this_property_assignment_finder.GetThisPropertyAssignments()); + } + } + return 0; +} + + +Statement* Parser::ParseStatement(ZoneStringList* labels, 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 + int statement_pos = scanner().peek_location().beg_pos; + Statement* stmt = NULL; + switch (peek()) { + case Token::LBRACE: + return ParseBlock(labels, ok); + + case Token::CONST: // fall through + case Token::VAR: + stmt = ParseVariableStatement(ok); + break; + + case Token::SEMICOLON: + Next(); + return EmptyStatement(); + + case Token::IF: + stmt = ParseIfStatement(labels, ok); + break; + + case Token::DO: + stmt = ParseDoWhileStatement(labels, ok); + break; + + case Token::WHILE: + stmt = ParseWhileStatement(labels, ok); + break; + + case Token::FOR: + stmt = ParseForStatement(labels, ok); + break; + + case Token::CONTINUE: + stmt = ParseContinueStatement(ok); + break; + + case Token::BREAK: + stmt = ParseBreakStatement(labels, ok); + break; + + case Token::RETURN: + stmt = ParseReturnStatement(ok); + break; + + case Token::WITH: + stmt = ParseWithStatement(labels, ok); + break; + + case Token::SWITCH: + stmt = ParseSwitchStatement(labels, ok); + break; + + case Token::THROW: + stmt = ParseThrowStatement(ok); + break; + + case Token::TRY: { + // NOTE: It is somewhat complicated to have labels on + // try-statements. When breaking out of a try-finally statement, + // one must take great care not to treat it as a + // fall-through. It is much easier just to wrap the entire + // try-statement in a statement block and put the labels there + Block* result = new(zone()) Block(labels, 1, false); + Target target(&this->target_stack_, result); + TryStatement* statement = ParseTryStatement(CHECK_OK); + if (statement) { + statement->set_statement_pos(statement_pos); + } + if (result) result->AddStatement(statement); + return result; + } + + case Token::FUNCTION: { + // In strict mode, FunctionDeclaration is only allowed in the context + // of SourceElements. + if (top_scope_->is_strict_mode()) { + ReportMessageAt(scanner().peek_location(), "strict_function", + Vector<const char*>::empty()); + *ok = false; + return NULL; + } + return ParseFunctionDeclaration(ok); + } + + case Token::NATIVE: + return ParseNativeDeclaration(ok); + + case Token::DEBUGGER: + stmt = ParseDebuggerStatement(ok); + break; + + default: + stmt = ParseExpressionOrLabelledStatement(labels, ok); + } + + // Store the source position of the statement + if (stmt != NULL) stmt->set_statement_pos(statement_pos); + return stmt; +} + + +VariableProxy* Parser::Declare(Handle<String> name, + Variable::Mode mode, + FunctionLiteral* fun, + bool resolve, + bool* ok) { + Variable* var = NULL; + // If we are inside a function, a declaration of a variable + // is a truly local variable, and the scope of the variable + // is always the function scope. + + // If a function scope exists, then we can statically declare this + // variable and also set its mode. In any case, a Declaration node + // will be added to the scope so that the declaration can be added + // to the corresponding activation frame at runtime if necessary. + // For instance declarations inside an eval scope need to be added + // to the calling function context. + if (top_scope_->is_function_scope()) { + // Declare the variable in the function scope. + var = top_scope_->LocalLookup(name); + if (var == NULL) { + // Declare the name. + var = top_scope_->DeclareLocal(name, mode); + } else { + // The name was declared before; check for conflicting + // re-declarations. If the previous declaration was a const or the + // current declaration is a const then we have a conflict. There is + // similar code in runtime.cc in the Declare functions. + if ((mode == Variable::CONST) || (var->mode() == Variable::CONST)) { + // We only have vars and consts in declarations. + ASSERT(var->mode() == Variable::VAR || + var->mode() == Variable::CONST); + const char* type = (var->mode() == Variable::VAR) ? "var" : "const"; + Handle<String> type_string = + isolate()->factory()->NewStringFromUtf8(CStrVector(type), TENURED); + Expression* expression = + NewThrowTypeError(isolate()->factory()->redeclaration_symbol(), + type_string, name); + top_scope_->SetIllegalRedeclaration(expression); + } + } + } + + // We add a declaration node for every declaration. The compiler + // will only generate code if necessary. In particular, declarations + // for inner local variables that do not represent functions won't + // result in any generated code. + // + // Note that we always add an unresolved proxy even if it's not + // used, simply because we don't know in this method (w/o extra + // parameters) if the proxy is needed or not. The proxy will be + // bound during variable resolution time unless it was pre-bound + // below. + // + // WARNING: This will lead to multiple declaration nodes for the + // same variable if it is declared several times. This is not a + // semantic issue as long as we keep the source order, but it may be + // a performance issue since it may lead to repeated + // Runtime::DeclareContextSlot() calls. + VariableProxy* proxy = top_scope_->NewUnresolved(name, inside_with()); + top_scope_->AddDeclaration(new(zone()) Declaration(proxy, mode, fun)); + + // For global const variables we bind the proxy to a variable. + if (mode == Variable::CONST && top_scope_->is_global_scope()) { + ASSERT(resolve); // should be set by all callers + Variable::Kind kind = Variable::NORMAL; + var = new(zone()) Variable(top_scope_, name, Variable::CONST, true, kind); + } + + // If requested and we have a local variable, bind the proxy to the variable + // at parse-time. This is used for functions (and consts) declared inside + // statements: the corresponding function (or const) variable must be in the + // function scope and not a statement-local scope, e.g. as provided with a + // 'with' statement: + // + // with (obj) { + // function f() {} + // } + // + // which is translated into: + // + // with (obj) { + // // in this case this is not: 'var f; f = function () {};' + // var f = function () {}; + // } + // + // Note that if 'f' is accessed from inside the 'with' statement, it + // will be allocated in the context (because we must be able to look + // it up dynamically) but it will also be accessed statically, i.e., + // with a context slot index and a context chain length for this + // initialization code. Thus, inside the 'with' statement, we need + // both access to the static and the dynamic context chain; the + // runtime needs to provide both. + if (resolve && var != NULL) proxy->BindTo(var); + + return proxy; +} + + +// 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* Parser::ParseNativeDeclaration(bool* ok) { + if (extension_ == NULL) { + ReportUnexpectedToken(Token::NATIVE); + *ok = false; + return NULL; + } + + Expect(Token::NATIVE, CHECK_OK); + Expect(Token::FUNCTION, CHECK_OK); + Handle<String> name = ParseIdentifier(CHECK_OK); + Expect(Token::LPAREN, CHECK_OK); + bool done = (peek() == Token::RPAREN); + while (!done) { + ParseIdentifier(CHECK_OK); + done = (peek() == Token::RPAREN); + if (!done) { + Expect(Token::COMMA, CHECK_OK); + } + } + Expect(Token::RPAREN, CHECK_OK); + Expect(Token::SEMICOLON, CHECK_OK); + + // Make sure that the function containing the native declaration + // isn't lazily compiled. The extension structures are only + // accessible while parsing the first time not when reparsing + // because of lazy compilation. + top_scope_->ForceEagerCompilation(); + + // Compute the function template for the native function. + v8::Handle<v8::FunctionTemplate> fun_template = + extension_->GetNativeFunction(v8::Utils::ToLocal(name)); + ASSERT(!fun_template.IsEmpty()); + + // Instantiate the function and create a shared function info from it. + Handle<JSFunction> fun = Utils::OpenHandle(*fun_template->GetFunction()); + const int literals = fun->NumberOfLiterals(); + Handle<Code> code = Handle<Code>(fun->shared()->code()); + Handle<Code> construct_stub = Handle<Code>(fun->shared()->construct_stub()); + Handle<SharedFunctionInfo> shared = + isolate()->factory()->NewSharedFunctionInfo(name, literals, code, + Handle<SerializedScopeInfo>(fun->shared()->scope_info())); + shared->set_construct_stub(*construct_stub); + + // Copy the function data to the shared function info. + shared->set_function_data(fun->shared()->function_data()); + int parameters = fun->shared()->formal_parameter_count(); + shared->set_formal_parameter_count(parameters); + + // TODO(1240846): It's weird that native function declarations are + // introduced dynamically when we meet their declarations, whereas + // other functions are setup when entering the surrounding scope. + SharedFunctionInfoLiteral* lit = + new(zone()) SharedFunctionInfoLiteral(shared); + VariableProxy* var = Declare(name, Variable::VAR, NULL, true, CHECK_OK); + return new(zone()) ExpressionStatement(new(zone()) Assignment( + Token::INIT_VAR, var, lit, RelocInfo::kNoPosition)); +} + + +Statement* Parser::ParseFunctionDeclaration(bool* ok) { + // FunctionDeclaration :: + // 'function' Identifier '(' FormalParameterListopt ')' '{' FunctionBody '}' + Expect(Token::FUNCTION, CHECK_OK); + int function_token_position = scanner().location().beg_pos; + bool is_reserved = false; + Handle<String> name = ParseIdentifierOrReservedWord(&is_reserved, CHECK_OK); + FunctionLiteral* fun = ParseFunctionLiteral(name, + is_reserved, + function_token_position, + DECLARATION, + CHECK_OK); + // Even if we're not at the top-level of the global or a function + // scope, we treat is as such and introduce the function with it's + // initial value upon entering the corresponding scope. + Declare(name, Variable::VAR, fun, true, CHECK_OK); + return EmptyStatement(); +} + + +Block* Parser::ParseBlock(ZoneStringList* labels, bool* ok) { + // Block :: + // '{' Statement* '}' + + // Note that a Block does not introduce a new execution scope! + // (ECMA-262, 3rd, 12.2) + // + // Construct block expecting 16 statements. + Block* result = new(zone()) Block(labels, 16, false); + Target target(&this->target_stack_, result); + Expect(Token::LBRACE, CHECK_OK); + while (peek() != Token::RBRACE) { + Statement* stat = ParseStatement(NULL, CHECK_OK); + if (stat && !stat->IsEmpty()) result->AddStatement(stat); + } + Expect(Token::RBRACE, CHECK_OK); + return result; +} + + +Block* Parser::ParseVariableStatement(bool* ok) { + // VariableStatement :: + // VariableDeclarations ';' + + Expression* dummy; // to satisfy the ParseVariableDeclarations() signature + Block* result = ParseVariableDeclarations(true, &dummy, CHECK_OK); + ExpectSemicolon(CHECK_OK); + return result; +} + + +bool Parser::IsEvalOrArguments(Handle<String> string) { + return string.is_identical_to(isolate()->factory()->eval_symbol()) || + string.is_identical_to(isolate()->factory()->arguments_symbol()); +} + + +// 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 used for the parsing +// of 'for-in' loops. +Block* Parser::ParseVariableDeclarations(bool accept_IN, + Expression** var, + bool* ok) { + // VariableDeclarations :: + // ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[','] + + Variable::Mode mode = Variable::VAR; + bool is_const = false; + if (peek() == Token::VAR) { + Consume(Token::VAR); + } else if (peek() == Token::CONST) { + Consume(Token::CONST); + if (top_scope_->is_strict_mode()) { + ReportMessage("strict_const", Vector<const char*>::empty()); + *ok = false; + return NULL; + } + mode = Variable::CONST; + is_const = true; + } else { + UNREACHABLE(); // by current callers + } + + // The scope of a variable/const declared anywhere inside a function + // is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). Thus we can + // transform a source-level variable/const declaration into a (Function) + // Scope declaration, and rewrite the source-level initialization into an + // assignment statement. We use a block to collect multiple assignments. + // + // We mark the block as initializer block because we don't want the + // rewriter to add a '.result' assignment to such a block (to get compliant + // behavior for code such as print(eval('var x = 7')), and for cosmetic + // reasons when pretty-printing. Also, unless an assignment (initialization) + // is inside an initializer block, it is ignored. + // + // Create new block with one expected declaration. + Block* block = new(zone()) Block(NULL, 1, true); + VariableProxy* last_var = NULL; // the last variable declared + int nvars = 0; // the number of variables declared + do { + if (fni_ != NULL) fni_->Enter(); + + // Parse variable name. + if (nvars > 0) Consume(Token::COMMA); + Handle<String> name = ParseIdentifier(CHECK_OK); + if (fni_ != NULL) fni_->PushVariableName(name); + + // Strict mode variables may not be named eval or arguments + if (top_scope_->is_strict_mode() && IsEvalOrArguments(name)) { + ReportMessage("strict_var_name", Vector<const char*>::empty()); + *ok = false; + return NULL; + } + + // Declare variable. + // Note that we *always* must treat the initial value via a separate init + // assignment for variables and constants because the value must be assigned + // when the variable is encountered in the source. But the variable/constant + // is declared (and set to 'undefined') upon entering the function within + // which the variable or constant is declared. Only function variables have + // an initial value in the declaration (because they are initialized upon + // entering the function). + // + // If we have a const declaration, in an inner scope, the proxy is always + // bound to the declared variable (independent of possibly surrounding with + // statements). + last_var = Declare(name, mode, NULL, + is_const /* always bound for CONST! */, + CHECK_OK); + nvars++; + + // Parse initialization expression if present and/or needed. A + // declaration of the form: + // + // var v = x; + // + // is syntactic sugar for: + // + // var v; v = x; + // + // In particular, we need to re-lookup 'v' as it may be a + // different 'v' than the 'v' in the declaration (if we are inside + // a 'with' statement that makes a object property with name 'v' + // visible). + // + // However, note that const declarations are different! A const + // declaration of the form: + // + // const c = x; + // + // is *not* syntactic sugar for: + // + // const c; c = x; + // + // The "variable" c initialized to x is the same as the declared + // one - there is no re-lookup (see the last parameter of the + // Declare() call above). + + Expression* value = NULL; + int position = -1; + if (peek() == Token::ASSIGN) { + Expect(Token::ASSIGN, CHECK_OK); + position = scanner().location().beg_pos; + value = ParseAssignmentExpression(accept_IN, CHECK_OK); + // Don't infer if it is "a = function(){...}();"-like expression. + if (fni_ != NULL && value->AsCall() == NULL) fni_->Infer(); + } + + // Make sure that 'const c' actually initializes 'c' to undefined + // even though it seems like a stupid thing to do. + if (value == NULL && is_const) { + value = GetLiteralUndefined(); + } + + // Global variable declarations must be compiled in a specific + // way. When the script containing the global variable declaration + // is entered, the global variable must be declared, so that if it + // doesn't exist (not even in a prototype of the global object) it + // gets created with an initial undefined value. This is handled + // by the declarations part of the function representing the + // top-level global code; see Runtime::DeclareGlobalVariable. If + // it already exists (in the object or in a prototype), it is + // *not* touched until the variable declaration statement is + // executed. + // + // Executing the variable declaration statement will always + // guarantee to give the global object a "local" variable; a + // variable defined in the global object and not in any + // prototype. This way, global variable declarations can shadow + // properties in the prototype chain, but only after the variable + // declaration statement has been executed. This is important in + // browsers where the global object (window) has lots of + // properties defined in prototype objects. + + if (top_scope_->is_global_scope()) { + // Compute the arguments for the runtime call. + ZoneList<Expression*>* arguments = new ZoneList<Expression*>(3); + // We have at least 1 parameter. + arguments->Add(new(zone()) Literal(name)); + CallRuntime* initialize; + + if (is_const) { + arguments->Add(value); + value = NULL; // zap the value to avoid the unnecessary assignment + + // Construct the call to Runtime_InitializeConstGlobal + // and add it to the initialization statement block. + // Note that the function does different things depending on + // the number of arguments (1 or 2). + initialize = + new(zone()) CallRuntime( + isolate()->factory()->InitializeConstGlobal_symbol(), + Runtime::FunctionForId(Runtime::kInitializeConstGlobal), + arguments); + } else { + // Add strict mode. + // We may want to pass singleton to avoid Literal allocations. + arguments->Add(NewNumberLiteral( + top_scope_->is_strict_mode() ? kStrictMode : kNonStrictMode)); + + // Be careful not to assign a value to the global variable if + // we're in a with. The initialization value should not + // necessarily be stored in the global object in that case, + // which is why we need to generate a separate assignment node. + if (value != NULL && !inside_with()) { + arguments->Add(value); + value = NULL; // zap the value to avoid the unnecessary assignment + } + + // Construct the call to Runtime_InitializeVarGlobal + // and add it to the initialization statement block. + // Note that the function does different things depending on + // the number of arguments (2 or 3). + initialize = + new(zone()) CallRuntime( + isolate()->factory()->InitializeVarGlobal_symbol(), + Runtime::FunctionForId(Runtime::kInitializeVarGlobal), + arguments); + } + + block->AddStatement(new(zone()) ExpressionStatement(initialize)); + } + + // Add an assignment node to the initialization statement block if + // we still have a pending initialization value. We must distinguish + // between variables and constants: Variable initializations are simply + // assignments (with all the consequences if they are inside a 'with' + // statement - they may change a 'with' object property). Constant + // initializations always assign to the declared constant which is + // always at the function scope level. This is only relevant for + // dynamically looked-up variables and constants (the start context + // for constant lookups is always the function context, while it is + // the top context for variables). Sigh... + if (value != NULL) { + Token::Value op = (is_const ? Token::INIT_CONST : Token::INIT_VAR); + Assignment* assignment = + new(zone()) Assignment(op, last_var, value, position); + if (block) { + block->AddStatement(new(zone()) ExpressionStatement(assignment)); + } + } + + if (fni_ != NULL) fni_->Leave(); + } while (peek() == Token::COMMA); + + if (!is_const && nvars == 1) { + // We have a single, non-const variable. + ASSERT(last_var != NULL); + *var = last_var; + } + + return block; +} + + +static bool ContainsLabel(ZoneStringList* labels, Handle<String> label) { + ASSERT(!label.is_null()); + if (labels != NULL) + for (int i = labels->length(); i-- > 0; ) + if (labels->at(i).is_identical_to(label)) + return true; + + return false; +} + + +Statement* Parser::ParseExpressionOrLabelledStatement(ZoneStringList* labels, + bool* ok) { + // ExpressionStatement | LabelledStatement :: + // Expression ';' + // Identifier ':' Statement + bool starts_with_idenfifier = peek_any_identifier(); + Expression* expr = ParseExpression(true, CHECK_OK); + if (peek() == Token::COLON && starts_with_idenfifier && expr && + expr->AsVariableProxy() != NULL && + !expr->AsVariableProxy()->is_this()) { + // Expression is a single identifier, and not, e.g., a parenthesized + // identifier. + VariableProxy* var = expr->AsVariableProxy(); + Handle<String> label = var->name(); + // TODO(1240780): We don't check for redeclaration of labels + // during preparsing since keeping track of the set of active + // labels requires nontrivial changes to the way scopes are + // structured. However, these are probably changes we want to + // make later anyway so we should go back and fix this then. + if (ContainsLabel(labels, label) || TargetStackContainsLabel(label)) { + SmartPointer<char> c_string = label->ToCString(DISALLOW_NULLS); + const char* elms[2] = { "Label", *c_string }; + Vector<const char*> args(elms, 2); + ReportMessage("redeclaration", args); + *ok = false; + return NULL; + } + if (labels == NULL) labels = new ZoneStringList(4); + labels->Add(label); + // Remove the "ghost" variable that turned out to be a label + // from the top scope. This way, we don't try to resolve it + // during the scope processing. + top_scope_->RemoveUnresolved(var); + Expect(Token::COLON, CHECK_OK); + return ParseStatement(labels, ok); + } + + // Parsed expression statement. + ExpectSemicolon(CHECK_OK); + return new(zone()) ExpressionStatement(expr); +} + + +IfStatement* Parser::ParseIfStatement(ZoneStringList* labels, bool* ok) { + // IfStatement :: + // 'if' '(' Expression ')' Statement ('else' Statement)? + + Expect(Token::IF, CHECK_OK); + Expect(Token::LPAREN, CHECK_OK); + Expression* condition = ParseExpression(true, CHECK_OK); + Expect(Token::RPAREN, CHECK_OK); + Statement* then_statement = ParseStatement(labels, CHECK_OK); + Statement* else_statement = NULL; + if (peek() == Token::ELSE) { + Next(); + else_statement = ParseStatement(labels, CHECK_OK); + } else { + else_statement = EmptyStatement(); + } + return new(zone()) IfStatement(condition, then_statement, else_statement); +} + + +Statement* Parser::ParseContinueStatement(bool* ok) { + // ContinueStatement :: + // 'continue' Identifier? ';' + + Expect(Token::CONTINUE, CHECK_OK); + Handle<String> label = Handle<String>::null(); + Token::Value tok = peek(); + if (!scanner().has_line_terminator_before_next() && + tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) { + label = ParseIdentifier(CHECK_OK); + } + IterationStatement* target = NULL; + target = LookupContinueTarget(label, CHECK_OK); + if (target == NULL) { + // Illegal continue statement. + const char* message = "illegal_continue"; + Vector<Handle<String> > args; + if (!label.is_null()) { + message = "unknown_label"; + args = Vector<Handle<String> >(&label, 1); + } + ReportMessageAt(scanner().location(), message, args); + *ok = false; + return NULL; + } + ExpectSemicolon(CHECK_OK); + return new(zone()) ContinueStatement(target); +} + + +Statement* Parser::ParseBreakStatement(ZoneStringList* labels, bool* ok) { + // BreakStatement :: + // 'break' Identifier? ';' + + Expect(Token::BREAK, CHECK_OK); + Handle<String> label; + Token::Value tok = peek(); + if (!scanner().has_line_terminator_before_next() && + tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) { + label = ParseIdentifier(CHECK_OK); + } + // Parse labeled break statements that target themselves into + // empty statements, e.g. 'l1: l2: l3: break l2;' + if (!label.is_null() && ContainsLabel(labels, label)) { + return EmptyStatement(); + } + BreakableStatement* target = NULL; + target = LookupBreakTarget(label, CHECK_OK); + if (target == NULL) { + // Illegal break statement. + const char* message = "illegal_break"; + Vector<Handle<String> > args; + if (!label.is_null()) { + message = "unknown_label"; + args = Vector<Handle<String> >(&label, 1); + } + ReportMessageAt(scanner().location(), message, args); + *ok = false; + return NULL; + } + ExpectSemicolon(CHECK_OK); + return new(zone()) BreakStatement(target); +} + + +Statement* Parser::ParseReturnStatement(bool* ok) { + // ReturnStatement :: + // 'return' 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(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. + // + // To be consistent with KJS we report the syntax error at runtime. + if (!top_scope_->is_function_scope()) { + Handle<String> type = isolate()->factory()->illegal_return_symbol(); + Expression* throw_error = NewThrowSyntaxError(type, Handle<Object>::null()); + return new(zone()) ExpressionStatement(throw_error); + } + + Token::Value tok = peek(); + if (scanner().has_line_terminator_before_next() || + tok == Token::SEMICOLON || + tok == Token::RBRACE || + tok == Token::EOS) { + ExpectSemicolon(CHECK_OK); + return new(zone()) ReturnStatement(GetLiteralUndefined()); + } + + Expression* expr = ParseExpression(true, CHECK_OK); + ExpectSemicolon(CHECK_OK); + return new(zone()) ReturnStatement(expr); +} + + +Block* Parser::WithHelper(Expression* obj, + ZoneStringList* labels, + bool is_catch_block, + bool* ok) { + // Parse the statement and collect escaping labels. + ZoneList<BreakTarget*>* target_list = new ZoneList<BreakTarget*>(0); + TargetCollector collector(target_list); + Statement* stat; + { Target target(&this->target_stack_, &collector); + with_nesting_level_++; + top_scope_->RecordWithStatement(); + stat = ParseStatement(labels, CHECK_OK); + with_nesting_level_--; + } + // Create resulting block with two statements. + // 1: Evaluate the with expression. + // 2: The try-finally block evaluating the body. + Block* result = new(zone()) Block(NULL, 2, false); + + if (result != NULL) { + result->AddStatement(new(zone()) WithEnterStatement(obj, is_catch_block)); + + // Create body block. + Block* body = new(zone()) Block(NULL, 1, false); + body->AddStatement(stat); + + // Create exit block. + Block* exit = new(zone()) Block(NULL, 1, false); + exit->AddStatement(new(zone()) WithExitStatement()); + + // Return a try-finally statement. + TryFinallyStatement* wrapper = new(zone()) TryFinallyStatement(body, exit); + wrapper->set_escaping_targets(collector.targets()); + result->AddStatement(wrapper); + } + return result; +} + + +Statement* Parser::ParseWithStatement(ZoneStringList* labels, bool* ok) { + // WithStatement :: + // 'with' '(' Expression ')' Statement + + Expect(Token::WITH, CHECK_OK); + + if (top_scope_->is_strict_mode()) { + ReportMessage("strict_mode_with", Vector<const char*>::empty()); + *ok = false; + return NULL; + } + + Expect(Token::LPAREN, CHECK_OK); + Expression* expr = ParseExpression(true, CHECK_OK); + Expect(Token::RPAREN, CHECK_OK); + + return WithHelper(expr, labels, false, CHECK_OK); +} + + +CaseClause* Parser::ParseCaseClause(bool* default_seen_ptr, bool* ok) { + // CaseClause :: + // 'case' Expression ':' Statement* + // 'default' ':' Statement* + + Expression* label = NULL; // NULL expression indicates default case + if (peek() == Token::CASE) { + Expect(Token::CASE, CHECK_OK); + label = ParseExpression(true, CHECK_OK); + } else { + Expect(Token::DEFAULT, CHECK_OK); + if (*default_seen_ptr) { + ReportMessage("multiple_defaults_in_switch", + Vector<const char*>::empty()); + *ok = false; + return NULL; + } + *default_seen_ptr = true; + } + Expect(Token::COLON, CHECK_OK); + int pos = scanner().location().beg_pos; + ZoneList<Statement*>* statements = new ZoneList<Statement*>(5); + while (peek() != Token::CASE && + peek() != Token::DEFAULT && + peek() != Token::RBRACE) { + Statement* stat = ParseStatement(NULL, CHECK_OK); + statements->Add(stat); + } + + return new(zone()) CaseClause(label, statements, pos); +} + + +SwitchStatement* Parser::ParseSwitchStatement(ZoneStringList* labels, + bool* ok) { + // SwitchStatement :: + // 'switch' '(' Expression ')' '{' CaseClause* '}' + + SwitchStatement* statement = new(zone()) SwitchStatement(labels); + Target target(&this->target_stack_, statement); + + Expect(Token::SWITCH, CHECK_OK); + Expect(Token::LPAREN, CHECK_OK); + Expression* tag = ParseExpression(true, CHECK_OK); + Expect(Token::RPAREN, CHECK_OK); + + bool default_seen = false; + ZoneList<CaseClause*>* cases = new ZoneList<CaseClause*>(4); + Expect(Token::LBRACE, CHECK_OK); + while (peek() != Token::RBRACE) { + CaseClause* clause = ParseCaseClause(&default_seen, CHECK_OK); + cases->Add(clause); + } + Expect(Token::RBRACE, CHECK_OK); + + if (statement) statement->Initialize(tag, cases); + return statement; +} + + +Statement* Parser::ParseThrowStatement(bool* ok) { + // ThrowStatement :: + // 'throw' Expression ';' + + Expect(Token::THROW, CHECK_OK); + int pos = scanner().location().beg_pos; + if (scanner().has_line_terminator_before_next()) { + ReportMessage("newline_after_throw", Vector<const char*>::empty()); + *ok = false; + return NULL; + } + Expression* exception = ParseExpression(true, CHECK_OK); + ExpectSemicolon(CHECK_OK); + + return new(zone()) ExpressionStatement(new(zone()) Throw(exception, pos)); +} + + +TryStatement* Parser::ParseTryStatement(bool* ok) { + // TryStatement :: + // 'try' Block Catch + // 'try' Block Finally + // 'try' Block Catch Finally + // + // Catch :: + // 'catch' '(' Identifier ')' Block + // + // Finally :: + // 'finally' Block + + Expect(Token::TRY, CHECK_OK); + + ZoneList<BreakTarget*>* target_list = new ZoneList<BreakTarget*>(0); + TargetCollector collector(target_list); + Block* try_block; + + { Target target(&this->target_stack_, &collector); + try_block = ParseBlock(NULL, CHECK_OK); + } + + Block* catch_block = NULL; + Variable* catch_var = NULL; + Block* finally_block = NULL; + + Token::Value tok = peek(); + if (tok != Token::CATCH && tok != Token::FINALLY) { + ReportMessage("no_catch_or_finally", Vector<const char*>::empty()); + *ok = false; + return NULL; + } + + // If we can break out from the catch block and there is a finally block, + // then we will need to collect jump targets from the catch block. Since + // we don't know yet if there will be a finally block, we always collect + // the jump targets. + ZoneList<BreakTarget*>* catch_target_list = new ZoneList<BreakTarget*>(0); + TargetCollector catch_collector(catch_target_list); + bool has_catch = false; + if (tok == Token::CATCH) { + has_catch = true; + Consume(Token::CATCH); + + Expect(Token::LPAREN, CHECK_OK); + Handle<String> name = ParseIdentifier(CHECK_OK); + + if (top_scope_->is_strict_mode() && IsEvalOrArguments(name)) { + ReportMessage("strict_catch_variable", Vector<const char*>::empty()); + *ok = false; + return NULL; + } + + Expect(Token::RPAREN, CHECK_OK); + + if (peek() == Token::LBRACE) { + // Allocate a temporary for holding the finally state while + // executing the finally block. + catch_var = + top_scope_->NewTemporary(isolate()->factory()->catch_var_symbol()); + Literal* name_literal = new(zone()) Literal(name); + VariableProxy* catch_var_use = new(zone()) VariableProxy(catch_var); + Expression* obj = + new(zone()) CatchExtensionObject(name_literal, catch_var_use); + { Target target(&this->target_stack_, &catch_collector); + catch_block = WithHelper(obj, NULL, true, CHECK_OK); + } + } else { + Expect(Token::LBRACE, CHECK_OK); + } + + tok = peek(); + } + + if (tok == Token::FINALLY || !has_catch) { + Consume(Token::FINALLY); + // Declare a variable for holding the finally state while + // executing the finally block. + finally_block = ParseBlock(NULL, CHECK_OK); + } + + // Simplify the AST nodes by converting: + // 'try { } catch { } finally { }' + // to: + // 'try { try { } catch { } } finally { }' + + if (catch_block != NULL && finally_block != NULL) { + VariableProxy* catch_var_defn = new(zone()) VariableProxy(catch_var); + TryCatchStatement* statement = + new(zone()) TryCatchStatement(try_block, catch_var_defn, catch_block); + statement->set_escaping_targets(collector.targets()); + try_block = new(zone()) Block(NULL, 1, false); + try_block->AddStatement(statement); + catch_block = NULL; + } + + TryStatement* result = NULL; + if (catch_block != NULL) { + ASSERT(finally_block == NULL); + VariableProxy* catch_var_defn = new(zone()) VariableProxy(catch_var); + result = + new(zone()) TryCatchStatement(try_block, catch_var_defn, catch_block); + result->set_escaping_targets(collector.targets()); + } else { + ASSERT(finally_block != NULL); + result = new(zone()) TryFinallyStatement(try_block, finally_block); + // Add the jump targets of the try block and the catch block. + for (int i = 0; i < collector.targets()->length(); i++) { + catch_collector.AddTarget(collector.targets()->at(i)); + } + result->set_escaping_targets(catch_collector.targets()); + } + + return result; +} + + +DoWhileStatement* Parser::ParseDoWhileStatement(ZoneStringList* labels, + bool* ok) { + // DoStatement :: + // 'do' Statement 'while' '(' Expression ')' ';' + + lexical_scope_->AddLoop(); + DoWhileStatement* loop = new(zone()) DoWhileStatement(labels); + Target target(&this->target_stack_, loop); + + Expect(Token::DO, CHECK_OK); + Statement* body = ParseStatement(NULL, CHECK_OK); + Expect(Token::WHILE, CHECK_OK); + Expect(Token::LPAREN, CHECK_OK); + + if (loop != NULL) { + int position = scanner().location().beg_pos; + loop->set_condition_position(position); + } + + Expression* cond = ParseExpression(true, CHECK_OK); + if (cond != NULL) cond->set_is_loop_condition(true); + Expect(Token::RPAREN, CHECK_OK); + + // Allow do-statements to be terminated with and without + // semi-colons. This allows code such as 'do;while(0)return' to + // parse, which would not be the case if we had used the + // ExpectSemicolon() functionality here. + if (peek() == Token::SEMICOLON) Consume(Token::SEMICOLON); + + if (loop != NULL) loop->Initialize(cond, body); + return loop; +} + + +WhileStatement* Parser::ParseWhileStatement(ZoneStringList* labels, bool* ok) { + // WhileStatement :: + // 'while' '(' Expression ')' Statement + + lexical_scope_->AddLoop(); + WhileStatement* loop = new(zone()) WhileStatement(labels); + Target target(&this->target_stack_, loop); + + Expect(Token::WHILE, CHECK_OK); + Expect(Token::LPAREN, CHECK_OK); + Expression* cond = ParseExpression(true, CHECK_OK); + if (cond != NULL) cond->set_is_loop_condition(true); + Expect(Token::RPAREN, CHECK_OK); + Statement* body = ParseStatement(NULL, CHECK_OK); + + if (loop != NULL) loop->Initialize(cond, body); + return loop; +} + + +Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) { + // ForStatement :: + // 'for' '(' Expression? ';' Expression? ';' Expression? ')' Statement + + lexical_scope_->AddLoop(); + Statement* init = NULL; + + Expect(Token::FOR, CHECK_OK); + Expect(Token::LPAREN, CHECK_OK); + if (peek() != Token::SEMICOLON) { + if (peek() == Token::VAR || peek() == Token::CONST) { + Expression* each = NULL; + Block* variable_statement = + ParseVariableDeclarations(false, &each, CHECK_OK); + if (peek() == Token::IN && each != NULL) { + ForInStatement* loop = new(zone()) ForInStatement(labels); + Target target(&this->target_stack_, loop); + + Expect(Token::IN, CHECK_OK); + Expression* enumerable = ParseExpression(true, CHECK_OK); + Expect(Token::RPAREN, CHECK_OK); + + Statement* body = ParseStatement(NULL, CHECK_OK); + loop->Initialize(each, enumerable, body); + Block* result = new(zone()) Block(NULL, 2, false); + result->AddStatement(variable_statement); + result->AddStatement(loop); + // Parsed for-in loop w/ variable/const declaration. + return result; + } else { + init = variable_statement; + } + + } else { + Expression* expression = ParseExpression(false, CHECK_OK); + if (peek() == Token::IN) { + // Signal a reference error if the expression is an invalid + // left-hand side expression. We could report this as a syntax + // error here but for compatibility with JSC we choose to report + // the error at runtime. + if (expression == NULL || !expression->IsValidLeftHandSide()) { + Handle<String> type = + isolate()->factory()->invalid_lhs_in_for_in_symbol(); + expression = NewThrowReferenceError(type); + } + ForInStatement* loop = new(zone()) ForInStatement(labels); + Target target(&this->target_stack_, loop); + + Expect(Token::IN, CHECK_OK); + Expression* enumerable = ParseExpression(true, CHECK_OK); + Expect(Token::RPAREN, CHECK_OK); + + Statement* body = ParseStatement(NULL, CHECK_OK); + if (loop) loop->Initialize(expression, enumerable, body); + // Parsed for-in loop. + return loop; + + } else { + init = new(zone()) ExpressionStatement(expression); + } + } + } + + // Standard 'for' loop + ForStatement* loop = new(zone()) ForStatement(labels); + Target target(&this->target_stack_, loop); + + // Parsed initializer at this point. + Expect(Token::SEMICOLON, CHECK_OK); + + Expression* cond = NULL; + if (peek() != Token::SEMICOLON) { + cond = ParseExpression(true, CHECK_OK); + if (cond != NULL) cond->set_is_loop_condition(true); + } + Expect(Token::SEMICOLON, CHECK_OK); + + Statement* next = NULL; + if (peek() != Token::RPAREN) { + Expression* exp = ParseExpression(true, CHECK_OK); + next = new(zone()) ExpressionStatement(exp); + } + Expect(Token::RPAREN, CHECK_OK); + + Statement* body = ParseStatement(NULL, CHECK_OK); + if (loop) loop->Initialize(init, cond, next, body); + return loop; +} + + +// Precedence = 1 +Expression* Parser::ParseExpression(bool accept_IN, bool* ok) { + // Expression :: + // AssignmentExpression + // Expression ',' AssignmentExpression + + Expression* result = ParseAssignmentExpression(accept_IN, CHECK_OK); + while (peek() == Token::COMMA) { + Expect(Token::COMMA, CHECK_OK); + int position = scanner().location().beg_pos; + Expression* right = ParseAssignmentExpression(accept_IN, CHECK_OK); + result = new(zone()) BinaryOperation(Token::COMMA, result, right, position); + } + return result; +} + + +// Precedence = 2 +Expression* Parser::ParseAssignmentExpression(bool accept_IN, bool* ok) { + // AssignmentExpression :: + // ConditionalExpression + // LeftHandSideExpression AssignmentOperator AssignmentExpression + + if (fni_ != NULL) fni_->Enter(); + Expression* expression = ParseConditionalExpression(accept_IN, CHECK_OK); + + if (!Token::IsAssignmentOp(peek())) { + if (fni_ != NULL) fni_->Leave(); + // Parsed conditional expression only (no assignment). + return expression; + } + + // Signal a reference error if the expression is an invalid left-hand + // side expression. We could report this as a syntax error here but + // for compatibility with JSC we choose to report the error at + // runtime. + if (expression == NULL || !expression->IsValidLeftHandSide()) { + Handle<String> type = + isolate()->factory()->invalid_lhs_in_assignment_symbol(); + expression = NewThrowReferenceError(type); + } + + if (top_scope_->is_strict_mode()) { + // Assignment to eval or arguments is disallowed in strict mode. + CheckStrictModeLValue(expression, "strict_lhs_assignment", CHECK_OK); + } + + Token::Value op = Next(); // Get assignment operator. + int pos = scanner().location().beg_pos; + Expression* right = ParseAssignmentExpression(accept_IN, CHECK_OK); + + // TODO(1231235): We try to estimate the set of properties set by + // constructors. We define a new property whenever there is an + // assignment to a property of 'this'. We should probably only add + // properties if we haven't seen them before. Otherwise we'll + // probably overestimate the number of properties. + Property* property = expression ? expression->AsProperty() : NULL; + if (op == Token::ASSIGN && + property != NULL && + property->obj()->AsVariableProxy() != NULL && + property->obj()->AsVariableProxy()->is_this()) { + lexical_scope_->AddProperty(); + } + + // If we assign a function literal to a property we pretenure the + // literal so it can be added as a constant function property. + if (property != NULL && right->AsFunctionLiteral() != NULL) { + right->AsFunctionLiteral()->set_pretenure(true); + } + + if (fni_ != NULL) { + // Check if the right hand side is a call to avoid inferring a + // name if we're dealing with "a = function(){...}();"-like + // expression. + if ((op == Token::INIT_VAR + || op == Token::INIT_CONST + || op == Token::ASSIGN) + && (right->AsCall() == NULL)) { + fni_->Infer(); + } + fni_->Leave(); + } + + return new(zone()) Assignment(op, expression, right, pos); +} + + +// Precedence = 3 +Expression* Parser::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() != Token::CONDITIONAL) return expression; + Consume(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. + int left_position = scanner().peek_location().beg_pos; + Expression* left = ParseAssignmentExpression(true, CHECK_OK); + Expect(Token::COLON, CHECK_OK); + int right_position = scanner().peek_location().beg_pos; + Expression* right = ParseAssignmentExpression(accept_IN, CHECK_OK); + return new(zone()) Conditional(expression, left, right, + left_position, right_position); +} + + +static int Precedence(Token::Value tok, bool accept_IN) { + if (tok == Token::IN && !accept_IN) + return 0; // 0 precedence will terminate binary expression parsing + + return Token::Precedence(tok); +} + + +// Precedence >= 4 +Expression* Parser::ParseBinaryExpression(int prec, bool accept_IN, bool* ok) { + ASSERT(prec >= 4); + Expression* x = ParseUnaryExpression(CHECK_OK); + for (int prec1 = Precedence(peek(), accept_IN); prec1 >= prec; prec1--) { + // prec1 >= 4 + while (Precedence(peek(), accept_IN) == prec1) { + Token::Value op = Next(); + int position = scanner().location().beg_pos; + Expression* y = ParseBinaryExpression(prec1 + 1, accept_IN, CHECK_OK); + + // Compute some expressions involving only number literals. + if (x && x->AsLiteral() && x->AsLiteral()->handle()->IsNumber() && + y && y->AsLiteral() && y->AsLiteral()->handle()->IsNumber()) { + double x_val = x->AsLiteral()->handle()->Number(); + double y_val = y->AsLiteral()->handle()->Number(); + + switch (op) { + case Token::ADD: + x = NewNumberLiteral(x_val + y_val); + continue; + case Token::SUB: + x = NewNumberLiteral(x_val - y_val); + continue; + case Token::MUL: + x = NewNumberLiteral(x_val * y_val); + continue; + case Token::DIV: + x = NewNumberLiteral(x_val / y_val); + continue; + case Token::BIT_OR: + x = NewNumberLiteral(DoubleToInt32(x_val) | DoubleToInt32(y_val)); + continue; + case Token::BIT_AND: + x = NewNumberLiteral(DoubleToInt32(x_val) & DoubleToInt32(y_val)); + continue; + case Token::BIT_XOR: + x = NewNumberLiteral(DoubleToInt32(x_val) ^ DoubleToInt32(y_val)); + continue; + case Token::SHL: { + int value = DoubleToInt32(x_val) << (DoubleToInt32(y_val) & 0x1f); + x = NewNumberLiteral(value); + continue; + } + case Token::SHR: { + uint32_t shift = DoubleToInt32(y_val) & 0x1f; + uint32_t value = DoubleToUint32(x_val) >> shift; + x = NewNumberLiteral(value); + continue; + } + case Token::SAR: { + uint32_t shift = DoubleToInt32(y_val) & 0x1f; + int value = ArithmeticShiftRight(DoubleToInt32(x_val), shift); + x = NewNumberLiteral(value); + continue; + } + default: + break; + } + } + + // For now we distinguish between comparisons and other binary + // operations. (We could combine the two and get rid of this + // code and AST node eventually.) + if (Token::IsCompareOp(op)) { + // We have a comparison. + Token::Value cmp = op; + switch (op) { + case Token::NE: cmp = Token::EQ; break; + case Token::NE_STRICT: cmp = Token::EQ_STRICT; break; + default: break; + } + x = NewCompareNode(cmp, x, y, position); + if (cmp != op) { + // The comparison was negated - add a NOT. + x = new(zone()) UnaryOperation(Token::NOT, x); + } + + } else { + // We have a "normal" binary operation. + x = new(zone()) BinaryOperation(op, x, y, position); + } + } + } + return x; +} + + +Expression* Parser::NewCompareNode(Token::Value op, + Expression* x, + Expression* y, + int position) { + ASSERT(op != Token::NE && op != Token::NE_STRICT); + if (op == Token::EQ || op == Token::EQ_STRICT) { + bool is_strict = (op == Token::EQ_STRICT); + Literal* x_literal = x->AsLiteral(); + if (x_literal != NULL && x_literal->IsNull()) { + return new(zone()) CompareToNull(is_strict, y); + } + + Literal* y_literal = y->AsLiteral(); + if (y_literal != NULL && y_literal->IsNull()) { + return new(zone()) CompareToNull(is_strict, x); + } + } + return new(zone()) CompareOperation(op, x, y, position); +} + + +Expression* Parser::ParseUnaryExpression(bool* ok) { + // UnaryExpression :: + // PostfixExpression + // 'delete' UnaryExpression + // 'void' UnaryExpression + // 'typeof' UnaryExpression + // '++' UnaryExpression + // '--' UnaryExpression + // '+' UnaryExpression + // '-' UnaryExpression + // '~' UnaryExpression + // '!' UnaryExpression + + Token::Value op = peek(); + if (Token::IsUnaryOp(op)) { + op = Next(); + Expression* expression = ParseUnaryExpression(CHECK_OK); + + // Compute some expressions involving only number literals. + if (expression != NULL && expression->AsLiteral() && + expression->AsLiteral()->handle()->IsNumber()) { + double value = expression->AsLiteral()->handle()->Number(); + switch (op) { + case Token::ADD: + return expression; + case Token::SUB: + return NewNumberLiteral(-value); + case Token::BIT_NOT: + return NewNumberLiteral(~DoubleToInt32(value)); + default: break; + } + } + + // "delete identifier" is a syntax error in strict mode. + if (op == Token::DELETE && top_scope_->is_strict_mode()) { + VariableProxy* operand = expression->AsVariableProxy(); + if (operand != NULL && !operand->is_this()) { + ReportMessage("strict_delete", Vector<const char*>::empty()); + *ok = false; + return NULL; + } + } + + return new(zone()) UnaryOperation(op, expression); + + } else if (Token::IsCountOp(op)) { + op = Next(); + Expression* expression = ParseUnaryExpression(CHECK_OK); + // Signal a reference error if the expression is an invalid + // left-hand side expression. We could report this as a syntax + // error here but for compatibility with JSC we choose to report the + // error at runtime. + if (expression == NULL || !expression->IsValidLeftHandSide()) { + Handle<String> type = + isolate()->factory()->invalid_lhs_in_prefix_op_symbol(); + expression = NewThrowReferenceError(type); + } + + if (top_scope_->is_strict_mode()) { + // Prefix expression operand in strict mode may not be eval or arguments. + CheckStrictModeLValue(expression, "strict_lhs_prefix", CHECK_OK); + } + + int position = scanner().location().beg_pos; + IncrementOperation* increment = + new(zone()) IncrementOperation(op, expression); + return new(zone()) CountOperation(true /* prefix */, increment, position); + + } else { + return ParsePostfixExpression(ok); + } +} + + +Expression* Parser::ParsePostfixExpression(bool* ok) { + // PostfixExpression :: + // LeftHandSideExpression ('++' | '--')? + + Expression* expression = ParseLeftHandSideExpression(CHECK_OK); + if (!scanner().has_line_terminator_before_next() && + Token::IsCountOp(peek())) { + // Signal a reference error if the expression is an invalid + // left-hand side expression. We could report this as a syntax + // error here but for compatibility with JSC we choose to report the + // error at runtime. + if (expression == NULL || !expression->IsValidLeftHandSide()) { + Handle<String> type = + isolate()->factory()->invalid_lhs_in_postfix_op_symbol(); + expression = NewThrowReferenceError(type); + } + + if (top_scope_->is_strict_mode()) { + // Postfix expression operand in strict mode may not be eval or arguments. + CheckStrictModeLValue(expression, "strict_lhs_prefix", CHECK_OK); + } + + Token::Value next = Next(); + int position = scanner().location().beg_pos; + IncrementOperation* increment = + new(zone()) IncrementOperation(next, expression); + expression = + new(zone()) CountOperation(false /* postfix */, increment, position); + } + return expression; +} + + +Expression* Parser::ParseLeftHandSideExpression(bool* ok) { + // LeftHandSideExpression :: + // (NewExpression | MemberExpression) ... + + Expression* result; + if (peek() == Token::NEW) { + result = ParseNewExpression(CHECK_OK); + } else { + result = ParseMemberExpression(CHECK_OK); + } + + while (true) { + switch (peek()) { + case Token::LBRACK: { + Consume(Token::LBRACK); + int pos = scanner().location().beg_pos; + Expression* index = ParseExpression(true, CHECK_OK); + result = new(zone()) Property(result, index, pos); + Expect(Token::RBRACK, CHECK_OK); + break; + } + + case Token::LPAREN: { + int pos = scanner().location().beg_pos; + ZoneList<Expression*>* args = ParseArguments(CHECK_OK); + + // Keep track of eval() calls since they disable all local variable + // optimizations. + // The calls that need special treatment are the + // direct (i.e. not aliased) eval calls. These calls are all of the + // form eval(...) with no explicit receiver object where eval is not + // declared in the current scope chain. + // These calls are marked as potentially direct eval calls. Whether + // they are actually direct calls to eval is determined at run time. + // TODO(994): In ES5, it doesn't matter if the "eval" var is declared + // in the local scope chain. It only matters that it's called "eval", + // is called without a receiver and it refers to the original eval + // function. + VariableProxy* callee = result->AsVariableProxy(); + if (callee != NULL && + callee->IsVariable(isolate()->factory()->eval_symbol())) { + Handle<String> name = callee->name(); + Variable* var = top_scope_->Lookup(name); + if (var == NULL) { + top_scope_->RecordEvalCall(); + } + } + result = NewCall(result, args, pos); + break; + } + + case Token::PERIOD: { + Consume(Token::PERIOD); + int pos = scanner().location().beg_pos; + Handle<String> name = ParseIdentifierName(CHECK_OK); + result = new(zone()) Property(result, new(zone()) Literal(name), pos); + if (fni_ != NULL) fni_->PushLiteralName(name); + break; + } + + default: + return result; + } + } +} + + +Expression* Parser::ParseNewPrefix(PositionStack* stack, 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 + Expect(Token::NEW, CHECK_OK); + PositionStack::Element pos(stack, scanner().location().beg_pos); + + Expression* result; + if (peek() == Token::NEW) { + result = ParseNewPrefix(stack, CHECK_OK); + } else { + result = ParseMemberWithNewPrefixesExpression(stack, CHECK_OK); + } + + if (!stack->is_empty()) { + int last = stack->pop(); + result = new(zone()) CallNew(result, new ZoneList<Expression*>(0), last); + } + return result; +} + + +Expression* Parser::ParseNewExpression(bool* ok) { + PositionStack stack(ok); + return ParseNewPrefix(&stack, ok); +} + + +Expression* Parser::ParseMemberExpression(bool* ok) { + return ParseMemberWithNewPrefixesExpression(NULL, ok); +} + + +Expression* Parser::ParseMemberWithNewPrefixesExpression(PositionStack* stack, + bool* ok) { + // MemberExpression :: + // (PrimaryExpression | FunctionLiteral) + // ('[' Expression ']' | '.' Identifier | Arguments)* + + // Parse the initial primary or function expression. + Expression* result = NULL; + if (peek() == Token::FUNCTION) { + Expect(Token::FUNCTION, CHECK_OK); + int function_token_position = scanner().location().beg_pos; + Handle<String> name; + bool is_reserved_name = false; + if (peek_any_identifier()) { + name = ParseIdentifierOrReservedWord(&is_reserved_name, CHECK_OK); + } + result = ParseFunctionLiteral(name, is_reserved_name, + function_token_position, NESTED, CHECK_OK); + } else { + result = ParsePrimaryExpression(CHECK_OK); + } + + while (true) { + switch (peek()) { + case Token::LBRACK: { + Consume(Token::LBRACK); + int pos = scanner().location().beg_pos; + Expression* index = ParseExpression(true, CHECK_OK); + result = new(zone()) Property(result, index, pos); + Expect(Token::RBRACK, CHECK_OK); + break; + } + case Token::PERIOD: { + Consume(Token::PERIOD); + int pos = scanner().location().beg_pos; + Handle<String> name = ParseIdentifierName(CHECK_OK); + result = new(zone()) Property(result, new(zone()) Literal(name), pos); + if (fni_ != NULL) fni_->PushLiteralName(name); + break; + } + case Token::LPAREN: { + if ((stack == NULL) || stack->is_empty()) return result; + // Consume one of the new prefixes (already parsed). + ZoneList<Expression*>* args = ParseArguments(CHECK_OK); + int last = stack->pop(); + result = new CallNew(result, args, last); + break; + } + default: + return result; + } + } +} + + +DebuggerStatement* Parser::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 i a + // break point is present. + // DebuggerStatement :: + // 'debugger' ';' + + Expect(Token::DEBUGGER, CHECK_OK); + ExpectSemicolon(CHECK_OK); + return new(zone()) DebuggerStatement(); +} + + +void Parser::ReportUnexpectedToken(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/ParseJson. + if (token == Token::ILLEGAL && stack_overflow_) return; + // Four of the tokens are treated specially + switch (token) { + case Token::EOS: + return ReportMessage("unexpected_eos", Vector<const char*>::empty()); + case Token::NUMBER: + return ReportMessage("unexpected_token_number", + Vector<const char*>::empty()); + case Token::STRING: + return ReportMessage("unexpected_token_string", + Vector<const char*>::empty()); + case Token::IDENTIFIER: + return ReportMessage("unexpected_token_identifier", + Vector<const char*>::empty()); + case Token::FUTURE_RESERVED_WORD: + return ReportMessage(top_scope_->is_strict_mode() ? + "unexpected_strict_reserved" : + "unexpected_token_identifier", + Vector<const char*>::empty()); + default: + const char* name = Token::String(token); + ASSERT(name != NULL); + ReportMessage("unexpected_token", Vector<const char*>(&name, 1)); + } +} + + +void Parser::ReportInvalidPreparseData(Handle<String> name, bool* ok) { + SmartPointer<char> name_string = name->ToCString(DISALLOW_NULLS); + const char* element[1] = { *name_string }; + ReportMessage("invalid_preparser_data", + Vector<const char*>(element, 1)); + *ok = false; +} + + +Expression* Parser::ParsePrimaryExpression(bool* ok) { + // PrimaryExpression :: + // 'this' + // 'null' + // 'true' + // 'false' + // Identifier + // Number + // String + // ArrayLiteral + // ObjectLiteral + // RegExpLiteral + // '(' Expression ')' + + Expression* result = NULL; + switch (peek()) { + case Token::THIS: { + Consume(Token::THIS); + VariableProxy* recv = top_scope_->receiver(); + result = recv; + break; + } + + case Token::NULL_LITERAL: + Consume(Token::NULL_LITERAL); + result = new(zone()) Literal(isolate()->factory()->null_value()); + break; + + case Token::TRUE_LITERAL: + Consume(Token::TRUE_LITERAL); + result = new(zone()) Literal(isolate()->factory()->true_value()); + break; + + case Token::FALSE_LITERAL: + Consume(Token::FALSE_LITERAL); + result = new(zone()) Literal(isolate()->factory()->false_value()); + break; + + case Token::IDENTIFIER: + case Token::FUTURE_RESERVED_WORD: { + Handle<String> name = ParseIdentifier(CHECK_OK); + if (fni_ != NULL) fni_->PushVariableName(name); + result = top_scope_->NewUnresolved(name, + inside_with(), + scanner().location().beg_pos); + break; + } + + case Token::NUMBER: { + Consume(Token::NUMBER); + ASSERT(scanner().is_literal_ascii()); + double value = StringToDouble(scanner().literal_ascii_string(), + ALLOW_HEX | ALLOW_OCTALS); + result = NewNumberLiteral(value); + break; + } + + case Token::STRING: { + Consume(Token::STRING); + Handle<String> symbol = GetSymbol(CHECK_OK); + result = new(zone()) Literal(symbol); + if (fni_ != NULL) fni_->PushLiteralName(symbol); + break; + } + + case Token::ASSIGN_DIV: + result = ParseRegExpLiteral(true, CHECK_OK); + break; + + case Token::DIV: + result = ParseRegExpLiteral(false, CHECK_OK); + break; + + case Token::LBRACK: + result = ParseArrayLiteral(CHECK_OK); + break; + + case Token::LBRACE: + result = ParseObjectLiteral(CHECK_OK); + break; + + case Token::LPAREN: + Consume(Token::LPAREN); + // Heuristically try to detect immediately called functions before + // seeing the call parentheses. + parenthesized_function_ = (peek() == Token::FUNCTION); + result = ParseExpression(true, CHECK_OK); + Expect(Token::RPAREN, CHECK_OK); + break; + + case Token::MOD: + if (allow_natives_syntax_ || extension_ != NULL) { + result = ParseV8Intrinsic(CHECK_OK); + break; + } + // If we're not allowing special syntax we fall-through to the + // default case. + + default: { + Token::Value tok = Next(); + ReportUnexpectedToken(tok); + *ok = false; + return NULL; + } + } + + return result; +} + + +void Parser::BuildArrayLiteralBoilerplateLiterals(ZoneList<Expression*>* values, + Handle<FixedArray> literals, + bool* is_simple, + int* depth) { + // Fill in the literals. + // Accumulate output values in local variables. + bool is_simple_acc = true; + int depth_acc = 1; + for (int i = 0; i < values->length(); i++) { + MaterializedLiteral* m_literal = values->at(i)->AsMaterializedLiteral(); + if (m_literal != NULL && m_literal->depth() >= depth_acc) { + depth_acc = m_literal->depth() + 1; + } + Handle<Object> boilerplate_value = GetBoilerplateValue(values->at(i)); + if (boilerplate_value->IsUndefined()) { + literals->set_the_hole(i); + is_simple_acc = false; + } else { + literals->set(i, *boilerplate_value); + } + } + + *is_simple = is_simple_acc; + *depth = depth_acc; +} + + +Expression* Parser::ParseArrayLiteral(bool* ok) { + // ArrayLiteral :: + // '[' Expression? (',' Expression?)* ']' + + ZoneList<Expression*>* values = new ZoneList<Expression*>(4); + Expect(Token::LBRACK, CHECK_OK); + while (peek() != Token::RBRACK) { + Expression* elem; + if (peek() == Token::COMMA) { + elem = GetLiteralTheHole(); + } else { + elem = ParseAssignmentExpression(true, CHECK_OK); + } + values->Add(elem); + if (peek() != Token::RBRACK) { + Expect(Token::COMMA, CHECK_OK); + } + } + Expect(Token::RBRACK, CHECK_OK); + + // Update the scope information before the pre-parsing bailout. + int literal_index = lexical_scope_->NextMaterializedLiteralIndex(); + + // Allocate a fixed array with all the literals. + Handle<FixedArray> literals = + isolate()->factory()->NewFixedArray(values->length(), TENURED); + + // Fill in the literals. + bool is_simple = true; + int depth = 1; + for (int i = 0, n = values->length(); i < n; i++) { + MaterializedLiteral* m_literal = values->at(i)->AsMaterializedLiteral(); + if (m_literal != NULL && m_literal->depth() + 1 > depth) { + depth = m_literal->depth() + 1; + } + Handle<Object> boilerplate_value = GetBoilerplateValue(values->at(i)); + if (boilerplate_value->IsUndefined()) { + literals->set_the_hole(i); + is_simple = false; + } else { + literals->set(i, *boilerplate_value); + } + } + + // Simple and shallow arrays can be lazily copied, we transform the + // elements array to a copy-on-write array. + if (is_simple && depth == 1 && values->length() > 0) { + literals->set_map(isolate()->heap()->fixed_cow_array_map()); + } + + return new(zone()) ArrayLiteral(literals, values, + literal_index, is_simple, depth); +} + + +bool Parser::IsBoilerplateProperty(ObjectLiteral::Property* property) { + return property != NULL && + property->kind() != ObjectLiteral::Property::PROTOTYPE; +} + + +bool CompileTimeValue::IsCompileTimeValue(Expression* expression) { + if (expression->AsLiteral() != NULL) return true; + MaterializedLiteral* lit = expression->AsMaterializedLiteral(); + return lit != NULL && lit->is_simple(); +} + + +bool CompileTimeValue::ArrayLiteralElementNeedsInitialization( + Expression* value) { + // If value is a literal the property value is already set in the + // boilerplate object. + if (value->AsLiteral() != NULL) return false; + // If value is a materialized literal the property value is already set + // in the boilerplate object if it is simple. + if (CompileTimeValue::IsCompileTimeValue(value)) return false; + return true; +} + + +Handle<FixedArray> CompileTimeValue::GetValue(Expression* expression) { + ASSERT(IsCompileTimeValue(expression)); + Handle<FixedArray> result = FACTORY->NewFixedArray(2, TENURED); + ObjectLiteral* object_literal = expression->AsObjectLiteral(); + if (object_literal != NULL) { + ASSERT(object_literal->is_simple()); + if (object_literal->fast_elements()) { + result->set(kTypeSlot, Smi::FromInt(OBJECT_LITERAL_FAST_ELEMENTS)); + } else { + result->set(kTypeSlot, Smi::FromInt(OBJECT_LITERAL_SLOW_ELEMENTS)); + } + result->set(kElementsSlot, *object_literal->constant_properties()); + } else { + ArrayLiteral* array_literal = expression->AsArrayLiteral(); + ASSERT(array_literal != NULL && array_literal->is_simple()); + result->set(kTypeSlot, Smi::FromInt(ARRAY_LITERAL)); + result->set(kElementsSlot, *array_literal->constant_elements()); + } + return result; +} + + +CompileTimeValue::Type CompileTimeValue::GetType(Handle<FixedArray> value) { + Smi* type_value = Smi::cast(value->get(kTypeSlot)); + return static_cast<Type>(type_value->value()); +} + + +Handle<FixedArray> CompileTimeValue::GetElements(Handle<FixedArray> value) { + return Handle<FixedArray>(FixedArray::cast(value->get(kElementsSlot))); +} + + +Handle<Object> Parser::GetBoilerplateValue(Expression* expression) { + if (expression->AsLiteral() != NULL) { + return expression->AsLiteral()->handle(); + } + if (CompileTimeValue::IsCompileTimeValue(expression)) { + return CompileTimeValue::GetValue(expression); + } + return isolate()->factory()->undefined_value(); +} + +// Defined in ast.cc +bool IsEqualString(void* first, void* second); +bool IsEqualNumber(void* first, void* second); + + +// Validation per 11.1.5 Object Initialiser +class ObjectLiteralPropertyChecker { + public: + ObjectLiteralPropertyChecker(Parser* parser, bool strict) : + props(&IsEqualString), + elems(&IsEqualNumber), + parser_(parser), + strict_(strict) { + } + + void CheckProperty( + ObjectLiteral::Property* property, + Scanner::Location loc, + bool* ok); + + private: + enum PropertyKind { + kGetAccessor = 0x01, + kSetAccessor = 0x02, + kAccessor = kGetAccessor | kSetAccessor, + kData = 0x04 + }; + + static intptr_t GetPropertyKind(ObjectLiteral::Property* property) { + switch (property->kind()) { + case ObjectLiteral::Property::GETTER: + return kGetAccessor; + case ObjectLiteral::Property::SETTER: + return kSetAccessor; + default: + return kData; + } + } + + HashMap props; + HashMap elems; + Parser* parser_; + bool strict_; +}; + + +void ObjectLiteralPropertyChecker::CheckProperty( + ObjectLiteral::Property* property, + Scanner::Location loc, + bool* ok) { + + ASSERT(property != NULL); + + Literal *lit = property->key(); + Handle<Object> handle = lit->handle(); + + uint32_t hash; + HashMap* map; + void* key; + + if (handle->IsSymbol()) { + Handle<String> name(String::cast(*handle)); + if (name->AsArrayIndex(&hash)) { + Handle<Object> key_handle = FACTORY->NewNumberFromUint(hash); + key = key_handle.location(); + map = &elems; + } else { + key = handle.location(); + hash = name->Hash(); + map = &props; + } + } else if (handle->ToArrayIndex(&hash)) { + key = handle.location(); + map = &elems; + } else { + ASSERT(handle->IsNumber()); + double num = handle->Number(); + char arr[100]; + Vector<char> buffer(arr, ARRAY_SIZE(arr)); + const char* str = DoubleToCString(num, buffer); + Handle<String> name = FACTORY->NewStringFromAscii(CStrVector(str)); + key = name.location(); + hash = name->Hash(); + map = &props; + } + + // Lookup property previously defined, if any. + HashMap::Entry* entry = map->Lookup(key, hash, true); + intptr_t prev = reinterpret_cast<intptr_t> (entry->value); + intptr_t curr = GetPropertyKind(property); + + // Duplicate data properties are illegal in strict mode. + if (strict_ && (curr & prev & kData) != 0) { + parser_->ReportMessageAt(loc, "strict_duplicate_property", + Vector<const char*>::empty()); + *ok = false; + return; + } + // Data property conflicting with an accessor. + if (((curr & kData) && (prev & kAccessor)) || + ((prev & kData) && (curr & kAccessor))) { + parser_->ReportMessageAt(loc, "accessor_data_property", + Vector<const char*>::empty()); + *ok = false; + return; + } + // Two accessors of the same type conflicting + if ((curr & prev & kAccessor) != 0) { + parser_->ReportMessageAt(loc, "accessor_get_set", + Vector<const char*>::empty()); + *ok = false; + return; + } + + // Update map + entry->value = reinterpret_cast<void*> (prev | curr); + *ok = true; +} + + +void Parser::BuildObjectLiteralConstantProperties( + ZoneList<ObjectLiteral::Property*>* properties, + Handle<FixedArray> constant_properties, + bool* is_simple, + bool* fast_elements, + int* depth) { + int position = 0; + // Accumulate the value in local variables and store it at the end. + bool is_simple_acc = true; + int depth_acc = 1; + uint32_t max_element_index = 0; + uint32_t elements = 0; + for (int i = 0; i < properties->length(); i++) { + ObjectLiteral::Property* property = properties->at(i); + if (!IsBoilerplateProperty(property)) { + is_simple_acc = false; + continue; + } + MaterializedLiteral* m_literal = property->value()->AsMaterializedLiteral(); + if (m_literal != NULL && m_literal->depth() >= depth_acc) { + depth_acc = m_literal->depth() + 1; + } + + // Add CONSTANT and COMPUTED properties to boilerplate. Use undefined + // value for COMPUTED properties, the real value is filled in at + // runtime. The enumeration order is maintained. + Handle<Object> key = property->key()->handle(); + Handle<Object> value = GetBoilerplateValue(property->value()); + is_simple_acc = is_simple_acc && !value->IsUndefined(); + + // Keep track of the number of elements in the object literal and + // the largest element index. If the largest element index is + // much larger than the number of elements, creating an object + // literal with fast elements will be a waste of space. + uint32_t element_index = 0; + if (key->IsString() + && Handle<String>::cast(key)->AsArrayIndex(&element_index) + && element_index > max_element_index) { + max_element_index = element_index; + elements++; + } else if (key->IsSmi()) { + int key_value = Smi::cast(*key)->value(); + if (key_value > 0 + && static_cast<uint32_t>(key_value) > max_element_index) { + max_element_index = key_value; + } + elements++; + } + + // Add name, value pair to the fixed array. + constant_properties->set(position++, *key); + constant_properties->set(position++, *value); + } + *fast_elements = + (max_element_index <= 32) || ((2 * elements) >= max_element_index); + *is_simple = is_simple_acc; + *depth = depth_acc; +} + + +ObjectLiteral::Property* Parser::ParseObjectLiteralGetSet(bool is_getter, + bool* ok) { + // Special handling of getter and setter syntax: + // { ... , get foo() { ... }, ... , set foo(v) { ... v ... } , ... } + // We have already read the "get" or "set" keyword. + Token::Value next = Next(); + bool is_keyword = Token::IsKeyword(next); + if (next == Token::IDENTIFIER || next == Token::NUMBER || + next == Token::FUTURE_RESERVED_WORD || + next == Token::STRING || is_keyword) { + Handle<String> name; + if (is_keyword) { + name = isolate_->factory()->LookupAsciiSymbol(Token::String(next)); + } else { + name = GetSymbol(CHECK_OK); + } + FunctionLiteral* value = + ParseFunctionLiteral(name, + false, // reserved words are allowed here + RelocInfo::kNoPosition, + DECLARATION, + CHECK_OK); + // Allow any number of parameters for compatiabilty with JSC. + // Specification only allows zero parameters for get and one for set. + ObjectLiteral::Property* property = + new(zone()) ObjectLiteral::Property(is_getter, value); + return property; + } else { + ReportUnexpectedToken(next); + *ok = false; + return NULL; + } +} + + +Expression* Parser::ParseObjectLiteral(bool* ok) { + // ObjectLiteral :: + // '{' ( + // ((IdentifierName | String | Number) ':' AssignmentExpression) + // | (('get' | 'set') (IdentifierName | String | Number) FunctionLiteral) + // )*[','] '}' + + ZoneList<ObjectLiteral::Property*>* properties = + new ZoneList<ObjectLiteral::Property*>(4); + int number_of_boilerplate_properties = 0; + bool has_function = false; + + ObjectLiteralPropertyChecker checker(this, top_scope_->is_strict_mode()); + + Expect(Token::LBRACE, CHECK_OK); + Scanner::Location loc = scanner().location(); + + while (peek() != Token::RBRACE) { + if (fni_ != NULL) fni_->Enter(); + + Literal* key = NULL; + Token::Value next = peek(); + + // Location of the property name token + Scanner::Location loc = scanner().peek_location(); + + switch (next) { + case Token::FUTURE_RESERVED_WORD: + case Token::IDENTIFIER: { + bool is_getter = false; + bool is_setter = false; + Handle<String> id = + ParseIdentifierOrGetOrSet(&is_getter, &is_setter, CHECK_OK); + if (fni_ != NULL) fni_->PushLiteralName(id); + + if ((is_getter || is_setter) && peek() != Token::COLON) { + // Update loc to point to the identifier + loc = scanner().peek_location(); + ObjectLiteral::Property* property = + ParseObjectLiteralGetSet(is_getter, CHECK_OK); + if (IsBoilerplateProperty(property)) { + number_of_boilerplate_properties++; + } + // Validate the property. + checker.CheckProperty(property, loc, CHECK_OK); + properties->Add(property); + if (peek() != Token::RBRACE) Expect(Token::COMMA, CHECK_OK); + + if (fni_ != NULL) { + fni_->Infer(); + fni_->Leave(); + } + continue; // restart the while + } + // Failed to parse as get/set property, so it's just a property + // called "get" or "set". + key = new(zone()) Literal(id); + break; + } + case Token::STRING: { + Consume(Token::STRING); + Handle<String> string = GetSymbol(CHECK_OK); + if (fni_ != NULL) fni_->PushLiteralName(string); + uint32_t index; + if (!string.is_null() && string->AsArrayIndex(&index)) { + key = NewNumberLiteral(index); + break; + } + key = new(zone()) Literal(string); + break; + } + case Token::NUMBER: { + Consume(Token::NUMBER); + ASSERT(scanner().is_literal_ascii()); + double value = StringToDouble(scanner().literal_ascii_string(), + ALLOW_HEX | ALLOW_OCTALS); + key = NewNumberLiteral(value); + break; + } + default: + if (Token::IsKeyword(next)) { + Consume(next); + Handle<String> string = GetSymbol(CHECK_OK); + key = new(zone()) Literal(string); + } else { + // Unexpected token. + Token::Value next = Next(); + ReportUnexpectedToken(next); + *ok = false; + return NULL; + } + } + + Expect(Token::COLON, CHECK_OK); + Expression* value = ParseAssignmentExpression(true, CHECK_OK); + + ObjectLiteral::Property* property = + new(zone()) ObjectLiteral::Property(key, value); + + // Mark object literals that contain function literals and pretenure the + // literal so it can be added as a constant function property. + if (value->AsFunctionLiteral() != NULL) { + has_function = true; + value->AsFunctionLiteral()->set_pretenure(true); + } + + // Count CONSTANT or COMPUTED properties to maintain the enumeration order. + if (IsBoilerplateProperty(property)) number_of_boilerplate_properties++; + // Validate the property + checker.CheckProperty(property, loc, CHECK_OK); + properties->Add(property); + + // TODO(1240767): Consider allowing trailing comma. + if (peek() != Token::RBRACE) Expect(Token::COMMA, CHECK_OK); + + if (fni_ != NULL) { + fni_->Infer(); + fni_->Leave(); + } + } + Expect(Token::RBRACE, CHECK_OK); + + // Computation of literal_index must happen before pre parse bailout. + int literal_index = lexical_scope_->NextMaterializedLiteralIndex(); + + Handle<FixedArray> constant_properties = isolate()->factory()->NewFixedArray( + number_of_boilerplate_properties * 2, TENURED); + + bool is_simple = true; + bool fast_elements = true; + int depth = 1; + BuildObjectLiteralConstantProperties(properties, + constant_properties, + &is_simple, + &fast_elements, + &depth); + return new(zone()) ObjectLiteral(constant_properties, + properties, + literal_index, + is_simple, + fast_elements, + depth, + has_function); +} + + +Expression* Parser::ParseRegExpLiteral(bool seen_equal, bool* ok) { + if (!scanner().ScanRegExpPattern(seen_equal)) { + Next(); + ReportMessage("unterminated_regexp", Vector<const char*>::empty()); + *ok = false; + return NULL; + } + + int literal_index = lexical_scope_->NextMaterializedLiteralIndex(); + + Handle<String> js_pattern = NextLiteralString(TENURED); + scanner().ScanRegExpFlags(); + Handle<String> js_flags = NextLiteralString(TENURED); + Next(); + + return new(zone()) RegExpLiteral(js_pattern, js_flags, literal_index); +} + + +ZoneList<Expression*>* Parser::ParseArguments(bool* ok) { + // Arguments :: + // '(' (AssignmentExpression)*[','] ')' + + ZoneList<Expression*>* result = new ZoneList<Expression*>(4); + Expect(Token::LPAREN, CHECK_OK); + bool done = (peek() == Token::RPAREN); + while (!done) { + Expression* argument = ParseAssignmentExpression(true, CHECK_OK); + result->Add(argument); + done = (peek() == Token::RPAREN); + if (!done) Expect(Token::COMMA, CHECK_OK); + } + Expect(Token::RPAREN, CHECK_OK); + return result; +} + + +FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name, + bool name_is_reserved, + int function_token_position, + FunctionLiteralType type, + bool* ok) { + // Function :: + // '(' FormalParameterList? ')' '{' FunctionBody '}' + bool is_named = !var_name.is_null(); + + // The name associated with this function. If it's a function expression, + // this is the actual function name, otherwise this is the name of the + // variable declared and initialized with the function (expression). In + // that case, we don't have a function name (it's empty). + Handle<String> name = + is_named ? var_name : isolate()->factory()->empty_symbol(); + // The function name, if any. + Handle<String> function_name = isolate()->factory()->empty_symbol(); + if (is_named && (type == EXPRESSION || type == NESTED)) { + function_name = name; + } + + int num_parameters = 0; + // Parse function body. + { Scope* scope = + NewScope(top_scope_, Scope::FUNCTION_SCOPE, inside_with()); + LexicalScope lexical_scope(this, scope, isolate()); + top_scope_->SetScopeName(name); + + // FormalParameterList :: + // '(' (Identifier)*[','] ')' + Expect(Token::LPAREN, CHECK_OK); + int start_pos = scanner().location().beg_pos; + Scanner::Location name_loc = Scanner::NoLocation(); + Scanner::Location dupe_loc = Scanner::NoLocation(); + Scanner::Location reserved_loc = Scanner::NoLocation(); + + bool done = (peek() == Token::RPAREN); + while (!done) { + bool is_reserved = false; + Handle<String> param_name = + ParseIdentifierOrReservedWord(&is_reserved, CHECK_OK); + + // Store locations for possible future error reports. + if (!name_loc.IsValid() && IsEvalOrArguments(param_name)) { + name_loc = scanner().location(); + } + if (!dupe_loc.IsValid() && top_scope_->IsDeclared(param_name)) { + dupe_loc = scanner().location(); + } + if (!reserved_loc.IsValid() && is_reserved) { + reserved_loc = scanner().location(); + } + + Variable* parameter = top_scope_->DeclareLocal(param_name, Variable::VAR); + top_scope_->AddParameter(parameter); + num_parameters++; + if (num_parameters > kMaxNumFunctionParameters) { + ReportMessageAt(scanner().location(), "too_many_parameters", + Vector<const char*>::empty()); + *ok = false; + return NULL; + } + done = (peek() == Token::RPAREN); + if (!done) Expect(Token::COMMA, CHECK_OK); + } + Expect(Token::RPAREN, CHECK_OK); + + Expect(Token::LBRACE, CHECK_OK); + ZoneList<Statement*>* body = new ZoneList<Statement*>(8); + + // If we have a named function expression, we add a local variable + // declaration to the body of the function with the name of the + // function and let it refer to the function itself (closure). + // NOTE: We create a proxy and resolve it here so that in the + // future we can change the AST to only refer to VariableProxies + // instead of Variables and Proxis as is the case now. + if (!function_name.is_null() && function_name->length() > 0) { + Variable* fvar = top_scope_->DeclareFunctionVar(function_name); + VariableProxy* fproxy = + top_scope_->NewUnresolved(function_name, inside_with()); + fproxy->BindTo(fvar); + body->Add(new(zone()) ExpressionStatement( + new(zone()) Assignment(Token::INIT_CONST, fproxy, + new(zone()) ThisFunction(), + RelocInfo::kNoPosition))); + } + + // Determine if the function will be lazily compiled. The mode can + // only be PARSE_LAZILY if the --lazy flag is true. + bool is_lazily_compiled = (mode() == PARSE_LAZILY && + top_scope_->outer_scope()->is_global_scope() && + top_scope_->HasTrivialOuterContext() && + !parenthesized_function_); + parenthesized_function_ = false; // The bit was set for this function only. + + int function_block_pos = scanner().location().beg_pos; + int materialized_literal_count; + int expected_property_count; + int end_pos; + bool only_simple_this_property_assignments; + Handle<FixedArray> this_property_assignments; + if (is_lazily_compiled && pre_data() != NULL) { + FunctionEntry entry = pre_data()->GetFunctionEntry(function_block_pos); + if (!entry.is_valid()) { + ReportInvalidPreparseData(name, CHECK_OK); + } + end_pos = entry.end_pos(); + if (end_pos <= function_block_pos) { + // End position greater than end of stream is safe, and hard to check. + ReportInvalidPreparseData(name, CHECK_OK); + } + isolate()->counters()->total_preparse_skipped()->Increment( + end_pos - function_block_pos); + // Seek to position just before terminal '}'. + scanner().SeekForward(end_pos - 1); + materialized_literal_count = entry.literal_count(); + expected_property_count = entry.property_count(); + only_simple_this_property_assignments = false; + this_property_assignments = isolate()->factory()->empty_fixed_array(); + Expect(Token::RBRACE, CHECK_OK); + } else { + ParseSourceElements(body, Token::RBRACE, CHECK_OK); + + materialized_literal_count = lexical_scope.materialized_literal_count(); + expected_property_count = lexical_scope.expected_property_count(); + only_simple_this_property_assignments = + lexical_scope.only_simple_this_property_assignments(); + this_property_assignments = lexical_scope.this_property_assignments(); + + Expect(Token::RBRACE, CHECK_OK); + end_pos = scanner().location().end_pos; + } + + // Validate strict mode. + if (top_scope_->is_strict_mode()) { + if (IsEvalOrArguments(name)) { + int position = function_token_position != RelocInfo::kNoPosition + ? function_token_position + : (start_pos > 0 ? start_pos - 1 : start_pos); + Scanner::Location location = Scanner::Location(position, start_pos); + ReportMessageAt(location, + "strict_function_name", Vector<const char*>::empty()); + *ok = false; + return NULL; + } + if (name_loc.IsValid()) { + ReportMessageAt(name_loc, "strict_param_name", + Vector<const char*>::empty()); + *ok = false; + return NULL; + } + if (dupe_loc.IsValid()) { + ReportMessageAt(dupe_loc, "strict_param_dupe", + Vector<const char*>::empty()); + *ok = false; + return NULL; + } + if (name_is_reserved) { + int position = function_token_position != RelocInfo::kNoPosition + ? function_token_position + : (start_pos > 0 ? start_pos - 1 : start_pos); + Scanner::Location location = Scanner::Location(position, start_pos); + ReportMessageAt(location, "strict_reserved_word", + Vector<const char*>::empty()); + *ok = false; + return NULL; + } + if (reserved_loc.IsValid()) { + ReportMessageAt(reserved_loc, "strict_reserved_word", + Vector<const char*>::empty()); + *ok = false; + return NULL; + } + CheckOctalLiteral(start_pos, end_pos, CHECK_OK); + } + + FunctionLiteral* function_literal = + new(zone()) FunctionLiteral(name, + top_scope_, + body, + materialized_literal_count, + expected_property_count, + only_simple_this_property_assignments, + this_property_assignments, + num_parameters, + start_pos, + end_pos, + function_name->length() > 0, + lexical_scope.ContainsLoops()); + function_literal->set_function_token_position(function_token_position); + + if (fni_ != NULL && !is_named) fni_->AddFunction(function_literal); + return function_literal; + } +} + + +Expression* Parser::ParseV8Intrinsic(bool* ok) { + // CallRuntime :: + // '%' Identifier Arguments + + Expect(Token::MOD, CHECK_OK); + Handle<String> name = ParseIdentifier(CHECK_OK); + ZoneList<Expression*>* args = ParseArguments(CHECK_OK); + + if (extension_ != NULL) { + // The extension structures are only accessible while parsing the + // very first time not when reparsing because of lazy compilation. + top_scope_->ForceEagerCompilation(); + } + + const Runtime::Function* function = Runtime::FunctionForSymbol(name); + + // Check for built-in IS_VAR macro. + if (function != NULL && + function->intrinsic_type == Runtime::RUNTIME && + function->function_id == Runtime::kIS_VAR) { + // %IS_VAR(x) evaluates to x if x is a variable, + // leads to a parse error otherwise. Could be implemented as an + // inline function %_IS_VAR(x) to eliminate this special case. + if (args->length() == 1 && args->at(0)->AsVariableProxy() != NULL) { + return args->at(0); + } else { + ReportMessage("unable_to_parse", Vector<const char*>::empty()); + *ok = false; + return NULL; + } + } + + // Check that the expected number of arguments are being passed. + if (function != NULL && + function->nargs != -1 && + function->nargs != args->length()) { + ReportMessage("illegal_access", Vector<const char*>::empty()); + *ok = false; + return NULL; + } + + // We have a valid intrinsics call or a call to a builtin. + return new(zone()) CallRuntime(name, function, args); +} + + +bool Parser::peek_any_identifier() { + Token::Value next = peek(); + return next == Token::IDENTIFIER || + next == Token::FUTURE_RESERVED_WORD; +} + + +void Parser::Consume(Token::Value token) { + Token::Value next = Next(); + USE(next); + USE(token); + ASSERT(next == token); +} + + +void Parser::Expect(Token::Value token, bool* ok) { + Token::Value next = Next(); + if (next == token) return; + ReportUnexpectedToken(next); + *ok = false; +} + + +bool Parser::Check(Token::Value token) { + Token::Value next = peek(); + if (next == token) { + Consume(next); + return true; + } + return false; +} + + +void Parser::ExpectSemicolon(bool* ok) { + // Check for automatic semicolon insertion according to + // the rules given in ECMA-262, section 7.9, page 21. + Token::Value tok = peek(); + if (tok == Token::SEMICOLON) { + Next(); + return; + } + if (scanner().has_line_terminator_before_next() || + tok == Token::RBRACE || + tok == Token::EOS) { + return; + } + Expect(Token::SEMICOLON, ok); +} + + +Literal* Parser::GetLiteralUndefined() { + return new(zone()) Literal(isolate()->factory()->undefined_value()); +} + + +Literal* Parser::GetLiteralTheHole() { + return new(zone()) Literal(isolate()->factory()->the_hole_value()); +} + + +Literal* Parser::GetLiteralNumber(double value) { + return NewNumberLiteral(value); +} + + +Handle<String> Parser::ParseIdentifier(bool* ok) { + bool is_reserved; + return ParseIdentifierOrReservedWord(&is_reserved, ok); +} + + +Handle<String> Parser::ParseIdentifierOrReservedWord(bool* is_reserved, + bool* ok) { + *is_reserved = false; + if (top_scope_->is_strict_mode()) { + Expect(Token::IDENTIFIER, ok); + } else { + if (!Check(Token::IDENTIFIER)) { + Expect(Token::FUTURE_RESERVED_WORD, ok); + *is_reserved = true; + } + } + if (!*ok) return Handle<String>(); + return GetSymbol(ok); +} + + +Handle<String> Parser::ParseIdentifierName(bool* ok) { + Token::Value next = Next(); + if (next != Token::IDENTIFIER && + next != Token::FUTURE_RESERVED_WORD && + !Token::IsKeyword(next)) { + ReportUnexpectedToken(next); + *ok = false; + return Handle<String>(); + } + return GetSymbol(ok); +} + + +// Checks LHS expression for assignment and prefix/postfix increment/decrement +// in strict mode. +void Parser::CheckStrictModeLValue(Expression* expression, + const char* error, + bool* ok) { + ASSERT(top_scope_->is_strict_mode()); + VariableProxy* lhs = expression != NULL + ? expression->AsVariableProxy() + : NULL; + + if (lhs != NULL && !lhs->is_this() && IsEvalOrArguments(lhs->name())) { + ReportMessage(error, Vector<const char*>::empty()); + *ok = false; + } +} + + +// Checks whether octal literal last seen is between beg_pos and end_pos. +// If so, reports an error. +void Parser::CheckOctalLiteral(int beg_pos, int end_pos, bool* ok) { + int octal = scanner().octal_position(); + if (beg_pos <= octal && octal <= end_pos) { + ReportMessageAt(Scanner::Location(octal, octal + 1), "strict_octal_literal", + Vector<const char*>::empty()); + scanner().clear_octal_position(); + *ok = false; + } +} + + +// This function reads an identifier and determines whether or not it +// is 'get' or 'set'. +Handle<String> Parser::ParseIdentifierOrGetOrSet(bool* is_get, + bool* is_set, + bool* ok) { + Handle<String> result = ParseIdentifier(ok); + if (!*ok) return Handle<String>(); + if (scanner().is_literal_ascii() && scanner().literal_length() == 3) { + const char* token = scanner().literal_ascii_string().start(); + *is_get = strncmp(token, "get", 3) == 0; + *is_set = !*is_get && strncmp(token, "set", 3) == 0; + } + return result; +} + + +// ---------------------------------------------------------------------------- +// Parser support + + +bool Parser::TargetStackContainsLabel(Handle<String> label) { + for (Target* t = target_stack_; t != NULL; t = t->previous()) { + BreakableStatement* stat = t->node()->AsBreakableStatement(); + if (stat != NULL && ContainsLabel(stat->labels(), label)) + return true; + } + return false; +} + + +BreakableStatement* Parser::LookupBreakTarget(Handle<String> label, bool* ok) { + bool anonymous = label.is_null(); + for (Target* t = target_stack_; t != NULL; t = t->previous()) { + BreakableStatement* stat = t->node()->AsBreakableStatement(); + if (stat == NULL) continue; + if ((anonymous && stat->is_target_for_anonymous()) || + (!anonymous && ContainsLabel(stat->labels(), label))) { + RegisterTargetUse(stat->break_target(), t->previous()); + return stat; + } + } + return NULL; +} + + +IterationStatement* Parser::LookupContinueTarget(Handle<String> label, + bool* ok) { + bool anonymous = label.is_null(); + for (Target* t = target_stack_; t != NULL; t = t->previous()) { + IterationStatement* stat = t->node()->AsIterationStatement(); + if (stat == NULL) continue; + + ASSERT(stat->is_target_for_anonymous()); + if (anonymous || ContainsLabel(stat->labels(), label)) { + RegisterTargetUse(stat->continue_target(), t->previous()); + return stat; + } + } + return NULL; +} + + +void Parser::RegisterTargetUse(BreakTarget* target, Target* stop) { + // Register that a break target found at the given stop in the + // target stack has been used from the top of the target stack. Add + // the break target to any TargetCollectors passed on the stack. + for (Target* t = target_stack_; t != stop; t = t->previous()) { + TargetCollector* collector = t->node()->AsTargetCollector(); + if (collector != NULL) collector->AddTarget(target); + } +} + + +Literal* Parser::NewNumberLiteral(double number) { + return new(zone()) Literal(isolate()->factory()->NewNumber(number, TENURED)); +} + + +Expression* Parser::NewThrowReferenceError(Handle<String> type) { + return NewThrowError(isolate()->factory()->MakeReferenceError_symbol(), + type, HandleVector<Object>(NULL, 0)); +} + + +Expression* Parser::NewThrowSyntaxError(Handle<String> type, + Handle<Object> first) { + int argc = first.is_null() ? 0 : 1; + Vector< Handle<Object> > arguments = HandleVector<Object>(&first, argc); + return NewThrowError( + isolate()->factory()->MakeSyntaxError_symbol(), type, arguments); +} + + +Expression* Parser::NewThrowTypeError(Handle<String> type, + Handle<Object> first, + Handle<Object> second) { + ASSERT(!first.is_null() && !second.is_null()); + Handle<Object> elements[] = { first, second }; + Vector< Handle<Object> > arguments = + HandleVector<Object>(elements, ARRAY_SIZE(elements)); + return NewThrowError( + isolate()->factory()->MakeTypeError_symbol(), type, arguments); +} + + +Expression* Parser::NewThrowError(Handle<String> constructor, + Handle<String> type, + Vector< Handle<Object> > arguments) { + int argc = arguments.length(); + Handle<FixedArray> elements = isolate()->factory()->NewFixedArray(argc, + TENURED); + for (int i = 0; i < argc; i++) { + Handle<Object> element = arguments[i]; + if (!element.is_null()) { + elements->set(i, *element); + } + } + Handle<JSArray> array = isolate()->factory()->NewJSArrayWithElements(elements, + TENURED); + + ZoneList<Expression*>* args = new ZoneList<Expression*>(2); + args->Add(new(zone()) Literal(type)); + args->Add(new(zone()) Literal(array)); + return new(zone()) Throw(new(zone()) CallRuntime(constructor, NULL, args), + scanner().location().beg_pos); +} + +// ---------------------------------------------------------------------------- +// JSON + +Handle<Object> JsonParser::ParseJson(Handle<String> script, + UC16CharacterStream* source) { + scanner_.Initialize(source); + stack_overflow_ = false; + Handle<Object> result = ParseJsonValue(); + if (result.is_null() || scanner_.Next() != Token::EOS) { + if (stack_overflow_) { + // Scanner failed. + isolate()->StackOverflow(); + } else { + // Parse failed. Scanner's current token is the unexpected token. + Token::Value token = scanner_.current_token(); + + const char* message; + const char* name_opt = NULL; + + switch (token) { + case Token::EOS: + message = "unexpected_eos"; + break; + case Token::NUMBER: + message = "unexpected_token_number"; + break; + case Token::STRING: + message = "unexpected_token_string"; + break; + case Token::IDENTIFIER: + case Token::FUTURE_RESERVED_WORD: + message = "unexpected_token_identifier"; + break; + default: + message = "unexpected_token"; + name_opt = Token::String(token); + ASSERT(name_opt != NULL); + break; + } + + Scanner::Location source_location = scanner_.location(); + Factory* factory = isolate()->factory(); + MessageLocation location(factory->NewScript(script), + source_location.beg_pos, + source_location.end_pos); + Handle<JSArray> array; + if (name_opt == NULL) { + array = factory->NewJSArray(0); + } else { + Handle<String> name = factory->NewStringFromUtf8(CStrVector(name_opt)); + Handle<FixedArray> element = factory->NewFixedArray(1); + element->set(0, *name); + array = factory->NewJSArrayWithElements(element); + } + Handle<Object> result = factory->NewSyntaxError(message, array); + isolate()->Throw(*result, &location); + return Handle<Object>::null(); + } + } + return result; +} + + +Handle<String> JsonParser::GetString() { + int literal_length = scanner_.literal_length(); + if (literal_length == 0) { + return isolate()->factory()->empty_string(); + } + if (scanner_.is_literal_ascii()) { + return isolate()->factory()->NewStringFromAscii( + scanner_.literal_ascii_string()); + } else { + return isolate()->factory()->NewStringFromTwoByte( + scanner_.literal_uc16_string()); + } +} + + +// Parse any JSON value. +Handle<Object> JsonParser::ParseJsonValue() { + Token::Value token = scanner_.Next(); + switch (token) { + case Token::STRING: + return GetString(); + case Token::NUMBER: + return isolate()->factory()->NewNumber(scanner_.number()); + case Token::FALSE_LITERAL: + return isolate()->factory()->false_value(); + case Token::TRUE_LITERAL: + return isolate()->factory()->true_value(); + case Token::NULL_LITERAL: + return isolate()->factory()->null_value(); + case Token::LBRACE: + return ParseJsonObject(); + case Token::LBRACK: + return ParseJsonArray(); + default: + return ReportUnexpectedToken(); + } +} + + +// Parse a JSON object. Scanner must be right after '{' token. +Handle<Object> JsonParser::ParseJsonObject() { + Handle<JSFunction> object_constructor( + isolate()->global_context()->object_function()); + Handle<JSObject> json_object = + isolate()->factory()->NewJSObject(object_constructor); + if (scanner_.peek() == Token::RBRACE) { + scanner_.Next(); + } else { + if (StackLimitCheck(isolate()).HasOverflowed()) { + stack_overflow_ = true; + return Handle<Object>::null(); + } + do { + if (scanner_.Next() != Token::STRING) { + return ReportUnexpectedToken(); + } + Handle<String> key = GetString(); + if (scanner_.Next() != Token::COLON) { + return ReportUnexpectedToken(); + } + Handle<Object> value = ParseJsonValue(); + if (value.is_null()) return Handle<Object>::null(); + uint32_t index; + if (key->AsArrayIndex(&index)) { + SetOwnElement(json_object, index, value, kNonStrictMode); + } else if (key->Equals(isolate()->heap()->Proto_symbol())) { + // We can't remove the __proto__ accessor since it's hardcoded + // in several places. Instead go along and add the value as + // the prototype of the created object if possible. + SetPrototype(json_object, value); + } else { + SetLocalPropertyIgnoreAttributes(json_object, key, value, NONE); + } + } while (scanner_.Next() == Token::COMMA); + if (scanner_.current_token() != Token::RBRACE) { + return ReportUnexpectedToken(); + } + } + return json_object; +} + + +// Parse a JSON array. Scanner must be right after '[' token. +Handle<Object> JsonParser::ParseJsonArray() { + ZoneScope zone_scope(DELETE_ON_EXIT); + ZoneList<Handle<Object> > elements(4); + + Token::Value token = scanner_.peek(); + if (token == Token::RBRACK) { + scanner_.Next(); + } else { + if (StackLimitCheck(isolate()).HasOverflowed()) { + stack_overflow_ = true; + return Handle<Object>::null(); + } + do { + Handle<Object> element = ParseJsonValue(); + if (element.is_null()) return Handle<Object>::null(); + elements.Add(element); + token = scanner_.Next(); + } while (token == Token::COMMA); + if (token != Token::RBRACK) { + return ReportUnexpectedToken(); + } + } + + // Allocate a fixed array with all the elements. + Handle<FixedArray> fast_elements = + isolate()->factory()->NewFixedArray(elements.length()); + + for (int i = 0, n = elements.length(); i < n; i++) { + fast_elements->set(i, *elements[i]); + } + + return isolate()->factory()->NewJSArrayWithElements(fast_elements); +} + +// ---------------------------------------------------------------------------- +// Regular expressions + + +RegExpParser::RegExpParser(FlatStringReader* in, + Handle<String>* error, + bool multiline) + : isolate_(Isolate::Current()), + error_(error), + captures_(NULL), + in_(in), + current_(kEndMarker), + next_pos_(0), + capture_count_(0), + has_more_(true), + multiline_(multiline), + simple_(false), + contains_anchor_(false), + is_scanned_for_captures_(false), + failed_(false) { + Advance(); +} + + +uc32 RegExpParser::Next() { + if (has_next()) { + return in()->Get(next_pos_); + } else { + return kEndMarker; + } +} + + +void RegExpParser::Advance() { + if (next_pos_ < in()->length()) { + StackLimitCheck check(isolate()); + if (check.HasOverflowed()) { + ReportError(CStrVector(Isolate::kStackOverflowMessage)); + } else if (isolate()->zone()->excess_allocation()) { + ReportError(CStrVector("Regular expression too large")); + } else { + current_ = in()->Get(next_pos_); + next_pos_++; + } + } else { + current_ = kEndMarker; + has_more_ = false; + } +} + + +void RegExpParser::Reset(int pos) { + next_pos_ = pos; + Advance(); +} + + +void RegExpParser::Advance(int dist) { + next_pos_ += dist - 1; + Advance(); +} + + +bool RegExpParser::simple() { + return simple_; +} + +RegExpTree* RegExpParser::ReportError(Vector<const char> message) { + failed_ = true; + *error_ = isolate()->factory()->NewStringFromAscii(message, NOT_TENURED); + // Zip to the end to make sure the no more input is read. + current_ = kEndMarker; + next_pos_ = in()->length(); + return NULL; +} + + +// Pattern :: +// Disjunction +RegExpTree* RegExpParser::ParsePattern() { + RegExpTree* result = ParseDisjunction(CHECK_FAILED); + ASSERT(!has_more()); + // If the result of parsing is a literal string atom, and it has the + // same length as the input, then the atom is identical to the input. + if (result->IsAtom() && result->AsAtom()->length() == in()->length()) { + simple_ = true; + } + return result; +} + + +// Disjunction :: +// Alternative +// Alternative | Disjunction +// Alternative :: +// [empty] +// Term Alternative +// Term :: +// Assertion +// Atom +// Atom Quantifier +RegExpTree* RegExpParser::ParseDisjunction() { + // Used to store current state while parsing subexpressions. + RegExpParserState initial_state(NULL, INITIAL, 0); + RegExpParserState* stored_state = &initial_state; + // Cache the builder in a local variable for quick access. + RegExpBuilder* builder = initial_state.builder(); + while (true) { + switch (current()) { + case kEndMarker: + if (stored_state->IsSubexpression()) { + // Inside a parenthesized group when hitting end of input. + ReportError(CStrVector("Unterminated group") CHECK_FAILED); + } + ASSERT_EQ(INITIAL, stored_state->group_type()); + // Parsing completed successfully. + return builder->ToRegExp(); + case ')': { + if (!stored_state->IsSubexpression()) { + ReportError(CStrVector("Unmatched ')'") CHECK_FAILED); + } + ASSERT_NE(INITIAL, stored_state->group_type()); + + Advance(); + // End disjunction parsing and convert builder content to new single + // regexp atom. + RegExpTree* body = builder->ToRegExp(); + + int end_capture_index = captures_started(); + + int capture_index = stored_state->capture_index(); + SubexpressionType type = stored_state->group_type(); + + // Restore previous state. + stored_state = stored_state->previous_state(); + builder = stored_state->builder(); + + // Build result of subexpression. + if (type == CAPTURE) { + RegExpCapture* capture = new(zone()) RegExpCapture(body, capture_index); + captures_->at(capture_index - 1) = capture; + body = capture; + } else if (type != GROUPING) { + ASSERT(type == POSITIVE_LOOKAHEAD || type == NEGATIVE_LOOKAHEAD); + bool is_positive = (type == POSITIVE_LOOKAHEAD); + body = new(zone()) RegExpLookahead(body, + is_positive, + end_capture_index - capture_index, + capture_index); + } + builder->AddAtom(body); + // For compatability with JSC and ES3, we allow quantifiers after + // lookaheads, and break in all cases. + break; + } + case '|': { + Advance(); + builder->NewAlternative(); + continue; + } + case '*': + case '+': + case '?': + return ReportError(CStrVector("Nothing to repeat")); + case '^': { + Advance(); + if (multiline_) { + builder->AddAssertion( + new(zone()) RegExpAssertion(RegExpAssertion::START_OF_LINE)); + } else { + builder->AddAssertion( + new(zone()) RegExpAssertion(RegExpAssertion::START_OF_INPUT)); + set_contains_anchor(); + } + continue; + } + case '$': { + Advance(); + RegExpAssertion::Type type = + multiline_ ? RegExpAssertion::END_OF_LINE : + RegExpAssertion::END_OF_INPUT; + builder->AddAssertion(new(zone()) RegExpAssertion(type)); + continue; + } + case '.': { + Advance(); + // everything except \x0a, \x0d, \u2028 and \u2029 + ZoneList<CharacterRange>* ranges = new ZoneList<CharacterRange>(2); + CharacterRange::AddClassEscape('.', ranges); + RegExpTree* atom = new(zone()) RegExpCharacterClass(ranges, false); + builder->AddAtom(atom); + break; + } + case '(': { + SubexpressionType type = CAPTURE; + Advance(); + if (current() == '?') { + switch (Next()) { + case ':': + type = GROUPING; + break; + case '=': + type = POSITIVE_LOOKAHEAD; + break; + case '!': + type = NEGATIVE_LOOKAHEAD; + break; + default: + ReportError(CStrVector("Invalid group") CHECK_FAILED); + break; + } + Advance(2); + } else { + if (captures_ == NULL) { + captures_ = new ZoneList<RegExpCapture*>(2); + } + if (captures_started() >= kMaxCaptures) { + ReportError(CStrVector("Too many captures") CHECK_FAILED); + } + captures_->Add(NULL); + } + // Store current state and begin new disjunction parsing. + stored_state = new(zone()) RegExpParserState(stored_state, + type, + captures_started()); + builder = stored_state->builder(); + continue; + } + case '[': { + RegExpTree* atom = ParseCharacterClass(CHECK_FAILED); + builder->AddAtom(atom); + break; + } + // Atom :: + // \ AtomEscape + case '\\': + switch (Next()) { + case kEndMarker: + return ReportError(CStrVector("\\ at end of pattern")); + case 'b': + Advance(2); + builder->AddAssertion( + new(zone()) RegExpAssertion(RegExpAssertion::BOUNDARY)); + continue; + case 'B': + Advance(2); + builder->AddAssertion( + new(zone()) RegExpAssertion(RegExpAssertion::NON_BOUNDARY)); + continue; + // AtomEscape :: + // CharacterClassEscape + // + // CharacterClassEscape :: one of + // d D s S w W + case 'd': case 'D': case 's': case 'S': case 'w': case 'W': { + uc32 c = Next(); + Advance(2); + ZoneList<CharacterRange>* ranges = new ZoneList<CharacterRange>(2); + CharacterRange::AddClassEscape(c, ranges); + RegExpTree* atom = new(zone()) RegExpCharacterClass(ranges, false); + builder->AddAtom(atom); + break; + } + case '1': case '2': case '3': case '4': case '5': case '6': + case '7': case '8': case '9': { + int index = 0; + if (ParseBackReferenceIndex(&index)) { + RegExpCapture* capture = NULL; + if (captures_ != NULL && index <= captures_->length()) { + capture = captures_->at(index - 1); + } + if (capture == NULL) { + builder->AddEmpty(); + break; + } + RegExpTree* atom = new(zone()) RegExpBackReference(capture); + builder->AddAtom(atom); + break; + } + uc32 first_digit = Next(); + if (first_digit == '8' || first_digit == '9') { + // Treat as identity escape + builder->AddCharacter(first_digit); + Advance(2); + break; + } + } + // FALLTHROUGH + case '0': { + Advance(); + uc32 octal = ParseOctalLiteral(); + builder->AddCharacter(octal); + break; + } + // ControlEscape :: one of + // f n r t v + case 'f': + Advance(2); + builder->AddCharacter('\f'); + break; + case 'n': + Advance(2); + builder->AddCharacter('\n'); + break; + case 'r': + Advance(2); + builder->AddCharacter('\r'); + break; + case 't': + Advance(2); + builder->AddCharacter('\t'); + break; + case 'v': + Advance(2); + builder->AddCharacter('\v'); + break; + case 'c': { + Advance(); + uc32 controlLetter = Next(); + // Special case if it is an ASCII letter. + // Convert lower case letters to uppercase. + uc32 letter = controlLetter & ~('a' ^ 'A'); + if (letter < 'A' || 'Z' < letter) { + // controlLetter is not in range 'A'-'Z' or 'a'-'z'. + // This is outside the specification. We match JSC in + // reading the backslash as a literal character instead + // of as starting an escape. + builder->AddCharacter('\\'); + } else { + Advance(2); + builder->AddCharacter(controlLetter & 0x1f); + } + break; + } + case 'x': { + Advance(2); + uc32 value; + if (ParseHexEscape(2, &value)) { + builder->AddCharacter(value); + } else { + builder->AddCharacter('x'); + } + break; + } + case 'u': { + Advance(2); + uc32 value; + if (ParseHexEscape(4, &value)) { + builder->AddCharacter(value); + } else { + builder->AddCharacter('u'); + } + break; + } + default: + // Identity escape. + builder->AddCharacter(Next()); + Advance(2); + break; + } + break; + case '{': { + int dummy; + if (ParseIntervalQuantifier(&dummy, &dummy)) { + ReportError(CStrVector("Nothing to repeat") CHECK_FAILED); + } + // fallthrough + } + default: + builder->AddCharacter(current()); + Advance(); + break; + } // end switch(current()) + + int min; + int max; + switch (current()) { + // QuantifierPrefix :: + // * + // + + // ? + // { + case '*': + min = 0; + max = RegExpTree::kInfinity; + Advance(); + break; + case '+': + min = 1; + max = RegExpTree::kInfinity; + Advance(); + break; + case '?': + min = 0; + max = 1; + Advance(); + break; + case '{': + if (ParseIntervalQuantifier(&min, &max)) { + if (max < min) { + ReportError(CStrVector("numbers out of order in {} quantifier.") + CHECK_FAILED); + } + break; + } else { + continue; + } + default: + continue; + } + RegExpQuantifier::Type type = RegExpQuantifier::GREEDY; + if (current() == '?') { + type = RegExpQuantifier::NON_GREEDY; + Advance(); + } else if (FLAG_regexp_possessive_quantifier && current() == '+') { + // FLAG_regexp_possessive_quantifier is a debug-only flag. + type = RegExpQuantifier::POSSESSIVE; + Advance(); + } + builder->AddQuantifierToAtom(min, max, type); + } +} + + +#ifdef DEBUG +// Currently only used in an ASSERT. +static bool IsSpecialClassEscape(uc32 c) { + switch (c) { + case 'd': case 'D': + case 's': case 'S': + case 'w': case 'W': + return true; + default: + return false; + } +} +#endif + + +// In order to know whether an escape is a backreference or not we have to scan +// the entire regexp and find the number of capturing parentheses. However we +// don't want to scan the regexp twice unless it is necessary. This mini-parser +// is called when needed. It can see the difference between capturing and +// noncapturing parentheses and can skip character classes and backslash-escaped +// characters. +void RegExpParser::ScanForCaptures() { + // Start with captures started previous to current position + int capture_count = captures_started(); + // Add count of captures after this position. + int n; + while ((n = current()) != kEndMarker) { + Advance(); + switch (n) { + case '\\': + Advance(); + break; + case '[': { + int c; + while ((c = current()) != kEndMarker) { + Advance(); + if (c == '\\') { + Advance(); + } else { + if (c == ']') break; + } + } + break; + } + case '(': + if (current() != '?') capture_count++; + break; + } + } + capture_count_ = capture_count; + is_scanned_for_captures_ = true; +} + + +bool RegExpParser::ParseBackReferenceIndex(int* index_out) { + ASSERT_EQ('\\', current()); + ASSERT('1' <= Next() && Next() <= '9'); + // Try to parse a decimal literal that is no greater than the total number + // of left capturing parentheses in the input. + int start = position(); + int value = Next() - '0'; + Advance(2); + while (true) { + uc32 c = current(); + if (IsDecimalDigit(c)) { + value = 10 * value + (c - '0'); + if (value > kMaxCaptures) { + Reset(start); + return false; + } + Advance(); + } else { + break; + } + } + if (value > captures_started()) { + if (!is_scanned_for_captures_) { + int saved_position = position(); + ScanForCaptures(); + Reset(saved_position); + } + if (value > capture_count_) { + Reset(start); + return false; + } + } + *index_out = value; + return true; +} + + +// QuantifierPrefix :: +// { DecimalDigits } +// { DecimalDigits , } +// { DecimalDigits , DecimalDigits } +// +// Returns true if parsing succeeds, and set the min_out and max_out +// values. Values are truncated to RegExpTree::kInfinity if they overflow. +bool RegExpParser::ParseIntervalQuantifier(int* min_out, int* max_out) { + ASSERT_EQ(current(), '{'); + int start = position(); + Advance(); + int min = 0; + if (!IsDecimalDigit(current())) { + Reset(start); + return false; + } + while (IsDecimalDigit(current())) { + int next = current() - '0'; + if (min > (RegExpTree::kInfinity - next) / 10) { + // Overflow. Skip past remaining decimal digits and return -1. + do { + Advance(); + } while (IsDecimalDigit(current())); + min = RegExpTree::kInfinity; + break; + } + min = 10 * min + next; + Advance(); + } + int max = 0; + if (current() == '}') { + max = min; + Advance(); + } else if (current() == ',') { + Advance(); + if (current() == '}') { + max = RegExpTree::kInfinity; + Advance(); + } else { + while (IsDecimalDigit(current())) { + int next = current() - '0'; + if (max > (RegExpTree::kInfinity - next) / 10) { + do { + Advance(); + } while (IsDecimalDigit(current())); + max = RegExpTree::kInfinity; + break; + } + max = 10 * max + next; + Advance(); + } + if (current() != '}') { + Reset(start); + return false; + } + Advance(); + } + } else { + Reset(start); + return false; + } + *min_out = min; + *max_out = max; + return true; +} + + +uc32 RegExpParser::ParseOctalLiteral() { + ASSERT('0' <= current() && current() <= '7'); + // For compatibility with some other browsers (not all), we parse + // up to three octal digits with a value below 256. + uc32 value = current() - '0'; + Advance(); + if ('0' <= current() && current() <= '7') { + value = value * 8 + current() - '0'; + Advance(); + if (value < 32 && '0' <= current() && current() <= '7') { + value = value * 8 + current() - '0'; + Advance(); + } + } + return value; +} + + +bool RegExpParser::ParseHexEscape(int length, uc32 *value) { + int start = position(); + uc32 val = 0; + bool done = false; + for (int i = 0; !done; i++) { + uc32 c = current(); + int d = HexValue(c); + if (d < 0) { + Reset(start); + return false; + } + val = val * 16 + d; + Advance(); + if (i == length - 1) { + done = true; + } + } + *value = val; + return true; +} + + +uc32 RegExpParser::ParseClassCharacterEscape() { + ASSERT(current() == '\\'); + ASSERT(has_next() && !IsSpecialClassEscape(Next())); + Advance(); + switch (current()) { + case 'b': + Advance(); + return '\b'; + // ControlEscape :: one of + // f n r t v + case 'f': + Advance(); + return '\f'; + case 'n': + Advance(); + return '\n'; + case 'r': + Advance(); + return '\r'; + case 't': + Advance(); + return '\t'; + case 'v': + Advance(); + return '\v'; + case 'c': { + uc32 controlLetter = Next(); + uc32 letter = controlLetter & ~('A' ^ 'a'); + // For compatibility with JSC, inside a character class + // we also accept digits and underscore as control characters. + if ((controlLetter >= '0' && controlLetter <= '9') || + controlLetter == '_' || + (letter >= 'A' && letter <= 'Z')) { + Advance(2); + // Control letters mapped to ASCII control characters in the range + // 0x00-0x1f. + return controlLetter & 0x1f; + } + // We match JSC in reading the backslash as a literal + // character instead of as starting an escape. + return '\\'; + } + case '0': case '1': case '2': case '3': case '4': case '5': + case '6': case '7': + // For compatibility, we interpret a decimal escape that isn't + // a back reference (and therefore either \0 or not valid according + // to the specification) as a 1..3 digit octal character code. + return ParseOctalLiteral(); + case 'x': { + Advance(); + uc32 value; + if (ParseHexEscape(2, &value)) { + return value; + } + // If \x is not followed by a two-digit hexadecimal, treat it + // as an identity escape. + return 'x'; + } + case 'u': { + Advance(); + uc32 value; + if (ParseHexEscape(4, &value)) { + return value; + } + // If \u is not followed by a four-digit hexadecimal, treat it + // as an identity escape. + return 'u'; + } + default: { + // Extended identity escape. We accept any character that hasn't + // been matched by a more specific case, not just the subset required + // by the ECMAScript specification. + uc32 result = current(); + Advance(); + return result; + } + } + return 0; +} + + +CharacterRange RegExpParser::ParseClassAtom(uc16* char_class) { + ASSERT_EQ(0, *char_class); + uc32 first = current(); + if (first == '\\') { + switch (Next()) { + case 'w': case 'W': case 'd': case 'D': case 's': case 'S': { + *char_class = Next(); + Advance(2); + return CharacterRange::Singleton(0); // Return dummy value. + } + case kEndMarker: + return ReportError(CStrVector("\\ at end of pattern")); + default: + uc32 c = ParseClassCharacterEscape(CHECK_FAILED); + return CharacterRange::Singleton(c); + } + } else { + Advance(); + return CharacterRange::Singleton(first); + } +} + + +static const uc16 kNoCharClass = 0; + +// Adds range or pre-defined character class to character ranges. +// If char_class is not kInvalidClass, it's interpreted as a class +// escape (i.e., 's' means whitespace, from '\s'). +static inline void AddRangeOrEscape(ZoneList<CharacterRange>* ranges, + uc16 char_class, + CharacterRange range) { + if (char_class != kNoCharClass) { + CharacterRange::AddClassEscape(char_class, ranges); + } else { + ranges->Add(range); + } +} + + +RegExpTree* RegExpParser::ParseCharacterClass() { + static const char* kUnterminated = "Unterminated character class"; + static const char* kRangeOutOfOrder = "Range out of order in character class"; + + ASSERT_EQ(current(), '['); + Advance(); + bool is_negated = false; + if (current() == '^') { + is_negated = true; + Advance(); + } + ZoneList<CharacterRange>* ranges = new ZoneList<CharacterRange>(2); + while (has_more() && current() != ']') { + uc16 char_class = kNoCharClass; + CharacterRange first = ParseClassAtom(&char_class CHECK_FAILED); + if (current() == '-') { + Advance(); + if (current() == kEndMarker) { + // If we reach the end we break out of the loop and let the + // following code report an error. + break; + } else if (current() == ']') { + AddRangeOrEscape(ranges, char_class, first); + ranges->Add(CharacterRange::Singleton('-')); + break; + } + uc16 char_class_2 = kNoCharClass; + CharacterRange next = ParseClassAtom(&char_class_2 CHECK_FAILED); + if (char_class != kNoCharClass || char_class_2 != kNoCharClass) { + // Either end is an escaped character class. Treat the '-' verbatim. + AddRangeOrEscape(ranges, char_class, first); + ranges->Add(CharacterRange::Singleton('-')); + AddRangeOrEscape(ranges, char_class_2, next); + continue; + } + if (first.from() > next.to()) { + return ReportError(CStrVector(kRangeOutOfOrder) CHECK_FAILED); + } + ranges->Add(CharacterRange::Range(first.from(), next.to())); + } else { + AddRangeOrEscape(ranges, char_class, first); + } + } + if (!has_more()) { + return ReportError(CStrVector(kUnterminated) CHECK_FAILED); + } + Advance(); + if (ranges->length() == 0) { + ranges->Add(CharacterRange::Everything()); + is_negated = !is_negated; + } + return new(zone()) RegExpCharacterClass(ranges, is_negated); +} + + +// ---------------------------------------------------------------------------- +// The Parser interface. + +ParserMessage::~ParserMessage() { + for (int i = 0; i < args().length(); i++) + DeleteArray(args()[i]); + DeleteArray(args().start()); +} + + +ScriptDataImpl::~ScriptDataImpl() { + if (owns_store_) store_.Dispose(); +} + + +int ScriptDataImpl::Length() { + return store_.length() * sizeof(unsigned); +} + + +const char* ScriptDataImpl::Data() { + return reinterpret_cast<const char*>(store_.start()); +} + + +bool ScriptDataImpl::HasError() { + return has_error(); +} + + +void ScriptDataImpl::Initialize() { + // Prepares state for use. + 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<byte*>(&store_[symbol_data_offset]); + } else { + // Partial preparse causes no symbol information. + symbol_data_ = reinterpret_cast<byte*>(&store_[0] + store_.length()); + } + symbol_data_end_ = reinterpret_cast<byte*>(&store_[0] + store_.length()); + } +} + + +int ScriptDataImpl::ReadNumber(byte** source) { + // Reads a number from symbol_data_ in base 128. The most significant + // bit marks that there are more digits. + // If the first byte is 0x80 (kNumberTerminator), it would normally + // represent a leading zero. Since that is useless, and therefore won't + // appear as the first digit of any actual value, it is used to + // mark the end of the input stream. + byte* data = *source; + if (data >= symbol_data_end_) return -1; + byte input = *data; + if (input == PreparseDataConstants::kNumberTerminator) { + // End of stream marker. + return -1; + } + int result = input & 0x7f; + data++; + while ((input & 0x80u) != 0) { + if (data >= symbol_data_end_) return -1; + input = *data; + result = (result << 7) | (input & 0x7f); + data++; + } + *source = data; + return result; +} + + +// Create a Scanner for the preparser to use as input, and preparse the source. +static ScriptDataImpl* DoPreParse(UC16CharacterStream* source, + bool allow_lazy, + ParserRecorder* recorder) { + Isolate* isolate = Isolate::Current(); + V8JavaScriptScanner scanner(isolate->scanner_constants()); + scanner.Initialize(source); + intptr_t stack_limit = isolate->stack_guard()->real_climit(); + if (!preparser::PreParser::PreParseProgram(&scanner, + recorder, + allow_lazy, + stack_limit)) { + isolate->StackOverflow(); + return NULL; + } + + // Extract the accumulated data from the recorder as a single + // contiguous vector that we are responsible for disposing. + Vector<unsigned> store = recorder->ExtractData(); + return new ScriptDataImpl(store); +} + + +// Preparse, but only collect data that is immediately useful, +// even if the preparser data is only used once. +ScriptDataImpl* ParserApi::PartialPreParse(UC16CharacterStream* source, + v8::Extension* extension) { + bool allow_lazy = FLAG_lazy && (extension == NULL); + if (!allow_lazy) { + // Partial preparsing is only about lazily compiled functions. + // If we don't allow lazy compilation, the log data will be empty. + return NULL; + } + PartialParserRecorder recorder; + return DoPreParse(source, allow_lazy, &recorder); +} + + +ScriptDataImpl* ParserApi::PreParse(UC16CharacterStream* source, + v8::Extension* extension) { + Handle<Script> no_script; + bool allow_lazy = FLAG_lazy && (extension == NULL); + CompleteParserRecorder recorder; + return DoPreParse(source, allow_lazy, &recorder); +} + + +bool RegExpParser::ParseRegExp(FlatStringReader* input, + bool multiline, + RegExpCompileData* result) { + ASSERT(result != NULL); + RegExpParser parser(input, &result->error, multiline); + RegExpTree* tree = parser.ParsePattern(); + if (parser.failed()) { + ASSERT(tree == NULL); + ASSERT(!result->error.is_null()); + } else { + ASSERT(tree != NULL); + ASSERT(result->error.is_null()); + result->tree = tree; + int capture_count = parser.captures_started(); + result->simple = tree->IsAtom() && parser.simple() && capture_count == 0; + result->contains_anchor = parser.contains_anchor(); + result->capture_count = capture_count; + } + return !parser.failed(); +} + + +bool ParserApi::Parse(CompilationInfo* info) { + ASSERT(info->function() == NULL); + FunctionLiteral* result = NULL; + Handle<Script> script = info->script(); + if (info->is_lazy()) { + Parser parser(script, true, NULL, NULL); + result = parser.ParseLazy(info); + } else { + bool allow_natives_syntax = + info->allows_natives_syntax() || FLAG_allow_natives_syntax; + ScriptDataImpl* pre_data = info->pre_parse_data(); + Parser parser(script, allow_natives_syntax, info->extension(), pre_data); + if (pre_data != NULL && pre_data->has_error()) { + Scanner::Location loc = pre_data->MessageLocation(); + const char* message = pre_data->BuildMessage(); + Vector<const char*> args = pre_data->BuildArgs(); + parser.ReportMessageAt(loc, message, args); + DeleteArray(message); + for (int i = 0; i < args.length(); i++) { + DeleteArray(args[i]); + } + DeleteArray(args.start()); + ASSERT(info->isolate()->has_pending_exception()); + } else { + Handle<String> source = Handle<String>(String::cast(script->source())); + result = parser.ParseProgram(source, + info->is_global(), + info->StrictMode()); + } + } + + info->SetFunction(result); + return (result != NULL); +} + +} } // namespace v8::internal |