diff options
author | Ali Ijaz Sheikh <ofrobots@google.com> | 2016-03-01 08:58:05 -0800 |
---|---|---|
committer | Ali Sheikh <ofrobots@lemonhope.roam.corp.google.com> | 2016-03-03 20:35:20 -0800 |
commit | 069e02ab47656b3efd1b6829c65856b2e1c2d1db (patch) | |
tree | eb643e0a2e88fd64bb9fc927423458d2ae96c2db /deps/v8/src/parsing | |
parent | 8938355398c79f583a468284b768652d12ba9bc9 (diff) | |
download | android-node-v8-069e02ab47656b3efd1b6829c65856b2e1c2d1db.tar.gz android-node-v8-069e02ab47656b3efd1b6829c65856b2e1c2d1db.tar.bz2 android-node-v8-069e02ab47656b3efd1b6829c65856b2e1c2d1db.zip |
deps: upgrade to V8 4.9.385.18
Pick up the current branch head for V8 4.9
https://github.com/v8/v8/commit/1ecba0f
PR-URL: https://github.com/nodejs/node/pull/4722
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Michaƫl Zasso <mic.besace@gmail.com>
Diffstat (limited to 'deps/v8/src/parsing')
24 files changed, 19118 insertions, 0 deletions
diff --git a/deps/v8/src/parsing/OWNERS b/deps/v8/src/parsing/OWNERS new file mode 100644 index 0000000000..fbab2056f8 --- /dev/null +++ b/deps/v8/src/parsing/OWNERS @@ -0,0 +1,6 @@ +set noparent + +adamk@chromium.org +littledan@chromium.org +marja@chromium.org +rossberg@chromium.org diff --git a/deps/v8/src/parsing/expression-classifier.h b/deps/v8/src/parsing/expression-classifier.h new file mode 100644 index 0000000000..96ccf871f4 --- /dev/null +++ b/deps/v8/src/parsing/expression-classifier.h @@ -0,0 +1,356 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_PARSING_EXPRESSION_CLASSIFIER_H +#define V8_PARSING_EXPRESSION_CLASSIFIER_H + +#include "src/messages.h" +#include "src/parsing/scanner.h" +#include "src/parsing/token.h" + +namespace v8 { +namespace internal { + + +class ExpressionClassifier { + public: + struct Error { + Error() + : location(Scanner::Location::invalid()), + message(MessageTemplate::kNone), + type(kSyntaxError), + arg(nullptr) {} + + Scanner::Location location; + MessageTemplate::Template message : 30; + ParseErrorType type : 2; + const char* arg; + }; + + enum TargetProduction { + ExpressionProduction = 1 << 0, + FormalParameterInitializerProduction = 1 << 1, + BindingPatternProduction = 1 << 2, + AssignmentPatternProduction = 1 << 3, + DistinctFormalParametersProduction = 1 << 4, + StrictModeFormalParametersProduction = 1 << 5, + StrongModeFormalParametersProduction = 1 << 6, + ArrowFormalParametersProduction = 1 << 7, + LetPatternProduction = 1 << 8, + CoverInitializedNameProduction = 1 << 9, + + ExpressionProductions = + (ExpressionProduction | FormalParameterInitializerProduction), + PatternProductions = (BindingPatternProduction | + AssignmentPatternProduction | LetPatternProduction), + FormalParametersProductions = (DistinctFormalParametersProduction | + StrictModeFormalParametersProduction | + StrongModeFormalParametersProduction), + StandardProductions = ExpressionProductions | PatternProductions, + AllProductions = + (StandardProductions | FormalParametersProductions | + ArrowFormalParametersProduction | CoverInitializedNameProduction) + }; + + enum FunctionProperties { NonSimpleParameter = 1 << 0 }; + + ExpressionClassifier() + : invalid_productions_(0), + function_properties_(0), + duplicate_finder_(nullptr) {} + + explicit ExpressionClassifier(DuplicateFinder* duplicate_finder) + : invalid_productions_(0), + function_properties_(0), + duplicate_finder_(duplicate_finder) {} + + bool is_valid(unsigned productions) const { + return (invalid_productions_ & productions) == 0; + } + + DuplicateFinder* duplicate_finder() const { return duplicate_finder_; } + + bool is_valid_expression() const { return is_valid(ExpressionProduction); } + + bool is_valid_formal_parameter_initializer() const { + return is_valid(FormalParameterInitializerProduction); + } + + bool is_valid_binding_pattern() const { + return is_valid(BindingPatternProduction); + } + + bool is_valid_assignment_pattern() const { + return is_valid(AssignmentPatternProduction); + } + + bool is_valid_arrow_formal_parameters() const { + return is_valid(ArrowFormalParametersProduction); + } + + bool is_valid_formal_parameter_list_without_duplicates() const { + return is_valid(DistinctFormalParametersProduction); + } + + // Note: callers should also check + // is_valid_formal_parameter_list_without_duplicates(). + bool is_valid_strict_mode_formal_parameters() const { + return is_valid(StrictModeFormalParametersProduction); + } + + // Note: callers should also check is_valid_strict_mode_formal_parameters() + // and is_valid_formal_parameter_list_without_duplicates(). + bool is_valid_strong_mode_formal_parameters() const { + return is_valid(StrongModeFormalParametersProduction); + } + + bool is_valid_let_pattern() const { return is_valid(LetPatternProduction); } + + const Error& expression_error() const { return expression_error_; } + + const Error& formal_parameter_initializer_error() const { + return formal_parameter_initializer_error_; + } + + const Error& binding_pattern_error() const { return binding_pattern_error_; } + + const Error& assignment_pattern_error() const { + return assignment_pattern_error_; + } + + const Error& arrow_formal_parameters_error() const { + return arrow_formal_parameters_error_; + } + + const Error& duplicate_formal_parameter_error() const { + return duplicate_formal_parameter_error_; + } + + const Error& strict_mode_formal_parameter_error() const { + return strict_mode_formal_parameter_error_; + } + + const Error& strong_mode_formal_parameter_error() const { + return strong_mode_formal_parameter_error_; + } + + const Error& let_pattern_error() const { return let_pattern_error_; } + + bool has_cover_initialized_name() const { + return !is_valid(CoverInitializedNameProduction); + } + const Error& cover_initialized_name_error() const { + return cover_initialized_name_error_; + } + + bool is_simple_parameter_list() const { + return !(function_properties_ & NonSimpleParameter); + } + + void RecordNonSimpleParameter() { + function_properties_ |= NonSimpleParameter; + } + + void RecordExpressionError(const Scanner::Location& loc, + MessageTemplate::Template message, + const char* arg = nullptr) { + if (!is_valid_expression()) return; + invalid_productions_ |= ExpressionProduction; + expression_error_.location = loc; + expression_error_.message = message; + expression_error_.arg = arg; + } + + void RecordExpressionError(const Scanner::Location& loc, + MessageTemplate::Template message, + ParseErrorType type, const char* arg = nullptr) { + if (!is_valid_expression()) return; + invalid_productions_ |= ExpressionProduction; + expression_error_.location = loc; + expression_error_.message = message; + expression_error_.arg = arg; + expression_error_.type = type; + } + + void RecordFormalParameterInitializerError(const Scanner::Location& loc, + MessageTemplate::Template message, + const char* arg = nullptr) { + if (!is_valid_formal_parameter_initializer()) return; + invalid_productions_ |= FormalParameterInitializerProduction; + formal_parameter_initializer_error_.location = loc; + formal_parameter_initializer_error_.message = message; + formal_parameter_initializer_error_.arg = arg; + } + + void RecordBindingPatternError(const Scanner::Location& loc, + MessageTemplate::Template message, + const char* arg = nullptr) { + if (!is_valid_binding_pattern()) return; + invalid_productions_ |= BindingPatternProduction; + binding_pattern_error_.location = loc; + binding_pattern_error_.message = message; + binding_pattern_error_.arg = arg; + } + + void RecordAssignmentPatternError(const Scanner::Location& loc, + MessageTemplate::Template message, + const char* arg = nullptr) { + if (!is_valid_assignment_pattern()) return; + invalid_productions_ |= AssignmentPatternProduction; + assignment_pattern_error_.location = loc; + assignment_pattern_error_.message = message; + assignment_pattern_error_.arg = arg; + } + + void RecordPatternError(const Scanner::Location& loc, + MessageTemplate::Template message, + const char* arg = nullptr) { + RecordBindingPatternError(loc, message, arg); + RecordAssignmentPatternError(loc, message, arg); + } + + void RecordArrowFormalParametersError(const Scanner::Location& loc, + MessageTemplate::Template message, + const char* arg = nullptr) { + if (!is_valid_arrow_formal_parameters()) return; + invalid_productions_ |= ArrowFormalParametersProduction; + arrow_formal_parameters_error_.location = loc; + arrow_formal_parameters_error_.message = message; + arrow_formal_parameters_error_.arg = arg; + } + + void RecordDuplicateFormalParameterError(const Scanner::Location& loc) { + if (!is_valid_formal_parameter_list_without_duplicates()) return; + invalid_productions_ |= DistinctFormalParametersProduction; + duplicate_formal_parameter_error_.location = loc; + duplicate_formal_parameter_error_.message = MessageTemplate::kParamDupe; + duplicate_formal_parameter_error_.arg = nullptr; + } + + // Record a binding that would be invalid in strict mode. Confusingly this + // is not the same as StrictFormalParameterList, which simply forbids + // duplicate bindings. + void RecordStrictModeFormalParameterError(const Scanner::Location& loc, + MessageTemplate::Template message, + const char* arg = nullptr) { + if (!is_valid_strict_mode_formal_parameters()) return; + invalid_productions_ |= StrictModeFormalParametersProduction; + strict_mode_formal_parameter_error_.location = loc; + strict_mode_formal_parameter_error_.message = message; + strict_mode_formal_parameter_error_.arg = arg; + } + + void RecordStrongModeFormalParameterError(const Scanner::Location& loc, + MessageTemplate::Template message, + const char* arg = nullptr) { + if (!is_valid_strong_mode_formal_parameters()) return; + invalid_productions_ |= StrongModeFormalParametersProduction; + strong_mode_formal_parameter_error_.location = loc; + strong_mode_formal_parameter_error_.message = message; + strong_mode_formal_parameter_error_.arg = arg; + } + + void RecordLetPatternError(const Scanner::Location& loc, + MessageTemplate::Template message, + const char* arg = nullptr) { + if (!is_valid_let_pattern()) return; + invalid_productions_ |= LetPatternProduction; + let_pattern_error_.location = loc; + let_pattern_error_.message = message; + let_pattern_error_.arg = arg; + } + + void RecordCoverInitializedNameError(const Scanner::Location& loc, + MessageTemplate::Template message, + const char* arg = nullptr) { + if (has_cover_initialized_name()) return; + invalid_productions_ |= CoverInitializedNameProduction; + cover_initialized_name_error_.location = loc; + cover_initialized_name_error_.message = message; + cover_initialized_name_error_.arg = arg; + } + + void ForgiveCoverInitializedNameError() { + invalid_productions_ &= ~CoverInitializedNameProduction; + cover_initialized_name_error_ = Error(); + } + + void ForgiveAssignmentPatternError() { + invalid_productions_ &= ~AssignmentPatternProduction; + assignment_pattern_error_ = Error(); + } + + void Accumulate(const ExpressionClassifier& inner, + unsigned productions = StandardProductions) { + // Propagate errors from inner, but don't overwrite already recorded + // errors. + unsigned non_arrow_inner_invalid_productions = + inner.invalid_productions_ & ~ArrowFormalParametersProduction; + if (non_arrow_inner_invalid_productions == 0) return; + unsigned non_arrow_productions = + productions & ~ArrowFormalParametersProduction; + unsigned errors = + non_arrow_productions & non_arrow_inner_invalid_productions; + errors &= ~invalid_productions_; + if (errors != 0) { + invalid_productions_ |= errors; + if (errors & ExpressionProduction) + expression_error_ = inner.expression_error_; + if (errors & FormalParameterInitializerProduction) + formal_parameter_initializer_error_ = + inner.formal_parameter_initializer_error_; + if (errors & BindingPatternProduction) + binding_pattern_error_ = inner.binding_pattern_error_; + if (errors & AssignmentPatternProduction) + assignment_pattern_error_ = inner.assignment_pattern_error_; + if (errors & DistinctFormalParametersProduction) + duplicate_formal_parameter_error_ = + inner.duplicate_formal_parameter_error_; + if (errors & StrictModeFormalParametersProduction) + strict_mode_formal_parameter_error_ = + inner.strict_mode_formal_parameter_error_; + if (errors & StrongModeFormalParametersProduction) + strong_mode_formal_parameter_error_ = + inner.strong_mode_formal_parameter_error_; + if (errors & LetPatternProduction) + let_pattern_error_ = inner.let_pattern_error_; + if (errors & CoverInitializedNameProduction) + cover_initialized_name_error_ = inner.cover_initialized_name_error_; + } + + // As an exception to the above, the result continues to be a valid arrow + // formal parameters if the inner expression is a valid binding pattern. + if (productions & ArrowFormalParametersProduction && + is_valid_arrow_formal_parameters()) { + // Also copy function properties if expecting an arrow function + // parameter. + function_properties_ |= inner.function_properties_; + + if (!inner.is_valid_binding_pattern()) { + invalid_productions_ |= ArrowFormalParametersProduction; + arrow_formal_parameters_error_ = inner.binding_pattern_error_; + } + } + } + + private: + unsigned invalid_productions_; + unsigned function_properties_; + Error expression_error_; + Error formal_parameter_initializer_error_; + Error binding_pattern_error_; + Error assignment_pattern_error_; + Error arrow_formal_parameters_error_; + Error duplicate_formal_parameter_error_; + Error strict_mode_formal_parameter_error_; + Error strong_mode_formal_parameter_error_; + Error let_pattern_error_; + Error cover_initialized_name_error_; + DuplicateFinder* duplicate_finder_; +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_PARSING_EXPRESSION_CLASSIFIER_H diff --git a/deps/v8/src/parsing/func-name-inferrer.cc b/deps/v8/src/parsing/func-name-inferrer.cc new file mode 100644 index 0000000000..12013afd28 --- /dev/null +++ b/deps/v8/src/parsing/func-name-inferrer.cc @@ -0,0 +1,85 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/parsing/func-name-inferrer.h" + +#include "src/ast/ast.h" +#include "src/ast/ast-value-factory.h" +#include "src/list-inl.h" + +namespace v8 { +namespace internal { + +FuncNameInferrer::FuncNameInferrer(AstValueFactory* ast_value_factory, + Zone* zone) + : ast_value_factory_(ast_value_factory), + entries_stack_(10, zone), + names_stack_(5, zone), + funcs_to_infer_(4, zone), + zone_(zone) { +} + + +void FuncNameInferrer::PushEnclosingName(const AstRawString* name) { + // Enclosing name is a name of a constructor function. To check + // that it is really a constructor, we check that it is not empty + // and starts with a capital letter. + if (!name->IsEmpty() && unibrow::Uppercase::Is(name->FirstCharacter())) { + names_stack_.Add(Name(name, kEnclosingConstructorName), zone()); + } +} + + +void FuncNameInferrer::PushLiteralName(const AstRawString* name) { + if (IsOpen() && name != ast_value_factory_->prototype_string()) { + names_stack_.Add(Name(name, kLiteralName), zone()); + } +} + + +void FuncNameInferrer::PushVariableName(const AstRawString* name) { + if (IsOpen() && name != ast_value_factory_->dot_result_string()) { + names_stack_.Add(Name(name, kVariableName), zone()); + } +} + + +const AstString* FuncNameInferrer::MakeNameFromStack() { + return MakeNameFromStackHelper(0, ast_value_factory_->empty_string()); +} + +const AstString* FuncNameInferrer::MakeNameFromStackHelper( + int pos, const AstString* prev) { + if (pos >= names_stack_.length()) return prev; + if (pos < names_stack_.length() - 1 && + names_stack_.at(pos).type == kVariableName && + names_stack_.at(pos + 1).type == kVariableName) { + // Skip consecutive variable declarations. + return MakeNameFromStackHelper(pos + 1, prev); + } else { + if (prev->length() > 0) { + const AstRawString* name = names_stack_.at(pos).name; + if (prev->length() + name->length() + 1 > String::kMaxLength) return prev; + const AstConsString* curr = ast_value_factory_->NewConsString( + ast_value_factory_->dot_string(), name); + curr = ast_value_factory_->NewConsString(prev, curr); + return MakeNameFromStackHelper(pos + 1, curr); + } else { + return MakeNameFromStackHelper(pos + 1, names_stack_.at(pos).name); + } + } +} + + +void FuncNameInferrer::InferFunctionsNames() { + const AstString* func_name = MakeNameFromStack(); + for (int i = 0; i < funcs_to_infer_.length(); ++i) { + funcs_to_infer_[i]->set_raw_inferred_name(func_name); + } + funcs_to_infer_.Rewind(0); +} + + +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/parsing/func-name-inferrer.h b/deps/v8/src/parsing/func-name-inferrer.h new file mode 100644 index 0000000000..ba38ffeb24 --- /dev/null +++ b/deps/v8/src/parsing/func-name-inferrer.h @@ -0,0 +1,127 @@ +// Copyright 2006-2009 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_PARSING_FUNC_NAME_INFERRER_H_ +#define V8_PARSING_FUNC_NAME_INFERRER_H_ + +#include "src/handles.h" +#include "src/zone.h" + +namespace v8 { +namespace internal { + +class AstRawString; +class AstString; +class AstValueFactory; +class FunctionLiteral; + +// FuncNameInferrer is a stateful class that is used to perform name +// inference for anonymous functions during static analysis of source code. +// Inference is performed in cases when an anonymous function is assigned +// to a variable or a property (see test-func-name-inference.cc for examples.) +// +// The basic idea is that during parsing of LHSs of certain expressions +// (assignments, declarations, object literals) we collect name strings, +// and during parsing of the RHS, a function literal can be collected. After +// parsing the RHS we can infer a name for function literals that do not have +// a name. +class FuncNameInferrer : public ZoneObject { + public: + FuncNameInferrer(AstValueFactory* ast_value_factory, Zone* zone); + + // To enter function name inference state, put a FuncNameInferrer::State + // on the stack. + class State { + public: + explicit State(FuncNameInferrer* fni) : fni_(fni) { + if (fni_ != nullptr) fni_->Enter(); + } + ~State() { + if (fni_ != nullptr) fni_->Leave(); + } + + private: + FuncNameInferrer* fni_; + + DISALLOW_COPY_AND_ASSIGN(State); + }; + + // Returns whether we have entered name collection state. + bool IsOpen() const { return !entries_stack_.is_empty(); } + + // Pushes an enclosing the name of enclosing function onto names stack. + void PushEnclosingName(const AstRawString* name); + + // Pushes an encountered name onto names stack when in collection state. + void PushLiteralName(const AstRawString* name); + + void PushVariableName(const AstRawString* name); + + // Adds a function to infer name for. + void AddFunction(FunctionLiteral* func_to_infer) { + if (IsOpen()) { + funcs_to_infer_.Add(func_to_infer, zone()); + } + } + + void RemoveLastFunction() { + if (IsOpen() && !funcs_to_infer_.is_empty()) { + funcs_to_infer_.RemoveLast(); + } + } + + // Infers a function name and leaves names collection state. + void Infer() { + DCHECK(IsOpen()); + if (!funcs_to_infer_.is_empty()) { + InferFunctionsNames(); + } + } + + private: + enum NameType { + kEnclosingConstructorName, + kLiteralName, + kVariableName + }; + struct Name { + Name(const AstRawString* name, NameType type) : name(name), type(type) {} + const AstRawString* name; + NameType type; + }; + + void Enter() { entries_stack_.Add(names_stack_.length(), zone()); } + + void Leave() { + DCHECK(IsOpen()); + names_stack_.Rewind(entries_stack_.RemoveLast()); + if (entries_stack_.is_empty()) funcs_to_infer_.Clear(); + } + + Zone* zone() const { return zone_; } + + // Constructs a full name in dotted notation from gathered names. + const AstString* MakeNameFromStack(); + + // A helper function for MakeNameFromStack. + const AstString* MakeNameFromStackHelper(int pos, + const AstString* prev); + + // Performs name inferring for added functions. + void InferFunctionsNames(); + + AstValueFactory* ast_value_factory_; + ZoneList<int> entries_stack_; + ZoneList<Name> names_stack_; + ZoneList<FunctionLiteral*> funcs_to_infer_; + Zone* zone_; + + DISALLOW_COPY_AND_ASSIGN(FuncNameInferrer); +}; + + +} // namespace internal +} // namespace v8 + +#endif // V8_PARSING_FUNC_NAME_INFERRER_H_ diff --git a/deps/v8/src/parsing/json-parser.h b/deps/v8/src/parsing/json-parser.h new file mode 100644 index 0000000000..e23c73383e --- /dev/null +++ b/deps/v8/src/parsing/json-parser.h @@ -0,0 +1,842 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_PARSING_JSON_PARSER_H_ +#define V8_PARSING_JSON_PARSER_H_ + +#include "src/char-predicates.h" +#include "src/conversions.h" +#include "src/debug/debug.h" +#include "src/factory.h" +#include "src/messages.h" +#include "src/parsing/scanner.h" +#include "src/parsing/token.h" +#include "src/transitions.h" +#include "src/types.h" + +namespace v8 { +namespace internal { + +enum ParseElementResult { kElementFound, kElementNotFound, kNullHandle }; + + +// A simple json parser. +template <bool seq_one_byte> +class JsonParser BASE_EMBEDDED { + public: + MUST_USE_RESULT static MaybeHandle<Object> Parse(Handle<String> source) { + return JsonParser(source).ParseJson(); + } + + static const int kEndOfString = -1; + + private: + explicit JsonParser(Handle<String> source) + : source_(source), + source_length_(source->length()), + isolate_(source->map()->GetHeap()->isolate()), + factory_(isolate_->factory()), + object_constructor_(isolate_->native_context()->object_function(), + isolate_), + position_(-1) { + source_ = String::Flatten(source_); + pretenure_ = (source_length_ >= kPretenureTreshold) ? TENURED : NOT_TENURED; + + // Optimized fast case where we only have Latin1 characters. + if (seq_one_byte) { + seq_source_ = Handle<SeqOneByteString>::cast(source_); + } + } + + // Parse a string containing a single JSON value. + MaybeHandle<Object> ParseJson(); + + inline void Advance() { + position_++; + if (position_ >= source_length_) { + c0_ = kEndOfString; + } else if (seq_one_byte) { + c0_ = seq_source_->SeqOneByteStringGet(position_); + } else { + c0_ = source_->Get(position_); + } + } + + // The JSON lexical grammar is specified in the ECMAScript 5 standard, + // section 15.12.1.1. The only allowed whitespace characters between tokens + // are tab, carriage-return, newline and space. + + inline void AdvanceSkipWhitespace() { + do { + Advance(); + } while (c0_ == ' ' || c0_ == '\t' || c0_ == '\n' || c0_ == '\r'); + } + + inline void SkipWhitespace() { + while (c0_ == ' ' || c0_ == '\t' || c0_ == '\n' || c0_ == '\r') { + Advance(); + } + } + + inline uc32 AdvanceGetChar() { + Advance(); + return c0_; + } + + // Checks that current charater is c. + // If so, then consume c and skip whitespace. + inline bool MatchSkipWhiteSpace(uc32 c) { + if (c0_ == c) { + AdvanceSkipWhitespace(); + return true; + } + return false; + } + + // A JSON string (production JSONString) is subset of valid JavaScript string + // literals. The string must only be double-quoted (not single-quoted), and + // the only allowed backslash-escapes are ", /, \, b, f, n, r, t and + // four-digit hex escapes (uXXXX). Any other use of backslashes is invalid. + Handle<String> ParseJsonString() { + return ScanJsonString<false>(); + } + + bool ParseJsonString(Handle<String> expected) { + int length = expected->length(); + if (source_->length() - position_ - 1 > length) { + DisallowHeapAllocation no_gc; + String::FlatContent content = expected->GetFlatContent(); + if (content.IsOneByte()) { + DCHECK_EQ('"', c0_); + const uint8_t* input_chars = seq_source_->GetChars() + position_ + 1; + const uint8_t* expected_chars = content.ToOneByteVector().start(); + for (int i = 0; i < length; i++) { + uint8_t c0 = input_chars[i]; + if (c0 != expected_chars[i] || c0 == '"' || c0 < 0x20 || c0 == '\\') { + return false; + } + } + if (input_chars[length] == '"') { + position_ = position_ + length + 1; + AdvanceSkipWhitespace(); + return true; + } + } + } + return false; + } + + Handle<String> ParseJsonInternalizedString() { + return ScanJsonString<true>(); + } + + template <bool is_internalized> + Handle<String> ScanJsonString(); + // Creates a new string and copies prefix[start..end] into the beginning + // of it. Then scans the rest of the string, adding characters after the + // prefix. Called by ScanJsonString when reaching a '\' or non-Latin1 char. + template <typename StringType, typename SinkChar> + Handle<String> SlowScanJsonString(Handle<String> prefix, int start, int end); + + // A JSON number (production JSONNumber) is a subset of the valid JavaScript + // decimal number literals. + // It includes an optional minus sign, must have at least one + // digit before and after a decimal point, may not have prefixed zeros (unless + // the integer part is zero), and may include an exponent part (e.g., "e-10"). + // Hexadecimal and octal numbers are not allowed. + Handle<Object> ParseJsonNumber(); + + // Parse a single JSON value from input (grammar production JSONValue). + // A JSON value is either a (double-quoted) string literal, a number literal, + // one of "true", "false", or "null", or an object or array literal. + Handle<Object> ParseJsonValue(); + + // Parse a JSON object literal (grammar production JSONObject). + // An object literal is a squiggly-braced and comma separated sequence + // (possibly empty) of key/value pairs, where the key is a JSON string + // literal, the value is a JSON value, and the two are separated by a colon. + // A JSON array doesn't allow numbers and identifiers as keys, like a + // JavaScript array. + Handle<Object> ParseJsonObject(); + + // Helper for ParseJsonObject. Parses the form "123": obj, which is recorded + // as an element, not a property. + ParseElementResult ParseElement(Handle<JSObject> json_object); + + // Parses a JSON array literal (grammar production JSONArray). An array + // literal is a square-bracketed and comma separated sequence (possibly empty) + // of JSON values. + // A JSON array doesn't allow leaving out values from the sequence, nor does + // it allow a terminal comma, like a JavaScript array does. + Handle<Object> ParseJsonArray(); + + + // Mark that a parsing error has happened at the current token, and + // return a null handle. Primarily for readability. + inline Handle<Object> ReportUnexpectedCharacter() { + return Handle<Object>::null(); + } + + inline Isolate* isolate() { return isolate_; } + inline Factory* factory() { return factory_; } + inline Handle<JSFunction> object_constructor() { return object_constructor_; } + + static const int kInitialSpecialStringLength = 32; + static const int kPretenureTreshold = 100 * 1024; + + + private: + Zone* zone() { return &zone_; } + + void CommitStateToJsonObject(Handle<JSObject> json_object, Handle<Map> map, + ZoneList<Handle<Object> >* properties); + + Handle<String> source_; + int source_length_; + Handle<SeqOneByteString> seq_source_; + + PretenureFlag pretenure_; + Isolate* isolate_; + Factory* factory_; + Zone zone_; + Handle<JSFunction> object_constructor_; + uc32 c0_; + int position_; +}; + +template <bool seq_one_byte> +MaybeHandle<Object> JsonParser<seq_one_byte>::ParseJson() { + // Advance to the first character (possibly EOS) + AdvanceSkipWhitespace(); + Handle<Object> result = ParseJsonValue(); + if (result.is_null() || c0_ != kEndOfString) { + // Some exception (for example stack overflow) is already pending. + if (isolate_->has_pending_exception()) return Handle<Object>::null(); + + // Parse failed. Current character is the unexpected token. + Factory* factory = this->factory(); + MessageTemplate::Template message; + Handle<String> argument; + + switch (c0_) { + case kEndOfString: + message = MessageTemplate::kUnexpectedEOS; + break; + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + message = MessageTemplate::kUnexpectedTokenNumber; + break; + case '"': + message = MessageTemplate::kUnexpectedTokenString; + break; + default: + message = MessageTemplate::kUnexpectedToken; + argument = factory->LookupSingleCharacterStringFromCode(c0_); + break; + } + + Handle<Script> script(factory->NewScript(source_)); + // We should sent compile error event because we compile JSON object in + // separated source file. + isolate()->debug()->OnCompileError(script); + MessageLocation location(script, position_, position_ + 1); + Handle<Object> error = factory->NewSyntaxError(message, argument); + return isolate()->template Throw<Object>(error, &location); + } + return result; +} + + +// Parse any JSON value. +template <bool seq_one_byte> +Handle<Object> JsonParser<seq_one_byte>::ParseJsonValue() { + StackLimitCheck stack_check(isolate_); + if (stack_check.HasOverflowed()) { + isolate_->StackOverflow(); + return Handle<Object>::null(); + } + + if (stack_check.InterruptRequested()) { + ExecutionAccess access(isolate_); + // Avoid blocking GC in long running parser (v8:3974). + isolate_->stack_guard()->HandleGCInterrupt(); + } + + if (c0_ == '"') return ParseJsonString(); + if ((c0_ >= '0' && c0_ <= '9') || c0_ == '-') return ParseJsonNumber(); + if (c0_ == '{') return ParseJsonObject(); + if (c0_ == '[') return ParseJsonArray(); + if (c0_ == 'f') { + if (AdvanceGetChar() == 'a' && AdvanceGetChar() == 'l' && + AdvanceGetChar() == 's' && AdvanceGetChar() == 'e') { + AdvanceSkipWhitespace(); + return factory()->false_value(); + } + return ReportUnexpectedCharacter(); + } + if (c0_ == 't') { + if (AdvanceGetChar() == 'r' && AdvanceGetChar() == 'u' && + AdvanceGetChar() == 'e') { + AdvanceSkipWhitespace(); + return factory()->true_value(); + } + return ReportUnexpectedCharacter(); + } + if (c0_ == 'n') { + if (AdvanceGetChar() == 'u' && AdvanceGetChar() == 'l' && + AdvanceGetChar() == 'l') { + AdvanceSkipWhitespace(); + return factory()->null_value(); + } + return ReportUnexpectedCharacter(); + } + return ReportUnexpectedCharacter(); +} + + +template <bool seq_one_byte> +ParseElementResult JsonParser<seq_one_byte>::ParseElement( + Handle<JSObject> json_object) { + uint32_t index = 0; + // Maybe an array index, try to parse it. + if (c0_ == '0') { + // With a leading zero, the string has to be "0" only to be an index. + Advance(); + } else { + do { + int d = c0_ - '0'; + if (index > 429496729U - ((d + 3) >> 3)) break; + index = (index * 10) + d; + Advance(); + } while (IsDecimalDigit(c0_)); + } + + if (c0_ == '"') { + // Successfully parsed index, parse and store element. + AdvanceSkipWhitespace(); + + if (c0_ == ':') { + AdvanceSkipWhitespace(); + Handle<Object> value = ParseJsonValue(); + if (!value.is_null()) { + JSObject::SetOwnElementIgnoreAttributes(json_object, index, value, NONE) + .Assert(); + return kElementFound; + } else { + return kNullHandle; + } + } + } + return kElementNotFound; +} + +// Parse a JSON object. Position must be right at '{'. +template <bool seq_one_byte> +Handle<Object> JsonParser<seq_one_byte>::ParseJsonObject() { + HandleScope scope(isolate()); + Handle<JSObject> json_object = + factory()->NewJSObject(object_constructor(), pretenure_); + Handle<Map> map(json_object->map()); + int descriptor = 0; + ZoneList<Handle<Object> > properties(8, zone()); + DCHECK_EQ(c0_, '{'); + + bool transitioning = true; + + AdvanceSkipWhitespace(); + if (c0_ != '}') { + do { + if (c0_ != '"') return ReportUnexpectedCharacter(); + + int start_position = position_; + Advance(); + + if (IsDecimalDigit(c0_)) { + ParseElementResult element_result = ParseElement(json_object); + if (element_result == kNullHandle) return Handle<Object>::null(); + if (element_result == kElementFound) continue; + } + // Not an index, fallback to the slow path. + + position_ = start_position; +#ifdef DEBUG + c0_ = '"'; +#endif + + Handle<String> key; + Handle<Object> value; + + // Try to follow existing transitions as long as possible. Once we stop + // transitioning, no transition can be found anymore. + DCHECK(transitioning); + // First check whether there is a single expected transition. If so, try + // to parse it first. + bool follow_expected = false; + Handle<Map> target; + if (seq_one_byte) { + key = TransitionArray::ExpectedTransitionKey(map); + follow_expected = !key.is_null() && ParseJsonString(key); + } + // If the expected transition hits, follow it. + if (follow_expected) { + target = TransitionArray::ExpectedTransitionTarget(map); + } else { + // If the expected transition failed, parse an internalized string and + // try to find a matching transition. + key = ParseJsonInternalizedString(); + if (key.is_null()) return ReportUnexpectedCharacter(); + + target = TransitionArray::FindTransitionToField(map, key); + // If a transition was found, follow it and continue. + transitioning = !target.is_null(); + } + if (c0_ != ':') return ReportUnexpectedCharacter(); + + AdvanceSkipWhitespace(); + value = ParseJsonValue(); + if (value.is_null()) return ReportUnexpectedCharacter(); + + if (transitioning) { + PropertyDetails details = + target->instance_descriptors()->GetDetails(descriptor); + Representation expected_representation = details.representation(); + + if (value->FitsRepresentation(expected_representation)) { + if (expected_representation.IsHeapObject() && + !target->instance_descriptors() + ->GetFieldType(descriptor) + ->NowContains(value)) { + Handle<HeapType> value_type( + value->OptimalType(isolate(), expected_representation)); + Map::GeneralizeFieldType(target, descriptor, + expected_representation, value_type); + } + DCHECK(target->instance_descriptors() + ->GetFieldType(descriptor) + ->NowContains(value)); + properties.Add(value, zone()); + map = target; + descriptor++; + continue; + } else { + transitioning = false; + } + } + + DCHECK(!transitioning); + + // Commit the intermediate state to the object and stop transitioning. + CommitStateToJsonObject(json_object, map, &properties); + + JSObject::DefinePropertyOrElementIgnoreAttributes(json_object, key, value) + .Check(); + } while (transitioning && MatchSkipWhiteSpace(',')); + + // If we transitioned until the very end, transition the map now. + if (transitioning) { + CommitStateToJsonObject(json_object, map, &properties); + } else { + while (MatchSkipWhiteSpace(',')) { + HandleScope local_scope(isolate()); + if (c0_ != '"') return ReportUnexpectedCharacter(); + + int start_position = position_; + Advance(); + + if (IsDecimalDigit(c0_)) { + ParseElementResult element_result = ParseElement(json_object); + if (element_result == kNullHandle) return Handle<Object>::null(); + if (element_result == kElementFound) continue; + } + // Not an index, fallback to the slow path. + + position_ = start_position; +#ifdef DEBUG + c0_ = '"'; +#endif + + Handle<String> key; + Handle<Object> value; + + key = ParseJsonInternalizedString(); + if (key.is_null() || c0_ != ':') return ReportUnexpectedCharacter(); + + AdvanceSkipWhitespace(); + value = ParseJsonValue(); + if (value.is_null()) return ReportUnexpectedCharacter(); + + JSObject::DefinePropertyOrElementIgnoreAttributes(json_object, key, + value).Check(); + } + } + + if (c0_ != '}') { + return ReportUnexpectedCharacter(); + } + } + AdvanceSkipWhitespace(); + return scope.CloseAndEscape(json_object); +} + + +template <bool seq_one_byte> +void JsonParser<seq_one_byte>::CommitStateToJsonObject( + Handle<JSObject> json_object, Handle<Map> map, + ZoneList<Handle<Object> >* properties) { + JSObject::AllocateStorageForMap(json_object, map); + DCHECK(!json_object->map()->is_dictionary_map()); + + DisallowHeapAllocation no_gc; + + int length = properties->length(); + for (int i = 0; i < length; i++) { + Handle<Object> value = (*properties)[i]; + json_object->WriteToField(i, *value); + } +} + + +// Parse a JSON array. Position must be right at '['. +template <bool seq_one_byte> +Handle<Object> JsonParser<seq_one_byte>::ParseJsonArray() { + HandleScope scope(isolate()); + ZoneList<Handle<Object> > elements(4, zone()); + DCHECK_EQ(c0_, '['); + + AdvanceSkipWhitespace(); + if (c0_ != ']') { + do { + Handle<Object> element = ParseJsonValue(); + if (element.is_null()) return ReportUnexpectedCharacter(); + elements.Add(element, zone()); + } while (MatchSkipWhiteSpace(',')); + if (c0_ != ']') { + return ReportUnexpectedCharacter(); + } + } + AdvanceSkipWhitespace(); + // Allocate a fixed array with all the elements. + Handle<FixedArray> fast_elements = + factory()->NewFixedArray(elements.length(), pretenure_); + for (int i = 0, n = elements.length(); i < n; i++) { + fast_elements->set(i, *elements[i]); + } + Handle<Object> json_array = factory()->NewJSArrayWithElements( + fast_elements, FAST_ELEMENTS, Strength::WEAK, pretenure_); + return scope.CloseAndEscape(json_array); +} + + +template <bool seq_one_byte> +Handle<Object> JsonParser<seq_one_byte>::ParseJsonNumber() { + bool negative = false; + int beg_pos = position_; + if (c0_ == '-') { + Advance(); + negative = true; + } + if (c0_ == '0') { + Advance(); + // Prefix zero is only allowed if it's the only digit before + // a decimal point or exponent. + if (IsDecimalDigit(c0_)) return ReportUnexpectedCharacter(); + } else { + int i = 0; + int digits = 0; + if (c0_ < '1' || c0_ > '9') return ReportUnexpectedCharacter(); + do { + i = i * 10 + c0_ - '0'; + digits++; + Advance(); + } while (IsDecimalDigit(c0_)); + if (c0_ != '.' && c0_ != 'e' && c0_ != 'E' && digits < 10) { + SkipWhitespace(); + return Handle<Smi>(Smi::FromInt((negative ? -i : i)), isolate()); + } + } + if (c0_ == '.') { + Advance(); + if (!IsDecimalDigit(c0_)) return ReportUnexpectedCharacter(); + do { + Advance(); + } while (IsDecimalDigit(c0_)); + } + if (AsciiAlphaToLower(c0_) == 'e') { + Advance(); + if (c0_ == '-' || c0_ == '+') Advance(); + if (!IsDecimalDigit(c0_)) return ReportUnexpectedCharacter(); + do { + Advance(); + } while (IsDecimalDigit(c0_)); + } + int length = position_ - beg_pos; + double number; + if (seq_one_byte) { + Vector<const uint8_t> chars(seq_source_->GetChars() + beg_pos, length); + number = StringToDouble(isolate()->unicode_cache(), chars, + NO_FLAGS, // Hex, octal or trailing junk. + std::numeric_limits<double>::quiet_NaN()); + } else { + Vector<uint8_t> buffer = Vector<uint8_t>::New(length); + String::WriteToFlat(*source_, buffer.start(), beg_pos, position_); + Vector<const uint8_t> result = + Vector<const uint8_t>(buffer.start(), length); + number = StringToDouble(isolate()->unicode_cache(), + result, + NO_FLAGS, // Hex, octal or trailing junk. + 0.0); + buffer.Dispose(); + } + SkipWhitespace(); + return factory()->NewNumber(number, pretenure_); +} + + +template <typename StringType> +inline void SeqStringSet(Handle<StringType> seq_str, int i, uc32 c); + +template <> +inline void SeqStringSet(Handle<SeqTwoByteString> seq_str, int i, uc32 c) { + seq_str->SeqTwoByteStringSet(i, c); +} + +template <> +inline void SeqStringSet(Handle<SeqOneByteString> seq_str, int i, uc32 c) { + seq_str->SeqOneByteStringSet(i, c); +} + +template <typename StringType> +inline Handle<StringType> NewRawString(Factory* factory, + int length, + PretenureFlag pretenure); + +template <> +inline Handle<SeqTwoByteString> NewRawString(Factory* factory, + int length, + PretenureFlag pretenure) { + return factory->NewRawTwoByteString(length, pretenure).ToHandleChecked(); +} + +template <> +inline Handle<SeqOneByteString> NewRawString(Factory* factory, + int length, + PretenureFlag pretenure) { + return factory->NewRawOneByteString(length, pretenure).ToHandleChecked(); +} + + +// Scans the rest of a JSON string starting from position_ and writes +// prefix[start..end] along with the scanned characters into a +// sequential string of type StringType. +template <bool seq_one_byte> +template <typename StringType, typename SinkChar> +Handle<String> JsonParser<seq_one_byte>::SlowScanJsonString( + Handle<String> prefix, int start, int end) { + int count = end - start; + int max_length = count + source_length_ - position_; + int length = Min(max_length, Max(kInitialSpecialStringLength, 2 * count)); + Handle<StringType> seq_string = + NewRawString<StringType>(factory(), length, pretenure_); + // Copy prefix into seq_str. + SinkChar* dest = seq_string->GetChars(); + String::WriteToFlat(*prefix, dest, start, end); + + while (c0_ != '"') { + // Check for control character (0x00-0x1f) or unterminated string (<0). + if (c0_ < 0x20) return Handle<String>::null(); + if (count >= length) { + // We need to create a longer sequential string for the result. + return SlowScanJsonString<StringType, SinkChar>(seq_string, 0, count); + } + if (c0_ != '\\') { + // If the sink can contain UC16 characters, or source_ contains only + // Latin1 characters, there's no need to test whether we can store the + // character. Otherwise check whether the UC16 source character can fit + // in the Latin1 sink. + if (sizeof(SinkChar) == kUC16Size || seq_one_byte || + c0_ <= String::kMaxOneByteCharCode) { + SeqStringSet(seq_string, count++, c0_); + Advance(); + } else { + // StringType is SeqOneByteString and we just read a non-Latin1 char. + return SlowScanJsonString<SeqTwoByteString, uc16>(seq_string, 0, count); + } + } else { + Advance(); // Advance past the \. + switch (c0_) { + case '"': + case '\\': + case '/': + SeqStringSet(seq_string, count++, c0_); + break; + case 'b': + SeqStringSet(seq_string, count++, '\x08'); + break; + case 'f': + SeqStringSet(seq_string, count++, '\x0c'); + break; + case 'n': + SeqStringSet(seq_string, count++, '\x0a'); + break; + case 'r': + SeqStringSet(seq_string, count++, '\x0d'); + break; + case 't': + SeqStringSet(seq_string, count++, '\x09'); + break; + case 'u': { + uc32 value = 0; + for (int i = 0; i < 4; i++) { + Advance(); + int digit = HexValue(c0_); + if (digit < 0) { + return Handle<String>::null(); + } + value = value * 16 + digit; + } + if (sizeof(SinkChar) == kUC16Size || + value <= String::kMaxOneByteCharCode) { + SeqStringSet(seq_string, count++, value); + break; + } else { + // StringType is SeqOneByteString and we just read a non-Latin1 + // char. + position_ -= 6; // Rewind position_ to \ in \uxxxx. + Advance(); + return SlowScanJsonString<SeqTwoByteString, uc16>(seq_string, + 0, + count); + } + } + default: + return Handle<String>::null(); + } + Advance(); + } + } + + DCHECK_EQ('"', c0_); + // Advance past the last '"'. + AdvanceSkipWhitespace(); + + // Shrink seq_string length to count and return. + return SeqString::Truncate(seq_string, count); +} + + +template <bool seq_one_byte> +template <bool is_internalized> +Handle<String> JsonParser<seq_one_byte>::ScanJsonString() { + DCHECK_EQ('"', c0_); + Advance(); + if (c0_ == '"') { + AdvanceSkipWhitespace(); + return factory()->empty_string(); + } + + if (seq_one_byte && is_internalized) { + // Fast path for existing internalized strings. If the the string being + // parsed is not a known internalized string, contains backslashes or + // unexpectedly reaches the end of string, return with an empty handle. + uint32_t running_hash = isolate()->heap()->HashSeed(); + int position = position_; + uc32 c0 = c0_; + do { + if (c0 == '\\') { + c0_ = c0; + int beg_pos = position_; + position_ = position; + return SlowScanJsonString<SeqOneByteString, uint8_t>(source_, + beg_pos, + position_); + } + if (c0 < 0x20) return Handle<String>::null(); + running_hash = StringHasher::AddCharacterCore(running_hash, + static_cast<uint16_t>(c0)); + position++; + if (position >= source_length_) return Handle<String>::null(); + c0 = seq_source_->SeqOneByteStringGet(position); + } while (c0 != '"'); + int length = position - position_; + uint32_t hash = (length <= String::kMaxHashCalcLength) + ? StringHasher::GetHashCore(running_hash) + : static_cast<uint32_t>(length); + Vector<const uint8_t> string_vector( + seq_source_->GetChars() + position_, length); + StringTable* string_table = isolate()->heap()->string_table(); + uint32_t capacity = string_table->Capacity(); + uint32_t entry = StringTable::FirstProbe(hash, capacity); + uint32_t count = 1; + Handle<String> result; + while (true) { + Object* element = string_table->KeyAt(entry); + if (element == isolate()->heap()->undefined_value()) { + // Lookup failure. + result = factory()->InternalizeOneByteString( + seq_source_, position_, length); + break; + } + if (element != isolate()->heap()->the_hole_value() && + String::cast(element)->IsOneByteEqualTo(string_vector)) { + result = Handle<String>(String::cast(element), isolate()); +#ifdef DEBUG + uint32_t hash_field = + (hash << String::kHashShift) | String::kIsNotArrayIndexMask; + DCHECK_EQ(static_cast<int>(result->Hash()), + static_cast<int>(hash_field >> String::kHashShift)); +#endif + break; + } + entry = StringTable::NextProbe(entry, count++, capacity); + } + position_ = position; + // Advance past the last '"'. + AdvanceSkipWhitespace(); + return result; + } + + int beg_pos = position_; + // Fast case for Latin1 only without escape characters. + do { + // Check for control character (0x00-0x1f) or unterminated string (<0). + if (c0_ < 0x20) return Handle<String>::null(); + if (c0_ != '\\') { + if (seq_one_byte || c0_ <= String::kMaxOneByteCharCode) { + Advance(); + } else { + return SlowScanJsonString<SeqTwoByteString, uc16>(source_, + beg_pos, + position_); + } + } else { + return SlowScanJsonString<SeqOneByteString, uint8_t>(source_, + beg_pos, + position_); + } + } while (c0_ != '"'); + int length = position_ - beg_pos; + Handle<String> result = + factory()->NewRawOneByteString(length, pretenure_).ToHandleChecked(); + uint8_t* dest = SeqOneByteString::cast(*result)->GetChars(); + String::WriteToFlat(*source_, dest, beg_pos, position_); + + DCHECK_EQ('"', c0_); + // Advance past the last '"'. + AdvanceSkipWhitespace(); + return result; +} + +} // namespace internal +} // namespace v8 + +#endif // V8_PARSING_JSON_PARSER_H_ diff --git a/deps/v8/src/parsing/parameter-initializer-rewriter.cc b/deps/v8/src/parsing/parameter-initializer-rewriter.cc new file mode 100644 index 0000000000..003bbebae0 --- /dev/null +++ b/deps/v8/src/parsing/parameter-initializer-rewriter.cc @@ -0,0 +1,88 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/parsing/parameter-initializer-rewriter.h" + +#include "src/ast/ast.h" +#include "src/ast/ast-expression-visitor.h" +#include "src/ast/scopes.h" + +namespace v8 { +namespace internal { + +namespace { + + +class Rewriter final : public AstExpressionVisitor { + public: + Rewriter(uintptr_t stack_limit, Expression* initializer, Scope* old_scope, + Scope* new_scope) + : AstExpressionVisitor(stack_limit, initializer), + old_scope_(old_scope), + new_scope_(new_scope) {} + + private: + void VisitExpression(Expression* expr) override {} + + void VisitFunctionLiteral(FunctionLiteral* expr) override; + void VisitClassLiteral(ClassLiteral* expr) override; + void VisitVariableProxy(VariableProxy* expr) override; + + Scope* old_scope_; + Scope* new_scope_; +}; + + +void Rewriter::VisitFunctionLiteral(FunctionLiteral* function_literal) { + function_literal->scope()->ReplaceOuterScope(new_scope_); +} + + +void Rewriter::VisitClassLiteral(ClassLiteral* class_literal) { + class_literal->scope()->ReplaceOuterScope(new_scope_); + if (class_literal->extends() != nullptr) { + Visit(class_literal->extends()); + } + // No need to visit the constructor since it will have the class + // scope on its scope chain. + ZoneList<ObjectLiteralProperty*>* props = class_literal->properties(); + for (int i = 0; i < props->length(); ++i) { + ObjectLiteralProperty* prop = props->at(i); + if (!prop->key()->IsLiteral()) { + Visit(prop->key()); + } + // No need to visit the values, since all values are functions with + // the class scope on their scope chain. + DCHECK(prop->value()->IsFunctionLiteral()); + } +} + + +void Rewriter::VisitVariableProxy(VariableProxy* proxy) { + if (proxy->is_resolved()) { + Variable* var = proxy->var(); + DCHECK_EQ(var->mode(), TEMPORARY); + if (old_scope_->RemoveTemporary(var)) { + var->set_scope(new_scope_); + new_scope_->AddTemporary(var); + } + } else if (old_scope_->RemoveUnresolved(proxy)) { + new_scope_->AddUnresolved(proxy); + } +} + + +} // anonymous namespace + + +void RewriteParameterInitializerScope(uintptr_t stack_limit, + Expression* initializer, Scope* old_scope, + Scope* new_scope) { + Rewriter rewriter(stack_limit, initializer, old_scope, new_scope); + rewriter.Run(); +} + + +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/parsing/parameter-initializer-rewriter.h b/deps/v8/src/parsing/parameter-initializer-rewriter.h new file mode 100644 index 0000000000..255534c99e --- /dev/null +++ b/deps/v8/src/parsing/parameter-initializer-rewriter.h @@ -0,0 +1,22 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_PARSING_PARAMETER_EXPRESSION_REWRITER_H_ +#define V8_PARSING_PARAMETER_EXPRESSION_REWRITER_H_ + +#include "src/ast/ast.h" + +namespace v8 { +namespace internal { + + +void RewriteParameterInitializerScope(uintptr_t stack_limit, + Expression* initializer, Scope* old_scope, + Scope* new_scope); + + +} // namespace internal +} // namespace v8 + +#endif // V8_PARSING_PARAMETER_EXPRESSION_REWRITER_H_ diff --git a/deps/v8/src/parsing/parser-base.h b/deps/v8/src/parsing/parser-base.h new file mode 100644 index 0000000000..2955b0b9d9 --- /dev/null +++ b/deps/v8/src/parsing/parser-base.h @@ -0,0 +1,3380 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_PARSING_PARSER_BASE_H +#define V8_PARSING_PARSER_BASE_H + +#include "src/ast/scopes.h" +#include "src/bailout-reason.h" +#include "src/hashmap.h" +#include "src/messages.h" +#include "src/parsing/expression-classifier.h" +#include "src/parsing/func-name-inferrer.h" +#include "src/parsing/scanner.h" +#include "src/parsing/token.h" + +namespace v8 { +namespace internal { + + +enum FunctionNameValidity { + kFunctionNameIsStrictReserved, + kSkipFunctionNameCheck, + kFunctionNameValidityUnknown +}; + + +struct FormalParametersBase { + explicit FormalParametersBase(Scope* scope) : scope(scope) {} + Scope* scope; + bool has_rest = false; + bool is_simple = true; + int materialized_literals_count = 0; +}; + + +// Common base class shared between parser and pre-parser. Traits encapsulate +// the differences between Parser and PreParser: + +// - Return types: For example, Parser functions return Expression* and +// PreParser functions return PreParserExpression. + +// - Creating parse tree nodes: Parser generates an AST during the recursive +// descent. PreParser doesn't create a tree. Instead, it passes around minimal +// data objects (PreParserExpression, PreParserIdentifier etc.) which contain +// just enough data for the upper layer functions. PreParserFactory is +// responsible for creating these dummy objects. It provides a similar kind of +// interface as AstNodeFactory, so ParserBase doesn't need to care which one is +// used. + +// - Miscellaneous other tasks interleaved with the recursive descent. For +// example, Parser keeps track of which function literals should be marked as +// pretenured, and PreParser doesn't care. + +// The traits are expected to contain the following typedefs: +// struct Traits { +// // In particular... +// struct Type { +// // Used by FunctionState and BlockState. +// typedef Scope; +// typedef GeneratorVariable; +// // Return types for traversing functions. +// typedef Identifier; +// typedef Expression; +// typedef FunctionLiteral; +// typedef ClassLiteral; +// typedef ObjectLiteralProperty; +// typedef Literal; +// typedef ExpressionList; +// typedef PropertyList; +// typedef FormalParameter; +// typedef FormalParameters; +// // For constructing objects returned by the traversing functions. +// typedef Factory; +// }; +// // ... +// }; + +template <typename Traits> +class ParserBase : public Traits { + public: + // Shorten type names defined by Traits. + typedef typename Traits::Type::Expression ExpressionT; + typedef typename Traits::Type::Identifier IdentifierT; + typedef typename Traits::Type::FormalParameter FormalParameterT; + typedef typename Traits::Type::FormalParameters FormalParametersT; + typedef typename Traits::Type::FunctionLiteral FunctionLiteralT; + typedef typename Traits::Type::Literal LiteralT; + typedef typename Traits::Type::ObjectLiteralProperty ObjectLiteralPropertyT; + typedef typename Traits::Type::StatementList StatementListT; + + ParserBase(Zone* zone, Scanner* scanner, uintptr_t stack_limit, + v8::Extension* extension, AstValueFactory* ast_value_factory, + ParserRecorder* log, typename Traits::Type::Parser this_object) + : Traits(this_object), + parenthesized_function_(false), + scope_(NULL), + function_state_(NULL), + extension_(extension), + fni_(NULL), + ast_value_factory_(ast_value_factory), + log_(log), + mode_(PARSE_EAGERLY), // Lazy mode must be set explicitly. + stack_limit_(stack_limit), + zone_(zone), + scanner_(scanner), + stack_overflow_(false), + allow_lazy_(false), + allow_natives_(false), + allow_harmony_sloppy_(false), + allow_harmony_sloppy_function_(false), + allow_harmony_sloppy_let_(false), + allow_harmony_default_parameters_(false), + allow_harmony_destructuring_bind_(false), + allow_harmony_destructuring_assignment_(false), + allow_strong_mode_(false), + allow_legacy_const_(true), + allow_harmony_do_expressions_(false), + allow_harmony_function_name_(false) {} + +#define ALLOW_ACCESSORS(name) \ + bool allow_##name() const { return allow_##name##_; } \ + void set_allow_##name(bool allow) { allow_##name##_ = allow; } + + ALLOW_ACCESSORS(lazy); + ALLOW_ACCESSORS(natives); + ALLOW_ACCESSORS(harmony_sloppy); + ALLOW_ACCESSORS(harmony_sloppy_function); + ALLOW_ACCESSORS(harmony_sloppy_let); + ALLOW_ACCESSORS(harmony_default_parameters); + ALLOW_ACCESSORS(harmony_destructuring_bind); + ALLOW_ACCESSORS(harmony_destructuring_assignment); + ALLOW_ACCESSORS(strong_mode); + ALLOW_ACCESSORS(legacy_const); + ALLOW_ACCESSORS(harmony_do_expressions); + ALLOW_ACCESSORS(harmony_function_name); +#undef ALLOW_ACCESSORS + + uintptr_t stack_limit() const { return stack_limit_; } + + protected: + enum AllowRestrictedIdentifiers { + kAllowRestrictedIdentifiers, + kDontAllowRestrictedIdentifiers + }; + + enum Mode { + PARSE_LAZILY, + PARSE_EAGERLY + }; + + enum VariableDeclarationContext { + kStatementListItem, + kStatement, + kForStatement + }; + + class Checkpoint; + class ObjectLiteralCheckerBase; + + // --------------------------------------------------------------------------- + // FunctionState and BlockState together implement the parser's scope stack. + // The parser's current scope is in scope_. BlockState and FunctionState + // constructors push on the scope stack and the destructors pop. They are also + // used to hold the parser's per-function and per-block state. + class BlockState BASE_EMBEDDED { + public: + BlockState(Scope** scope_stack, Scope* scope) + : scope_stack_(scope_stack), outer_scope_(*scope_stack) { + *scope_stack_ = scope; + } + ~BlockState() { *scope_stack_ = outer_scope_; } + + private: + Scope** scope_stack_; + Scope* outer_scope_; + }; + + struct DestructuringAssignment { + public: + DestructuringAssignment(ExpressionT expression, Scope* scope) + : assignment(expression), scope(scope) {} + + ExpressionT assignment; + Scope* scope; + }; + + class FunctionState BASE_EMBEDDED { + public: + FunctionState(FunctionState** function_state_stack, Scope** scope_stack, + Scope* scope, FunctionKind kind, + typename Traits::Type::Factory* factory); + ~FunctionState(); + + int NextMaterializedLiteralIndex() { + return next_materialized_literal_index_++; + } + int materialized_literal_count() { + return next_materialized_literal_index_; + } + + void SkipMaterializedLiterals(int count) { + next_materialized_literal_index_ += count; + } + + void AddProperty() { expected_property_count_++; } + int expected_property_count() { return expected_property_count_; } + + Scanner::Location this_location() const { return this_location_; } + Scanner::Location super_location() const { return super_location_; } + Scanner::Location return_location() const { return return_location_; } + void set_this_location(Scanner::Location location) { + this_location_ = location; + } + void set_super_location(Scanner::Location location) { + super_location_ = location; + } + void set_return_location(Scanner::Location location) { + return_location_ = location; + } + + bool is_generator() const { return IsGeneratorFunction(kind_); } + + FunctionKind kind() const { return kind_; } + FunctionState* outer() const { return outer_function_state_; } + + void set_generator_object_variable( + typename Traits::Type::GeneratorVariable* variable) { + DCHECK(variable != NULL); + DCHECK(is_generator()); + generator_object_variable_ = variable; + } + typename Traits::Type::GeneratorVariable* generator_object_variable() + const { + return generator_object_variable_; + } + + typename Traits::Type::Factory* factory() { return factory_; } + + const List<DestructuringAssignment>& destructuring_assignments_to_rewrite() + const { + return destructuring_assignments_to_rewrite_; + } + + void AddDestructuringAssignment(DestructuringAssignment pair) { + destructuring_assignments_to_rewrite_.Add(pair); + } + + private: + // Used to assign an index to each literal that needs materialization in + // the function. Includes regexp literals, and boilerplate for object and + // array literals. + int next_materialized_literal_index_; + + // Properties count estimation. + int expected_property_count_; + + // Location of most recent use of 'this' (invalid if none). + Scanner::Location this_location_; + + // Location of most recent 'return' statement (invalid if none). + Scanner::Location return_location_; + + // Location of call to the "super" constructor (invalid if none). + Scanner::Location super_location_; + + FunctionKind kind_; + // For generators, this variable may hold the generator object. It variable + // is used by yield expressions and return statements. It is not necessary + // for generator functions to have this variable set. + Variable* generator_object_variable_; + + FunctionState** function_state_stack_; + FunctionState* outer_function_state_; + Scope** scope_stack_; + Scope* outer_scope_; + + List<DestructuringAssignment> destructuring_assignments_to_rewrite_; + + void RewriteDestructuringAssignments(); + + typename Traits::Type::Factory* factory_; + + friend class ParserTraits; + friend class Checkpoint; + }; + + // Annoyingly, arrow functions first parse as comma expressions, then when we + // see the => we have to go back and reinterpret the arguments as being formal + // parameters. To do so we need to reset some of the parser state back to + // what it was before the arguments were first seen. + class Checkpoint BASE_EMBEDDED { + public: + explicit Checkpoint(ParserBase* parser) { + function_state_ = parser->function_state_; + next_materialized_literal_index_ = + function_state_->next_materialized_literal_index_; + expected_property_count_ = function_state_->expected_property_count_; + } + + void Restore(int* materialized_literal_index_delta) { + *materialized_literal_index_delta = + function_state_->next_materialized_literal_index_ - + next_materialized_literal_index_; + function_state_->next_materialized_literal_index_ = + next_materialized_literal_index_; + function_state_->expected_property_count_ = expected_property_count_; + } + + private: + FunctionState* function_state_; + int next_materialized_literal_index_; + int expected_property_count_; + }; + + class ParsingModeScope BASE_EMBEDDED { + public: + ParsingModeScope(ParserBase* parser, Mode mode) + : parser_(parser), + old_mode_(parser->mode()) { + parser_->mode_ = mode; + } + ~ParsingModeScope() { + parser_->mode_ = old_mode_; + } + + private: + ParserBase* parser_; + Mode old_mode_; + }; + + Scope* NewScope(Scope* parent, ScopeType scope_type) { + // Must always pass the function kind for FUNCTION_SCOPE. + DCHECK(scope_type != FUNCTION_SCOPE); + return NewScope(parent, scope_type, kNormalFunction); + } + + Scope* NewScope(Scope* parent, ScopeType scope_type, FunctionKind kind) { + DCHECK(ast_value_factory()); + DCHECK(scope_type != MODULE_SCOPE || FLAG_harmony_modules); + Scope* result = new (zone()) + Scope(zone(), parent, scope_type, ast_value_factory(), kind); + result->Initialize(); + return result; + } + + Scanner* scanner() const { return scanner_; } + AstValueFactory* ast_value_factory() const { return ast_value_factory_; } + int position() { return scanner_->location().beg_pos; } + int peek_position() { return scanner_->peek_location().beg_pos; } + bool stack_overflow() const { return stack_overflow_; } + void set_stack_overflow() { stack_overflow_ = true; } + Mode mode() const { return mode_; } + Zone* zone() const { return zone_; } + + INLINE(Token::Value peek()) { + if (stack_overflow_) return Token::ILLEGAL; + return scanner()->peek(); + } + + INLINE(Token::Value PeekAhead()) { + if (stack_overflow_) return Token::ILLEGAL; + return scanner()->PeekAhead(); + } + + INLINE(Token::Value Next()) { + if (stack_overflow_) return Token::ILLEGAL; + { + if (GetCurrentStackPosition() < stack_limit_) { + // Any further calls to Next or peek will return the illegal token. + // The current call must return the next token, which might already + // have been peek'ed. + stack_overflow_ = true; + } + } + return scanner()->Next(); + } + + void Consume(Token::Value token) { + Token::Value next = Next(); + USE(next); + USE(token); + DCHECK(next == token); + } + + bool Check(Token::Value token) { + Token::Value next = peek(); + if (next == token) { + Consume(next); + return true; + } + return false; + } + + void Expect(Token::Value token, bool* ok) { + Token::Value next = Next(); + if (next != token) { + ReportUnexpectedToken(next); + *ok = false; + } + } + + void 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()->HasAnyLineTerminatorBeforeNext() || + tok == Token::RBRACE || + tok == Token::EOS) { + return; + } + Expect(Token::SEMICOLON, ok); + } + + bool peek_any_identifier() { + Token::Value next = peek(); + return next == Token::IDENTIFIER || next == Token::FUTURE_RESERVED_WORD || + next == Token::FUTURE_STRICT_RESERVED_WORD || next == Token::LET || + next == Token::STATIC || next == Token::YIELD; + } + + bool CheckContextualKeyword(Vector<const char> keyword) { + if (PeekContextualKeyword(keyword)) { + Consume(Token::IDENTIFIER); + return true; + } + return false; + } + + bool PeekContextualKeyword(Vector<const char> keyword) { + return peek() == Token::IDENTIFIER && + scanner()->is_next_contextual_keyword(keyword); + } + + void ExpectContextualKeyword(Vector<const char> keyword, bool* ok) { + Expect(Token::IDENTIFIER, ok); + if (!*ok) return; + if (!scanner()->is_literal_contextual_keyword(keyword)) { + ReportUnexpectedToken(scanner()->current_token()); + *ok = false; + } + } + + bool CheckInOrOf(ForEachStatement::VisitMode* visit_mode, bool* ok) { + if (Check(Token::IN)) { + if (is_strong(language_mode())) { + ReportMessageAt(scanner()->location(), MessageTemplate::kStrongForIn); + *ok = false; + } else { + *visit_mode = ForEachStatement::ENUMERATE; + } + return true; + } else if (CheckContextualKeyword(CStrVector("of"))) { + *visit_mode = ForEachStatement::ITERATE; + return true; + } + return false; + } + + // Checks whether an octal literal was last seen between beg_pos and end_pos. + // If so, reports an error. Only called for strict mode and template strings. + void CheckOctalLiteral(int beg_pos, int end_pos, + MessageTemplate::Template message, bool* ok) { + Scanner::Location octal = scanner()->octal_position(); + if (octal.IsValid() && beg_pos <= octal.beg_pos && + octal.end_pos <= end_pos) { + ReportMessageAt(octal, message); + scanner()->clear_octal_position(); + *ok = false; + } + } + + inline void CheckStrictOctalLiteral(int beg_pos, int end_pos, bool* ok) { + CheckOctalLiteral(beg_pos, end_pos, MessageTemplate::kStrictOctalLiteral, + ok); + } + + inline void CheckTemplateOctalLiteral(int beg_pos, int end_pos, bool* ok) { + CheckOctalLiteral(beg_pos, end_pos, MessageTemplate::kTemplateOctalLiteral, + ok); + } + + void CheckDestructuringElement(ExpressionT element, + ExpressionClassifier* classifier, int beg_pos, + int end_pos); + + // Checking the name of a function literal. This has to be done after parsing + // the function, since the function can declare itself strict. + void CheckFunctionName(LanguageMode language_mode, IdentifierT function_name, + FunctionNameValidity function_name_validity, + const Scanner::Location& function_name_loc, bool* ok) { + if (function_name_validity == kSkipFunctionNameCheck) return; + // The function name needs to be checked in strict mode. + if (is_sloppy(language_mode)) return; + + if (this->IsEvalOrArguments(function_name)) { + Traits::ReportMessageAt(function_name_loc, + MessageTemplate::kStrictEvalArguments); + *ok = false; + return; + } + if (function_name_validity == kFunctionNameIsStrictReserved) { + Traits::ReportMessageAt(function_name_loc, + MessageTemplate::kUnexpectedStrictReserved); + *ok = false; + return; + } + if (is_strong(language_mode) && this->IsUndefined(function_name)) { + Traits::ReportMessageAt(function_name_loc, + MessageTemplate::kStrongUndefined); + *ok = false; + return; + } + } + + // Determine precedence of given token. + static int Precedence(Token::Value token, bool accept_IN) { + if (token == Token::IN && !accept_IN) + return 0; // 0 precedence will terminate binary expression parsing + return Token::Precedence(token); + } + + typename Traits::Type::Factory* factory() { + return function_state_->factory(); + } + + LanguageMode language_mode() { return scope_->language_mode(); } + bool is_generator() const { return function_state_->is_generator(); } + + bool allow_const() { + return is_strict(language_mode()) || allow_harmony_sloppy() || + allow_legacy_const(); + } + + bool allow_let() { + return is_strict(language_mode()) || allow_harmony_sloppy_let(); + } + + // Report syntax errors. + void ReportMessage(MessageTemplate::Template message, const char* arg = NULL, + ParseErrorType error_type = kSyntaxError) { + Scanner::Location source_location = scanner()->location(); + Traits::ReportMessageAt(source_location, message, arg, error_type); + } + + void ReportMessageAt(Scanner::Location location, + MessageTemplate::Template message, + ParseErrorType error_type = kSyntaxError) { + Traits::ReportMessageAt(location, message, reinterpret_cast<const char*>(0), + error_type); + } + + void GetUnexpectedTokenMessage( + Token::Value token, MessageTemplate::Template* message, const char** arg, + MessageTemplate::Template default_ = MessageTemplate::kUnexpectedToken); + + void ReportUnexpectedToken(Token::Value token); + void ReportUnexpectedTokenAt( + Scanner::Location location, Token::Value token, + MessageTemplate::Template message = MessageTemplate::kUnexpectedToken); + + + void ReportClassifierError(const ExpressionClassifier::Error& error) { + Traits::ReportMessageAt(error.location, error.message, error.arg, + error.type); + } + + void ValidateExpression(const ExpressionClassifier* classifier, bool* ok) { + if (!classifier->is_valid_expression() || + classifier->has_cover_initialized_name()) { + const Scanner::Location& a = classifier->expression_error().location; + const Scanner::Location& b = + classifier->cover_initialized_name_error().location; + if (a.beg_pos < 0 || (b.beg_pos >= 0 && a.beg_pos > b.beg_pos)) { + ReportClassifierError(classifier->cover_initialized_name_error()); + } else { + ReportClassifierError(classifier->expression_error()); + } + *ok = false; + } + } + + void ValidateFormalParameterInitializer( + const ExpressionClassifier* classifier, bool* ok) { + if (!classifier->is_valid_formal_parameter_initializer()) { + ReportClassifierError(classifier->formal_parameter_initializer_error()); + *ok = false; + } + } + + void ValidateBindingPattern(const ExpressionClassifier* classifier, + bool* ok) { + if (!classifier->is_valid_binding_pattern()) { + ReportClassifierError(classifier->binding_pattern_error()); + *ok = false; + } + } + + void ValidateAssignmentPattern(const ExpressionClassifier* classifier, + bool* ok) { + if (!classifier->is_valid_assignment_pattern()) { + ReportClassifierError(classifier->assignment_pattern_error()); + *ok = false; + } + } + + void ValidateFormalParameters(const ExpressionClassifier* classifier, + LanguageMode language_mode, + bool allow_duplicates, bool* ok) { + if (!allow_duplicates && + !classifier->is_valid_formal_parameter_list_without_duplicates()) { + ReportClassifierError(classifier->duplicate_formal_parameter_error()); + *ok = false; + } else if (is_strict(language_mode) && + !classifier->is_valid_strict_mode_formal_parameters()) { + ReportClassifierError(classifier->strict_mode_formal_parameter_error()); + *ok = false; + } else if (is_strong(language_mode) && + !classifier->is_valid_strong_mode_formal_parameters()) { + ReportClassifierError(classifier->strong_mode_formal_parameter_error()); + *ok = false; + } + } + + void ValidateArrowFormalParameters(const ExpressionClassifier* classifier, + ExpressionT expr, + bool parenthesized_formals, bool* ok) { + if (classifier->is_valid_binding_pattern()) { + // A simple arrow formal parameter: IDENTIFIER => BODY. + if (!this->IsIdentifier(expr)) { + Traits::ReportMessageAt(scanner()->location(), + MessageTemplate::kUnexpectedToken, + Token::String(scanner()->current_token())); + *ok = false; + } + } else if (!classifier->is_valid_arrow_formal_parameters()) { + // If after parsing the expr, we see an error but the expression is + // neither a valid binding pattern nor a valid parenthesized formal + // parameter list, show the "arrow formal parameters" error if the formals + // started with a parenthesis, and the binding pattern error otherwise. + const ExpressionClassifier::Error& error = + parenthesized_formals ? classifier->arrow_formal_parameters_error() + : classifier->binding_pattern_error(); + ReportClassifierError(error); + *ok = false; + } + } + + void ValidateLetPattern(const ExpressionClassifier* classifier, bool* ok) { + if (!classifier->is_valid_let_pattern()) { + ReportClassifierError(classifier->let_pattern_error()); + *ok = false; + } + } + + void ExpressionUnexpectedToken(ExpressionClassifier* classifier) { + MessageTemplate::Template message = MessageTemplate::kUnexpectedToken; + const char* arg; + GetUnexpectedTokenMessage(peek(), &message, &arg); + classifier->RecordExpressionError(scanner()->peek_location(), message, arg); + } + + void BindingPatternUnexpectedToken(ExpressionClassifier* classifier) { + MessageTemplate::Template message = MessageTemplate::kUnexpectedToken; + const char* arg; + GetUnexpectedTokenMessage(peek(), &message, &arg); + classifier->RecordBindingPatternError(scanner()->peek_location(), message, + arg); + } + + void ArrowFormalParametersUnexpectedToken(ExpressionClassifier* classifier) { + MessageTemplate::Template message = MessageTemplate::kUnexpectedToken; + const char* arg; + GetUnexpectedTokenMessage(peek(), &message, &arg); + classifier->RecordArrowFormalParametersError(scanner()->peek_location(), + message, arg); + } + + void FormalParameterInitializerUnexpectedToken( + ExpressionClassifier* classifier) { + MessageTemplate::Template message = MessageTemplate::kUnexpectedToken; + const char* arg; + GetUnexpectedTokenMessage(peek(), &message, &arg); + classifier->RecordFormalParameterInitializerError( + scanner()->peek_location(), message, arg); + } + + // Recursive descent functions: + + // Parses an identifier that is valid for the current scope, in particular it + // fails on strict mode future reserved keywords in a strict scope. If + // allow_eval_or_arguments is kAllowEvalOrArguments, we allow "eval" or + // "arguments" as identifier even in strict mode (this is needed in cases like + // "var foo = eval;"). + IdentifierT ParseIdentifier(AllowRestrictedIdentifiers, bool* ok); + IdentifierT ParseAndClassifyIdentifier(ExpressionClassifier* classifier, + bool* ok); + // Parses an identifier or a strict mode future reserved word, and indicate + // whether it is strict mode future reserved. Allows passing in is_generator + // for the case of parsing the identifier in a function expression, where the + // relevant "is_generator" bit is of the function being parsed, not the + // containing + // function. + IdentifierT ParseIdentifierOrStrictReservedWord(bool is_generator, + bool* is_strict_reserved, + bool* ok); + IdentifierT ParseIdentifierOrStrictReservedWord(bool* is_strict_reserved, + bool* ok) { + return ParseIdentifierOrStrictReservedWord(this->is_generator(), + is_strict_reserved, ok); + } + + IdentifierT ParseIdentifierName(bool* ok); + // Parses an identifier and determines whether or not it is 'get' or 'set'. + IdentifierT ParseIdentifierNameOrGetOrSet(bool* is_get, bool* is_set, + bool* ok); + + + ExpressionT ParseRegExpLiteral(bool seen_equal, + ExpressionClassifier* classifier, bool* ok); + + ExpressionT ParsePrimaryExpression(ExpressionClassifier* classifier, + bool* ok); + ExpressionT ParseExpression(bool accept_IN, bool* ok); + ExpressionT ParseExpression(bool accept_IN, ExpressionClassifier* classifier, + bool* ok); + ExpressionT ParseExpression(bool accept_IN, int flags, + ExpressionClassifier* classifier, bool* ok); + ExpressionT ParseArrayLiteral(ExpressionClassifier* classifier, bool* ok); + ExpressionT ParsePropertyName(IdentifierT* name, bool* is_get, bool* is_set, + bool* is_static, bool* is_computed_name, + bool* is_identifier, bool* is_escaped_keyword, + ExpressionClassifier* classifier, bool* ok); + ExpressionT ParseObjectLiteral(ExpressionClassifier* classifier, bool* ok); + ObjectLiteralPropertyT ParsePropertyDefinition( + ObjectLiteralCheckerBase* checker, bool in_class, bool has_extends, + bool is_static, bool* is_computed_name, bool* has_seen_constructor, + ExpressionClassifier* classifier, IdentifierT* name, bool* ok); + typename Traits::Type::ExpressionList ParseArguments( + Scanner::Location* first_spread_pos, ExpressionClassifier* classifier, + bool* ok); + + enum AssignmentExpressionFlags { + kIsNormalAssignment = 0, + kIsPossiblePatternElement = 1 << 0, + kIsPossibleArrowFormals = 1 << 1 + }; + + ExpressionT ParseAssignmentExpression(bool accept_IN, int flags, + ExpressionClassifier* classifier, + bool* ok); + ExpressionT ParseAssignmentExpression(bool accept_IN, + ExpressionClassifier* classifier, + bool* ok) { + return ParseAssignmentExpression(accept_IN, kIsNormalAssignment, classifier, + ok); + } + ExpressionT ParseYieldExpression(ExpressionClassifier* classifier, bool* ok); + ExpressionT ParseConditionalExpression(bool accept_IN, + ExpressionClassifier* classifier, + bool* ok); + ExpressionT ParseBinaryExpression(int prec, bool accept_IN, + ExpressionClassifier* classifier, bool* ok); + ExpressionT ParseUnaryExpression(ExpressionClassifier* classifier, bool* ok); + ExpressionT ParsePostfixExpression(ExpressionClassifier* classifier, + bool* ok); + ExpressionT ParseLeftHandSideExpression(ExpressionClassifier* classifier, + bool* ok); + ExpressionT ParseMemberWithNewPrefixesExpression( + ExpressionClassifier* classifier, bool* ok); + ExpressionT ParseMemberExpression(ExpressionClassifier* classifier, bool* ok); + ExpressionT ParseMemberExpressionContinuation( + ExpressionT expression, ExpressionClassifier* classifier, bool* ok); + ExpressionT ParseArrowFunctionLiteral(bool accept_IN, + const FormalParametersT& parameters, + const ExpressionClassifier& classifier, + bool* ok); + ExpressionT ParseTemplateLiteral(ExpressionT tag, int start, + ExpressionClassifier* classifier, bool* ok); + void AddTemplateExpression(ExpressionT); + ExpressionT ParseSuperExpression(bool is_new, + ExpressionClassifier* classifier, bool* ok); + ExpressionT ParseNewTargetExpression(bool* ok); + ExpressionT ParseStrongInitializationExpression( + ExpressionClassifier* classifier, bool* ok); + ExpressionT ParseStrongSuperCallExpression(ExpressionClassifier* classifier, + bool* ok); + + void ParseFormalParameter(FormalParametersT* parameters, + ExpressionClassifier* classifier, bool* ok); + void ParseFormalParameterList(FormalParametersT* parameters, + ExpressionClassifier* classifier, bool* ok); + void CheckArityRestrictions( + int param_count, FunctionLiteral::ArityRestriction arity_restriction, + bool has_rest, int formals_start_pos, int formals_end_pos, bool* ok); + + bool IsNextLetKeyword(); + + // Checks if the expression is a valid reference expression (e.g., on the + // left-hand side of assignments). Although ruled out by ECMA as early errors, + // we allow calls for web compatibility and rewrite them to a runtime throw. + ExpressionT CheckAndRewriteReferenceExpression( + ExpressionT expression, int beg_pos, int end_pos, + MessageTemplate::Template message, bool* ok); + ExpressionT ClassifyAndRewriteReferenceExpression( + ExpressionClassifier* classifier, ExpressionT expression, int beg_pos, + int end_pos, MessageTemplate::Template message, + ParseErrorType type = kSyntaxError); + ExpressionT CheckAndRewriteReferenceExpression( + ExpressionT expression, int beg_pos, int end_pos, + MessageTemplate::Template message, ParseErrorType type, bool* ok); + + bool IsValidReferenceExpression(ExpressionT expression); + + bool IsAssignableIdentifier(ExpressionT expression) { + if (!Traits::IsIdentifier(expression)) return false; + if (is_strict(language_mode()) && + Traits::IsEvalOrArguments(Traits::AsIdentifier(expression))) { + return false; + } + if (is_strong(language_mode()) && + Traits::IsUndefined(Traits::AsIdentifier(expression))) { + return false; + } + return true; + } + + // Keep track of eval() calls since they disable all local variable + // optimizations. This checks if expression is an eval call, and if yes, + // forwards the information to scope. + void CheckPossibleEvalCall(ExpressionT expression, Scope* scope) { + if (Traits::IsIdentifier(expression) && + Traits::IsEval(Traits::AsIdentifier(expression))) { + scope->DeclarationScope()->RecordEvalCall(); + scope->RecordEvalCall(); + } + } + + // Used to validate property names in object literals and class literals + enum PropertyKind { + kAccessorProperty, + kValueProperty, + kMethodProperty + }; + + class ObjectLiteralCheckerBase { + public: + explicit ObjectLiteralCheckerBase(ParserBase* parser) : parser_(parser) {} + + virtual void CheckProperty(Token::Value property, PropertyKind type, + bool is_static, bool is_generator, bool* ok) = 0; + + virtual ~ObjectLiteralCheckerBase() {} + + protected: + ParserBase* parser() const { return parser_; } + Scanner* scanner() const { return parser_->scanner(); } + + private: + ParserBase* parser_; + }; + + // Validation per ES6 object literals. + class ObjectLiteralChecker : public ObjectLiteralCheckerBase { + public: + explicit ObjectLiteralChecker(ParserBase* parser) + : ObjectLiteralCheckerBase(parser), has_seen_proto_(false) {} + + void CheckProperty(Token::Value property, PropertyKind type, bool is_static, + bool is_generator, bool* ok) override; + + private: + bool IsProto() { return this->scanner()->LiteralMatches("__proto__", 9); } + + bool has_seen_proto_; + }; + + // Validation per ES6 class literals. + class ClassLiteralChecker : public ObjectLiteralCheckerBase { + public: + explicit ClassLiteralChecker(ParserBase* parser) + : ObjectLiteralCheckerBase(parser), has_seen_constructor_(false) {} + + void CheckProperty(Token::Value property, PropertyKind type, bool is_static, + bool is_generator, bool* ok) override; + + private: + bool IsConstructor() { + return this->scanner()->LiteralMatches("constructor", 11); + } + bool IsPrototype() { + return this->scanner()->LiteralMatches("prototype", 9); + } + + bool has_seen_constructor_; + }; + + // If true, the next (and immediately following) function literal is + // preceded by a parenthesis. + // Heuristically that means that the function will be called immediately, + // so never lazily compile it. + bool parenthesized_function_; + + Scope* scope_; // Scope stack. + FunctionState* function_state_; // Function state stack. + v8::Extension* extension_; + FuncNameInferrer* fni_; + AstValueFactory* ast_value_factory_; // Not owned. + ParserRecorder* log_; + Mode mode_; + uintptr_t stack_limit_; + + private: + Zone* zone_; + + Scanner* scanner_; + bool stack_overflow_; + + bool allow_lazy_; + bool allow_natives_; + bool allow_harmony_sloppy_; + bool allow_harmony_sloppy_function_; + bool allow_harmony_sloppy_let_; + bool allow_harmony_default_parameters_; + bool allow_harmony_destructuring_bind_; + bool allow_harmony_destructuring_assignment_; + bool allow_strong_mode_; + bool allow_legacy_const_; + bool allow_harmony_do_expressions_; + bool allow_harmony_function_name_; +}; + + +template <class Traits> +ParserBase<Traits>::FunctionState::FunctionState( + FunctionState** function_state_stack, Scope** scope_stack, Scope* scope, + FunctionKind kind, typename Traits::Type::Factory* factory) + : next_materialized_literal_index_(0), + expected_property_count_(0), + this_location_(Scanner::Location::invalid()), + return_location_(Scanner::Location::invalid()), + super_location_(Scanner::Location::invalid()), + kind_(kind), + generator_object_variable_(NULL), + function_state_stack_(function_state_stack), + outer_function_state_(*function_state_stack), + scope_stack_(scope_stack), + outer_scope_(*scope_stack), + factory_(factory) { + *scope_stack_ = scope; + *function_state_stack = this; +} + + +template <class Traits> +ParserBase<Traits>::FunctionState::~FunctionState() { + *scope_stack_ = outer_scope_; + *function_state_stack_ = outer_function_state_; +} + + +template <class Traits> +void ParserBase<Traits>::GetUnexpectedTokenMessage( + Token::Value token, MessageTemplate::Template* message, const char** arg, + MessageTemplate::Template default_) { + // Four of the tokens are treated specially + switch (token) { + case Token::EOS: + *message = MessageTemplate::kUnexpectedEOS; + *arg = nullptr; + break; + case Token::SMI: + case Token::NUMBER: + *message = MessageTemplate::kUnexpectedTokenNumber; + *arg = nullptr; + break; + case Token::STRING: + *message = MessageTemplate::kUnexpectedTokenString; + *arg = nullptr; + break; + case Token::IDENTIFIER: + *message = MessageTemplate::kUnexpectedTokenIdentifier; + *arg = nullptr; + break; + case Token::FUTURE_RESERVED_WORD: + *message = MessageTemplate::kUnexpectedReserved; + *arg = nullptr; + break; + case Token::LET: + case Token::STATIC: + case Token::YIELD: + case Token::FUTURE_STRICT_RESERVED_WORD: + *message = is_strict(language_mode()) + ? MessageTemplate::kUnexpectedStrictReserved + : MessageTemplate::kUnexpectedTokenIdentifier; + *arg = nullptr; + break; + case Token::TEMPLATE_SPAN: + case Token::TEMPLATE_TAIL: + *message = MessageTemplate::kUnexpectedTemplateString; + *arg = nullptr; + break; + case Token::ESCAPED_STRICT_RESERVED_WORD: + case Token::ESCAPED_KEYWORD: + *message = MessageTemplate::kInvalidEscapedReservedWord; + *arg = nullptr; + break; + default: + const char* name = Token::String(token); + DCHECK(name != NULL); + *arg = name; + break; + } +} + + +template <class Traits> +void ParserBase<Traits>::ReportUnexpectedToken(Token::Value token) { + return ReportUnexpectedTokenAt(scanner_->location(), token); +} + + +template <class Traits> +void ParserBase<Traits>::ReportUnexpectedTokenAt( + Scanner::Location source_location, Token::Value token, + MessageTemplate::Template message) { + const char* arg; + GetUnexpectedTokenMessage(token, &message, &arg); + Traits::ReportMessageAt(source_location, message, arg); +} + + +template <class Traits> +typename ParserBase<Traits>::IdentifierT ParserBase<Traits>::ParseIdentifier( + AllowRestrictedIdentifiers allow_restricted_identifiers, bool* ok) { + ExpressionClassifier classifier; + auto result = ParseAndClassifyIdentifier(&classifier, ok); + if (!*ok) return Traits::EmptyIdentifier(); + + if (allow_restricted_identifiers == kDontAllowRestrictedIdentifiers) { + ValidateAssignmentPattern(&classifier, ok); + if (!*ok) return Traits::EmptyIdentifier(); + ValidateBindingPattern(&classifier, ok); + if (!*ok) return Traits::EmptyIdentifier(); + } + + return result; +} + + +template <class Traits> +typename ParserBase<Traits>::IdentifierT +ParserBase<Traits>::ParseAndClassifyIdentifier(ExpressionClassifier* classifier, + bool* ok) { + Token::Value next = Next(); + if (next == Token::IDENTIFIER) { + IdentifierT name = this->GetSymbol(scanner()); + // When this function is used to read a formal parameter, we don't always + // know whether the function is going to be strict or sloppy. Indeed for + // arrow functions we don't always know that the identifier we are reading + // is actually a formal parameter. Therefore besides the errors that we + // must detect because we know we're in strict mode, we also record any + // error that we might make in the future once we know the language mode. + if (this->IsEval(name)) { + classifier->RecordStrictModeFormalParameterError( + scanner()->location(), MessageTemplate::kStrictEvalArguments); + if (is_strict(language_mode())) { + classifier->RecordBindingPatternError( + scanner()->location(), MessageTemplate::kStrictEvalArguments); + } + } + if (this->IsArguments(name)) { + scope_->RecordArgumentsUsage(); + classifier->RecordStrictModeFormalParameterError( + scanner()->location(), MessageTemplate::kStrictEvalArguments); + if (is_strict(language_mode())) { + classifier->RecordBindingPatternError( + scanner()->location(), MessageTemplate::kStrictEvalArguments); + } + if (is_strong(language_mode())) { + classifier->RecordExpressionError(scanner()->location(), + MessageTemplate::kStrongArguments); + } + } + if (this->IsUndefined(name)) { + classifier->RecordStrongModeFormalParameterError( + scanner()->location(), MessageTemplate::kStrongUndefined); + if (is_strong(language_mode())) { + // TODO(dslomov): allow 'undefined' in nested patterns. + classifier->RecordBindingPatternError( + scanner()->location(), MessageTemplate::kStrongUndefined); + classifier->RecordAssignmentPatternError( + scanner()->location(), MessageTemplate::kStrongUndefined); + } + } + + if (classifier->duplicate_finder() != nullptr && + scanner()->FindSymbol(classifier->duplicate_finder(), 1) != 0) { + classifier->RecordDuplicateFormalParameterError(scanner()->location()); + } + return name; + } else if (is_sloppy(language_mode()) && + (next == Token::FUTURE_STRICT_RESERVED_WORD || + next == Token::ESCAPED_STRICT_RESERVED_WORD || + next == Token::LET || next == Token::STATIC || + (next == Token::YIELD && !is_generator()))) { + classifier->RecordStrictModeFormalParameterError( + scanner()->location(), MessageTemplate::kUnexpectedStrictReserved); + if (next == Token::ESCAPED_STRICT_RESERVED_WORD && + is_strict(language_mode())) { + ReportUnexpectedToken(next); + *ok = false; + return Traits::EmptyIdentifier(); + } + if (next == Token::LET) { + classifier->RecordLetPatternError(scanner()->location(), + MessageTemplate::kLetInLexicalBinding); + } + return this->GetSymbol(scanner()); + } else { + this->ReportUnexpectedToken(next); + *ok = false; + return Traits::EmptyIdentifier(); + } +} + + +template <class Traits> +typename ParserBase<Traits>::IdentifierT +ParserBase<Traits>::ParseIdentifierOrStrictReservedWord( + bool is_generator, bool* is_strict_reserved, bool* ok) { + Token::Value next = Next(); + if (next == Token::IDENTIFIER) { + *is_strict_reserved = false; + } else if (next == Token::FUTURE_STRICT_RESERVED_WORD || next == Token::LET || + next == Token::STATIC || (next == Token::YIELD && !is_generator)) { + *is_strict_reserved = true; + } else { + ReportUnexpectedToken(next); + *ok = false; + return Traits::EmptyIdentifier(); + } + + IdentifierT name = this->GetSymbol(scanner()); + if (this->IsArguments(name)) scope_->RecordArgumentsUsage(); + return name; +} + + +template <class Traits> +typename ParserBase<Traits>::IdentifierT +ParserBase<Traits>::ParseIdentifierName(bool* ok) { + Token::Value next = Next(); + if (next != Token::IDENTIFIER && next != Token::FUTURE_RESERVED_WORD && + next != Token::LET && next != Token::STATIC && next != Token::YIELD && + next != Token::FUTURE_STRICT_RESERVED_WORD && + next != Token::ESCAPED_KEYWORD && + next != Token::ESCAPED_STRICT_RESERVED_WORD && !Token::IsKeyword(next)) { + this->ReportUnexpectedToken(next); + *ok = false; + return Traits::EmptyIdentifier(); + } + + IdentifierT name = this->GetSymbol(scanner()); + if (this->IsArguments(name)) scope_->RecordArgumentsUsage(); + return name; +} + + +template <class Traits> +typename ParserBase<Traits>::IdentifierT +ParserBase<Traits>::ParseIdentifierNameOrGetOrSet(bool* is_get, + bool* is_set, + bool* ok) { + IdentifierT result = ParseIdentifierName(ok); + if (!*ok) return Traits::EmptyIdentifier(); + scanner()->IsGetOrSet(is_get, is_set); + return result; +} + + +template <class Traits> +typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseRegExpLiteral( + bool seen_equal, ExpressionClassifier* classifier, bool* ok) { + int pos = peek_position(); + if (!scanner()->ScanRegExpPattern(seen_equal)) { + Next(); + ReportMessage(MessageTemplate::kUnterminatedRegExp); + *ok = false; + return Traits::EmptyExpression(); + } + + int literal_index = function_state_->NextMaterializedLiteralIndex(); + + IdentifierT js_pattern = this->GetNextSymbol(scanner()); + Maybe<RegExp::Flags> flags = scanner()->ScanRegExpFlags(); + if (flags.IsNothing()) { + Next(); + ReportMessage(MessageTemplate::kMalformedRegExpFlags); + *ok = false; + return Traits::EmptyExpression(); + } + int js_flags = flags.FromJust(); + Next(); + return factory()->NewRegExpLiteral(js_pattern, js_flags, literal_index, + is_strong(language_mode()), pos); +} + + +#define CHECK_OK ok); \ + if (!*ok) return this->EmptyExpression(); \ + ((void)0 +#define DUMMY ) // to make indentation work +#undef DUMMY + +// Used in functions where the return type is not ExpressionT. +#define CHECK_OK_CUSTOM(x) ok); \ + if (!*ok) return this->x(); \ + ((void)0 +#define DUMMY ) // to make indentation work +#undef DUMMY + + +template <class Traits> +typename ParserBase<Traits>::ExpressionT +ParserBase<Traits>::ParsePrimaryExpression(ExpressionClassifier* classifier, + bool* ok) { + // PrimaryExpression :: + // 'this' + // 'null' + // 'true' + // 'false' + // Identifier + // Number + // String + // ArrayLiteral + // ObjectLiteral + // RegExpLiteral + // ClassLiteral + // '(' Expression ')' + // TemplateLiteral + // do Block + + int beg_pos = peek_position(); + switch (peek()) { + case Token::THIS: { + BindingPatternUnexpectedToken(classifier); + Consume(Token::THIS); + if (FLAG_strong_this && is_strong(language_mode())) { + // Constructors' usages of 'this' in strong mode are parsed separately. + // TODO(rossberg): this does not work with arrow functions yet. + if (IsClassConstructor(function_state_->kind())) { + ReportMessage(MessageTemplate::kStrongConstructorThis); + *ok = false; + return this->EmptyExpression(); + } + } + return this->ThisExpression(scope_, factory(), beg_pos); + } + + case Token::NULL_LITERAL: + case Token::TRUE_LITERAL: + case Token::FALSE_LITERAL: + BindingPatternUnexpectedToken(classifier); + return this->ExpressionFromLiteral(Next(), beg_pos, scanner(), factory()); + case Token::SMI: + case Token::NUMBER: + classifier->RecordBindingPatternError( + scanner()->peek_location(), MessageTemplate::kUnexpectedTokenNumber); + return this->ExpressionFromLiteral(Next(), beg_pos, scanner(), factory()); + + case Token::IDENTIFIER: + case Token::LET: + case Token::STATIC: + case Token::YIELD: + case Token::ESCAPED_STRICT_RESERVED_WORD: + case Token::FUTURE_STRICT_RESERVED_WORD: { + // Using eval or arguments in this context is OK even in strict mode. + IdentifierT name = ParseAndClassifyIdentifier(classifier, CHECK_OK); + return this->ExpressionFromIdentifier( + name, beg_pos, scanner()->location().end_pos, scope_, factory()); + } + + case Token::STRING: { + classifier->RecordBindingPatternError( + scanner()->peek_location(), MessageTemplate::kUnexpectedTokenString); + Consume(Token::STRING); + return this->ExpressionFromString(beg_pos, scanner(), factory()); + } + + case Token::ASSIGN_DIV: + classifier->RecordBindingPatternError( + scanner()->peek_location(), MessageTemplate::kUnexpectedTokenRegExp); + return this->ParseRegExpLiteral(true, classifier, ok); + + case Token::DIV: + classifier->RecordBindingPatternError( + scanner()->peek_location(), MessageTemplate::kUnexpectedTokenRegExp); + return this->ParseRegExpLiteral(false, classifier, ok); + + case Token::LBRACK: + if (!allow_harmony_destructuring_bind()) { + BindingPatternUnexpectedToken(classifier); + } + return this->ParseArrayLiteral(classifier, ok); + + case Token::LBRACE: + if (!allow_harmony_destructuring_bind()) { + BindingPatternUnexpectedToken(classifier); + } + return this->ParseObjectLiteral(classifier, ok); + + case Token::LPAREN: { + // Arrow function formal parameters are either a single identifier or a + // list of BindingPattern productions enclosed in parentheses. + // Parentheses are not valid on the LHS of a BindingPattern, so we use the + // is_valid_binding_pattern() check to detect multiple levels of + // parenthesization. + if (!classifier->is_valid_binding_pattern()) { + ArrowFormalParametersUnexpectedToken(classifier); + } + BindingPatternUnexpectedToken(classifier); + Consume(Token::LPAREN); + if (Check(Token::RPAREN)) { + // ()=>x. The continuation that looks for the => is in + // ParseAssignmentExpression. + classifier->RecordExpressionError(scanner()->location(), + MessageTemplate::kUnexpectedToken, + Token::String(Token::RPAREN)); + classifier->RecordBindingPatternError(scanner()->location(), + MessageTemplate::kUnexpectedToken, + Token::String(Token::RPAREN)); + return factory()->NewEmptyParentheses(beg_pos); + } else if (Check(Token::ELLIPSIS)) { + // (...x)=>x. The continuation that looks for the => is in + // ParseAssignmentExpression. + int ellipsis_pos = position(); + classifier->RecordExpressionError(scanner()->location(), + MessageTemplate::kUnexpectedToken, + Token::String(Token::ELLIPSIS)); + classifier->RecordNonSimpleParameter(); + ExpressionT expr = + this->ParseAssignmentExpression(true, classifier, CHECK_OK); + if (peek() == Token::COMMA) { + ReportMessageAt(scanner()->peek_location(), + MessageTemplate::kParamAfterRest); + *ok = false; + return this->EmptyExpression(); + } + Expect(Token::RPAREN, CHECK_OK); + return factory()->NewSpread(expr, ellipsis_pos); + } + // Heuristically try to detect immediately called functions before + // seeing the call parentheses. + parenthesized_function_ = (peek() == Token::FUNCTION); + ExpressionT expr = this->ParseExpression(true, kIsPossibleArrowFormals, + classifier, CHECK_OK); + Expect(Token::RPAREN, CHECK_OK); + if (peek() != Token::ARROW) { + expr->set_is_parenthesized(); + } + return expr; + } + + case Token::CLASS: { + BindingPatternUnexpectedToken(classifier); + Consume(Token::CLASS); + if (!allow_harmony_sloppy() && is_sloppy(language_mode())) { + ReportMessage(MessageTemplate::kSloppyLexical); + *ok = false; + return this->EmptyExpression(); + } + int class_token_position = position(); + IdentifierT name = this->EmptyIdentifier(); + bool is_strict_reserved_name = false; + Scanner::Location class_name_location = Scanner::Location::invalid(); + if (peek_any_identifier()) { + name = ParseIdentifierOrStrictReservedWord(&is_strict_reserved_name, + CHECK_OK); + class_name_location = scanner()->location(); + } + return this->ParseClassLiteral(name, class_name_location, + is_strict_reserved_name, + class_token_position, ok); + } + + case Token::TEMPLATE_SPAN: + case Token::TEMPLATE_TAIL: + classifier->RecordBindingPatternError( + scanner()->peek_location(), + MessageTemplate::kUnexpectedTemplateString); + return this->ParseTemplateLiteral(Traits::NoTemplateTag(), beg_pos, + classifier, ok); + + case Token::MOD: + if (allow_natives() || extension_ != NULL) { + BindingPatternUnexpectedToken(classifier); + return this->ParseV8Intrinsic(ok); + } + break; + + case Token::DO: + if (allow_harmony_do_expressions()) { + BindingPatternUnexpectedToken(classifier); + return Traits::ParseDoExpression(ok); + } + break; + + default: + break; + } + + ReportUnexpectedToken(Next()); + *ok = false; + return this->EmptyExpression(); +} + + +template <class Traits> +typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression( + bool accept_IN, bool* ok) { + ExpressionClassifier classifier; + ExpressionT result = ParseExpression(accept_IN, &classifier, CHECK_OK); + result = Traits::RewriteNonPattern(result, &classifier, CHECK_OK); + return result; +} + + +template <class Traits> +typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression( + bool accept_IN, ExpressionClassifier* classifier, bool* ok) { + return ParseExpression(accept_IN, kIsNormalAssignment, classifier, ok); +} + + +template <class Traits> +typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression( + bool accept_IN, int flags, ExpressionClassifier* classifier, bool* ok) { + // Expression :: + // AssignmentExpression + // Expression ',' AssignmentExpression + + ExpressionClassifier binding_classifier; + ExpressionT result = this->ParseAssignmentExpression( + accept_IN, flags, &binding_classifier, CHECK_OK); + classifier->Accumulate(binding_classifier, + ExpressionClassifier::AllProductions); + bool is_simple_parameter_list = this->IsIdentifier(result); + bool seen_rest = false; + while (peek() == Token::COMMA) { + if (seen_rest) { + // At this point the production can't possibly be valid, but we don't know + // which error to signal. + classifier->RecordArrowFormalParametersError( + scanner()->peek_location(), MessageTemplate::kParamAfterRest); + } + Consume(Token::COMMA); + bool is_rest = false; + if (peek() == Token::ELLIPSIS) { + // 'x, y, ...z' in CoverParenthesizedExpressionAndArrowParameterList only + // as the formal parameters of'(x, y, ...z) => foo', and is not itself a + // valid expression or binding pattern. + ExpressionUnexpectedToken(classifier); + BindingPatternUnexpectedToken(classifier); + Consume(Token::ELLIPSIS); + seen_rest = is_rest = true; + } + int pos = position(); + ExpressionT right = this->ParseAssignmentExpression( + accept_IN, flags, &binding_classifier, CHECK_OK); + if (is_rest) right = factory()->NewSpread(right, pos); + is_simple_parameter_list = + is_simple_parameter_list && this->IsIdentifier(right); + classifier->Accumulate(binding_classifier, + ExpressionClassifier::AllProductions); + result = factory()->NewBinaryOperation(Token::COMMA, result, right, pos); + } + if (!is_simple_parameter_list || seen_rest) { + classifier->RecordNonSimpleParameter(); + } + + return result; +} + + +template <class Traits> +typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseArrayLiteral( + ExpressionClassifier* classifier, bool* ok) { + // ArrayLiteral :: + // '[' Expression? (',' Expression?)* ']' + + int pos = peek_position(); + typename Traits::Type::ExpressionList values = + this->NewExpressionList(4, zone_); + int first_spread_index = -1; + Expect(Token::LBRACK, CHECK_OK); + while (peek() != Token::RBRACK) { + ExpressionT elem = this->EmptyExpression(); + if (peek() == Token::COMMA) { + if (is_strong(language_mode())) { + ReportMessageAt(scanner()->peek_location(), + MessageTemplate::kStrongEllision); + *ok = false; + return this->EmptyExpression(); + } + elem = this->GetLiteralTheHole(peek_position(), factory()); + } else if (peek() == Token::ELLIPSIS) { + int start_pos = peek_position(); + Consume(Token::ELLIPSIS); + ExpressionT argument = + this->ParseAssignmentExpression(true, classifier, CHECK_OK); + elem = factory()->NewSpread(argument, start_pos); + + if (first_spread_index < 0) { + first_spread_index = values->length(); + } + + if (argument->IsAssignment()) { + classifier->RecordPatternError( + Scanner::Location(start_pos, scanner()->location().end_pos), + MessageTemplate::kInvalidDestructuringTarget); + } else { + CheckDestructuringElement(argument, classifier, start_pos, + scanner()->location().end_pos); + } + + if (peek() == Token::COMMA) { + classifier->RecordPatternError( + Scanner::Location(start_pos, scanner()->location().end_pos), + MessageTemplate::kElementAfterRest); + } + } else { + elem = this->ParseAssignmentExpression(true, kIsPossiblePatternElement, + classifier, CHECK_OK); + } + values->Add(elem, zone_); + 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 = function_state_->NextMaterializedLiteralIndex(); + + return factory()->NewArrayLiteral(values, first_spread_index, literal_index, + is_strong(language_mode()), pos); +} + + +template <class Traits> +typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParsePropertyName( + IdentifierT* name, bool* is_get, bool* is_set, bool* is_static, + bool* is_computed_name, bool* is_identifier, bool* is_escaped_keyword, + ExpressionClassifier* classifier, bool* ok) { + Token::Value token = peek(); + int pos = peek_position(); + + // For non computed property names we normalize the name a bit: + // + // "12" -> 12 + // 12.3 -> "12.3" + // 12.30 -> "12.3" + // identifier -> "identifier" + // + // This is important because we use the property name as a key in a hash + // table when we compute constant properties. + switch (token) { + case Token::STRING: + Consume(Token::STRING); + *name = this->GetSymbol(scanner()); + break; + + case Token::SMI: + Consume(Token::SMI); + *name = this->GetNumberAsSymbol(scanner()); + break; + + case Token::NUMBER: + Consume(Token::NUMBER); + *name = this->GetNumberAsSymbol(scanner()); + break; + + case Token::LBRACK: { + *is_computed_name = true; + Consume(Token::LBRACK); + ExpressionClassifier computed_name_classifier; + ExpressionT expression = + ParseAssignmentExpression(true, &computed_name_classifier, CHECK_OK); + expression = Traits::RewriteNonPattern( + expression, &computed_name_classifier, CHECK_OK); + classifier->Accumulate(computed_name_classifier, + ExpressionClassifier::ExpressionProductions); + Expect(Token::RBRACK, CHECK_OK); + return expression; + } + + case Token::ESCAPED_KEYWORD: + *is_escaped_keyword = true; + *name = ParseIdentifierNameOrGetOrSet(is_get, is_set, CHECK_OK); + break; + + case Token::STATIC: + *is_static = true; + + // Fall through. + default: + *is_identifier = true; + *name = ParseIdentifierNameOrGetOrSet(is_get, is_set, CHECK_OK); + break; + } + + uint32_t index; + return this->IsArrayIndex(*name, &index) + ? factory()->NewNumberLiteral(index, pos) + : factory()->NewStringLiteral(*name, pos); +} + + +template <class Traits> +typename ParserBase<Traits>::ObjectLiteralPropertyT +ParserBase<Traits>::ParsePropertyDefinition( + ObjectLiteralCheckerBase* checker, bool in_class, bool has_extends, + bool is_static, bool* is_computed_name, bool* has_seen_constructor, + ExpressionClassifier* classifier, IdentifierT* name, bool* ok) { + DCHECK(!in_class || is_static || has_seen_constructor != nullptr); + ExpressionT value = this->EmptyExpression(); + bool is_get = false; + bool is_set = false; + bool name_is_static = false; + bool is_generator = Check(Token::MUL); + + Token::Value name_token = peek(); + int next_beg_pos = scanner()->peek_location().beg_pos; + int next_end_pos = scanner()->peek_location().end_pos; + bool is_identifier = false; + bool is_escaped_keyword = false; + ExpressionT name_expression = ParsePropertyName( + name, &is_get, &is_set, &name_is_static, is_computed_name, &is_identifier, + &is_escaped_keyword, classifier, + CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); + + if (fni_ != nullptr && !*is_computed_name) { + this->PushLiteralName(fni_, *name); + } + + bool escaped_static = + is_escaped_keyword && + scanner()->is_literal_contextual_keyword(CStrVector("static")); + + if (!in_class && !is_generator) { + DCHECK(!is_static); + + if (peek() == Token::COLON) { + // PropertyDefinition + // PropertyName ':' AssignmentExpression + if (!*is_computed_name) { + checker->CheckProperty(name_token, kValueProperty, false, false, + CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); + } + Consume(Token::COLON); + value = this->ParseAssignmentExpression( + true, kIsPossiblePatternElement, classifier, + CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); + + return factory()->NewObjectLiteralProperty(name_expression, value, false, + *is_computed_name); + } + + if ((is_identifier || is_escaped_keyword) && + (peek() == Token::COMMA || peek() == Token::RBRACE || + peek() == Token::ASSIGN)) { + // PropertyDefinition + // IdentifierReference + // CoverInitializedName + // + // CoverInitializedName + // IdentifierReference Initializer? + if (!Token::IsIdentifier(name_token, language_mode(), + this->is_generator())) { + if (!escaped_static) { + ReportUnexpectedTokenAt(scanner()->location(), name_token); + *ok = false; + return this->EmptyObjectLiteralProperty(); + } + } + if (classifier->duplicate_finder() != nullptr && + scanner()->FindSymbol(classifier->duplicate_finder(), 1) != 0) { + classifier->RecordDuplicateFormalParameterError(scanner()->location()); + } + if (name_token == Token::LET) { + classifier->RecordLetPatternError( + scanner()->location(), MessageTemplate::kLetInLexicalBinding); + } + + ExpressionT lhs = this->ExpressionFromIdentifier( + *name, next_beg_pos, next_end_pos, scope_, factory()); + CheckDestructuringElement(lhs, classifier, next_beg_pos, next_end_pos); + + if (peek() == Token::ASSIGN) { + Consume(Token::ASSIGN); + ExpressionClassifier rhs_classifier; + ExpressionT rhs = this->ParseAssignmentExpression( + true, &rhs_classifier, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); + rhs = Traits::RewriteNonPattern( + rhs, &rhs_classifier, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); + classifier->Accumulate(rhs_classifier, + ExpressionClassifier::ExpressionProductions); + value = factory()->NewAssignment(Token::ASSIGN, lhs, rhs, + RelocInfo::kNoPosition); + classifier->RecordCoverInitializedNameError( + Scanner::Location(next_beg_pos, scanner()->location().end_pos), + MessageTemplate::kInvalidCoverInitializedName); + } else { + value = lhs; + } + + return factory()->NewObjectLiteralProperty( + name_expression, value, ObjectLiteralProperty::COMPUTED, false, + false); + } + } + + if (in_class && escaped_static && !is_static) { + ReportUnexpectedTokenAt(scanner()->location(), name_token); + *ok = false; + return this->EmptyObjectLiteralProperty(); + } + + // Method definitions are never valid in patterns. + classifier->RecordPatternError( + Scanner::Location(next_beg_pos, scanner()->location().end_pos), + MessageTemplate::kInvalidDestructuringTarget); + + if (is_generator || peek() == Token::LPAREN) { + // MethodDefinition + // PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}' + // '*' PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}' + if (!*is_computed_name) { + checker->CheckProperty(name_token, kMethodProperty, is_static, + is_generator, + CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); + } + + FunctionKind kind = is_generator ? FunctionKind::kConciseGeneratorMethod + : FunctionKind::kConciseMethod; + + if (in_class && !is_static && this->IsConstructor(*name)) { + *has_seen_constructor = true; + kind = has_extends ? FunctionKind::kSubclassConstructor + : FunctionKind::kBaseConstructor; + } + + if (!in_class) kind = WithObjectLiteralBit(kind); + + value = this->ParseFunctionLiteral( + *name, scanner()->location(), kSkipFunctionNameCheck, kind, + RelocInfo::kNoPosition, FunctionLiteral::kAnonymousExpression, + FunctionLiteral::kNormalArity, language_mode(), + CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); + + return factory()->NewObjectLiteralProperty(name_expression, value, + ObjectLiteralProperty::COMPUTED, + is_static, *is_computed_name); + } + + if (in_class && name_is_static && !is_static) { + // ClassElement (static) + // 'static' MethodDefinition + *name = this->EmptyIdentifier(); + ObjectLiteralPropertyT property = ParsePropertyDefinition( + checker, true, has_extends, true, is_computed_name, nullptr, classifier, + name, ok); + property = Traits::RewriteNonPatternObjectLiteralProperty(property, + classifier, ok); + return property; + } + + if (is_get || is_set) { + // MethodDefinition (Accessors) + // get PropertyName '(' ')' '{' FunctionBody '}' + // set PropertyName '(' PropertySetParameterList ')' '{' FunctionBody '}' + *name = this->EmptyIdentifier(); + bool dont_care = false; + name_token = peek(); + + name_expression = ParsePropertyName( + name, &dont_care, &dont_care, &dont_care, is_computed_name, &dont_care, + &dont_care, classifier, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); + + if (!*is_computed_name) { + checker->CheckProperty(name_token, kAccessorProperty, is_static, + is_generator, + CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); + } + + FunctionKind kind = FunctionKind::kAccessorFunction; + if (!in_class) kind = WithObjectLiteralBit(kind); + typename Traits::Type::FunctionLiteral value = this->ParseFunctionLiteral( + *name, scanner()->location(), kSkipFunctionNameCheck, kind, + RelocInfo::kNoPosition, FunctionLiteral::kAnonymousExpression, + is_get ? FunctionLiteral::kGetterArity : FunctionLiteral::kSetterArity, + language_mode(), CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); + + // Make sure the name expression is a string since we need a Name for + // Runtime_DefineAccessorPropertyUnchecked and since we can determine this + // statically we can skip the extra runtime check. + if (!*is_computed_name) { + name_expression = + factory()->NewStringLiteral(*name, name_expression->position()); + } + + return factory()->NewObjectLiteralProperty( + name_expression, value, + is_get ? ObjectLiteralProperty::GETTER : ObjectLiteralProperty::SETTER, + is_static, *is_computed_name); + } + + Token::Value next = Next(); + ReportUnexpectedToken(next); + *ok = false; + return this->EmptyObjectLiteralProperty(); +} + + +template <class Traits> +typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseObjectLiteral( + ExpressionClassifier* classifier, bool* ok) { + // ObjectLiteral :: + // '{' (PropertyDefinition (',' PropertyDefinition)* ','? )? '}' + + int pos = peek_position(); + typename Traits::Type::PropertyList properties = + this->NewPropertyList(4, zone_); + int number_of_boilerplate_properties = 0; + bool has_function = false; + bool has_computed_names = false; + ObjectLiteralChecker checker(this); + + Expect(Token::LBRACE, CHECK_OK); + + while (peek() != Token::RBRACE) { + FuncNameInferrer::State fni_state(fni_); + + const bool in_class = false; + const bool is_static = false; + const bool has_extends = false; + bool is_computed_name = false; + IdentifierT name = this->EmptyIdentifier(); + ObjectLiteralPropertyT property = this->ParsePropertyDefinition( + &checker, in_class, has_extends, is_static, &is_computed_name, NULL, + classifier, &name, CHECK_OK); + + if (is_computed_name) { + has_computed_names = true; + } + + // Mark top-level object literals that contain function literals and + // pretenure the literal so it can be added as a constant function + // property. (Parser only.) + this->CheckFunctionLiteralInsideTopLevelObjectLiteral(scope_, property, + &has_function); + + // Count CONSTANT or COMPUTED properties to maintain the enumeration order. + if (!has_computed_names && this->IsBoilerplateProperty(property)) { + number_of_boilerplate_properties++; + } + properties->Add(property, zone()); + + if (peek() != Token::RBRACE) { + // Need {} because of the CHECK_OK macro. + Expect(Token::COMMA, CHECK_OK); + } + + if (fni_ != nullptr) fni_->Infer(); + + if (allow_harmony_function_name()) { + Traits::SetFunctionNameFromPropertyName(property, name); + } + } + Expect(Token::RBRACE, CHECK_OK); + + // Computation of literal_index must happen before pre parse bailout. + int literal_index = function_state_->NextMaterializedLiteralIndex(); + + return factory()->NewObjectLiteral(properties, + literal_index, + number_of_boilerplate_properties, + has_function, + is_strong(language_mode()), + pos); +} + + +template <class Traits> +typename Traits::Type::ExpressionList ParserBase<Traits>::ParseArguments( + Scanner::Location* first_spread_arg_loc, ExpressionClassifier* classifier, + bool* ok) { + // Arguments :: + // '(' (AssignmentExpression)*[','] ')' + + Scanner::Location spread_arg = Scanner::Location::invalid(); + typename Traits::Type::ExpressionList result = + this->NewExpressionList(4, zone_); + Expect(Token::LPAREN, CHECK_OK_CUSTOM(NullExpressionList)); + bool done = (peek() == Token::RPAREN); + bool was_unspread = false; + int unspread_sequences_count = 0; + while (!done) { + int start_pos = peek_position(); + bool is_spread = Check(Token::ELLIPSIS); + + ExpressionT argument = this->ParseAssignmentExpression( + true, classifier, CHECK_OK_CUSTOM(NullExpressionList)); + argument = Traits::RewriteNonPattern(argument, classifier, + CHECK_OK_CUSTOM(NullExpressionList)); + if (is_spread) { + if (!spread_arg.IsValid()) { + spread_arg.beg_pos = start_pos; + spread_arg.end_pos = peek_position(); + } + argument = factory()->NewSpread(argument, start_pos); + } + result->Add(argument, zone_); + + // unspread_sequences_count is the number of sequences of parameters which + // are not prefixed with a spread '...' operator. + if (is_spread) { + was_unspread = false; + } else if (!was_unspread) { + was_unspread = true; + unspread_sequences_count++; + } + + if (result->length() > Code::kMaxArguments) { + ReportMessage(MessageTemplate::kTooManyArguments); + *ok = false; + return this->NullExpressionList(); + } + done = (peek() != Token::COMMA); + if (!done) { + Next(); + } + } + Scanner::Location location = scanner_->location(); + if (Token::RPAREN != Next()) { + ReportMessageAt(location, MessageTemplate::kUnterminatedArgList); + *ok = false; + return this->NullExpressionList(); + } + *first_spread_arg_loc = spread_arg; + + if (spread_arg.IsValid()) { + // Unspread parameter sequences are translated into array literals in the + // parser. Ensure that the number of materialized literals matches between + // the parser and preparser + Traits::MaterializeUnspreadArgumentsLiterals(unspread_sequences_count); + } + + return result; +} + +// Precedence = 2 +template <class Traits> +typename ParserBase<Traits>::ExpressionT +ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN, int flags, + ExpressionClassifier* classifier, + bool* ok) { + // AssignmentExpression :: + // ConditionalExpression + // ArrowFunction + // YieldExpression + // LeftHandSideExpression AssignmentOperator AssignmentExpression + bool maybe_pattern_element = flags & kIsPossiblePatternElement; + bool maybe_arrow_formals = flags & kIsPossibleArrowFormals; + bool is_destructuring_assignment = false; + int lhs_beg_pos = peek_position(); + + if (peek() == Token::YIELD && is_generator()) { + return this->ParseYieldExpression(classifier, ok); + } + + FuncNameInferrer::State fni_state(fni_); + ParserBase<Traits>::Checkpoint checkpoint(this); + ExpressionClassifier arrow_formals_classifier(classifier->duplicate_finder()); + bool parenthesized_formals = peek() == Token::LPAREN; + if (!parenthesized_formals) { + ArrowFormalParametersUnexpectedToken(&arrow_formals_classifier); + } + ExpressionT expression = this->ParseConditionalExpression( + accept_IN, &arrow_formals_classifier, CHECK_OK); + if (peek() == Token::ARROW) { + BindingPatternUnexpectedToken(classifier); + ValidateArrowFormalParameters(&arrow_formals_classifier, expression, + parenthesized_formals, CHECK_OK); + Scanner::Location loc(lhs_beg_pos, scanner()->location().end_pos); + Scope* scope = + this->NewScope(scope_, FUNCTION_SCOPE, FunctionKind::kArrowFunction); + // Because the arrow's parameters were parsed in the outer scope, any + // usage flags that might have been triggered there need to be copied + // to the arrow scope. + scope_->PropagateUsageFlagsToScope(scope); + FormalParametersT parameters(scope); + if (!arrow_formals_classifier.is_simple_parameter_list()) { + scope->SetHasNonSimpleParameters(); + parameters.is_simple = false; + } + + checkpoint.Restore(¶meters.materialized_literals_count); + + scope->set_start_position(lhs_beg_pos); + Scanner::Location duplicate_loc = Scanner::Location::invalid(); + this->ParseArrowFunctionFormalParameterList(¶meters, expression, loc, + &duplicate_loc, CHECK_OK); + if (duplicate_loc.IsValid()) { + arrow_formals_classifier.RecordDuplicateFormalParameterError( + duplicate_loc); + } + expression = this->ParseArrowFunctionLiteral( + accept_IN, parameters, arrow_formals_classifier, CHECK_OK); + if (maybe_pattern_element) { + classifier->RecordPatternError( + Scanner::Location(lhs_beg_pos, scanner()->location().end_pos), + MessageTemplate::kInvalidDestructuringTarget); + } + + if (fni_ != nullptr) fni_->Infer(); + + return expression; + } + + if (this->IsValidReferenceExpression(expression)) { + arrow_formals_classifier.ForgiveAssignmentPatternError(); + } + + // "expression" was not itself an arrow function parameter list, but it might + // form part of one. Propagate speculative formal parameter error locations. + classifier->Accumulate( + arrow_formals_classifier, + ExpressionClassifier::StandardProductions | + ExpressionClassifier::FormalParametersProductions | + ExpressionClassifier::CoverInitializedNameProduction); + + bool maybe_pattern = + (expression->IsObjectLiteral() || expression->IsArrayLiteral()) && + !expression->is_parenthesized(); + + if (!Token::IsAssignmentOp(peek())) { + // Parsed conditional expression only (no assignment). + if (maybe_pattern_element) { + CheckDestructuringElement(expression, classifier, lhs_beg_pos, + scanner()->location().end_pos); + } + return expression; + } + + if (!(allow_harmony_destructuring_bind() || + allow_harmony_default_parameters())) { + BindingPatternUnexpectedToken(classifier); + } + + if (allow_harmony_destructuring_assignment() && maybe_pattern && + peek() == Token::ASSIGN) { + classifier->ForgiveCoverInitializedNameError(); + ValidateAssignmentPattern(classifier, CHECK_OK); + is_destructuring_assignment = true; + } else if (maybe_arrow_formals) { + expression = this->ClassifyAndRewriteReferenceExpression( + classifier, expression, lhs_beg_pos, scanner()->location().end_pos, + MessageTemplate::kInvalidLhsInAssignment); + } else { + if (maybe_pattern_element) { + CheckDestructuringElement(expression, classifier, lhs_beg_pos, + scanner()->location().end_pos); + } + expression = this->CheckAndRewriteReferenceExpression( + expression, lhs_beg_pos, scanner()->location().end_pos, + MessageTemplate::kInvalidLhsInAssignment, CHECK_OK); + } + + expression = this->MarkExpressionAsAssigned(expression); + + Token::Value op = Next(); // Get assignment operator. + if (op != Token::ASSIGN) { + classifier->RecordBindingPatternError(scanner()->location(), + MessageTemplate::kUnexpectedToken, + Token::String(op)); + } + int pos = position(); + + ExpressionClassifier rhs_classifier; + + ExpressionT right = + this->ParseAssignmentExpression(accept_IN, &rhs_classifier, CHECK_OK); + right = Traits::RewriteNonPattern(right, &rhs_classifier, CHECK_OK); + classifier->Accumulate( + rhs_classifier, ExpressionClassifier::ExpressionProductions | + ExpressionClassifier::CoverInitializedNameProduction); + + // 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. + if (op == Token::ASSIGN && this->IsThisProperty(expression)) { + function_state_->AddProperty(); + } + + if (op != Token::ASSIGN && maybe_pattern_element) { + classifier->RecordAssignmentPatternError( + Scanner::Location(lhs_beg_pos, scanner()->location().end_pos), + MessageTemplate::kInvalidDestructuringTarget); + } + + this->CheckAssigningFunctionLiteralToProperty(expression, right); + + 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 || op == Token::ASSIGN) && + (!right->IsCall() && !right->IsCallNew())) { + fni_->Infer(); + } else { + fni_->RemoveLastFunction(); + } + } + + if (op == Token::ASSIGN && allow_harmony_function_name()) { + Traits::SetFunctionNameFromIdentifierRef(right, expression); + } + + ExpressionT result = factory()->NewAssignment(op, expression, right, pos); + + if (is_destructuring_assignment) { + result = factory()->NewRewritableAssignmentExpression(result); + Traits::QueueDestructuringAssignmentForRewriting(result); + } + + return result; +} + +template <class Traits> +typename ParserBase<Traits>::ExpressionT +ParserBase<Traits>::ParseYieldExpression(ExpressionClassifier* classifier, + bool* ok) { + // YieldExpression :: + // 'yield' ([no line terminator] '*'? AssignmentExpression)? + int pos = peek_position(); + BindingPatternUnexpectedToken(classifier); + FormalParameterInitializerUnexpectedToken(classifier); + Expect(Token::YIELD, CHECK_OK); + ExpressionT generator_object = + factory()->NewVariableProxy(function_state_->generator_object_variable()); + ExpressionT expression = Traits::EmptyExpression(); + Yield::Kind kind = Yield::kSuspend; + if (!scanner()->HasAnyLineTerminatorBeforeNext()) { + if (Check(Token::MUL)) kind = Yield::kDelegating; + switch (peek()) { + case Token::EOS: + case Token::SEMICOLON: + case Token::RBRACE: + case Token::RBRACK: + case Token::RPAREN: + case Token::COLON: + case Token::COMMA: + // The above set of tokens is the complete set of tokens that can appear + // after an AssignmentExpression, and none of them can start an + // AssignmentExpression. This allows us to avoid looking for an RHS for + // a Yield::kSuspend operation, given only one look-ahead token. + if (kind == Yield::kSuspend) + break; + DCHECK_EQ(Yield::kDelegating, kind); + // Delegating yields require an RHS; fall through. + default: + expression = ParseAssignmentExpression(false, classifier, CHECK_OK); + expression = + Traits::RewriteNonPattern(expression, classifier, CHECK_OK); + break; + } + } + if (kind == Yield::kDelegating) { + // var iterator = subject[Symbol.iterator](); + // Hackily disambiguate o from o.next and o [Symbol.iterator](). + // TODO(verwaest): Come up with a better solution. + expression = this->GetIterator(expression, factory(), pos + 1); + } + // Hackily disambiguate o from o.next and o [Symbol.iterator](). + // TODO(verwaest): Come up with a better solution. + typename Traits::Type::YieldExpression yield = + factory()->NewYield(generator_object, expression, kind, pos); + return yield; +} + + +// Precedence = 3 +template <class Traits> +typename ParserBase<Traits>::ExpressionT +ParserBase<Traits>::ParseConditionalExpression(bool accept_IN, + ExpressionClassifier* classifier, + bool* ok) { + // ConditionalExpression :: + // LogicalOrExpression + // LogicalOrExpression '?' AssignmentExpression ':' AssignmentExpression + + int pos = peek_position(); + // We start using the binary expression parser for prec >= 4 only! + ExpressionT expression = + this->ParseBinaryExpression(4, accept_IN, classifier, CHECK_OK); + if (peek() != Token::CONDITIONAL) return expression; + expression = Traits::RewriteNonPattern(expression, classifier, CHECK_OK); + ArrowFormalParametersUnexpectedToken(classifier); + BindingPatternUnexpectedToken(classifier); + 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. + ExpressionT left = ParseAssignmentExpression(true, classifier, CHECK_OK); + left = Traits::RewriteNonPattern(left, classifier, CHECK_OK); + Expect(Token::COLON, CHECK_OK); + ExpressionT right = + ParseAssignmentExpression(accept_IN, classifier, CHECK_OK); + right = Traits::RewriteNonPattern(right, classifier, CHECK_OK); + return factory()->NewConditional(expression, left, right, pos); +} + + +// Precedence >= 4 +template <class Traits> +typename ParserBase<Traits>::ExpressionT +ParserBase<Traits>::ParseBinaryExpression(int prec, bool accept_IN, + ExpressionClassifier* classifier, + bool* ok) { + DCHECK(prec >= 4); + ExpressionT x = this->ParseUnaryExpression(classifier, CHECK_OK); + for (int prec1 = Precedence(peek(), accept_IN); prec1 >= prec; prec1--) { + // prec1 >= 4 + while (Precedence(peek(), accept_IN) == prec1) { + x = Traits::RewriteNonPattern(x, classifier, CHECK_OK); + BindingPatternUnexpectedToken(classifier); + ArrowFormalParametersUnexpectedToken(classifier); + Token::Value op = Next(); + Scanner::Location op_location = scanner()->location(); + int pos = position(); + ExpressionT y = + ParseBinaryExpression(prec1 + 1, accept_IN, classifier, CHECK_OK); + y = Traits::RewriteNonPattern(y, classifier, CHECK_OK); + + if (this->ShortcutNumericLiteralBinaryExpression(&x, y, op, pos, + factory())) { + continue; + } + + // 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; + } + if (cmp == Token::EQ && is_strong(language_mode())) { + ReportMessageAt(op_location, MessageTemplate::kStrongEqual); + *ok = false; + return this->EmptyExpression(); + } + x = factory()->NewCompareOperation(cmp, x, y, pos); + if (cmp != op) { + // The comparison was negated - add a NOT. + x = factory()->NewUnaryOperation(Token::NOT, x, pos); + } + + } else { + // We have a "normal" binary operation. + x = factory()->NewBinaryOperation(op, x, y, pos); + } + } + } + return x; +} + + +template <class Traits> +typename ParserBase<Traits>::ExpressionT +ParserBase<Traits>::ParseUnaryExpression(ExpressionClassifier* classifier, + bool* ok) { + // UnaryExpression :: + // PostfixExpression + // 'delete' UnaryExpression + // 'void' UnaryExpression + // 'typeof' UnaryExpression + // '++' UnaryExpression + // '--' UnaryExpression + // '+' UnaryExpression + // '-' UnaryExpression + // '~' UnaryExpression + // '!' UnaryExpression + + Token::Value op = peek(); + if (Token::IsUnaryOp(op)) { + BindingPatternUnexpectedToken(classifier); + ArrowFormalParametersUnexpectedToken(classifier); + + op = Next(); + int pos = position(); + ExpressionT expression = ParseUnaryExpression(classifier, CHECK_OK); + expression = Traits::RewriteNonPattern(expression, classifier, CHECK_OK); + + if (op == Token::DELETE && is_strict(language_mode())) { + if (is_strong(language_mode())) { + ReportMessage(MessageTemplate::kStrongDelete); + *ok = false; + return this->EmptyExpression(); + } else if (this->IsIdentifier(expression)) { + // "delete identifier" is a syntax error in strict mode. + ReportMessage(MessageTemplate::kStrictDelete); + *ok = false; + return this->EmptyExpression(); + } + } + + // Allow Traits do rewrite the expression. + return this->BuildUnaryExpression(expression, op, pos, factory()); + } else if (Token::IsCountOp(op)) { + BindingPatternUnexpectedToken(classifier); + ArrowFormalParametersUnexpectedToken(classifier); + op = Next(); + int beg_pos = peek_position(); + ExpressionT expression = this->ParseUnaryExpression(classifier, CHECK_OK); + expression = this->CheckAndRewriteReferenceExpression( + expression, beg_pos, scanner()->location().end_pos, + MessageTemplate::kInvalidLhsInPrefixOp, CHECK_OK); + this->MarkExpressionAsAssigned(expression); + expression = Traits::RewriteNonPattern(expression, classifier, CHECK_OK); + + return factory()->NewCountOperation(op, + true /* prefix */, + expression, + position()); + + } else { + return this->ParsePostfixExpression(classifier, ok); + } +} + + +template <class Traits> +typename ParserBase<Traits>::ExpressionT +ParserBase<Traits>::ParsePostfixExpression(ExpressionClassifier* classifier, + bool* ok) { + // PostfixExpression :: + // LeftHandSideExpression ('++' | '--')? + + int lhs_beg_pos = peek_position(); + ExpressionT expression = + this->ParseLeftHandSideExpression(classifier, CHECK_OK); + if (!scanner()->HasAnyLineTerminatorBeforeNext() && + Token::IsCountOp(peek())) { + BindingPatternUnexpectedToken(classifier); + ArrowFormalParametersUnexpectedToken(classifier); + + expression = this->CheckAndRewriteReferenceExpression( + expression, lhs_beg_pos, scanner()->location().end_pos, + MessageTemplate::kInvalidLhsInPostfixOp, CHECK_OK); + expression = this->MarkExpressionAsAssigned(expression); + expression = Traits::RewriteNonPattern(expression, classifier, CHECK_OK); + + Token::Value next = Next(); + expression = + factory()->NewCountOperation(next, + false /* postfix */, + expression, + position()); + } + return expression; +} + + +template <class Traits> +typename ParserBase<Traits>::ExpressionT +ParserBase<Traits>::ParseLeftHandSideExpression( + ExpressionClassifier* classifier, bool* ok) { + // LeftHandSideExpression :: + // (NewExpression | MemberExpression) ... + + ExpressionT result = + this->ParseMemberWithNewPrefixesExpression(classifier, CHECK_OK); + + while (true) { + switch (peek()) { + case Token::LBRACK: { + BindingPatternUnexpectedToken(classifier); + ArrowFormalParametersUnexpectedToken(classifier); + Consume(Token::LBRACK); + int pos = position(); + ExpressionT index = ParseExpression(true, classifier, CHECK_OK); + index = Traits::RewriteNonPattern(index, classifier, CHECK_OK); + result = factory()->NewProperty(result, index, pos); + Expect(Token::RBRACK, CHECK_OK); + break; + } + + case Token::LPAREN: { + result = Traits::RewriteNonPattern(result, classifier, CHECK_OK); + BindingPatternUnexpectedToken(classifier); + ArrowFormalParametersUnexpectedToken(classifier); + + if (is_strong(language_mode()) && this->IsIdentifier(result) && + this->IsEval(this->AsIdentifier(result))) { + ReportMessage(MessageTemplate::kStrongDirectEval); + *ok = false; + return this->EmptyExpression(); + } + int pos; + if (scanner()->current_token() == Token::IDENTIFIER || + scanner()->current_token() == Token::SUPER) { + // For call of an identifier we want to report position of + // the identifier as position of the call in the stack trace. + pos = position(); + } else { + // For other kinds of calls we record position of the parenthesis as + // position of the call. Note that this is extremely important for + // expressions of the form function(){...}() for which call position + // should not point to the closing brace otherwise it will intersect + // with positions recorded for function literal and confuse debugger. + pos = peek_position(); + // Also the trailing parenthesis are a hint that the function will + // be called immediately. If we happen to have parsed a preceding + // function literal eagerly, we can also compile it eagerly. + if (result->IsFunctionLiteral() && mode() == PARSE_EAGERLY) { + result->AsFunctionLiteral()->set_should_eager_compile(); + } + } + Scanner::Location spread_pos; + typename Traits::Type::ExpressionList args = + ParseArguments(&spread_pos, classifier, CHECK_OK); + + // Keep track of eval() calls since they disable all local variable + // optimizations. + // The calls that need special treatment are the + // direct eval calls. These calls are all of the form eval(...), with + // no explicit receiver. + // These calls are marked as potentially direct eval calls. Whether + // they are actually direct calls to eval is determined at run time. + this->CheckPossibleEvalCall(result, scope_); + + bool is_super_call = result->IsSuperCallReference(); + if (spread_pos.IsValid()) { + args = Traits::PrepareSpreadArguments(args); + result = Traits::SpreadCall(result, args, pos); + } else { + result = factory()->NewCall(result, args, pos); + } + + // Explicit calls to the super constructor using super() perform an + // implicit binding assignment to the 'this' variable. + if (is_super_call) { + ExpressionT this_expr = this->ThisExpression(scope_, factory(), pos); + result = + factory()->NewAssignment(Token::INIT, this_expr, result, pos); + } + + if (fni_ != NULL) fni_->RemoveLastFunction(); + break; + } + + case Token::PERIOD: { + BindingPatternUnexpectedToken(classifier); + ArrowFormalParametersUnexpectedToken(classifier); + Consume(Token::PERIOD); + int pos = position(); + IdentifierT name = ParseIdentifierName(CHECK_OK); + result = factory()->NewProperty( + result, factory()->NewStringLiteral(name, pos), pos); + if (fni_ != NULL) this->PushLiteralName(fni_, name); + break; + } + + case Token::TEMPLATE_SPAN: + case Token::TEMPLATE_TAIL: { + BindingPatternUnexpectedToken(classifier); + ArrowFormalParametersUnexpectedToken(classifier); + result = ParseTemplateLiteral(result, position(), classifier, CHECK_OK); + break; + } + + default: + return result; + } + } +} + + +template <class Traits> +typename ParserBase<Traits>::ExpressionT +ParserBase<Traits>::ParseMemberWithNewPrefixesExpression( + ExpressionClassifier* classifier, bool* ok) { + // NewExpression :: + // ('new')+ MemberExpression + // + // NewTarget :: + // 'new' '.' 'target' + + // The grammar for new expressions is pretty warped. We can have several 'new' + // keywords following each other, and then a MemberExpression. When we see '(' + // after the MemberExpression, it's associated with the rightmost unassociated + // 'new' to create a NewExpression with arguments. However, a NewExpression + // can also occur without arguments. + + // Examples of new expression: + // new foo.bar().baz means (new (foo.bar)()).baz + // new foo()() means (new foo())() + // new new foo()() means (new (new foo())()) + // new new foo means new (new foo) + // new new foo() means new (new foo()) + // new new foo().bar().baz means (new (new foo()).bar()).baz + + if (peek() == Token::NEW) { + BindingPatternUnexpectedToken(classifier); + ArrowFormalParametersUnexpectedToken(classifier); + Consume(Token::NEW); + int new_pos = position(); + ExpressionT result = this->EmptyExpression(); + if (peek() == Token::SUPER) { + const bool is_new = true; + result = ParseSuperExpression(is_new, classifier, CHECK_OK); + } else if (peek() == Token::PERIOD) { + return ParseNewTargetExpression(CHECK_OK); + } else { + result = this->ParseMemberWithNewPrefixesExpression(classifier, CHECK_OK); + } + result = Traits::RewriteNonPattern(result, classifier, CHECK_OK); + if (peek() == Token::LPAREN) { + // NewExpression with arguments. + Scanner::Location spread_pos; + typename Traits::Type::ExpressionList args = + this->ParseArguments(&spread_pos, classifier, CHECK_OK); + + if (spread_pos.IsValid()) { + args = Traits::PrepareSpreadArguments(args); + result = Traits::SpreadCallNew(result, args, new_pos); + } else { + result = factory()->NewCallNew(result, args, new_pos); + } + // The expression can still continue with . or [ after the arguments. + result = + this->ParseMemberExpressionContinuation(result, classifier, CHECK_OK); + return result; + } + // NewExpression without arguments. + return factory()->NewCallNew(result, this->NewExpressionList(0, zone_), + new_pos); + } + // No 'new' or 'super' keyword. + return this->ParseMemberExpression(classifier, ok); +} + + +template <class Traits> +typename ParserBase<Traits>::ExpressionT +ParserBase<Traits>::ParseMemberExpression(ExpressionClassifier* classifier, + bool* ok) { + // MemberExpression :: + // (PrimaryExpression | FunctionLiteral | ClassLiteral) + // ('[' Expression ']' | '.' Identifier | Arguments | TemplateLiteral)* + + // The '[' Expression ']' and '.' Identifier parts are parsed by + // ParseMemberExpressionContinuation, and the Arguments part is parsed by the + // caller. + + // Parse the initial primary or function expression. + ExpressionT result = this->EmptyExpression(); + if (peek() == Token::FUNCTION) { + BindingPatternUnexpectedToken(classifier); + ArrowFormalParametersUnexpectedToken(classifier); + + Consume(Token::FUNCTION); + int function_token_position = position(); + bool is_generator = Check(Token::MUL); + IdentifierT name = this->EmptyIdentifier(); + bool is_strict_reserved_name = false; + Scanner::Location function_name_location = Scanner::Location::invalid(); + FunctionLiteral::FunctionType function_type = + FunctionLiteral::kAnonymousExpression; + if (peek_any_identifier()) { + name = ParseIdentifierOrStrictReservedWord( + is_generator, &is_strict_reserved_name, CHECK_OK); + function_name_location = scanner()->location(); + function_type = FunctionLiteral::kNamedExpression; + } + result = this->ParseFunctionLiteral( + name, function_name_location, + is_strict_reserved_name ? kFunctionNameIsStrictReserved + : kFunctionNameValidityUnknown, + is_generator ? FunctionKind::kGeneratorFunction + : FunctionKind::kNormalFunction, + function_token_position, function_type, FunctionLiteral::kNormalArity, + language_mode(), CHECK_OK); + } else if (peek() == Token::SUPER) { + const bool is_new = false; + result = ParseSuperExpression(is_new, classifier, CHECK_OK); + } else { + result = ParsePrimaryExpression(classifier, CHECK_OK); + } + + result = ParseMemberExpressionContinuation(result, classifier, CHECK_OK); + return result; +} + + +template <class Traits> +typename ParserBase<Traits>::ExpressionT +ParserBase<Traits>::ParseStrongInitializationExpression( + ExpressionClassifier* classifier, bool* ok) { + // InitializationExpression :: (strong mode) + // 'this' '.' IdentifierName '=' AssignmentExpression + // 'this' '[' Expression ']' '=' AssignmentExpression + + FuncNameInferrer::State fni_state(fni_); + + Consume(Token::THIS); + int pos = position(); + function_state_->set_this_location(scanner()->location()); + ExpressionT this_expr = this->ThisExpression(scope_, factory(), pos); + + ExpressionT left = this->EmptyExpression(); + switch (peek()) { + case Token::LBRACK: { + Consume(Token::LBRACK); + int pos = position(); + ExpressionT index = this->ParseExpression(true, classifier, CHECK_OK); + index = Traits::RewriteNonPattern(index, classifier, CHECK_OK); + left = factory()->NewProperty(this_expr, index, pos); + if (fni_ != NULL) { + this->PushPropertyName(fni_, index); + } + Expect(Token::RBRACK, CHECK_OK); + break; + } + case Token::PERIOD: { + Consume(Token::PERIOD); + int pos = position(); + IdentifierT name = ParseIdentifierName(CHECK_OK); + left = factory()->NewProperty( + this_expr, factory()->NewStringLiteral(name, pos), pos); + if (fni_ != NULL) { + this->PushLiteralName(fni_, name); + } + break; + } + default: + ReportMessage(MessageTemplate::kStrongConstructorThis); + *ok = false; + return this->EmptyExpression(); + } + + if (peek() != Token::ASSIGN) { + ReportMessageAt(function_state_->this_location(), + MessageTemplate::kStrongConstructorThis); + *ok = false; + return this->EmptyExpression(); + } + Consume(Token::ASSIGN); + left = this->MarkExpressionAsAssigned(left); + + ExpressionT right = + this->ParseAssignmentExpression(true, classifier, CHECK_OK); + right = Traits::RewriteNonPattern(right, classifier, CHECK_OK); + this->CheckAssigningFunctionLiteralToProperty(left, right); + function_state_->AddProperty(); + if (fni_ != NULL) { + // Check if the right hand side is a call to avoid inferring a + // name if we're dealing with "this.a = function(){...}();"-like + // expression. + if (!right->IsCall() && !right->IsCallNew()) { + fni_->Infer(); + } else { + fni_->RemoveLastFunction(); + } + } + + if (function_state_->return_location().IsValid()) { + ReportMessageAt(function_state_->return_location(), + MessageTemplate::kStrongConstructorReturnMisplaced); + *ok = false; + return this->EmptyExpression(); + } + + return factory()->NewAssignment(Token::ASSIGN, left, right, pos); +} + + +template <class Traits> +typename ParserBase<Traits>::ExpressionT +ParserBase<Traits>::ParseStrongSuperCallExpression( + ExpressionClassifier* classifier, bool* ok) { + // SuperCallExpression :: (strong mode) + // 'super' '(' ExpressionList ')' + BindingPatternUnexpectedToken(classifier); + + Consume(Token::SUPER); + int pos = position(); + Scanner::Location super_loc = scanner()->location(); + ExpressionT expr = this->SuperCallReference(scope_, factory(), pos); + + if (peek() != Token::LPAREN) { + ReportMessage(MessageTemplate::kStrongConstructorSuper); + *ok = false; + return this->EmptyExpression(); + } + + Scanner::Location spread_pos; + typename Traits::Type::ExpressionList args = + ParseArguments(&spread_pos, classifier, CHECK_OK); + + // TODO(rossberg): This doesn't work with arrow functions yet. + if (!IsSubclassConstructor(function_state_->kind())) { + ReportMessage(MessageTemplate::kUnexpectedSuper); + *ok = false; + return this->EmptyExpression(); + } else if (function_state_->super_location().IsValid()) { + ReportMessageAt(scanner()->location(), + MessageTemplate::kStrongSuperCallDuplicate); + *ok = false; + return this->EmptyExpression(); + } else if (function_state_->this_location().IsValid()) { + ReportMessageAt(scanner()->location(), + MessageTemplate::kStrongSuperCallMisplaced); + *ok = false; + return this->EmptyExpression(); + } else if (function_state_->return_location().IsValid()) { + ReportMessageAt(function_state_->return_location(), + MessageTemplate::kStrongConstructorReturnMisplaced); + *ok = false; + return this->EmptyExpression(); + } + + function_state_->set_super_location(super_loc); + if (spread_pos.IsValid()) { + args = Traits::PrepareSpreadArguments(args); + expr = Traits::SpreadCall(expr, args, pos); + } else { + expr = factory()->NewCall(expr, args, pos); + } + + // Explicit calls to the super constructor using super() perform an implicit + // binding assignment to the 'this' variable. + ExpressionT this_expr = this->ThisExpression(scope_, factory(), pos); + return factory()->NewAssignment(Token::INIT, this_expr, expr, pos); +} + + +template <class Traits> +typename ParserBase<Traits>::ExpressionT +ParserBase<Traits>::ParseSuperExpression(bool is_new, + ExpressionClassifier* classifier, + bool* ok) { + Expect(Token::SUPER, CHECK_OK); + int pos = position(); + + Scope* scope = scope_->ReceiverScope(); + FunctionKind kind = scope->function_kind(); + if (IsConciseMethod(kind) || IsAccessorFunction(kind) || + IsClassConstructor(kind)) { + if (peek() == Token::PERIOD || peek() == Token::LBRACK) { + scope->RecordSuperPropertyUsage(); + return this->SuperPropertyReference(scope_, factory(), pos); + } + // new super() is never allowed. + // super() is only allowed in derived constructor + if (!is_new && peek() == Token::LPAREN && IsSubclassConstructor(kind)) { + if (is_strong(language_mode())) { + // Super calls in strong mode are parsed separately. + ReportMessageAt(scanner()->location(), + MessageTemplate::kStrongConstructorSuper); + *ok = false; + return this->EmptyExpression(); + } + // TODO(rossberg): This might not be the correct FunctionState for the + // method here. + function_state_->set_super_location(scanner()->location()); + return this->SuperCallReference(scope_, factory(), pos); + } + } + + ReportMessageAt(scanner()->location(), MessageTemplate::kUnexpectedSuper); + *ok = false; + return this->EmptyExpression(); +} + + +template <class Traits> +typename ParserBase<Traits>::ExpressionT +ParserBase<Traits>::ParseNewTargetExpression(bool* ok) { + int pos = position(); + Consume(Token::PERIOD); + ExpectContextualKeyword(CStrVector("target"), CHECK_OK); + + if (!scope_->ReceiverScope()->is_function_scope()) { + ReportMessageAt(scanner()->location(), + MessageTemplate::kUnexpectedNewTarget); + *ok = false; + return this->EmptyExpression(); + } + + return this->NewTargetExpression(scope_, factory(), pos); +} + + +template <class Traits> +typename ParserBase<Traits>::ExpressionT +ParserBase<Traits>::ParseMemberExpressionContinuation( + ExpressionT expression, ExpressionClassifier* classifier, bool* ok) { + // Parses this part of MemberExpression: + // ('[' Expression ']' | '.' Identifier | TemplateLiteral)* + while (true) { + switch (peek()) { + case Token::LBRACK: { + BindingPatternUnexpectedToken(classifier); + ArrowFormalParametersUnexpectedToken(classifier); + + Consume(Token::LBRACK); + int pos = position(); + ExpressionT index = this->ParseExpression(true, classifier, CHECK_OK); + index = Traits::RewriteNonPattern(index, classifier, CHECK_OK); + expression = factory()->NewProperty(expression, index, pos); + if (fni_ != NULL) { + this->PushPropertyName(fni_, index); + } + Expect(Token::RBRACK, CHECK_OK); + break; + } + case Token::PERIOD: { + BindingPatternUnexpectedToken(classifier); + ArrowFormalParametersUnexpectedToken(classifier); + + Consume(Token::PERIOD); + int pos = position(); + IdentifierT name = ParseIdentifierName(CHECK_OK); + expression = factory()->NewProperty( + expression, factory()->NewStringLiteral(name, pos), pos); + if (fni_ != NULL) { + this->PushLiteralName(fni_, name); + } + break; + } + case Token::TEMPLATE_SPAN: + case Token::TEMPLATE_TAIL: { + BindingPatternUnexpectedToken(classifier); + ArrowFormalParametersUnexpectedToken(classifier); + int pos; + if (scanner()->current_token() == Token::IDENTIFIER) { + pos = position(); + } else { + pos = peek_position(); + if (expression->IsFunctionLiteral() && mode() == PARSE_EAGERLY) { + // If the tag function looks like an IIFE, set_parenthesized() to + // force eager compilation. + expression->AsFunctionLiteral()->set_should_eager_compile(); + } + } + expression = + ParseTemplateLiteral(expression, pos, classifier, CHECK_OK); + break; + } + default: + return expression; + } + } + DCHECK(false); + return this->EmptyExpression(); +} + + +template <class Traits> +void ParserBase<Traits>::ParseFormalParameter( + FormalParametersT* parameters, ExpressionClassifier* classifier, bool* ok) { + // FormalParameter[Yield,GeneratorParameter] : + // BindingElement[?Yield, ?GeneratorParameter] + bool is_rest = parameters->has_rest; + + Token::Value next = peek(); + ExpressionT pattern = ParsePrimaryExpression(classifier, ok); + if (!*ok) return; + + ValidateBindingPattern(classifier, ok); + if (!*ok) return; + + if (!Traits::IsIdentifier(pattern)) { + if (!allow_harmony_destructuring_bind()) { + ReportUnexpectedToken(next); + *ok = false; + return; + } + parameters->is_simple = false; + ValidateFormalParameterInitializer(classifier, ok); + if (!*ok) return; + classifier->RecordNonSimpleParameter(); + } + + ExpressionT initializer = Traits::EmptyExpression(); + if (!is_rest && allow_harmony_default_parameters() && Check(Token::ASSIGN)) { + ExpressionClassifier init_classifier; + initializer = ParseAssignmentExpression(true, &init_classifier, ok); + if (!*ok) return; + initializer = Traits::RewriteNonPattern(initializer, &init_classifier, ok); + ValidateFormalParameterInitializer(&init_classifier, ok); + if (!*ok) return; + parameters->is_simple = false; + classifier->RecordNonSimpleParameter(); + } + + Traits::AddFormalParameter(parameters, pattern, initializer, + scanner()->location().end_pos, is_rest); +} + + +template <class Traits> +void ParserBase<Traits>::ParseFormalParameterList( + FormalParametersT* parameters, ExpressionClassifier* classifier, bool* ok) { + // FormalParameters[Yield,GeneratorParameter] : + // [empty] + // FormalParameterList[?Yield, ?GeneratorParameter] + // + // FormalParameterList[Yield,GeneratorParameter] : + // FunctionRestParameter[?Yield] + // FormalsList[?Yield, ?GeneratorParameter] + // FormalsList[?Yield, ?GeneratorParameter] , FunctionRestParameter[?Yield] + // + // FormalsList[Yield,GeneratorParameter] : + // FormalParameter[?Yield, ?GeneratorParameter] + // FormalsList[?Yield, ?GeneratorParameter] , + // FormalParameter[?Yield,?GeneratorParameter] + + DCHECK_EQ(0, parameters->Arity()); + + if (peek() != Token::RPAREN) { + do { + if (parameters->Arity() > Code::kMaxArguments) { + ReportMessage(MessageTemplate::kTooManyParameters); + *ok = false; + return; + } + parameters->has_rest = Check(Token::ELLIPSIS); + ParseFormalParameter(parameters, classifier, ok); + if (!*ok) return; + } while (!parameters->has_rest && Check(Token::COMMA)); + + if (parameters->has_rest) { + parameters->is_simple = false; + classifier->RecordNonSimpleParameter(); + if (peek() == Token::COMMA) { + ReportMessageAt(scanner()->peek_location(), + MessageTemplate::kParamAfterRest); + *ok = false; + return; + } + } + } + + for (int i = 0; i < parameters->Arity(); ++i) { + auto parameter = parameters->at(i); + Traits::DeclareFormalParameter(parameters->scope, parameter, classifier); + } +} + + +template <class Traits> +void ParserBase<Traits>::CheckArityRestrictions( + int param_count, FunctionLiteral::ArityRestriction arity_restriction, + bool has_rest, int formals_start_pos, int formals_end_pos, bool* ok) { + switch (arity_restriction) { + case FunctionLiteral::kGetterArity: + if (param_count != 0) { + ReportMessageAt(Scanner::Location(formals_start_pos, formals_end_pos), + MessageTemplate::kBadGetterArity); + *ok = false; + } + break; + case FunctionLiteral::kSetterArity: + if (param_count != 1) { + ReportMessageAt(Scanner::Location(formals_start_pos, formals_end_pos), + MessageTemplate::kBadSetterArity); + *ok = false; + } + if (has_rest) { + ReportMessageAt(Scanner::Location(formals_start_pos, formals_end_pos), + MessageTemplate::kBadSetterRestParameter); + *ok = false; + } + break; + default: + break; + } +} + + +template <class Traits> +bool ParserBase<Traits>::IsNextLetKeyword() { + DCHECK(peek() == Token::LET); + if (!allow_let()) { + return false; + } + Token::Value next_next = PeekAhead(); + switch (next_next) { + case Token::LBRACE: + case Token::LBRACK: + case Token::IDENTIFIER: + case Token::STATIC: + case Token::LET: // Yes, you can do let let = ... in sloppy mode + case Token::YIELD: + return true; + default: + return false; + } +} + + +template <class Traits> +typename ParserBase<Traits>::ExpressionT +ParserBase<Traits>::ParseArrowFunctionLiteral( + bool accept_IN, const FormalParametersT& formal_parameters, + const ExpressionClassifier& formals_classifier, bool* ok) { + if (peek() == Token::ARROW && scanner_->HasAnyLineTerminatorBeforeNext()) { + // ASI inserts `;` after arrow parameters if a line terminator is found. + // `=> ...` is never a valid expression, so report as syntax error. + // If next token is not `=>`, it's a syntax error anyways. + ReportUnexpectedTokenAt(scanner_->peek_location(), Token::ARROW); + *ok = false; + return this->EmptyExpression(); + } + + typename Traits::Type::StatementList body; + int num_parameters = formal_parameters.scope->num_parameters(); + int materialized_literal_count = -1; + int expected_property_count = -1; + Scanner::Location super_loc; + + { + typename Traits::Type::Factory function_factory(ast_value_factory()); + FunctionState function_state(&function_state_, &scope_, + formal_parameters.scope, kArrowFunction, + &function_factory); + + function_state.SkipMaterializedLiterals( + formal_parameters.materialized_literals_count); + + this->ReindexLiterals(formal_parameters); + + Expect(Token::ARROW, CHECK_OK); + + if (peek() == Token::LBRACE) { + // Multiple statement body + Consume(Token::LBRACE); + bool is_lazily_parsed = + (mode() == PARSE_LAZILY && scope_->AllowsLazyCompilation()); + if (is_lazily_parsed) { + body = this->NewStatementList(0, zone()); + this->SkipLazyFunctionBody(&materialized_literal_count, + &expected_property_count, CHECK_OK); + if (formal_parameters.materialized_literals_count > 0) { + materialized_literal_count += + formal_parameters.materialized_literals_count; + } + } else { + body = this->ParseEagerFunctionBody( + this->EmptyIdentifier(), RelocInfo::kNoPosition, formal_parameters, + kArrowFunction, FunctionLiteral::kAnonymousExpression, CHECK_OK); + materialized_literal_count = + function_state.materialized_literal_count(); + expected_property_count = function_state.expected_property_count(); + } + } else { + // Single-expression body + int pos = position(); + parenthesized_function_ = false; + ExpressionClassifier classifier; + ExpressionT expression = + ParseAssignmentExpression(accept_IN, &classifier, CHECK_OK); + expression = Traits::RewriteNonPattern(expression, &classifier, CHECK_OK); + body = this->NewStatementList(1, zone()); + this->AddParameterInitializationBlock(formal_parameters, body, CHECK_OK); + body->Add(factory()->NewReturnStatement(expression, pos), zone()); + materialized_literal_count = function_state.materialized_literal_count(); + expected_property_count = function_state.expected_property_count(); + } + super_loc = function_state.super_location(); + + formal_parameters.scope->set_end_position(scanner()->location().end_pos); + + // Arrow function formal parameters are parsed as StrictFormalParameterList, + // which is not the same as "parameters of a strict function"; it only means + // that duplicates are not allowed. Of course, the arrow function may + // itself be strict as well. + const bool allow_duplicate_parameters = false; + this->ValidateFormalParameters(&formals_classifier, language_mode(), + allow_duplicate_parameters, CHECK_OK); + + // Validate strict mode. + if (is_strict(language_mode())) { + CheckStrictOctalLiteral(formal_parameters.scope->start_position(), + scanner()->location().end_pos, CHECK_OK); + } + if (is_strict(language_mode()) || allow_harmony_sloppy()) { + this->CheckConflictingVarDeclarations(formal_parameters.scope, CHECK_OK); + } + + Traits::RewriteDestructuringAssignments(); + } + + FunctionLiteralT function_literal = factory()->NewFunctionLiteral( + this->EmptyIdentifierString(), formal_parameters.scope, body, + materialized_literal_count, expected_property_count, num_parameters, + FunctionLiteral::kNoDuplicateParameters, + FunctionLiteral::kAnonymousExpression, + FunctionLiteral::kShouldLazyCompile, FunctionKind::kArrowFunction, + formal_parameters.scope->start_position()); + + function_literal->set_function_token_position( + formal_parameters.scope->start_position()); + if (super_loc.IsValid()) function_state_->set_super_location(super_loc); + + if (fni_ != NULL) this->InferFunctionName(fni_, function_literal); + + return function_literal; +} + + +template <typename Traits> +typename ParserBase<Traits>::ExpressionT +ParserBase<Traits>::ParseTemplateLiteral(ExpressionT tag, int start, + ExpressionClassifier* classifier, + bool* ok) { + // A TemplateLiteral is made up of 0 or more TEMPLATE_SPAN tokens (literal + // text followed by a substitution expression), finalized by a single + // TEMPLATE_TAIL. + // + // In terms of draft language, TEMPLATE_SPAN may be either the TemplateHead or + // TemplateMiddle productions, while TEMPLATE_TAIL is either TemplateTail, or + // NoSubstitutionTemplate. + // + // When parsing a TemplateLiteral, we must have scanned either an initial + // TEMPLATE_SPAN, or a TEMPLATE_TAIL. + CHECK(peek() == Token::TEMPLATE_SPAN || peek() == Token::TEMPLATE_TAIL); + + // If we reach a TEMPLATE_TAIL first, we are parsing a NoSubstitutionTemplate. + // In this case we may simply consume the token and build a template with a + // single TEMPLATE_SPAN and no expressions. + if (peek() == Token::TEMPLATE_TAIL) { + Consume(Token::TEMPLATE_TAIL); + int pos = position(); + CheckTemplateOctalLiteral(pos, peek_position(), CHECK_OK); + typename Traits::TemplateLiteralState ts = Traits::OpenTemplateLiteral(pos); + Traits::AddTemplateSpan(&ts, true); + return Traits::CloseTemplateLiteral(&ts, start, tag); + } + + Consume(Token::TEMPLATE_SPAN); + int pos = position(); + typename Traits::TemplateLiteralState ts = Traits::OpenTemplateLiteral(pos); + Traits::AddTemplateSpan(&ts, false); + Token::Value next; + + // If we open with a TEMPLATE_SPAN, we must scan the subsequent expression, + // and repeat if the following token is a TEMPLATE_SPAN as well (in this + // case, representing a TemplateMiddle). + + do { + CheckTemplateOctalLiteral(pos, peek_position(), CHECK_OK); + next = peek(); + if (next == Token::EOS) { + ReportMessageAt(Scanner::Location(start, peek_position()), + MessageTemplate::kUnterminatedTemplate); + *ok = false; + return Traits::EmptyExpression(); + } else if (next == Token::ILLEGAL) { + Traits::ReportMessageAt( + Scanner::Location(position() + 1, peek_position()), + MessageTemplate::kUnexpectedToken, "ILLEGAL", kSyntaxError); + *ok = false; + return Traits::EmptyExpression(); + } + + int expr_pos = peek_position(); + ExpressionT expression = this->ParseExpression(true, classifier, CHECK_OK); + expression = Traits::RewriteNonPattern(expression, classifier, CHECK_OK); + Traits::AddTemplateExpression(&ts, expression); + + if (peek() != Token::RBRACE) { + ReportMessageAt(Scanner::Location(expr_pos, peek_position()), + MessageTemplate::kUnterminatedTemplateExpr); + *ok = false; + return Traits::EmptyExpression(); + } + + // If we didn't die parsing that expression, our next token should be a + // TEMPLATE_SPAN or TEMPLATE_TAIL. + next = scanner()->ScanTemplateContinuation(); + Next(); + pos = position(); + + if (next == Token::EOS) { + ReportMessageAt(Scanner::Location(start, pos), + MessageTemplate::kUnterminatedTemplate); + *ok = false; + return Traits::EmptyExpression(); + } else if (next == Token::ILLEGAL) { + Traits::ReportMessageAt( + Scanner::Location(position() + 1, peek_position()), + MessageTemplate::kUnexpectedToken, "ILLEGAL", kSyntaxError); + *ok = false; + return Traits::EmptyExpression(); + } + + Traits::AddTemplateSpan(&ts, next == Token::TEMPLATE_TAIL); + } while (next == Token::TEMPLATE_SPAN); + + DCHECK_EQ(next, Token::TEMPLATE_TAIL); + CheckTemplateOctalLiteral(pos, peek_position(), CHECK_OK); + // Once we've reached a TEMPLATE_TAIL, we can close the TemplateLiteral. + return Traits::CloseTemplateLiteral(&ts, start, tag); +} + + +template <typename Traits> +typename ParserBase<Traits>::ExpressionT +ParserBase<Traits>::CheckAndRewriteReferenceExpression( + ExpressionT expression, int beg_pos, int end_pos, + MessageTemplate::Template message, bool* ok) { + return this->CheckAndRewriteReferenceExpression(expression, beg_pos, end_pos, + message, kReferenceError, ok); +} + + +template <typename Traits> +typename ParserBase<Traits>::ExpressionT +ParserBase<Traits>::CheckAndRewriteReferenceExpression( + ExpressionT expression, int beg_pos, int end_pos, + MessageTemplate::Template message, ParseErrorType type, bool* ok) { + ExpressionClassifier classifier; + ExpressionT result = ClassifyAndRewriteReferenceExpression( + &classifier, expression, beg_pos, end_pos, message, type); + ValidateExpression(&classifier, ok); + if (!*ok) return this->EmptyExpression(); + return result; +} + + +template <typename Traits> +typename ParserBase<Traits>::ExpressionT +ParserBase<Traits>::ClassifyAndRewriteReferenceExpression( + ExpressionClassifier* classifier, ExpressionT expression, int beg_pos, + int end_pos, MessageTemplate::Template message, ParseErrorType type) { + Scanner::Location location(beg_pos, end_pos); + if (this->IsIdentifier(expression)) { + if (is_strict(language_mode()) && + this->IsEvalOrArguments(this->AsIdentifier(expression))) { + classifier->RecordExpressionError( + location, MessageTemplate::kStrictEvalArguments, kSyntaxError); + return expression; + } + if (is_strong(language_mode()) && + this->IsUndefined(this->AsIdentifier(expression))) { + classifier->RecordExpressionError( + location, MessageTemplate::kStrongUndefined, kSyntaxError); + return expression; + } + } + if (expression->IsValidReferenceExpression()) { + return expression; + } else if (expression->IsCall()) { + // If it is a call, make it a runtime error for legacy web compatibility. + // Rewrite `expr' to `expr[throw ReferenceError]'. + int pos = location.beg_pos; + ExpressionT error = this->NewThrowReferenceError(message, pos); + return factory()->NewProperty(expression, error, pos); + } else { + classifier->RecordExpressionError(location, message, type); + return expression; + } +} + + +template <typename Traits> +bool ParserBase<Traits>::IsValidReferenceExpression(ExpressionT expression) { + return this->IsAssignableIdentifier(expression) || expression->IsProperty(); +} + + +template <typename Traits> +void ParserBase<Traits>::CheckDestructuringElement( + ExpressionT expression, ExpressionClassifier* classifier, int begin, + int end) { + static const MessageTemplate::Template message = + MessageTemplate::kInvalidDestructuringTarget; + const Scanner::Location location(begin, end); + if (expression->IsArrayLiteral() || expression->IsObjectLiteral() || + expression->IsAssignment()) { + if (expression->is_parenthesized()) { + classifier->RecordPatternError(location, message); + } + return; + } + + if (expression->IsProperty()) { + classifier->RecordBindingPatternError(location, message); + } else if (!this->IsAssignableIdentifier(expression)) { + classifier->RecordPatternError(location, message); + } +} + + +#undef CHECK_OK +#undef CHECK_OK_CUSTOM + + +template <typename Traits> +void ParserBase<Traits>::ObjectLiteralChecker::CheckProperty( + Token::Value property, PropertyKind type, bool is_static, bool is_generator, + bool* ok) { + DCHECK(!is_static); + DCHECK(!is_generator || type == kMethodProperty); + + if (property == Token::SMI || property == Token::NUMBER) return; + + if (type == kValueProperty && IsProto()) { + if (has_seen_proto_) { + this->parser()->ReportMessage(MessageTemplate::kDuplicateProto); + *ok = false; + return; + } + has_seen_proto_ = true; + return; + } +} + + +template <typename Traits> +void ParserBase<Traits>::ClassLiteralChecker::CheckProperty( + Token::Value property, PropertyKind type, bool is_static, bool is_generator, + bool* ok) { + DCHECK(type == kMethodProperty || type == kAccessorProperty); + + if (property == Token::SMI || property == Token::NUMBER) return; + + if (is_static) { + if (IsPrototype()) { + this->parser()->ReportMessage(MessageTemplate::kStaticPrototype); + *ok = false; + return; + } + } else if (IsConstructor()) { + if (is_generator || type == kAccessorProperty) { + MessageTemplate::Template msg = + is_generator ? MessageTemplate::kConstructorIsGenerator + : MessageTemplate::kConstructorIsAccessor; + this->parser()->ReportMessage(msg); + *ok = false; + return; + } + if (has_seen_constructor_) { + this->parser()->ReportMessage(MessageTemplate::kDuplicateConstructor); + *ok = false; + return; + } + has_seen_constructor_ = true; + return; + } +} +} // namespace internal +} // namespace v8 + +#endif // V8_PARSING_PARSER_BASE_H diff --git a/deps/v8/src/parsing/parser.cc b/deps/v8/src/parsing/parser.cc new file mode 100644 index 0000000000..b1b8c1316b --- /dev/null +++ b/deps/v8/src/parsing/parser.cc @@ -0,0 +1,5548 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/parsing/parser.h" + +#include "src/api.h" +#include "src/ast/ast.h" +#include "src/ast/ast-expression-visitor.h" +#include "src/ast/ast-literal-reindexer.h" +#include "src/ast/scopeinfo.h" +#include "src/bailout-reason.h" +#include "src/base/platform/platform.h" +#include "src/bootstrapper.h" +#include "src/char-predicates-inl.h" +#include "src/codegen.h" +#include "src/compiler.h" +#include "src/messages.h" +#include "src/parsing/parameter-initializer-rewriter.h" +#include "src/parsing/parser-base.h" +#include "src/parsing/rewriter.h" +#include "src/parsing/scanner-character-streams.h" +#include "src/runtime/runtime.h" +#include "src/string-stream.h" + +namespace v8 { +namespace internal { + +ScriptData::ScriptData(const byte* data, int length) + : owns_data_(false), rejected_(false), data_(data), length_(length) { + if (!IsAligned(reinterpret_cast<intptr_t>(data), kPointerAlignment)) { + byte* copy = NewArray<byte>(length); + DCHECK(IsAligned(reinterpret_cast<intptr_t>(copy), kPointerAlignment)); + CopyBytes(copy, data, length); + data_ = copy; + AcquireDataOwnership(); + } +} + + +ParseInfo::ParseInfo(Zone* zone) + : zone_(zone), + flags_(0), + source_stream_(nullptr), + source_stream_encoding_(ScriptCompiler::StreamedSource::ONE_BYTE), + extension_(nullptr), + compile_options_(ScriptCompiler::kNoCompileOptions), + script_scope_(nullptr), + unicode_cache_(nullptr), + stack_limit_(0), + hash_seed_(0), + cached_data_(nullptr), + ast_value_factory_(nullptr), + literal_(nullptr), + scope_(nullptr) {} + + +ParseInfo::ParseInfo(Zone* zone, Handle<JSFunction> function) + : ParseInfo(zone, Handle<SharedFunctionInfo>(function->shared())) { + set_closure(function); + set_context(Handle<Context>(function->context())); +} + + +ParseInfo::ParseInfo(Zone* zone, Handle<SharedFunctionInfo> shared) + : ParseInfo(zone) { + isolate_ = shared->GetIsolate(); + + set_lazy(); + set_hash_seed(isolate_->heap()->HashSeed()); + set_stack_limit(isolate_->stack_guard()->real_climit()); + set_unicode_cache(isolate_->unicode_cache()); + set_language_mode(shared->language_mode()); + set_shared_info(shared); + + Handle<Script> script(Script::cast(shared->script())); + set_script(script); + if (!script.is_null() && script->type() == Script::TYPE_NATIVE) { + set_native(); + } +} + + +ParseInfo::ParseInfo(Zone* zone, Handle<Script> script) : ParseInfo(zone) { + isolate_ = script->GetIsolate(); + + set_hash_seed(isolate_->heap()->HashSeed()); + set_stack_limit(isolate_->stack_guard()->real_climit()); + set_unicode_cache(isolate_->unicode_cache()); + set_script(script); + + if (script->type() == Script::TYPE_NATIVE) { + set_native(); + } +} + + +FunctionEntry ParseData::GetFunctionEntry(int start) { + // The current pre-data entry must be a FunctionEntry with the given + // start position. + if ((function_index_ + FunctionEntry::kSize <= Length()) && + (static_cast<int>(Data()[function_index_]) == start)) { + int index = function_index_; + function_index_ += FunctionEntry::kSize; + Vector<unsigned> subvector(&(Data()[index]), FunctionEntry::kSize); + return FunctionEntry(subvector); + } + return FunctionEntry(); +} + + +int ParseData::FunctionCount() { + int functions_size = FunctionsSize(); + if (functions_size < 0) return 0; + if (functions_size % FunctionEntry::kSize != 0) return 0; + return functions_size / FunctionEntry::kSize; +} + + +bool ParseData::IsSane() { + if (!IsAligned(script_data_->length(), sizeof(unsigned))) return false; + // Check that the header data is valid and doesn't specify + // point to positions outside the store. + int data_length = Length(); + if (data_length < PreparseDataConstants::kHeaderSize) return false; + if (Magic() != PreparseDataConstants::kMagicNumber) return false; + if (Version() != PreparseDataConstants::kCurrentVersion) return false; + if (HasError()) return false; + // Check that the space allocated for function entries is sane. + int functions_size = FunctionsSize(); + if (functions_size < 0) return false; + if (functions_size % FunctionEntry::kSize != 0) return false; + // Check that the total size has room for header and function entries. + int minimum_size = + PreparseDataConstants::kHeaderSize + functions_size; + if (data_length < minimum_size) return false; + return true; +} + + +void ParseData::Initialize() { + // Prepares state for use. + int data_length = Length(); + if (data_length >= PreparseDataConstants::kHeaderSize) { + function_index_ = PreparseDataConstants::kHeaderSize; + } +} + + +bool ParseData::HasError() { + return Data()[PreparseDataConstants::kHasErrorOffset]; +} + + +unsigned ParseData::Magic() { + return Data()[PreparseDataConstants::kMagicOffset]; +} + + +unsigned ParseData::Version() { + return Data()[PreparseDataConstants::kVersionOffset]; +} + + +int ParseData::FunctionsSize() { + return static_cast<int>(Data()[PreparseDataConstants::kFunctionsSizeOffset]); +} + + +void Parser::SetCachedData(ParseInfo* info) { + if (compile_options_ == ScriptCompiler::kNoCompileOptions) { + cached_parse_data_ = NULL; + } else { + DCHECK(info->cached_data() != NULL); + if (compile_options_ == ScriptCompiler::kConsumeParserCache) { + cached_parse_data_ = ParseData::FromCachedData(*info->cached_data()); + } + } +} + + +FunctionLiteral* Parser::DefaultConstructor(bool call_super, Scope* scope, + int pos, int end_pos, + LanguageMode language_mode) { + int materialized_literal_count = -1; + int expected_property_count = -1; + int parameter_count = 0; + const AstRawString* name = ast_value_factory()->empty_string(); + + + FunctionKind kind = call_super ? FunctionKind::kDefaultSubclassConstructor + : FunctionKind::kDefaultBaseConstructor; + Scope* function_scope = NewScope(scope, FUNCTION_SCOPE, kind); + SetLanguageMode(function_scope, + static_cast<LanguageMode>(language_mode | STRICT)); + // Set start and end position to the same value + function_scope->set_start_position(pos); + function_scope->set_end_position(pos); + ZoneList<Statement*>* body = NULL; + + { + AstNodeFactory function_factory(ast_value_factory()); + FunctionState function_state(&function_state_, &scope_, function_scope, + kind, &function_factory); + + body = new (zone()) ZoneList<Statement*>(call_super ? 2 : 1, zone()); + if (call_super) { + // $super_constructor = %_GetSuperConstructor(<this-function>) + // %reflect_construct($super_constructor, arguments, new.target) + ZoneList<Expression*>* args = + new (zone()) ZoneList<Expression*>(2, zone()); + VariableProxy* this_function_proxy = scope_->NewUnresolved( + factory(), ast_value_factory()->this_function_string(), + Variable::NORMAL, pos); + ZoneList<Expression*>* tmp = + new (zone()) ZoneList<Expression*>(1, zone()); + tmp->Add(this_function_proxy, zone()); + Expression* super_constructor = factory()->NewCallRuntime( + Runtime::kInlineGetSuperConstructor, tmp, pos); + args->Add(super_constructor, zone()); + VariableProxy* arguments_proxy = scope_->NewUnresolved( + factory(), ast_value_factory()->arguments_string(), Variable::NORMAL, + pos); + args->Add(arguments_proxy, zone()); + VariableProxy* new_target_proxy = scope_->NewUnresolved( + factory(), ast_value_factory()->new_target_string(), Variable::NORMAL, + pos); + args->Add(new_target_proxy, zone()); + CallRuntime* call = factory()->NewCallRuntime( + Context::REFLECT_CONSTRUCT_INDEX, args, pos); + body->Add(factory()->NewReturnStatement(call, pos), zone()); + } + + materialized_literal_count = function_state.materialized_literal_count(); + expected_property_count = function_state.expected_property_count(); + } + + FunctionLiteral* function_literal = factory()->NewFunctionLiteral( + name, function_scope, body, materialized_literal_count, + expected_property_count, parameter_count, + FunctionLiteral::kNoDuplicateParameters, + FunctionLiteral::kAnonymousExpression, + FunctionLiteral::kShouldLazyCompile, kind, pos); + + return function_literal; +} + + +// ---------------------------------------------------------------------------- +// 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, BreakableStatement* statement) + : variable_(variable), statement_(statement), previous_(*variable) { + *variable = this; + } + + ~Target() { + *variable_ = previous_; + } + + Target* previous() { return previous_; } + BreakableStatement* statement() { return statement_; } + + private: + Target** variable_; + BreakableStatement* statement_; + 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_; +}; + + +// ---------------------------------------------------------------------------- +// 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 + +bool ParserTraits::IsEval(const AstRawString* identifier) const { + return identifier == parser_->ast_value_factory()->eval_string(); +} + + +bool ParserTraits::IsArguments(const AstRawString* identifier) const { + return identifier == parser_->ast_value_factory()->arguments_string(); +} + + +bool ParserTraits::IsEvalOrArguments(const AstRawString* identifier) const { + return IsEval(identifier) || IsArguments(identifier); +} + +bool ParserTraits::IsUndefined(const AstRawString* identifier) const { + return identifier == parser_->ast_value_factory()->undefined_string(); +} + +bool ParserTraits::IsPrototype(const AstRawString* identifier) const { + return identifier == parser_->ast_value_factory()->prototype_string(); +} + + +bool ParserTraits::IsConstructor(const AstRawString* identifier) const { + return identifier == parser_->ast_value_factory()->constructor_string(); +} + + +bool ParserTraits::IsThisProperty(Expression* expression) { + DCHECK(expression != NULL); + Property* property = expression->AsProperty(); + return property != NULL && property->obj()->IsVariableProxy() && + property->obj()->AsVariableProxy()->is_this(); +} + + +bool ParserTraits::IsIdentifier(Expression* expression) { + VariableProxy* operand = expression->AsVariableProxy(); + return operand != NULL && !operand->is_this(); +} + + +void ParserTraits::PushPropertyName(FuncNameInferrer* fni, + Expression* expression) { + if (expression->IsPropertyName()) { + fni->PushLiteralName(expression->AsLiteral()->AsRawPropertyName()); + } else { + fni->PushLiteralName( + parser_->ast_value_factory()->anonymous_function_string()); + } +} + + +void ParserTraits::CheckAssigningFunctionLiteralToProperty(Expression* left, + Expression* right) { + DCHECK(left != NULL); + if (left->IsProperty() && right->IsFunctionLiteral()) { + right->AsFunctionLiteral()->set_pretenure(); + } +} + + +Expression* ParserTraits::MarkExpressionAsAssigned(Expression* expression) { + VariableProxy* proxy = + expression != NULL ? expression->AsVariableProxy() : NULL; + if (proxy != NULL) proxy->set_is_assigned(); + return expression; +} + + +bool ParserTraits::ShortcutNumericLiteralBinaryExpression( + Expression** x, Expression* y, Token::Value op, int pos, + AstNodeFactory* factory) { + if ((*x)->AsLiteral() && (*x)->AsLiteral()->raw_value()->IsNumber() && + y->AsLiteral() && y->AsLiteral()->raw_value()->IsNumber()) { + double x_val = (*x)->AsLiteral()->raw_value()->AsNumber(); + double y_val = y->AsLiteral()->raw_value()->AsNumber(); + bool x_has_dot = (*x)->AsLiteral()->raw_value()->ContainsDot(); + bool y_has_dot = y->AsLiteral()->raw_value()->ContainsDot(); + bool has_dot = x_has_dot || y_has_dot; + switch (op) { + case Token::ADD: + *x = factory->NewNumberLiteral(x_val + y_val, pos, has_dot); + return true; + case Token::SUB: + *x = factory->NewNumberLiteral(x_val - y_val, pos, has_dot); + return true; + case Token::MUL: + *x = factory->NewNumberLiteral(x_val * y_val, pos, has_dot); + return true; + case Token::DIV: + *x = factory->NewNumberLiteral(x_val / y_val, pos, has_dot); + return true; + case Token::BIT_OR: { + int value = DoubleToInt32(x_val) | DoubleToInt32(y_val); + *x = factory->NewNumberLiteral(value, pos, has_dot); + return true; + } + case Token::BIT_AND: { + int value = DoubleToInt32(x_val) & DoubleToInt32(y_val); + *x = factory->NewNumberLiteral(value, pos, has_dot); + return true; + } + case Token::BIT_XOR: { + int value = DoubleToInt32(x_val) ^ DoubleToInt32(y_val); + *x = factory->NewNumberLiteral(value, pos, has_dot); + return true; + } + case Token::SHL: { + int value = DoubleToInt32(x_val) << (DoubleToInt32(y_val) & 0x1f); + *x = factory->NewNumberLiteral(value, pos, has_dot); + return true; + } + case Token::SHR: { + uint32_t shift = DoubleToInt32(y_val) & 0x1f; + uint32_t value = DoubleToUint32(x_val) >> shift; + *x = factory->NewNumberLiteral(value, pos, has_dot); + return true; + } + case Token::SAR: { + uint32_t shift = DoubleToInt32(y_val) & 0x1f; + int value = ArithmeticShiftRight(DoubleToInt32(x_val), shift); + *x = factory->NewNumberLiteral(value, pos, has_dot); + return true; + } + default: + break; + } + } + return false; +} + + +Expression* ParserTraits::BuildUnaryExpression(Expression* expression, + Token::Value op, int pos, + AstNodeFactory* factory) { + DCHECK(expression != NULL); + if (expression->IsLiteral()) { + const AstValue* literal = expression->AsLiteral()->raw_value(); + if (op == Token::NOT) { + // Convert the literal to a boolean condition and negate it. + bool condition = literal->BooleanValue(); + return factory->NewBooleanLiteral(!condition, pos); + } else if (literal->IsNumber()) { + // Compute some expressions involving only number literals. + double value = literal->AsNumber(); + bool has_dot = literal->ContainsDot(); + switch (op) { + case Token::ADD: + return expression; + case Token::SUB: + return factory->NewNumberLiteral(-value, pos, has_dot); + case Token::BIT_NOT: + return factory->NewNumberLiteral(~DoubleToInt32(value), pos, has_dot); + default: + break; + } + } + } + // Desugar '+foo' => 'foo*1' + if (op == Token::ADD) { + return factory->NewBinaryOperation( + Token::MUL, expression, factory->NewNumberLiteral(1, pos, true), pos); + } + // The same idea for '-foo' => 'foo*(-1)'. + if (op == Token::SUB) { + return factory->NewBinaryOperation( + Token::MUL, expression, factory->NewNumberLiteral(-1, pos), pos); + } + // ...and one more time for '~foo' => 'foo^(~0)'. + if (op == Token::BIT_NOT) { + return factory->NewBinaryOperation( + Token::BIT_XOR, expression, factory->NewNumberLiteral(~0, pos), pos); + } + return factory->NewUnaryOperation(op, expression, pos); +} + + +Expression* ParserTraits::NewThrowReferenceError( + MessageTemplate::Template message, int pos) { + return NewThrowError(Runtime::kNewReferenceError, message, + parser_->ast_value_factory()->empty_string(), pos); +} + + +Expression* ParserTraits::NewThrowSyntaxError(MessageTemplate::Template message, + const AstRawString* arg, + int pos) { + return NewThrowError(Runtime::kNewSyntaxError, message, arg, pos); +} + + +Expression* ParserTraits::NewThrowTypeError(MessageTemplate::Template message, + const AstRawString* arg, int pos) { + return NewThrowError(Runtime::kNewTypeError, message, arg, pos); +} + + +Expression* ParserTraits::NewThrowError(Runtime::FunctionId id, + MessageTemplate::Template message, + const AstRawString* arg, int pos) { + Zone* zone = parser_->zone(); + ZoneList<Expression*>* args = new (zone) ZoneList<Expression*>(2, zone); + args->Add(parser_->factory()->NewSmiLiteral(message, pos), zone); + args->Add(parser_->factory()->NewStringLiteral(arg, pos), zone); + CallRuntime* call_constructor = + parser_->factory()->NewCallRuntime(id, args, pos); + return parser_->factory()->NewThrow(call_constructor, pos); +} + + +void ParserTraits::ReportMessageAt(Scanner::Location source_location, + MessageTemplate::Template message, + const char* arg, ParseErrorType error_type) { + if (parser_->stack_overflow()) { + // Suppress the error message (syntax error or such) in the presence of a + // stack overflow. The isolate allows only one pending exception at at time + // and we want to report the stack overflow later. + return; + } + parser_->pending_error_handler_.ReportMessageAt(source_location.beg_pos, + source_location.end_pos, + message, arg, error_type); +} + + +void ParserTraits::ReportMessage(MessageTemplate::Template message, + const char* arg, ParseErrorType error_type) { + Scanner::Location source_location = parser_->scanner()->location(); + ReportMessageAt(source_location, message, arg, error_type); +} + + +void ParserTraits::ReportMessage(MessageTemplate::Template message, + const AstRawString* arg, + ParseErrorType error_type) { + Scanner::Location source_location = parser_->scanner()->location(); + ReportMessageAt(source_location, message, arg, error_type); +} + + +void ParserTraits::ReportMessageAt(Scanner::Location source_location, + MessageTemplate::Template message, + const AstRawString* arg, + ParseErrorType error_type) { + if (parser_->stack_overflow()) { + // Suppress the error message (syntax error or such) in the presence of a + // stack overflow. The isolate allows only one pending exception at at time + // and we want to report the stack overflow later. + return; + } + parser_->pending_error_handler_.ReportMessageAt(source_location.beg_pos, + source_location.end_pos, + message, arg, error_type); +} + + +const AstRawString* ParserTraits::GetSymbol(Scanner* scanner) { + const AstRawString* result = + parser_->scanner()->CurrentSymbol(parser_->ast_value_factory()); + DCHECK(result != NULL); + return result; +} + + +const AstRawString* ParserTraits::GetNumberAsSymbol(Scanner* scanner) { + double double_value = parser_->scanner()->DoubleValue(); + char array[100]; + const char* string = + DoubleToCString(double_value, Vector<char>(array, arraysize(array))); + return parser_->ast_value_factory()->GetOneByteString(string); +} + + +const AstRawString* ParserTraits::GetNextSymbol(Scanner* scanner) { + return parser_->scanner()->NextSymbol(parser_->ast_value_factory()); +} + + +Expression* ParserTraits::ThisExpression(Scope* scope, AstNodeFactory* factory, + int pos) { + return scope->NewUnresolved(factory, + parser_->ast_value_factory()->this_string(), + Variable::THIS, pos, pos + 4); +} + + +Expression* ParserTraits::SuperPropertyReference(Scope* scope, + AstNodeFactory* factory, + int pos) { + // this_function[home_object_symbol] + VariableProxy* this_function_proxy = scope->NewUnresolved( + factory, parser_->ast_value_factory()->this_function_string(), + Variable::NORMAL, pos); + Expression* home_object_symbol_literal = + factory->NewSymbolLiteral("home_object_symbol", RelocInfo::kNoPosition); + Expression* home_object = factory->NewProperty( + this_function_proxy, home_object_symbol_literal, pos); + return factory->NewSuperPropertyReference( + ThisExpression(scope, factory, pos)->AsVariableProxy(), home_object, pos); +} + + +Expression* ParserTraits::SuperCallReference(Scope* scope, + AstNodeFactory* factory, int pos) { + VariableProxy* new_target_proxy = scope->NewUnresolved( + factory, parser_->ast_value_factory()->new_target_string(), + Variable::NORMAL, pos); + VariableProxy* this_function_proxy = scope->NewUnresolved( + factory, parser_->ast_value_factory()->this_function_string(), + Variable::NORMAL, pos); + return factory->NewSuperCallReference( + ThisExpression(scope, factory, pos)->AsVariableProxy(), new_target_proxy, + this_function_proxy, pos); +} + + +Expression* ParserTraits::NewTargetExpression(Scope* scope, + AstNodeFactory* factory, + int pos) { + static const int kNewTargetStringLength = 10; + auto proxy = scope->NewUnresolved( + factory, parser_->ast_value_factory()->new_target_string(), + Variable::NORMAL, pos, pos + kNewTargetStringLength); + proxy->set_is_new_target(); + return proxy; +} + + +Expression* ParserTraits::DefaultConstructor(bool call_super, Scope* scope, + int pos, int end_pos, + LanguageMode mode) { + return parser_->DefaultConstructor(call_super, scope, pos, end_pos, mode); +} + + +Literal* ParserTraits::ExpressionFromLiteral(Token::Value token, int pos, + Scanner* scanner, + AstNodeFactory* factory) { + switch (token) { + case Token::NULL_LITERAL: + return factory->NewNullLiteral(pos); + case Token::TRUE_LITERAL: + return factory->NewBooleanLiteral(true, pos); + case Token::FALSE_LITERAL: + return factory->NewBooleanLiteral(false, pos); + case Token::SMI: { + int value = scanner->smi_value(); + return factory->NewSmiLiteral(value, pos); + } + case Token::NUMBER: { + bool has_dot = scanner->ContainsDot(); + double value = scanner->DoubleValue(); + return factory->NewNumberLiteral(value, pos, has_dot); + } + default: + DCHECK(false); + } + return NULL; +} + + +Expression* ParserTraits::ExpressionFromIdentifier(const AstRawString* name, + int start_position, + int end_position, + Scope* scope, + AstNodeFactory* factory) { + if (parser_->fni_ != NULL) parser_->fni_->PushVariableName(name); + return scope->NewUnresolved(factory, name, Variable::NORMAL, start_position, + end_position); +} + + +Expression* ParserTraits::ExpressionFromString(int pos, Scanner* scanner, + AstNodeFactory* factory) { + const AstRawString* symbol = GetSymbol(scanner); + if (parser_->fni_ != NULL) parser_->fni_->PushLiteralName(symbol); + return factory->NewStringLiteral(symbol, pos); +} + + +Expression* ParserTraits::GetIterator(Expression* iterable, + AstNodeFactory* factory, int pos) { + Expression* iterator_symbol_literal = + factory->NewSymbolLiteral("iterator_symbol", RelocInfo::kNoPosition); + Expression* prop = + factory->NewProperty(iterable, iterator_symbol_literal, pos); + Zone* zone = parser_->zone(); + ZoneList<Expression*>* args = new (zone) ZoneList<Expression*>(0, zone); + return factory->NewCall(prop, args, pos); +} + + +Literal* ParserTraits::GetLiteralTheHole(int position, + AstNodeFactory* factory) { + return factory->NewTheHoleLiteral(RelocInfo::kNoPosition); +} + + +Expression* ParserTraits::ParseV8Intrinsic(bool* ok) { + return parser_->ParseV8Intrinsic(ok); +} + + +FunctionLiteral* ParserTraits::ParseFunctionLiteral( + const AstRawString* name, Scanner::Location function_name_location, + FunctionNameValidity function_name_validity, FunctionKind kind, + int function_token_position, FunctionLiteral::FunctionType type, + FunctionLiteral::ArityRestriction arity_restriction, + LanguageMode language_mode, bool* ok) { + return parser_->ParseFunctionLiteral( + name, function_name_location, function_name_validity, kind, + function_token_position, type, arity_restriction, language_mode, ok); +} + + +ClassLiteral* ParserTraits::ParseClassLiteral( + const AstRawString* name, Scanner::Location class_name_location, + bool name_is_strict_reserved, int pos, bool* ok) { + return parser_->ParseClassLiteral(name, class_name_location, + name_is_strict_reserved, pos, ok); +} + + +Parser::Parser(ParseInfo* info) + : ParserBase<ParserTraits>(info->zone(), &scanner_, info->stack_limit(), + info->extension(), info->ast_value_factory(), + NULL, this), + scanner_(info->unicode_cache()), + reusable_preparser_(NULL), + original_scope_(NULL), + target_stack_(NULL), + compile_options_(info->compile_options()), + cached_parse_data_(NULL), + total_preparse_skipped_(0), + pre_parse_timer_(NULL), + parsing_on_main_thread_(true) { + // Even though we were passed ParseInfo, we should not store it in + // Parser - this makes sure that Isolate is not accidentally accessed via + // ParseInfo during background parsing. + DCHECK(!info->script().is_null() || info->source_stream() != NULL); + set_allow_lazy(info->allow_lazy_parsing()); + set_allow_natives(FLAG_allow_natives_syntax || info->is_native()); + set_allow_harmony_sloppy(FLAG_harmony_sloppy); + set_allow_harmony_sloppy_function(FLAG_harmony_sloppy_function); + set_allow_harmony_sloppy_let(FLAG_harmony_sloppy_let); + set_allow_harmony_default_parameters(FLAG_harmony_default_parameters); + set_allow_harmony_destructuring_bind(FLAG_harmony_destructuring_bind); + set_allow_harmony_destructuring_assignment( + FLAG_harmony_destructuring_assignment); + set_allow_strong_mode(FLAG_strong_mode); + set_allow_legacy_const(FLAG_legacy_const); + set_allow_harmony_do_expressions(FLAG_harmony_do_expressions); + set_allow_harmony_function_name(FLAG_harmony_function_name); + for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount; + ++feature) { + use_counts_[feature] = 0; + } + if (info->ast_value_factory() == NULL) { + // info takes ownership of AstValueFactory. + info->set_ast_value_factory(new AstValueFactory(zone(), info->hash_seed())); + info->set_ast_value_factory_owned(); + ast_value_factory_ = info->ast_value_factory(); + } +} + + +FunctionLiteral* Parser::ParseProgram(Isolate* isolate, ParseInfo* info) { + // TODO(bmeurer): We temporarily need to pass allow_nesting = true here, + // see comment for HistogramTimerScope class. + + // It's OK to use the Isolate & counters here, since this function is only + // called in the main thread. + DCHECK(parsing_on_main_thread_); + + HistogramTimerScope timer_scope(isolate->counters()->parse(), true); + Handle<String> source(String::cast(info->script()->source())); + isolate->counters()->total_parse_size()->Increment(source->length()); + base::ElapsedTimer timer; + if (FLAG_trace_parse) { + timer.Start(); + } + fni_ = new (zone()) FuncNameInferrer(ast_value_factory(), zone()); + + // Initialize parser state. + CompleteParserRecorder recorder; + + if (produce_cached_parse_data()) { + log_ = &recorder; + } else if (consume_cached_parse_data()) { + cached_parse_data_->Initialize(); + } + + source = String::Flatten(source); + FunctionLiteral* result; + + 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. + ExternalTwoByteStringUtf16CharacterStream stream( + Handle<ExternalTwoByteString>::cast(source), 0, source->length()); + scanner_.Initialize(&stream); + result = DoParseProgram(info); + } else { + GenericStringUtf16CharacterStream stream(source, 0, source->length()); + scanner_.Initialize(&stream); + result = DoParseProgram(info); + } + if (result != NULL) { + DCHECK_EQ(scanner_.peek_location().beg_pos, source->length()); + } + HandleSourceURLComments(isolate, info->script()); + + if (FLAG_trace_parse && result != NULL) { + double ms = timer.Elapsed().InMillisecondsF(); + if (info->is_eval()) { + PrintF("[parsing eval"); + } else if (info->script()->name()->IsString()) { + String* name = String::cast(info->script()->name()); + base::SmartArrayPointer<char> name_chars = name->ToCString(); + PrintF("[parsing script: %s", name_chars.get()); + } else { + PrintF("[parsing script"); + } + PrintF(" - took %0.3f ms]\n", ms); + } + if (produce_cached_parse_data()) { + if (result != NULL) *info->cached_data() = recorder.GetScriptData(); + log_ = NULL; + } + return result; +} + + +FunctionLiteral* Parser::DoParseProgram(ParseInfo* info) { + // Note that this function can be called from the main thread or from a + // background thread. We should not access anything Isolate / heap dependent + // via ParseInfo, and also not pass it forward. + DCHECK(scope_ == NULL); + DCHECK(target_stack_ == NULL); + + Mode parsing_mode = FLAG_lazy && allow_lazy() ? PARSE_LAZILY : PARSE_EAGERLY; + if (allow_natives() || extension_ != NULL) parsing_mode = PARSE_EAGERLY; + + FunctionLiteral* result = NULL; + { + // TODO(wingo): Add an outer SCRIPT_SCOPE corresponding to the native + // context, which will have the "this" binding for script scopes. + Scope* scope = NewScope(scope_, SCRIPT_SCOPE); + info->set_script_scope(scope); + if (!info->context().is_null() && !info->context()->IsNativeContext()) { + scope = Scope::DeserializeScopeChain(info->isolate(), zone(), + *info->context(), scope); + // The Scope is backed up by ScopeInfo (which is in the V8 heap); this + // means the Parser cannot operate independent of the V8 heap. Tell the + // string table to internalize strings and values right after they're + // created. This kind of parsing can only be done in the main thread. + DCHECK(parsing_on_main_thread_); + ast_value_factory()->Internalize(info->isolate()); + } + original_scope_ = scope; + if (info->is_eval()) { + if (!scope->is_script_scope() || is_strict(info->language_mode())) { + parsing_mode = PARSE_EAGERLY; + } + scope = NewScope(scope, EVAL_SCOPE); + } else if (info->is_module()) { + scope = NewScope(scope, MODULE_SCOPE); + } + + scope->set_start_position(0); + + // Enter 'scope' with the given parsing mode. + ParsingModeScope parsing_mode_scope(this, parsing_mode); + AstNodeFactory function_factory(ast_value_factory()); + FunctionState function_state(&function_state_, &scope_, scope, + kNormalFunction, &function_factory); + + // Don't count the mode in the use counters--give the program a chance + // to enable script/module-wide strict/strong mode below. + scope_->SetLanguageMode(info->language_mode()); + ZoneList<Statement*>* body = new(zone()) ZoneList<Statement*>(16, zone()); + bool ok = true; + int beg_pos = scanner()->location().beg_pos; + if (info->is_module()) { + ParseModuleItemList(body, &ok); + } else { + ParseStatementList(body, Token::EOS, &ok); + } + + // The parser will peek but not consume EOS. Our scope logically goes all + // the way to the EOS, though. + scope->set_end_position(scanner()->peek_location().beg_pos); + + if (ok && is_strict(language_mode())) { + CheckStrictOctalLiteral(beg_pos, scanner()->location().end_pos, &ok); + } + if (ok && is_sloppy(language_mode()) && allow_harmony_sloppy_function()) { + // TODO(littledan): Function bindings on the global object that modify + // pre-existing bindings should be made writable, enumerable and + // nonconfigurable if possible, whereas this code will leave attributes + // unchanged if the property already exists. + InsertSloppyBlockFunctionVarBindings(scope, &ok); + } + if (ok && (is_strict(language_mode()) || allow_harmony_sloppy() || + allow_harmony_destructuring_bind())) { + CheckConflictingVarDeclarations(scope_, &ok); + } + + if (ok && info->parse_restriction() == ONLY_SINGLE_FUNCTION_LITERAL) { + if (body->length() != 1 || + !body->at(0)->IsExpressionStatement() || + !body->at(0)->AsExpressionStatement()-> + expression()->IsFunctionLiteral()) { + ReportMessage(MessageTemplate::kSingleFunctionLiteral); + ok = false; + } + } + + if (ok) { + ParserTraits::RewriteDestructuringAssignments(); + result = factory()->NewFunctionLiteral( + ast_value_factory()->empty_string(), scope_, body, + function_state.materialized_literal_count(), + function_state.expected_property_count(), 0, + FunctionLiteral::kNoDuplicateParameters, + FunctionLiteral::kGlobalOrEval, FunctionLiteral::kShouldLazyCompile, + FunctionKind::kNormalFunction, 0); + } + } + + // Make sure the target stack is empty. + DCHECK(target_stack_ == NULL); + + return result; +} + + +FunctionLiteral* Parser::ParseLazy(Isolate* isolate, ParseInfo* info) { + // It's OK to use the Isolate & counters here, since this function is only + // called in the main thread. + DCHECK(parsing_on_main_thread_); + HistogramTimerScope timer_scope(isolate->counters()->parse_lazy()); + Handle<String> source(String::cast(info->script()->source())); + isolate->counters()->total_parse_size()->Increment(source->length()); + base::ElapsedTimer timer; + if (FLAG_trace_parse) { + timer.Start(); + } + Handle<SharedFunctionInfo> shared_info = info->shared_info(); + + // Initialize parser state. + source = String::Flatten(source); + FunctionLiteral* result; + if (source->IsExternalTwoByteString()) { + ExternalTwoByteStringUtf16CharacterStream stream( + Handle<ExternalTwoByteString>::cast(source), + shared_info->start_position(), + shared_info->end_position()); + result = ParseLazy(isolate, info, &stream); + } else { + GenericStringUtf16CharacterStream stream(source, + shared_info->start_position(), + shared_info->end_position()); + result = ParseLazy(isolate, info, &stream); + } + + if (FLAG_trace_parse && result != NULL) { + double ms = timer.Elapsed().InMillisecondsF(); + base::SmartArrayPointer<char> name_chars = + result->debug_name()->ToCString(); + PrintF("[parsing function: %s - took %0.3f ms]\n", name_chars.get(), ms); + } + return result; +} + + +FunctionLiteral* Parser::ParseLazy(Isolate* isolate, ParseInfo* info, + Utf16CharacterStream* source) { + Handle<SharedFunctionInfo> shared_info = info->shared_info(); + scanner_.Initialize(source); + DCHECK(scope_ == NULL); + DCHECK(target_stack_ == NULL); + + Handle<String> name(String::cast(shared_info->name())); + DCHECK(ast_value_factory()); + fni_ = new (zone()) FuncNameInferrer(ast_value_factory(), zone()); + const AstRawString* raw_name = ast_value_factory()->GetString(name); + fni_->PushEnclosingName(raw_name); + + ParsingModeScope parsing_mode(this, PARSE_EAGERLY); + + // Place holder for the result. + FunctionLiteral* result = NULL; + + { + // Parse the function literal. + Scope* scope = NewScope(scope_, SCRIPT_SCOPE); + info->set_script_scope(scope); + if (!info->closure().is_null()) { + // Ok to use Isolate here, since lazy function parsing is only done in the + // main thread. + DCHECK(parsing_on_main_thread_); + scope = Scope::DeserializeScopeChain(isolate, zone(), + info->closure()->context(), scope); + } + original_scope_ = scope; + AstNodeFactory function_factory(ast_value_factory()); + FunctionState function_state(&function_state_, &scope_, scope, + shared_info->kind(), &function_factory); + DCHECK(is_sloppy(scope->language_mode()) || + is_strict(info->language_mode())); + DCHECK(info->language_mode() == shared_info->language_mode()); + FunctionLiteral::FunctionType function_type = + shared_info->is_expression() + ? (shared_info->is_anonymous() + ? FunctionLiteral::kAnonymousExpression + : FunctionLiteral::kNamedExpression) + : FunctionLiteral::kDeclaration; + bool ok = true; + + if (shared_info->is_arrow()) { + // TODO(adamk): We should construct this scope from the ScopeInfo. + Scope* scope = + NewScope(scope_, FUNCTION_SCOPE, FunctionKind::kArrowFunction); + + // These two bits only need to be explicitly set because we're + // not passing the ScopeInfo to the Scope constructor. + // TODO(adamk): Remove these calls once the above NewScope call + // passes the ScopeInfo. + if (shared_info->scope_info()->CallsEval()) { + scope->RecordEvalCall(); + } + SetLanguageMode(scope, shared_info->language_mode()); + + scope->set_start_position(shared_info->start_position()); + ExpressionClassifier formals_classifier; + ParserFormalParameters formals(scope); + Checkpoint checkpoint(this); + { + // Parsing patterns as variable reference expression creates + // NewUnresolved references in current scope. Entrer arrow function + // scope for formal parameter parsing. + BlockState block_state(&scope_, scope); + if (Check(Token::LPAREN)) { + // '(' StrictFormalParameters ')' + ParseFormalParameterList(&formals, &formals_classifier, &ok); + if (ok) ok = Check(Token::RPAREN); + } else { + // BindingIdentifier + ParseFormalParameter(&formals, &formals_classifier, &ok); + if (ok) { + DeclareFormalParameter(formals.scope, formals.at(0), + &formals_classifier); + } + } + } + + if (ok) { + checkpoint.Restore(&formals.materialized_literals_count); + // Pass `accept_IN=true` to ParseArrowFunctionLiteral --- This should + // not be observable, or else the preparser would have failed. + Expression* expression = + ParseArrowFunctionLiteral(true, formals, formals_classifier, &ok); + if (ok) { + // Scanning must end at the same position that was recorded + // previously. If not, parsing has been interrupted due to a stack + // overflow, at which point the partially parsed arrow function + // concise body happens to be a valid expression. This is a problem + // only for arrow functions with single expression bodies, since there + // is no end token such as "}" for normal functions. + if (scanner()->location().end_pos == shared_info->end_position()) { + // The pre-parser saw an arrow function here, so the full parser + // must produce a FunctionLiteral. + DCHECK(expression->IsFunctionLiteral()); + result = expression->AsFunctionLiteral(); + } else { + ok = false; + } + } + } + } else if (shared_info->is_default_constructor()) { + result = DefaultConstructor(IsSubclassConstructor(shared_info->kind()), + scope, shared_info->start_position(), + shared_info->end_position(), + shared_info->language_mode()); + } else { + result = ParseFunctionLiteral( + raw_name, Scanner::Location::invalid(), kSkipFunctionNameCheck, + shared_info->kind(), RelocInfo::kNoPosition, function_type, + FunctionLiteral::kNormalArity, shared_info->language_mode(), &ok); + } + // Make sure the results agree. + DCHECK(ok == (result != NULL)); + } + + // Make sure the target stack is empty. + DCHECK(target_stack_ == NULL); + + if (result != NULL) { + Handle<String> inferred_name(shared_info->inferred_name()); + result->set_inferred_name(inferred_name); + } + return result; +} + + +void* Parser::ParseStatementList(ZoneList<Statement*>* body, int end_token, + bool* ok) { + // StatementList :: + // (StatementListItem)* <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_); + + DCHECK(body != NULL); + 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(); + Scanner::Location old_this_loc = function_state_->this_location(); + Scanner::Location old_super_loc = function_state_->super_location(); + Statement* stat = ParseStatementListItem(CHECK_OK); + + if (is_strong(language_mode()) && scope_->is_function_scope() && + IsClassConstructor(function_state_->kind())) { + Scanner::Location this_loc = function_state_->this_location(); + Scanner::Location super_loc = function_state_->super_location(); + if (this_loc.beg_pos != old_this_loc.beg_pos && + this_loc.beg_pos != token_loc.beg_pos) { + ReportMessageAt(this_loc, MessageTemplate::kStrongConstructorThis); + *ok = false; + return nullptr; + } + if (super_loc.beg_pos != old_super_loc.beg_pos && + super_loc.beg_pos != token_loc.beg_pos) { + ReportMessageAt(super_loc, MessageTemplate::kStrongConstructorSuper); + *ok = false; + return nullptr; + } + } + + 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->raw_value()->IsString()) { + // Check "use strict" directive (ES5 14.1), "use asm" directive, and + // "use strong" directive (experimental). + bool use_strict_found = + literal->raw_value()->AsString() == + ast_value_factory()->use_strict_string() && + token_loc.end_pos - token_loc.beg_pos == + ast_value_factory()->use_strict_string()->length() + 2; + bool use_strong_found = + allow_strong_mode() && + literal->raw_value()->AsString() == + ast_value_factory()->use_strong_string() && + token_loc.end_pos - token_loc.beg_pos == + ast_value_factory()->use_strong_string()->length() + 2; + if (use_strict_found || use_strong_found) { + // Strong mode implies strict mode. If there are several "use strict" + // / "use strong" directives, do the strict mode changes only once. + if (is_sloppy(scope_->language_mode())) { + RaiseLanguageMode(STRICT); + } + + if (use_strong_found) { + RaiseLanguageMode(STRONG); + if (IsClassConstructor(function_state_->kind())) { + // "use strong" cannot occur in a class constructor body, to avoid + // unintuitive strong class object semantics. + ParserTraits::ReportMessageAt( + token_loc, MessageTemplate::kStrongConstructorDirective); + *ok = false; + return nullptr; + } + } + if (!scope_->HasSimpleParameters()) { + // TC39 deemed "use strict" directives to be an error when occurring + // in the body of a function with non-simple parameter list, on + // 29/7/2015. https://goo.gl/ueA7Ln + // + // In V8, this also applies to "use strong " directives. + const AstRawString* string = literal->raw_value()->AsString(); + ParserTraits::ReportMessageAt( + token_loc, MessageTemplate::kIllegalLanguageModeDirective, + string); + *ok = false; + return nullptr; + } + // Because declarations in strict eval code don't leak into the scope + // of the eval call, it is likely that functions declared in strict + // eval code will be used within the eval code, so lazy parsing is + // probably not a win. + if (scope_->is_eval_scope()) mode_ = PARSE_EAGERLY; + } else if (literal->raw_value()->AsString() == + ast_value_factory()->use_asm_string() && + token_loc.end_pos - token_loc.beg_pos == + ast_value_factory()->use_asm_string()->length() + 2) { + // Store the usage count; The actual use counter on the isolate is + // incremented after parsing is done. + ++use_counts_[v8::Isolate::kUseAsm]; + scope_->SetAsmModule(); + } else { + // Should not change mode, but will increment UseCounter + // if appropriate. Ditto usages below. + RaiseLanguageMode(SLOPPY); + } + } else { + // End of the directive prologue. + directive_prologue = false; + RaiseLanguageMode(SLOPPY); + } + } else { + RaiseLanguageMode(SLOPPY); + } + + body->Add(stat, zone()); + } + + return 0; +} + + +Statement* Parser::ParseStatementListItem(bool* ok) { + // (Ecma 262 6th Edition, 13.1): + // StatementListItem: + // Statement + // Declaration + + if (peek() != Token::CLASS) { + // No more classes follow; reset the start position for the consecutive + // class declaration group. + scope_->set_class_declaration_group_start(-1); + } + + switch (peek()) { + case Token::FUNCTION: + return ParseFunctionDeclaration(NULL, ok); + case Token::CLASS: + if (scope_->class_declaration_group_start() < 0) { + scope_->set_class_declaration_group_start( + scanner()->peek_location().beg_pos); + } + return ParseClassDeclaration(NULL, ok); + case Token::CONST: + if (allow_const()) { + return ParseVariableStatement(kStatementListItem, NULL, ok); + } + break; + case Token::VAR: + return ParseVariableStatement(kStatementListItem, NULL, ok); + case Token::LET: + if (IsNextLetKeyword()) { + return ParseVariableStatement(kStatementListItem, NULL, ok); + } + break; + default: + break; + } + return ParseStatement(NULL, ok); +} + + +Statement* Parser::ParseModuleItem(bool* ok) { + // (Ecma 262 6th Edition, 15.2): + // ModuleItem : + // ImportDeclaration + // ExportDeclaration + // StatementListItem + + switch (peek()) { + case Token::IMPORT: + return ParseImportDeclaration(ok); + case Token::EXPORT: + return ParseExportDeclaration(ok); + default: + return ParseStatementListItem(ok); + } +} + + +void* Parser::ParseModuleItemList(ZoneList<Statement*>* body, bool* ok) { + // (Ecma 262 6th Edition, 15.2): + // Module : + // ModuleBody? + // + // ModuleBody : + // ModuleItem* + + DCHECK(scope_->is_module_scope()); + RaiseLanguageMode(STRICT); + + while (peek() != Token::EOS) { + Statement* stat = ParseModuleItem(CHECK_OK); + if (stat && !stat->IsEmpty()) { + body->Add(stat, zone()); + } + } + + // Check that all exports are bound. + ModuleDescriptor* descriptor = scope_->module(); + for (ModuleDescriptor::Iterator it = descriptor->iterator(); !it.done(); + it.Advance()) { + if (scope_->LookupLocal(it.local_name()) == NULL) { + // TODO(adamk): Pass both local_name and export_name once ParserTraits + // supports multiple arg error messages. + // Also try to report this at a better location. + ParserTraits::ReportMessage(MessageTemplate::kModuleExportUndefined, + it.local_name()); + *ok = false; + return NULL; + } + } + + scope_->module()->Freeze(); + return NULL; +} + + +const AstRawString* Parser::ParseModuleSpecifier(bool* ok) { + // ModuleSpecifier : + // StringLiteral + + Expect(Token::STRING, CHECK_OK); + return GetSymbol(scanner()); +} + + +void* Parser::ParseExportClause(ZoneList<const AstRawString*>* export_names, + ZoneList<Scanner::Location>* export_locations, + ZoneList<const AstRawString*>* local_names, + Scanner::Location* reserved_loc, bool* ok) { + // ExportClause : + // '{' '}' + // '{' ExportsList '}' + // '{' ExportsList ',' '}' + // + // ExportsList : + // ExportSpecifier + // ExportsList ',' ExportSpecifier + // + // ExportSpecifier : + // IdentifierName + // IdentifierName 'as' IdentifierName + + Expect(Token::LBRACE, CHECK_OK); + + Token::Value name_tok; + while ((name_tok = peek()) != Token::RBRACE) { + // Keep track of the first reserved word encountered in case our + // caller needs to report an error. + if (!reserved_loc->IsValid() && + !Token::IsIdentifier(name_tok, STRICT, false)) { + *reserved_loc = scanner()->location(); + } + const AstRawString* local_name = ParseIdentifierName(CHECK_OK); + const AstRawString* export_name = NULL; + if (CheckContextualKeyword(CStrVector("as"))) { + export_name = ParseIdentifierName(CHECK_OK); + } + if (export_name == NULL) { + export_name = local_name; + } + export_names->Add(export_name, zone()); + local_names->Add(local_name, zone()); + export_locations->Add(scanner()->location(), zone()); + if (peek() == Token::RBRACE) break; + Expect(Token::COMMA, CHECK_OK); + } + + Expect(Token::RBRACE, CHECK_OK); + + return 0; +} + + +ZoneList<ImportDeclaration*>* Parser::ParseNamedImports(int pos, bool* ok) { + // NamedImports : + // '{' '}' + // '{' ImportsList '}' + // '{' ImportsList ',' '}' + // + // ImportsList : + // ImportSpecifier + // ImportsList ',' ImportSpecifier + // + // ImportSpecifier : + // BindingIdentifier + // IdentifierName 'as' BindingIdentifier + + Expect(Token::LBRACE, CHECK_OK); + + ZoneList<ImportDeclaration*>* result = + new (zone()) ZoneList<ImportDeclaration*>(1, zone()); + while (peek() != Token::RBRACE) { + const AstRawString* import_name = ParseIdentifierName(CHECK_OK); + const AstRawString* local_name = import_name; + // In the presence of 'as', the left-side of the 'as' can + // be any IdentifierName. But without 'as', it must be a valid + // BindingIdentifier. + if (CheckContextualKeyword(CStrVector("as"))) { + local_name = ParseIdentifierName(CHECK_OK); + } + if (!Token::IsIdentifier(scanner()->current_token(), STRICT, false)) { + *ok = false; + ReportMessage(MessageTemplate::kUnexpectedReserved); + return NULL; + } else if (IsEvalOrArguments(local_name)) { + *ok = false; + ReportMessage(MessageTemplate::kStrictEvalArguments); + return NULL; + } else if (is_strong(language_mode()) && IsUndefined(local_name)) { + *ok = false; + ReportMessage(MessageTemplate::kStrongUndefined); + return NULL; + } + VariableProxy* proxy = NewUnresolved(local_name, IMPORT); + ImportDeclaration* declaration = + factory()->NewImportDeclaration(proxy, import_name, NULL, scope_, pos); + Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK); + result->Add(declaration, zone()); + if (peek() == Token::RBRACE) break; + Expect(Token::COMMA, CHECK_OK); + } + + Expect(Token::RBRACE, CHECK_OK); + + return result; +} + + +Statement* Parser::ParseImportDeclaration(bool* ok) { + // ImportDeclaration : + // 'import' ImportClause 'from' ModuleSpecifier ';' + // 'import' ModuleSpecifier ';' + // + // ImportClause : + // NameSpaceImport + // NamedImports + // ImportedDefaultBinding + // ImportedDefaultBinding ',' NameSpaceImport + // ImportedDefaultBinding ',' NamedImports + // + // NameSpaceImport : + // '*' 'as' ImportedBinding + + int pos = peek_position(); + Expect(Token::IMPORT, CHECK_OK); + + Token::Value tok = peek(); + + // 'import' ModuleSpecifier ';' + if (tok == Token::STRING) { + const AstRawString* module_specifier = ParseModuleSpecifier(CHECK_OK); + scope_->module()->AddModuleRequest(module_specifier, zone()); + ExpectSemicolon(CHECK_OK); + return factory()->NewEmptyStatement(pos); + } + + // Parse ImportedDefaultBinding if present. + ImportDeclaration* import_default_declaration = NULL; + if (tok != Token::MUL && tok != Token::LBRACE) { + const AstRawString* local_name = + ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK); + VariableProxy* proxy = NewUnresolved(local_name, IMPORT); + import_default_declaration = factory()->NewImportDeclaration( + proxy, ast_value_factory()->default_string(), NULL, scope_, pos); + Declare(import_default_declaration, DeclarationDescriptor::NORMAL, true, + CHECK_OK); + } + + const AstRawString* module_instance_binding = NULL; + ZoneList<ImportDeclaration*>* named_declarations = NULL; + if (import_default_declaration == NULL || Check(Token::COMMA)) { + switch (peek()) { + case Token::MUL: { + Consume(Token::MUL); + ExpectContextualKeyword(CStrVector("as"), CHECK_OK); + module_instance_binding = + ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK); + // TODO(ES6): Add an appropriate declaration. + break; + } + + case Token::LBRACE: + named_declarations = ParseNamedImports(pos, CHECK_OK); + break; + + default: + *ok = false; + ReportUnexpectedToken(scanner()->current_token()); + return NULL; + } + } + + ExpectContextualKeyword(CStrVector("from"), CHECK_OK); + const AstRawString* module_specifier = ParseModuleSpecifier(CHECK_OK); + scope_->module()->AddModuleRequest(module_specifier, zone()); + + if (module_instance_binding != NULL) { + // TODO(ES6): Set the module specifier for the module namespace binding. + } + + if (import_default_declaration != NULL) { + import_default_declaration->set_module_specifier(module_specifier); + } + + if (named_declarations != NULL) { + for (int i = 0; i < named_declarations->length(); ++i) { + named_declarations->at(i)->set_module_specifier(module_specifier); + } + } + + ExpectSemicolon(CHECK_OK); + return factory()->NewEmptyStatement(pos); +} + + +Statement* Parser::ParseExportDefault(bool* ok) { + // Supports the following productions, starting after the 'default' token: + // 'export' 'default' FunctionDeclaration + // 'export' 'default' ClassDeclaration + // 'export' 'default' AssignmentExpression[In] ';' + + Expect(Token::DEFAULT, CHECK_OK); + Scanner::Location default_loc = scanner()->location(); + + ZoneList<const AstRawString*> names(1, zone()); + Statement* result = NULL; + switch (peek()) { + case Token::FUNCTION: + // TODO(ES6): Support parsing anonymous function declarations here. + result = ParseFunctionDeclaration(&names, CHECK_OK); + break; + + case Token::CLASS: + // TODO(ES6): Support parsing anonymous class declarations here. + result = ParseClassDeclaration(&names, CHECK_OK); + break; + + default: { + int pos = peek_position(); + ExpressionClassifier classifier; + Expression* expr = ParseAssignmentExpression(true, &classifier, CHECK_OK); + expr = ParserTraits::RewriteNonPattern(expr, &classifier, CHECK_OK); + + ExpectSemicolon(CHECK_OK); + result = factory()->NewExpressionStatement(expr, pos); + break; + } + } + + const AstRawString* default_string = ast_value_factory()->default_string(); + + DCHECK_LE(names.length(), 1); + if (names.length() == 1) { + scope_->module()->AddLocalExport(default_string, names.first(), zone(), ok); + if (!*ok) { + ParserTraits::ReportMessageAt( + default_loc, MessageTemplate::kDuplicateExport, default_string); + return NULL; + } + } else { + // TODO(ES6): Assign result to a const binding with the name "*default*" + // and add an export entry with "*default*" as the local name. + } + + return result; +} + + +Statement* Parser::ParseExportDeclaration(bool* ok) { + // ExportDeclaration: + // 'export' '*' 'from' ModuleSpecifier ';' + // 'export' ExportClause ('from' ModuleSpecifier)? ';' + // 'export' VariableStatement + // 'export' Declaration + // 'export' 'default' ... (handled in ParseExportDefault) + + int pos = peek_position(); + Expect(Token::EXPORT, CHECK_OK); + + Statement* result = NULL; + ZoneList<const AstRawString*> names(1, zone()); + switch (peek()) { + case Token::DEFAULT: + return ParseExportDefault(ok); + + case Token::MUL: { + Consume(Token::MUL); + ExpectContextualKeyword(CStrVector("from"), CHECK_OK); + const AstRawString* module_specifier = ParseModuleSpecifier(CHECK_OK); + scope_->module()->AddModuleRequest(module_specifier, zone()); + // TODO(ES6): scope_->module()->AddStarExport(...) + ExpectSemicolon(CHECK_OK); + return factory()->NewEmptyStatement(pos); + } + + case Token::LBRACE: { + // There are two cases here: + // + // 'export' ExportClause ';' + // and + // 'export' ExportClause FromClause ';' + // + // In the first case, the exported identifiers in ExportClause must + // not be reserved words, while in the latter they may be. We + // pass in a location that gets filled with the first reserved word + // encountered, and then throw a SyntaxError if we are in the + // non-FromClause case. + Scanner::Location reserved_loc = Scanner::Location::invalid(); + ZoneList<const AstRawString*> export_names(1, zone()); + ZoneList<Scanner::Location> export_locations(1, zone()); + ZoneList<const AstRawString*> local_names(1, zone()); + ParseExportClause(&export_names, &export_locations, &local_names, + &reserved_loc, CHECK_OK); + const AstRawString* indirect_export_module_specifier = NULL; + if (CheckContextualKeyword(CStrVector("from"))) { + indirect_export_module_specifier = ParseModuleSpecifier(CHECK_OK); + } else if (reserved_loc.IsValid()) { + // No FromClause, so reserved words are invalid in ExportClause. + *ok = false; + ReportMessageAt(reserved_loc, MessageTemplate::kUnexpectedReserved); + return NULL; + } + ExpectSemicolon(CHECK_OK); + const int length = export_names.length(); + DCHECK_EQ(length, local_names.length()); + DCHECK_EQ(length, export_locations.length()); + if (indirect_export_module_specifier == NULL) { + for (int i = 0; i < length; ++i) { + scope_->module()->AddLocalExport(export_names[i], local_names[i], + zone(), ok); + if (!*ok) { + ParserTraits::ReportMessageAt(export_locations[i], + MessageTemplate::kDuplicateExport, + export_names[i]); + return NULL; + } + } + } else { + scope_->module()->AddModuleRequest(indirect_export_module_specifier, + zone()); + for (int i = 0; i < length; ++i) { + // TODO(ES6): scope_->module()->AddIndirectExport(...);( + } + } + return factory()->NewEmptyStatement(pos); + } + + case Token::FUNCTION: + result = ParseFunctionDeclaration(&names, CHECK_OK); + break; + + case Token::CLASS: + result = ParseClassDeclaration(&names, CHECK_OK); + break; + + case Token::VAR: + case Token::LET: + case Token::CONST: + result = ParseVariableStatement(kStatementListItem, &names, CHECK_OK); + break; + + default: + *ok = false; + ReportUnexpectedToken(scanner()->current_token()); + return NULL; + } + + // Extract declared names into export declarations. + ModuleDescriptor* descriptor = scope_->module(); + for (int i = 0; i < names.length(); ++i) { + descriptor->AddLocalExport(names[i], names[i], zone(), ok); + if (!*ok) { + // TODO(adamk): Possibly report this error at the right place. + ParserTraits::ReportMessage(MessageTemplate::kDuplicateExport, names[i]); + return NULL; + } + } + + DCHECK_NOT_NULL(result); + return result; +} + + +Statement* Parser::ParseStatement(ZoneList<const AstRawString*>* labels, + bool* ok) { + // Statement :: + // EmptyStatement + // ... + + if (peek() == Token::SEMICOLON) { + Next(); + return factory()->NewEmptyStatement(RelocInfo::kNoPosition); + } + return ParseSubStatement(labels, ok); +} + + +Statement* Parser::ParseSubStatement(ZoneList<const AstRawString*>* 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. + switch (peek()) { + case Token::LBRACE: + return ParseBlock(labels, ok); + + case Token::SEMICOLON: + if (is_strong(language_mode())) { + ReportMessageAt(scanner()->peek_location(), + MessageTemplate::kStrongEmpty); + *ok = false; + return NULL; + } + Next(); + return factory()->NewEmptyStatement(RelocInfo::kNoPosition); + + case Token::IF: + return ParseIfStatement(labels, ok); + + case Token::DO: + return ParseDoWhileStatement(labels, ok); + + case Token::WHILE: + return ParseWhileStatement(labels, ok); + + case Token::FOR: + return ParseForStatement(labels, ok); + + case Token::CONTINUE: + case Token::BREAK: + case Token::RETURN: + case Token::THROW: + case Token::TRY: { + // These statements must have their labels preserved in an enclosing + // block + if (labels == NULL) { + return ParseStatementAsUnlabelled(labels, ok); + } else { + Block* result = + factory()->NewBlock(labels, 1, false, RelocInfo::kNoPosition); + Target target(&this->target_stack_, result); + Statement* statement = ParseStatementAsUnlabelled(labels, CHECK_OK); + if (result) result->statements()->Add(statement, zone()); + return result; + } + } + + case Token::WITH: + return ParseWithStatement(labels, ok); + + case Token::SWITCH: + return ParseSwitchStatement(labels, ok); + + case 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. + // + // In Harmony mode, this case also handles the extension: + // Statement: + // GeneratorDeclaration + if (is_strict(language_mode())) { + ReportMessageAt(scanner()->peek_location(), + MessageTemplate::kStrictFunction); + *ok = false; + return NULL; + } + return ParseFunctionDeclaration(NULL, ok); + } + + case Token::DEBUGGER: + return ParseDebuggerStatement(ok); + + case Token::VAR: + return ParseVariableStatement(kStatement, NULL, ok); + + case Token::CONST: + // In ES6 CONST is not allowed as a Statement, only as a + // LexicalDeclaration, however we continue to allow it in sloppy mode for + // backwards compatibility. + if (is_sloppy(language_mode()) && allow_legacy_const()) { + return ParseVariableStatement(kStatement, NULL, ok); + } + + // Fall through. + default: + return ParseExpressionOrLabelledStatement(labels, ok); + } +} + +Statement* Parser::ParseStatementAsUnlabelled( + ZoneList<const AstRawString*>* labels, bool* ok) { + switch (peek()) { + case Token::CONTINUE: + return ParseContinueStatement(ok); + + case Token::BREAK: + return ParseBreakStatement(labels, ok); + + case Token::RETURN: + return ParseReturnStatement(ok); + + case Token::THROW: + return ParseThrowStatement(ok); + + case Token::TRY: + return ParseTryStatement(ok); + + default: + UNREACHABLE(); + return NULL; + } +} + + +VariableProxy* Parser::NewUnresolved(const AstRawString* name, + VariableMode mode) { + // If we are inside a function, a declaration of a var/const variable is a + // truly local variable, and the scope of the variable is always the function + // scope. + // Let/const variables in harmony mode are always added to the immediately + // enclosing scope. + Scope* scope = + IsLexicalVariableMode(mode) ? scope_ : scope_->DeclarationScope(); + return scope->NewUnresolved(factory(), name, Variable::NORMAL, + scanner()->location().beg_pos, + scanner()->location().end_pos); +} + + +Variable* Parser::Declare(Declaration* declaration, + DeclarationDescriptor::Kind declaration_kind, + bool resolve, bool* ok, Scope* scope) { + VariableProxy* proxy = declaration->proxy(); + DCHECK(proxy->raw_name() != NULL); + const AstRawString* name = proxy->raw_name(); + VariableMode mode = declaration->mode(); + bool is_function_declaration = declaration->IsFunctionDeclaration(); + if (scope == nullptr) scope = scope_; + Scope* declaration_scope = + IsLexicalVariableMode(mode) ? scope : scope->DeclarationScope(); + Variable* var = NULL; + + // If a suitable 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, var declarations inside a sloppy eval scope need + // to be added to the calling function context. Similarly, strict + // mode eval scope and lexical eval bindings do not leak variable + // declarations to the caller's scope so we declare all locals, too. + if (declaration_scope->is_function_scope() || + declaration_scope->is_block_scope() || + declaration_scope->is_module_scope() || + declaration_scope->is_script_scope() || + (declaration_scope->is_eval_scope() && + (is_strict(declaration_scope->language_mode()) || + IsLexicalVariableMode(mode)))) { + // Declare the variable in the declaration scope. + var = declaration_scope->LookupLocal(name); + if (var == NULL) { + // Declare the name. + Variable::Kind kind = Variable::NORMAL; + int declaration_group_start = -1; + if (is_function_declaration) { + kind = Variable::FUNCTION; + } else if (declaration->IsVariableDeclaration() && + declaration->AsVariableDeclaration()->is_class_declaration()) { + kind = Variable::CLASS; + declaration_group_start = + declaration->AsVariableDeclaration()->declaration_group_start(); + } + var = declaration_scope->DeclareLocal( + name, mode, declaration->initialization(), kind, kNotAssigned, + declaration_group_start); + } else if (((IsLexicalVariableMode(mode) || + IsLexicalVariableMode(var->mode())) && + // Allow duplicate function decls for web compat, see bug 4693. + (is_strict(language_mode()) || !is_function_declaration || + !var->is_function())) || + ((mode == CONST_LEGACY || var->mode() == CONST_LEGACY) && + !declaration_scope->is_script_scope())) { + // The name was declared in this scope before; check for conflicting + // re-declarations. We have a conflict if either of the declarations is + // not a var (in script scope, we also have to ignore legacy const for + // compatibility). There is similar code in runtime.cc in the Declare + // functions. The function CheckConflictingVarDeclarations checks for + // var and let bindings from different scopes whereas this is a check for + // conflicting declarations within the same scope. This check also covers + // the special case + // + // function () { let x; { var x; } } + // + // because the var declaration is hoisted to the function scope where 'x' + // is already bound. + DCHECK(IsDeclaredVariableMode(var->mode())); + if (is_strict(language_mode()) || + (allow_harmony_sloppy() && mode != CONST_LEGACY && + var->mode() != CONST_LEGACY)) { + // In harmony we treat re-declarations as early errors. See + // ES5 16 for a definition of early errors. + if (declaration_kind == DeclarationDescriptor::NORMAL) { + ParserTraits::ReportMessage(MessageTemplate::kVarRedeclaration, name); + } else { + ParserTraits::ReportMessage(MessageTemplate::kParamDupe); + } + *ok = false; + return nullptr; + } + Expression* expression = NewThrowSyntaxError( + MessageTemplate::kVarRedeclaration, name, declaration->position()); + declaration_scope->SetIllegalRedeclaration(expression); + } else if (mode == VAR) { + var->set_maybe_assigned(); + } + } else if (declaration_scope->is_eval_scope() && + is_sloppy(declaration_scope->language_mode()) && + !IsLexicalVariableMode(mode)) { + // In a var binding in a sloppy direct eval, pollute the enclosing scope + // with this new binding by doing the following: + // The proxy is bound to a lookup variable to force a dynamic declaration + // using the DeclareLookupSlot runtime function. + Variable::Kind kind = Variable::NORMAL; + // TODO(sigurds) figure out if kNotAssigned is OK here + var = new (zone()) Variable(declaration_scope, name, mode, kind, + declaration->initialization(), kNotAssigned); + var->AllocateTo(VariableLocation::LOOKUP, -1); + var->SetFromEval(); + resolve = true; + } + + + // 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 + // RuntimeHidden_DeclareLookupSlot calls. + declaration_scope->AddDeclaration(declaration); + + if (mode == CONST_LEGACY && declaration_scope->is_script_scope()) { + // For global const variables we bind the proxy to a variable. + DCHECK(resolve); // should be set by all callers + Variable::Kind kind = Variable::NORMAL; + var = new (zone()) Variable(declaration_scope, name, mode, kind, + kNeedsInitialization, kNotAssigned); + } + + // 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 var; +} + + +// 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) { + int pos = peek_position(); + Expect(Token::FUNCTION, CHECK_OK); + // Allow "eval" or "arguments" for backward compatibility. + const AstRawString* name = + ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK); + Expect(Token::LPAREN, CHECK_OK); + bool done = (peek() == Token::RPAREN); + while (!done) { + ParseIdentifier(kAllowRestrictedIdentifiers, 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. + // TODO(adamk): Should this be ClosureScope()? + scope_->DeclarationScope()->ForceEagerCompilation(); + + // TODO(1240846): It's weird that native function declarations are + // introduced dynamically when we meet their declarations, whereas + // other functions are set up when entering the surrounding scope. + VariableProxy* proxy = NewUnresolved(name, VAR); + Declaration* declaration = + factory()->NewVariableDeclaration(proxy, VAR, scope_, pos); + Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK); + NativeFunctionLiteral* lit = factory()->NewNativeFunctionLiteral( + name, extension_, RelocInfo::kNoPosition); + return factory()->NewExpressionStatement( + factory()->NewAssignment(Token::INIT, proxy, lit, RelocInfo::kNoPosition), + pos); +} + + +Statement* Parser::ParseFunctionDeclaration( + ZoneList<const AstRawString*>* names, bool* ok) { + // FunctionDeclaration :: + // 'function' Identifier '(' FormalParameterListopt ')' '{' FunctionBody '}' + // GeneratorDeclaration :: + // 'function' '*' Identifier '(' FormalParameterListopt ')' + // '{' FunctionBody '}' + Expect(Token::FUNCTION, CHECK_OK); + int pos = position(); + bool is_generator = Check(Token::MUL); + bool is_strict_reserved = false; + const AstRawString* name = ParseIdentifierOrStrictReservedWord( + &is_strict_reserved, CHECK_OK); + + FuncNameInferrer::State fni_state(fni_); + if (fni_ != NULL) fni_->PushEnclosingName(name); + FunctionLiteral* fun = ParseFunctionLiteral( + name, scanner()->location(), + is_strict_reserved ? kFunctionNameIsStrictReserved + : kFunctionNameValidityUnknown, + is_generator ? FunctionKind::kGeneratorFunction + : FunctionKind::kNormalFunction, + pos, FunctionLiteral::kDeclaration, FunctionLiteral::kNormalArity, + language_mode(), CHECK_OK); + + // Even if we're not at the top-level of the global or a function + // scope, we treat it as such and introduce the function with its + // initial value upon entering the corresponding scope. + // In ES6, a function behaves as a lexical binding, except in + // a script scope, or the initial scope of eval or another function. + VariableMode mode = + is_strong(language_mode()) + ? CONST + : (is_strict(language_mode()) || allow_harmony_sloppy_function()) && + !scope_->is_declaration_scope() + ? LET + : VAR; + VariableProxy* proxy = NewUnresolved(name, mode); + Declaration* declaration = + factory()->NewFunctionDeclaration(proxy, mode, fun, scope_, pos); + Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK); + if (names) names->Add(name, zone()); + EmptyStatement* empty = factory()->NewEmptyStatement(RelocInfo::kNoPosition); + if (is_sloppy(language_mode()) && allow_harmony_sloppy_function() && + !scope_->is_declaration_scope()) { + SloppyBlockFunctionStatement* delegate = + factory()->NewSloppyBlockFunctionStatement(empty, scope_); + scope_->DeclarationScope()->sloppy_block_function_map()->Declare(name, + delegate); + return delegate; + } + return empty; +} + + +Statement* Parser::ParseClassDeclaration(ZoneList<const AstRawString*>* names, + bool* ok) { + // ClassDeclaration :: + // 'class' Identifier ('extends' LeftHandExpression)? '{' ClassBody '}' + // + // A ClassDeclaration + // + // class C { ... } + // + // has the same semantics as: + // + // let C = class C { ... }; + // + // so rewrite it as such. + + Expect(Token::CLASS, CHECK_OK); + if (!allow_harmony_sloppy() && is_sloppy(language_mode())) { + ReportMessage(MessageTemplate::kSloppyLexical); + *ok = false; + return NULL; + } + + int pos = position(); + bool is_strict_reserved = false; + const AstRawString* name = + ParseIdentifierOrStrictReservedWord(&is_strict_reserved, CHECK_OK); + ClassLiteral* value = ParseClassLiteral(name, scanner()->location(), + is_strict_reserved, pos, CHECK_OK); + + VariableMode mode = is_strong(language_mode()) ? CONST : LET; + VariableProxy* proxy = NewUnresolved(name, mode); + const bool is_class_declaration = true; + Declaration* declaration = factory()->NewVariableDeclaration( + proxy, mode, scope_, pos, is_class_declaration, + scope_->class_declaration_group_start()); + Variable* outer_class_variable = + Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK); + proxy->var()->set_initializer_position(position()); + // This is needed because a class ("class Name { }") creates two bindings (one + // in the outer scope, and one in the class scope). The method is a function + // scope inside the inner scope (class scope). The consecutive class + // declarations are in the outer scope. + if (value->class_variable_proxy() && value->class_variable_proxy()->var() && + outer_class_variable->is_class()) { + // In some cases, the outer variable is not detected as a class variable; + // this happens e.g., for lazy methods. They are excluded from strong mode + // checks for now. TODO(marja, rossberg): re-create variables with the + // correct Kind and remove this hack. + value->class_variable_proxy() + ->var() + ->AsClassVariable() + ->set_declaration_group_start( + outer_class_variable->AsClassVariable()->declaration_group_start()); + } + + Assignment* assignment = + factory()->NewAssignment(Token::INIT, proxy, value, pos); + Statement* assignment_statement = + factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition); + if (names) names->Add(name, zone()); + return assignment_statement; +} + + +Block* Parser::ParseBlock(ZoneList<const AstRawString*>* labels, + bool finalize_block_scope, bool* ok) { + // The harmony mode uses block elements instead of statements. + // + // Block :: + // '{' StatementList '}' + + // Construct block expecting 16 statements. + Block* body = + factory()->NewBlock(labels, 16, false, RelocInfo::kNoPosition); + Scope* block_scope = NewScope(scope_, BLOCK_SCOPE); + + // Parse the statements and collect escaping labels. + Expect(Token::LBRACE, CHECK_OK); + block_scope->set_start_position(scanner()->location().beg_pos); + { BlockState block_state(&scope_, block_scope); + Target target(&this->target_stack_, body); + + while (peek() != Token::RBRACE) { + Statement* stat = ParseStatementListItem(CHECK_OK); + if (stat && !stat->IsEmpty()) { + body->statements()->Add(stat, zone()); + } + } + } + Expect(Token::RBRACE, CHECK_OK); + block_scope->set_end_position(scanner()->location().end_pos); + if (finalize_block_scope) { + block_scope = block_scope->FinalizeBlockScope(); + } + body->set_scope(block_scope); + return body; +} + + +Block* Parser::ParseBlock(ZoneList<const AstRawString*>* labels, bool* ok) { + return ParseBlock(labels, true, ok); +} + + +Block* Parser::DeclarationParsingResult::BuildInitializationBlock( + ZoneList<const AstRawString*>* names, bool* ok) { + Block* result = descriptor.parser->factory()->NewBlock( + NULL, 1, true, descriptor.declaration_pos); + for (auto declaration : declarations) { + PatternRewriter::DeclareAndInitializeVariables( + result, &descriptor, &declaration, names, CHECK_OK); + } + return result; +} + + +Block* Parser::ParseVariableStatement(VariableDeclarationContext var_context, + ZoneList<const AstRawString*>* names, + bool* ok) { + // VariableStatement :: + // VariableDeclarations ';' + + // The scope of a var/const declared variable anywhere inside a function + // is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). Thus we can + // transform a source-level var/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. + + DeclarationParsingResult parsing_result; + ParseVariableDeclarations(var_context, &parsing_result, CHECK_OK); + ExpectSemicolon(CHECK_OK); + + Block* result = parsing_result.BuildInitializationBlock(names, CHECK_OK); + return result; +} + + +void Parser::ParseVariableDeclarations(VariableDeclarationContext var_context, + DeclarationParsingResult* parsing_result, + bool* ok) { + // VariableDeclarations :: + // ('var' | 'const' | 'let') (Identifier ('=' AssignmentExpression)?)+[','] + // + // The ES6 Draft Rev3 specifies the following grammar for const declarations + // + // ConstDeclaration :: + // const ConstBinding (',' ConstBinding)* ';' + // ConstBinding :: + // Identifier '=' AssignmentExpression + // + // TODO(ES6): + // ConstBinding :: + // BindingPattern '=' AssignmentExpression + + parsing_result->descriptor.parser = this; + parsing_result->descriptor.declaration_kind = DeclarationDescriptor::NORMAL; + parsing_result->descriptor.declaration_pos = peek_position(); + parsing_result->descriptor.initialization_pos = peek_position(); + parsing_result->descriptor.mode = VAR; + // True if the binding needs initialization. 'let' and 'const' declared + // bindings are created uninitialized by their declaration nodes and + // need initialization. 'var' declared bindings are always initialized + // immediately by their declaration nodes. + parsing_result->descriptor.needs_init = false; + if (peek() == Token::VAR) { + if (is_strong(language_mode())) { + Scanner::Location location = scanner()->peek_location(); + ReportMessageAt(location, MessageTemplate::kStrongVar); + *ok = false; + return; + } + Consume(Token::VAR); + } else if (peek() == Token::CONST && allow_const()) { + Consume(Token::CONST); + if (is_sloppy(language_mode()) && allow_legacy_const()) { + parsing_result->descriptor.mode = CONST_LEGACY; + ++use_counts_[v8::Isolate::kLegacyConst]; + } else { + DCHECK(is_strict(language_mode()) || allow_harmony_sloppy()); + DCHECK(var_context != kStatement); + parsing_result->descriptor.mode = CONST; + } + parsing_result->descriptor.needs_init = true; + } else if (peek() == Token::LET && allow_let()) { + Consume(Token::LET); + DCHECK(var_context != kStatement); + parsing_result->descriptor.mode = LET; + parsing_result->descriptor.needs_init = true; + } else { + UNREACHABLE(); // by current callers + } + + parsing_result->descriptor.scope = scope_; + parsing_result->descriptor.hoist_scope = nullptr; + + + bool first_declaration = true; + int bindings_start = peek_position(); + bool is_for_iteration_variable; + do { + FuncNameInferrer::State fni_state(fni_); + + // Parse name. + if (!first_declaration) Consume(Token::COMMA); + + Expression* pattern; + int decl_pos = peek_position(); + { + ExpressionClassifier pattern_classifier; + Token::Value next = peek(); + pattern = ParsePrimaryExpression(&pattern_classifier, ok); + if (!*ok) return; + ValidateBindingPattern(&pattern_classifier, ok); + if (!*ok) return; + if (IsLexicalVariableMode(parsing_result->descriptor.mode)) { + ValidateLetPattern(&pattern_classifier, ok); + if (!*ok) return; + } + if (!allow_harmony_destructuring_bind() && !pattern->IsVariableProxy()) { + ReportUnexpectedToken(next); + *ok = false; + return; + } + } + + bool is_pattern = + (pattern->IsObjectLiteral() || pattern->IsArrayLiteral()) && + !pattern->is_parenthesized(); + + Scanner::Location variable_loc = scanner()->location(); + const AstRawString* single_name = + pattern->IsVariableProxy() ? pattern->AsVariableProxy()->raw_name() + : nullptr; + if (single_name != nullptr) { + if (fni_ != NULL) fni_->PushVariableName(single_name); + } + + is_for_iteration_variable = + var_context == kForStatement && + (peek() == Token::IN || PeekContextualKeyword(CStrVector("of"))); + if (is_for_iteration_variable && + (parsing_result->descriptor.mode == CONST || + parsing_result->descriptor.mode == CONST_LEGACY)) { + parsing_result->descriptor.needs_init = false; + } + + Expression* value = NULL; + // Harmony consts have non-optional initializers. + int initializer_position = RelocInfo::kNoPosition; + if (Check(Token::ASSIGN)) { + ExpressionClassifier classifier; + value = ParseAssignmentExpression(var_context != kForStatement, + &classifier, ok); + if (!*ok) return; + value = ParserTraits::RewriteNonPattern(value, &classifier, ok); + if (!*ok) return; + variable_loc.end_pos = scanner()->location().end_pos; + + if (!parsing_result->first_initializer_loc.IsValid()) { + parsing_result->first_initializer_loc = variable_loc; + } + + // Don't infer if it is "a = function(){...}();"-like expression. + if (single_name) { + if (fni_ != NULL && value->AsCall() == NULL && + value->AsCallNew() == NULL) { + fni_->Infer(); + } else { + fni_->RemoveLastFunction(); + } + } + + if (allow_harmony_function_name() && single_name) { + if (value->IsFunctionLiteral()) { + auto function_literal = value->AsFunctionLiteral(); + if (function_literal->is_anonymous()) { + function_literal->set_raw_name(single_name); + } + } else if (value->IsClassLiteral()) { + auto class_literal = value->AsClassLiteral(); + if (class_literal->raw_name() == nullptr) { + class_literal->set_raw_name(single_name); + } + } + } + + // End position of the initializer is after the assignment expression. + initializer_position = scanner()->location().end_pos; + } else { + if ((parsing_result->descriptor.mode == CONST || is_pattern) && + !is_for_iteration_variable) { + ParserTraits::ReportMessageAt( + Scanner::Location(decl_pos, scanner()->location().end_pos), + MessageTemplate::kDeclarationMissingInitializer, + is_pattern ? "destructuring" : "const"); + *ok = false; + return; + } + // End position of the initializer is after the variable. + initializer_position = position(); + } + + // Make sure that 'const x' and 'let x' initialize 'x' to undefined. + if (value == NULL && parsing_result->descriptor.needs_init) { + value = GetLiteralUndefined(position()); + } + + parsing_result->declarations.Add(DeclarationParsingResult::Declaration( + pattern, initializer_position, value)); + first_declaration = false; + } while (peek() == Token::COMMA); + + parsing_result->bindings_loc = + Scanner::Location(bindings_start, scanner()->location().end_pos); +} + + +static bool ContainsLabel(ZoneList<const AstRawString*>* labels, + const AstRawString* label) { + DCHECK(label != NULL); + if (labels != NULL) { + for (int i = labels->length(); i-- > 0; ) { + if (labels->at(i) == label) { + return true; + } + } + } + return false; +} + + +Statement* Parser::ParseExpressionOrLabelledStatement( + ZoneList<const AstRawString*>* labels, bool* ok) { + // ExpressionStatement | LabelledStatement :: + // Expression ';' + // Identifier ':' Statement + // + // ExpressionStatement[Yield] : + // [lookahead ā {{, function, class, let [}] Expression[In, ?Yield] ; + + int pos = peek_position(); + + switch (peek()) { + case Token::FUNCTION: + case Token::LBRACE: + UNREACHABLE(); // Always handled by the callers. + case Token::CLASS: + ReportUnexpectedToken(Next()); + *ok = false; + return nullptr; + + case Token::THIS: + if (!FLAG_strong_this) break; + // Fall through. + case Token::SUPER: + if (is_strong(language_mode()) && + IsClassConstructor(function_state_->kind())) { + bool is_this = peek() == Token::THIS; + Expression* expr; + ExpressionClassifier classifier; + if (is_this) { + expr = ParseStrongInitializationExpression(&classifier, CHECK_OK); + } else { + expr = ParseStrongSuperCallExpression(&classifier, CHECK_OK); + } + expr = ParserTraits::RewriteNonPattern(expr, &classifier, CHECK_OK); + switch (peek()) { + case Token::SEMICOLON: + Consume(Token::SEMICOLON); + break; + case Token::RBRACE: + case Token::EOS: + break; + default: + if (!scanner()->HasAnyLineTerminatorBeforeNext()) { + ReportMessageAt(function_state_->this_location(), + is_this + ? MessageTemplate::kStrongConstructorThis + : MessageTemplate::kStrongConstructorSuper); + *ok = false; + return nullptr; + } + } + return factory()->NewExpressionStatement(expr, pos); + } + break; + + default: + break; + } + + bool starts_with_idenfifier = peek_any_identifier(); + Expression* expr = ParseExpression(true, CHECK_OK); + if (peek() == Token::COLON && starts_with_idenfifier && expr != NULL && + expr->AsVariableProxy() != NULL && + !expr->AsVariableProxy()->is_this()) { + // Expression is a single identifier, and not, e.g., a parenthesized + // identifier. + VariableProxy* var = expr->AsVariableProxy(); + const AstRawString* label = var->raw_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)) { + ParserTraits::ReportMessage(MessageTemplate::kLabelRedeclaration, label); + *ok = false; + return NULL; + } + if (labels == NULL) { + labels = new(zone()) ZoneList<const AstRawString*>(4, zone()); + } + labels->Add(label, zone()); + // 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. + scope_->RemoveUnresolved(var); + Expect(Token::COLON, CHECK_OK); + return ParseStatement(labels, ok); + } + + // If we have an extension, we allow a native function declaration. + // A native function declaration starts with "native function" with + // no line-terminator between the two words. + if (extension_ != NULL && peek() == Token::FUNCTION && + !scanner()->HasAnyLineTerminatorBeforeNext() && expr != NULL && + expr->AsVariableProxy() != NULL && + expr->AsVariableProxy()->raw_name() == + ast_value_factory()->native_string() && + !scanner()->literal_contains_escapes()) { + return ParseNativeDeclaration(ok); + } + + // Parsed expression statement, followed by semicolon. + // Detect attempts at 'let' declarations in sloppy mode. + if (!allow_harmony_sloppy_let() && peek() == Token::IDENTIFIER && + expr->AsVariableProxy() != NULL && + expr->AsVariableProxy()->raw_name() == + ast_value_factory()->let_string()) { + ReportMessage(MessageTemplate::kSloppyLexical, NULL); + *ok = false; + return NULL; + } + ExpectSemicolon(CHECK_OK); + return factory()->NewExpressionStatement(expr, pos); +} + + +IfStatement* Parser::ParseIfStatement(ZoneList<const AstRawString*>* labels, + bool* ok) { + // IfStatement :: + // 'if' '(' Expression ')' Statement ('else' Statement)? + + int pos = peek_position(); + Expect(Token::IF, CHECK_OK); + Expect(Token::LPAREN, CHECK_OK); + Expression* condition = ParseExpression(true, CHECK_OK); + Expect(Token::RPAREN, CHECK_OK); + Statement* then_statement = ParseSubStatement(labels, CHECK_OK); + Statement* else_statement = NULL; + if (peek() == Token::ELSE) { + Next(); + else_statement = ParseSubStatement(labels, CHECK_OK); + } else { + else_statement = factory()->NewEmptyStatement(RelocInfo::kNoPosition); + } + return factory()->NewIfStatement( + condition, then_statement, else_statement, pos); +} + + +Statement* Parser::ParseContinueStatement(bool* ok) { + // ContinueStatement :: + // 'continue' Identifier? ';' + + int pos = peek_position(); + Expect(Token::CONTINUE, CHECK_OK); + const AstRawString* label = NULL; + Token::Value tok = peek(); + if (!scanner()->HasAnyLineTerminatorBeforeNext() && + tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) { + // ECMA allows "eval" or "arguments" as labels even in strict mode. + label = ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK); + } + IterationStatement* target = LookupContinueTarget(label, CHECK_OK); + if (target == NULL) { + // Illegal continue statement. + MessageTemplate::Template message = MessageTemplate::kIllegalContinue; + if (label != NULL) { + message = MessageTemplate::kUnknownLabel; + } + ParserTraits::ReportMessage(message, label); + *ok = false; + return NULL; + } + ExpectSemicolon(CHECK_OK); + return factory()->NewContinueStatement(target, pos); +} + + +Statement* Parser::ParseBreakStatement(ZoneList<const AstRawString*>* labels, + bool* ok) { + // BreakStatement :: + // 'break' Identifier? ';' + + int pos = peek_position(); + Expect(Token::BREAK, CHECK_OK); + const AstRawString* label = NULL; + Token::Value tok = peek(); + if (!scanner()->HasAnyLineTerminatorBeforeNext() && + tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) { + // ECMA allows "eval" or "arguments" as labels even in strict mode. + label = ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK); + } + // Parse labeled break statements that target themselves into + // empty statements, e.g. 'l1: l2: l3: break l2;' + if (label != NULL && ContainsLabel(labels, label)) { + ExpectSemicolon(CHECK_OK); + return factory()->NewEmptyStatement(pos); + } + BreakableStatement* target = NULL; + target = LookupBreakTarget(label, CHECK_OK); + if (target == NULL) { + // Illegal break statement. + MessageTemplate::Template message = MessageTemplate::kIllegalBreak; + if (label != NULL) { + message = MessageTemplate::kUnknownLabel; + } + ParserTraits::ReportMessage(message, label); + *ok = false; + return NULL; + } + ExpectSemicolon(CHECK_OK); + return factory()->NewBreakStatement(target, pos); +} + + +Statement* Parser::ParseReturnStatement(bool* ok) { + // ReturnStatement :: + // 'return' Expression? ';' + + // Consume the return token. It is necessary to do that before + // reporting any errors on it, because of the way errors are + // reported (underlining). + Expect(Token::RETURN, CHECK_OK); + Scanner::Location loc = scanner()->location(); + function_state_->set_return_location(loc); + + Token::Value tok = peek(); + Statement* result; + Expression* return_value; + if (scanner()->HasAnyLineTerminatorBeforeNext() || + tok == Token::SEMICOLON || + tok == Token::RBRACE || + tok == Token::EOS) { + if (IsSubclassConstructor(function_state_->kind())) { + return_value = ThisExpression(scope_, factory(), loc.beg_pos); + } else { + return_value = GetLiteralUndefined(position()); + } + } else { + if (is_strong(language_mode()) && + IsClassConstructor(function_state_->kind())) { + int pos = peek_position(); + ReportMessageAt(Scanner::Location(pos, pos + 1), + MessageTemplate::kStrongConstructorReturnValue); + *ok = false; + return NULL; + } + + int pos = peek_position(); + return_value = ParseExpression(true, CHECK_OK); + + if (IsSubclassConstructor(function_state_->kind())) { + // For subclass constructors we need to return this in case of undefined + // and throw an exception in case of a non object. + // + // return expr; + // + // Is rewritten as: + // + // return (temp = expr) === undefined ? this : + // %_IsJSReceiver(temp) ? temp : throw new TypeError(...); + Variable* temp = scope_->NewTemporary( + ast_value_factory()->empty_string()); + Assignment* assign = factory()->NewAssignment( + Token::ASSIGN, factory()->NewVariableProxy(temp), return_value, pos); + + Expression* throw_expression = + NewThrowTypeError(MessageTemplate::kDerivedConstructorReturn, + ast_value_factory()->empty_string(), pos); + + // %_IsJSReceiver(temp) + ZoneList<Expression*>* is_spec_object_args = + new (zone()) ZoneList<Expression*>(1, zone()); + is_spec_object_args->Add(factory()->NewVariableProxy(temp), zone()); + Expression* is_spec_object_call = factory()->NewCallRuntime( + Runtime::kInlineIsJSReceiver, is_spec_object_args, pos); + + // %_IsJSReceiver(temp) ? temp : throw_expression + Expression* is_object_conditional = factory()->NewConditional( + is_spec_object_call, factory()->NewVariableProxy(temp), + throw_expression, pos); + + // temp === undefined + Expression* is_undefined = factory()->NewCompareOperation( + Token::EQ_STRICT, assign, + factory()->NewUndefinedLiteral(RelocInfo::kNoPosition), pos); + + // is_undefined ? this : is_object_conditional + return_value = factory()->NewConditional( + is_undefined, ThisExpression(scope_, factory(), pos), + is_object_conditional, pos); + } + + return_value->MarkTail(); + } + ExpectSemicolon(CHECK_OK); + + if (is_generator()) { + Expression* generator = factory()->NewVariableProxy( + function_state_->generator_object_variable()); + Expression* yield = factory()->NewYield( + generator, return_value, Yield::kFinal, loc.beg_pos); + result = factory()->NewExpressionStatement(yield, loc.beg_pos); + } else { + result = factory()->NewReturnStatement(return_value, loc.beg_pos); + } + + Scope* decl_scope = scope_->DeclarationScope(); + if (decl_scope->is_script_scope() || decl_scope->is_eval_scope()) { + ReportMessageAt(loc, MessageTemplate::kIllegalReturn); + *ok = false; + return NULL; + } + return result; +} + + +Statement* Parser::ParseWithStatement(ZoneList<const AstRawString*>* labels, + bool* ok) { + // WithStatement :: + // 'with' '(' Expression ')' Statement + + Expect(Token::WITH, CHECK_OK); + int pos = position(); + + if (is_strict(language_mode())) { + ReportMessage(MessageTemplate::kStrictWith); + *ok = false; + return NULL; + } + + Expect(Token::LPAREN, CHECK_OK); + Expression* expr = ParseExpression(true, CHECK_OK); + Expect(Token::RPAREN, CHECK_OK); + + scope_->DeclarationScope()->RecordWithStatement(); + Scope* with_scope = NewScope(scope_, WITH_SCOPE); + Block* body; + { BlockState block_state(&scope_, with_scope); + with_scope->set_start_position(scanner()->peek_location().beg_pos); + + // The body of the with statement must be enclosed in an additional + // lexical scope in case the body is a FunctionDeclaration. + body = factory()->NewBlock(labels, 1, false, RelocInfo::kNoPosition); + Scope* block_scope = NewScope(scope_, BLOCK_SCOPE); + block_scope->set_start_position(scanner()->location().beg_pos); + { + BlockState block_state(&scope_, block_scope); + Target target(&this->target_stack_, body); + Statement* stmt = ParseSubStatement(labels, CHECK_OK); + body->statements()->Add(stmt, zone()); + block_scope->set_end_position(scanner()->location().end_pos); + block_scope = block_scope->FinalizeBlockScope(); + body->set_scope(block_scope); + } + + with_scope->set_end_position(scanner()->location().end_pos); + } + return factory()->NewWithStatement(with_scope, expr, body, pos); +} + + +CaseClause* Parser::ParseCaseClause(bool* default_seen_ptr, bool* ok) { + // CaseClause :: + // 'case' Expression ':' StatementList + // 'default' ':' StatementList + + 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(MessageTemplate::kMultipleDefaultsInSwitch); + *ok = false; + return NULL; + } + *default_seen_ptr = true; + } + Expect(Token::COLON, CHECK_OK); + int pos = position(); + ZoneList<Statement*>* statements = + new(zone()) ZoneList<Statement*>(5, zone()); + Statement* stat = NULL; + while (peek() != Token::CASE && + peek() != Token::DEFAULT && + peek() != Token::RBRACE) { + stat = ParseStatementListItem(CHECK_OK); + statements->Add(stat, zone()); + } + if (is_strong(language_mode()) && stat != NULL && !stat->IsJump() && + peek() != Token::RBRACE) { + ReportMessageAt(scanner()->location(), + MessageTemplate::kStrongSwitchFallthrough); + *ok = false; + return NULL; + } + return factory()->NewCaseClause(label, statements, pos); +} + + +Statement* Parser::ParseSwitchStatement(ZoneList<const AstRawString*>* labels, + bool* ok) { + // SwitchStatement :: + // 'switch' '(' Expression ')' '{' CaseClause* '}' + // In order to get the CaseClauses to execute in their own lexical scope, + // but without requiring downstream code to have special scope handling + // code for switch statements, desugar into blocks as follows: + // { // To group the statements--harmless to evaluate Expression in scope + // .tag_variable = Expression; + // { // To give CaseClauses a scope + // switch (.tag_variable) { CaseClause* } + // } + // } + + Block* switch_block = + factory()->NewBlock(NULL, 2, false, RelocInfo::kNoPosition); + int switch_pos = peek_position(); + + Expect(Token::SWITCH, CHECK_OK); + Expect(Token::LPAREN, CHECK_OK); + Expression* tag = ParseExpression(true, CHECK_OK); + Expect(Token::RPAREN, CHECK_OK); + + Variable* tag_variable = + scope_->NewTemporary(ast_value_factory()->dot_switch_tag_string()); + Assignment* tag_assign = factory()->NewAssignment( + Token::ASSIGN, factory()->NewVariableProxy(tag_variable), tag, + tag->position()); + Statement* tag_statement = + factory()->NewExpressionStatement(tag_assign, RelocInfo::kNoPosition); + switch_block->statements()->Add(tag_statement, zone()); + + // make statement: undefined; + // This is needed so the tag isn't returned as the value, in case the switch + // statements don't have a value. + switch_block->statements()->Add( + factory()->NewExpressionStatement( + factory()->NewUndefinedLiteral(RelocInfo::kNoPosition), + RelocInfo::kNoPosition), + zone()); + + Block* cases_block = + factory()->NewBlock(NULL, 1, false, RelocInfo::kNoPosition); + Scope* cases_scope = NewScope(scope_, BLOCK_SCOPE); + cases_scope->SetNonlinear(); + + SwitchStatement* switch_statement = + factory()->NewSwitchStatement(labels, switch_pos); + + cases_scope->set_start_position(scanner()->location().beg_pos); + { + BlockState cases_block_state(&scope_, cases_scope); + Target target(&this->target_stack_, switch_statement); + + Expression* tag_read = factory()->NewVariableProxy(tag_variable); + + bool default_seen = false; + ZoneList<CaseClause*>* cases = + new (zone()) ZoneList<CaseClause*>(4, zone()); + Expect(Token::LBRACE, CHECK_OK); + while (peek() != Token::RBRACE) { + CaseClause* clause = ParseCaseClause(&default_seen, CHECK_OK); + cases->Add(clause, zone()); + } + switch_statement->Initialize(tag_read, cases); + cases_block->statements()->Add(switch_statement, zone()); + } + Expect(Token::RBRACE, CHECK_OK); + + cases_scope->set_end_position(scanner()->location().end_pos); + cases_scope = cases_scope->FinalizeBlockScope(); + cases_block->set_scope(cases_scope); + + switch_block->statements()->Add(cases_block, zone()); + + return switch_block; +} + + +Statement* Parser::ParseThrowStatement(bool* ok) { + // ThrowStatement :: + // 'throw' Expression ';' + + Expect(Token::THROW, CHECK_OK); + int pos = position(); + if (scanner()->HasAnyLineTerminatorBeforeNext()) { + ReportMessage(MessageTemplate::kNewlineAfterThrow); + *ok = false; + return NULL; + } + Expression* exception = ParseExpression(true, CHECK_OK); + ExpectSemicolon(CHECK_OK); + + return factory()->NewExpressionStatement( + factory()->NewThrow(exception, pos), 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); + int pos = position(); + + Block* try_block = ParseBlock(NULL, CHECK_OK); + + Token::Value tok = peek(); + if (tok != Token::CATCH && tok != Token::FINALLY) { + ReportMessage(MessageTemplate::kNoCatchOrFinally); + *ok = false; + return NULL; + } + + Scope* catch_scope = NULL; + Variable* catch_variable = NULL; + Block* catch_block = NULL; + if (tok == Token::CATCH) { + Consume(Token::CATCH); + + Expect(Token::LPAREN, CHECK_OK); + catch_scope = NewScope(scope_, CATCH_SCOPE); + catch_scope->set_start_position(scanner()->location().beg_pos); + + ExpressionClassifier pattern_classifier; + Expression* pattern = ParsePrimaryExpression(&pattern_classifier, CHECK_OK); + ValidateBindingPattern(&pattern_classifier, CHECK_OK); + + const AstRawString* name = ast_value_factory()->dot_catch_string(); + bool is_simple = pattern->IsVariableProxy(); + if (is_simple) { + auto proxy = pattern->AsVariableProxy(); + scope_->RemoveUnresolved(proxy); + name = proxy->raw_name(); + } + + catch_variable = catch_scope->DeclareLocal(name, VAR, kCreatedInitialized, + Variable::NORMAL); + + Expect(Token::RPAREN, CHECK_OK); + + { + BlockState block_state(&scope_, catch_scope); + + // TODO(adamk): Make a version of ParseBlock that takes a scope and + // a block. + catch_block = + factory()->NewBlock(nullptr, 16, false, RelocInfo::kNoPosition); + Scope* block_scope = NewScope(scope_, BLOCK_SCOPE); + + block_scope->set_start_position(scanner()->location().beg_pos); + { + BlockState block_state(&scope_, block_scope); + Target target(&this->target_stack_, catch_block); + + if (!is_simple) { + DeclarationDescriptor descriptor; + descriptor.declaration_kind = DeclarationDescriptor::NORMAL; + descriptor.parser = this; + descriptor.scope = scope_; + descriptor.hoist_scope = nullptr; + descriptor.mode = LET; + descriptor.needs_init = true; + descriptor.declaration_pos = pattern->position(); + descriptor.initialization_pos = pattern->position(); + + DeclarationParsingResult::Declaration decl( + pattern, pattern->position(), + factory()->NewVariableProxy(catch_variable)); + + PatternRewriter::DeclareAndInitializeVariables( + catch_block, &descriptor, &decl, nullptr, CHECK_OK); + } + + Expect(Token::LBRACE, CHECK_OK); + while (peek() != Token::RBRACE) { + Statement* stat = ParseStatementListItem(CHECK_OK); + if (stat && !stat->IsEmpty()) { + catch_block->statements()->Add(stat, zone()); + } + } + Consume(Token::RBRACE); + } + block_scope->set_end_position(scanner()->location().end_pos); + block_scope = block_scope->FinalizeBlockScope(); + catch_block->set_scope(block_scope); + } + + catch_scope->set_end_position(scanner()->location().end_pos); + tok = peek(); + } + + Block* finally_block = NULL; + DCHECK(tok == Token::FINALLY || catch_block != NULL); + if (tok == Token::FINALLY) { + Consume(Token::FINALLY); + finally_block = ParseBlock(NULL, CHECK_OK); + } + + // Simplify the AST nodes by converting: + // 'try B0 catch B1 finally B2' + // to: + // 'try { try B0 catch B1 } finally B2' + + if (catch_block != NULL && finally_block != NULL) { + // If we have both, create an inner try/catch. + DCHECK(catch_scope != NULL && catch_variable != NULL); + TryCatchStatement* statement = + factory()->NewTryCatchStatement(try_block, catch_scope, catch_variable, + catch_block, RelocInfo::kNoPosition); + try_block = factory()->NewBlock(NULL, 1, false, RelocInfo::kNoPosition); + try_block->statements()->Add(statement, zone()); + catch_block = NULL; // Clear to indicate it's been handled. + } + + TryStatement* result = NULL; + if (catch_block != NULL) { + DCHECK(finally_block == NULL); + DCHECK(catch_scope != NULL && catch_variable != NULL); + result = factory()->NewTryCatchStatement(try_block, catch_scope, + catch_variable, catch_block, pos); + } else { + DCHECK(finally_block != NULL); + result = factory()->NewTryFinallyStatement(try_block, finally_block, pos); + } + + return result; +} + + +DoWhileStatement* Parser::ParseDoWhileStatement( + ZoneList<const AstRawString*>* labels, bool* ok) { + // DoStatement :: + // 'do' Statement 'while' '(' Expression ')' ';' + + DoWhileStatement* loop = + factory()->NewDoWhileStatement(labels, peek_position()); + Target target(&this->target_stack_, loop); + + Expect(Token::DO, CHECK_OK); + Statement* body = ParseSubStatement(NULL, CHECK_OK); + Expect(Token::WHILE, CHECK_OK); + Expect(Token::LPAREN, CHECK_OK); + + Expression* cond = ParseExpression(true, CHECK_OK); + 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( + ZoneList<const AstRawString*>* labels, bool* ok) { + // WhileStatement :: + // 'while' '(' Expression ')' Statement + + WhileStatement* loop = factory()->NewWhileStatement(labels, peek_position()); + Target target(&this->target_stack_, loop); + + Expect(Token::WHILE, CHECK_OK); + Expect(Token::LPAREN, CHECK_OK); + Expression* cond = ParseExpression(true, CHECK_OK); + Expect(Token::RPAREN, CHECK_OK); + Statement* body = ParseSubStatement(NULL, CHECK_OK); + + if (loop != NULL) loop->Initialize(cond, body); + return loop; +} + + +// !%_IsJSReceiver(result = iterator.next()) && +// %ThrowIteratorResultNotAnObject(result) +Expression* Parser::BuildIteratorNextResult(Expression* iterator, + Variable* result, int pos) { + Expression* next_literal = factory()->NewStringLiteral( + ast_value_factory()->next_string(), RelocInfo::kNoPosition); + Expression* next_property = + factory()->NewProperty(iterator, next_literal, RelocInfo::kNoPosition); + ZoneList<Expression*>* next_arguments = + new (zone()) ZoneList<Expression*>(0, zone()); + Expression* next_call = + factory()->NewCall(next_property, next_arguments, pos); + Expression* result_proxy = factory()->NewVariableProxy(result); + Expression* left = + factory()->NewAssignment(Token::ASSIGN, result_proxy, next_call, pos); + + // %_IsJSReceiver(...) + ZoneList<Expression*>* is_spec_object_args = + new (zone()) ZoneList<Expression*>(1, zone()); + is_spec_object_args->Add(left, zone()); + Expression* is_spec_object_call = factory()->NewCallRuntime( + Runtime::kInlineIsJSReceiver, is_spec_object_args, pos); + + // %ThrowIteratorResultNotAnObject(result) + Expression* result_proxy_again = factory()->NewVariableProxy(result); + ZoneList<Expression*>* throw_arguments = + new (zone()) ZoneList<Expression*>(1, zone()); + throw_arguments->Add(result_proxy_again, zone()); + Expression* throw_call = factory()->NewCallRuntime( + Runtime::kThrowIteratorResultNotAnObject, throw_arguments, pos); + + return factory()->NewBinaryOperation( + Token::AND, + factory()->NewUnaryOperation(Token::NOT, is_spec_object_call, pos), + throw_call, pos); +} + + +void Parser::InitializeForEachStatement(ForEachStatement* stmt, + Expression* each, Expression* subject, + Statement* body, + bool is_destructuring) { + DCHECK(!is_destructuring || allow_harmony_destructuring_assignment()); + ForOfStatement* for_of = stmt->AsForOfStatement(); + + if (for_of != NULL) { + Variable* iterator = scope_->NewTemporary( + ast_value_factory()->dot_iterator_string()); + Variable* result = scope_->NewTemporary( + ast_value_factory()->dot_result_string()); + + Expression* assign_iterator; + Expression* next_result; + Expression* result_done; + Expression* assign_each; + + // iterator = subject[Symbol.iterator]() + // Hackily disambiguate o from o.next and o [Symbol.iterator](). + // TODO(verwaest): Come up with a better solution. + assign_iterator = factory()->NewAssignment( + Token::ASSIGN, factory()->NewVariableProxy(iterator), + GetIterator(subject, factory(), subject->position() - 2), + subject->position()); + + // !%_IsJSReceiver(result = iterator.next()) && + // %ThrowIteratorResultNotAnObject(result) + { + // result = iterator.next() + Expression* iterator_proxy = factory()->NewVariableProxy(iterator); + // Hackily disambiguate o from o.next and o [Symbol.iterator](). + // TODO(verwaest): Come up with a better solution. + next_result = BuildIteratorNextResult(iterator_proxy, result, + subject->position() - 1); + } + + // result.done + { + Expression* done_literal = factory()->NewStringLiteral( + ast_value_factory()->done_string(), RelocInfo::kNoPosition); + Expression* result_proxy = factory()->NewVariableProxy(result); + result_done = factory()->NewProperty( + result_proxy, done_literal, RelocInfo::kNoPosition); + } + + // each = result.value + { + Expression* value_literal = factory()->NewStringLiteral( + ast_value_factory()->value_string(), RelocInfo::kNoPosition); + Expression* result_proxy = factory()->NewVariableProxy(result); + Expression* result_value = factory()->NewProperty( + result_proxy, value_literal, RelocInfo::kNoPosition); + assign_each = factory()->NewAssignment(Token::ASSIGN, each, result_value, + RelocInfo::kNoPosition); + if (is_destructuring) { + assign_each = PatternRewriter::RewriteDestructuringAssignment( + this, assign_each->AsAssignment(), scope_); + } + } + + for_of->Initialize(each, subject, body, + assign_iterator, + next_result, + result_done, + assign_each); + } else { + if (is_destructuring) { + Variable* temp = + scope_->NewTemporary(ast_value_factory()->empty_string()); + VariableProxy* temp_proxy = factory()->NewVariableProxy(temp); + Expression* assign_each = PatternRewriter::RewriteDestructuringAssignment( + this, factory()->NewAssignment(Token::ASSIGN, each, temp_proxy, + RelocInfo::kNoPosition), + scope_); + auto block = + factory()->NewBlock(nullptr, 2, false, RelocInfo::kNoPosition); + block->statements()->Add(factory()->NewExpressionStatement( + assign_each, RelocInfo::kNoPosition), + zone()); + block->statements()->Add(body, zone()); + body = block; + each = factory()->NewVariableProxy(temp); + } + stmt->Initialize(each, subject, body); + } +} + + +Statement* Parser::DesugarLexicalBindingsInForStatement( + Scope* inner_scope, bool is_const, ZoneList<const AstRawString*>* names, + ForStatement* loop, Statement* init, Expression* cond, Statement* next, + Statement* body, bool* ok) { + // ES6 13.7.4.8 specifies that on each loop iteration the let variables are + // copied into a new environment. Moreover, the "next" statement must be + // evaluated not in the environment of the just completed iteration but in + // that of the upcoming one. We achieve this with the following desugaring. + // Extra care is needed to preserve the completion value of the original loop. + // + // We are given a for statement of the form + // + // labels: for (let/const x = i; cond; next) body + // + // and rewrite it as follows. Here we write {{ ... }} for init-blocks, ie., + // blocks whose ignore_completion_value_ flag is set. + // + // { + // let/const x = i; + // temp_x = x; + // first = 1; + // undefined; + // outer: for (;;) { + // let/const x = temp_x; + // {{ if (first == 1) { + // first = 0; + // } else { + // next; + // } + // flag = 1; + // if (!cond) break; + // }} + // labels: for (; flag == 1; flag = 0, temp_x = x) { + // body + // } + // {{ if (flag == 1) // Body used break. + // break; + // }} + // } + // } + + DCHECK(names->length() > 0); + Scope* for_scope = scope_; + ZoneList<Variable*> temps(names->length(), zone()); + + Block* outer_block = factory()->NewBlock(NULL, names->length() + 4, false, + RelocInfo::kNoPosition); + + // Add statement: let/const x = i. + outer_block->statements()->Add(init, zone()); + + const AstRawString* temp_name = ast_value_factory()->dot_for_string(); + + // For each lexical variable x: + // make statement: temp_x = x. + for (int i = 0; i < names->length(); i++) { + VariableProxy* proxy = NewUnresolved(names->at(i), LET); + Variable* temp = scope_->NewTemporary(temp_name); + VariableProxy* temp_proxy = factory()->NewVariableProxy(temp); + Assignment* assignment = factory()->NewAssignment( + Token::ASSIGN, temp_proxy, proxy, RelocInfo::kNoPosition); + Statement* assignment_statement = factory()->NewExpressionStatement( + assignment, RelocInfo::kNoPosition); + outer_block->statements()->Add(assignment_statement, zone()); + temps.Add(temp, zone()); + } + + Variable* first = NULL; + // Make statement: first = 1. + if (next) { + first = scope_->NewTemporary(temp_name); + VariableProxy* first_proxy = factory()->NewVariableProxy(first); + Expression* const1 = factory()->NewSmiLiteral(1, RelocInfo::kNoPosition); + Assignment* assignment = factory()->NewAssignment( + Token::ASSIGN, first_proxy, const1, RelocInfo::kNoPosition); + Statement* assignment_statement = + factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition); + outer_block->statements()->Add(assignment_statement, zone()); + } + + // make statement: undefined; + outer_block->statements()->Add( + factory()->NewExpressionStatement( + factory()->NewUndefinedLiteral(RelocInfo::kNoPosition), + RelocInfo::kNoPosition), + zone()); + + // Make statement: outer: for (;;) + // Note that we don't actually create the label, or set this loop up as an + // explicit break target, instead handing it directly to those nodes that + // need to know about it. This should be safe because we don't run any code + // in this function that looks up break targets. + ForStatement* outer_loop = + factory()->NewForStatement(NULL, RelocInfo::kNoPosition); + outer_block->statements()->Add(outer_loop, zone()); + + outer_block->set_scope(for_scope); + scope_ = inner_scope; + + Block* inner_block = + factory()->NewBlock(NULL, 3, false, RelocInfo::kNoPosition); + Block* ignore_completion_block = factory()->NewBlock( + NULL, names->length() + 3, true, RelocInfo::kNoPosition); + ZoneList<Variable*> inner_vars(names->length(), zone()); + // For each let variable x: + // make statement: let/const x = temp_x. + VariableMode mode = is_const ? CONST : LET; + for (int i = 0; i < names->length(); i++) { + VariableProxy* proxy = NewUnresolved(names->at(i), mode); + Declaration* declaration = factory()->NewVariableDeclaration( + proxy, mode, scope_, RelocInfo::kNoPosition); + Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK); + inner_vars.Add(declaration->proxy()->var(), zone()); + VariableProxy* temp_proxy = factory()->NewVariableProxy(temps.at(i)); + Assignment* assignment = factory()->NewAssignment( + Token::INIT, proxy, temp_proxy, RelocInfo::kNoPosition); + Statement* assignment_statement = + factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition); + DCHECK(init->position() != RelocInfo::kNoPosition); + proxy->var()->set_initializer_position(init->position()); + ignore_completion_block->statements()->Add(assignment_statement, zone()); + } + + // Make statement: if (first == 1) { first = 0; } else { next; } + if (next) { + DCHECK(first); + Expression* compare = NULL; + // Make compare expression: first == 1. + { + Expression* const1 = factory()->NewSmiLiteral(1, RelocInfo::kNoPosition); + VariableProxy* first_proxy = factory()->NewVariableProxy(first); + compare = factory()->NewCompareOperation(Token::EQ, first_proxy, const1, + RelocInfo::kNoPosition); + } + Statement* clear_first = NULL; + // Make statement: first = 0. + { + VariableProxy* first_proxy = factory()->NewVariableProxy(first); + Expression* const0 = factory()->NewSmiLiteral(0, RelocInfo::kNoPosition); + Assignment* assignment = factory()->NewAssignment( + Token::ASSIGN, first_proxy, const0, RelocInfo::kNoPosition); + clear_first = + factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition); + } + Statement* clear_first_or_next = factory()->NewIfStatement( + compare, clear_first, next, RelocInfo::kNoPosition); + ignore_completion_block->statements()->Add(clear_first_or_next, zone()); + } + + Variable* flag = scope_->NewTemporary(temp_name); + // Make statement: flag = 1. + { + VariableProxy* flag_proxy = factory()->NewVariableProxy(flag); + Expression* const1 = factory()->NewSmiLiteral(1, RelocInfo::kNoPosition); + Assignment* assignment = factory()->NewAssignment( + Token::ASSIGN, flag_proxy, const1, RelocInfo::kNoPosition); + Statement* assignment_statement = + factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition); + ignore_completion_block->statements()->Add(assignment_statement, zone()); + } + + // Make statement: if (!cond) break. + if (cond) { + Statement* stop = + factory()->NewBreakStatement(outer_loop, RelocInfo::kNoPosition); + Statement* noop = factory()->NewEmptyStatement(RelocInfo::kNoPosition); + ignore_completion_block->statements()->Add( + factory()->NewIfStatement(cond, noop, stop, cond->position()), zone()); + } + + inner_block->statements()->Add(ignore_completion_block, zone()); + // Make cond expression for main loop: flag == 1. + Expression* flag_cond = NULL; + { + Expression* const1 = factory()->NewSmiLiteral(1, RelocInfo::kNoPosition); + VariableProxy* flag_proxy = factory()->NewVariableProxy(flag); + flag_cond = factory()->NewCompareOperation(Token::EQ, flag_proxy, const1, + RelocInfo::kNoPosition); + } + + // Create chain of expressions "flag = 0, temp_x = x, ..." + Statement* compound_next_statement = NULL; + { + Expression* compound_next = NULL; + // Make expression: flag = 0. + { + VariableProxy* flag_proxy = factory()->NewVariableProxy(flag); + Expression* const0 = factory()->NewSmiLiteral(0, RelocInfo::kNoPosition); + compound_next = factory()->NewAssignment(Token::ASSIGN, flag_proxy, + const0, RelocInfo::kNoPosition); + } + + // Make the comma-separated list of temp_x = x assignments. + int inner_var_proxy_pos = scanner()->location().beg_pos; + for (int i = 0; i < names->length(); i++) { + VariableProxy* temp_proxy = factory()->NewVariableProxy(temps.at(i)); + VariableProxy* proxy = + factory()->NewVariableProxy(inner_vars.at(i), inner_var_proxy_pos); + Assignment* assignment = factory()->NewAssignment( + Token::ASSIGN, temp_proxy, proxy, RelocInfo::kNoPosition); + compound_next = factory()->NewBinaryOperation( + Token::COMMA, compound_next, assignment, RelocInfo::kNoPosition); + } + + compound_next_statement = factory()->NewExpressionStatement( + compound_next, RelocInfo::kNoPosition); + } + + // Make statement: labels: for (; flag == 1; flag = 0, temp_x = x) + // Note that we re-use the original loop node, which retains its labels + // and ensures that any break or continue statements in body point to + // the right place. + loop->Initialize(NULL, flag_cond, compound_next_statement, body); + inner_block->statements()->Add(loop, zone()); + + // Make statement: {{if (flag == 1) break;}} + { + Expression* compare = NULL; + // Make compare expresion: flag == 1. + { + Expression* const1 = factory()->NewSmiLiteral(1, RelocInfo::kNoPosition); + VariableProxy* flag_proxy = factory()->NewVariableProxy(flag); + compare = factory()->NewCompareOperation(Token::EQ, flag_proxy, const1, + RelocInfo::kNoPosition); + } + Statement* stop = + factory()->NewBreakStatement(outer_loop, RelocInfo::kNoPosition); + Statement* empty = factory()->NewEmptyStatement(RelocInfo::kNoPosition); + Statement* if_flag_break = + factory()->NewIfStatement(compare, stop, empty, RelocInfo::kNoPosition); + Block* ignore_completion_block = + factory()->NewBlock(NULL, 1, true, RelocInfo::kNoPosition); + ignore_completion_block->statements()->Add(if_flag_break, zone()); + inner_block->statements()->Add(ignore_completion_block, zone()); + } + + inner_scope->set_end_position(scanner()->location().end_pos); + inner_block->set_scope(inner_scope); + scope_ = for_scope; + + outer_loop->Initialize(NULL, NULL, NULL, inner_block); + return outer_block; +} + + +Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, + bool* ok) { + // ForStatement :: + // 'for' '(' Expression? ';' Expression? ';' Expression? ')' Statement + + int stmt_pos = peek_position(); + bool is_const = false; + Statement* init = NULL; + ZoneList<const AstRawString*> lexical_bindings(1, zone()); + + // Create an in-between scope for let-bound iteration variables. + Scope* saved_scope = scope_; + Scope* for_scope = NewScope(scope_, BLOCK_SCOPE); + scope_ = for_scope; + Expect(Token::FOR, CHECK_OK); + Expect(Token::LPAREN, CHECK_OK); + for_scope->set_start_position(scanner()->location().beg_pos); + bool is_let_identifier_expression = false; + DeclarationParsingResult parsing_result; + if (peek() != Token::SEMICOLON) { + if (peek() == Token::VAR || (peek() == Token::CONST && allow_const()) || + (peek() == Token::LET && IsNextLetKeyword())) { + ParseVariableDeclarations(kForStatement, &parsing_result, CHECK_OK); + is_const = parsing_result.descriptor.mode == CONST; + + int num_decl = parsing_result.declarations.length(); + bool accept_IN = num_decl >= 1; + ForEachStatement::VisitMode mode; + int each_beg_pos = scanner()->location().beg_pos; + int each_end_pos = scanner()->location().end_pos; + + if (accept_IN && CheckInOrOf(&mode, ok)) { + if (!*ok) return nullptr; + if (num_decl != 1) { + const char* loop_type = + mode == ForEachStatement::ITERATE ? "for-of" : "for-in"; + ParserTraits::ReportMessageAt( + parsing_result.bindings_loc, + MessageTemplate::kForInOfLoopMultiBindings, loop_type); + *ok = false; + return nullptr; + } + DeclarationParsingResult::Declaration& decl = + parsing_result.declarations[0]; + if (parsing_result.first_initializer_loc.IsValid() && + (is_strict(language_mode()) || mode == ForEachStatement::ITERATE || + IsLexicalVariableMode(parsing_result.descriptor.mode) || + !decl.pattern->IsVariableProxy())) { + if (mode == ForEachStatement::ITERATE) { + ReportMessageAt(parsing_result.first_initializer_loc, + MessageTemplate::kForOfLoopInitializer); + } else { + // TODO(caitp): This should be an error in sloppy mode too. + ReportMessageAt(parsing_result.first_initializer_loc, + MessageTemplate::kForInLoopInitializer); + } + *ok = false; + return nullptr; + } + + Block* init_block = nullptr; + + // special case for legacy for (var/const x =.... in) + if (!IsLexicalVariableMode(parsing_result.descriptor.mode) && + decl.pattern->IsVariableProxy() && decl.initializer != nullptr) { + const AstRawString* name = + decl.pattern->AsVariableProxy()->raw_name(); + VariableProxy* single_var = scope_->NewUnresolved( + factory(), name, Variable::NORMAL, each_beg_pos, each_end_pos); + init_block = factory()->NewBlock( + nullptr, 2, true, parsing_result.descriptor.declaration_pos); + init_block->statements()->Add( + factory()->NewExpressionStatement( + factory()->NewAssignment(Token::ASSIGN, single_var, + decl.initializer, + RelocInfo::kNoPosition), + RelocInfo::kNoPosition), + zone()); + } + + // Rewrite a for-in/of statement of the form + // + // for (let/const/var x in/of e) b + // + // into + // + // { + // <let x' be a temporary variable> + // for (x' in/of e) { + // let/const/var x; + // x = x'; + // b; + // } + // let x; // for TDZ + // } + + Variable* temp = scope_->NewTemporary( + ast_value_factory()->dot_for_string()); + ForEachStatement* loop = + factory()->NewForEachStatement(mode, labels, stmt_pos); + Target target(&this->target_stack_, loop); + + Expression* enumerable = ParseExpression(true, CHECK_OK); + + Expect(Token::RPAREN, CHECK_OK); + + Scope* body_scope = NewScope(scope_, BLOCK_SCOPE); + body_scope->set_start_position(scanner()->location().beg_pos); + scope_ = body_scope; + + Statement* body = ParseSubStatement(NULL, CHECK_OK); + + Block* body_block = + factory()->NewBlock(NULL, 3, false, RelocInfo::kNoPosition); + + auto each_initialization_block = + factory()->NewBlock(nullptr, 1, true, RelocInfo::kNoPosition); + { + auto descriptor = parsing_result.descriptor; + descriptor.declaration_pos = RelocInfo::kNoPosition; + descriptor.initialization_pos = RelocInfo::kNoPosition; + decl.initializer = factory()->NewVariableProxy(temp); + + PatternRewriter::DeclareAndInitializeVariables( + each_initialization_block, &descriptor, &decl, + IsLexicalVariableMode(descriptor.mode) ? &lexical_bindings + : nullptr, + CHECK_OK); + } + + body_block->statements()->Add(each_initialization_block, zone()); + body_block->statements()->Add(body, zone()); + VariableProxy* temp_proxy = + factory()->NewVariableProxy(temp, each_beg_pos, each_end_pos); + InitializeForEachStatement(loop, temp_proxy, enumerable, body_block, + false); + scope_ = for_scope; + body_scope->set_end_position(scanner()->location().end_pos); + body_scope = body_scope->FinalizeBlockScope(); + if (body_scope != nullptr) { + body_block->set_scope(body_scope); + } + + // Create a TDZ for any lexically-bound names. + if (IsLexicalVariableMode(parsing_result.descriptor.mode)) { + DCHECK_NULL(init_block); + + init_block = + factory()->NewBlock(nullptr, 1, false, RelocInfo::kNoPosition); + + for (int i = 0; i < lexical_bindings.length(); ++i) { + // TODO(adamk): This needs to be some sort of special + // INTERNAL variable that's invisible to the debugger + // but visible to everything else. + VariableProxy* tdz_proxy = NewUnresolved(lexical_bindings[i], LET); + Declaration* tdz_decl = factory()->NewVariableDeclaration( + tdz_proxy, LET, scope_, RelocInfo::kNoPosition); + Variable* tdz_var = Declare(tdz_decl, DeclarationDescriptor::NORMAL, + true, CHECK_OK); + tdz_var->set_initializer_position(position()); + } + } + + scope_ = saved_scope; + for_scope->set_end_position(scanner()->location().end_pos); + for_scope = for_scope->FinalizeBlockScope(); + // Parsed for-in loop w/ variable declarations. + if (init_block != nullptr) { + init_block->statements()->Add(loop, zone()); + if (for_scope != nullptr) { + init_block->set_scope(for_scope); + } + return init_block; + } else { + DCHECK_NULL(for_scope); + return loop; + } + } else { + init = parsing_result.BuildInitializationBlock( + IsLexicalVariableMode(parsing_result.descriptor.mode) + ? &lexical_bindings + : nullptr, + CHECK_OK); + } + } else { + int lhs_beg_pos = peek_position(); + ExpressionClassifier classifier; + Expression* expression = ParseExpression(false, &classifier, CHECK_OK); + int lhs_end_pos = scanner()->location().end_pos; + ForEachStatement::VisitMode mode; + is_let_identifier_expression = + expression->IsVariableProxy() && + expression->AsVariableProxy()->raw_name() == + ast_value_factory()->let_string(); + + bool is_for_each = CheckInOrOf(&mode, ok); + if (!*ok) return nullptr; + bool is_destructuring = + is_for_each && allow_harmony_destructuring_assignment() && + (expression->IsArrayLiteral() || expression->IsObjectLiteral()); + + if (is_destructuring) { + ValidateAssignmentPattern(&classifier, CHECK_OK); + } else { + expression = + ParserTraits::RewriteNonPattern(expression, &classifier, CHECK_OK); + } + + if (is_for_each) { + if (!is_destructuring) { + expression = this->CheckAndRewriteReferenceExpression( + expression, lhs_beg_pos, lhs_end_pos, + MessageTemplate::kInvalidLhsInFor, kSyntaxError, CHECK_OK); + } + + ForEachStatement* loop = + factory()->NewForEachStatement(mode, labels, stmt_pos); + Target target(&this->target_stack_, loop); + + Expression* enumerable = ParseExpression(true, CHECK_OK); + Expect(Token::RPAREN, CHECK_OK); + + // Make a block around the statement in case a lexical binding + // is introduced, e.g. by a FunctionDeclaration. + // This block must not use for_scope as its scope because if a + // lexical binding is introduced which overlaps with the for-in/of, + // expressions in head of the loop should actually have variables + // resolved in the outer scope. + Scope* body_scope = NewScope(for_scope, BLOCK_SCOPE); + scope_ = body_scope; + Block* block = + factory()->NewBlock(NULL, 1, false, RelocInfo::kNoPosition); + Statement* body = ParseSubStatement(NULL, CHECK_OK); + block->statements()->Add(body, zone()); + InitializeForEachStatement(loop, expression, enumerable, block, + is_destructuring); + scope_ = saved_scope; + body_scope->set_end_position(scanner()->location().end_pos); + body_scope = body_scope->FinalizeBlockScope(); + if (body_scope != nullptr) { + block->set_scope(body_scope); + } + for_scope->set_end_position(scanner()->location().end_pos); + for_scope = for_scope->FinalizeBlockScope(); + DCHECK(for_scope == nullptr); + // Parsed for-in loop. + return loop; + + } else { + init = factory()->NewExpressionStatement(expression, lhs_beg_pos); + } + } + } + + // Standard 'for' loop + ForStatement* loop = factory()->NewForStatement(labels, stmt_pos); + Target target(&this->target_stack_, loop); + + // Parsed initializer at this point. + // Detect attempts at 'let' declarations in sloppy mode. + if (!allow_harmony_sloppy_let() && peek() == Token::IDENTIFIER && + is_sloppy(language_mode()) && is_let_identifier_expression) { + ReportMessage(MessageTemplate::kSloppyLexical, NULL); + *ok = false; + return NULL; + } + Expect(Token::SEMICOLON, CHECK_OK); + + // If there are let bindings, then condition and the next statement of the + // for loop must be parsed in a new scope. + Scope* inner_scope = NULL; + if (lexical_bindings.length() > 0) { + inner_scope = NewScope(for_scope, BLOCK_SCOPE); + inner_scope->set_start_position(scanner()->location().beg_pos); + scope_ = inner_scope; + } + + Expression* cond = NULL; + if (peek() != Token::SEMICOLON) { + cond = ParseExpression(true, CHECK_OK); + } + Expect(Token::SEMICOLON, CHECK_OK); + + Statement* next = NULL; + if (peek() != Token::RPAREN) { + Expression* exp = ParseExpression(true, CHECK_OK); + next = factory()->NewExpressionStatement(exp, exp->position()); + } + Expect(Token::RPAREN, CHECK_OK); + + Statement* body = ParseSubStatement(NULL, CHECK_OK); + + Statement* result = NULL; + if (lexical_bindings.length() > 0) { + scope_ = for_scope; + result = DesugarLexicalBindingsInForStatement( + inner_scope, is_const, &lexical_bindings, loop, init, cond, + next, body, CHECK_OK); + scope_ = saved_scope; + for_scope->set_end_position(scanner()->location().end_pos); + } else { + scope_ = saved_scope; + for_scope->set_end_position(scanner()->location().end_pos); + for_scope = for_scope->FinalizeBlockScope(); + if (for_scope) { + // Rewrite a for statement of the form + // for (const x = i; c; n) b + // + // into + // + // { + // const x = i; + // for (; c; n) b + // } + // + // or, desugar + // for (; c; n) b + // into + // { + // for (; c; n) b + // } + // just in case b introduces a lexical binding some other way, e.g., if b + // is a FunctionDeclaration. + Block* block = + factory()->NewBlock(NULL, 2, false, RelocInfo::kNoPosition); + if (init != nullptr) { + block->statements()->Add(init, zone()); + } + block->statements()->Add(loop, zone()); + block->set_scope(for_scope); + loop->Initialize(NULL, cond, next, body); + result = block; + } else { + loop->Initialize(init, cond, next, body); + result = loop; + } + } + 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' ';' + + int pos = peek_position(); + Expect(Token::DEBUGGER, CHECK_OK); + ExpectSemicolon(CHECK_OK); + return factory()->NewDebuggerStatement(pos); +} + + +bool CompileTimeValue::IsCompileTimeValue(Expression* expression) { + if (expression->IsLiteral()) return true; + MaterializedLiteral* lit = expression->AsMaterializedLiteral(); + return lit != NULL && lit->is_simple(); +} + + +Handle<FixedArray> CompileTimeValue::GetValue(Isolate* isolate, + Expression* expression) { + Factory* factory = isolate->factory(); + DCHECK(IsCompileTimeValue(expression)); + Handle<FixedArray> result = factory->NewFixedArray(2, TENURED); + ObjectLiteral* object_literal = expression->AsObjectLiteral(); + if (object_literal != NULL) { + DCHECK(object_literal->is_simple()); + if (object_literal->fast_elements()) { + result->set(kLiteralTypeSlot, Smi::FromInt(OBJECT_LITERAL_FAST_ELEMENTS)); + } else { + result->set(kLiteralTypeSlot, Smi::FromInt(OBJECT_LITERAL_SLOW_ELEMENTS)); + } + result->set(kElementsSlot, *object_literal->constant_properties()); + } else { + ArrayLiteral* array_literal = expression->AsArrayLiteral(); + DCHECK(array_literal != NULL && array_literal->is_simple()); + result->set(kLiteralTypeSlot, Smi::FromInt(ARRAY_LITERAL)); + result->set(kElementsSlot, *array_literal->constant_elements()); + } + return result; +} + + +CompileTimeValue::LiteralType CompileTimeValue::GetLiteralType( + Handle<FixedArray> value) { + Smi* literal_type = Smi::cast(value->get(kLiteralTypeSlot)); + return static_cast<LiteralType>(literal_type->value()); +} + + +Handle<FixedArray> CompileTimeValue::GetElements(Handle<FixedArray> value) { + return Handle<FixedArray>(FixedArray::cast(value->get(kElementsSlot))); +} + + +void ParserTraits::ParseArrowFunctionFormalParameters( + ParserFormalParameters* parameters, Expression* expr, + const Scanner::Location& params_loc, bool* ok) { + if (parameters->Arity() >= Code::kMaxArguments) { + ReportMessageAt(params_loc, MessageTemplate::kMalformedArrowFunParamList); + *ok = false; + return; + } + + // ArrowFunctionFormals :: + // Binary(Token::COMMA, NonTailArrowFunctionFormals, Tail) + // Tail + // NonTailArrowFunctionFormals :: + // Binary(Token::COMMA, NonTailArrowFunctionFormals, VariableProxy) + // VariableProxy + // Tail :: + // VariableProxy + // Spread(VariableProxy) + // + // As we need to visit the parameters in left-to-right order, we recurse on + // the left-hand side of comma expressions. + // + if (expr->IsBinaryOperation()) { + BinaryOperation* binop = expr->AsBinaryOperation(); + // The classifier has already run, so we know that the expression is a valid + // arrow function formals production. + DCHECK_EQ(binop->op(), Token::COMMA); + Expression* left = binop->left(); + Expression* right = binop->right(); + ParseArrowFunctionFormalParameters(parameters, left, params_loc, ok); + if (!*ok) return; + // LHS of comma expression should be unparenthesized. + expr = right; + } + + // Only the right-most expression may be a rest parameter. + DCHECK(!parameters->has_rest); + + bool is_rest = expr->IsSpread(); + if (is_rest) { + expr = expr->AsSpread()->expression(); + parameters->has_rest = true; + } + if (parameters->is_simple) { + parameters->is_simple = !is_rest && expr->IsVariableProxy(); + } + + Expression* initializer = nullptr; + if (expr->IsVariableProxy()) { + // When the formal parameter was originally seen, it was parsed as a + // VariableProxy and recorded as unresolved in the scope. Here we undo that + // parse-time side-effect for parameters that are single-names (not + // patterns; for patterns that happens uniformly in + // PatternRewriter::VisitVariableProxy). + parser_->scope_->RemoveUnresolved(expr->AsVariableProxy()); + } else if (expr->IsAssignment()) { + Assignment* assignment = expr->AsAssignment(); + DCHECK(parser_->allow_harmony_default_parameters()); + DCHECK(!assignment->is_compound()); + initializer = assignment->value(); + expr = assignment->target(); + + // TODO(adamk): Only call this if necessary. + RewriteParameterInitializerScope(parser_->stack_limit(), initializer, + parser_->scope_, parameters->scope); + } + + // TODO(adamk): params_loc.end_pos is not the correct initializer position, + // but it should be conservative enough to trigger hole checks for variables + // referenced in the initializer (if any). + AddFormalParameter(parameters, expr, initializer, params_loc.end_pos, + is_rest); +} + + +DoExpression* Parser::ParseDoExpression(bool* ok) { + // AssignmentExpression :: + // do '{' StatementList '}' + int pos = peek_position(); + + Expect(Token::DO, CHECK_OK); + Variable* result = + scope_->NewTemporary(ast_value_factory()->dot_result_string()); + Block* block = ParseBlock(nullptr, false, CHECK_OK); + DoExpression* expr = factory()->NewDoExpression(block, result, pos); + if (!Rewriter::Rewrite(this, expr, ast_value_factory())) { + *ok = false; + return nullptr; + } + block->set_scope(block->scope()->FinalizeBlockScope()); + return expr; +} + + +void ParserTraits::ParseArrowFunctionFormalParameterList( + ParserFormalParameters* parameters, Expression* expr, + const Scanner::Location& params_loc, + Scanner::Location* duplicate_loc, bool* ok) { + if (expr->IsEmptyParentheses()) return; + + ParseArrowFunctionFormalParameters(parameters, expr, params_loc, ok); + if (!*ok) return; + + ExpressionClassifier classifier; + if (!parameters->is_simple) { + classifier.RecordNonSimpleParameter(); + } + for (int i = 0; i < parameters->Arity(); ++i) { + auto parameter = parameters->at(i); + DeclareFormalParameter(parameters->scope, parameter, &classifier); + if (!duplicate_loc->IsValid()) { + *duplicate_loc = classifier.duplicate_formal_parameter_error().location; + } + } + DCHECK_EQ(parameters->is_simple, parameters->scope->has_simple_parameters()); +} + + +void ParserTraits::ReindexLiterals(const ParserFormalParameters& parameters) { + if (parser_->function_state_->materialized_literal_count() > 0) { + AstLiteralReindexer reindexer; + + for (const auto p : parameters.params) { + if (p.pattern != nullptr) reindexer.Reindex(p.pattern); + if (p.initializer != nullptr) reindexer.Reindex(p.initializer); + } + + DCHECK(reindexer.count() <= + parser_->function_state_->materialized_literal_count()); + } +} + + +FunctionLiteral* Parser::ParseFunctionLiteral( + const AstRawString* function_name, Scanner::Location function_name_location, + FunctionNameValidity function_name_validity, FunctionKind kind, + int function_token_pos, FunctionLiteral::FunctionType function_type, + FunctionLiteral::ArityRestriction arity_restriction, + LanguageMode language_mode, bool* ok) { + // Function :: + // '(' FormalParameterList? ')' '{' FunctionBody '}' + // + // Getter :: + // '(' ')' '{' FunctionBody '}' + // + // Setter :: + // '(' PropertySetParameterList ')' '{' FunctionBody '}' + + int pos = function_token_pos == RelocInfo::kNoPosition + ? peek_position() : function_token_pos; + + bool is_generator = IsGeneratorFunction(kind); + + // Anonymous functions were passed either the empty symbol or a null + // handle as the function name. Remember if we were passed a non-empty + // handle to decide whether to invoke function name inference. + bool should_infer_name = function_name == NULL; + + // We want a non-null handle as the function name. + if (should_infer_name) { + function_name = ast_value_factory()->empty_string(); + } + + // Function declarations are function scoped in normal mode, so they are + // hoisted. In harmony block scoping mode they are block scoped, so they + // are not hoisted. + // + // One tricky case are function declarations in a local sloppy-mode eval: + // their declaration is hoisted, but they still see the local scope. E.g., + // + // function() { + // var x = 0 + // try { throw 1 } catch (x) { eval("function g() { return x }") } + // return g() + // } + // + // needs to return 1. To distinguish such cases, we need to detect + // (1) whether a function stems from a sloppy eval, and + // (2) whether it actually hoists across the eval. + // Unfortunately, we do not represent sloppy eval scopes, so we do not have + // either information available directly, especially not when lazily compiling + // a function like 'g'. We hence rely on the following invariants: + // - (1) is the case iff the innermost scope of the deserialized scope chain + // under which we compile is _not_ a declaration scope. This holds because + // in all normal cases, function declarations are fully hoisted to a + // declaration scope and compiled relative to that. + // - (2) is the case iff the current declaration scope is still the original + // one relative to the deserialized scope chain. Otherwise we must be + // compiling a function in an inner declaration scope in the eval, e.g. a + // nested function, and hoisting works normally relative to that. + Scope* declaration_scope = scope_->DeclarationScope(); + Scope* original_declaration_scope = original_scope_->DeclarationScope(); + Scope* scope = function_type == FunctionLiteral::kDeclaration && + is_sloppy(language_mode) && + !allow_harmony_sloppy_function() && + (original_scope_ == original_declaration_scope || + declaration_scope != original_declaration_scope) + ? NewScope(declaration_scope, FUNCTION_SCOPE, kind) + : NewScope(scope_, FUNCTION_SCOPE, kind); + SetLanguageMode(scope, language_mode); + ZoneList<Statement*>* body = NULL; + int arity = -1; + int materialized_literal_count = -1; + int expected_property_count = -1; + DuplicateFinder duplicate_finder(scanner()->unicode_cache()); + ExpressionClassifier formals_classifier(&duplicate_finder); + FunctionLiteral::EagerCompileHint eager_compile_hint = + parenthesized_function_ ? FunctionLiteral::kShouldEagerCompile + : FunctionLiteral::kShouldLazyCompile; + bool should_be_used_once_hint = false; + // Parse function. + { + AstNodeFactory function_factory(ast_value_factory()); + FunctionState function_state(&function_state_, &scope_, scope, kind, + &function_factory); + scope_->SetScopeName(function_name); + + if (is_generator) { + // For generators, allocating variables in contexts is currently a win + // because it minimizes the work needed to suspend and resume an + // activation. + scope_->ForceContextAllocation(); + + // Calling a generator returns a generator object. That object is stored + // in a temporary variable, a definition that is used by "yield" + // expressions. This also marks the FunctionState as a generator. + Variable* temp = scope_->NewTemporary( + ast_value_factory()->dot_generator_object_string()); + function_state.set_generator_object_variable(temp); + } + + Expect(Token::LPAREN, CHECK_OK); + int start_position = scanner()->location().beg_pos; + scope_->set_start_position(start_position); + ParserFormalParameters formals(scope); + ParseFormalParameterList(&formals, &formals_classifier, CHECK_OK); + arity = formals.Arity(); + Expect(Token::RPAREN, CHECK_OK); + int formals_end_position = scanner()->location().end_pos; + + CheckArityRestrictions(arity, arity_restriction, + formals.has_rest, start_position, + formals_end_position, CHECK_OK); + Expect(Token::LBRACE, CHECK_OK); + + // Determine if the function can be parsed lazily. Lazy parsing is different + // from lazy compilation; we need to parse more eagerly than we compile. + + // We can only parse lazily if we also compile lazily. The heuristics for + // lazy compilation are: + // - It must not have been prohibited by the caller to Parse (some callers + // need a full AST). + // - The outer scope must allow lazy compilation of inner functions. + // - The function mustn't be a function expression with an open parenthesis + // before; we consider that a hint that the function will be called + // immediately, and it would be a waste of time to make it lazily + // compiled. + // These are all things we can know at this point, without looking at the + // function itself. + + // In addition, we need to distinguish between these cases: + // (function foo() { + // bar = function() { return 1; } + // })(); + // and + // (function foo() { + // var a = 1; + // bar = function() { return a; } + // })(); + + // Now foo will be parsed eagerly and compiled eagerly (optimization: assume + // parenthesis before the function means that it will be called + // immediately). The inner function *must* be parsed eagerly to resolve the + // possible reference to the variable in foo's scope. However, it's possible + // that it will be compiled lazily. + + // To make this additional case work, both Parser and PreParser implement a + // logic where only top-level functions will be parsed lazily. + bool is_lazily_parsed = mode() == PARSE_LAZILY && + scope_->AllowsLazyParsing() && + !parenthesized_function_; + parenthesized_function_ = false; // The bit was set for this function only. + + // Eager or lazy parse? + // If is_lazily_parsed, we'll parse lazy. If we can set a bookmark, we'll + // pass it to SkipLazyFunctionBody, which may use it to abort lazy + // parsing if it suspect that wasn't a good idea. If so, or if we didn't + // try to lazy parse in the first place, we'll have to parse eagerly. + Scanner::BookmarkScope bookmark(scanner()); + if (is_lazily_parsed) { + Scanner::BookmarkScope* maybe_bookmark = + bookmark.Set() ? &bookmark : nullptr; + SkipLazyFunctionBody(&materialized_literal_count, + &expected_property_count, /*CHECK_OK*/ ok, + maybe_bookmark); + + materialized_literal_count += formals.materialized_literals_count + + function_state.materialized_literal_count(); + + if (bookmark.HasBeenReset()) { + // Trigger eager (re-)parsing, just below this block. + is_lazily_parsed = false; + + // This is probably an initialization function. Inform the compiler it + // should also eager-compile this function, and that we expect it to be + // used once. + eager_compile_hint = FunctionLiteral::kShouldEagerCompile; + should_be_used_once_hint = true; + } + } + if (!is_lazily_parsed) { + // Determine whether the function body can be discarded after parsing. + // The preconditions are: + // - Lazy compilation has to be enabled. + // - Neither V8 natives nor native function declarations can be allowed, + // since parsing one would retroactively force the function to be + // eagerly compiled. + // - The invoker of this parser can't depend on the AST being eagerly + // built (either because the function is about to be compiled, or + // because the AST is going to be inspected for some reason). + // - Because of the above, we can't be attempting to parse a + // FunctionExpression; even without enclosing parentheses it might be + // immediately invoked. + // - The function literal shouldn't be hinted to eagerly compile. + bool use_temp_zone = + FLAG_lazy && !allow_natives() && extension_ == NULL && allow_lazy() && + function_type == FunctionLiteral::kDeclaration && + eager_compile_hint != FunctionLiteral::kShouldEagerCompile; + // Open a new BodyScope, which sets our AstNodeFactory to allocate in the + // new temporary zone if the preconditions are satisfied, and ensures that + // the previous zone is always restored after parsing the body. + // For the purpose of scope analysis, some ZoneObjects allocated by the + // factory must persist after the function body is thrown away and + // temp_zone is deallocated. These objects are instead allocated in a + // parser-persistent zone (see parser_zone_ in AstNodeFactory). + { + Zone temp_zone; + AstNodeFactory::BodyScope inner(factory(), &temp_zone, use_temp_zone); + + body = ParseEagerFunctionBody(function_name, pos, formals, kind, + function_type, CHECK_OK); + } + materialized_literal_count = function_state.materialized_literal_count(); + expected_property_count = function_state.expected_property_count(); + if (use_temp_zone) { + // If the preconditions are correct the function body should never be + // accessed, but do this anyway for better behaviour if they're wrong. + body = NULL; + } + } + + // Parsing the body may change the language mode in our scope. + language_mode = scope->language_mode(); + + if (is_strong(language_mode) && IsSubclassConstructor(kind)) { + if (!function_state.super_location().IsValid()) { + ReportMessageAt(function_name_location, + MessageTemplate::kStrongSuperCallMissing, + kReferenceError); + *ok = false; + return nullptr; + } + } + + // Validate name and parameter names. We can do this only after parsing the + // function, since the function can declare itself strict. + CheckFunctionName(language_mode, function_name, function_name_validity, + function_name_location, CHECK_OK); + const bool allow_duplicate_parameters = + is_sloppy(language_mode) && formals.is_simple && !IsConciseMethod(kind); + ValidateFormalParameters(&formals_classifier, language_mode, + allow_duplicate_parameters, CHECK_OK); + + if (is_strict(language_mode)) { + CheckStrictOctalLiteral(scope->start_position(), scope->end_position(), + CHECK_OK); + } + if (is_sloppy(language_mode) && allow_harmony_sloppy_function()) { + InsertSloppyBlockFunctionVarBindings(scope, CHECK_OK); + } + if (is_strict(language_mode) || allow_harmony_sloppy() || + allow_harmony_destructuring_bind()) { + CheckConflictingVarDeclarations(scope, CHECK_OK); + } + + if (body) { + // If body can be inspected, rewrite queued destructuring assignments + ParserTraits::RewriteDestructuringAssignments(); + } + } + + bool has_duplicate_parameters = + !formals_classifier.is_valid_formal_parameter_list_without_duplicates(); + FunctionLiteral::ParameterFlag duplicate_parameters = + has_duplicate_parameters ? FunctionLiteral::kHasDuplicateParameters + : FunctionLiteral::kNoDuplicateParameters; + + FunctionLiteral* function_literal = factory()->NewFunctionLiteral( + function_name, scope, body, materialized_literal_count, + expected_property_count, arity, duplicate_parameters, function_type, + eager_compile_hint, kind, pos); + function_literal->set_function_token_position(function_token_pos); + if (should_be_used_once_hint) + function_literal->set_should_be_used_once_hint(); + + if (scope->has_rest_parameter()) { + function_literal->set_dont_optimize_reason(kRestParameter); + } + + if (fni_ != NULL && should_infer_name) fni_->AddFunction(function_literal); + return function_literal; +} + + +void Parser::SkipLazyFunctionBody(int* materialized_literal_count, + int* expected_property_count, bool* ok, + Scanner::BookmarkScope* bookmark) { + DCHECK_IMPLIES(bookmark, bookmark->HasBeenSet()); + if (produce_cached_parse_data()) CHECK(log_); + + int function_block_pos = position(); + if (consume_cached_parse_data() && !cached_parse_data_->rejected()) { + // If we have cached data, we use it to skip parsing the function body. The + // data contains the information we need to construct the lazy function. + FunctionEntry entry = + cached_parse_data_->GetFunctionEntry(function_block_pos); + // Check that cached data is valid. If not, mark it as invalid (the embedder + // handles it). Note that end position greater than end of stream is safe, + // and hard to check. + if (entry.is_valid() && entry.end_pos() > function_block_pos) { + scanner()->SeekForward(entry.end_pos() - 1); + + scope_->set_end_position(entry.end_pos()); + Expect(Token::RBRACE, ok); + if (!*ok) { + return; + } + total_preparse_skipped_ += scope_->end_position() - function_block_pos; + *materialized_literal_count = entry.literal_count(); + *expected_property_count = entry.property_count(); + SetLanguageMode(scope_, entry.language_mode()); + if (entry.uses_super_property()) scope_->RecordSuperPropertyUsage(); + if (entry.calls_eval()) scope_->RecordEvalCall(); + return; + } + cached_parse_data_->Reject(); + } + // With no cached data, we partially parse the function, without building an + // AST. This gathers the data needed to build a lazy function. + SingletonLogger logger; + PreParser::PreParseResult result = + ParseLazyFunctionBodyWithPreParser(&logger, bookmark); + if (bookmark && bookmark->HasBeenReset()) { + return; // Return immediately if pre-parser devided to abort parsing. + } + if (result == PreParser::kPreParseStackOverflow) { + // Propagate stack overflow. + set_stack_overflow(); + *ok = false; + return; + } + if (logger.has_error()) { + ParserTraits::ReportMessageAt( + Scanner::Location(logger.start(), logger.end()), logger.message(), + logger.argument_opt(), logger.error_type()); + *ok = false; + return; + } + scope_->set_end_position(logger.end()); + Expect(Token::RBRACE, ok); + if (!*ok) { + return; + } + total_preparse_skipped_ += scope_->end_position() - function_block_pos; + *materialized_literal_count = logger.literals(); + *expected_property_count = logger.properties(); + SetLanguageMode(scope_, logger.language_mode()); + if (logger.uses_super_property()) { + scope_->RecordSuperPropertyUsage(); + } + if (logger.calls_eval()) { + scope_->RecordEvalCall(); + } + if (produce_cached_parse_data()) { + DCHECK(log_); + // Position right after terminal '}'. + int body_end = scanner()->location().end_pos; + log_->LogFunction(function_block_pos, body_end, *materialized_literal_count, + *expected_property_count, scope_->language_mode(), + scope_->uses_super_property(), scope_->calls_eval()); + } +} + + +Statement* Parser::BuildAssertIsCoercible(Variable* var) { + // if (var === null || var === undefined) + // throw /* type error kNonCoercible) */; + + Expression* condition = factory()->NewBinaryOperation( + Token::OR, factory()->NewCompareOperation( + Token::EQ_STRICT, factory()->NewVariableProxy(var), + factory()->NewUndefinedLiteral(RelocInfo::kNoPosition), + RelocInfo::kNoPosition), + factory()->NewCompareOperation( + Token::EQ_STRICT, factory()->NewVariableProxy(var), + factory()->NewNullLiteral(RelocInfo::kNoPosition), + RelocInfo::kNoPosition), + RelocInfo::kNoPosition); + Expression* throw_type_error = this->NewThrowTypeError( + MessageTemplate::kNonCoercible, ast_value_factory()->empty_string(), + RelocInfo::kNoPosition); + IfStatement* if_statement = factory()->NewIfStatement( + condition, factory()->NewExpressionStatement(throw_type_error, + RelocInfo::kNoPosition), + factory()->NewEmptyStatement(RelocInfo::kNoPosition), + RelocInfo::kNoPosition); + return if_statement; +} + + +class InitializerRewriter : public AstExpressionVisitor { + public: + InitializerRewriter(uintptr_t stack_limit, Expression* root, Parser* parser, + Scope* scope) + : AstExpressionVisitor(stack_limit, root), + parser_(parser), + scope_(scope) {} + + private: + void VisitExpression(Expression* expr) { + RewritableAssignmentExpression* to_rewrite = + expr->AsRewritableAssignmentExpression(); + if (to_rewrite == nullptr || to_rewrite->is_rewritten()) return; + + Parser::PatternRewriter::RewriteDestructuringAssignment(parser_, to_rewrite, + scope_); + } + + private: + Parser* parser_; + Scope* scope_; +}; + + +void Parser::RewriteParameterInitializer(Expression* expr, Scope* scope) { + InitializerRewriter rewriter(stack_limit_, expr, this, scope); + rewriter.Run(); +} + + +Block* Parser::BuildParameterInitializationBlock( + const ParserFormalParameters& parameters, bool* ok) { + DCHECK(!parameters.is_simple); + DCHECK(scope_->is_function_scope()); + Block* init_block = + factory()->NewBlock(NULL, 1, true, RelocInfo::kNoPosition); + for (int i = 0; i < parameters.params.length(); ++i) { + auto parameter = parameters.params[i]; + if (parameter.is_rest && parameter.pattern->IsVariableProxy()) break; + DeclarationDescriptor descriptor; + descriptor.declaration_kind = DeclarationDescriptor::PARAMETER; + descriptor.parser = this; + descriptor.scope = scope_; + descriptor.hoist_scope = nullptr; + descriptor.mode = LET; + descriptor.needs_init = true; + descriptor.declaration_pos = parameter.pattern->position(); + // The position that will be used by the AssignmentExpression + // which copies from the temp parameter to the pattern. + // + // TODO(adamk): Should this be RelocInfo::kNoPosition, since + // it's just copying from a temp var to the real param var? + descriptor.initialization_pos = parameter.pattern->position(); + // The initializer position which will end up in, + // Variable::initializer_position(), used for hole check elimination. + int initializer_position = parameter.pattern->position(); + Expression* initial_value = + factory()->NewVariableProxy(parameters.scope->parameter(i)); + if (parameter.initializer != nullptr) { + // IS_UNDEFINED($param) ? initializer : $param + + // Ensure initializer is rewritten + RewriteParameterInitializer(parameter.initializer, scope_); + + auto condition = factory()->NewCompareOperation( + Token::EQ_STRICT, + factory()->NewVariableProxy(parameters.scope->parameter(i)), + factory()->NewUndefinedLiteral(RelocInfo::kNoPosition), + RelocInfo::kNoPosition); + initial_value = factory()->NewConditional( + condition, parameter.initializer, initial_value, + RelocInfo::kNoPosition); + descriptor.initialization_pos = parameter.initializer->position(); + initializer_position = parameter.initializer_end_position; + } + + Scope* param_scope = scope_; + Block* param_block = init_block; + if (!parameter.is_simple() && scope_->calls_sloppy_eval()) { + param_scope = NewScope(scope_, BLOCK_SCOPE); + param_scope->set_is_declaration_scope(); + param_scope->set_start_position(parameter.pattern->position()); + param_scope->set_end_position(RelocInfo::kNoPosition); + param_scope->RecordEvalCall(); + param_block = factory()->NewBlock(NULL, 8, true, RelocInfo::kNoPosition); + param_block->set_scope(param_scope); + descriptor.hoist_scope = scope_; + } + + { + BlockState block_state(&scope_, param_scope); + DeclarationParsingResult::Declaration decl( + parameter.pattern, initializer_position, initial_value); + PatternRewriter::DeclareAndInitializeVariables(param_block, &descriptor, + &decl, nullptr, CHECK_OK); + } + + if (!parameter.is_simple() && scope_->calls_sloppy_eval()) { + param_scope = param_scope->FinalizeBlockScope(); + if (param_scope != nullptr) { + CheckConflictingVarDeclarations(param_scope, CHECK_OK); + } + init_block->statements()->Add(param_block, zone()); + } + } + return init_block; +} + + +ZoneList<Statement*>* Parser::ParseEagerFunctionBody( + const AstRawString* function_name, int pos, + const ParserFormalParameters& parameters, FunctionKind kind, + FunctionLiteral::FunctionType function_type, bool* ok) { + // Everything inside an eagerly parsed function will be parsed eagerly + // (see comment above). + ParsingModeScope parsing_mode(this, PARSE_EAGERLY); + ZoneList<Statement*>* result = new(zone()) ZoneList<Statement*>(8, zone()); + + static const int kFunctionNameAssignmentIndex = 0; + if (function_type == FunctionLiteral::kNamedExpression) { + DCHECK(function_name != NULL); + // 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). + // Not having parsed the function body, the language mode may still change, + // so we reserve a spot and create the actual const assignment later. + DCHECK_EQ(kFunctionNameAssignmentIndex, result->length()); + result->Add(NULL, zone()); + } + + ZoneList<Statement*>* body = result; + Scope* inner_scope = scope_; + Block* inner_block = nullptr; + if (!parameters.is_simple) { + inner_scope = NewScope(scope_, BLOCK_SCOPE); + inner_scope->set_is_declaration_scope(); + inner_scope->set_start_position(scanner()->location().beg_pos); + inner_block = factory()->NewBlock(NULL, 8, true, RelocInfo::kNoPosition); + inner_block->set_scope(inner_scope); + body = inner_block->statements(); + } + + { + BlockState block_state(&scope_, inner_scope); + + // For generators, allocate and yield an iterator on function entry. + if (IsGeneratorFunction(kind)) { + ZoneList<Expression*>* arguments = + new(zone()) ZoneList<Expression*>(0, zone()); + CallRuntime* allocation = factory()->NewCallRuntime( + Runtime::kCreateJSGeneratorObject, arguments, pos); + VariableProxy* init_proxy = factory()->NewVariableProxy( + function_state_->generator_object_variable()); + Assignment* assignment = factory()->NewAssignment( + Token::INIT, init_proxy, allocation, RelocInfo::kNoPosition); + VariableProxy* get_proxy = factory()->NewVariableProxy( + function_state_->generator_object_variable()); + Yield* yield = factory()->NewYield( + get_proxy, assignment, Yield::kInitial, RelocInfo::kNoPosition); + body->Add(factory()->NewExpressionStatement( + yield, RelocInfo::kNoPosition), zone()); + } + + ParseStatementList(body, Token::RBRACE, CHECK_OK); + + if (IsGeneratorFunction(kind)) { + VariableProxy* get_proxy = factory()->NewVariableProxy( + function_state_->generator_object_variable()); + Expression* undefined = + factory()->NewUndefinedLiteral(RelocInfo::kNoPosition); + Yield* yield = factory()->NewYield(get_proxy, undefined, Yield::kFinal, + RelocInfo::kNoPosition); + body->Add(factory()->NewExpressionStatement( + yield, RelocInfo::kNoPosition), zone()); + } + + if (IsSubclassConstructor(kind)) { + body->Add( + factory()->NewReturnStatement( + this->ThisExpression(scope_, factory(), RelocInfo::kNoPosition), + RelocInfo::kNoPosition), + zone()); + } + } + + Expect(Token::RBRACE, CHECK_OK); + scope_->set_end_position(scanner()->location().end_pos); + + if (!parameters.is_simple) { + DCHECK_NOT_NULL(inner_scope); + DCHECK_EQ(body, inner_block->statements()); + SetLanguageMode(scope_, inner_scope->language_mode()); + Block* init_block = BuildParameterInitializationBlock(parameters, CHECK_OK); + DCHECK_NOT_NULL(init_block); + + inner_scope->set_end_position(scanner()->location().end_pos); + inner_scope = inner_scope->FinalizeBlockScope(); + if (inner_scope != nullptr) { + CheckConflictingVarDeclarations(inner_scope, CHECK_OK); + InsertShadowingVarBindingInitializers(inner_block); + } + + result->Add(init_block, zone()); + result->Add(inner_block, zone()); + } + + if (function_type == FunctionLiteral::kNamedExpression) { + // Now that we know the language mode, we can create the const assignment + // in the previously reserved spot. + // 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 Proxies as is the case now. + VariableMode fvar_mode = is_strict(language_mode()) ? CONST : CONST_LEGACY; + Variable* fvar = new (zone()) + Variable(scope_, function_name, fvar_mode, Variable::NORMAL, + kCreatedInitialized, kNotAssigned); + VariableProxy* proxy = factory()->NewVariableProxy(fvar); + VariableDeclaration* fvar_declaration = factory()->NewVariableDeclaration( + proxy, fvar_mode, scope_, RelocInfo::kNoPosition); + scope_->DeclareFunctionVar(fvar_declaration); + + VariableProxy* fproxy = factory()->NewVariableProxy(fvar); + result->Set(kFunctionNameAssignmentIndex, + factory()->NewExpressionStatement( + factory()->NewAssignment(Token::INIT, fproxy, + factory()->NewThisFunction(pos), + RelocInfo::kNoPosition), + RelocInfo::kNoPosition)); + } + + return result; +} + + +PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser( + SingletonLogger* logger, Scanner::BookmarkScope* bookmark) { + // This function may be called on a background thread too; record only the + // main thread preparse times. + if (pre_parse_timer_ != NULL) { + pre_parse_timer_->Start(); + } + DCHECK_EQ(Token::LBRACE, scanner()->current_token()); + + if (reusable_preparser_ == NULL) { + reusable_preparser_ = new PreParser(zone(), &scanner_, ast_value_factory(), + NULL, stack_limit_); + reusable_preparser_->set_allow_lazy(true); +#define SET_ALLOW(name) reusable_preparser_->set_allow_##name(allow_##name()); + SET_ALLOW(natives); + SET_ALLOW(harmony_sloppy); + SET_ALLOW(harmony_sloppy_let); + SET_ALLOW(harmony_default_parameters); + SET_ALLOW(harmony_destructuring_bind); + SET_ALLOW(harmony_destructuring_assignment); + SET_ALLOW(strong_mode); + SET_ALLOW(harmony_do_expressions); + SET_ALLOW(harmony_function_name); +#undef SET_ALLOW + } + PreParser::PreParseResult result = reusable_preparser_->PreParseLazyFunction( + language_mode(), function_state_->kind(), scope_->has_simple_parameters(), + logger, bookmark); + if (pre_parse_timer_ != NULL) { + pre_parse_timer_->Stop(); + } + return result; +} + + +ClassLiteral* Parser::ParseClassLiteral(const AstRawString* name, + Scanner::Location class_name_location, + bool name_is_strict_reserved, int pos, + bool* ok) { + // All parts of a ClassDeclaration and ClassExpression are strict code. + if (name_is_strict_reserved) { + ReportMessageAt(class_name_location, + MessageTemplate::kUnexpectedStrictReserved); + *ok = false; + return NULL; + } + if (IsEvalOrArguments(name)) { + ReportMessageAt(class_name_location, MessageTemplate::kStrictEvalArguments); + *ok = false; + return NULL; + } + if (is_strong(language_mode()) && IsUndefined(name)) { + ReportMessageAt(class_name_location, MessageTemplate::kStrongUndefined); + *ok = false; + return NULL; + } + + Scope* block_scope = NewScope(scope_, BLOCK_SCOPE); + BlockState block_state(&scope_, block_scope); + RaiseLanguageMode(STRICT); + scope_->SetScopeName(name); + + VariableProxy* proxy = NULL; + if (name != NULL) { + proxy = NewUnresolved(name, CONST); + const bool is_class_declaration = true; + Declaration* declaration = factory()->NewVariableDeclaration( + proxy, CONST, block_scope, pos, is_class_declaration, + scope_->class_declaration_group_start()); + Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK); + } + + Expression* extends = NULL; + if (Check(Token::EXTENDS)) { + block_scope->set_start_position(scanner()->location().end_pos); + ExpressionClassifier classifier; + extends = ParseLeftHandSideExpression(&classifier, CHECK_OK); + extends = ParserTraits::RewriteNonPattern(extends, &classifier, CHECK_OK); + } else { + block_scope->set_start_position(scanner()->location().end_pos); + } + + + ClassLiteralChecker checker(this); + ZoneList<ObjectLiteral::Property*>* properties = NewPropertyList(4, zone()); + FunctionLiteral* constructor = NULL; + bool has_seen_constructor = false; + + Expect(Token::LBRACE, CHECK_OK); + + const bool has_extends = extends != nullptr; + while (peek() != Token::RBRACE) { + if (Check(Token::SEMICOLON)) continue; + FuncNameInferrer::State fni_state(fni_); + const bool in_class = true; + const bool is_static = false; + bool is_computed_name = false; // Classes do not care about computed + // property names here. + ExpressionClassifier classifier; + const AstRawString* name = nullptr; + ObjectLiteral::Property* property = ParsePropertyDefinition( + &checker, in_class, has_extends, is_static, &is_computed_name, + &has_seen_constructor, &classifier, &name, CHECK_OK); + property = ParserTraits::RewriteNonPatternObjectLiteralProperty( + property, &classifier, CHECK_OK); + + if (has_seen_constructor && constructor == NULL) { + constructor = GetPropertyValue(property)->AsFunctionLiteral(); + DCHECK_NOT_NULL(constructor); + } else { + properties->Add(property, zone()); + } + + if (fni_ != NULL) fni_->Infer(); + + if (allow_harmony_function_name()) { + SetFunctionNameFromPropertyName(property, name); + } + } + + Expect(Token::RBRACE, CHECK_OK); + int end_pos = scanner()->location().end_pos; + + if (constructor == NULL) { + constructor = DefaultConstructor(extends != NULL, block_scope, pos, end_pos, + block_scope->language_mode()); + } + + // Note that we do not finalize this block scope because strong + // mode uses it as a sentinel value indicating an anonymous class. + block_scope->set_end_position(end_pos); + + if (name != NULL) { + DCHECK_NOT_NULL(proxy); + proxy->var()->set_initializer_position(end_pos); + } + + return factory()->NewClassLiteral(name, block_scope, proxy, extends, + constructor, properties, pos, end_pos); +} + + +Expression* Parser::ParseV8Intrinsic(bool* ok) { + // CallRuntime :: + // '%' Identifier Arguments + + int pos = peek_position(); + Expect(Token::MOD, CHECK_OK); + // Allow "eval" or "arguments" for backward compatibility. + const AstRawString* name = ParseIdentifier(kAllowRestrictedIdentifiers, + CHECK_OK); + Scanner::Location spread_pos; + ExpressionClassifier classifier; + ZoneList<Expression*>* args = + ParseArguments(&spread_pos, &classifier, CHECK_OK); + args = RewriteNonPatternArguments(args, &classifier, CHECK_OK); + + DCHECK(!spread_pos.IsValid()); + + if (extension_ != NULL) { + // The extension structures are only accessible while parsing the + // very first time not when reparsing because of lazy compilation. + scope_->DeclarationScope()->ForceEagerCompilation(); + } + + const Runtime::Function* function = Runtime::FunctionForName(name->string()); + + if (function != NULL) { + // Check for possible name clash. + DCHECK_EQ(Context::kNotFound, + Context::IntrinsicIndexForName(name->string())); + // Check for built-in IS_VAR macro. + if (function->function_id == Runtime::kIS_VAR) { + DCHECK_EQ(Runtime::RUNTIME, function->intrinsic_type); + // %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(MessageTemplate::kNotIsvar); + *ok = false; + return NULL; + } + } + + // Check that the expected number of arguments are being passed. + if (function->nargs != -1 && function->nargs != args->length()) { + ReportMessage(MessageTemplate::kIllegalAccess); + *ok = false; + return NULL; + } + + return factory()->NewCallRuntime(function, args, pos); + } + + int context_index = Context::IntrinsicIndexForName(name->string()); + + // Check that the function is defined. + if (context_index == Context::kNotFound) { + ParserTraits::ReportMessage(MessageTemplate::kNotDefined, name); + *ok = false; + return NULL; + } + + return factory()->NewCallRuntime(context_index, args, pos); +} + + +Literal* Parser::GetLiteralUndefined(int position) { + return factory()->NewUndefinedLiteral(position); +} + + +void Parser::CheckConflictingVarDeclarations(Scope* scope, bool* ok) { + Declaration* decl = scope->CheckConflictingVarDeclarations(); + if (decl != NULL) { + // In ES6, conflicting variable bindings are early errors. + const AstRawString* name = decl->proxy()->raw_name(); + int position = decl->proxy()->position(); + Scanner::Location location = position == RelocInfo::kNoPosition + ? Scanner::Location::invalid() + : Scanner::Location(position, position + 1); + ParserTraits::ReportMessageAt(location, MessageTemplate::kVarRedeclaration, + name); + *ok = false; + } +} + + +void Parser::InsertShadowingVarBindingInitializers(Block* inner_block) { + // For each var-binding that shadows a parameter, insert an assignment + // initializing the variable with the parameter. + Scope* inner_scope = inner_block->scope(); + DCHECK(inner_scope->is_declaration_scope()); + Scope* function_scope = inner_scope->outer_scope(); + DCHECK(function_scope->is_function_scope()); + ZoneList<Declaration*>* decls = inner_scope->declarations(); + for (int i = 0; i < decls->length(); ++i) { + Declaration* decl = decls->at(i); + if (decl->mode() != VAR || !decl->IsVariableDeclaration()) continue; + const AstRawString* name = decl->proxy()->raw_name(); + Variable* parameter = function_scope->LookupLocal(name); + if (parameter == nullptr) continue; + VariableProxy* to = inner_scope->NewUnresolved(factory(), name); + VariableProxy* from = factory()->NewVariableProxy(parameter); + Expression* assignment = factory()->NewAssignment( + Token::ASSIGN, to, from, RelocInfo::kNoPosition); + Statement* statement = factory()->NewExpressionStatement( + assignment, RelocInfo::kNoPosition); + inner_block->statements()->InsertAt(0, statement, zone()); + } +} + + +void Parser::InsertSloppyBlockFunctionVarBindings(Scope* scope, bool* ok) { + // For each variable which is used as a function declaration in a sloppy + // block, + DCHECK(scope->is_declaration_scope()); + SloppyBlockFunctionMap* map = scope->sloppy_block_function_map(); + for (ZoneHashMap::Entry* p = map->Start(); p != nullptr; p = map->Next(p)) { + AstRawString* name = static_cast<AstRawString*>(p->key); + // If the variable wouldn't conflict with a lexical declaration, + Variable* var = scope->LookupLocal(name); + if (var == nullptr || !IsLexicalVariableMode(var->mode())) { + // Declare a var-style binding for the function in the outer scope + VariableProxy* proxy = scope->NewUnresolved(factory(), name); + Declaration* declaration = factory()->NewVariableDeclaration( + proxy, VAR, scope, RelocInfo::kNoPosition); + Declare(declaration, DeclarationDescriptor::NORMAL, true, ok, scope); + DCHECK(ok); // Based on the preceding check, this should not fail + if (!ok) return; + + // Write in assignments to var for each block-scoped function declaration + auto delegates = static_cast<SloppyBlockFunctionMap::Vector*>(p->value); + for (SloppyBlockFunctionStatement* delegate : *delegates) { + // Read from the local lexical scope and write to the function scope + VariableProxy* to = scope->NewUnresolved(factory(), name); + VariableProxy* from = delegate->scope()->NewUnresolved(factory(), name); + Expression* assignment = factory()->NewAssignment( + Token::ASSIGN, to, from, RelocInfo::kNoPosition); + Statement* statement = factory()->NewExpressionStatement( + assignment, RelocInfo::kNoPosition); + delegate->set_statement(statement); + } + } + } +} + + +// ---------------------------------------------------------------------------- +// Parser support + +bool Parser::TargetStackContainsLabel(const AstRawString* label) { + for (Target* t = target_stack_; t != NULL; t = t->previous()) { + if (ContainsLabel(t->statement()->labels(), label)) return true; + } + return false; +} + + +BreakableStatement* Parser::LookupBreakTarget(const AstRawString* label, + bool* ok) { + bool anonymous = label == NULL; + for (Target* t = target_stack_; t != NULL; t = t->previous()) { + BreakableStatement* stat = t->statement(); + if ((anonymous && stat->is_target_for_anonymous()) || + (!anonymous && ContainsLabel(stat->labels(), label))) { + return stat; + } + } + return NULL; +} + + +IterationStatement* Parser::LookupContinueTarget(const AstRawString* label, + bool* ok) { + bool anonymous = label == NULL; + for (Target* t = target_stack_; t != NULL; t = t->previous()) { + IterationStatement* stat = t->statement()->AsIterationStatement(); + if (stat == NULL) continue; + + DCHECK(stat->is_target_for_anonymous()); + if (anonymous || ContainsLabel(stat->labels(), label)) { + return stat; + } + } + return NULL; +} + + +void Parser::HandleSourceURLComments(Isolate* isolate, Handle<Script> script) { + if (scanner_.source_url()->length() > 0) { + Handle<String> source_url = scanner_.source_url()->Internalize(isolate); + script->set_source_url(*source_url); + } + if (scanner_.source_mapping_url()->length() > 0) { + Handle<String> source_mapping_url = + scanner_.source_mapping_url()->Internalize(isolate); + script->set_source_mapping_url(*source_mapping_url); + } +} + + +void Parser::Internalize(Isolate* isolate, Handle<Script> script, bool error) { + // Internalize strings. + ast_value_factory()->Internalize(isolate); + + // Error processing. + if (error) { + if (stack_overflow()) { + isolate->StackOverflow(); + } else { + DCHECK(pending_error_handler_.has_pending_error()); + pending_error_handler_.ThrowPendingError(isolate, script); + } + } + + // Move statistics to Isolate. + for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount; + ++feature) { + for (int i = 0; i < use_counts_[feature]; ++i) { + isolate->CountUsage(v8::Isolate::UseCounterFeature(feature)); + } + } + isolate->counters()->total_preparse_skipped()->Increment( + total_preparse_skipped_); +} + + +// ---------------------------------------------------------------------------- +// The Parser interface. + + +bool Parser::ParseStatic(ParseInfo* info) { + Parser parser(info); + if (parser.Parse(info)) { + info->set_language_mode(info->literal()->language_mode()); + return true; + } + return false; +} + + +bool Parser::Parse(ParseInfo* info) { + DCHECK(info->literal() == NULL); + FunctionLiteral* result = NULL; + // Ok to use Isolate here; this function is only called in the main thread. + DCHECK(parsing_on_main_thread_); + Isolate* isolate = info->isolate(); + pre_parse_timer_ = isolate->counters()->pre_parse(); + if (FLAG_trace_parse || allow_natives() || extension_ != NULL) { + // If intrinsics are allowed, the Parser cannot operate independent of the + // V8 heap because of Runtime. Tell the string table to internalize strings + // and values right after they're created. + ast_value_factory()->Internalize(isolate); + } + + if (info->is_lazy()) { + DCHECK(!info->is_eval()); + if (info->shared_info()->is_function()) { + result = ParseLazy(isolate, info); + } else { + result = ParseProgram(isolate, info); + } + } else { + SetCachedData(info); + result = ParseProgram(isolate, info); + } + info->set_literal(result); + + Internalize(isolate, info->script(), result == NULL); + DCHECK(ast_value_factory()->IsInternalized()); + return (result != NULL); +} + + +void Parser::ParseOnBackground(ParseInfo* info) { + parsing_on_main_thread_ = false; + + DCHECK(info->literal() == NULL); + FunctionLiteral* result = NULL; + fni_ = new (zone()) FuncNameInferrer(ast_value_factory(), zone()); + + CompleteParserRecorder recorder; + if (produce_cached_parse_data()) log_ = &recorder; + + DCHECK(info->source_stream() != NULL); + ExternalStreamingStream stream(info->source_stream(), + info->source_stream_encoding()); + scanner_.Initialize(&stream); + DCHECK(info->context().is_null() || info->context()->IsNativeContext()); + + // When streaming, we don't know the length of the source until we have parsed + // it. The raw data can be UTF-8, so we wouldn't know the source length until + // we have decoded it anyway even if we knew the raw data length (which we + // don't). We work around this by storing all the scopes which need their end + // position set at the end of the script (the top scope and possible eval + // scopes) and set their end position after we know the script length. + result = DoParseProgram(info); + + info->set_literal(result); + + // We cannot internalize on a background thread; a foreground task will take + // care of calling Parser::Internalize just before compilation. + + if (produce_cached_parse_data()) { + if (result != NULL) *info->cached_data() = recorder.GetScriptData(); + log_ = NULL; + } +} + + +ParserTraits::TemplateLiteralState Parser::OpenTemplateLiteral(int pos) { + return new (zone()) ParserTraits::TemplateLiteral(zone(), pos); +} + + +void Parser::AddTemplateSpan(TemplateLiteralState* state, bool tail) { + int pos = scanner()->location().beg_pos; + int end = scanner()->location().end_pos - (tail ? 1 : 2); + const AstRawString* tv = scanner()->CurrentSymbol(ast_value_factory()); + const AstRawString* trv = scanner()->CurrentRawSymbol(ast_value_factory()); + Literal* cooked = factory()->NewStringLiteral(tv, pos); + Literal* raw = factory()->NewStringLiteral(trv, pos); + (*state)->AddTemplateSpan(cooked, raw, end, zone()); +} + + +void Parser::AddTemplateExpression(TemplateLiteralState* state, + Expression* expression) { + (*state)->AddExpression(expression, zone()); +} + + +Expression* Parser::CloseTemplateLiteral(TemplateLiteralState* state, int start, + Expression* tag) { + TemplateLiteral* lit = *state; + int pos = lit->position(); + const ZoneList<Expression*>* cooked_strings = lit->cooked(); + const ZoneList<Expression*>* raw_strings = lit->raw(); + const ZoneList<Expression*>* expressions = lit->expressions(); + DCHECK_EQ(cooked_strings->length(), raw_strings->length()); + DCHECK_EQ(cooked_strings->length(), expressions->length() + 1); + + if (!tag) { + // Build tree of BinaryOps to simplify code-generation + Expression* expr = cooked_strings->at(0); + int i = 0; + while (i < expressions->length()) { + Expression* sub = expressions->at(i++); + Expression* cooked_str = cooked_strings->at(i); + + // Let middle be ToString(sub). + ZoneList<Expression*>* args = + new (zone()) ZoneList<Expression*>(1, zone()); + args->Add(sub, zone()); + Expression* middle = factory()->NewCallRuntime(Runtime::kInlineToString, + args, sub->position()); + + expr = factory()->NewBinaryOperation( + Token::ADD, factory()->NewBinaryOperation( + Token::ADD, expr, middle, expr->position()), + cooked_str, sub->position()); + } + return expr; + } else { + uint32_t hash = ComputeTemplateLiteralHash(lit); + + int cooked_idx = function_state_->NextMaterializedLiteralIndex(); + int raw_idx = function_state_->NextMaterializedLiteralIndex(); + + // $getTemplateCallSite + ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(4, zone()); + args->Add(factory()->NewArrayLiteral( + const_cast<ZoneList<Expression*>*>(cooked_strings), + cooked_idx, is_strong(language_mode()), pos), + zone()); + args->Add( + factory()->NewArrayLiteral( + const_cast<ZoneList<Expression*>*>(raw_strings), raw_idx, + is_strong(language_mode()), pos), + zone()); + + // Ensure hash is suitable as a Smi value + Smi* hash_obj = Smi::cast(Internals::IntToSmi(static_cast<int>(hash))); + args->Add(factory()->NewSmiLiteral(hash_obj->value(), pos), zone()); + + Expression* call_site = factory()->NewCallRuntime( + Context::GET_TEMPLATE_CALL_SITE_INDEX, args, start); + + // Call TagFn + ZoneList<Expression*>* call_args = + new (zone()) ZoneList<Expression*>(expressions->length() + 1, zone()); + call_args->Add(call_site, zone()); + call_args->AddAll(*expressions, zone()); + return factory()->NewCall(tag, call_args, pos); + } +} + + +uint32_t Parser::ComputeTemplateLiteralHash(const TemplateLiteral* lit) { + const ZoneList<Expression*>* raw_strings = lit->raw(); + int total = raw_strings->length(); + DCHECK(total); + + uint32_t running_hash = 0; + + for (int index = 0; index < total; ++index) { + if (index) { + running_hash = StringHasher::ComputeRunningHashOneByte( + running_hash, "${}", 3); + } + + const AstRawString* raw_string = + raw_strings->at(index)->AsLiteral()->raw_value()->AsString(); + if (raw_string->is_one_byte()) { + const char* data = reinterpret_cast<const char*>(raw_string->raw_data()); + running_hash = StringHasher::ComputeRunningHashOneByte( + running_hash, data, raw_string->length()); + } else { + const uc16* data = reinterpret_cast<const uc16*>(raw_string->raw_data()); + running_hash = StringHasher::ComputeRunningHash(running_hash, data, + raw_string->length()); + } + } + + return running_hash; +} + + +ZoneList<v8::internal::Expression*>* Parser::PrepareSpreadArguments( + ZoneList<v8::internal::Expression*>* list) { + ZoneList<v8::internal::Expression*>* args = + new (zone()) ZoneList<v8::internal::Expression*>(1, zone()); + if (list->length() == 1) { + // Spread-call with single spread argument produces an InternalArray + // containing the values from the array. + // + // Function is called or constructed with the produced array of arguments + // + // EG: Apply(Func, Spread(spread0)) + ZoneList<Expression*>* spread_list = + new (zone()) ZoneList<Expression*>(0, zone()); + spread_list->Add(list->at(0)->AsSpread()->expression(), zone()); + args->Add(factory()->NewCallRuntime(Context::SPREAD_ITERABLE_INDEX, + spread_list, RelocInfo::kNoPosition), + zone()); + return args; + } else { + // Spread-call with multiple arguments produces array literals for each + // sequences of unspread arguments, and converts each spread iterable to + // an Internal array. Finally, all of these produced arrays are flattened + // into a single InternalArray, containing the arguments for the call. + // + // EG: Apply(Func, Flatten([unspread0, unspread1], Spread(spread0), + // Spread(spread1), [unspread2, unspread3])) + int i = 0; + int n = list->length(); + while (i < n) { + if (!list->at(i)->IsSpread()) { + ZoneList<v8::internal::Expression*>* unspread = + new (zone()) ZoneList<v8::internal::Expression*>(1, zone()); + + // Push array of unspread parameters + while (i < n && !list->at(i)->IsSpread()) { + unspread->Add(list->at(i++), zone()); + } + int literal_index = function_state_->NextMaterializedLiteralIndex(); + args->Add(factory()->NewArrayLiteral(unspread, literal_index, + is_strong(language_mode()), + RelocInfo::kNoPosition), + zone()); + + if (i == n) break; + } + + // Push eagerly spread argument + ZoneList<v8::internal::Expression*>* spread_list = + new (zone()) ZoneList<v8::internal::Expression*>(1, zone()); + spread_list->Add(list->at(i++)->AsSpread()->expression(), zone()); + args->Add(factory()->NewCallRuntime(Context::SPREAD_ITERABLE_INDEX, + spread_list, RelocInfo::kNoPosition), + zone()); + } + + list = new (zone()) ZoneList<v8::internal::Expression*>(1, zone()); + list->Add(factory()->NewCallRuntime(Context::SPREAD_ARGUMENTS_INDEX, args, + RelocInfo::kNoPosition), + zone()); + return list; + } + UNREACHABLE(); +} + + +Expression* Parser::SpreadCall(Expression* function, + ZoneList<v8::internal::Expression*>* args, + int pos) { + if (function->IsSuperCallReference()) { + // Super calls + // $super_constructor = %_GetSuperConstructor(<this-function>) + // %reflect_construct($super_constructor, args, new.target) + ZoneList<Expression*>* tmp = new (zone()) ZoneList<Expression*>(1, zone()); + tmp->Add(function->AsSuperCallReference()->this_function_var(), zone()); + Expression* super_constructor = factory()->NewCallRuntime( + Runtime::kInlineGetSuperConstructor, tmp, pos); + args->InsertAt(0, super_constructor, zone()); + args->Add(function->AsSuperCallReference()->new_target_var(), zone()); + return factory()->NewCallRuntime(Context::REFLECT_CONSTRUCT_INDEX, args, + pos); + } else { + if (function->IsProperty()) { + // Method calls + if (function->AsProperty()->IsSuperAccess()) { + Expression* home = + ThisExpression(scope_, factory(), RelocInfo::kNoPosition); + args->InsertAt(0, function, zone()); + args->InsertAt(1, home, zone()); + } else { + Variable* temp = + scope_->NewTemporary(ast_value_factory()->empty_string()); + VariableProxy* obj = factory()->NewVariableProxy(temp); + Assignment* assign_obj = factory()->NewAssignment( + Token::ASSIGN, obj, function->AsProperty()->obj(), + RelocInfo::kNoPosition); + function = factory()->NewProperty( + assign_obj, function->AsProperty()->key(), RelocInfo::kNoPosition); + args->InsertAt(0, function, zone()); + obj = factory()->NewVariableProxy(temp); + args->InsertAt(1, obj, zone()); + } + } else { + // Non-method calls + args->InsertAt(0, function, zone()); + args->InsertAt(1, factory()->NewUndefinedLiteral(RelocInfo::kNoPosition), + zone()); + } + return factory()->NewCallRuntime(Context::REFLECT_APPLY_INDEX, args, pos); + } +} + + +Expression* Parser::SpreadCallNew(Expression* function, + ZoneList<v8::internal::Expression*>* args, + int pos) { + args->InsertAt(0, function, zone()); + + return factory()->NewCallRuntime(Context::REFLECT_CONSTRUCT_INDEX, args, pos); +} + + +void Parser::SetLanguageMode(Scope* scope, LanguageMode mode) { + v8::Isolate::UseCounterFeature feature; + if (is_sloppy(mode)) + feature = v8::Isolate::kSloppyMode; + else if (is_strong(mode)) + feature = v8::Isolate::kStrongMode; + else if (is_strict(mode)) + feature = v8::Isolate::kStrictMode; + else + UNREACHABLE(); + ++use_counts_[feature]; + scope->SetLanguageMode(mode); +} + + +void Parser::RaiseLanguageMode(LanguageMode mode) { + SetLanguageMode(scope_, + static_cast<LanguageMode>(scope_->language_mode() | mode)); +} + + +void ParserTraits::RewriteDestructuringAssignments() { + parser_->RewriteDestructuringAssignments(); +} + + +Expression* ParserTraits::RewriteNonPattern( + Expression* expr, const ExpressionClassifier* classifier, bool* ok) { + return parser_->RewriteNonPattern(expr, classifier, ok); +} + + +ZoneList<Expression*>* ParserTraits::RewriteNonPatternArguments( + ZoneList<Expression*>* args, const ExpressionClassifier* classifier, + bool* ok) { + return parser_->RewriteNonPatternArguments(args, classifier, ok); +} + + +ObjectLiteralProperty* ParserTraits::RewriteNonPatternObjectLiteralProperty( + ObjectLiteralProperty* property, const ExpressionClassifier* classifier, + bool* ok) { + return parser_->RewriteNonPatternObjectLiteralProperty(property, classifier, + ok); +} + + +Expression* Parser::RewriteNonPattern(Expression* expr, + const ExpressionClassifier* classifier, + bool* ok) { + // For the time being, this does no rewriting at all. + ValidateExpression(classifier, ok); + return expr; +} + + +ZoneList<Expression*>* Parser::RewriteNonPatternArguments( + ZoneList<Expression*>* args, const ExpressionClassifier* classifier, + bool* ok) { + // For the time being, this does no rewriting at all. + ValidateExpression(classifier, ok); + return args; +} + + +ObjectLiteralProperty* Parser::RewriteNonPatternObjectLiteralProperty( + ObjectLiteralProperty* property, const ExpressionClassifier* classifier, + bool* ok) { + if (property != nullptr) { + Expression* key = RewriteNonPattern(property->key(), classifier, ok); + property->set_key(key); + Expression* value = RewriteNonPattern(property->value(), classifier, ok); + property->set_value(value); + } + return property; +} + + +void Parser::RewriteDestructuringAssignments() { + FunctionState* func = function_state_; + if (!allow_harmony_destructuring_assignment()) return; + const List<DestructuringAssignment>& assignments = + func->destructuring_assignments_to_rewrite(); + for (int i = assignments.length() - 1; i >= 0; --i) { + // Rewrite list in reverse, so that nested assignment patterns are rewritten + // correctly. + DestructuringAssignment pair = assignments.at(i); + RewritableAssignmentExpression* to_rewrite = + pair.assignment->AsRewritableAssignmentExpression(); + Scope* scope = pair.scope; + DCHECK_NOT_NULL(to_rewrite); + if (!to_rewrite->is_rewritten()) { + PatternRewriter::RewriteDestructuringAssignment(this, to_rewrite, scope); + } + } +} + + +void ParserTraits::QueueDestructuringAssignmentForRewriting(Expression* expr) { + DCHECK(expr->IsRewritableAssignmentExpression()); + parser_->function_state_->AddDestructuringAssignment( + Parser::DestructuringAssignment(expr, parser_->scope_)); +} + + +void ParserTraits::SetFunctionNameFromPropertyName( + ObjectLiteralProperty* property, const AstRawString* name) { + Expression* value = property->value(); + if (!value->IsFunctionLiteral() && !value->IsClassLiteral()) return; + + // TODO(adamk): Support computed names. + if (property->is_computed_name()) return; + DCHECK_NOT_NULL(name); + + // Ignore "__proto__" as a name when it's being used to set the [[Prototype]] + // of an object literal. + if (property->kind() == ObjectLiteralProperty::PROTOTYPE) return; + + if (value->IsFunctionLiteral()) { + auto function = value->AsFunctionLiteral(); + if (function->is_anonymous()) { + if (property->kind() == ObjectLiteralProperty::GETTER) { + function->set_raw_name(parser_->ast_value_factory()->NewConsString( + parser_->ast_value_factory()->get_space_string(), name)); + } else if (property->kind() == ObjectLiteralProperty::SETTER) { + function->set_raw_name(parser_->ast_value_factory()->NewConsString( + parser_->ast_value_factory()->set_space_string(), name)); + } else { + function->set_raw_name(name); + DCHECK_EQ(ObjectLiteralProperty::COMPUTED, property->kind()); + } + } + } else { + DCHECK(value->IsClassLiteral()); + DCHECK_EQ(ObjectLiteralProperty::COMPUTED, property->kind()); + auto class_literal = value->AsClassLiteral(); + if (class_literal->raw_name() == nullptr) { + class_literal->set_raw_name(name); + } + } +} + + +void ParserTraits::SetFunctionNameFromIdentifierRef(Expression* value, + Expression* identifier) { + if (!value->IsFunctionLiteral() && !value->IsClassLiteral()) return; + if (!identifier->IsVariableProxy()) return; + + auto name = identifier->AsVariableProxy()->raw_name(); + DCHECK_NOT_NULL(name); + + if (value->IsFunctionLiteral()) { + auto function = value->AsFunctionLiteral(); + if (function->is_anonymous()) { + function->set_raw_name(name); + } + } else { + DCHECK(value->IsClassLiteral()); + auto class_literal = value->AsClassLiteral(); + if (class_literal->raw_name() == nullptr) { + class_literal->set_raw_name(name); + } + } +} + + +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/parsing/parser.h b/deps/v8/src/parsing/parser.h new file mode 100644 index 0000000000..7d50221334 --- /dev/null +++ b/deps/v8/src/parsing/parser.h @@ -0,0 +1,1220 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_PARSING_PARSER_H_ +#define V8_PARSING_PARSER_H_ + +#include "src/allocation.h" +#include "src/ast/ast.h" +#include "src/ast/scopes.h" +#include "src/compiler.h" // TODO(titzer): remove this include dependency +#include "src/parsing/parser-base.h" +#include "src/parsing/preparse-data.h" +#include "src/parsing/preparse-data-format.h" +#include "src/parsing/preparser.h" +#include "src/pending-compilation-error-handler.h" + +namespace v8 { + +class ScriptCompiler; + +namespace internal { + +class Target; + +// A container for the inputs, configuration options, and outputs of parsing. +class ParseInfo { + public: + explicit ParseInfo(Zone* zone); + ParseInfo(Zone* zone, Handle<JSFunction> function); + ParseInfo(Zone* zone, Handle<Script> script); + // TODO(all) Only used via Debug::FindSharedFunctionInfoInScript, remove? + ParseInfo(Zone* zone, Handle<SharedFunctionInfo> shared); + + ~ParseInfo() { + if (ast_value_factory_owned()) { + delete ast_value_factory_; + set_ast_value_factory_owned(false); + } + ast_value_factory_ = nullptr; + } + + Zone* zone() { return zone_; } + +// Convenience accessor methods for flags. +#define FLAG_ACCESSOR(flag, getter, setter) \ + bool getter() const { return GetFlag(flag); } \ + void setter() { SetFlag(flag); } \ + void setter(bool val) { SetFlag(flag, val); } + + FLAG_ACCESSOR(kToplevel, is_toplevel, set_toplevel) + FLAG_ACCESSOR(kLazy, is_lazy, set_lazy) + FLAG_ACCESSOR(kEval, is_eval, set_eval) + FLAG_ACCESSOR(kGlobal, is_global, set_global) + FLAG_ACCESSOR(kStrictMode, is_strict_mode, set_strict_mode) + FLAG_ACCESSOR(kStrongMode, is_strong_mode, set_strong_mode) + FLAG_ACCESSOR(kNative, is_native, set_native) + FLAG_ACCESSOR(kModule, is_module, set_module) + FLAG_ACCESSOR(kAllowLazyParsing, allow_lazy_parsing, set_allow_lazy_parsing) + FLAG_ACCESSOR(kAstValueFactoryOwned, ast_value_factory_owned, + set_ast_value_factory_owned) + +#undef FLAG_ACCESSOR + + void set_parse_restriction(ParseRestriction restriction) { + SetFlag(kParseRestriction, restriction != NO_PARSE_RESTRICTION); + } + + ParseRestriction parse_restriction() const { + return GetFlag(kParseRestriction) ? ONLY_SINGLE_FUNCTION_LITERAL + : NO_PARSE_RESTRICTION; + } + + ScriptCompiler::ExternalSourceStream* source_stream() { + return source_stream_; + } + void set_source_stream(ScriptCompiler::ExternalSourceStream* source_stream) { + source_stream_ = source_stream; + } + + ScriptCompiler::StreamedSource::Encoding source_stream_encoding() { + return source_stream_encoding_; + } + void set_source_stream_encoding( + ScriptCompiler::StreamedSource::Encoding source_stream_encoding) { + source_stream_encoding_ = source_stream_encoding; + } + + v8::Extension* extension() { return extension_; } + void set_extension(v8::Extension* extension) { extension_ = extension; } + + ScriptData** cached_data() { return cached_data_; } + void set_cached_data(ScriptData** cached_data) { cached_data_ = cached_data; } + + ScriptCompiler::CompileOptions compile_options() { return compile_options_; } + void set_compile_options(ScriptCompiler::CompileOptions compile_options) { + compile_options_ = compile_options; + } + + Scope* script_scope() { return script_scope_; } + void set_script_scope(Scope* script_scope) { script_scope_ = script_scope; } + + AstValueFactory* ast_value_factory() { return ast_value_factory_; } + void set_ast_value_factory(AstValueFactory* ast_value_factory) { + ast_value_factory_ = ast_value_factory; + } + + FunctionLiteral* literal() { return literal_; } + void set_literal(FunctionLiteral* literal) { literal_ = literal; } + + Scope* scope() { return scope_; } + void set_scope(Scope* scope) { scope_ = scope; } + + UnicodeCache* unicode_cache() { return unicode_cache_; } + void set_unicode_cache(UnicodeCache* unicode_cache) { + unicode_cache_ = unicode_cache; + } + + uintptr_t stack_limit() { return stack_limit_; } + void set_stack_limit(uintptr_t stack_limit) { stack_limit_ = stack_limit; } + + uint32_t hash_seed() { return hash_seed_; } + void set_hash_seed(uint32_t hash_seed) { hash_seed_ = hash_seed; } + + //-------------------------------------------------------------------------- + // TODO(titzer): these should not be part of ParseInfo. + //-------------------------------------------------------------------------- + Isolate* isolate() { return isolate_; } + Handle<JSFunction> closure() { return closure_; } + Handle<SharedFunctionInfo> shared_info() { return shared_; } + Handle<Script> script() { return script_; } + Handle<Context> context() { return context_; } + void clear_script() { script_ = Handle<Script>::null(); } + void set_isolate(Isolate* isolate) { isolate_ = isolate; } + void set_context(Handle<Context> context) { context_ = context; } + void set_script(Handle<Script> script) { script_ = script; } + //-------------------------------------------------------------------------- + + LanguageMode language_mode() { + return construct_language_mode(is_strict_mode(), is_strong_mode()); + } + void set_language_mode(LanguageMode language_mode) { + STATIC_ASSERT(LANGUAGE_END == 3); + set_strict_mode(language_mode & STRICT_BIT); + set_strong_mode(language_mode & STRONG_BIT); + } + + void ReopenHandlesInNewHandleScope() { + closure_ = Handle<JSFunction>(*closure_); + shared_ = Handle<SharedFunctionInfo>(*shared_); + script_ = Handle<Script>(*script_); + context_ = Handle<Context>(*context_); + } + +#ifdef DEBUG + bool script_is_native() { return script_->type() == Script::TYPE_NATIVE; } +#endif // DEBUG + + private: + // Various configuration flags for parsing. + enum Flag { + // ---------- Input flags --------------------------- + kToplevel = 1 << 0, + kLazy = 1 << 1, + kEval = 1 << 2, + kGlobal = 1 << 3, + kStrictMode = 1 << 4, + kStrongMode = 1 << 5, + kNative = 1 << 6, + kParseRestriction = 1 << 7, + kModule = 1 << 8, + kAllowLazyParsing = 1 << 9, + // ---------- Output flags -------------------------- + kAstValueFactoryOwned = 1 << 10 + }; + + //------------- Inputs to parsing and scope analysis ----------------------- + Zone* zone_; + unsigned flags_; + ScriptCompiler::ExternalSourceStream* source_stream_; + ScriptCompiler::StreamedSource::Encoding source_stream_encoding_; + v8::Extension* extension_; + ScriptCompiler::CompileOptions compile_options_; + Scope* script_scope_; + UnicodeCache* unicode_cache_; + uintptr_t stack_limit_; + uint32_t hash_seed_; + + // TODO(titzer): Move handles and isolate out of ParseInfo. + Isolate* isolate_; + Handle<JSFunction> closure_; + Handle<SharedFunctionInfo> shared_; + Handle<Script> script_; + Handle<Context> context_; + + //----------- Inputs+Outputs of parsing and scope analysis ----------------- + ScriptData** cached_data_; // used if available, populated if requested. + AstValueFactory* ast_value_factory_; // used if available, otherwise new. + + //----------- Outputs of parsing and scope analysis ------------------------ + FunctionLiteral* literal_; // produced by full parser. + Scope* scope_; // produced by scope analysis. + + void SetFlag(Flag f) { flags_ |= f; } + void SetFlag(Flag f, bool v) { flags_ = v ? flags_ | f : flags_ & ~f; } + bool GetFlag(Flag f) const { return (flags_ & f) != 0; } + + void set_shared_info(Handle<SharedFunctionInfo> shared) { shared_ = shared; } + void set_closure(Handle<JSFunction> closure) { closure_ = closure; } +}; + +class FunctionEntry BASE_EMBEDDED { + public: + enum { + kStartPositionIndex, + kEndPositionIndex, + kLiteralCountIndex, + kPropertyCountIndex, + kLanguageModeIndex, + kUsesSuperPropertyIndex, + kCallsEvalIndex, + kSize + }; + + explicit FunctionEntry(Vector<unsigned> backing) + : backing_(backing) { } + + FunctionEntry() : backing_() { } + + int start_pos() { return backing_[kStartPositionIndex]; } + int end_pos() { return backing_[kEndPositionIndex]; } + int literal_count() { return backing_[kLiteralCountIndex]; } + int property_count() { return backing_[kPropertyCountIndex]; } + LanguageMode language_mode() { + DCHECK(is_valid_language_mode(backing_[kLanguageModeIndex])); + return static_cast<LanguageMode>(backing_[kLanguageModeIndex]); + } + bool uses_super_property() { return backing_[kUsesSuperPropertyIndex]; } + bool calls_eval() { return backing_[kCallsEvalIndex]; } + + bool is_valid() { return !backing_.is_empty(); } + + private: + Vector<unsigned> backing_; +}; + + +// Wrapper around ScriptData to provide parser-specific functionality. +class ParseData { + public: + static ParseData* FromCachedData(ScriptData* cached_data) { + ParseData* pd = new ParseData(cached_data); + if (pd->IsSane()) return pd; + cached_data->Reject(); + delete pd; + return NULL; + } + + void Initialize(); + FunctionEntry GetFunctionEntry(int start); + int FunctionCount(); + + bool HasError(); + + unsigned* Data() { // Writable data as unsigned int array. + return reinterpret_cast<unsigned*>(const_cast<byte*>(script_data_->data())); + } + + void Reject() { script_data_->Reject(); } + + bool rejected() const { return script_data_->rejected(); } + + private: + explicit ParseData(ScriptData* script_data) : script_data_(script_data) {} + + bool IsSane(); + unsigned Magic(); + unsigned Version(); + int FunctionsSize(); + int Length() const { + // Script data length is already checked to be a multiple of unsigned size. + return script_data_->length() / sizeof(unsigned); + } + + ScriptData* script_data_; + int function_index_; + + DISALLOW_COPY_AND_ASSIGN(ParseData); +}; + +// ---------------------------------------------------------------------------- +// JAVASCRIPT PARSING + +class Parser; +class SingletonLogger; + + +struct ParserFormalParameters : FormalParametersBase { + struct Parameter { + Parameter(const AstRawString* name, Expression* pattern, + Expression* initializer, int initializer_end_position, + bool is_rest) + : name(name), + pattern(pattern), + initializer(initializer), + initializer_end_position(initializer_end_position), + is_rest(is_rest) {} + const AstRawString* name; + Expression* pattern; + Expression* initializer; + int initializer_end_position; + bool is_rest; + bool is_simple() const { + return pattern->IsVariableProxy() && initializer == nullptr && !is_rest; + } + }; + + explicit ParserFormalParameters(Scope* scope) + : FormalParametersBase(scope), params(4, scope->zone()) {} + ZoneList<Parameter> params; + + int Arity() const { return params.length(); } + const Parameter& at(int i) const { return params[i]; } +}; + + +class ParserTraits { + public: + struct Type { + // TODO(marja): To be removed. The Traits object should contain all the data + // it needs. + typedef v8::internal::Parser* Parser; + + typedef Variable GeneratorVariable; + + typedef v8::internal::AstProperties AstProperties; + + // Return types for traversing functions. + typedef const AstRawString* Identifier; + typedef v8::internal::Expression* Expression; + typedef Yield* YieldExpression; + typedef v8::internal::FunctionLiteral* FunctionLiteral; + typedef v8::internal::ClassLiteral* ClassLiteral; + typedef v8::internal::Literal* Literal; + typedef ObjectLiteral::Property* ObjectLiteralProperty; + typedef ZoneList<v8::internal::Expression*>* ExpressionList; + typedef ZoneList<ObjectLiteral::Property*>* PropertyList; + typedef ParserFormalParameters::Parameter FormalParameter; + typedef ParserFormalParameters FormalParameters; + typedef ZoneList<v8::internal::Statement*>* StatementList; + + // For constructing objects returned by the traversing functions. + typedef AstNodeFactory Factory; + }; + + explicit ParserTraits(Parser* parser) : parser_(parser) {} + + // Helper functions for recursive descent. + bool IsEval(const AstRawString* identifier) const; + bool IsArguments(const AstRawString* identifier) const; + bool IsEvalOrArguments(const AstRawString* identifier) const; + bool IsUndefined(const AstRawString* identifier) const; + V8_INLINE bool IsFutureStrictReserved(const AstRawString* identifier) const; + + // Returns true if the expression is of type "this.foo". + static bool IsThisProperty(Expression* expression); + + static bool IsIdentifier(Expression* expression); + + bool IsPrototype(const AstRawString* identifier) const; + + bool IsConstructor(const AstRawString* identifier) const; + + static const AstRawString* AsIdentifier(Expression* expression) { + DCHECK(IsIdentifier(expression)); + return expression->AsVariableProxy()->raw_name(); + } + + static bool IsBoilerplateProperty(ObjectLiteral::Property* property) { + return ObjectLiteral::IsBoilerplateProperty(property); + } + + static bool IsArrayIndex(const AstRawString* string, uint32_t* index) { + return string->AsArrayIndex(index); + } + + static Expression* GetPropertyValue(ObjectLiteral::Property* property) { + return property->value(); + } + + // Functions for encapsulating the differences between parsing and preparsing; + // operations interleaved with the recursive descent. + static void PushLiteralName(FuncNameInferrer* fni, const AstRawString* id) { + fni->PushLiteralName(id); + } + + void PushPropertyName(FuncNameInferrer* fni, Expression* expression); + + static void InferFunctionName(FuncNameInferrer* fni, + FunctionLiteral* func_to_infer) { + fni->AddFunction(func_to_infer); + } + + static void CheckFunctionLiteralInsideTopLevelObjectLiteral( + Scope* scope, ObjectLiteralProperty* property, bool* has_function) { + Expression* value = property->value(); + if (scope->DeclarationScope()->is_script_scope() && + value->AsFunctionLiteral() != NULL) { + *has_function = true; + value->AsFunctionLiteral()->set_pretenure(); + } + } + + // If we assign a function literal to a property we pretenure the + // literal so it can be added as a constant function property. + static void CheckAssigningFunctionLiteralToProperty(Expression* left, + Expression* right); + + // Determine if the expression is a variable proxy and mark it as being used + // in an assignment or with a increment/decrement operator. + static Expression* MarkExpressionAsAssigned(Expression* expression); + + // Returns true if we have a binary expression between two numeric + // literals. In that case, *x will be changed to an expression which is the + // computed value. + bool ShortcutNumericLiteralBinaryExpression(Expression** x, Expression* y, + Token::Value op, int pos, + AstNodeFactory* factory); + + // Rewrites the following types of unary expressions: + // not <literal> -> true / false + // + <numeric literal> -> <numeric literal> + // - <numeric literal> -> <numeric literal with value negated> + // ! <literal> -> true / false + // The following rewriting rules enable the collection of type feedback + // without any special stub and the multiplication is removed later in + // Crankshaft's canonicalization pass. + // + foo -> foo * 1 + // - foo -> foo * (-1) + // ~ foo -> foo ^(~0) + Expression* BuildUnaryExpression(Expression* expression, Token::Value op, + int pos, AstNodeFactory* factory); + + // Generate AST node that throws a ReferenceError with the given type. + Expression* NewThrowReferenceError(MessageTemplate::Template message, + int pos); + + // Generate AST node that throws a SyntaxError with the given + // type. The first argument may be null (in the handle sense) in + // which case no arguments are passed to the constructor. + Expression* NewThrowSyntaxError(MessageTemplate::Template message, + const AstRawString* arg, int pos); + + // Generate AST node that throws a TypeError with the given + // type. Both arguments must be non-null (in the handle sense). + Expression* NewThrowTypeError(MessageTemplate::Template message, + const AstRawString* arg, int pos); + + // Generic AST generator for throwing errors from compiled code. + Expression* NewThrowError(Runtime::FunctionId function_id, + MessageTemplate::Template message, + const AstRawString* arg, int pos); + + // Reporting errors. + void ReportMessageAt(Scanner::Location source_location, + MessageTemplate::Template message, + const char* arg = NULL, + ParseErrorType error_type = kSyntaxError); + void ReportMessage(MessageTemplate::Template message, const char* arg = NULL, + ParseErrorType error_type = kSyntaxError); + void ReportMessage(MessageTemplate::Template message, const AstRawString* arg, + ParseErrorType error_type = kSyntaxError); + void ReportMessageAt(Scanner::Location source_location, + MessageTemplate::Template message, + const AstRawString* arg, + ParseErrorType error_type = kSyntaxError); + + // "null" return type creators. + static const AstRawString* EmptyIdentifier() { + return NULL; + } + static Expression* EmptyExpression() { + return NULL; + } + static Literal* EmptyLiteral() { + return NULL; + } + static ObjectLiteralProperty* EmptyObjectLiteralProperty() { return NULL; } + static FunctionLiteral* EmptyFunctionLiteral() { return NULL; } + + // Used in error return values. + static ZoneList<Expression*>* NullExpressionList() { + return NULL; + } + static const AstRawString* EmptyFormalParameter() { return NULL; } + + // Non-NULL empty string. + V8_INLINE const AstRawString* EmptyIdentifierString(); + + // Odd-ball literal creators. + Literal* GetLiteralTheHole(int position, AstNodeFactory* factory); + + // Producing data during the recursive descent. + const AstRawString* GetSymbol(Scanner* scanner); + const AstRawString* GetNextSymbol(Scanner* scanner); + const AstRawString* GetNumberAsSymbol(Scanner* scanner); + + Expression* ThisExpression(Scope* scope, AstNodeFactory* factory, + int pos = RelocInfo::kNoPosition); + Expression* SuperPropertyReference(Scope* scope, AstNodeFactory* factory, + int pos); + Expression* SuperCallReference(Scope* scope, AstNodeFactory* factory, + int pos); + Expression* NewTargetExpression(Scope* scope, AstNodeFactory* factory, + int pos); + Expression* DefaultConstructor(bool call_super, Scope* scope, int pos, + int end_pos, LanguageMode language_mode); + Literal* ExpressionFromLiteral(Token::Value token, int pos, Scanner* scanner, + AstNodeFactory* factory); + Expression* ExpressionFromIdentifier(const AstRawString* name, + int start_position, int end_position, + Scope* scope, AstNodeFactory* factory); + Expression* ExpressionFromString(int pos, Scanner* scanner, + AstNodeFactory* factory); + Expression* GetIterator(Expression* iterable, AstNodeFactory* factory, + int pos); + ZoneList<v8::internal::Expression*>* NewExpressionList(int size, Zone* zone) { + return new(zone) ZoneList<v8::internal::Expression*>(size, zone); + } + ZoneList<ObjectLiteral::Property*>* NewPropertyList(int size, Zone* zone) { + return new(zone) ZoneList<ObjectLiteral::Property*>(size, zone); + } + ZoneList<v8::internal::Statement*>* NewStatementList(int size, Zone* zone) { + return new(zone) ZoneList<v8::internal::Statement*>(size, zone); + } + + V8_INLINE void AddParameterInitializationBlock( + const ParserFormalParameters& parameters, + ZoneList<v8::internal::Statement*>* body, bool* ok); + + V8_INLINE Scope* NewScope(Scope* parent_scope, ScopeType scope_type, + FunctionKind kind = kNormalFunction); + + V8_INLINE void AddFormalParameter(ParserFormalParameters* parameters, + Expression* pattern, + Expression* initializer, + int initializer_end_position, bool is_rest); + V8_INLINE void DeclareFormalParameter( + Scope* scope, const ParserFormalParameters::Parameter& parameter, + ExpressionClassifier* classifier); + void ParseArrowFunctionFormalParameters(ParserFormalParameters* parameters, + Expression* params, + const Scanner::Location& params_loc, + bool* ok); + void ParseArrowFunctionFormalParameterList( + ParserFormalParameters* parameters, Expression* params, + const Scanner::Location& params_loc, + Scanner::Location* duplicate_loc, bool* ok); + + V8_INLINE DoExpression* ParseDoExpression(bool* ok); + + void ReindexLiterals(const ParserFormalParameters& parameters); + + // Temporary glue; these functions will move to ParserBase. + Expression* ParseV8Intrinsic(bool* ok); + FunctionLiteral* ParseFunctionLiteral( + const AstRawString* name, Scanner::Location function_name_location, + FunctionNameValidity function_name_validity, FunctionKind kind, + int function_token_position, FunctionLiteral::FunctionType type, + FunctionLiteral::ArityRestriction arity_restriction, + LanguageMode language_mode, bool* ok); + V8_INLINE void SkipLazyFunctionBody( + int* materialized_literal_count, int* expected_property_count, bool* ok, + Scanner::BookmarkScope* bookmark = nullptr); + V8_INLINE ZoneList<Statement*>* ParseEagerFunctionBody( + const AstRawString* name, int pos, + const ParserFormalParameters& parameters, FunctionKind kind, + FunctionLiteral::FunctionType function_type, bool* ok); + + ClassLiteral* ParseClassLiteral(const AstRawString* name, + Scanner::Location class_name_location, + bool name_is_strict_reserved, int pos, + bool* ok); + + V8_INLINE void CheckConflictingVarDeclarations(v8::internal::Scope* scope, + bool* ok); + + class TemplateLiteral : public ZoneObject { + public: + TemplateLiteral(Zone* zone, int pos) + : cooked_(8, zone), raw_(8, zone), expressions_(8, zone), pos_(pos) {} + + const ZoneList<Expression*>* cooked() const { return &cooked_; } + const ZoneList<Expression*>* raw() const { return &raw_; } + const ZoneList<Expression*>* expressions() const { return &expressions_; } + int position() const { return pos_; } + + void AddTemplateSpan(Literal* cooked, Literal* raw, int end, Zone* zone) { + DCHECK_NOT_NULL(cooked); + DCHECK_NOT_NULL(raw); + cooked_.Add(cooked, zone); + raw_.Add(raw, zone); + } + + void AddExpression(Expression* expression, Zone* zone) { + DCHECK_NOT_NULL(expression); + expressions_.Add(expression, zone); + } + + private: + ZoneList<Expression*> cooked_; + ZoneList<Expression*> raw_; + ZoneList<Expression*> expressions_; + int pos_; + }; + + typedef TemplateLiteral* TemplateLiteralState; + + V8_INLINE TemplateLiteralState OpenTemplateLiteral(int pos); + V8_INLINE void AddTemplateSpan(TemplateLiteralState* state, bool tail); + V8_INLINE void AddTemplateExpression(TemplateLiteralState* state, + Expression* expression); + V8_INLINE Expression* CloseTemplateLiteral(TemplateLiteralState* state, + int start, Expression* tag); + V8_INLINE Expression* NoTemplateTag() { return NULL; } + V8_INLINE static bool IsTaggedTemplate(const Expression* tag) { + return tag != NULL; + } + + V8_INLINE ZoneList<v8::internal::Expression*>* PrepareSpreadArguments( + ZoneList<v8::internal::Expression*>* list); + V8_INLINE void MaterializeUnspreadArgumentsLiterals(int count) {} + V8_INLINE Expression* SpreadCall(Expression* function, + ZoneList<v8::internal::Expression*>* args, + int pos); + V8_INLINE Expression* SpreadCallNew(Expression* function, + ZoneList<v8::internal::Expression*>* args, + int pos); + + // Rewrite all DestructuringAssignments in the current FunctionState. + V8_INLINE void RewriteDestructuringAssignments(); + + V8_INLINE void QueueDestructuringAssignmentForRewriting( + Expression* assignment); + + void SetFunctionNameFromPropertyName(ObjectLiteralProperty* property, + const AstRawString* name); + + void SetFunctionNameFromIdentifierRef(Expression* value, + Expression* identifier); + + // Rewrite expressions that are not used as patterns + V8_INLINE Expression* RewriteNonPattern( + Expression* expr, const ExpressionClassifier* classifier, bool* ok); + V8_INLINE ZoneList<Expression*>* RewriteNonPatternArguments( + ZoneList<Expression*>* args, const ExpressionClassifier* classifier, + bool* ok); + V8_INLINE ObjectLiteralProperty* RewriteNonPatternObjectLiteralProperty( + ObjectLiteralProperty* property, const ExpressionClassifier* classifier, + bool* ok); + + private: + Parser* parser_; +}; + + +class Parser : public ParserBase<ParserTraits> { + public: + explicit Parser(ParseInfo* info); + ~Parser() { + delete reusable_preparser_; + reusable_preparser_ = NULL; + delete cached_parse_data_; + cached_parse_data_ = NULL; + } + + // Parses the source code represented by the compilation info and sets its + // function literal. Returns false (and deallocates any allocated AST + // nodes) if parsing failed. + static bool ParseStatic(ParseInfo* info); + bool Parse(ParseInfo* info); + void ParseOnBackground(ParseInfo* info); + + // Handle errors detected during parsing, move statistics to Isolate, + // internalize strings (move them to the heap). + void Internalize(Isolate* isolate, Handle<Script> script, bool error); + void HandleSourceURLComments(Isolate* isolate, Handle<Script> script); + + private: + friend class ParserTraits; + + // Limit the allowed number of local variables in a function. The hard limit + // is that offsets computed by FullCodeGenerator::StackOperand and similar + // functions are ints, and they should not overflow. In addition, accessing + // local variables creates user-controlled constants in the generated code, + // and we don't want too much user-controlled memory inside the code (this was + // the reason why this limit was introduced in the first place; see + // https://codereview.chromium.org/7003030/ ). + static const int kMaxNumFunctionLocals = 4194303; // 2^22-1 + + // Returns NULL if parsing failed. + FunctionLiteral* ParseProgram(Isolate* isolate, ParseInfo* info); + + FunctionLiteral* ParseLazy(Isolate* isolate, ParseInfo* info); + FunctionLiteral* ParseLazy(Isolate* isolate, ParseInfo* info, + Utf16CharacterStream* source); + + // Called by ParseProgram after setting up the scanner. + FunctionLiteral* DoParseProgram(ParseInfo* info); + + void SetCachedData(ParseInfo* info); + + ScriptCompiler::CompileOptions compile_options() const { + return compile_options_; + } + bool consume_cached_parse_data() const { + return compile_options_ == ScriptCompiler::kConsumeParserCache && + cached_parse_data_ != NULL; + } + bool produce_cached_parse_data() const { + return compile_options_ == ScriptCompiler::kProduceParserCache; + } + + // All ParseXXX functions take as the last argument an *ok parameter + // which is set to false if parsing failed; it is unchanged otherwise. + // By making the 'exception handling' explicit, we are forced to check + // for failure at the call sites. + void* ParseStatementList(ZoneList<Statement*>* body, int end_token, bool* ok); + Statement* ParseStatementListItem(bool* ok); + void* ParseModuleItemList(ZoneList<Statement*>* body, bool* ok); + Statement* ParseModuleItem(bool* ok); + const AstRawString* ParseModuleSpecifier(bool* ok); + Statement* ParseImportDeclaration(bool* ok); + Statement* ParseExportDeclaration(bool* ok); + Statement* ParseExportDefault(bool* ok); + void* ParseExportClause(ZoneList<const AstRawString*>* export_names, + ZoneList<Scanner::Location>* export_locations, + ZoneList<const AstRawString*>* local_names, + Scanner::Location* reserved_loc, bool* ok); + ZoneList<ImportDeclaration*>* ParseNamedImports(int pos, bool* ok); + Statement* ParseStatement(ZoneList<const AstRawString*>* labels, bool* ok); + Statement* ParseSubStatement(ZoneList<const AstRawString*>* labels, bool* ok); + Statement* ParseStatementAsUnlabelled(ZoneList<const AstRawString*>* labels, + bool* ok); + Statement* ParseFunctionDeclaration(ZoneList<const AstRawString*>* names, + bool* ok); + Statement* ParseClassDeclaration(ZoneList<const AstRawString*>* names, + bool* ok); + Statement* ParseNativeDeclaration(bool* ok); + Block* ParseBlock(ZoneList<const AstRawString*>* labels, bool* ok); + Block* ParseBlock(ZoneList<const AstRawString*>* labels, + bool finalize_block_scope, bool* ok); + Block* ParseVariableStatement(VariableDeclarationContext var_context, + ZoneList<const AstRawString*>* names, + bool* ok); + DoExpression* ParseDoExpression(bool* ok); + + struct DeclarationDescriptor { + enum Kind { NORMAL, PARAMETER }; + Parser* parser; + Scope* scope; + Scope* hoist_scope; + VariableMode mode; + bool needs_init; + int declaration_pos; + int initialization_pos; + Kind declaration_kind; + }; + + struct DeclarationParsingResult { + struct Declaration { + Declaration(Expression* pattern, int initializer_position, + Expression* initializer) + : pattern(pattern), + initializer_position(initializer_position), + initializer(initializer) {} + + Expression* pattern; + int initializer_position; + Expression* initializer; + }; + + DeclarationParsingResult() + : declarations(4), + first_initializer_loc(Scanner::Location::invalid()), + bindings_loc(Scanner::Location::invalid()) {} + + Block* BuildInitializationBlock(ZoneList<const AstRawString*>* names, + bool* ok); + + DeclarationDescriptor descriptor; + List<Declaration> declarations; + Scanner::Location first_initializer_loc; + Scanner::Location bindings_loc; + }; + + class PatternRewriter : private AstVisitor { + public: + static void DeclareAndInitializeVariables( + Block* block, const DeclarationDescriptor* declaration_descriptor, + const DeclarationParsingResult::Declaration* declaration, + ZoneList<const AstRawString*>* names, bool* ok); + + static void RewriteDestructuringAssignment( + Parser* parser, RewritableAssignmentExpression* expr, Scope* Scope); + + static Expression* RewriteDestructuringAssignment(Parser* parser, + Assignment* assignment, + Scope* scope); + + void set_initializer_position(int pos) { initializer_position_ = pos; } + + private: + PatternRewriter() {} + +#define DECLARE_VISIT(type) void Visit##type(v8::internal::type* node) override; + // Visiting functions for AST nodes make this an AstVisitor. + AST_NODE_LIST(DECLARE_VISIT) +#undef DECLARE_VISIT + void Visit(AstNode* node) override; + + enum PatternContext { + BINDING, + INITIALIZER, + ASSIGNMENT, + ASSIGNMENT_INITIALIZER + }; + + PatternContext context() const { return context_; } + void set_context(PatternContext context) { context_ = context; } + + void RecurseIntoSubpattern(AstNode* pattern, Expression* value) { + Expression* old_value = current_value_; + current_value_ = value; + recursion_level_++; + pattern->Accept(this); + recursion_level_--; + current_value_ = old_value; + } + + void VisitObjectLiteral(ObjectLiteral* node, Variable** temp_var); + void VisitArrayLiteral(ArrayLiteral* node, Variable** temp_var); + + bool IsBindingContext() const { return IsBindingContext(context_); } + bool IsInitializerContext() const { return context_ != ASSIGNMENT; } + bool IsAssignmentContext() const { return IsAssignmentContext(context_); } + bool IsAssignmentContext(PatternContext c) const; + bool IsBindingContext(PatternContext c) const; + bool IsSubPattern() const { return recursion_level_ > 1; } + PatternContext SetAssignmentContextIfNeeded(Expression* node); + PatternContext SetInitializerContextIfNeeded(Expression* node); + + Variable* CreateTempVar(Expression* value = nullptr); + + AstNodeFactory* factory() const { return parser_->factory(); } + AstValueFactory* ast_value_factory() const { + return parser_->ast_value_factory(); + } + Zone* zone() const { return parser_->zone(); } + Scope* scope() const { return scope_; } + + Scope* scope_; + Parser* parser_; + PatternContext context_; + Expression* pattern_; + int initializer_position_; + Block* block_; + const DeclarationDescriptor* descriptor_; + ZoneList<const AstRawString*>* names_; + Expression* current_value_; + int recursion_level_; + bool* ok_; + }; + + + void ParseVariableDeclarations(VariableDeclarationContext var_context, + DeclarationParsingResult* parsing_result, + bool* ok); + Statement* ParseExpressionOrLabelledStatement( + ZoneList<const AstRawString*>* labels, bool* ok); + IfStatement* ParseIfStatement(ZoneList<const AstRawString*>* labels, + bool* ok); + Statement* ParseContinueStatement(bool* ok); + Statement* ParseBreakStatement(ZoneList<const AstRawString*>* labels, + bool* ok); + Statement* ParseReturnStatement(bool* ok); + Statement* ParseWithStatement(ZoneList<const AstRawString*>* labels, + bool* ok); + CaseClause* ParseCaseClause(bool* default_seen_ptr, bool* ok); + Statement* ParseSwitchStatement(ZoneList<const AstRawString*>* labels, + bool* ok); + DoWhileStatement* ParseDoWhileStatement(ZoneList<const AstRawString*>* labels, + bool* ok); + WhileStatement* ParseWhileStatement(ZoneList<const AstRawString*>* labels, + bool* ok); + Statement* ParseForStatement(ZoneList<const AstRawString*>* labels, bool* ok); + Statement* ParseThrowStatement(bool* ok); + Expression* MakeCatchContext(Handle<String> id, VariableProxy* value); + TryStatement* ParseTryStatement(bool* ok); + DebuggerStatement* ParseDebuggerStatement(bool* ok); + + // !%_IsJSReceiver(result = iterator.next()) && + // %ThrowIteratorResultNotAnObject(result) + Expression* BuildIteratorNextResult(Expression* iterator, Variable* result, + int pos); + + + // Initialize the components of a for-in / for-of statement. + void InitializeForEachStatement(ForEachStatement* stmt, Expression* each, + Expression* subject, Statement* body, + bool is_destructuring); + Statement* DesugarLexicalBindingsInForStatement( + Scope* inner_scope, bool is_const, ZoneList<const AstRawString*>* names, + ForStatement* loop, Statement* init, Expression* cond, Statement* next, + Statement* body, bool* ok); + + void RewriteDoExpression(Expression* expr, bool* ok); + + FunctionLiteral* ParseFunctionLiteral( + const AstRawString* name, Scanner::Location function_name_location, + FunctionNameValidity function_name_validity, FunctionKind kind, + int function_token_position, FunctionLiteral::FunctionType type, + FunctionLiteral::ArityRestriction arity_restriction, + LanguageMode language_mode, bool* ok); + + + ClassLiteral* ParseClassLiteral(const AstRawString* name, + Scanner::Location class_name_location, + bool name_is_strict_reserved, int pos, + bool* ok); + + // Magical syntax support. + Expression* ParseV8Intrinsic(bool* ok); + + // Get odd-ball literals. + Literal* GetLiteralUndefined(int position); + + // Check if the scope has conflicting var/let declarations from different + // scopes. This covers for example + // + // function f() { { { var x; } let x; } } + // function g() { { var x; let x; } } + // + // The var declarations are hoisted to the function scope, but originate from + // a scope where the name has also been let bound or the var declaration is + // hoisted over such a scope. + void CheckConflictingVarDeclarations(Scope* scope, bool* ok); + + // Insert initializer statements for var-bindings shadowing parameter bindings + // from a non-simple parameter list. + void InsertShadowingVarBindingInitializers(Block* block); + + // Implement sloppy block-scoped functions, ES2015 Annex B 3.3 + void InsertSloppyBlockFunctionVarBindings(Scope* scope, bool* ok); + + // Parser support + VariableProxy* NewUnresolved(const AstRawString* name, VariableMode mode); + Variable* Declare(Declaration* declaration, + DeclarationDescriptor::Kind declaration_kind, bool resolve, + bool* ok, Scope* declaration_scope = nullptr); + + bool TargetStackContainsLabel(const AstRawString* label); + BreakableStatement* LookupBreakTarget(const AstRawString* label, bool* ok); + IterationStatement* LookupContinueTarget(const AstRawString* label, bool* ok); + + Statement* BuildAssertIsCoercible(Variable* var); + + // Factory methods. + FunctionLiteral* DefaultConstructor(bool call_super, Scope* scope, int pos, + int end_pos, LanguageMode language_mode); + + // Skip over a lazy function, either using cached data if we have it, or + // by parsing the function with PreParser. Consumes the ending }. + // + // If bookmark is set, the (pre-)parser may decide to abort skipping + // in order to force the function to be eagerly parsed, after all. + // In this case, it'll reset the scanner using the bookmark. + void SkipLazyFunctionBody(int* materialized_literal_count, + int* expected_property_count, bool* ok, + Scanner::BookmarkScope* bookmark = nullptr); + + PreParser::PreParseResult ParseLazyFunctionBodyWithPreParser( + SingletonLogger* logger, Scanner::BookmarkScope* bookmark = nullptr); + + Block* BuildParameterInitializationBlock( + const ParserFormalParameters& parameters, bool* ok); + + // Consumes the ending }. + ZoneList<Statement*>* ParseEagerFunctionBody( + const AstRawString* function_name, int pos, + const ParserFormalParameters& parameters, FunctionKind kind, + FunctionLiteral::FunctionType function_type, bool* ok); + + void ThrowPendingError(Isolate* isolate, Handle<Script> script); + + TemplateLiteralState OpenTemplateLiteral(int pos); + void AddTemplateSpan(TemplateLiteralState* state, bool tail); + void AddTemplateExpression(TemplateLiteralState* state, + Expression* expression); + Expression* CloseTemplateLiteral(TemplateLiteralState* state, int start, + Expression* tag); + uint32_t ComputeTemplateLiteralHash(const TemplateLiteral* lit); + + ZoneList<v8::internal::Expression*>* PrepareSpreadArguments( + ZoneList<v8::internal::Expression*>* list); + Expression* SpreadCall(Expression* function, + ZoneList<v8::internal::Expression*>* args, int pos); + Expression* SpreadCallNew(Expression* function, + ZoneList<v8::internal::Expression*>* args, int pos); + + void SetLanguageMode(Scope* scope, LanguageMode mode); + void RaiseLanguageMode(LanguageMode mode); + + V8_INLINE void RewriteDestructuringAssignments(); + + V8_INLINE Expression* RewriteNonPattern( + Expression* expr, const ExpressionClassifier* classifier, bool* ok); + V8_INLINE ZoneList<Expression*>* RewriteNonPatternArguments( + ZoneList<Expression*>* args, const ExpressionClassifier* classifier, + bool* ok); + V8_INLINE ObjectLiteralProperty* RewriteNonPatternObjectLiteralProperty( + ObjectLiteralProperty* property, const ExpressionClassifier* classifier, + bool* ok); + + friend class InitializerRewriter; + void RewriteParameterInitializer(Expression* expr, Scope* scope); + + Scanner scanner_; + PreParser* reusable_preparser_; + Scope* original_scope_; // for ES5 function declarations in sloppy eval + Target* target_stack_; // for break, continue statements + ScriptCompiler::CompileOptions compile_options_; + ParseData* cached_parse_data_; + + PendingCompilationErrorHandler pending_error_handler_; + + // Other information which will be stored in Parser and moved to Isolate after + // parsing. + int use_counts_[v8::Isolate::kUseCounterFeatureCount]; + int total_preparse_skipped_; + HistogramTimer* pre_parse_timer_; + + bool parsing_on_main_thread_; +}; + + +bool ParserTraits::IsFutureStrictReserved( + const AstRawString* identifier) const { + return parser_->scanner()->IdentifierIsFutureStrictReserved(identifier); +} + + +Scope* ParserTraits::NewScope(Scope* parent_scope, ScopeType scope_type, + FunctionKind kind) { + return parser_->NewScope(parent_scope, scope_type, kind); +} + + +const AstRawString* ParserTraits::EmptyIdentifierString() { + return parser_->ast_value_factory()->empty_string(); +} + + +void ParserTraits::SkipLazyFunctionBody(int* materialized_literal_count, + int* expected_property_count, bool* ok, + Scanner::BookmarkScope* bookmark) { + return parser_->SkipLazyFunctionBody(materialized_literal_count, + expected_property_count, ok, bookmark); +} + + +ZoneList<Statement*>* ParserTraits::ParseEagerFunctionBody( + const AstRawString* name, int pos, const ParserFormalParameters& parameters, + FunctionKind kind, FunctionLiteral::FunctionType function_type, bool* ok) { + return parser_->ParseEagerFunctionBody(name, pos, parameters, kind, + function_type, ok); +} + + +void ParserTraits::CheckConflictingVarDeclarations(v8::internal::Scope* scope, + bool* ok) { + parser_->CheckConflictingVarDeclarations(scope, ok); +} + + +// Support for handling complex values (array and object literals) that +// can be fully handled at compile time. +class CompileTimeValue: public AllStatic { + public: + enum LiteralType { + OBJECT_LITERAL_FAST_ELEMENTS, + OBJECT_LITERAL_SLOW_ELEMENTS, + ARRAY_LITERAL + }; + + static bool IsCompileTimeValue(Expression* expression); + + // Get the value as a compile time value. + static Handle<FixedArray> GetValue(Isolate* isolate, Expression* expression); + + // Get the type of a compile time value returned by GetValue(). + static LiteralType GetLiteralType(Handle<FixedArray> value); + + // Get the elements array of a compile time value returned by GetValue(). + static Handle<FixedArray> GetElements(Handle<FixedArray> value); + + private: + static const int kLiteralTypeSlot = 0; + static const int kElementsSlot = 1; + + DISALLOW_IMPLICIT_CONSTRUCTORS(CompileTimeValue); +}; + + +ParserTraits::TemplateLiteralState ParserTraits::OpenTemplateLiteral(int pos) { + return parser_->OpenTemplateLiteral(pos); +} + + +void ParserTraits::AddTemplateSpan(TemplateLiteralState* state, bool tail) { + parser_->AddTemplateSpan(state, tail); +} + + +void ParserTraits::AddTemplateExpression(TemplateLiteralState* state, + Expression* expression) { + parser_->AddTemplateExpression(state, expression); +} + + +Expression* ParserTraits::CloseTemplateLiteral(TemplateLiteralState* state, + int start, Expression* tag) { + return parser_->CloseTemplateLiteral(state, start, tag); +} + + +ZoneList<v8::internal::Expression*>* ParserTraits::PrepareSpreadArguments( + ZoneList<v8::internal::Expression*>* list) { + return parser_->PrepareSpreadArguments(list); +} + + +Expression* ParserTraits::SpreadCall(Expression* function, + ZoneList<v8::internal::Expression*>* args, + int pos) { + return parser_->SpreadCall(function, args, pos); +} + + +Expression* ParserTraits::SpreadCallNew( + Expression* function, ZoneList<v8::internal::Expression*>* args, int pos) { + return parser_->SpreadCallNew(function, args, pos); +} + + +void ParserTraits::AddFormalParameter(ParserFormalParameters* parameters, + Expression* pattern, + Expression* initializer, + int initializer_end_position, + bool is_rest) { + bool is_simple = pattern->IsVariableProxy() && initializer == nullptr; + const AstRawString* name = is_simple + ? pattern->AsVariableProxy()->raw_name() + : parser_->ast_value_factory()->empty_string(); + parameters->params.Add( + ParserFormalParameters::Parameter(name, pattern, initializer, + initializer_end_position, is_rest), + parameters->scope->zone()); +} + + +void ParserTraits::DeclareFormalParameter( + Scope* scope, const ParserFormalParameters::Parameter& parameter, + ExpressionClassifier* classifier) { + bool is_duplicate = false; + bool is_simple = classifier->is_simple_parameter_list(); + auto name = is_simple || parameter.is_rest + ? parameter.name + : parser_->ast_value_factory()->empty_string(); + auto mode = is_simple || parameter.is_rest ? VAR : TEMPORARY; + if (!is_simple) scope->SetHasNonSimpleParameters(); + bool is_optional = parameter.initializer != nullptr; + Variable* var = scope->DeclareParameter( + name, mode, is_optional, parameter.is_rest, &is_duplicate); + if (is_duplicate) { + classifier->RecordDuplicateFormalParameterError( + parser_->scanner()->location()); + } + if (is_sloppy(scope->language_mode())) { + // TODO(sigurds) Mark every parameter as maybe assigned. This is a + // conservative approximation necessary to account for parameters + // that are assigned via the arguments array. + var->set_maybe_assigned(); + } +} + + +void ParserTraits::AddParameterInitializationBlock( + const ParserFormalParameters& parameters, + ZoneList<v8::internal::Statement*>* body, bool* ok) { + if (!parameters.is_simple) { + auto* init_block = + parser_->BuildParameterInitializationBlock(parameters, ok); + if (!*ok) return; + if (init_block != nullptr) { + body->Add(init_block, parser_->zone()); + } + } +} + + +DoExpression* ParserTraits::ParseDoExpression(bool* ok) { + return parser_->ParseDoExpression(ok); +} + + +} // namespace internal +} // namespace v8 + +#endif // V8_PARSING_PARSER_H_ diff --git a/deps/v8/src/parsing/pattern-rewriter.cc b/deps/v8/src/parsing/pattern-rewriter.cc new file mode 100644 index 0000000000..6e20282785 --- /dev/null +++ b/deps/v8/src/parsing/pattern-rewriter.cc @@ -0,0 +1,628 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/ast/ast.h" +#include "src/messages.h" +#include "src/parsing/parameter-initializer-rewriter.h" +#include "src/parsing/parser.h" + +namespace v8 { + +namespace internal { + +void Parser::PatternRewriter::DeclareAndInitializeVariables( + Block* block, const DeclarationDescriptor* declaration_descriptor, + const DeclarationParsingResult::Declaration* declaration, + ZoneList<const AstRawString*>* names, bool* ok) { + PatternRewriter rewriter; + + rewriter.scope_ = declaration_descriptor->scope; + rewriter.parser_ = declaration_descriptor->parser; + rewriter.context_ = BINDING; + rewriter.pattern_ = declaration->pattern; + rewriter.initializer_position_ = declaration->initializer_position; + rewriter.block_ = block; + rewriter.descriptor_ = declaration_descriptor; + rewriter.names_ = names; + rewriter.ok_ = ok; + rewriter.recursion_level_ = 0; + + rewriter.RecurseIntoSubpattern(rewriter.pattern_, declaration->initializer); +} + + +void Parser::PatternRewriter::RewriteDestructuringAssignment( + Parser* parser, RewritableAssignmentExpression* to_rewrite, Scope* scope) { + PatternRewriter rewriter; + + DCHECK(!to_rewrite->is_rewritten()); + + bool ok = true; + rewriter.scope_ = scope; + rewriter.parser_ = parser; + rewriter.context_ = ASSIGNMENT; + rewriter.pattern_ = to_rewrite; + rewriter.block_ = nullptr; + rewriter.descriptor_ = nullptr; + rewriter.names_ = nullptr; + rewriter.ok_ = &ok; + rewriter.recursion_level_ = 0; + + rewriter.RecurseIntoSubpattern(rewriter.pattern_, nullptr); + DCHECK(ok); +} + + +Expression* Parser::PatternRewriter::RewriteDestructuringAssignment( + Parser* parser, Assignment* assignment, Scope* scope) { + DCHECK_NOT_NULL(assignment); + DCHECK_EQ(Token::ASSIGN, assignment->op()); + auto to_rewrite = + parser->factory()->NewRewritableAssignmentExpression(assignment); + RewriteDestructuringAssignment(parser, to_rewrite, scope); + return to_rewrite->expression(); +} + + +bool Parser::PatternRewriter::IsAssignmentContext(PatternContext c) const { + return c == ASSIGNMENT || c == ASSIGNMENT_INITIALIZER; +} + + +bool Parser::PatternRewriter::IsBindingContext(PatternContext c) const { + return c == BINDING || c == INITIALIZER; +} + + +Parser::PatternRewriter::PatternContext +Parser::PatternRewriter::SetAssignmentContextIfNeeded(Expression* node) { + PatternContext old_context = context(); + if (node->IsAssignment() && node->AsAssignment()->op() == Token::ASSIGN) { + set_context(ASSIGNMENT); + } + return old_context; +} + + +Parser::PatternRewriter::PatternContext +Parser::PatternRewriter::SetInitializerContextIfNeeded(Expression* node) { + // Set appropriate initializer context for BindingElement and + // AssignmentElement nodes + PatternContext old_context = context(); + bool is_destructuring_assignment = + node->IsRewritableAssignmentExpression() && + !node->AsRewritableAssignmentExpression()->is_rewritten(); + bool is_assignment = + node->IsAssignment() && node->AsAssignment()->op() == Token::ASSIGN; + if (is_destructuring_assignment || is_assignment) { + switch (old_context) { + case BINDING: + set_context(INITIALIZER); + break; + case ASSIGNMENT: + set_context(ASSIGNMENT_INITIALIZER); + break; + default: + break; + } + } + return old_context; +} + + +void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) { + Expression* value = current_value_; + + if (IsAssignmentContext()) { + // In an assignment context, simply perform the assignment + Assignment* assignment = factory()->NewAssignment( + Token::ASSIGN, pattern, value, pattern->position()); + block_->statements()->Add( + factory()->NewExpressionStatement(assignment, pattern->position()), + zone()); + return; + } + + descriptor_->scope->RemoveUnresolved(pattern); + + // 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 legacy const declaration, in an inner scope, the proxy + // is always bound to the declared variable (independent of possibly + // surrounding 'with' statements). + // For let/const declarations in harmony mode, we can also immediately + // pre-resolve the proxy because it resides in the same scope as the + // declaration. + const AstRawString* name = pattern->raw_name(); + VariableProxy* proxy = parser_->NewUnresolved(name, descriptor_->mode); + Declaration* declaration = factory()->NewVariableDeclaration( + proxy, descriptor_->mode, descriptor_->scope, + descriptor_->declaration_pos); + Variable* var = + parser_->Declare(declaration, descriptor_->declaration_kind, + descriptor_->mode != VAR, ok_, descriptor_->hoist_scope); + if (!*ok_) return; + DCHECK_NOT_NULL(var); + DCHECK(!proxy->is_resolved() || proxy->var() == var); + var->set_initializer_position(initializer_position_); + + DCHECK(initializer_position_ != RelocInfo::kNoPosition); + + Scope* declaration_scope = IsLexicalVariableMode(descriptor_->mode) + ? descriptor_->scope + : descriptor_->scope->DeclarationScope(); + if (declaration_scope->num_var_or_const() > kMaxNumFunctionLocals) { + parser_->ReportMessage(MessageTemplate::kTooManyVariables); + *ok_ = false; + return; + } + if (names_) { + names_->Add(name, zone()); + } + + // Initialize variables if 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' (in scope_, not + // declaration_scope) as it may be a different 'v' than the 'v' in the + // declaration (e.g., if we are inside a 'with' statement or 'catch' + // block). + // + // 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). + Scope* initialization_scope = IsImmutableVariableMode(descriptor_->mode) + ? declaration_scope + : descriptor_->scope; + + + // 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 (on the global object itself, see ES5 errata) 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 an own property. + // 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 (initialization_scope->is_script_scope() && + !IsLexicalVariableMode(descriptor_->mode)) { + // Compute the arguments for the runtime + // call.test-parsing/InitializedDeclarationsInStrictForOfError + ZoneList<Expression*>* arguments = + new (zone()) ZoneList<Expression*>(3, zone()); + // We have at least 1 parameter. + arguments->Add( + factory()->NewStringLiteral(name, descriptor_->declaration_pos), + zone()); + CallRuntime* initialize; + + if (IsImmutableVariableMode(descriptor_->mode)) { + arguments->Add(value, zone()); + 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 = + factory()->NewCallRuntime(Runtime::kInitializeConstGlobal, arguments, + descriptor_->initialization_pos); + } else { + // Add language mode. + // We may want to pass singleton to avoid Literal allocations. + LanguageMode language_mode = initialization_scope->language_mode(); + arguments->Add(factory()->NewNumberLiteral(language_mode, + descriptor_->declaration_pos), + zone()); + + // 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 && !descriptor_->scope->inside_with()) { + arguments->Add(value, zone()); + value = NULL; // zap the value to avoid the unnecessary assignment + // Construct the call to Runtime_InitializeVarGlobal + // and add it to the initialization statement block. + initialize = + factory()->NewCallRuntime(Runtime::kInitializeVarGlobal, arguments, + descriptor_->declaration_pos); + } else { + initialize = NULL; + } + } + + if (initialize != NULL) { + block_->statements()->Add( + factory()->NewExpressionStatement(initialize, RelocInfo::kNoPosition), + zone()); + } + } else if (value != nullptr && (descriptor_->mode == CONST_LEGACY || + IsLexicalVariableMode(descriptor_->mode))) { + // 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 var declared variables). Sigh... + // For 'let' and 'const' declared variables in harmony mode the + // initialization also always assigns to the declared variable. + DCHECK_NOT_NULL(proxy); + DCHECK_NOT_NULL(proxy->var()); + DCHECK_NOT_NULL(value); + // Add break location for destructured sub-pattern. + int pos = IsSubPattern() ? pattern->position() : RelocInfo::kNoPosition; + Assignment* assignment = + factory()->NewAssignment(Token::INIT, proxy, value, pos); + block_->statements()->Add( + factory()->NewExpressionStatement(assignment, pos), zone()); + value = NULL; + } + + // Add an assignment node to the initialization statement block if we still + // have a pending initialization value. + if (value != NULL) { + DCHECK(descriptor_->mode == VAR); + // 'var' initializations are simply assignments (with all the consequences + // if they are inside a 'with' statement - they may change a 'with' object + // property). + VariableProxy* proxy = initialization_scope->NewUnresolved(factory(), name); + // Add break location for destructured sub-pattern. + int pos = IsSubPattern() ? pattern->position() : RelocInfo::kNoPosition; + Assignment* assignment = + factory()->NewAssignment(Token::INIT, proxy, value, pos); + block_->statements()->Add( + factory()->NewExpressionStatement(assignment, pos), zone()); + } +} + + +Variable* Parser::PatternRewriter::CreateTempVar(Expression* value) { + auto temp = scope()->NewTemporary(ast_value_factory()->empty_string()); + if (value != nullptr) { + auto assignment = factory()->NewAssignment( + Token::ASSIGN, factory()->NewVariableProxy(temp), value, + RelocInfo::kNoPosition); + + block_->statements()->Add( + factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition), + zone()); + } + return temp; +} + + +void Parser::PatternRewriter::VisitRewritableAssignmentExpression( + RewritableAssignmentExpression* node) { + if (!IsAssignmentContext()) { + // Mark the assignment as rewritten to prevent redundant rewriting, and + // perform BindingPattern rewriting + DCHECK(!node->is_rewritten()); + node->Rewrite(node->expression()); + return node->expression()->Accept(this); + } + + if (node->is_rewritten()) return; + DCHECK(IsAssignmentContext()); + Assignment* assign = node->expression()->AsAssignment(); + DCHECK_NOT_NULL(assign); + DCHECK_EQ(Token::ASSIGN, assign->op()); + + auto initializer = assign->value(); + auto value = initializer; + + if (IsInitializerContext()) { + // let {<pattern> = <init>} = <value> + // becomes + // temp = <value>; + // <pattern> = temp === undefined ? <init> : temp; + auto temp_var = CreateTempVar(current_value_); + Expression* is_undefined = factory()->NewCompareOperation( + Token::EQ_STRICT, factory()->NewVariableProxy(temp_var), + factory()->NewUndefinedLiteral(RelocInfo::kNoPosition), + RelocInfo::kNoPosition); + value = factory()->NewConditional(is_undefined, initializer, + factory()->NewVariableProxy(temp_var), + RelocInfo::kNoPosition); + } + + PatternContext old_context = SetAssignmentContextIfNeeded(initializer); + int pos = assign->position(); + Block* old_block = block_; + block_ = factory()->NewBlock(nullptr, 8, false, pos); + Variable* temp = nullptr; + Expression* pattern = assign->target(); + Expression* old_value = current_value_; + current_value_ = value; + if (pattern->IsObjectLiteral()) { + VisitObjectLiteral(pattern->AsObjectLiteral(), &temp); + } else { + DCHECK(pattern->IsArrayLiteral()); + VisitArrayLiteral(pattern->AsArrayLiteral(), &temp); + } + DCHECK_NOT_NULL(temp); + current_value_ = old_value; + Expression* expr = factory()->NewDoExpression(block_, temp, pos); + node->Rewrite(expr); + block_ = old_block; + if (block_) { + block_->statements()->Add(factory()->NewExpressionStatement(expr, pos), + zone()); + } + return set_context(old_context); +} + + +void Parser::PatternRewriter::VisitObjectLiteral(ObjectLiteral* pattern, + Variable** temp_var) { + auto temp = *temp_var = CreateTempVar(current_value_); + + block_->statements()->Add(parser_->BuildAssertIsCoercible(temp), zone()); + + for (ObjectLiteralProperty* property : *pattern->properties()) { + PatternContext context = SetInitializerContextIfNeeded(property->value()); + RecurseIntoSubpattern( + property->value(), + factory()->NewProperty(factory()->NewVariableProxy(temp), + property->key(), RelocInfo::kNoPosition)); + set_context(context); + } +} + + +void Parser::PatternRewriter::VisitObjectLiteral(ObjectLiteral* node) { + Variable* temp_var = nullptr; + VisitObjectLiteral(node, &temp_var); +} + + +void Parser::PatternRewriter::VisitArrayLiteral(ArrayLiteral* node, + Variable** temp_var) { + auto temp = *temp_var = CreateTempVar(current_value_); + + block_->statements()->Add(parser_->BuildAssertIsCoercible(temp), zone()); + + auto iterator = CreateTempVar(parser_->GetIterator( + factory()->NewVariableProxy(temp), factory(), RelocInfo::kNoPosition)); + auto done = CreateTempVar( + factory()->NewBooleanLiteral(false, RelocInfo::kNoPosition)); + auto result = CreateTempVar(); + auto v = CreateTempVar(); + + Spread* spread = nullptr; + for (Expression* value : *node->values()) { + if (value->IsSpread()) { + spread = value->AsSpread(); + break; + } + + PatternContext context = SetInitializerContextIfNeeded(value); + // if (!done) { + // result = IteratorNext(iterator); + // v = (done = result.done) ? undefined : result.value; + // } + auto next_block = + factory()->NewBlock(nullptr, 2, true, RelocInfo::kNoPosition); + next_block->statements()->Add(factory()->NewExpressionStatement( + parser_->BuildIteratorNextResult( + factory()->NewVariableProxy(iterator), + result, RelocInfo::kNoPosition), + RelocInfo::kNoPosition), + zone()); + + auto assign_to_done = factory()->NewAssignment( + Token::ASSIGN, factory()->NewVariableProxy(done), + factory()->NewProperty( + factory()->NewVariableProxy(result), + factory()->NewStringLiteral(ast_value_factory()->done_string(), + RelocInfo::kNoPosition), + RelocInfo::kNoPosition), + RelocInfo::kNoPosition); + auto next_value = factory()->NewConditional( + assign_to_done, factory()->NewUndefinedLiteral(RelocInfo::kNoPosition), + factory()->NewProperty( + factory()->NewVariableProxy(result), + factory()->NewStringLiteral(ast_value_factory()->value_string(), + RelocInfo::kNoPosition), + RelocInfo::kNoPosition), + RelocInfo::kNoPosition); + next_block->statements()->Add( + factory()->NewExpressionStatement( + factory()->NewAssignment(Token::ASSIGN, + factory()->NewVariableProxy(v), next_value, + RelocInfo::kNoPosition), + RelocInfo::kNoPosition), + zone()); + + auto if_statement = factory()->NewIfStatement( + factory()->NewUnaryOperation(Token::NOT, + factory()->NewVariableProxy(done), + RelocInfo::kNoPosition), + next_block, factory()->NewEmptyStatement(RelocInfo::kNoPosition), + RelocInfo::kNoPosition); + block_->statements()->Add(if_statement, zone()); + + if (!(value->IsLiteral() && value->AsLiteral()->raw_value()->IsTheHole())) { + RecurseIntoSubpattern(value, factory()->NewVariableProxy(v)); + } + set_context(context); + } + + if (spread != nullptr) { + // array = []; + // if (!done) %concat_iterable_to_array(array, iterator); + auto empty_exprs = new (zone()) ZoneList<Expression*>(0, zone()); + auto array = CreateTempVar(factory()->NewArrayLiteral( + empty_exprs, + // Reuse pattern's literal index - it is unused since there is no + // actual literal allocated. + node->literal_index(), is_strong(scope()->language_mode()), + RelocInfo::kNoPosition)); + + auto arguments = new (zone()) ZoneList<Expression*>(2, zone()); + arguments->Add(factory()->NewVariableProxy(array), zone()); + arguments->Add(factory()->NewVariableProxy(iterator), zone()); + auto spread_into_array_call = + factory()->NewCallRuntime(Context::CONCAT_ITERABLE_TO_ARRAY_INDEX, + arguments, RelocInfo::kNoPosition); + + auto if_statement = factory()->NewIfStatement( + factory()->NewUnaryOperation(Token::NOT, + factory()->NewVariableProxy(done), + RelocInfo::kNoPosition), + factory()->NewExpressionStatement(spread_into_array_call, + RelocInfo::kNoPosition), + factory()->NewEmptyStatement(RelocInfo::kNoPosition), + RelocInfo::kNoPosition); + block_->statements()->Add(if_statement, zone()); + + RecurseIntoSubpattern(spread->expression(), + factory()->NewVariableProxy(array)); + } +} + + +void Parser::PatternRewriter::VisitArrayLiteral(ArrayLiteral* node) { + Variable* temp_var = nullptr; + VisitArrayLiteral(node, &temp_var); +} + + +void Parser::PatternRewriter::VisitAssignment(Assignment* node) { + // let {<pattern> = <init>} = <value> + // becomes + // temp = <value>; + // <pattern> = temp === undefined ? <init> : temp; + DCHECK_EQ(Token::ASSIGN, node->op()); + + auto initializer = node->value(); + auto value = initializer; + auto temp = CreateTempVar(current_value_); + + if (IsInitializerContext()) { + Expression* is_undefined = factory()->NewCompareOperation( + Token::EQ_STRICT, factory()->NewVariableProxy(temp), + factory()->NewUndefinedLiteral(RelocInfo::kNoPosition), + RelocInfo::kNoPosition); + value = factory()->NewConditional(is_undefined, initializer, + factory()->NewVariableProxy(temp), + RelocInfo::kNoPosition); + } + + if (IsBindingContext() && + descriptor_->declaration_kind == DeclarationDescriptor::PARAMETER && + scope()->is_arrow_scope()) { + RewriteParameterInitializerScope(parser_->stack_limit(), initializer, + scope()->outer_scope(), scope()); + } + + PatternContext old_context = SetAssignmentContextIfNeeded(initializer); + RecurseIntoSubpattern(node->target(), value); + set_context(old_context); +} + + +// =============== AssignmentPattern only ================== + +void Parser::PatternRewriter::VisitProperty(v8::internal::Property* node) { + DCHECK(IsAssignmentContext()); + auto value = current_value_; + + Assignment* assignment = + factory()->NewAssignment(Token::ASSIGN, node, value, node->position()); + + block_->statements()->Add( + factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition), + zone()); +} + + +// =============== UNREACHABLE ============================= + +void Parser::PatternRewriter::Visit(AstNode* node) { UNREACHABLE(); } + +#define NOT_A_PATTERN(Node) \ + void Parser::PatternRewriter::Visit##Node(v8::internal::Node*) { \ + UNREACHABLE(); \ + } + +NOT_A_PATTERN(BinaryOperation) +NOT_A_PATTERN(Block) +NOT_A_PATTERN(BreakStatement) +NOT_A_PATTERN(Call) +NOT_A_PATTERN(CallNew) +NOT_A_PATTERN(CallRuntime) +NOT_A_PATTERN(CaseClause) +NOT_A_PATTERN(ClassLiteral) +NOT_A_PATTERN(CompareOperation) +NOT_A_PATTERN(Conditional) +NOT_A_PATTERN(ContinueStatement) +NOT_A_PATTERN(CountOperation) +NOT_A_PATTERN(DebuggerStatement) +NOT_A_PATTERN(DoExpression) +NOT_A_PATTERN(DoWhileStatement) +NOT_A_PATTERN(EmptyStatement) +NOT_A_PATTERN(EmptyParentheses) +NOT_A_PATTERN(ExportDeclaration) +NOT_A_PATTERN(ExpressionStatement) +NOT_A_PATTERN(ForInStatement) +NOT_A_PATTERN(ForOfStatement) +NOT_A_PATTERN(ForStatement) +NOT_A_PATTERN(FunctionDeclaration) +NOT_A_PATTERN(FunctionLiteral) +NOT_A_PATTERN(IfStatement) +NOT_A_PATTERN(ImportDeclaration) +NOT_A_PATTERN(Literal) +NOT_A_PATTERN(NativeFunctionLiteral) +NOT_A_PATTERN(RegExpLiteral) +NOT_A_PATTERN(ReturnStatement) +NOT_A_PATTERN(SloppyBlockFunctionStatement) +NOT_A_PATTERN(Spread) +NOT_A_PATTERN(SuperPropertyReference) +NOT_A_PATTERN(SuperCallReference) +NOT_A_PATTERN(SwitchStatement) +NOT_A_PATTERN(ThisFunction) +NOT_A_PATTERN(Throw) +NOT_A_PATTERN(TryCatchStatement) +NOT_A_PATTERN(TryFinallyStatement) +NOT_A_PATTERN(UnaryOperation) +NOT_A_PATTERN(VariableDeclaration) +NOT_A_PATTERN(WhileStatement) +NOT_A_PATTERN(WithStatement) +NOT_A_PATTERN(Yield) + +#undef NOT_A_PATTERN +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/parsing/preparse-data-format.h b/deps/v8/src/parsing/preparse-data-format.h new file mode 100644 index 0000000000..f7d9f68cce --- /dev/null +++ b/deps/v8/src/parsing/preparse-data-format.h @@ -0,0 +1,41 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_PARSING_PREPARSE_DATA_FORMAT_H_ +#define V8_PARSING_PREPARSE_DATA_FORMAT_H_ + +namespace v8 { +namespace internal { + +// Generic and general data used by preparse data recorders and readers. + +struct PreparseDataConstants { + public: + // Layout and constants of the preparse data exchange format. + static const unsigned kMagicNumber = 0xBadDead; + static const unsigned kCurrentVersion = 11; + + static const int kMagicOffset = 0; + static const int kVersionOffset = 1; + static const int kHasErrorOffset = 2; + static const int kFunctionsSizeOffset = 3; + static const int kSizeOffset = 4; + static const int kHeaderSize = 5; + + // If encoding a message, the following positions are fixed. + static const int kMessageStartPos = 0; + static const int kMessageEndPos = 1; + static const int kMessageArgCountPos = 2; + static const int kParseErrorTypePos = 3; + static const int kMessageTemplatePos = 4; + static const int kMessageArgPos = 5; + + static const unsigned char kNumberTerminator = 0x80u; +}; + + +} // namespace internal +} // namespace v8. + +#endif // V8_PARSING_PREPARSE_DATA_FORMAT_H_ diff --git a/deps/v8/src/parsing/preparse-data.cc b/deps/v8/src/parsing/preparse-data.cc new file mode 100644 index 0000000000..d02cd63d66 --- /dev/null +++ b/deps/v8/src/parsing/preparse-data.cc @@ -0,0 +1,80 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/base/logging.h" +#include "src/globals.h" +#include "src/hashmap.h" +#include "src/parsing/parser.h" +#include "src/parsing/preparse-data.h" +#include "src/parsing/preparse-data-format.h" + +namespace v8 { +namespace internal { + + +CompleteParserRecorder::CompleteParserRecorder() { + preamble_[PreparseDataConstants::kMagicOffset] = + PreparseDataConstants::kMagicNumber; + preamble_[PreparseDataConstants::kVersionOffset] = + PreparseDataConstants::kCurrentVersion; + preamble_[PreparseDataConstants::kHasErrorOffset] = false; + preamble_[PreparseDataConstants::kFunctionsSizeOffset] = 0; + preamble_[PreparseDataConstants::kSizeOffset] = 0; + DCHECK_EQ(5, PreparseDataConstants::kHeaderSize); +#ifdef DEBUG + prev_start_ = -1; +#endif +} + + +void CompleteParserRecorder::LogMessage(int start_pos, int end_pos, + MessageTemplate::Template message, + const char* arg_opt, + ParseErrorType error_type) { + if (HasError()) return; + preamble_[PreparseDataConstants::kHasErrorOffset] = true; + function_store_.Reset(); + STATIC_ASSERT(PreparseDataConstants::kMessageStartPos == 0); + function_store_.Add(start_pos); + STATIC_ASSERT(PreparseDataConstants::kMessageEndPos == 1); + function_store_.Add(end_pos); + STATIC_ASSERT(PreparseDataConstants::kMessageArgCountPos == 2); + function_store_.Add((arg_opt == NULL) ? 0 : 1); + STATIC_ASSERT(PreparseDataConstants::kParseErrorTypePos == 3); + function_store_.Add(error_type); + STATIC_ASSERT(PreparseDataConstants::kMessageTemplatePos == 4); + function_store_.Add(static_cast<unsigned>(message)); + STATIC_ASSERT(PreparseDataConstants::kMessageArgPos == 5); + if (arg_opt != NULL) WriteString(CStrVector(arg_opt)); +} + + +void CompleteParserRecorder::WriteString(Vector<const char> str) { + function_store_.Add(str.length()); + for (int i = 0; i < str.length(); i++) { + function_store_.Add(str[i]); + } +} + + +ScriptData* CompleteParserRecorder::GetScriptData() { + int function_size = function_store_.size(); + int total_size = PreparseDataConstants::kHeaderSize + function_size; + unsigned* data = NewArray<unsigned>(total_size); + preamble_[PreparseDataConstants::kFunctionsSizeOffset] = function_size; + MemCopy(data, preamble_, sizeof(preamble_)); + if (function_size > 0) { + function_store_.WriteTo(Vector<unsigned>( + data + PreparseDataConstants::kHeaderSize, function_size)); + } + DCHECK(IsAligned(reinterpret_cast<intptr_t>(data), kPointerAlignment)); + ScriptData* result = new ScriptData(reinterpret_cast<byte*>(data), + total_size * sizeof(unsigned)); + result->AcquireDataOwnership(); + return result; +} + + +} // namespace internal +} // namespace v8. diff --git a/deps/v8/src/parsing/preparse-data.h b/deps/v8/src/parsing/preparse-data.h new file mode 100644 index 0000000000..dbe1022d1e --- /dev/null +++ b/deps/v8/src/parsing/preparse-data.h @@ -0,0 +1,212 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_PARSING_PREPARSE_DATA_H_ +#define V8_PARSING_PREPARSE_DATA_H_ + +#include "src/allocation.h" +#include "src/hashmap.h" +#include "src/messages.h" +#include "src/parsing/preparse-data-format.h" + +namespace v8 { +namespace internal { + +class ScriptData { + public: + ScriptData(const byte* data, int length); + ~ScriptData() { + if (owns_data_) DeleteArray(data_); + } + + const byte* data() const { return data_; } + int length() const { return length_; } + bool rejected() const { return rejected_; } + + void Reject() { rejected_ = true; } + + void AcquireDataOwnership() { + DCHECK(!owns_data_); + owns_data_ = true; + } + + void ReleaseDataOwnership() { + DCHECK(owns_data_); + owns_data_ = false; + } + + private: + bool owns_data_ : 1; + bool rejected_ : 1; + const byte* data_; + int length_; + + DISALLOW_COPY_AND_ASSIGN(ScriptData); +}; + +// Abstract interface for preparse data recorder. +class ParserRecorder { + public: + ParserRecorder() { } + virtual ~ParserRecorder() { } + + // Logs the scope and some details of a function literal in the source. + virtual void LogFunction(int start, int end, int literals, int properties, + LanguageMode language_mode, bool uses_super_property, + bool calls_eval) = 0; + + // Logs an error message and marks the log as containing an error. + // Further logging will be ignored, and ExtractData will return a vector + // representing the error only. + virtual void LogMessage(int start, int end, MessageTemplate::Template message, + const char* argument_opt, + ParseErrorType error_type) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(ParserRecorder); +}; + + +class SingletonLogger : public ParserRecorder { + public: + SingletonLogger() + : has_error_(false), start_(-1), end_(-1), error_type_(kSyntaxError) {} + virtual ~SingletonLogger() {} + + void Reset() { has_error_ = false; } + + virtual void LogFunction(int start, int end, int literals, int properties, + LanguageMode language_mode, bool uses_super_property, + bool calls_eval) { + DCHECK(!has_error_); + start_ = start; + end_ = end; + literals_ = literals; + properties_ = properties; + language_mode_ = language_mode; + uses_super_property_ = uses_super_property; + calls_eval_ = calls_eval; + } + + // Logs an error message and marks the log as containing an error. + // Further logging will be ignored, and ExtractData will return a vector + // representing the error only. + virtual void LogMessage(int start, int end, MessageTemplate::Template message, + const char* argument_opt, ParseErrorType error_type) { + if (has_error_) return; + has_error_ = true; + start_ = start; + end_ = end; + message_ = message; + argument_opt_ = argument_opt; + error_type_ = error_type; + } + + bool has_error() const { return has_error_; } + + int start() const { return start_; } + int end() const { return end_; } + int literals() const { + DCHECK(!has_error_); + return literals_; + } + int properties() const { + DCHECK(!has_error_); + return properties_; + } + LanguageMode language_mode() const { + DCHECK(!has_error_); + return language_mode_; + } + bool uses_super_property() const { + DCHECK(!has_error_); + return uses_super_property_; + } + bool calls_eval() const { + DCHECK(!has_error_); + return calls_eval_; + } + ParseErrorType error_type() const { + DCHECK(has_error_); + return error_type_; + } + MessageTemplate::Template message() { + DCHECK(has_error_); + return message_; + } + const char* argument_opt() const { + DCHECK(has_error_); + return argument_opt_; + } + + private: + bool has_error_; + int start_; + int end_; + // For function entries. + int literals_; + int properties_; + LanguageMode language_mode_; + bool uses_super_property_; + bool calls_eval_; + // For error messages. + MessageTemplate::Template message_; + const char* argument_opt_; + ParseErrorType error_type_; +}; + + +class CompleteParserRecorder : public ParserRecorder { + public: + struct Key { + bool is_one_byte; + Vector<const byte> literal_bytes; + }; + + CompleteParserRecorder(); + virtual ~CompleteParserRecorder() {} + + virtual void LogFunction(int start, int end, int literals, int properties, + LanguageMode language_mode, bool uses_super_property, + bool calls_eval) { + function_store_.Add(start); + function_store_.Add(end); + function_store_.Add(literals); + function_store_.Add(properties); + function_store_.Add(language_mode); + function_store_.Add(uses_super_property); + function_store_.Add(calls_eval); + } + + // Logs an error message and marks the log as containing an error. + // Further logging will be ignored, and ExtractData will return a vector + // representing the error only. + virtual void LogMessage(int start, int end, MessageTemplate::Template message, + const char* argument_opt, ParseErrorType error_type); + ScriptData* GetScriptData(); + + bool HasError() { + return static_cast<bool>(preamble_[PreparseDataConstants::kHasErrorOffset]); + } + Vector<unsigned> ErrorMessageData() { + DCHECK(HasError()); + return function_store_.ToVector(); + } + + private: + void WriteString(Vector<const char> str); + + Collector<unsigned> function_store_; + unsigned preamble_[PreparseDataConstants::kHeaderSize]; + +#ifdef DEBUG + int prev_start_; +#endif +}; + + +} // namespace internal +} // namespace v8. + +#endif // V8_PARSING_PREPARSE_DATA_H_ diff --git a/deps/v8/src/parsing/preparser.cc b/deps/v8/src/parsing/preparser.cc new file mode 100644 index 0000000000..64511acc39 --- /dev/null +++ b/deps/v8/src/parsing/preparser.cc @@ -0,0 +1,1292 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <cmath> + +#include "src/allocation.h" +#include "src/base/logging.h" +#include "src/conversions-inl.h" +#include "src/conversions.h" +#include "src/globals.h" +#include "src/hashmap.h" +#include "src/list.h" +#include "src/parsing/parser-base.h" +#include "src/parsing/preparse-data.h" +#include "src/parsing/preparse-data-format.h" +#include "src/parsing/preparser.h" +#include "src/unicode.h" +#include "src/utils.h" + +namespace v8 { +namespace internal { + +void PreParserTraits::ReportMessageAt(Scanner::Location location, + MessageTemplate::Template message, + const char* arg, + ParseErrorType error_type) { + ReportMessageAt(location.beg_pos, location.end_pos, message, arg, error_type); +} + + +void PreParserTraits::ReportMessageAt(int start_pos, int end_pos, + MessageTemplate::Template message, + const char* arg, + ParseErrorType error_type) { + pre_parser_->log_->LogMessage(start_pos, end_pos, message, arg, error_type); +} + + +PreParserIdentifier PreParserTraits::GetSymbol(Scanner* scanner) { + if (scanner->current_token() == Token::FUTURE_RESERVED_WORD) { + return PreParserIdentifier::FutureReserved(); + } else if (scanner->current_token() == + Token::FUTURE_STRICT_RESERVED_WORD) { + return PreParserIdentifier::FutureStrictReserved(); + } else if (scanner->current_token() == Token::LET) { + return PreParserIdentifier::Let(); + } else if (scanner->current_token() == Token::STATIC) { + return PreParserIdentifier::Static(); + } else if (scanner->current_token() == Token::YIELD) { + return PreParserIdentifier::Yield(); + } + if (scanner->UnescapedLiteralMatches("eval", 4)) { + return PreParserIdentifier::Eval(); + } + if (scanner->UnescapedLiteralMatches("arguments", 9)) { + return PreParserIdentifier::Arguments(); + } + if (scanner->UnescapedLiteralMatches("undefined", 9)) { + return PreParserIdentifier::Undefined(); + } + if (scanner->LiteralMatches("prototype", 9)) { + return PreParserIdentifier::Prototype(); + } + if (scanner->LiteralMatches("constructor", 11)) { + return PreParserIdentifier::Constructor(); + } + return PreParserIdentifier::Default(); +} + + +PreParserIdentifier PreParserTraits::GetNumberAsSymbol(Scanner* scanner) { + return PreParserIdentifier::Default(); +} + + +PreParserExpression PreParserTraits::ExpressionFromString( + int pos, Scanner* scanner, PreParserFactory* factory) { + if (scanner->UnescapedLiteralMatches("use strict", 10)) { + return PreParserExpression::UseStrictStringLiteral(); + } else if (scanner->UnescapedLiteralMatches("use strong", 10)) { + return PreParserExpression::UseStrongStringLiteral(); + } + return PreParserExpression::StringLiteral(); +} + + +PreParserExpression PreParserTraits::ParseV8Intrinsic(bool* ok) { + return pre_parser_->ParseV8Intrinsic(ok); +} + + +PreParserExpression PreParserTraits::ParseFunctionLiteral( + PreParserIdentifier name, Scanner::Location function_name_location, + FunctionNameValidity function_name_validity, FunctionKind kind, + int function_token_position, FunctionLiteral::FunctionType type, + FunctionLiteral::ArityRestriction arity_restriction, + LanguageMode language_mode, bool* ok) { + return pre_parser_->ParseFunctionLiteral( + name, function_name_location, function_name_validity, kind, + function_token_position, type, arity_restriction, language_mode, ok); +} + + +PreParser::PreParseResult PreParser::PreParseLazyFunction( + LanguageMode language_mode, FunctionKind kind, bool has_simple_parameters, + ParserRecorder* log, Scanner::BookmarkScope* bookmark) { + log_ = log; + // Lazy functions always have trivial outer scopes (no with/catch scopes). + Scope* top_scope = NewScope(scope_, SCRIPT_SCOPE); + PreParserFactory top_factory(NULL); + FunctionState top_state(&function_state_, &scope_, top_scope, kNormalFunction, + &top_factory); + scope_->SetLanguageMode(language_mode); + Scope* function_scope = NewScope(scope_, FUNCTION_SCOPE, kind); + if (!has_simple_parameters) function_scope->SetHasNonSimpleParameters(); + PreParserFactory function_factory(NULL); + FunctionState function_state(&function_state_, &scope_, function_scope, kind, + &function_factory); + DCHECK_EQ(Token::LBRACE, scanner()->current_token()); + bool ok = true; + int start_position = peek_position(); + ParseLazyFunctionLiteralBody(&ok, bookmark); + if (bookmark && bookmark->HasBeenReset()) { + // Do nothing, as we've just aborted scanning this function. + } else if (stack_overflow()) { + return kPreParseStackOverflow; + } else if (!ok) { + ReportUnexpectedToken(scanner()->current_token()); + } else { + DCHECK_EQ(Token::RBRACE, scanner()->peek()); + if (is_strict(scope_->language_mode())) { + int end_pos = scanner()->location().end_pos; + CheckStrictOctalLiteral(start_position, end_pos, &ok); + if (!ok) return kPreParseSuccess; + + if (is_strong(scope_->language_mode()) && IsSubclassConstructor(kind)) { + if (!function_state.super_location().IsValid()) { + ReportMessageAt(Scanner::Location(start_position, start_position + 1), + MessageTemplate::kStrongSuperCallMissing, + kReferenceError); + return kPreParseSuccess; + } + } + } + } + return kPreParseSuccess; +} + + +PreParserExpression PreParserTraits::ParseClassLiteral( + PreParserIdentifier name, Scanner::Location class_name_location, + bool name_is_strict_reserved, int pos, bool* ok) { + return pre_parser_->ParseClassLiteral(name, class_name_location, + name_is_strict_reserved, pos, ok); +} + + +// Preparsing checks a JavaScript program and emits preparse-data that helps +// a later parsing to be faster. +// See preparser-data.h for the data. + +// The PreParser checks that the syntax follows the grammar for JavaScript, +// and collects some information about the program along the way. +// The grammar check is only performed in order to understand the program +// sufficiently to deduce some information about it, that can be used +// to speed up later parsing. Finding errors is not the goal of pre-parsing, +// rather it is to speed up properly written and correct programs. +// That means that contextual checks (like a label being declared where +// it is used) are generally omitted. + + +PreParser::Statement PreParser::ParseStatementListItem(bool* ok) { + // ECMA 262 6th Edition + // StatementListItem[Yield, Return] : + // Statement[?Yield, ?Return] + // Declaration[?Yield] + // + // Declaration[Yield] : + // HoistableDeclaration[?Yield] + // ClassDeclaration[?Yield] + // LexicalDeclaration[In, ?Yield] + // + // HoistableDeclaration[Yield, Default] : + // FunctionDeclaration[?Yield, ?Default] + // GeneratorDeclaration[?Yield, ?Default] + // + // LexicalDeclaration[In, Yield] : + // LetOrConst BindingList[?In, ?Yield] ; + + switch (peek()) { + case Token::FUNCTION: + return ParseFunctionDeclaration(ok); + case Token::CLASS: + return ParseClassDeclaration(ok); + case Token::CONST: + if (allow_const()) { + return ParseVariableStatement(kStatementListItem, ok); + } + break; + case Token::LET: + if (IsNextLetKeyword()) { + return ParseVariableStatement(kStatementListItem, ok); + } + break; + default: + break; + } + return ParseStatement(ok); +} + + +void PreParser::ParseStatementList(int end_token, bool* ok, + Scanner::BookmarkScope* bookmark) { + // SourceElements :: + // (Statement)* <end_token> + + // Bookkeeping for trial parse if bookmark is set: + DCHECK_IMPLIES(bookmark, bookmark->HasBeenSet()); + bool maybe_reset = bookmark != nullptr; + int count_statements = 0; + + bool directive_prologue = true; + while (peek() != end_token) { + if (directive_prologue && peek() != Token::STRING) { + directive_prologue = false; + } + bool starts_with_identifier = peek() == Token::IDENTIFIER; + Scanner::Location token_loc = scanner()->peek_location(); + Scanner::Location old_this_loc = function_state_->this_location(); + Scanner::Location old_super_loc = function_state_->super_location(); + Statement statement = ParseStatementListItem(ok); + if (!*ok) return; + + if (is_strong(language_mode()) && scope_->is_function_scope() && + IsClassConstructor(function_state_->kind())) { + Scanner::Location this_loc = function_state_->this_location(); + Scanner::Location super_loc = function_state_->super_location(); + if (this_loc.beg_pos != old_this_loc.beg_pos && + this_loc.beg_pos != token_loc.beg_pos) { + ReportMessageAt(this_loc, MessageTemplate::kStrongConstructorThis); + *ok = false; + return; + } + if (super_loc.beg_pos != old_super_loc.beg_pos && + super_loc.beg_pos != token_loc.beg_pos) { + ReportMessageAt(super_loc, MessageTemplate::kStrongConstructorSuper); + *ok = false; + return; + } + } + + if (directive_prologue) { + bool use_strict_found = statement.IsUseStrictLiteral(); + bool use_strong_found = + statement.IsUseStrongLiteral() && allow_strong_mode(); + + if (use_strict_found) { + scope_->SetLanguageMode( + static_cast<LanguageMode>(scope_->language_mode() | STRICT)); + } else if (use_strong_found) { + scope_->SetLanguageMode(static_cast<LanguageMode>( + scope_->language_mode() | STRONG)); + if (IsClassConstructor(function_state_->kind())) { + // "use strong" cannot occur in a class constructor body, to avoid + // unintuitive strong class object semantics. + PreParserTraits::ReportMessageAt( + token_loc, MessageTemplate::kStrongConstructorDirective); + *ok = false; + return; + } + } else if (!statement.IsStringLiteral()) { + directive_prologue = false; + } + + if ((use_strict_found || use_strong_found) && + !scope_->HasSimpleParameters()) { + // TC39 deemed "use strict" directives to be an error when occurring + // in the body of a function with non-simple parameter list, on + // 29/7/2015. https://goo.gl/ueA7Ln + // + // In V8, this also applies to "use strong " directives. + PreParserTraits::ReportMessageAt( + token_loc, MessageTemplate::kIllegalLanguageModeDirective, + use_strict_found ? "use strict" : "use strong"); + *ok = false; + return; + } + } + + // If we're allowed to reset to a bookmark, we will do so when we see a long + // and trivial function. + // Our current definition of 'long and trivial' is: + // - over 200 statements + // - all starting with an identifier (i.e., no if, for, while, etc.) + if (maybe_reset && (!starts_with_identifier || + ++count_statements > kLazyParseTrialLimit)) { + if (count_statements > kLazyParseTrialLimit) { + bookmark->Reset(); + return; + } + maybe_reset = false; + } + } +} + + +#define CHECK_OK ok); \ + if (!*ok) return Statement::Default(); \ + ((void)0 +#define DUMMY ) // to make indentation work +#undef DUMMY + + +PreParser::Statement PreParser::ParseStatement(bool* ok) { + // Statement :: + // EmptyStatement + // ... + + if (peek() == Token::SEMICOLON) { + Next(); + return Statement::Default(); + } + return ParseSubStatement(ok); +} + + +PreParser::Statement PreParser::ParseSubStatement(bool* ok) { + // Statement :: + // Block + // VariableStatement + // EmptyStatement + // ExpressionStatement + // IfStatement + // IterationStatement + // ContinueStatement + // BreakStatement + // ReturnStatement + // WithStatement + // LabelledStatement + // SwitchStatement + // ThrowStatement + // TryStatement + // DebuggerStatement + + // Note: Since labels can only be used by 'break' and 'continue' + // statements, which themselves are only valid within blocks, + // iterations or 'switch' statements (i.e., BreakableStatements), + // labels can be simply ignored in all other cases; except for + // trivial labeled break statements 'label: break label' which is + // parsed into an empty statement. + + // Keep the source position of the statement + switch (peek()) { + case Token::LBRACE: + return ParseBlock(ok); + + case Token::SEMICOLON: + if (is_strong(language_mode())) { + PreParserTraits::ReportMessageAt(scanner()->peek_location(), + MessageTemplate::kStrongEmpty); + *ok = false; + return Statement::Default(); + } + Next(); + return Statement::Default(); + + case Token::IF: + return ParseIfStatement(ok); + + case Token::DO: + return ParseDoWhileStatement(ok); + + case Token::WHILE: + return ParseWhileStatement(ok); + + case Token::FOR: + return ParseForStatement(ok); + + case Token::CONTINUE: + return ParseContinueStatement(ok); + + case Token::BREAK: + return ParseBreakStatement(ok); + + case Token::RETURN: + return ParseReturnStatement(ok); + + case Token::WITH: + return ParseWithStatement(ok); + + case Token::SWITCH: + return ParseSwitchStatement(ok); + + case Token::THROW: + return ParseThrowStatement(ok); + + case Token::TRY: + return ParseTryStatement(ok); + + case Token::FUNCTION: { + Scanner::Location start_location = scanner()->peek_location(); + Statement statement = ParseFunctionDeclaration(CHECK_OK); + Scanner::Location end_location = scanner()->location(); + if (is_strict(language_mode())) { + PreParserTraits::ReportMessageAt(start_location.beg_pos, + end_location.end_pos, + MessageTemplate::kStrictFunction); + *ok = false; + return Statement::Default(); + } else { + return statement; + } + } + + case Token::DEBUGGER: + return ParseDebuggerStatement(ok); + + case Token::VAR: + return ParseVariableStatement(kStatement, ok); + + case Token::CONST: + // In ES6 CONST is not allowed as a Statement, only as a + // LexicalDeclaration, however we continue to allow it in sloppy mode for + // backwards compatibility. + if (is_sloppy(language_mode()) && allow_legacy_const()) { + return ParseVariableStatement(kStatement, ok); + } + + // Fall through. + default: + return ParseExpressionOrLabelledStatement(ok); + } +} + + +PreParser::Statement PreParser::ParseFunctionDeclaration(bool* ok) { + // FunctionDeclaration :: + // 'function' Identifier '(' FormalParameterListopt ')' '{' FunctionBody '}' + // GeneratorDeclaration :: + // 'function' '*' Identifier '(' FormalParameterListopt ')' + // '{' FunctionBody '}' + Expect(Token::FUNCTION, CHECK_OK); + int pos = position(); + bool is_generator = Check(Token::MUL); + bool is_strict_reserved = false; + Identifier name = ParseIdentifierOrStrictReservedWord( + &is_strict_reserved, CHECK_OK); + ParseFunctionLiteral(name, scanner()->location(), + is_strict_reserved ? kFunctionNameIsStrictReserved + : kFunctionNameValidityUnknown, + is_generator ? FunctionKind::kGeneratorFunction + : FunctionKind::kNormalFunction, + pos, FunctionLiteral::kDeclaration, + FunctionLiteral::kNormalArity, language_mode(), + CHECK_OK); + return Statement::FunctionDeclaration(); +} + + +PreParser::Statement PreParser::ParseClassDeclaration(bool* ok) { + Expect(Token::CLASS, CHECK_OK); + if (!allow_harmony_sloppy() && is_sloppy(language_mode())) { + ReportMessage(MessageTemplate::kSloppyLexical); + *ok = false; + return Statement::Default(); + } + + int pos = position(); + bool is_strict_reserved = false; + Identifier name = + ParseIdentifierOrStrictReservedWord(&is_strict_reserved, CHECK_OK); + ParseClassLiteral(name, scanner()->location(), is_strict_reserved, pos, + CHECK_OK); + return Statement::Default(); +} + + +PreParser::Statement PreParser::ParseBlock(bool* ok) { + // Block :: + // '{' StatementList '}' + + Expect(Token::LBRACE, CHECK_OK); + Statement final = Statement::Default(); + while (peek() != Token::RBRACE) { + final = ParseStatementListItem(CHECK_OK); + } + Expect(Token::RBRACE, ok); + return final; +} + + +PreParser::Statement PreParser::ParseVariableStatement( + VariableDeclarationContext var_context, + bool* ok) { + // VariableStatement :: + // VariableDeclarations ';' + + Statement result = ParseVariableDeclarations( + var_context, nullptr, nullptr, nullptr, nullptr, nullptr, CHECK_OK); + ExpectSemicolon(CHECK_OK); + return result; +} + + +// If the variable declaration declares exactly one non-const +// variable, then *var is set to that variable. In all other cases, +// *var is untouched; in particular, it is the caller's responsibility +// to initialize it properly. This mechanism is also used for the parsing +// of 'for-in' loops. +PreParser::Statement PreParser::ParseVariableDeclarations( + VariableDeclarationContext var_context, int* num_decl, bool* is_lexical, + bool* is_binding_pattern, Scanner::Location* first_initializer_loc, + Scanner::Location* bindings_loc, bool* ok) { + // VariableDeclarations :: + // ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[','] + // + // The ES6 Draft Rev3 specifies the following grammar for const declarations + // + // ConstDeclaration :: + // const ConstBinding (',' ConstBinding)* ';' + // ConstBinding :: + // Identifier '=' AssignmentExpression + // + // TODO(ES6): + // ConstBinding :: + // BindingPattern '=' AssignmentExpression + bool require_initializer = false; + bool lexical = false; + bool is_pattern = false; + if (peek() == Token::VAR) { + if (is_strong(language_mode())) { + Scanner::Location location = scanner()->peek_location(); + ReportMessageAt(location, MessageTemplate::kStrongVar); + *ok = false; + return Statement::Default(); + } + Consume(Token::VAR); + } else if (peek() == Token::CONST && allow_const()) { + // TODO(ES6): The ES6 Draft Rev4 section 12.2.2 reads: + // + // ConstDeclaration : const ConstBinding (',' ConstBinding)* ';' + // + // * It is a Syntax Error if the code that matches this production is not + // contained in extended code. + // + // However disallowing const in sloppy mode will break compatibility with + // existing pages. Therefore we keep allowing const with the old + // non-harmony semantics in sloppy mode. + Consume(Token::CONST); + if (is_strict(language_mode()) || + (allow_harmony_sloppy() && !allow_legacy_const())) { + DCHECK(var_context != kStatement); + require_initializer = true; + lexical = true; + } + } else if (peek() == Token::LET && allow_let()) { + Consume(Token::LET); + DCHECK(var_context != kStatement); + lexical = true; + } else { + *ok = false; + return Statement::Default(); + } + + // The scope of a var/const declared variable anywhere inside a function + // is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). The scope + // of a let declared variable is the scope of the immediately enclosing + // block. + int nvars = 0; // the number of variables declared + int bindings_start = peek_position(); + do { + // Parse binding pattern. + if (nvars > 0) Consume(Token::COMMA); + int decl_pos = peek_position(); + PreParserExpression pattern = PreParserExpression::Default(); + { + ExpressionClassifier pattern_classifier; + Token::Value next = peek(); + pattern = ParsePrimaryExpression(&pattern_classifier, CHECK_OK); + + ValidateBindingPattern(&pattern_classifier, CHECK_OK); + if (lexical) { + ValidateLetPattern(&pattern_classifier, CHECK_OK); + } + + if (!allow_harmony_destructuring_bind() && !pattern.IsIdentifier()) { + ReportUnexpectedToken(next); + *ok = false; + return Statement::Default(); + } + } + + is_pattern = (pattern.IsObjectLiteral() || pattern.IsArrayLiteral()) && + !pattern.is_parenthesized(); + + bool is_for_iteration_variable = + var_context == kForStatement && + (peek() == Token::IN || PeekContextualKeyword(CStrVector("of"))); + + Scanner::Location variable_loc = scanner()->location(); + nvars++; + if (Check(Token::ASSIGN)) { + ExpressionClassifier classifier; + ParseAssignmentExpression(var_context != kForStatement, &classifier, + CHECK_OK); + ValidateExpression(&classifier, CHECK_OK); + + variable_loc.end_pos = scanner()->location().end_pos; + if (first_initializer_loc && !first_initializer_loc->IsValid()) { + *first_initializer_loc = variable_loc; + } + } else if ((require_initializer || is_pattern) && + !is_for_iteration_variable) { + PreParserTraits::ReportMessageAt( + Scanner::Location(decl_pos, scanner()->location().end_pos), + MessageTemplate::kDeclarationMissingInitializer, + is_pattern ? "destructuring" : "const"); + *ok = false; + return Statement::Default(); + } + } while (peek() == Token::COMMA); + + if (bindings_loc) { + *bindings_loc = + Scanner::Location(bindings_start, scanner()->location().end_pos); + } + + if (num_decl != nullptr) *num_decl = nvars; + if (is_lexical != nullptr) *is_lexical = lexical; + if (is_binding_pattern != nullptr) *is_binding_pattern = is_pattern; + return Statement::Default(); +} + + +PreParser::Statement PreParser::ParseExpressionOrLabelledStatement(bool* ok) { + // ExpressionStatement | LabelledStatement :: + // Expression ';' + // Identifier ':' Statement + + switch (peek()) { + case Token::FUNCTION: + case Token::LBRACE: + UNREACHABLE(); // Always handled by the callers. + case Token::CLASS: + ReportUnexpectedToken(Next()); + *ok = false; + return Statement::Default(); + + case Token::THIS: + if (!FLAG_strong_this) break; + // Fall through. + case Token::SUPER: + if (is_strong(language_mode()) && + IsClassConstructor(function_state_->kind())) { + bool is_this = peek() == Token::THIS; + Expression expr = Expression::Default(); + ExpressionClassifier classifier; + if (is_this) { + expr = ParseStrongInitializationExpression(&classifier, CHECK_OK); + } else { + expr = ParseStrongSuperCallExpression(&classifier, CHECK_OK); + } + ValidateExpression(&classifier, CHECK_OK); + switch (peek()) { + case Token::SEMICOLON: + Consume(Token::SEMICOLON); + break; + case Token::RBRACE: + case Token::EOS: + break; + default: + if (!scanner()->HasAnyLineTerminatorBeforeNext()) { + ReportMessageAt(function_state_->this_location(), + is_this + ? MessageTemplate::kStrongConstructorThis + : MessageTemplate::kStrongConstructorSuper); + *ok = false; + return Statement::Default(); + } + } + return Statement::ExpressionStatement(expr); + } + break; + + // TODO(arv): Handle `let [` + // https://code.google.com/p/v8/issues/detail?id=3847 + + default: + break; + } + + bool starts_with_identifier = peek_any_identifier(); + ExpressionClassifier classifier; + Expression expr = ParseExpression(true, &classifier, CHECK_OK); + ValidateExpression(&classifier, CHECK_OK); + + // Even if the expression starts with an identifier, it is not necessarily an + // identifier. For example, "foo + bar" starts with an identifier but is not + // an identifier. + if (starts_with_identifier && expr.IsIdentifier() && peek() == Token::COLON) { + // Expression is a single identifier, and not, e.g., a parenthesized + // identifier. + DCHECK(!expr.AsIdentifier().IsFutureReserved()); + DCHECK(is_sloppy(language_mode()) || + !IsFutureStrictReserved(expr.AsIdentifier())); + Consume(Token::COLON); + Statement statement = ParseStatement(ok); + return statement.IsJumpStatement() ? Statement::Default() : statement; + // Preparsing is disabled for extensions (because the extension details + // aren't passed to lazily compiled functions), so we don't + // accept "native function" in the preparser. + } + // Parsed expression statement. + // Detect attempts at 'let' declarations in sloppy mode. + if (!allow_harmony_sloppy_let() && peek() == Token::IDENTIFIER && + is_sloppy(language_mode()) && expr.IsIdentifier() && + expr.AsIdentifier().IsLet()) { + ReportMessage(MessageTemplate::kSloppyLexical, NULL); + *ok = false; + return Statement::Default(); + } + ExpectSemicolon(CHECK_OK); + return Statement::ExpressionStatement(expr); +} + + +PreParser::Statement PreParser::ParseIfStatement(bool* ok) { + // IfStatement :: + // 'if' '(' Expression ')' Statement ('else' Statement)? + + Expect(Token::IF, CHECK_OK); + Expect(Token::LPAREN, CHECK_OK); + ParseExpression(true, CHECK_OK); + Expect(Token::RPAREN, CHECK_OK); + Statement stat = ParseSubStatement(CHECK_OK); + if (peek() == Token::ELSE) { + Next(); + Statement else_stat = ParseSubStatement(CHECK_OK); + stat = (stat.IsJumpStatement() && else_stat.IsJumpStatement()) ? + Statement::Jump() : Statement::Default(); + } else { + stat = Statement::Default(); + } + return stat; +} + + +PreParser::Statement PreParser::ParseContinueStatement(bool* ok) { + // ContinueStatement :: + // 'continue' [no line terminator] Identifier? ';' + + Expect(Token::CONTINUE, CHECK_OK); + Token::Value tok = peek(); + if (!scanner()->HasAnyLineTerminatorBeforeNext() && + tok != Token::SEMICOLON && + tok != Token::RBRACE && + tok != Token::EOS) { + // ECMA allows "eval" or "arguments" as labels even in strict mode. + ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK); + } + ExpectSemicolon(CHECK_OK); + return Statement::Jump(); +} + + +PreParser::Statement PreParser::ParseBreakStatement(bool* ok) { + // BreakStatement :: + // 'break' [no line terminator] Identifier? ';' + + Expect(Token::BREAK, CHECK_OK); + Token::Value tok = peek(); + if (!scanner()->HasAnyLineTerminatorBeforeNext() && + tok != Token::SEMICOLON && + tok != Token::RBRACE && + tok != Token::EOS) { + // ECMA allows "eval" or "arguments" as labels even in strict mode. + ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK); + } + ExpectSemicolon(CHECK_OK); + return Statement::Jump(); +} + + +PreParser::Statement PreParser::ParseReturnStatement(bool* ok) { + // ReturnStatement :: + // 'return' [no line terminator] Expression? ';' + + // Consume the return token. It is necessary to do before + // reporting any errors on it, because of the way errors are + // reported (underlining). + Expect(Token::RETURN, CHECK_OK); + function_state_->set_return_location(scanner()->location()); + + // An ECMAScript program is considered syntactically incorrect if it + // contains a return statement that is not within the body of a + // function. See ECMA-262, section 12.9, page 67. + // This is not handled during preparsing. + + Token::Value tok = peek(); + if (!scanner()->HasAnyLineTerminatorBeforeNext() && + tok != Token::SEMICOLON && + tok != Token::RBRACE && + tok != Token::EOS) { + if (is_strong(language_mode()) && + IsClassConstructor(function_state_->kind())) { + int pos = peek_position(); + ReportMessageAt(Scanner::Location(pos, pos + 1), + MessageTemplate::kStrongConstructorReturnValue); + *ok = false; + return Statement::Default(); + } + ParseExpression(true, CHECK_OK); + } + ExpectSemicolon(CHECK_OK); + return Statement::Jump(); +} + + +PreParser::Statement PreParser::ParseWithStatement(bool* ok) { + // WithStatement :: + // 'with' '(' Expression ')' Statement + Expect(Token::WITH, CHECK_OK); + if (is_strict(language_mode())) { + ReportMessageAt(scanner()->location(), MessageTemplate::kStrictWith); + *ok = false; + return Statement::Default(); + } + Expect(Token::LPAREN, CHECK_OK); + ParseExpression(true, CHECK_OK); + Expect(Token::RPAREN, CHECK_OK); + + Scope* with_scope = NewScope(scope_, WITH_SCOPE); + BlockState block_state(&scope_, with_scope); + ParseSubStatement(CHECK_OK); + return Statement::Default(); +} + + +PreParser::Statement PreParser::ParseSwitchStatement(bool* ok) { + // SwitchStatement :: + // 'switch' '(' Expression ')' '{' CaseClause* '}' + + Expect(Token::SWITCH, CHECK_OK); + Expect(Token::LPAREN, CHECK_OK); + ParseExpression(true, CHECK_OK); + Expect(Token::RPAREN, CHECK_OK); + + Expect(Token::LBRACE, CHECK_OK); + Token::Value token = peek(); + while (token != Token::RBRACE) { + if (token == Token::CASE) { + Expect(Token::CASE, CHECK_OK); + ParseExpression(true, CHECK_OK); + } else { + Expect(Token::DEFAULT, CHECK_OK); + } + Expect(Token::COLON, CHECK_OK); + token = peek(); + Statement statement = Statement::Jump(); + while (token != Token::CASE && + token != Token::DEFAULT && + token != Token::RBRACE) { + statement = ParseStatementListItem(CHECK_OK); + token = peek(); + } + if (is_strong(language_mode()) && !statement.IsJumpStatement() && + token != Token::RBRACE) { + ReportMessageAt(scanner()->location(), + MessageTemplate::kStrongSwitchFallthrough); + *ok = false; + return Statement::Default(); + } + } + Expect(Token::RBRACE, ok); + return Statement::Default(); +} + + +PreParser::Statement PreParser::ParseDoWhileStatement(bool* ok) { + // DoStatement :: + // 'do' Statement 'while' '(' Expression ')' ';' + + Expect(Token::DO, CHECK_OK); + ParseSubStatement(CHECK_OK); + Expect(Token::WHILE, CHECK_OK); + Expect(Token::LPAREN, CHECK_OK); + ParseExpression(true, CHECK_OK); + Expect(Token::RPAREN, ok); + if (peek() == Token::SEMICOLON) Consume(Token::SEMICOLON); + return Statement::Default(); +} + + +PreParser::Statement PreParser::ParseWhileStatement(bool* ok) { + // WhileStatement :: + // 'while' '(' Expression ')' Statement + + Expect(Token::WHILE, CHECK_OK); + Expect(Token::LPAREN, CHECK_OK); + ParseExpression(true, CHECK_OK); + Expect(Token::RPAREN, CHECK_OK); + ParseSubStatement(ok); + return Statement::Default(); +} + + +PreParser::Statement PreParser::ParseForStatement(bool* ok) { + // ForStatement :: + // 'for' '(' Expression? ';' Expression? ';' Expression? ')' Statement + + Expect(Token::FOR, CHECK_OK); + Expect(Token::LPAREN, CHECK_OK); + bool is_let_identifier_expression = false; + if (peek() != Token::SEMICOLON) { + ForEachStatement::VisitMode mode; + if (peek() == Token::VAR || (peek() == Token::CONST && allow_const()) || + (peek() == Token::LET && IsNextLetKeyword())) { + int decl_count; + bool is_lexical; + bool is_binding_pattern; + Scanner::Location first_initializer_loc = Scanner::Location::invalid(); + Scanner::Location bindings_loc = Scanner::Location::invalid(); + ParseVariableDeclarations(kForStatement, &decl_count, &is_lexical, + &is_binding_pattern, &first_initializer_loc, + &bindings_loc, CHECK_OK); + bool accept_IN = decl_count >= 1; + if (accept_IN && CheckInOrOf(&mode, ok)) { + if (!*ok) return Statement::Default(); + if (decl_count != 1) { + const char* loop_type = + mode == ForEachStatement::ITERATE ? "for-of" : "for-in"; + PreParserTraits::ReportMessageAt( + bindings_loc, MessageTemplate::kForInOfLoopMultiBindings, + loop_type); + *ok = false; + return Statement::Default(); + } + if (first_initializer_loc.IsValid() && + (is_strict(language_mode()) || mode == ForEachStatement::ITERATE || + is_lexical || is_binding_pattern)) { + if (mode == ForEachStatement::ITERATE) { + ReportMessageAt(first_initializer_loc, + MessageTemplate::kForOfLoopInitializer); + } else { + // TODO(caitp): This should be an error in sloppy mode, too. + ReportMessageAt(first_initializer_loc, + MessageTemplate::kForInLoopInitializer); + } + *ok = false; + return Statement::Default(); + } + ParseExpression(true, CHECK_OK); + Expect(Token::RPAREN, CHECK_OK); + ParseSubStatement(CHECK_OK); + return Statement::Default(); + } + } else { + int lhs_beg_pos = peek_position(); + ExpressionClassifier classifier; + Expression lhs = ParseExpression(false, &classifier, CHECK_OK); + int lhs_end_pos = scanner()->location().end_pos; + is_let_identifier_expression = + lhs.IsIdentifier() && lhs.AsIdentifier().IsLet(); + bool is_for_each = CheckInOrOf(&mode, ok); + if (!*ok) return Statement::Default(); + bool is_destructuring = is_for_each && + allow_harmony_destructuring_assignment() && + (lhs->IsArrayLiteral() || lhs->IsObjectLiteral()); + + if (is_destructuring) { + ValidateAssignmentPattern(&classifier, CHECK_OK); + } else { + ValidateExpression(&classifier, CHECK_OK); + } + + if (is_for_each) { + if (!is_destructuring) { + lhs = CheckAndRewriteReferenceExpression( + lhs, lhs_beg_pos, lhs_end_pos, MessageTemplate::kInvalidLhsInFor, + kSyntaxError, CHECK_OK); + } + ParseExpression(true, CHECK_OK); + Expect(Token::RPAREN, CHECK_OK); + ParseSubStatement(CHECK_OK); + return Statement::Default(); + } + } + } + + // Parsed initializer at this point. + // Detect attempts at 'let' declarations in sloppy mode. + if (!allow_harmony_sloppy_let() && peek() == Token::IDENTIFIER && + is_sloppy(language_mode()) && is_let_identifier_expression) { + ReportMessage(MessageTemplate::kSloppyLexical, NULL); + *ok = false; + return Statement::Default(); + } + Expect(Token::SEMICOLON, CHECK_OK); + + if (peek() != Token::SEMICOLON) { + ParseExpression(true, CHECK_OK); + } + Expect(Token::SEMICOLON, CHECK_OK); + + if (peek() != Token::RPAREN) { + ParseExpression(true, CHECK_OK); + } + Expect(Token::RPAREN, CHECK_OK); + + ParseSubStatement(ok); + return Statement::Default(); +} + + +PreParser::Statement PreParser::ParseThrowStatement(bool* ok) { + // ThrowStatement :: + // 'throw' [no line terminator] Expression ';' + + Expect(Token::THROW, CHECK_OK); + if (scanner()->HasAnyLineTerminatorBeforeNext()) { + ReportMessageAt(scanner()->location(), MessageTemplate::kNewlineAfterThrow); + *ok = false; + return Statement::Default(); + } + ParseExpression(true, CHECK_OK); + ExpectSemicolon(ok); + return Statement::Jump(); +} + + +PreParser::Statement PreParser::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); + + ParseBlock(CHECK_OK); + + Token::Value tok = peek(); + if (tok != Token::CATCH && tok != Token::FINALLY) { + ReportMessageAt(scanner()->location(), MessageTemplate::kNoCatchOrFinally); + *ok = false; + return Statement::Default(); + } + if (tok == Token::CATCH) { + Consume(Token::CATCH); + Expect(Token::LPAREN, CHECK_OK); + ExpressionClassifier pattern_classifier; + ParsePrimaryExpression(&pattern_classifier, CHECK_OK); + ValidateBindingPattern(&pattern_classifier, CHECK_OK); + Expect(Token::RPAREN, CHECK_OK); + { + // TODO(adamk): Make this CATCH_SCOPE + Scope* with_scope = NewScope(scope_, WITH_SCOPE); + BlockState block_state(&scope_, with_scope); + ParseBlock(CHECK_OK); + } + tok = peek(); + } + if (tok == Token::FINALLY) { + Consume(Token::FINALLY); + ParseBlock(CHECK_OK); + } + return Statement::Default(); +} + + +PreParser::Statement PreParser::ParseDebuggerStatement(bool* ok) { + // In ECMA-262 'debugger' is defined as a reserved keyword. In some browser + // contexts this is used as a statement which invokes the debugger as if a + // break point is present. + // DebuggerStatement :: + // 'debugger' ';' + + Expect(Token::DEBUGGER, CHECK_OK); + ExpectSemicolon(ok); + return Statement::Default(); +} + + +#undef CHECK_OK +#define CHECK_OK ok); \ + if (!*ok) return Expression::Default(); \ + ((void)0 +#define DUMMY ) // to make indentation work +#undef DUMMY + + +PreParser::Expression PreParser::ParseFunctionLiteral( + Identifier function_name, Scanner::Location function_name_location, + FunctionNameValidity function_name_validity, FunctionKind kind, + int function_token_pos, FunctionLiteral::FunctionType function_type, + FunctionLiteral::ArityRestriction arity_restriction, + LanguageMode language_mode, bool* ok) { + // Function :: + // '(' FormalParameterList? ')' '{' FunctionBody '}' + + // Parse function body. + bool outer_is_script_scope = scope_->is_script_scope(); + Scope* function_scope = NewScope(scope_, FUNCTION_SCOPE, kind); + function_scope->SetLanguageMode(language_mode); + PreParserFactory factory(NULL); + FunctionState function_state(&function_state_, &scope_, function_scope, kind, + &factory); + DuplicateFinder duplicate_finder(scanner()->unicode_cache()); + ExpressionClassifier formals_classifier(&duplicate_finder); + + Expect(Token::LPAREN, CHECK_OK); + int start_position = scanner()->location().beg_pos; + function_scope->set_start_position(start_position); + PreParserFormalParameters formals(function_scope); + ParseFormalParameterList(&formals, &formals_classifier, CHECK_OK); + Expect(Token::RPAREN, CHECK_OK); + int formals_end_position = scanner()->location().end_pos; + + CheckArityRestrictions(formals.arity, arity_restriction, + formals.has_rest, start_position, + formals_end_position, CHECK_OK); + + // See Parser::ParseFunctionLiteral for more information about lazy parsing + // and lazy compilation. + bool is_lazily_parsed = + (outer_is_script_scope && allow_lazy() && !parenthesized_function_); + parenthesized_function_ = false; + + Expect(Token::LBRACE, CHECK_OK); + if (is_lazily_parsed) { + ParseLazyFunctionLiteralBody(CHECK_OK); + } else { + ParseStatementList(Token::RBRACE, CHECK_OK); + } + Expect(Token::RBRACE, CHECK_OK); + + // Parsing the body may change the language mode in our scope. + language_mode = function_scope->language_mode(); + + // Validate name and parameter names. We can do this only after parsing the + // function, since the function can declare itself strict. + CheckFunctionName(language_mode, function_name, function_name_validity, + function_name_location, CHECK_OK); + const bool allow_duplicate_parameters = + is_sloppy(language_mode) && formals.is_simple && !IsConciseMethod(kind); + ValidateFormalParameters(&formals_classifier, language_mode, + allow_duplicate_parameters, CHECK_OK); + + if (is_strict(language_mode)) { + int end_position = scanner()->location().end_pos; + CheckStrictOctalLiteral(start_position, end_position, CHECK_OK); + } + + if (is_strong(language_mode) && IsSubclassConstructor(kind)) { + if (!function_state.super_location().IsValid()) { + ReportMessageAt(function_name_location, + MessageTemplate::kStrongSuperCallMissing, + kReferenceError); + *ok = false; + return Expression::Default(); + } + } + + return Expression::Default(); +} + + +void PreParser::ParseLazyFunctionLiteralBody(bool* ok, + Scanner::BookmarkScope* bookmark) { + int body_start = position(); + ParseStatementList(Token::RBRACE, ok, bookmark); + if (!*ok) return; + if (bookmark && bookmark->HasBeenReset()) return; + + // Position right after terminal '}'. + DCHECK_EQ(Token::RBRACE, scanner()->peek()); + int body_end = scanner()->peek_location().end_pos; + log_->LogFunction(body_start, body_end, + function_state_->materialized_literal_count(), + function_state_->expected_property_count(), language_mode(), + scope_->uses_super_property(), scope_->calls_eval()); +} + + +PreParserExpression PreParser::ParseClassLiteral( + PreParserIdentifier name, Scanner::Location class_name_location, + bool name_is_strict_reserved, int pos, bool* ok) { + // All parts of a ClassDeclaration and ClassExpression are strict code. + if (name_is_strict_reserved) { + ReportMessageAt(class_name_location, + MessageTemplate::kUnexpectedStrictReserved); + *ok = false; + return EmptyExpression(); + } + if (IsEvalOrArguments(name)) { + ReportMessageAt(class_name_location, MessageTemplate::kStrictEvalArguments); + *ok = false; + return EmptyExpression(); + } + LanguageMode class_language_mode = language_mode(); + if (is_strong(class_language_mode) && IsUndefined(name)) { + ReportMessageAt(class_name_location, MessageTemplate::kStrongUndefined); + *ok = false; + return EmptyExpression(); + } + + Scope* scope = NewScope(scope_, BLOCK_SCOPE); + BlockState block_state(&scope_, scope); + scope_->SetLanguageMode( + static_cast<LanguageMode>(class_language_mode | STRICT)); + // TODO(marja): Make PreParser use scope names too. + // scope_->SetScopeName(name); + + bool has_extends = Check(Token::EXTENDS); + if (has_extends) { + ExpressionClassifier classifier; + ParseLeftHandSideExpression(&classifier, CHECK_OK); + ValidateExpression(&classifier, CHECK_OK); + } + + ClassLiteralChecker checker(this); + bool has_seen_constructor = false; + + Expect(Token::LBRACE, CHECK_OK); + while (peek() != Token::RBRACE) { + if (Check(Token::SEMICOLON)) continue; + const bool in_class = true; + const bool is_static = false; + bool is_computed_name = false; // Classes do not care about computed + // property names here. + Identifier name; + ExpressionClassifier classifier; + ParsePropertyDefinition(&checker, in_class, has_extends, is_static, + &is_computed_name, &has_seen_constructor, + &classifier, &name, CHECK_OK); + ValidateExpression(&classifier, CHECK_OK); + } + + Expect(Token::RBRACE, CHECK_OK); + + return Expression::Default(); +} + + +PreParser::Expression PreParser::ParseV8Intrinsic(bool* ok) { + // CallRuntime :: + // '%' Identifier Arguments + Expect(Token::MOD, CHECK_OK); + if (!allow_natives()) { + *ok = false; + return Expression::Default(); + } + // Allow "eval" or "arguments" for backward compatibility. + ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK); + Scanner::Location spread_pos; + ExpressionClassifier classifier; + ParseArguments(&spread_pos, &classifier, ok); + ValidateExpression(&classifier, CHECK_OK); + + DCHECK(!spread_pos.IsValid()); + + return Expression::Default(); +} + + +PreParserExpression PreParser::ParseDoExpression(bool* ok) { + // AssignmentExpression :: + // do '{' StatementList '}' + Expect(Token::DO, CHECK_OK); + Expect(Token::LBRACE, CHECK_OK); + Scope* block_scope = NewScope(scope_, BLOCK_SCOPE); + { + BlockState block_state(&scope_, block_scope); + while (peek() != Token::RBRACE) { + ParseStatementListItem(CHECK_OK); + } + Expect(Token::RBRACE, CHECK_OK); + return PreParserExpression::Default(); + } +} + +#undef CHECK_OK + + +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/parsing/preparser.h b/deps/v8/src/parsing/preparser.h new file mode 100644 index 0000000000..59100f1ae9 --- /dev/null +++ b/deps/v8/src/parsing/preparser.h @@ -0,0 +1,1175 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_PARSING_PREPARSER_H +#define V8_PARSING_PREPARSER_H + +#include "src/ast/scopes.h" +#include "src/bailout-reason.h" +#include "src/hashmap.h" +#include "src/messages.h" +#include "src/parsing/expression-classifier.h" +#include "src/parsing/func-name-inferrer.h" +#include "src/parsing/parser-base.h" +#include "src/parsing/scanner.h" +#include "src/parsing/token.h" + +namespace v8 { +namespace internal { + + +class PreParserIdentifier { + public: + PreParserIdentifier() : type_(kUnknownIdentifier) {} + static PreParserIdentifier Default() { + return PreParserIdentifier(kUnknownIdentifier); + } + static PreParserIdentifier Eval() { + return PreParserIdentifier(kEvalIdentifier); + } + static PreParserIdentifier Arguments() { + return PreParserIdentifier(kArgumentsIdentifier); + } + static PreParserIdentifier Undefined() { + return PreParserIdentifier(kUndefinedIdentifier); + } + static PreParserIdentifier FutureReserved() { + return PreParserIdentifier(kFutureReservedIdentifier); + } + static PreParserIdentifier FutureStrictReserved() { + return PreParserIdentifier(kFutureStrictReservedIdentifier); + } + static PreParserIdentifier Let() { + return PreParserIdentifier(kLetIdentifier); + } + static PreParserIdentifier Static() { + return PreParserIdentifier(kStaticIdentifier); + } + static PreParserIdentifier Yield() { + return PreParserIdentifier(kYieldIdentifier); + } + static PreParserIdentifier Prototype() { + return PreParserIdentifier(kPrototypeIdentifier); + } + static PreParserIdentifier Constructor() { + return PreParserIdentifier(kConstructorIdentifier); + } + bool IsEval() const { return type_ == kEvalIdentifier; } + bool IsArguments() const { return type_ == kArgumentsIdentifier; } + bool IsEvalOrArguments() const { return IsEval() || IsArguments(); } + bool IsUndefined() const { return type_ == kUndefinedIdentifier; } + bool IsLet() const { return type_ == kLetIdentifier; } + bool IsStatic() const { return type_ == kStaticIdentifier; } + bool IsYield() const { return type_ == kYieldIdentifier; } + bool IsPrototype() const { return type_ == kPrototypeIdentifier; } + bool IsConstructor() const { return type_ == kConstructorIdentifier; } + bool IsFutureReserved() const { return type_ == kFutureReservedIdentifier; } + bool IsFutureStrictReserved() const { + return type_ == kFutureStrictReservedIdentifier || + type_ == kLetIdentifier || type_ == kStaticIdentifier || + type_ == kYieldIdentifier; + } + + // Allow identifier->name()[->length()] to work. The preparser + // does not need the actual positions/lengths of the identifiers. + const PreParserIdentifier* operator->() const { return this; } + const PreParserIdentifier raw_name() const { return *this; } + + int position() const { return 0; } + int length() const { return 0; } + + private: + enum Type { + kUnknownIdentifier, + kFutureReservedIdentifier, + kFutureStrictReservedIdentifier, + kLetIdentifier, + kStaticIdentifier, + kYieldIdentifier, + kEvalIdentifier, + kArgumentsIdentifier, + kUndefinedIdentifier, + kPrototypeIdentifier, + kConstructorIdentifier + }; + + explicit PreParserIdentifier(Type type) : type_(type) {} + Type type_; + + friend class PreParserExpression; +}; + + +class PreParserExpression { + public: + static PreParserExpression Default() { + return PreParserExpression(TypeField::encode(kExpression)); + } + + static PreParserExpression Spread(PreParserExpression expression) { + return PreParserExpression(TypeField::encode(kSpreadExpression)); + } + + static PreParserExpression FromIdentifier(PreParserIdentifier id) { + return PreParserExpression(TypeField::encode(kIdentifierExpression) | + IdentifierTypeField::encode(id.type_)); + } + + static PreParserExpression BinaryOperation(PreParserExpression left, + Token::Value op, + PreParserExpression right) { + return PreParserExpression(TypeField::encode(kBinaryOperationExpression)); + } + + static PreParserExpression Assignment() { + return PreParserExpression(TypeField::encode(kExpression) | + ExpressionTypeField::encode(kAssignment)); + } + + static PreParserExpression ObjectLiteral() { + return PreParserExpression(TypeField::encode(kObjectLiteralExpression)); + } + + static PreParserExpression ArrayLiteral() { + return PreParserExpression(TypeField::encode(kArrayLiteralExpression)); + } + + static PreParserExpression StringLiteral() { + return PreParserExpression(TypeField::encode(kStringLiteralExpression)); + } + + static PreParserExpression UseStrictStringLiteral() { + return PreParserExpression(TypeField::encode(kStringLiteralExpression) | + IsUseStrictField::encode(true)); + } + + static PreParserExpression UseStrongStringLiteral() { + return PreParserExpression(TypeField::encode(kStringLiteralExpression) | + IsUseStrongField::encode(true)); + } + + static PreParserExpression This() { + return PreParserExpression(TypeField::encode(kExpression) | + ExpressionTypeField::encode(kThisExpression)); + } + + static PreParserExpression ThisProperty() { + return PreParserExpression( + TypeField::encode(kExpression) | + ExpressionTypeField::encode(kThisPropertyExpression)); + } + + static PreParserExpression Property() { + return PreParserExpression( + TypeField::encode(kExpression) | + ExpressionTypeField::encode(kPropertyExpression)); + } + + static PreParserExpression Call() { + return PreParserExpression(TypeField::encode(kExpression) | + ExpressionTypeField::encode(kCallExpression)); + } + + static PreParserExpression SuperCallReference() { + return PreParserExpression( + TypeField::encode(kExpression) | + ExpressionTypeField::encode(kSuperCallReference)); + } + + static PreParserExpression NoTemplateTag() { + return PreParserExpression( + TypeField::encode(kExpression) | + ExpressionTypeField::encode(kNoTemplateTagExpression)); + } + + bool IsIdentifier() const { + return TypeField::decode(code_) == kIdentifierExpression; + } + + PreParserIdentifier AsIdentifier() const { + DCHECK(IsIdentifier()); + return PreParserIdentifier(IdentifierTypeField::decode(code_)); + } + + bool IsAssignment() const { + return TypeField::decode(code_) == kExpression && + ExpressionTypeField::decode(code_) == kAssignment; + } + + bool IsObjectLiteral() const { + return TypeField::decode(code_) == kObjectLiteralExpression; + } + + bool IsArrayLiteral() const { + return TypeField::decode(code_) == kArrayLiteralExpression; + } + + bool IsStringLiteral() const { + return TypeField::decode(code_) == kStringLiteralExpression; + } + + bool IsUseStrictLiteral() const { + return TypeField::decode(code_) == kStringLiteralExpression && + IsUseStrictField::decode(code_); + } + + bool IsUseStrongLiteral() const { + return TypeField::decode(code_) == kStringLiteralExpression && + IsUseStrongField::decode(code_); + } + + bool IsThis() const { + return TypeField::decode(code_) == kExpression && + ExpressionTypeField::decode(code_) == kThisExpression; + } + + bool IsThisProperty() const { + return TypeField::decode(code_) == kExpression && + ExpressionTypeField::decode(code_) == kThisPropertyExpression; + } + + bool IsProperty() const { + return TypeField::decode(code_) == kExpression && + (ExpressionTypeField::decode(code_) == kPropertyExpression || + ExpressionTypeField::decode(code_) == kThisPropertyExpression); + } + + bool IsCall() const { + return TypeField::decode(code_) == kExpression && + ExpressionTypeField::decode(code_) == kCallExpression; + } + + bool IsSuperCallReference() const { + return TypeField::decode(code_) == kExpression && + ExpressionTypeField::decode(code_) == kSuperCallReference; + } + + bool IsValidReferenceExpression() const { + return IsIdentifier() || IsProperty(); + } + + // At the moment PreParser doesn't track these expression types. + bool IsFunctionLiteral() const { return false; } + bool IsCallNew() const { return false; } + + bool IsNoTemplateTag() const { + return TypeField::decode(code_) == kExpression && + ExpressionTypeField::decode(code_) == kNoTemplateTagExpression; + } + + bool IsSpreadExpression() const { + return TypeField::decode(code_) == kSpreadExpression; + } + + PreParserExpression AsFunctionLiteral() { return *this; } + + bool IsBinaryOperation() const { + return TypeField::decode(code_) == kBinaryOperationExpression; + } + + // Dummy implementation for making expression->somefunc() work in both Parser + // and PreParser. + PreParserExpression* operator->() { return this; } + + // More dummy implementations of things PreParser doesn't need to track: + void set_index(int index) {} // For YieldExpressions + void set_should_eager_compile() {} + + int position() const { return RelocInfo::kNoPosition; } + void set_function_token_position(int position) {} + + // Parenthesized expressions in the form `( Expression )`. + void set_is_parenthesized() { + code_ = ParenthesizedField::update(code_, true); + } + bool is_parenthesized() const { return ParenthesizedField::decode(code_); } + + private: + enum Type { + kExpression, + kIdentifierExpression, + kStringLiteralExpression, + kBinaryOperationExpression, + kSpreadExpression, + kObjectLiteralExpression, + kArrayLiteralExpression + }; + + enum ExpressionType { + kThisExpression, + kThisPropertyExpression, + kPropertyExpression, + kCallExpression, + kSuperCallReference, + kNoTemplateTagExpression, + kAssignment + }; + + explicit PreParserExpression(uint32_t expression_code) + : code_(expression_code) {} + + // The first three bits are for the Type. + typedef BitField<Type, 0, 3> TypeField; + + // The high order bit applies only to nodes which would inherit from the + // Expression ASTNode --- This is by necessity, due to the fact that + // Expression nodes may be represented as multiple Types, not exclusively + // through kExpression. + // TODO(caitp, adamk): clean up PreParserExpression bitfields. + typedef BitField<bool, 31, 1> ParenthesizedField; + + // The rest of the bits are interpreted depending on the value + // of the Type field, so they can share the storage. + typedef BitField<ExpressionType, TypeField::kNext, 3> ExpressionTypeField; + typedef BitField<bool, TypeField::kNext, 1> IsUseStrictField; + typedef BitField<bool, IsUseStrictField::kNext, 1> IsUseStrongField; + typedef BitField<PreParserIdentifier::Type, TypeField::kNext, 10> + IdentifierTypeField; + typedef BitField<bool, TypeField::kNext, 1> HasCoverInitializedNameField; + + uint32_t code_; +}; + + +// The pre-parser doesn't need to build lists of expressions, identifiers, or +// the like. +template <typename T> +class PreParserList { + public: + // These functions make list->Add(some_expression) work (and do nothing). + PreParserList() : length_(0) {} + PreParserList* operator->() { return this; } + void Add(T, void*) { ++length_; } + int length() const { return length_; } + private: + int length_; +}; + + +typedef PreParserList<PreParserExpression> PreParserExpressionList; + + +class PreParserStatement { + public: + static PreParserStatement Default() { + return PreParserStatement(kUnknownStatement); + } + + static PreParserStatement Jump() { + return PreParserStatement(kJumpStatement); + } + + static PreParserStatement FunctionDeclaration() { + return PreParserStatement(kFunctionDeclaration); + } + + // Creates expression statement from expression. + // Preserves being an unparenthesized string literal, possibly + // "use strict". + static PreParserStatement ExpressionStatement( + PreParserExpression expression) { + if (expression.IsUseStrictLiteral()) { + return PreParserStatement(kUseStrictExpressionStatement); + } + if (expression.IsUseStrongLiteral()) { + return PreParserStatement(kUseStrongExpressionStatement); + } + if (expression.IsStringLiteral()) { + return PreParserStatement(kStringLiteralExpressionStatement); + } + return Default(); + } + + bool IsStringLiteral() { + return code_ == kStringLiteralExpressionStatement; + } + + bool IsUseStrictLiteral() { + return code_ == kUseStrictExpressionStatement; + } + + bool IsUseStrongLiteral() { return code_ == kUseStrongExpressionStatement; } + + bool IsFunctionDeclaration() { + return code_ == kFunctionDeclaration; + } + + bool IsJumpStatement() { + return code_ == kJumpStatement; + } + + private: + enum Type { + kUnknownStatement, + kJumpStatement, + kStringLiteralExpressionStatement, + kUseStrictExpressionStatement, + kUseStrongExpressionStatement, + kFunctionDeclaration + }; + + explicit PreParserStatement(Type code) : code_(code) {} + Type code_; +}; + + +typedef PreParserList<PreParserStatement> PreParserStatementList; + + +class PreParserFactory { + public: + explicit PreParserFactory(void* unused_value_factory) {} + PreParserExpression NewStringLiteral(PreParserIdentifier identifier, + int pos) { + return PreParserExpression::Default(); + } + PreParserExpression NewNumberLiteral(double number, + int pos) { + return PreParserExpression::Default(); + } + PreParserExpression NewRegExpLiteral(PreParserIdentifier js_pattern, + int js_flags, int literal_index, + bool is_strong, int pos) { + return PreParserExpression::Default(); + } + PreParserExpression NewArrayLiteral(PreParserExpressionList values, + int literal_index, + bool is_strong, + int pos) { + return PreParserExpression::ArrayLiteral(); + } + PreParserExpression NewArrayLiteral(PreParserExpressionList values, + int first_spread_index, int literal_index, + bool is_strong, int pos) { + return PreParserExpression::ArrayLiteral(); + } + PreParserExpression NewObjectLiteralProperty(PreParserExpression key, + PreParserExpression value, + ObjectLiteralProperty::Kind kind, + bool is_static, + bool is_computed_name) { + return PreParserExpression::Default(); + } + PreParserExpression NewObjectLiteralProperty(PreParserExpression key, + PreParserExpression value, + bool is_static, + bool is_computed_name) { + return PreParserExpression::Default(); + } + PreParserExpression NewObjectLiteral(PreParserExpressionList properties, + int literal_index, + int boilerplate_properties, + bool has_function, + bool is_strong, + int pos) { + return PreParserExpression::ObjectLiteral(); + } + PreParserExpression NewVariableProxy(void* variable) { + return PreParserExpression::Default(); + } + PreParserExpression NewProperty(PreParserExpression obj, + PreParserExpression key, + int pos) { + if (obj.IsThis()) { + return PreParserExpression::ThisProperty(); + } + return PreParserExpression::Property(); + } + PreParserExpression NewUnaryOperation(Token::Value op, + PreParserExpression expression, + int pos) { + return PreParserExpression::Default(); + } + PreParserExpression NewBinaryOperation(Token::Value op, + PreParserExpression left, + PreParserExpression right, int pos) { + return PreParserExpression::BinaryOperation(left, op, right); + } + PreParserExpression NewCompareOperation(Token::Value op, + PreParserExpression left, + PreParserExpression right, int pos) { + return PreParserExpression::Default(); + } + PreParserExpression NewRewritableAssignmentExpression( + PreParserExpression expression) { + return expression; + } + PreParserExpression NewAssignment(Token::Value op, + PreParserExpression left, + PreParserExpression right, + int pos) { + return PreParserExpression::Assignment(); + } + PreParserExpression NewYield(PreParserExpression generator_object, + PreParserExpression expression, + Yield::Kind yield_kind, + int pos) { + return PreParserExpression::Default(); + } + PreParserExpression NewConditional(PreParserExpression condition, + PreParserExpression then_expression, + PreParserExpression else_expression, + int pos) { + return PreParserExpression::Default(); + } + PreParserExpression NewCountOperation(Token::Value op, + bool is_prefix, + PreParserExpression expression, + int pos) { + return PreParserExpression::Default(); + } + PreParserExpression NewCall(PreParserExpression expression, + PreParserExpressionList arguments, + int pos) { + return PreParserExpression::Call(); + } + PreParserExpression NewCallNew(PreParserExpression expression, + PreParserExpressionList arguments, + int pos) { + return PreParserExpression::Default(); + } + PreParserExpression NewCallRuntime(const AstRawString* name, + const Runtime::Function* function, + PreParserExpressionList arguments, + int pos) { + return PreParserExpression::Default(); + } + PreParserStatement NewReturnStatement(PreParserExpression expression, + int pos) { + return PreParserStatement::Default(); + } + PreParserExpression NewFunctionLiteral( + PreParserIdentifier name, Scope* scope, PreParserStatementList body, + int materialized_literal_count, int expected_property_count, + int parameter_count, + FunctionLiteral::ParameterFlag has_duplicate_parameters, + FunctionLiteral::FunctionType function_type, + FunctionLiteral::EagerCompileHint eager_compile_hint, FunctionKind kind, + int position) { + return PreParserExpression::Default(); + } + + PreParserExpression NewSpread(PreParserExpression expression, int pos) { + return PreParserExpression::Spread(expression); + } + + PreParserExpression NewEmptyParentheses(int pos) { + return PreParserExpression::Default(); + } + + // Return the object itself as AstVisitor and implement the needed + // dummy method right in this class. + PreParserFactory* visitor() { return this; } + int* ast_properties() { + static int dummy = 42; + return &dummy; + } +}; + + +struct PreParserFormalParameters : FormalParametersBase { + explicit PreParserFormalParameters(Scope* scope) + : FormalParametersBase(scope) {} + int arity = 0; + + int Arity() const { return arity; } + PreParserIdentifier at(int i) { return PreParserIdentifier(); } // Dummy +}; + + +class PreParser; + +class PreParserTraits { + public: + struct Type { + // TODO(marja): To be removed. The Traits object should contain all the data + // it needs. + typedef PreParser* Parser; + + // PreParser doesn't need to store generator variables. + typedef void GeneratorVariable; + + typedef int AstProperties; + + // Return types for traversing functions. + typedef PreParserIdentifier Identifier; + typedef PreParserExpression Expression; + typedef PreParserExpression YieldExpression; + typedef PreParserExpression FunctionLiteral; + typedef PreParserExpression ClassLiteral; + typedef PreParserExpression ObjectLiteralProperty; + typedef PreParserExpression Literal; + typedef PreParserExpressionList ExpressionList; + typedef PreParserExpressionList PropertyList; + typedef PreParserIdentifier FormalParameter; + typedef PreParserFormalParameters FormalParameters; + typedef PreParserStatementList StatementList; + + // For constructing objects returned by the traversing functions. + typedef PreParserFactory Factory; + }; + + explicit PreParserTraits(PreParser* pre_parser) : pre_parser_(pre_parser) {} + + // Helper functions for recursive descent. + static bool IsEval(PreParserIdentifier identifier) { + return identifier.IsEval(); + } + + static bool IsArguments(PreParserIdentifier identifier) { + return identifier.IsArguments(); + } + + static bool IsEvalOrArguments(PreParserIdentifier identifier) { + return identifier.IsEvalOrArguments(); + } + + static bool IsUndefined(PreParserIdentifier identifier) { + return identifier.IsUndefined(); + } + + static bool IsPrototype(PreParserIdentifier identifier) { + return identifier.IsPrototype(); + } + + static bool IsConstructor(PreParserIdentifier identifier) { + return identifier.IsConstructor(); + } + + // Returns true if the expression is of type "this.foo". + static bool IsThisProperty(PreParserExpression expression) { + return expression.IsThisProperty(); + } + + static bool IsIdentifier(PreParserExpression expression) { + return expression.IsIdentifier(); + } + + static PreParserIdentifier AsIdentifier(PreParserExpression expression) { + return expression.AsIdentifier(); + } + + static bool IsFutureStrictReserved(PreParserIdentifier identifier) { + return identifier.IsFutureStrictReserved(); + } + + static bool IsBoilerplateProperty(PreParserExpression property) { + // PreParser doesn't count boilerplate properties. + return false; + } + + static bool IsArrayIndex(PreParserIdentifier string, uint32_t* index) { + return false; + } + + static PreParserExpression GetPropertyValue(PreParserExpression property) { + return PreParserExpression::Default(); + } + + // Functions for encapsulating the differences between parsing and preparsing; + // operations interleaved with the recursive descent. + static void PushLiteralName(FuncNameInferrer* fni, PreParserIdentifier id) { + // PreParser should not use FuncNameInferrer. + UNREACHABLE(); + } + + static void PushPropertyName(FuncNameInferrer* fni, + PreParserExpression expression) { + // PreParser should not use FuncNameInferrer. + UNREACHABLE(); + } + + static void InferFunctionName(FuncNameInferrer* fni, + PreParserExpression expression) { + // PreParser should not use FuncNameInferrer. + UNREACHABLE(); + } + + static void CheckFunctionLiteralInsideTopLevelObjectLiteral( + Scope* scope, PreParserExpression property, bool* has_function) {} + + static void CheckAssigningFunctionLiteralToProperty( + PreParserExpression left, PreParserExpression right) {} + + static PreParserExpression MarkExpressionAsAssigned( + PreParserExpression expression) { + // TODO(marja): To be able to produce the same errors, the preparser needs + // to start tracking which expressions are variables and which are assigned. + return expression; + } + + bool ShortcutNumericLiteralBinaryExpression(PreParserExpression* x, + PreParserExpression y, + Token::Value op, + int pos, + PreParserFactory* factory) { + return false; + } + + PreParserExpression BuildUnaryExpression(PreParserExpression expression, + Token::Value op, int pos, + PreParserFactory* factory) { + return PreParserExpression::Default(); + } + + PreParserExpression NewThrowReferenceError(MessageTemplate::Template message, + int pos) { + return PreParserExpression::Default(); + } + PreParserExpression NewThrowSyntaxError(MessageTemplate::Template message, + Handle<Object> arg, int pos) { + return PreParserExpression::Default(); + } + PreParserExpression NewThrowTypeError(MessageTemplate::Template message, + Handle<Object> arg, int pos) { + return PreParserExpression::Default(); + } + + // Reporting errors. + void ReportMessageAt(Scanner::Location location, + MessageTemplate::Template message, + const char* arg = NULL, + ParseErrorType error_type = kSyntaxError); + void ReportMessageAt(int start_pos, int end_pos, + MessageTemplate::Template message, + const char* arg = NULL, + ParseErrorType error_type = kSyntaxError); + + // "null" return type creators. + static PreParserIdentifier EmptyIdentifier() { + return PreParserIdentifier::Default(); + } + static PreParserIdentifier EmptyIdentifierString() { + return PreParserIdentifier::Default(); + } + static PreParserExpression EmptyExpression() { + return PreParserExpression::Default(); + } + static PreParserExpression EmptyLiteral() { + return PreParserExpression::Default(); + } + static PreParserExpression EmptyObjectLiteralProperty() { + return PreParserExpression::Default(); + } + static PreParserExpression EmptyFunctionLiteral() { + return PreParserExpression::Default(); + } + static PreParserExpressionList NullExpressionList() { + return PreParserExpressionList(); + } + + // Odd-ball literal creators. + static PreParserExpression GetLiteralTheHole(int position, + PreParserFactory* factory) { + return PreParserExpression::Default(); + } + + // Producing data during the recursive descent. + PreParserIdentifier GetSymbol(Scanner* scanner); + PreParserIdentifier GetNumberAsSymbol(Scanner* scanner); + + static PreParserIdentifier GetNextSymbol(Scanner* scanner) { + return PreParserIdentifier::Default(); + } + + static PreParserExpression ThisExpression(Scope* scope, + PreParserFactory* factory, + int pos) { + return PreParserExpression::This(); + } + + static PreParserExpression SuperPropertyReference(Scope* scope, + PreParserFactory* factory, + int pos) { + return PreParserExpression::Default(); + } + + static PreParserExpression SuperCallReference(Scope* scope, + PreParserFactory* factory, + int pos) { + return PreParserExpression::SuperCallReference(); + } + + static PreParserExpression NewTargetExpression(Scope* scope, + PreParserFactory* factory, + int pos) { + return PreParserExpression::Default(); + } + + static PreParserExpression DefaultConstructor(bool call_super, Scope* scope, + int pos, int end_pos) { + return PreParserExpression::Default(); + } + + static PreParserExpression ExpressionFromLiteral( + Token::Value token, int pos, Scanner* scanner, + PreParserFactory* factory) { + return PreParserExpression::Default(); + } + + static PreParserExpression ExpressionFromIdentifier( + PreParserIdentifier name, int start_position, int end_position, + Scope* scope, PreParserFactory* factory) { + return PreParserExpression::FromIdentifier(name); + } + + PreParserExpression ExpressionFromString(int pos, + Scanner* scanner, + PreParserFactory* factory = NULL); + + PreParserExpression GetIterator(PreParserExpression iterable, + PreParserFactory* factory, int pos) { + return PreParserExpression::Default(); + } + + static PreParserExpressionList NewExpressionList(int size, Zone* zone) { + return PreParserExpressionList(); + } + + static PreParserStatementList NewStatementList(int size, Zone* zone) { + return PreParserStatementList(); + } + + static PreParserExpressionList NewPropertyList(int size, Zone* zone) { + return PreParserExpressionList(); + } + + static void AddParameterInitializationBlock( + const PreParserFormalParameters& parameters, + PreParserStatementList list, bool* ok) {} + + V8_INLINE void SkipLazyFunctionBody(int* materialized_literal_count, + int* expected_property_count, bool* ok) { + UNREACHABLE(); + } + + V8_INLINE PreParserStatementList ParseEagerFunctionBody( + PreParserIdentifier function_name, int pos, + const PreParserFormalParameters& parameters, FunctionKind kind, + FunctionLiteral::FunctionType function_type, bool* ok); + + V8_INLINE void ParseArrowFunctionFormalParameterList( + PreParserFormalParameters* parameters, + PreParserExpression expression, const Scanner::Location& params_loc, + Scanner::Location* duplicate_loc, bool* ok); + + void ReindexLiterals(const PreParserFormalParameters& paramaters) {} + + struct TemplateLiteralState {}; + + TemplateLiteralState OpenTemplateLiteral(int pos) { + return TemplateLiteralState(); + } + void AddTemplateSpan(TemplateLiteralState*, bool) {} + void AddTemplateExpression(TemplateLiteralState*, PreParserExpression) {} + PreParserExpression CloseTemplateLiteral(TemplateLiteralState*, int, + PreParserExpression tag) { + if (IsTaggedTemplate(tag)) { + // Emulate generation of array literals for tag callsite + // 1st is array of cooked strings, second is array of raw strings + MaterializeTemplateCallsiteLiterals(); + } + return EmptyExpression(); + } + inline void MaterializeTemplateCallsiteLiterals(); + PreParserExpression NoTemplateTag() { + return PreParserExpression::NoTemplateTag(); + } + static bool IsTaggedTemplate(const PreParserExpression tag) { + return !tag.IsNoTemplateTag(); + } + + void AddFormalParameter(PreParserFormalParameters* parameters, + PreParserExpression pattern, + PreParserExpression initializer, + int initializer_end_position, bool is_rest) { + ++parameters->arity; + } + void DeclareFormalParameter(Scope* scope, PreParserIdentifier parameter, + ExpressionClassifier* classifier) { + if (!classifier->is_simple_parameter_list()) { + scope->SetHasNonSimpleParameters(); + } + } + + void CheckConflictingVarDeclarations(Scope* scope, bool* ok) {} + + // Temporary glue; these functions will move to ParserBase. + PreParserExpression ParseV8Intrinsic(bool* ok); + V8_INLINE PreParserExpression ParseDoExpression(bool* ok); + PreParserExpression ParseFunctionLiteral( + PreParserIdentifier name, Scanner::Location function_name_location, + FunctionNameValidity function_name_validity, FunctionKind kind, + int function_token_position, FunctionLiteral::FunctionType type, + FunctionLiteral::ArityRestriction arity_restriction, + LanguageMode language_mode, bool* ok); + + PreParserExpression ParseClassLiteral(PreParserIdentifier name, + Scanner::Location class_name_location, + bool name_is_strict_reserved, int pos, + bool* ok); + + PreParserExpressionList PrepareSpreadArguments(PreParserExpressionList list) { + return list; + } + + inline void MaterializeUnspreadArgumentsLiterals(int count); + + inline PreParserExpression SpreadCall(PreParserExpression function, + PreParserExpressionList args, int pos); + + inline PreParserExpression SpreadCallNew(PreParserExpression function, + PreParserExpressionList args, + int pos); + + inline void RewriteDestructuringAssignments() {} + + inline void QueueDestructuringAssignmentForRewriting(PreParserExpression) {} + + void SetFunctionNameFromPropertyName(PreParserExpression, + PreParserIdentifier) {} + void SetFunctionNameFromIdentifierRef(PreParserExpression, + PreParserExpression) {} + + inline PreParserExpression RewriteNonPattern( + PreParserExpression expr, const ExpressionClassifier* classifier, + bool* ok); + inline PreParserExpression RewriteNonPatternArguments( + PreParserExpression args, const ExpressionClassifier* classifier, + bool* ok); + inline PreParserExpression RewriteNonPatternObjectLiteralProperty( + PreParserExpression property, const ExpressionClassifier* classifier, + bool* ok); + + private: + PreParser* pre_parser_; +}; + + +// Preparsing checks a JavaScript program and emits preparse-data that helps +// a later parsing to be faster. +// See preparse-data-format.h for the data format. + +// The PreParser checks that the syntax follows the grammar for JavaScript, +// and collects some information about the program along the way. +// The grammar check is only performed in order to understand the program +// sufficiently to deduce some information about it, that can be used +// to speed up later parsing. Finding errors is not the goal of pre-parsing, +// rather it is to speed up properly written and correct programs. +// That means that contextual checks (like a label being declared where +// it is used) are generally omitted. +class PreParser : public ParserBase<PreParserTraits> { + public: + typedef PreParserIdentifier Identifier; + typedef PreParserExpression Expression; + typedef PreParserStatement Statement; + + enum PreParseResult { + kPreParseStackOverflow, + kPreParseSuccess + }; + + PreParser(Zone* zone, Scanner* scanner, AstValueFactory* ast_value_factory, + ParserRecorder* log, uintptr_t stack_limit) + : ParserBase<PreParserTraits>(zone, scanner, stack_limit, NULL, + ast_value_factory, log, this) {} + + // Pre-parse the program from the character stream; returns true on + // success (even if parsing failed, the pre-parse data successfully + // captured the syntax error), and false if a stack-overflow happened + // during parsing. + PreParseResult PreParseProgram(int* materialized_literals = 0) { + Scope* scope = NewScope(scope_, SCRIPT_SCOPE); + PreParserFactory factory(NULL); + FunctionState top_scope(&function_state_, &scope_, scope, kNormalFunction, + &factory); + bool ok = true; + int start_position = scanner()->peek_location().beg_pos; + ParseStatementList(Token::EOS, &ok); + if (stack_overflow()) return kPreParseStackOverflow; + if (!ok) { + ReportUnexpectedToken(scanner()->current_token()); + } else if (is_strict(scope_->language_mode())) { + CheckStrictOctalLiteral(start_position, scanner()->location().end_pos, + &ok); + } + if (materialized_literals) { + *materialized_literals = function_state_->materialized_literal_count(); + } + return kPreParseSuccess; + } + + // Parses a single function literal, from the opening parentheses before + // parameters to the closing brace after the body. + // Returns a FunctionEntry describing the body of the function in enough + // detail that it can be lazily compiled. + // The scanner is expected to have matched the "function" or "function*" + // keyword and parameters, and have consumed the initial '{'. + // At return, unless an error occurred, the scanner is positioned before the + // the final '}'. + PreParseResult PreParseLazyFunction( + LanguageMode language_mode, FunctionKind kind, bool has_simple_parameters, + ParserRecorder* log, Scanner::BookmarkScope* bookmark = nullptr); + + private: + friend class PreParserTraits; + + static const int kLazyParseTrialLimit = 200; + + // These types form an algebra over syntactic categories that is just + // rich enough to let us recognize and propagate the constructs that + // are either being counted in the preparser data, or is important + // to throw the correct syntax error exceptions. + + // All ParseXXX functions take as the last argument an *ok parameter + // which is set to false if parsing failed; it is unchanged otherwise. + // By making the 'exception handling' explicit, we are forced to check + // for failure at the call sites. + Statement ParseStatementListItem(bool* ok); + void ParseStatementList(int end_token, bool* ok, + Scanner::BookmarkScope* bookmark = nullptr); + Statement ParseStatement(bool* ok); + Statement ParseSubStatement(bool* ok); + Statement ParseFunctionDeclaration(bool* ok); + Statement ParseClassDeclaration(bool* ok); + Statement ParseBlock(bool* ok); + Statement ParseVariableStatement(VariableDeclarationContext var_context, + bool* ok); + Statement ParseVariableDeclarations(VariableDeclarationContext var_context, + int* num_decl, bool* is_lexical, + bool* is_binding_pattern, + Scanner::Location* first_initializer_loc, + Scanner::Location* bindings_loc, + bool* ok); + Statement ParseExpressionOrLabelledStatement(bool* ok); + Statement ParseIfStatement(bool* ok); + Statement ParseContinueStatement(bool* ok); + Statement ParseBreakStatement(bool* ok); + Statement ParseReturnStatement(bool* ok); + Statement ParseWithStatement(bool* ok); + Statement ParseSwitchStatement(bool* ok); + Statement ParseDoWhileStatement(bool* ok); + Statement ParseWhileStatement(bool* ok); + Statement ParseForStatement(bool* ok); + Statement ParseThrowStatement(bool* ok); + Statement ParseTryStatement(bool* ok); + Statement ParseDebuggerStatement(bool* ok); + Expression ParseConditionalExpression(bool accept_IN, bool* ok); + Expression ParseObjectLiteral(bool* ok); + Expression ParseV8Intrinsic(bool* ok); + Expression ParseDoExpression(bool* ok); + + V8_INLINE void SkipLazyFunctionBody(int* materialized_literal_count, + int* expected_property_count, bool* ok); + V8_INLINE PreParserStatementList ParseEagerFunctionBody( + PreParserIdentifier function_name, int pos, + const PreParserFormalParameters& parameters, FunctionKind kind, + FunctionLiteral::FunctionType function_type, bool* ok); + + Expression ParseFunctionLiteral( + Identifier name, Scanner::Location function_name_location, + FunctionNameValidity function_name_validity, FunctionKind kind, + int function_token_pos, FunctionLiteral::FunctionType function_type, + FunctionLiteral::ArityRestriction arity_restriction, + LanguageMode language_mode, bool* ok); + void ParseLazyFunctionLiteralBody(bool* ok, + Scanner::BookmarkScope* bookmark = nullptr); + + PreParserExpression ParseClassLiteral(PreParserIdentifier name, + Scanner::Location class_name_location, + bool name_is_strict_reserved, int pos, + bool* ok); +}; + + +void PreParserTraits::MaterializeTemplateCallsiteLiterals() { + pre_parser_->function_state_->NextMaterializedLiteralIndex(); + pre_parser_->function_state_->NextMaterializedLiteralIndex(); +} + + +void PreParserTraits::MaterializeUnspreadArgumentsLiterals(int count) { + for (int i = 0; i < count; ++i) { + pre_parser_->function_state_->NextMaterializedLiteralIndex(); + } +} + + +PreParserExpression PreParserTraits::SpreadCall(PreParserExpression function, + PreParserExpressionList args, + int pos) { + return pre_parser_->factory()->NewCall(function, args, pos); +} + +PreParserExpression PreParserTraits::SpreadCallNew(PreParserExpression function, + PreParserExpressionList args, + int pos) { + return pre_parser_->factory()->NewCallNew(function, args, pos); +} + + +void PreParserTraits::ParseArrowFunctionFormalParameterList( + PreParserFormalParameters* parameters, + PreParserExpression params, const Scanner::Location& params_loc, + Scanner::Location* duplicate_loc, bool* ok) { + // TODO(wingo): Detect duplicated identifiers in paramlists. Detect parameter + // lists that are too long. +} + + +PreParserExpression PreParserTraits::ParseDoExpression(bool* ok) { + return pre_parser_->ParseDoExpression(ok); +} + + +PreParserExpression PreParserTraits::RewriteNonPattern( + PreParserExpression expr, const ExpressionClassifier* classifier, + bool* ok) { + pre_parser_->ValidateExpression(classifier, ok); + return expr; +} + + +PreParserExpression PreParserTraits::RewriteNonPatternArguments( + PreParserExpression args, const ExpressionClassifier* classifier, + bool* ok) { + pre_parser_->ValidateExpression(classifier, ok); + return args; +} + + +PreParserExpression PreParserTraits::RewriteNonPatternObjectLiteralProperty( + PreParserExpression property, const ExpressionClassifier* classifier, + bool* ok) { + pre_parser_->ValidateExpression(classifier, ok); + return property; +} + + +PreParserStatementList PreParser::ParseEagerFunctionBody( + PreParserIdentifier function_name, int pos, + const PreParserFormalParameters& parameters, FunctionKind kind, + FunctionLiteral::FunctionType function_type, bool* ok) { + ParsingModeScope parsing_mode(this, PARSE_EAGERLY); + + ParseStatementList(Token::RBRACE, ok); + if (!*ok) return PreParserStatementList(); + + Expect(Token::RBRACE, ok); + return PreParserStatementList(); +} + + +PreParserStatementList PreParserTraits::ParseEagerFunctionBody( + PreParserIdentifier function_name, int pos, + const PreParserFormalParameters& parameters, FunctionKind kind, + FunctionLiteral::FunctionType function_type, bool* ok) { + return pre_parser_->ParseEagerFunctionBody(function_name, pos, parameters, + kind, function_type, ok); +} + +} // namespace internal +} // namespace v8 + +#endif // V8_PARSING_PREPARSER_H diff --git a/deps/v8/src/parsing/rewriter.cc b/deps/v8/src/parsing/rewriter.cc new file mode 100644 index 0000000000..4da60aca18 --- /dev/null +++ b/deps/v8/src/parsing/rewriter.cc @@ -0,0 +1,403 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/parsing/rewriter.h" + +#include "src/ast/ast.h" +#include "src/ast/scopes.h" +#include "src/parsing/parser.h" + +namespace v8 { +namespace internal { + +class Processor: public AstVisitor { + public: + Processor(Isolate* isolate, Scope* scope, Variable* result, + AstValueFactory* ast_value_factory) + : result_(result), + result_assigned_(false), + replacement_(nullptr), + is_set_(false), + zone_(ast_value_factory->zone()), + scope_(scope), + factory_(ast_value_factory) { + InitializeAstVisitor(isolate); + } + + Processor(Parser* parser, Scope* scope, Variable* result, + AstValueFactory* ast_value_factory) + : result_(result), + result_assigned_(false), + replacement_(nullptr), + is_set_(false), + scope_(scope), + factory_(ast_value_factory) { + InitializeAstVisitor(parser->stack_limit()); + } + + ~Processor() override {} + + void Process(ZoneList<Statement*>* statements); + bool result_assigned() const { return result_assigned_; } + + Zone* zone() { return zone_; } + Scope* scope() { return scope_; } + AstNodeFactory* factory() { return &factory_; } + + // Returns ".result = value" + Expression* SetResult(Expression* value) { + result_assigned_ = true; + VariableProxy* result_proxy = factory()->NewVariableProxy(result_); + return factory()->NewAssignment(Token::ASSIGN, result_proxy, value, + RelocInfo::kNoPosition); + } + + // Inserts '.result = undefined' in front of the given statement. + Statement* AssignUndefinedBefore(Statement* s); + + private: + Variable* result_; + + // We are not tracking result usage via the result_'s use + // counts (we leave the accurate computation to the + // usage analyzer). Instead we simple remember if + // there was ever an assignment to result_. + bool result_assigned_; + + // When visiting a node, we "return" a replacement for that node in + // [replacement_]. In many cases this will just be the original node. + Statement* replacement_; + + // To avoid storing to .result all the time, we eliminate some of + // the stores by keeping track of whether or not we're sure .result + // will be overwritten anyway. This is a bit more tricky than what I + // was hoping for. + bool is_set_; + + Zone* zone_; + Scope* scope_; + AstNodeFactory factory_; + + // Node visitors. +#define DEF_VISIT(type) void Visit##type(type* node) override; + AST_NODE_LIST(DEF_VISIT) +#undef DEF_VISIT + + void VisitIterationStatement(IterationStatement* stmt); + + DEFINE_AST_VISITOR_SUBCLASS_MEMBERS(); +}; + + +Statement* Processor::AssignUndefinedBefore(Statement* s) { + Expression* result_proxy = factory()->NewVariableProxy(result_); + Expression* undef = factory()->NewUndefinedLiteral(RelocInfo::kNoPosition); + Expression* assignment = factory()->NewAssignment( + Token::ASSIGN, result_proxy, undef, RelocInfo::kNoPosition); + Block* b = factory()->NewBlock(NULL, 2, false, RelocInfo::kNoPosition); + b->statements()->Add( + factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition), + zone()); + b->statements()->Add(s, zone()); + return b; +} + + +void Processor::Process(ZoneList<Statement*>* statements) { + for (int i = statements->length() - 1; i >= 0; --i) { + Visit(statements->at(i)); + statements->Set(i, replacement_); + } +} + + +void Processor::VisitBlock(Block* node) { + // An initializer block is the rewritten form of a variable declaration + // with initialization expressions. The initializer block contains the + // list of assignments corresponding to the initialization expressions. + // While unclear from the spec (ECMA-262, 3rd., 12.2), the value of + // a variable declaration with initialization expression is 'undefined' + // with some JS VMs: For instance, using smjs, print(eval('var x = 7')) + // returns 'undefined'. To obtain the same behavior with v8, we need + // to prevent rewriting in that case. + if (!node->ignore_completion_value()) Process(node->statements()); + replacement_ = node; +} + + +void Processor::VisitExpressionStatement(ExpressionStatement* node) { + // Rewrite : <x>; -> .result = <x>; + if (!is_set_) { + node->set_expression(SetResult(node->expression())); + is_set_ = true; + } + replacement_ = node; +} + + +void Processor::VisitIfStatement(IfStatement* node) { + // Rewrite both branches. + bool set_after = is_set_; + Visit(node->then_statement()); + node->set_then_statement(replacement_); + bool set_in_then = is_set_; + is_set_ = set_after; + Visit(node->else_statement()); + node->set_else_statement(replacement_); + is_set_ = is_set_ && set_in_then; + replacement_ = node; + + if (FLAG_harmony_completion && !is_set_) { + is_set_ = true; + replacement_ = AssignUndefinedBefore(node); + } +} + + +void Processor::VisitIterationStatement(IterationStatement* node) { + // Rewrite the body. + bool set_after = is_set_; + is_set_ = false; // We are in a loop, so we can't rely on [set_after]. + Visit(node->body()); + node->set_body(replacement_); + is_set_ = is_set_ && set_after; + replacement_ = node; + + if (FLAG_harmony_completion && !is_set_) { + is_set_ = true; + replacement_ = AssignUndefinedBefore(node); + } +} + + +void Processor::VisitDoWhileStatement(DoWhileStatement* node) { + VisitIterationStatement(node); +} + + +void Processor::VisitWhileStatement(WhileStatement* node) { + VisitIterationStatement(node); +} + + +void Processor::VisitForStatement(ForStatement* node) { + VisitIterationStatement(node); +} + + +void Processor::VisitForInStatement(ForInStatement* node) { + VisitIterationStatement(node); +} + + +void Processor::VisitForOfStatement(ForOfStatement* node) { + VisitIterationStatement(node); +} + + +void Processor::VisitTryCatchStatement(TryCatchStatement* node) { + // Rewrite both try and catch block. + bool set_after = is_set_; + Visit(node->try_block()); + node->set_try_block(static_cast<Block*>(replacement_)); + bool set_in_try = is_set_; + is_set_ = set_after; + Visit(node->catch_block()); + node->set_catch_block(static_cast<Block*>(replacement_)); + is_set_ = is_set_ && set_in_try; + replacement_ = node; + + if (FLAG_harmony_completion && !is_set_) { + is_set_ = true; + replacement_ = AssignUndefinedBefore(node); + } +} + + +void Processor::VisitTryFinallyStatement(TryFinallyStatement* node) { + // Rewrite both try and finally block (in reverse order). + bool set_after = is_set_; + is_set_ = true; // Don't normally need to assign in finally block. + Visit(node->finally_block()); + node->set_finally_block(replacement_->AsBlock()); + { // Save .result value at the beginning of the finally block and restore it + // at the end again: ".backup = .result; ...; .result = .backup" + // This is necessary because the finally block does not normally contribute + // to the completion value. + Variable* backup = scope()->NewTemporary( + factory()->ast_value_factory()->dot_result_string()); + Expression* backup_proxy = factory()->NewVariableProxy(backup); + Expression* result_proxy = factory()->NewVariableProxy(result_); + Expression* save = factory()->NewAssignment( + Token::ASSIGN, backup_proxy, result_proxy, RelocInfo::kNoPosition); + Expression* restore = factory()->NewAssignment( + Token::ASSIGN, result_proxy, backup_proxy, RelocInfo::kNoPosition); + node->finally_block()->statements()->InsertAt( + 0, factory()->NewExpressionStatement(save, RelocInfo::kNoPosition), + zone()); + node->finally_block()->statements()->Add( + factory()->NewExpressionStatement(restore, RelocInfo::kNoPosition), + zone()); + } + is_set_ = set_after; + Visit(node->try_block()); + node->set_try_block(replacement_->AsBlock()); + replacement_ = node; + + if (FLAG_harmony_completion && !is_set_) { + is_set_ = true; + replacement_ = AssignUndefinedBefore(node); + } +} + + +void Processor::VisitSwitchStatement(SwitchStatement* node) { + // Rewrite statements in all case clauses (in reverse order). + ZoneList<CaseClause*>* clauses = node->cases(); + bool set_after = is_set_; + for (int i = clauses->length() - 1; i >= 0; --i) { + CaseClause* clause = clauses->at(i); + Process(clause->statements()); + } + is_set_ = is_set_ && set_after; + replacement_ = node; + + if (FLAG_harmony_completion && !is_set_) { + is_set_ = true; + replacement_ = AssignUndefinedBefore(node); + } +} + + +void Processor::VisitContinueStatement(ContinueStatement* node) { + is_set_ = false; + replacement_ = node; +} + + +void Processor::VisitBreakStatement(BreakStatement* node) { + is_set_ = false; + replacement_ = node; +} + + +void Processor::VisitWithStatement(WithStatement* node) { + Visit(node->statement()); + node->set_statement(replacement_); + replacement_ = node; + + if (FLAG_harmony_completion && !is_set_) { + is_set_ = true; + replacement_ = AssignUndefinedBefore(node); + } +} + + +void Processor::VisitSloppyBlockFunctionStatement( + SloppyBlockFunctionStatement* node) { + Visit(node->statement()); + node->set_statement(replacement_); + replacement_ = node; +} + + +void Processor::VisitEmptyStatement(EmptyStatement* node) { + replacement_ = node; +} + + +void Processor::VisitReturnStatement(ReturnStatement* node) { + is_set_ = true; + replacement_ = node; +} + + +void Processor::VisitDebuggerStatement(DebuggerStatement* node) { + replacement_ = node; +} + + +// Expressions are never visited. +#define DEF_VISIT(type) \ + void Processor::Visit##type(type* expr) { UNREACHABLE(); } +EXPRESSION_NODE_LIST(DEF_VISIT) +#undef DEF_VISIT + + +// Declarations are never visited. +#define DEF_VISIT(type) \ + void Processor::Visit##type(type* expr) { UNREACHABLE(); } +DECLARATION_NODE_LIST(DEF_VISIT) +#undef DEF_VISIT + + +// Assumes code has been parsed. Mutates the AST, so the AST should not +// continue to be used in the case of failure. +bool Rewriter::Rewrite(ParseInfo* info) { + FunctionLiteral* function = info->literal(); + DCHECK(function != NULL); + Scope* scope = function->scope(); + DCHECK(scope != NULL); + if (!scope->is_script_scope() && !scope->is_eval_scope()) return true; + + ZoneList<Statement*>* body = function->body(); + if (!body->is_empty()) { + Variable* result = + scope->NewTemporary(info->ast_value_factory()->dot_result_string()); + // The name string must be internalized at this point. + DCHECK(!result->name().is_null()); + Processor processor(info->isolate(), scope, result, + info->ast_value_factory()); + processor.Process(body); + if (processor.HasStackOverflow()) return false; + + if (processor.result_assigned()) { + DCHECK(function->end_position() != RelocInfo::kNoPosition); + // Set the position of the assignment statement one character past the + // source code, such that it definitely is not in the source code range + // of an immediate inner scope. For example in + // eval('with ({x:1}) x = 1'); + // the end position of the function generated for executing the eval code + // coincides with the end of the with scope which is the position of '1'. + int pos = function->end_position(); + VariableProxy* result_proxy = + processor.factory()->NewVariableProxy(result, pos); + Statement* result_statement = + processor.factory()->NewReturnStatement(result_proxy, pos); + body->Add(result_statement, info->zone()); + } + } + + return true; +} + + +bool Rewriter::Rewrite(Parser* parser, DoExpression* expr, + AstValueFactory* factory) { + Block* block = expr->block(); + Scope* scope = block->scope(); + ZoneList<Statement*>* body = block->statements(); + VariableProxy* result = expr->result(); + Variable* result_var = result->var(); + + if (!body->is_empty()) { + Processor processor(parser, scope, result_var, factory); + processor.Process(body); + if (processor.HasStackOverflow()) return false; + + if (!processor.result_assigned()) { + AstNodeFactory* node_factory = processor.factory(); + Expression* undef = + node_factory->NewUndefinedLiteral(RelocInfo::kNoPosition); + Statement* completion = node_factory->NewExpressionStatement( + processor.SetResult(undef), expr->position()); + body->Add(completion, factory->zone()); + } + } + return true; +} + + +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/parsing/rewriter.h b/deps/v8/src/parsing/rewriter.h new file mode 100644 index 0000000000..477644a756 --- /dev/null +++ b/deps/v8/src/parsing/rewriter.h @@ -0,0 +1,36 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_PARSING_REWRITER_H_ +#define V8_PARSING_REWRITER_H_ + +namespace v8 { +namespace internal { + +class AstValueFactory; +class DoExpression; +class ParseInfo; +class Parser; + +class Rewriter { + public: + // Rewrite top-level code (ECMA 262 "programs") so as to conservatively + // include an assignment of the value of the last statement in the code to + // a compiler-generated temporary variable wherever needed. + // + // Assumes code has been parsed and scopes have been analyzed. Mutates the + // AST, so the AST should not continue to be used in the case of failure. + static bool Rewrite(ParseInfo* info); + + // Rewrite a list of statements, using the same rules as a top-level program, + // to ensure identical behaviour of completion result. + static bool Rewrite(Parser* parser, DoExpression* expr, + AstValueFactory* factory); +}; + + +} // namespace internal +} // namespace v8 + +#endif // V8_PARSING_REWRITER_H_ diff --git a/deps/v8/src/parsing/scanner-character-streams.cc b/deps/v8/src/parsing/scanner-character-streams.cc new file mode 100644 index 0000000000..91ed54f7be --- /dev/null +++ b/deps/v8/src/parsing/scanner-character-streams.cc @@ -0,0 +1,589 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/parsing/scanner-character-streams.h" + +#include "include/v8.h" +#include "src/globals.h" +#include "src/handles.h" +#include "src/list-inl.h" // TODO(mstarzinger): Temporary cycle breaker! +#include "src/objects.h" +#include "src/unicode-inl.h" + +namespace v8 { +namespace internal { + +namespace { + +size_t CopyCharsHelper(uint16_t* dest, size_t length, const uint8_t* src, + size_t* src_pos, size_t src_length, + ScriptCompiler::StreamedSource::Encoding encoding) { + // It's possible that this will be called with length 0, but don't assume that + // the functions this calls handle it gracefully. + if (length == 0) return 0; + + if (encoding == ScriptCompiler::StreamedSource::UTF8) { + return v8::internal::Utf8ToUtf16CharacterStream::CopyChars( + dest, length, src, src_pos, src_length); + } + + size_t to_fill = length; + if (to_fill > src_length - *src_pos) to_fill = src_length - *src_pos; + + if (encoding == ScriptCompiler::StreamedSource::ONE_BYTE) { + v8::internal::CopyChars<uint8_t, uint16_t>(dest, src + *src_pos, to_fill); + } else { + DCHECK(encoding == ScriptCompiler::StreamedSource::TWO_BYTE); + v8::internal::CopyChars<uint16_t, uint16_t>( + dest, reinterpret_cast<const uint16_t*>(src + *src_pos), to_fill); + } + *src_pos += to_fill; + return to_fill; +} + +} // namespace + + +// ---------------------------------------------------------------------------- +// BufferedUtf16CharacterStreams + +BufferedUtf16CharacterStream::BufferedUtf16CharacterStream() + : Utf16CharacterStream(), + pushback_limit_(NULL) { + // Initialize buffer as being empty. First read will fill the buffer. + buffer_cursor_ = buffer_; + buffer_end_ = buffer_; +} + + +BufferedUtf16CharacterStream::~BufferedUtf16CharacterStream() { } + +void BufferedUtf16CharacterStream::PushBack(uc32 character) { + if (character == kEndOfInput) { + pos_--; + return; + } + if (pushback_limit_ == NULL && buffer_cursor_ > buffer_) { + // buffer_ is writable, buffer_cursor_ is const pointer. + buffer_[--buffer_cursor_ - buffer_] = static_cast<uc16>(character); + pos_--; + return; + } + SlowPushBack(static_cast<uc16>(character)); +} + + +void BufferedUtf16CharacterStream::SlowPushBack(uc16 character) { + // In pushback mode, the end of the buffer contains pushback, + // and the start of the buffer (from buffer start to pushback_limit_) + // contains valid data that comes just after the pushback. + // We NULL the pushback_limit_ if pushing all the way back to the + // start of the buffer. + + if (pushback_limit_ == NULL) { + // Enter pushback mode. + pushback_limit_ = buffer_end_; + buffer_end_ = buffer_ + kBufferSize; + buffer_cursor_ = buffer_end_; + } + // Ensure that there is room for at least one pushback. + DCHECK(buffer_cursor_ > buffer_); + DCHECK(pos_ > 0); + buffer_[--buffer_cursor_ - buffer_] = character; + if (buffer_cursor_ == buffer_) { + pushback_limit_ = NULL; + } else if (buffer_cursor_ < pushback_limit_) { + pushback_limit_ = buffer_cursor_; + } + pos_--; +} + + +bool BufferedUtf16CharacterStream::ReadBlock() { + buffer_cursor_ = buffer_; + if (pushback_limit_ != NULL) { + // Leave pushback mode. + buffer_end_ = pushback_limit_; + pushback_limit_ = NULL; + // If there were any valid characters left at the + // start of the buffer, use those. + if (buffer_cursor_ < buffer_end_) return true; + // Otherwise read a new block. + } + size_t length = FillBuffer(pos_); + buffer_end_ = buffer_ + length; + return length > 0; +} + + +size_t BufferedUtf16CharacterStream::SlowSeekForward(size_t delta) { + // Leave pushback mode (i.e., ignore that there might be valid data + // in the buffer before the pushback_limit_ point). + pushback_limit_ = NULL; + return BufferSeekForward(delta); +} + + +// ---------------------------------------------------------------------------- +// GenericStringUtf16CharacterStream + + +GenericStringUtf16CharacterStream::GenericStringUtf16CharacterStream( + Handle<String> data, size_t start_position, size_t end_position) + : string_(data), length_(end_position), bookmark_(kNoBookmark) { + DCHECK(end_position >= start_position); + pos_ = start_position; +} + + +GenericStringUtf16CharacterStream::~GenericStringUtf16CharacterStream() { } + + +bool GenericStringUtf16CharacterStream::SetBookmark() { + bookmark_ = pos_; + return true; +} + + +void GenericStringUtf16CharacterStream::ResetToBookmark() { + DCHECK(bookmark_ != kNoBookmark); + pos_ = bookmark_; + buffer_cursor_ = buffer_; + buffer_end_ = buffer_ + FillBuffer(pos_); +} + + +size_t GenericStringUtf16CharacterStream::BufferSeekForward(size_t delta) { + size_t old_pos = pos_; + pos_ = Min(pos_ + delta, length_); + ReadBlock(); + return pos_ - old_pos; +} + + +size_t GenericStringUtf16CharacterStream::FillBuffer(size_t from_pos) { + if (from_pos >= length_) return 0; + size_t length = kBufferSize; + if (from_pos + length > length_) { + length = length_ - from_pos; + } + String::WriteToFlat<uc16>(*string_, buffer_, static_cast<int>(from_pos), + static_cast<int>(from_pos + length)); + return length; +} + + +// ---------------------------------------------------------------------------- +// Utf8ToUtf16CharacterStream +Utf8ToUtf16CharacterStream::Utf8ToUtf16CharacterStream(const byte* data, + size_t length) + : BufferedUtf16CharacterStream(), + raw_data_(data), + raw_data_length_(length), + raw_data_pos_(0), + raw_character_position_(0) { + ReadBlock(); +} + + +Utf8ToUtf16CharacterStream::~Utf8ToUtf16CharacterStream() { } + + +size_t Utf8ToUtf16CharacterStream::CopyChars(uint16_t* dest, size_t length, + const byte* src, size_t* src_pos, + size_t src_length) { + static const unibrow::uchar kMaxUtf16Character = + unibrow::Utf16::kMaxNonSurrogateCharCode; + size_t i = 0; + // Because of the UTF-16 lead and trail surrogates, we stop filling the buffer + // one character early (in the normal case), because we need to have at least + // two free spaces in the buffer to be sure that the next character will fit. + while (i < length - 1) { + if (*src_pos == src_length) break; + unibrow::uchar c = src[*src_pos]; + if (c <= unibrow::Utf8::kMaxOneByteChar) { + *src_pos = *src_pos + 1; + } else { + c = unibrow::Utf8::CalculateValue(src + *src_pos, src_length - *src_pos, + src_pos); + } + if (c > kMaxUtf16Character) { + dest[i++] = unibrow::Utf16::LeadSurrogate(c); + dest[i++] = unibrow::Utf16::TrailSurrogate(c); + } else { + dest[i++] = static_cast<uc16>(c); + } + } + return i; +} + + +size_t Utf8ToUtf16CharacterStream::BufferSeekForward(size_t delta) { + size_t old_pos = pos_; + size_t target_pos = pos_ + delta; + SetRawPosition(target_pos); + pos_ = raw_character_position_; + ReadBlock(); + return pos_ - old_pos; +} + + +size_t Utf8ToUtf16CharacterStream::FillBuffer(size_t char_position) { + SetRawPosition(char_position); + if (raw_character_position_ != char_position) { + // char_position was not a valid position in the stream (hit the end + // while spooling to it). + return 0u; + } + size_t i = CopyChars(buffer_, kBufferSize, raw_data_, &raw_data_pos_, + raw_data_length_); + raw_character_position_ = char_position + i; + return i; +} + + +static const byte kUtf8MultiByteMask = 0xC0; +static const byte kUtf8MultiByteCharFollower = 0x80; + + +#ifdef DEBUG +static const byte kUtf8MultiByteCharStart = 0xC0; +static bool IsUtf8MultiCharacterStart(byte first_byte) { + return (first_byte & kUtf8MultiByteMask) == kUtf8MultiByteCharStart; +} +#endif + + +static bool IsUtf8MultiCharacterFollower(byte later_byte) { + return (later_byte & kUtf8MultiByteMask) == kUtf8MultiByteCharFollower; +} + + +// Move the cursor back to point at the preceding UTF-8 character start +// in the buffer. +static inline void Utf8CharacterBack(const byte* buffer, size_t* cursor) { + byte character = buffer[--*cursor]; + if (character > unibrow::Utf8::kMaxOneByteChar) { + DCHECK(IsUtf8MultiCharacterFollower(character)); + // Last byte of a multi-byte character encoding. Step backwards until + // pointing to the first byte of the encoding, recognized by having the + // top two bits set. + while (IsUtf8MultiCharacterFollower(buffer[--*cursor])) { } + DCHECK(IsUtf8MultiCharacterStart(buffer[*cursor])); + } +} + + +// Move the cursor forward to point at the next following UTF-8 character start +// in the buffer. +static inline void Utf8CharacterForward(const byte* buffer, size_t* cursor) { + byte character = buffer[(*cursor)++]; + if (character > unibrow::Utf8::kMaxOneByteChar) { + // First character of a multi-byte character encoding. + // The number of most-significant one-bits determines the length of the + // encoding: + // 110..... - (0xCx, 0xDx) one additional byte (minimum). + // 1110.... - (0xEx) two additional bytes. + // 11110... - (0xFx) three additional bytes (maximum). + DCHECK(IsUtf8MultiCharacterStart(character)); + // Additional bytes is: + // 1 if value in range 0xC0 .. 0xDF. + // 2 if value in range 0xE0 .. 0xEF. + // 3 if value in range 0xF0 .. 0xF7. + // Encode that in a single value. + size_t additional_bytes = + ((0x3211u) >> (((character - 0xC0) >> 2) & 0xC)) & 0x03; + *cursor += additional_bytes; + DCHECK(!IsUtf8MultiCharacterFollower(buffer[1 + additional_bytes])); + } +} + + +// This can't set a raw position between two surrogate pairs, since there +// is no position in the UTF8 stream that corresponds to that. This assumes +// that the surrogate pair is correctly coded as a 4 byte UTF-8 sequence. If +// it is illegally coded as two 3 byte sequences then there is no problem here. +void Utf8ToUtf16CharacterStream::SetRawPosition(size_t target_position) { + if (raw_character_position_ > target_position) { + // Spool backwards in utf8 buffer. + do { + size_t old_pos = raw_data_pos_; + Utf8CharacterBack(raw_data_, &raw_data_pos_); + raw_character_position_--; + DCHECK(old_pos - raw_data_pos_ <= 4); + // Step back over both code units for surrogate pairs. + if (old_pos - raw_data_pos_ == 4) raw_character_position_--; + } while (raw_character_position_ > target_position); + // No surrogate pair splitting. + DCHECK(raw_character_position_ == target_position); + return; + } + // Spool forwards in the utf8 buffer. + while (raw_character_position_ < target_position) { + if (raw_data_pos_ == raw_data_length_) return; + size_t old_pos = raw_data_pos_; + Utf8CharacterForward(raw_data_, &raw_data_pos_); + raw_character_position_++; + DCHECK(raw_data_pos_ - old_pos <= 4); + if (raw_data_pos_ - old_pos == 4) raw_character_position_++; + } + // No surrogate pair splitting. + DCHECK(raw_character_position_ == target_position); +} + + +size_t ExternalStreamingStream::FillBuffer(size_t position) { + // Ignore "position" which is the position in the decoded data. Instead, + // ExternalStreamingStream keeps track of the position in the raw data. + size_t data_in_buffer = 0; + // Note that the UTF-8 decoder might not be able to fill the buffer + // completely; it will typically leave the last character empty (see + // Utf8ToUtf16CharacterStream::CopyChars). + while (data_in_buffer < kBufferSize - 1) { + if (current_data_ == NULL) { + // GetSomeData will wait until the embedder has enough data. Here's an + // interface between the API which uses size_t (which is the correct type + // here) and the internal parts which use size_t. + current_data_length_ = source_stream_->GetMoreData(¤t_data_); + current_data_offset_ = 0; + bool data_ends = current_data_length_ == 0; + bookmark_data_is_from_current_data_ = false; + + // A caveat: a data chunk might end with bytes from an incomplete UTF-8 + // character (the rest of the bytes will be in the next chunk). + if (encoding_ == ScriptCompiler::StreamedSource::UTF8) { + HandleUtf8SplitCharacters(&data_in_buffer); + if (!data_ends && current_data_offset_ == current_data_length_) { + // The data stream didn't end, but we used all the data in the + // chunk. This will only happen when the chunk was really small. We + // don't handle the case where a UTF-8 character is split over several + // chunks; in that case V8 won't crash, but it will be a parse error. + FlushCurrent(); + continue; // Request a new chunk. + } + } + + // Did the data stream end? + if (data_ends) { + DCHECK(utf8_split_char_buffer_length_ == 0); + return data_in_buffer; + } + } + + // Fill the buffer from current_data_. + size_t new_offset = 0; + size_t new_chars_in_buffer = + CopyCharsHelper(buffer_ + data_in_buffer, kBufferSize - data_in_buffer, + current_data_ + current_data_offset_, &new_offset, + current_data_length_ - current_data_offset_, encoding_); + data_in_buffer += new_chars_in_buffer; + current_data_offset_ += new_offset; + DCHECK(data_in_buffer <= kBufferSize); + + // Did we use all the data in the data chunk? + if (current_data_offset_ == current_data_length_) { + FlushCurrent(); + } + } + return data_in_buffer; +} + + +bool ExternalStreamingStream::SetBookmark() { + // Bookmarking for this stream is a bit more complex than expected, since + // the stream state is distributed over several places: + // - pos_ (inherited from Utf16CharacterStream) + // - buffer_cursor_ and buffer_end_ (also from Utf16CharacterStream) + // - buffer_ (from BufferedUtf16CharacterStream) + // - current_data_ (+ .._offset_ and .._length) (this class) + // - utf8_split_char_buffer_* (a partial utf8 symbol at the block boundary) + // + // The underlying source_stream_ instance likely could re-construct this + // local data for us, but with the given interfaces we have no way of + // accomplishing this. Thus, we'll have to save all data locally. + // + // What gets saved where: + // - pos_ => bookmark_ + // - buffer_[buffer_cursor_ .. buffer_end_] => bookmark_buffer_ + // - current_data_[.._offset_ .. .._length_] => bookmark_data_ + // - utf8_split_char_buffer_* => bookmark_utf8_split... + // + // To make sure we don't unnecessarily copy data, we also maintain + // whether bookmark_data_ contains a copy of the current current_data_ + // block. This is done with: + // - bookmark_data_is_from_current_data_ + // - bookmark_data_offset_: offset into bookmark_data_ + // + // Note that bookmark_data_is_from_current_data_ must be maintained + // whenever current_data_ is updated. + + bookmark_ = pos_; + + size_t buffer_length = buffer_end_ - buffer_cursor_; + bookmark_buffer_.Dispose(); + bookmark_buffer_ = Vector<uint16_t>::New(static_cast<int>(buffer_length)); + CopyCharsUnsigned(bookmark_buffer_.start(), buffer_cursor_, buffer_length); + + size_t data_length = current_data_length_ - current_data_offset_; + size_t bookmark_data_length = static_cast<size_t>(bookmark_data_.length()); + if (bookmark_data_is_from_current_data_ && + data_length < bookmark_data_length) { + // Fast case: bookmark_data_ was previously copied from the current + // data block, and we have enough data for this bookmark. + bookmark_data_offset_ = bookmark_data_length - data_length; + } else { + // Slow case: We need to copy current_data_. + bookmark_data_.Dispose(); + bookmark_data_ = Vector<uint8_t>::New(static_cast<int>(data_length)); + CopyBytes(bookmark_data_.start(), current_data_ + current_data_offset_, + data_length); + bookmark_data_is_from_current_data_ = true; + bookmark_data_offset_ = 0; + } + + bookmark_utf8_split_char_buffer_length_ = utf8_split_char_buffer_length_; + for (size_t i = 0; i < utf8_split_char_buffer_length_; i++) { + bookmark_utf8_split_char_buffer_[i] = utf8_split_char_buffer_[i]; + } + + return source_stream_->SetBookmark(); +} + + +void ExternalStreamingStream::ResetToBookmark() { + source_stream_->ResetToBookmark(); + FlushCurrent(); + + pos_ = bookmark_; + + // bookmark_data_* => current_data_* + // (current_data_ assumes ownership of its memory.) + current_data_offset_ = 0; + current_data_length_ = bookmark_data_.length() - bookmark_data_offset_; + uint8_t* data = new uint8_t[current_data_length_]; + CopyCharsUnsigned(data, bookmark_data_.begin() + bookmark_data_offset_, + current_data_length_); + delete[] current_data_; + current_data_ = data; + bookmark_data_is_from_current_data_ = true; + + // bookmark_buffer_ needs to be copied to buffer_. + CopyCharsUnsigned(buffer_, bookmark_buffer_.begin(), + bookmark_buffer_.length()); + buffer_cursor_ = buffer_; + buffer_end_ = buffer_ + bookmark_buffer_.length(); + + // utf8 split char buffer + utf8_split_char_buffer_length_ = bookmark_utf8_split_char_buffer_length_; + for (size_t i = 0; i < bookmark_utf8_split_char_buffer_length_; i++) { + utf8_split_char_buffer_[i] = bookmark_utf8_split_char_buffer_[i]; + } +} + + +void ExternalStreamingStream::FlushCurrent() { + delete[] current_data_; + current_data_ = NULL; + current_data_length_ = 0; + current_data_offset_ = 0; + bookmark_data_is_from_current_data_ = false; +} + + +void ExternalStreamingStream::HandleUtf8SplitCharacters( + size_t* data_in_buffer) { + // Note the following property of UTF-8 which makes this function possible: + // Given any byte, we can always read its local environment (in both + // directions) to find out the (possibly multi-byte) character it belongs + // to. Single byte characters are of the form 0b0XXXXXXX. The first byte of a + // multi-byte character is of the form 0b110XXXXX, 0b1110XXXX or + // 0b11110XXX. The continuation bytes are of the form 0b10XXXXXX. + + // First check if we have leftover data from the last chunk. + unibrow::uchar c; + if (utf8_split_char_buffer_length_ > 0) { + // Move the bytes which are part of the split character (which started in + // the previous chunk) into utf8_split_char_buffer_. Note that the + // continuation bytes are of the form 0b10XXXXXX, thus c >> 6 == 2. + while (current_data_offset_ < current_data_length_ && + utf8_split_char_buffer_length_ < 4 && + (c = current_data_[current_data_offset_]) >> 6 == 2) { + utf8_split_char_buffer_[utf8_split_char_buffer_length_] = c; + ++utf8_split_char_buffer_length_; + ++current_data_offset_; + } + + // Convert the data in utf8_split_char_buffer_. + size_t new_offset = 0; + size_t new_chars_in_buffer = + CopyCharsHelper(buffer_ + *data_in_buffer, + kBufferSize - *data_in_buffer, utf8_split_char_buffer_, + &new_offset, utf8_split_char_buffer_length_, encoding_); + *data_in_buffer += new_chars_in_buffer; + // Make sure we used all the data. + DCHECK(new_offset == utf8_split_char_buffer_length_); + DCHECK(*data_in_buffer <= kBufferSize); + + utf8_split_char_buffer_length_ = 0; + } + + // Move bytes which are part of an incomplete character from the end of the + // current chunk to utf8_split_char_buffer_. They will be converted when the + // next data chunk arrives. Note that all valid UTF-8 characters are at most 4 + // bytes long, but if the data is invalid, we can have character values bigger + // than unibrow::Utf8::kMaxOneByteChar for more than 4 consecutive bytes. + while (current_data_length_ > current_data_offset_ && + (c = current_data_[current_data_length_ - 1]) > + unibrow::Utf8::kMaxOneByteChar && + utf8_split_char_buffer_length_ < 4) { + --current_data_length_; + ++utf8_split_char_buffer_length_; + if (c >= (3 << 6)) { + // 3 << 6 = 0b11000000; this is the first byte of the multi-byte + // character. No need to copy the previous characters into the conversion + // buffer (even if they're multi-byte). + break; + } + } + CHECK(utf8_split_char_buffer_length_ <= 4); + for (size_t i = 0; i < utf8_split_char_buffer_length_; ++i) { + utf8_split_char_buffer_[i] = current_data_[current_data_length_ + i]; + } +} + + +// ---------------------------------------------------------------------------- +// ExternalTwoByteStringUtf16CharacterStream + +ExternalTwoByteStringUtf16CharacterStream:: + ~ExternalTwoByteStringUtf16CharacterStream() { } + + +ExternalTwoByteStringUtf16CharacterStream:: + ExternalTwoByteStringUtf16CharacterStream( + Handle<ExternalTwoByteString> data, int start_position, + int end_position) + : Utf16CharacterStream(), + source_(data), + raw_data_(data->GetTwoByteData(start_position)), + bookmark_(kNoBookmark) { + buffer_cursor_ = raw_data_, + buffer_end_ = raw_data_ + (end_position - start_position); + pos_ = start_position; +} + + +bool ExternalTwoByteStringUtf16CharacterStream::SetBookmark() { + bookmark_ = pos_; + return true; +} + + +void ExternalTwoByteStringUtf16CharacterStream::ResetToBookmark() { + DCHECK(bookmark_ != kNoBookmark); + pos_ = bookmark_; + buffer_cursor_ = raw_data_ + bookmark_; +} +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/parsing/scanner-character-streams.h b/deps/v8/src/parsing/scanner-character-streams.h new file mode 100644 index 0000000000..603db93d02 --- /dev/null +++ b/deps/v8/src/parsing/scanner-character-streams.h @@ -0,0 +1,189 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_PARSING_SCANNER_CHARACTER_STREAMS_H_ +#define V8_PARSING_SCANNER_CHARACTER_STREAMS_H_ + +#include "src/handles.h" +#include "src/parsing/scanner.h" +#include "src/vector.h" + +namespace v8 { +namespace internal { + +// Forward declarations. +class ExternalTwoByteString; + +// A buffered character stream based on a random access character +// source (ReadBlock can be called with pos_ pointing to any position, +// even positions before the current). +class BufferedUtf16CharacterStream: public Utf16CharacterStream { + public: + BufferedUtf16CharacterStream(); + ~BufferedUtf16CharacterStream() override; + + void PushBack(uc32 character) override; + + protected: + static const size_t kBufferSize = 512; + static const size_t kPushBackStepSize = 16; + + size_t SlowSeekForward(size_t delta) override; + bool ReadBlock() override; + virtual void SlowPushBack(uc16 character); + + virtual size_t BufferSeekForward(size_t delta) = 0; + virtual size_t FillBuffer(size_t position) = 0; + + const uc16* pushback_limit_; + uc16 buffer_[kBufferSize]; +}; + + +// Generic string stream. +class GenericStringUtf16CharacterStream: public BufferedUtf16CharacterStream { + public: + GenericStringUtf16CharacterStream(Handle<String> data, size_t start_position, + size_t end_position); + ~GenericStringUtf16CharacterStream() override; + + bool SetBookmark() override; + void ResetToBookmark() override; + + protected: + static const size_t kNoBookmark = -1; + + size_t BufferSeekForward(size_t delta) override; + size_t FillBuffer(size_t position) override; + + Handle<String> string_; + size_t length_; + size_t bookmark_; +}; + + +// Utf16 stream based on a literal UTF-8 string. +class Utf8ToUtf16CharacterStream: public BufferedUtf16CharacterStream { + public: + Utf8ToUtf16CharacterStream(const byte* data, size_t length); + ~Utf8ToUtf16CharacterStream() override; + + static size_t CopyChars(uint16_t* dest, size_t length, const byte* src, + size_t* src_pos, size_t src_length); + + protected: + size_t BufferSeekForward(size_t delta) override; + size_t FillBuffer(size_t char_position) override; + void SetRawPosition(size_t char_position); + + const byte* raw_data_; + size_t raw_data_length_; // Measured in bytes, not characters. + size_t raw_data_pos_; + // The character position of the character at raw_data[raw_data_pos_]. + // Not necessarily the same as pos_. + size_t raw_character_position_; +}; + + +// ExternalStreamingStream is a wrapper around an ExternalSourceStream (see +// include/v8.h) subclass implemented by the embedder. +class ExternalStreamingStream : public BufferedUtf16CharacterStream { + public: + ExternalStreamingStream(ScriptCompiler::ExternalSourceStream* source_stream, + v8::ScriptCompiler::StreamedSource::Encoding encoding) + : source_stream_(source_stream), + encoding_(encoding), + current_data_(NULL), + current_data_offset_(0), + current_data_length_(0), + utf8_split_char_buffer_length_(0), + bookmark_(0), + bookmark_data_is_from_current_data_(false), + bookmark_data_offset_(0), + bookmark_utf8_split_char_buffer_length_(0) {} + + ~ExternalStreamingStream() override { + delete[] current_data_; + bookmark_buffer_.Dispose(); + bookmark_data_.Dispose(); + } + + size_t BufferSeekForward(size_t delta) override { + // We never need to seek forward when streaming scripts. We only seek + // forward when we want to parse a function whose location we already know, + // and when streaming, we don't know the locations of anything we haven't + // seen yet. + UNREACHABLE(); + return 0; + } + + size_t FillBuffer(size_t position) override; + + bool SetBookmark() override; + void ResetToBookmark() override; + + private: + void HandleUtf8SplitCharacters(size_t* data_in_buffer); + void FlushCurrent(); + + ScriptCompiler::ExternalSourceStream* source_stream_; + v8::ScriptCompiler::StreamedSource::Encoding encoding_; + const uint8_t* current_data_; + size_t current_data_offset_; + size_t current_data_length_; + // For converting UTF-8 characters which are split across two data chunks. + uint8_t utf8_split_char_buffer_[4]; + size_t utf8_split_char_buffer_length_; + + // Bookmark support. See comments in ExternalStreamingStream::SetBookmark + // for additional details. + size_t bookmark_; + Vector<uint16_t> bookmark_buffer_; + Vector<uint8_t> bookmark_data_; + bool bookmark_data_is_from_current_data_; + size_t bookmark_data_offset_; + uint8_t bookmark_utf8_split_char_buffer_[4]; + size_t bookmark_utf8_split_char_buffer_length_; +}; + + +// UTF16 buffer to read characters from an external string. +class ExternalTwoByteStringUtf16CharacterStream: public Utf16CharacterStream { + public: + ExternalTwoByteStringUtf16CharacterStream(Handle<ExternalTwoByteString> data, + int start_position, + int end_position); + ~ExternalTwoByteStringUtf16CharacterStream() override; + + void PushBack(uc32 character) override { + DCHECK(buffer_cursor_ > raw_data_); + buffer_cursor_--; + pos_--; + } + + bool SetBookmark() override; + void ResetToBookmark() override; + + protected: + size_t SlowSeekForward(size_t delta) override { + // Fast case always handles seeking. + return 0; + } + bool ReadBlock() override { + // Entire string is read at start. + return false; + } + Handle<ExternalTwoByteString> source_; + const uc16* raw_data_; // Pointer to the actual array of characters. + + private: + static const size_t kNoBookmark = -1; + + size_t bookmark_; +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_PARSING_SCANNER_CHARACTER_STREAMS_H_ diff --git a/deps/v8/src/parsing/scanner.cc b/deps/v8/src/parsing/scanner.cc new file mode 100644 index 0000000000..19fab9355e --- /dev/null +++ b/deps/v8/src/parsing/scanner.cc @@ -0,0 +1,1673 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Features shared by parsing and pre-parsing scanners. + +#include "src/parsing/scanner.h" + +#include <stdint.h> + +#include <cmath> + +#include "src/ast/ast-value-factory.h" +#include "src/char-predicates-inl.h" +#include "src/conversions-inl.h" +#include "src/list-inl.h" +#include "src/parsing/parser.h" + +namespace v8 { +namespace internal { + + +Handle<String> LiteralBuffer::Internalize(Isolate* isolate) const { + if (is_one_byte()) { + return isolate->factory()->InternalizeOneByteString(one_byte_literal()); + } + return isolate->factory()->InternalizeTwoByteString(two_byte_literal()); +} + + +// Default implementation for streams that do not support bookmarks. +bool Utf16CharacterStream::SetBookmark() { return false; } +void Utf16CharacterStream::ResetToBookmark() { UNREACHABLE(); } + + +// ---------------------------------------------------------------------------- +// Scanner + +Scanner::Scanner(UnicodeCache* unicode_cache) + : unicode_cache_(unicode_cache), + bookmark_c0_(kNoBookmark), + octal_pos_(Location::invalid()) { + bookmark_current_.literal_chars = &bookmark_current_literal_; + bookmark_current_.raw_literal_chars = &bookmark_current_raw_literal_; + bookmark_next_.literal_chars = &bookmark_next_literal_; + bookmark_next_.raw_literal_chars = &bookmark_next_raw_literal_; +} + + +void Scanner::Initialize(Utf16CharacterStream* source) { + source_ = source; + // Need to capture identifiers in order to recognize "get" and "set" + // in object literals. + Init(); + // Skip initial whitespace allowing HTML comment ends just like + // after a newline and scan first token. + has_line_terminator_before_next_ = true; + SkipWhiteSpace(); + Scan(); +} + + +template <bool capture_raw> +uc32 Scanner::ScanHexNumber(int expected_length) { + DCHECK(expected_length <= 4); // prevent overflow + + uc32 x = 0; + for (int i = 0; i < expected_length; i++) { + int d = HexValue(c0_); + if (d < 0) { + return -1; + } + x = x * 16 + d; + Advance<capture_raw>(); + } + + return x; +} + + +template <bool capture_raw> +uc32 Scanner::ScanUnlimitedLengthHexNumber(int max_value) { + uc32 x = 0; + int d = HexValue(c0_); + if (d < 0) { + return -1; + } + while (d >= 0) { + x = x * 16 + d; + if (x > max_value) return -1; + Advance<capture_raw>(); + d = HexValue(c0_); + } + return x; +} + + +// Ensure that tokens can be stored in a byte. +STATIC_ASSERT(Token::NUM_TOKENS <= 0x100); + +// Table of one-character tokens, by character (0x00..0x7f only). +static const byte one_char_tokens[] = { + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::LPAREN, // 0x28 + Token::RPAREN, // 0x29 + Token::ILLEGAL, + Token::ILLEGAL, + Token::COMMA, // 0x2c + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::COLON, // 0x3a + Token::SEMICOLON, // 0x3b + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::CONDITIONAL, // 0x3f + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::LBRACK, // 0x5b + Token::ILLEGAL, + Token::RBRACK, // 0x5d + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::LBRACE, // 0x7b + Token::ILLEGAL, + Token::RBRACE, // 0x7d + Token::BIT_NOT, // 0x7e + Token::ILLEGAL +}; + + +Token::Value Scanner::Next() { + if (next_.token == Token::EOS) { + next_.location.beg_pos = current_.location.beg_pos; + next_.location.end_pos = current_.location.end_pos; + } + current_ = next_; + if (V8_UNLIKELY(next_next_.token != Token::UNINITIALIZED)) { + next_ = next_next_; + next_next_.token = Token::UNINITIALIZED; + return current_.token; + } + has_line_terminator_before_next_ = false; + has_multiline_comment_before_next_ = false; + if (static_cast<unsigned>(c0_) <= 0x7f) { + Token::Value token = static_cast<Token::Value>(one_char_tokens[c0_]); + if (token != Token::ILLEGAL) { + int pos = source_pos(); + next_.token = token; + next_.location.beg_pos = pos; + next_.location.end_pos = pos + 1; + Advance(); + return current_.token; + } + } + Scan(); + return current_.token; +} + + +Token::Value Scanner::PeekAhead() { + if (next_next_.token != Token::UNINITIALIZED) { + return next_next_.token; + } + TokenDesc prev = current_; + Next(); + Token::Value ret = next_.token; + next_next_ = next_; + next_ = current_; + current_ = prev; + return ret; +} + + +// TODO(yangguo): check whether this is actually necessary. +static inline bool IsLittleEndianByteOrderMark(uc32 c) { + // The Unicode value U+FFFE is guaranteed never to be assigned as a + // Unicode character; this implies that in a Unicode context the + // 0xFF, 0xFE byte pattern can only be interpreted as the U+FEFF + // character expressed in little-endian byte order (since it could + // not be a U+FFFE character expressed in big-endian byte + // order). Nevertheless, we check for it to be compatible with + // Spidermonkey. + return c == 0xFFFE; +} + + +bool Scanner::SkipWhiteSpace() { + int start_position = source_pos(); + + while (true) { + while (true) { + // The unicode cache accepts unsigned inputs. + if (c0_ < 0) break; + // Advance as long as character is a WhiteSpace or LineTerminator. + // Remember if the latter is the case. + if (unicode_cache_->IsLineTerminator(c0_)) { + has_line_terminator_before_next_ = true; + } else if (!unicode_cache_->IsWhiteSpace(c0_) && + !IsLittleEndianByteOrderMark(c0_)) { + break; + } + Advance(); + } + + // If there is an HTML comment end '-->' at the beginning of a + // line (with only whitespace in front of it), we treat the rest + // of the line as a comment. This is in line with the way + // SpiderMonkey handles it. + if (c0_ == '-' && has_line_terminator_before_next_) { + Advance(); + if (c0_ == '-') { + Advance(); + if (c0_ == '>') { + // Treat the rest of the line as a comment. + SkipSingleLineComment(); + // Continue skipping white space after the comment. + continue; + } + PushBack('-'); // undo Advance() + } + PushBack('-'); // undo Advance() + } + // Return whether or not we skipped any characters. + return source_pos() != start_position; + } +} + + +Token::Value Scanner::SkipSingleLineComment() { + Advance(); + + // The line terminator at the end of the line is not considered + // to be part of the single-line comment; it is recognized + // separately by the lexical grammar and becomes part of the + // stream of input elements for the syntactic grammar (see + // ECMA-262, section 7.4). + while (c0_ >= 0 && !unicode_cache_->IsLineTerminator(c0_)) { + Advance(); + } + + return Token::WHITESPACE; +} + + +Token::Value Scanner::SkipSourceURLComment() { + TryToParseSourceURLComment(); + while (c0_ >= 0 && !unicode_cache_->IsLineTerminator(c0_)) { + Advance(); + } + + return Token::WHITESPACE; +} + + +void Scanner::TryToParseSourceURLComment() { + // Magic comments are of the form: //[#]\s<name>=\s*<value>\s*.* and this + // function will just return if it cannot parse a magic comment. + if (c0_ < 0 || !unicode_cache_->IsWhiteSpace(c0_)) return; + Advance(); + LiteralBuffer name; + while (c0_ >= 0 && !unicode_cache_->IsWhiteSpaceOrLineTerminator(c0_) && + c0_ != '=') { + name.AddChar(c0_); + Advance(); + } + if (!name.is_one_byte()) return; + Vector<const uint8_t> name_literal = name.one_byte_literal(); + LiteralBuffer* value; + if (name_literal == STATIC_CHAR_VECTOR("sourceURL")) { + value = &source_url_; + } else if (name_literal == STATIC_CHAR_VECTOR("sourceMappingURL")) { + value = &source_mapping_url_; + } else { + return; + } + if (c0_ != '=') + return; + Advance(); + value->Reset(); + while (c0_ >= 0 && unicode_cache_->IsWhiteSpace(c0_)) { + Advance(); + } + while (c0_ >= 0 && !unicode_cache_->IsLineTerminator(c0_)) { + // Disallowed characters. + if (c0_ == '"' || c0_ == '\'') { + value->Reset(); + return; + } + if (unicode_cache_->IsWhiteSpace(c0_)) { + break; + } + value->AddChar(c0_); + Advance(); + } + // Allow whitespace at the end. + while (c0_ >= 0 && !unicode_cache_->IsLineTerminator(c0_)) { + if (!unicode_cache_->IsWhiteSpace(c0_)) { + value->Reset(); + break; + } + Advance(); + } +} + + +Token::Value Scanner::SkipMultiLineComment() { + DCHECK(c0_ == '*'); + Advance(); + + while (c0_ >= 0) { + uc32 ch = c0_; + Advance(); + if (c0_ >= 0 && unicode_cache_->IsLineTerminator(ch)) { + // Following ECMA-262, section 7.4, a comment containing + // a newline will make the comment count as a line-terminator. + has_multiline_comment_before_next_ = true; + } + // If we have reached the end of the multi-line comment, we + // consume the '/' and insert a whitespace. This way all + // multi-line comments are treated as whitespace. + if (ch == '*' && c0_ == '/') { + c0_ = ' '; + return Token::WHITESPACE; + } + } + + // Unterminated multi-line comment. + return Token::ILLEGAL; +} + + +Token::Value Scanner::ScanHtmlComment() { + // Check for <!-- comments. + DCHECK(c0_ == '!'); + Advance(); + if (c0_ == '-') { + Advance(); + if (c0_ == '-') return SkipSingleLineComment(); + PushBack('-'); // undo Advance() + } + PushBack('!'); // undo Advance() + DCHECK(c0_ == '!'); + return Token::LT; +} + + +void Scanner::Scan() { + next_.literal_chars = NULL; + next_.raw_literal_chars = NULL; + Token::Value token; + do { + // Remember the position of the next token + next_.location.beg_pos = source_pos(); + + switch (c0_) { + case ' ': + case '\t': + Advance(); + token = Token::WHITESPACE; + break; + + case '\n': + Advance(); + has_line_terminator_before_next_ = true; + token = Token::WHITESPACE; + break; + + case '"': case '\'': + token = ScanString(); + break; + + case '<': + // < <= << <<= <!-- + Advance(); + if (c0_ == '=') { + token = Select(Token::LTE); + } else if (c0_ == '<') { + token = Select('=', Token::ASSIGN_SHL, Token::SHL); + } else if (c0_ == '!') { + token = ScanHtmlComment(); + } else { + token = Token::LT; + } + break; + + case '>': + // > >= >> >>= >>> >>>= + Advance(); + if (c0_ == '=') { + token = Select(Token::GTE); + } else if (c0_ == '>') { + // >> >>= >>> >>>= + Advance(); + if (c0_ == '=') { + token = Select(Token::ASSIGN_SAR); + } else if (c0_ == '>') { + token = Select('=', Token::ASSIGN_SHR, Token::SHR); + } else { + token = Token::SAR; + } + } else { + token = Token::GT; + } + break; + + case '=': + // = == === => + Advance(); + if (c0_ == '=') { + token = Select('=', Token::EQ_STRICT, Token::EQ); + } else if (c0_ == '>') { + token = Select(Token::ARROW); + } else { + token = Token::ASSIGN; + } + break; + + case '!': + // ! != !== + Advance(); + if (c0_ == '=') { + token = Select('=', Token::NE_STRICT, Token::NE); + } else { + token = Token::NOT; + } + break; + + case '+': + // + ++ += + Advance(); + if (c0_ == '+') { + token = Select(Token::INC); + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_ADD); + } else { + token = Token::ADD; + } + break; + + case '-': + // - -- --> -= + Advance(); + if (c0_ == '-') { + Advance(); + if (c0_ == '>' && has_line_terminator_before_next_) { + // For compatibility with SpiderMonkey, we skip lines that + // start with an HTML comment end '-->'. + token = SkipSingleLineComment(); + } else { + token = Token::DEC; + } + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_SUB); + } else { + token = Token::SUB; + } + break; + + case '*': + // * *= + token = Select('=', Token::ASSIGN_MUL, Token::MUL); + break; + + case '%': + // % %= + token = Select('=', Token::ASSIGN_MOD, Token::MOD); + break; + + case '/': + // / // /* /= + Advance(); + if (c0_ == '/') { + Advance(); + if (c0_ == '#') { + Advance(); + token = SkipSourceURLComment(); + } else { + PushBack(c0_); + token = SkipSingleLineComment(); + } + } else if (c0_ == '*') { + token = SkipMultiLineComment(); + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_DIV); + } else { + token = Token::DIV; + } + break; + + case '&': + // & && &= + Advance(); + if (c0_ == '&') { + token = Select(Token::AND); + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_BIT_AND); + } else { + token = Token::BIT_AND; + } + break; + + case '|': + // | || |= + Advance(); + if (c0_ == '|') { + token = Select(Token::OR); + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_BIT_OR); + } else { + token = Token::BIT_OR; + } + break; + + case '^': + // ^ ^= + token = Select('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR); + break; + + case '.': + // . Number + Advance(); + if (IsDecimalDigit(c0_)) { + token = ScanNumber(true); + } else { + token = Token::PERIOD; + if (c0_ == '.') { + Advance(); + if (c0_ == '.') { + Advance(); + token = Token::ELLIPSIS; + } else { + PushBack('.'); + } + } + } + break; + + case ':': + token = Select(Token::COLON); + break; + + case ';': + token = Select(Token::SEMICOLON); + break; + + case ',': + token = Select(Token::COMMA); + break; + + case '(': + token = Select(Token::LPAREN); + break; + + case ')': + token = Select(Token::RPAREN); + break; + + case '[': + token = Select(Token::LBRACK); + break; + + case ']': + token = Select(Token::RBRACK); + break; + + case '{': + token = Select(Token::LBRACE); + break; + + case '}': + token = Select(Token::RBRACE); + break; + + case '?': + token = Select(Token::CONDITIONAL); + break; + + case '~': + token = Select(Token::BIT_NOT); + break; + + case '`': + token = ScanTemplateStart(); + break; + + default: + if (c0_ < 0) { + token = Token::EOS; + } else if (unicode_cache_->IsIdentifierStart(c0_)) { + token = ScanIdentifierOrKeyword(); + } else if (IsDecimalDigit(c0_)) { + token = ScanNumber(false); + } else if (SkipWhiteSpace()) { + token = Token::WHITESPACE; + } else { + token = Select(Token::ILLEGAL); + } + break; + } + + // Continue scanning for tokens as long as we're just skipping + // whitespace. + } while (token == Token::WHITESPACE); + + next_.location.end_pos = source_pos(); + next_.token = token; +} + + +void Scanner::SeekForward(int pos) { + // After this call, we will have the token at the given position as + // the "next" token. The "current" token will be invalid. + if (pos == next_.location.beg_pos) return; + int current_pos = source_pos(); + DCHECK_EQ(next_.location.end_pos, current_pos); + // Positions inside the lookahead token aren't supported. + DCHECK(pos >= current_pos); + if (pos != current_pos) { + source_->SeekForward(pos - source_->pos()); + Advance(); + // This function is only called to seek to the location + // of the end of a function (at the "}" token). It doesn't matter + // whether there was a line terminator in the part we skip. + has_line_terminator_before_next_ = false; + has_multiline_comment_before_next_ = false; + } + Scan(); +} + + +template <bool capture_raw, bool in_template_literal> +bool Scanner::ScanEscape() { + uc32 c = c0_; + Advance<capture_raw>(); + + // Skip escaped newlines. + if (!in_template_literal && c0_ >= 0 && unicode_cache_->IsLineTerminator(c)) { + // Allow CR+LF newlines in multiline string literals. + if (IsCarriageReturn(c) && IsLineFeed(c0_)) Advance<capture_raw>(); + // Allow LF+CR newlines in multiline string literals. + if (IsLineFeed(c) && IsCarriageReturn(c0_)) Advance<capture_raw>(); + return true; + } + + switch (c) { + case '\'': // fall through + case '"' : // fall through + case '\\': break; + case 'b' : c = '\b'; break; + case 'f' : c = '\f'; break; + case 'n' : c = '\n'; break; + case 'r' : c = '\r'; break; + case 't' : c = '\t'; break; + case 'u' : { + c = ScanUnicodeEscape<capture_raw>(); + if (c < 0) return false; + break; + } + case 'v': + c = '\v'; + break; + case 'x': { + c = ScanHexNumber<capture_raw>(2); + if (c < 0) return false; + break; + } + case '0': // Fall through. + case '1': // fall through + case '2': // fall through + case '3': // fall through + case '4': // fall through + case '5': // fall through + case '6': // fall through + case '7': + c = ScanOctalEscape<capture_raw>(c, 2); + break; + } + + // According to ECMA-262, section 7.8.4, characters not covered by the + // above cases should be illegal, but they are commonly handled as + // non-escaped characters by JS VMs. + AddLiteralChar(c); + return true; +} + + +// Octal escapes of the forms '\0xx' and '\xxx' are not a part of +// ECMA-262. Other JS VMs support them. +template <bool capture_raw> +uc32 Scanner::ScanOctalEscape(uc32 c, int length) { + uc32 x = c - '0'; + int i = 0; + for (; i < length; i++) { + int d = c0_ - '0'; + if (d < 0 || d > 7) break; + int nx = x * 8 + d; + if (nx >= 256) break; + x = nx; + Advance<capture_raw>(); + } + // Anything except '\0' is an octal escape sequence, illegal in strict mode. + // Remember the position of octal escape sequences so that an error + // can be reported later (in strict mode). + // We don't report the error immediately, because the octal escape can + // occur before the "use strict" directive. + if (c != '0' || i > 0) { + octal_pos_ = Location(source_pos() - i - 1, source_pos() - 1); + } + return x; +} + + +const int kMaxAscii = 127; + + +Token::Value Scanner::ScanString() { + uc32 quote = c0_; + Advance<false, false>(); // consume quote + + LiteralScope literal(this); + while (true) { + if (c0_ > kMaxAscii) { + HandleLeadSurrogate(); + break; + } + if (c0_ < 0 || c0_ == '\n' || c0_ == '\r') return Token::ILLEGAL; + if (c0_ == quote) { + literal.Complete(); + Advance<false, false>(); + return Token::STRING; + } + uc32 c = c0_; + if (c == '\\') break; + Advance<false, false>(); + AddLiteralChar(c); + } + + while (c0_ != quote && c0_ >= 0 + && !unicode_cache_->IsLineTerminator(c0_)) { + uc32 c = c0_; + Advance(); + if (c == '\\') { + if (c0_ < 0 || !ScanEscape<false, false>()) return Token::ILLEGAL; + } else { + AddLiteralChar(c); + } + } + if (c0_ != quote) return Token::ILLEGAL; + literal.Complete(); + + Advance(); // consume quote + return Token::STRING; +} + + +Token::Value Scanner::ScanTemplateSpan() { + // When scanning a TemplateSpan, we are looking for the following construct: + // TEMPLATE_SPAN :: + // ` LiteralChars* ${ + // | } LiteralChars* ${ + // + // TEMPLATE_TAIL :: + // ` LiteralChars* ` + // | } LiteralChar* ` + // + // A TEMPLATE_SPAN should always be followed by an Expression, while a + // TEMPLATE_TAIL terminates a TemplateLiteral and does not need to be + // followed by an Expression. + + Token::Value result = Token::TEMPLATE_SPAN; + LiteralScope literal(this); + StartRawLiteral(); + const bool capture_raw = true; + const bool in_template_literal = true; + + while (true) { + uc32 c = c0_; + Advance<capture_raw>(); + if (c == '`') { + result = Token::TEMPLATE_TAIL; + ReduceRawLiteralLength(1); + break; + } else if (c == '$' && c0_ == '{') { + Advance<capture_raw>(); // Consume '{' + ReduceRawLiteralLength(2); + break; + } else if (c == '\\') { + if (c0_ > 0 && unicode_cache_->IsLineTerminator(c0_)) { + // The TV of LineContinuation :: \ LineTerminatorSequence is the empty + // code unit sequence. + uc32 lastChar = c0_; + Advance<capture_raw>(); + if (lastChar == '\r') { + ReduceRawLiteralLength(1); // Remove \r + if (c0_ == '\n') { + Advance<capture_raw>(); // Adds \n + } else { + AddRawLiteralChar('\n'); + } + } + } else if (!ScanEscape<capture_raw, in_template_literal>()) { + return Token::ILLEGAL; + } + } else if (c < 0) { + // Unterminated template literal + PushBack(c); + break; + } else { + // The TRV of LineTerminatorSequence :: <CR> is the CV 0x000A. + // The TRV of LineTerminatorSequence :: <CR><LF> is the sequence + // consisting of the CV 0x000A. + if (c == '\r') { + ReduceRawLiteralLength(1); // Remove \r + if (c0_ == '\n') { + Advance<capture_raw>(); // Adds \n + } else { + AddRawLiteralChar('\n'); + } + c = '\n'; + } + AddLiteralChar(c); + } + } + literal.Complete(); + next_.location.end_pos = source_pos(); + next_.token = result; + return result; +} + + +Token::Value Scanner::ScanTemplateStart() { + DCHECK(c0_ == '`'); + next_.location.beg_pos = source_pos(); + Advance(); // Consume ` + return ScanTemplateSpan(); +} + + +Token::Value Scanner::ScanTemplateContinuation() { + DCHECK_EQ(next_.token, Token::RBRACE); + next_.location.beg_pos = source_pos() - 1; // We already consumed } + return ScanTemplateSpan(); +} + + +void Scanner::ScanDecimalDigits() { + while (IsDecimalDigit(c0_)) + AddLiteralCharAdvance(); +} + + +Token::Value Scanner::ScanNumber(bool seen_period) { + DCHECK(IsDecimalDigit(c0_)); // the first digit of the number or the fraction + + enum { DECIMAL, HEX, OCTAL, IMPLICIT_OCTAL, BINARY } kind = DECIMAL; + + LiteralScope literal(this); + bool at_start = !seen_period; + if (seen_period) { + // we have already seen a decimal point of the float + AddLiteralChar('.'); + ScanDecimalDigits(); // we know we have at least one digit + + } else { + // if the first character is '0' we must check for octals and hex + if (c0_ == '0') { + int start_pos = source_pos(); // For reporting octal positions. + AddLiteralCharAdvance(); + + // either 0, 0exxx, 0Exxx, 0.xxx, a hex number, a binary number or + // an octal number. + if (c0_ == 'x' || c0_ == 'X') { + // hex number + kind = HEX; + AddLiteralCharAdvance(); + if (!IsHexDigit(c0_)) { + // we must have at least one hex digit after 'x'/'X' + return Token::ILLEGAL; + } + while (IsHexDigit(c0_)) { + AddLiteralCharAdvance(); + } + } else if (c0_ == 'o' || c0_ == 'O') { + kind = OCTAL; + AddLiteralCharAdvance(); + if (!IsOctalDigit(c0_)) { + // we must have at least one octal digit after 'o'/'O' + return Token::ILLEGAL; + } + while (IsOctalDigit(c0_)) { + AddLiteralCharAdvance(); + } + } else if (c0_ == 'b' || c0_ == 'B') { + kind = BINARY; + AddLiteralCharAdvance(); + if (!IsBinaryDigit(c0_)) { + // we must have at least one binary digit after 'b'/'B' + return Token::ILLEGAL; + } + while (IsBinaryDigit(c0_)) { + AddLiteralCharAdvance(); + } + } else if ('0' <= c0_ && c0_ <= '7') { + // (possible) octal number + kind = IMPLICIT_OCTAL; + while (true) { + if (c0_ == '8' || c0_ == '9') { + at_start = false; + kind = DECIMAL; + break; + } + if (c0_ < '0' || '7' < c0_) { + // Octal literal finished. + octal_pos_ = Location(start_pos, source_pos()); + break; + } + AddLiteralCharAdvance(); + } + } + } + + // Parse decimal digits and allow trailing fractional part. + if (kind == DECIMAL) { + if (at_start) { + uint64_t value = 0; + while (IsDecimalDigit(c0_)) { + value = 10 * value + (c0_ - '0'); + + uc32 first_char = c0_; + Advance<false, false>(); + AddLiteralChar(first_char); + } + + if (next_.literal_chars->one_byte_literal().length() <= 10 && + value <= Smi::kMaxValue && c0_ != '.' && c0_ != 'e' && c0_ != 'E') { + next_.smi_value_ = static_cast<int>(value); + literal.Complete(); + HandleLeadSurrogate(); + + return Token::SMI; + } + HandleLeadSurrogate(); + } + + ScanDecimalDigits(); // optional + if (c0_ == '.') { + AddLiteralCharAdvance(); + ScanDecimalDigits(); // optional + } + } + } + + // scan exponent, if any + if (c0_ == 'e' || c0_ == 'E') { + DCHECK(kind != HEX); // 'e'/'E' must be scanned as part of the hex number + if (kind != DECIMAL) return Token::ILLEGAL; + // scan exponent + AddLiteralCharAdvance(); + if (c0_ == '+' || c0_ == '-') + AddLiteralCharAdvance(); + if (!IsDecimalDigit(c0_)) { + // we must have at least one decimal digit after 'e'/'E' + return Token::ILLEGAL; + } + ScanDecimalDigits(); + } + + // The source character immediately following a numeric literal must + // not be an identifier start or a decimal digit; see ECMA-262 + // section 7.8.3, page 17 (note that we read only one decimal digit + // if the value is 0). + if (IsDecimalDigit(c0_) || + (c0_ >= 0 && unicode_cache_->IsIdentifierStart(c0_))) + return Token::ILLEGAL; + + literal.Complete(); + + return Token::NUMBER; +} + + +uc32 Scanner::ScanIdentifierUnicodeEscape() { + Advance(); + if (c0_ != 'u') return -1; + Advance(); + return ScanUnicodeEscape<false>(); +} + + +template <bool capture_raw> +uc32 Scanner::ScanUnicodeEscape() { + // Accept both \uxxxx and \u{xxxxxx}. In the latter case, the number of + // hex digits between { } is arbitrary. \ and u have already been read. + if (c0_ == '{') { + Advance<capture_raw>(); + uc32 cp = ScanUnlimitedLengthHexNumber<capture_raw>(0x10ffff); + if (cp < 0) { + return -1; + } + if (c0_ != '}') { + return -1; + } + Advance<capture_raw>(); + return cp; + } + return ScanHexNumber<capture_raw>(4); +} + + +// ---------------------------------------------------------------------------- +// Keyword Matcher + +#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \ + KEYWORD_GROUP('b') \ + KEYWORD("break", Token::BREAK) \ + KEYWORD_GROUP('c') \ + KEYWORD("case", Token::CASE) \ + KEYWORD("catch", Token::CATCH) \ + KEYWORD("class", Token::CLASS) \ + KEYWORD("const", Token::CONST) \ + KEYWORD("continue", Token::CONTINUE) \ + KEYWORD_GROUP('d') \ + KEYWORD("debugger", Token::DEBUGGER) \ + KEYWORD("default", Token::DEFAULT) \ + KEYWORD("delete", Token::DELETE) \ + KEYWORD("do", Token::DO) \ + KEYWORD_GROUP('e') \ + KEYWORD("else", Token::ELSE) \ + KEYWORD("enum", Token::FUTURE_RESERVED_WORD) \ + KEYWORD("export", Token::EXPORT) \ + KEYWORD("extends", Token::EXTENDS) \ + KEYWORD_GROUP('f') \ + KEYWORD("false", Token::FALSE_LITERAL) \ + KEYWORD("finally", Token::FINALLY) \ + KEYWORD("for", Token::FOR) \ + KEYWORD("function", Token::FUNCTION) \ + KEYWORD_GROUP('i') \ + KEYWORD("if", Token::IF) \ + KEYWORD("implements", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("import", Token::IMPORT) \ + KEYWORD("in", Token::IN) \ + KEYWORD("instanceof", Token::INSTANCEOF) \ + KEYWORD("interface", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD_GROUP('l') \ + KEYWORD("let", Token::LET) \ + KEYWORD_GROUP('n') \ + KEYWORD("new", Token::NEW) \ + KEYWORD("null", Token::NULL_LITERAL) \ + KEYWORD_GROUP('p') \ + KEYWORD("package", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("private", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("protected", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("public", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD_GROUP('r') \ + KEYWORD("return", Token::RETURN) \ + KEYWORD_GROUP('s') \ + KEYWORD("static", Token::STATIC) \ + KEYWORD("super", Token::SUPER) \ + KEYWORD("switch", Token::SWITCH) \ + KEYWORD_GROUP('t') \ + KEYWORD("this", Token::THIS) \ + KEYWORD("throw", Token::THROW) \ + KEYWORD("true", Token::TRUE_LITERAL) \ + KEYWORD("try", Token::TRY) \ + KEYWORD("typeof", Token::TYPEOF) \ + KEYWORD_GROUP('v') \ + KEYWORD("var", Token::VAR) \ + KEYWORD("void", Token::VOID) \ + KEYWORD_GROUP('w') \ + KEYWORD("while", Token::WHILE) \ + KEYWORD("with", Token::WITH) \ + KEYWORD_GROUP('y') \ + KEYWORD("yield", Token::YIELD) + + +static Token::Value KeywordOrIdentifierToken(const uint8_t* input, + int input_length, bool escaped) { + DCHECK(input_length >= 1); + const int kMinLength = 2; + const int kMaxLength = 10; + if (input_length < kMinLength || input_length > kMaxLength) { + return Token::IDENTIFIER; + } + switch (input[0]) { + default: +#define KEYWORD_GROUP_CASE(ch) \ + break; \ + case ch: +#define KEYWORD(keyword, token) \ + { \ + /* 'keyword' is a char array, so sizeof(keyword) is */ \ + /* strlen(keyword) plus 1 for the NUL char. */ \ + const int keyword_length = sizeof(keyword) - 1; \ + STATIC_ASSERT(keyword_length >= kMinLength); \ + STATIC_ASSERT(keyword_length <= kMaxLength); \ + if (input_length == keyword_length && input[1] == keyword[1] && \ + (keyword_length <= 2 || input[2] == keyword[2]) && \ + (keyword_length <= 3 || input[3] == keyword[3]) && \ + (keyword_length <= 4 || input[4] == keyword[4]) && \ + (keyword_length <= 5 || input[5] == keyword[5]) && \ + (keyword_length <= 6 || input[6] == keyword[6]) && \ + (keyword_length <= 7 || input[7] == keyword[7]) && \ + (keyword_length <= 8 || input[8] == keyword[8]) && \ + (keyword_length <= 9 || input[9] == keyword[9])) { \ + if (escaped) { \ + return token == Token::FUTURE_STRICT_RESERVED_WORD \ + ? Token::ESCAPED_STRICT_RESERVED_WORD \ + : Token::ESCAPED_KEYWORD; \ + } \ + return token; \ + } \ + } + KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD) + } + return Token::IDENTIFIER; +} + + +bool Scanner::IdentifierIsFutureStrictReserved( + const AstRawString* string) const { + // Keywords are always 1-byte strings. + if (!string->is_one_byte()) return false; + if (string->IsOneByteEqualTo("let") || string->IsOneByteEqualTo("static") || + string->IsOneByteEqualTo("yield")) { + return true; + } + return Token::FUTURE_STRICT_RESERVED_WORD == + KeywordOrIdentifierToken(string->raw_data(), string->length(), false); +} + + +Token::Value Scanner::ScanIdentifierOrKeyword() { + DCHECK(unicode_cache_->IsIdentifierStart(c0_)); + LiteralScope literal(this); + if (IsInRange(c0_, 'a', 'z')) { + do { + uc32 first_char = c0_; + Advance<false, false>(); + AddLiteralChar(first_char); + } while (IsInRange(c0_, 'a', 'z')); + + if (IsDecimalDigit(c0_) || IsInRange(c0_, 'A', 'Z') || c0_ == '_' || + c0_ == '$') { + // Identifier starting with lowercase. + uc32 first_char = c0_; + Advance<false, false>(); + AddLiteralChar(first_char); + while (IsAsciiIdentifier(c0_)) { + uc32 first_char = c0_; + Advance<false, false>(); + AddLiteralChar(first_char); + } + if (c0_ <= kMaxAscii && c0_ != '\\') { + literal.Complete(); + return Token::IDENTIFIER; + } + } else if (c0_ <= kMaxAscii && c0_ != '\\') { + // Only a-z+: could be a keyword or identifier. + literal.Complete(); + Vector<const uint8_t> chars = next_.literal_chars->one_byte_literal(); + return KeywordOrIdentifierToken(chars.start(), chars.length(), false); + } + + HandleLeadSurrogate(); + } else if (IsInRange(c0_, 'A', 'Z') || c0_ == '_' || c0_ == '$') { + do { + uc32 first_char = c0_; + Advance<false, false>(); + AddLiteralChar(first_char); + } while (IsAsciiIdentifier(c0_)); + + if (c0_ <= kMaxAscii && c0_ != '\\') { + literal.Complete(); + return Token::IDENTIFIER; + } + + HandleLeadSurrogate(); + } else if (c0_ == '\\') { + // Scan identifier start character. + uc32 c = ScanIdentifierUnicodeEscape(); + // Only allow legal identifier start characters. + if (c < 0 || + c == '\\' || // No recursive escapes. + !unicode_cache_->IsIdentifierStart(c)) { + return Token::ILLEGAL; + } + AddLiteralChar(c); + return ScanIdentifierSuffix(&literal, true); + } else { + uc32 first_char = c0_; + Advance(); + AddLiteralChar(first_char); + } + + // Scan the rest of the identifier characters. + while (c0_ >= 0 && unicode_cache_->IsIdentifierPart(c0_)) { + if (c0_ != '\\') { + uc32 next_char = c0_; + Advance(); + AddLiteralChar(next_char); + continue; + } + // Fallthrough if no longer able to complete keyword. + return ScanIdentifierSuffix(&literal, false); + } + + literal.Complete(); + + if (next_.literal_chars->is_one_byte()) { + Vector<const uint8_t> chars = next_.literal_chars->one_byte_literal(); + return KeywordOrIdentifierToken(chars.start(), chars.length(), false); + } + return Token::IDENTIFIER; +} + + +Token::Value Scanner::ScanIdentifierSuffix(LiteralScope* literal, + bool escaped) { + // Scan the rest of the identifier characters. + while (c0_ >= 0 && unicode_cache_->IsIdentifierPart(c0_)) { + if (c0_ == '\\') { + uc32 c = ScanIdentifierUnicodeEscape(); + escaped = true; + // Only allow legal identifier part characters. + if (c < 0 || + c == '\\' || + !unicode_cache_->IsIdentifierPart(c)) { + return Token::ILLEGAL; + } + AddLiteralChar(c); + } else { + AddLiteralChar(c0_); + Advance(); + } + } + literal->Complete(); + + if (escaped && next_.literal_chars->is_one_byte()) { + Vector<const uint8_t> chars = next_.literal_chars->one_byte_literal(); + return KeywordOrIdentifierToken(chars.start(), chars.length(), true); + } + return Token::IDENTIFIER; +} + + +bool Scanner::ScanRegExpPattern(bool seen_equal) { + // Scan: ('/' | '/=') RegularExpressionBody '/' RegularExpressionFlags + bool in_character_class = false; + + // Previous token is either '/' or '/=', in the second case, the + // pattern starts at =. + next_.location.beg_pos = source_pos() - (seen_equal ? 2 : 1); + next_.location.end_pos = source_pos() - (seen_equal ? 1 : 0); + + // Scan regular expression body: According to ECMA-262, 3rd, 7.8.5, + // the scanner should pass uninterpreted bodies to the RegExp + // constructor. + LiteralScope literal(this); + if (seen_equal) { + AddLiteralChar('='); + } + + while (c0_ != '/' || in_character_class) { + if (c0_ < 0 || unicode_cache_->IsLineTerminator(c0_)) return false; + if (c0_ == '\\') { // Escape sequence. + AddLiteralCharAdvance(); + if (c0_ < 0 || unicode_cache_->IsLineTerminator(c0_)) return false; + AddLiteralCharAdvance(); + // If the escape allows more characters, i.e., \x??, \u????, or \c?, + // only "safe" characters are allowed (letters, digits, underscore), + // otherwise the escape isn't valid and the invalid character has + // its normal meaning. I.e., we can just continue scanning without + // worrying whether the following characters are part of the escape + // or not, since any '/', '\\' or '[' is guaranteed to not be part + // of the escape sequence. + + // TODO(896): At some point, parse RegExps more throughly to capture + // octal esacpes in strict mode. + } else { // Unescaped character. + if (c0_ == '[') in_character_class = true; + if (c0_ == ']') in_character_class = false; + AddLiteralCharAdvance(); + } + } + Advance(); // consume '/' + + literal.Complete(); + + return true; +} + + +Maybe<RegExp::Flags> Scanner::ScanRegExpFlags() { + // Scan regular expression flags. + LiteralScope literal(this); + int flags = 0; + while (c0_ >= 0 && unicode_cache_->IsIdentifierPart(c0_)) { + RegExp::Flags flag = RegExp::kNone; + switch (c0_) { + case 'g': + flag = RegExp::kGlobal; + break; + case 'i': + flag = RegExp::kIgnoreCase; + break; + case 'm': + flag = RegExp::kMultiline; + break; + case 'u': + if (!FLAG_harmony_unicode_regexps) return Nothing<RegExp::Flags>(); + flag = RegExp::kUnicode; + break; + case 'y': + if (!FLAG_harmony_regexps) return Nothing<RegExp::Flags>(); + flag = RegExp::kSticky; + break; + default: + return Nothing<RegExp::Flags>(); + } + if (flags & flag) return Nothing<RegExp::Flags>(); + AddLiteralCharAdvance(); + flags |= flag; + } + literal.Complete(); + + next_.location.end_pos = source_pos(); + return Just(RegExp::Flags(flags)); +} + + +const AstRawString* Scanner::CurrentSymbol(AstValueFactory* ast_value_factory) { + if (is_literal_one_byte()) { + return ast_value_factory->GetOneByteString(literal_one_byte_string()); + } + return ast_value_factory->GetTwoByteString(literal_two_byte_string()); +} + + +const AstRawString* Scanner::NextSymbol(AstValueFactory* ast_value_factory) { + if (is_next_literal_one_byte()) { + return ast_value_factory->GetOneByteString(next_literal_one_byte_string()); + } + return ast_value_factory->GetTwoByteString(next_literal_two_byte_string()); +} + + +const AstRawString* Scanner::CurrentRawSymbol( + AstValueFactory* ast_value_factory) { + if (is_raw_literal_one_byte()) { + return ast_value_factory->GetOneByteString(raw_literal_one_byte_string()); + } + return ast_value_factory->GetTwoByteString(raw_literal_two_byte_string()); +} + + +double Scanner::DoubleValue() { + DCHECK(is_literal_one_byte()); + return StringToDouble( + unicode_cache_, + literal_one_byte_string(), + ALLOW_HEX | ALLOW_OCTAL | ALLOW_IMPLICIT_OCTAL | ALLOW_BINARY); +} + + +bool Scanner::ContainsDot() { + DCHECK(is_literal_one_byte()); + Vector<const uint8_t> str = literal_one_byte_string(); + return std::find(str.begin(), str.end(), '.') != str.end(); +} + + +int Scanner::FindSymbol(DuplicateFinder* finder, int value) { + if (is_literal_one_byte()) { + return finder->AddOneByteSymbol(literal_one_byte_string(), value); + } + return finder->AddTwoByteSymbol(literal_two_byte_string(), value); +} + + +bool Scanner::SetBookmark() { + if (c0_ != kNoBookmark && bookmark_c0_ == kNoBookmark && + next_next_.token == Token::UNINITIALIZED && source_->SetBookmark()) { + bookmark_c0_ = c0_; + CopyTokenDesc(&bookmark_current_, ¤t_); + CopyTokenDesc(&bookmark_next_, &next_); + return true; + } + return false; +} + + +void Scanner::ResetToBookmark() { + DCHECK(BookmarkHasBeenSet()); // Caller hasn't called SetBookmark. + + source_->ResetToBookmark(); + c0_ = bookmark_c0_; + StartLiteral(); + StartRawLiteral(); + CopyTokenDesc(&next_, &bookmark_current_); + current_ = next_; + StartLiteral(); + StartRawLiteral(); + CopyTokenDesc(&next_, &bookmark_next_); + + bookmark_c0_ = kBookmarkWasApplied; +} + + +bool Scanner::BookmarkHasBeenSet() { return bookmark_c0_ >= 0; } + + +bool Scanner::BookmarkHasBeenReset() { + return bookmark_c0_ == kBookmarkWasApplied; +} + + +void Scanner::DropBookmark() { bookmark_c0_ = kNoBookmark; } + + +void Scanner::CopyTokenDesc(TokenDesc* to, TokenDesc* from) { + DCHECK_NOT_NULL(to); + DCHECK_NOT_NULL(from); + to->token = from->token; + to->location = from->location; + to->literal_chars->CopyFrom(from->literal_chars); + to->raw_literal_chars->CopyFrom(from->raw_literal_chars); +} + + +int DuplicateFinder::AddOneByteSymbol(Vector<const uint8_t> key, int value) { + return AddSymbol(key, true, value); +} + + +int DuplicateFinder::AddTwoByteSymbol(Vector<const uint16_t> key, int value) { + return AddSymbol(Vector<const uint8_t>::cast(key), false, value); +} + + +int DuplicateFinder::AddSymbol(Vector<const uint8_t> key, + bool is_one_byte, + int value) { + uint32_t hash = Hash(key, is_one_byte); + byte* encoding = BackupKey(key, is_one_byte); + HashMap::Entry* entry = map_.LookupOrInsert(encoding, hash); + int old_value = static_cast<int>(reinterpret_cast<intptr_t>(entry->value)); + entry->value = + reinterpret_cast<void*>(static_cast<intptr_t>(value | old_value)); + return old_value; +} + + +int DuplicateFinder::AddNumber(Vector<const uint8_t> key, int value) { + DCHECK(key.length() > 0); + // Quick check for already being in canonical form. + if (IsNumberCanonical(key)) { + return AddOneByteSymbol(key, value); + } + + int flags = ALLOW_HEX | ALLOW_OCTAL | ALLOW_IMPLICIT_OCTAL | ALLOW_BINARY; + double double_value = StringToDouble( + unicode_constants_, key, flags, 0.0); + int length; + const char* string; + if (!std::isfinite(double_value)) { + string = "Infinity"; + length = 8; // strlen("Infinity"); + } else { + string = DoubleToCString(double_value, + Vector<char>(number_buffer_, kBufferSize)); + length = StrLength(string); + } + return AddSymbol(Vector<const byte>(reinterpret_cast<const byte*>(string), + length), true, value); +} + + +bool DuplicateFinder::IsNumberCanonical(Vector<const uint8_t> number) { + // Test for a safe approximation of number literals that are already + // in canonical form: max 15 digits, no leading zeroes, except an + // integer part that is a single zero, and no trailing zeros below + // the decimal point. + int pos = 0; + int length = number.length(); + if (number.length() > 15) return false; + if (number[pos] == '0') { + pos++; + } else { + while (pos < length && + static_cast<unsigned>(number[pos] - '0') <= ('9' - '0')) pos++; + } + if (length == pos) return true; + if (number[pos] != '.') return false; + pos++; + bool invalid_last_digit = true; + while (pos < length) { + uint8_t digit = number[pos] - '0'; + if (digit > '9' - '0') return false; + invalid_last_digit = (digit == 0); + pos++; + } + return !invalid_last_digit; +} + + +uint32_t DuplicateFinder::Hash(Vector<const uint8_t> key, bool is_one_byte) { + // Primitive hash function, almost identical to the one used + // for strings (except that it's seeded by the length and representation). + int length = key.length(); + uint32_t hash = (length << 1) | (is_one_byte ? 1 : 0); + for (int i = 0; i < length; i++) { + uint32_t c = key[i]; + hash = (hash + c) * 1025; + hash ^= (hash >> 6); + } + return hash; +} + + +bool DuplicateFinder::Match(void* first, void* second) { + // Decode lengths. + // Length + representation is encoded as base 128, most significant heptet + // first, with a 8th bit being non-zero while there are more heptets. + // The value encodes the number of bytes following, and whether the original + // was Latin1. + byte* s1 = reinterpret_cast<byte*>(first); + byte* s2 = reinterpret_cast<byte*>(second); + uint32_t length_one_byte_field = 0; + byte c1; + do { + c1 = *s1; + if (c1 != *s2) return false; + length_one_byte_field = (length_one_byte_field << 7) | (c1 & 0x7f); + s1++; + s2++; + } while ((c1 & 0x80) != 0); + int length = static_cast<int>(length_one_byte_field >> 1); + return memcmp(s1, s2, length) == 0; +} + + +byte* DuplicateFinder::BackupKey(Vector<const uint8_t> bytes, + bool is_one_byte) { + uint32_t one_byte_length = (bytes.length() << 1) | (is_one_byte ? 1 : 0); + backing_store_.StartSequence(); + // Emit one_byte_length as base-128 encoded number, with the 7th bit set + // on the byte of every heptet except the last, least significant, one. + if (one_byte_length >= (1 << 7)) { + if (one_byte_length >= (1 << 14)) { + if (one_byte_length >= (1 << 21)) { + if (one_byte_length >= (1 << 28)) { + backing_store_.Add( + static_cast<uint8_t>((one_byte_length >> 28) | 0x80)); + } + backing_store_.Add( + static_cast<uint8_t>((one_byte_length >> 21) | 0x80u)); + } + backing_store_.Add( + static_cast<uint8_t>((one_byte_length >> 14) | 0x80u)); + } + backing_store_.Add(static_cast<uint8_t>((one_byte_length >> 7) | 0x80u)); + } + backing_store_.Add(static_cast<uint8_t>(one_byte_length & 0x7f)); + + backing_store_.AddBlock(bytes); + return backing_store_.EndSequence().start(); +} + +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/parsing/scanner.h b/deps/v8/src/parsing/scanner.h new file mode 100644 index 0000000000..1d0aba0611 --- /dev/null +++ b/deps/v8/src/parsing/scanner.h @@ -0,0 +1,760 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Features shared by parsing and pre-parsing scanners. + +#ifndef V8_PARSING_SCANNER_H_ +#define V8_PARSING_SCANNER_H_ + +#include "src/allocation.h" +#include "src/base/logging.h" +#include "src/char-predicates.h" +#include "src/globals.h" +#include "src/hashmap.h" +#include "src/list.h" +#include "src/parsing/token.h" +#include "src/unicode.h" +#include "src/unicode-decoder.h" +#include "src/utils.h" + +namespace v8 { +namespace internal { + + +class AstRawString; +class AstValueFactory; +class ParserRecorder; +class UnicodeCache; + + +// --------------------------------------------------------------------- +// Buffered stream of UTF-16 code units, using an internal UTF-16 buffer. +// A code unit is a 16 bit value representing either a 16 bit code point +// or one part of a surrogate pair that make a single 21 bit code point. + +class Utf16CharacterStream { + public: + Utf16CharacterStream() : pos_(0) { } + virtual ~Utf16CharacterStream() { } + + // Returns and advances past the next UTF-16 code unit in the input + // stream. If there are no more code units, it returns a negative + // value. + inline uc32 Advance() { + if (buffer_cursor_ < buffer_end_ || ReadBlock()) { + pos_++; + return static_cast<uc32>(*(buffer_cursor_++)); + } + // Note: currently the following increment is necessary to avoid a + // parser problem! The scanner treats the final kEndOfInput as + // a code unit with a position, and does math relative to that + // position. + pos_++; + + return kEndOfInput; + } + + // Return the current position in the code unit stream. + // Starts at zero. + inline size_t pos() const { return pos_; } + + // Skips forward past the next code_unit_count UTF-16 code units + // in the input, or until the end of input if that comes sooner. + // Returns the number of code units actually skipped. If less + // than code_unit_count, + inline size_t SeekForward(size_t code_unit_count) { + size_t buffered_chars = buffer_end_ - buffer_cursor_; + if (code_unit_count <= buffered_chars) { + buffer_cursor_ += code_unit_count; + pos_ += code_unit_count; + return code_unit_count; + } + return SlowSeekForward(code_unit_count); + } + + // Pushes back the most recently read UTF-16 code unit (or negative + // value if at end of input), i.e., the value returned by the most recent + // call to Advance. + // Must not be used right after calling SeekForward. + virtual void PushBack(int32_t code_unit) = 0; + + virtual bool SetBookmark(); + virtual void ResetToBookmark(); + + protected: + static const uc32 kEndOfInput = -1; + + // Ensures that the buffer_cursor_ points to the code_unit at + // position pos_ of the input, if possible. If the position + // is at or after the end of the input, return false. If there + // are more code_units available, return true. + virtual bool ReadBlock() = 0; + virtual size_t SlowSeekForward(size_t code_unit_count) = 0; + + const uint16_t* buffer_cursor_; + const uint16_t* buffer_end_; + size_t pos_; +}; + + +// --------------------------------------------------------------------- +// DuplicateFinder discovers duplicate symbols. + +class DuplicateFinder { + public: + explicit DuplicateFinder(UnicodeCache* constants) + : unicode_constants_(constants), + backing_store_(16), + map_(&Match) { } + + int AddOneByteSymbol(Vector<const uint8_t> key, int value); + int AddTwoByteSymbol(Vector<const uint16_t> key, int value); + // Add a a number literal by converting it (if necessary) + // to the string that ToString(ToNumber(literal)) would generate. + // and then adding that string with AddOneByteSymbol. + // This string is the actual value used as key in an object literal, + // and the one that must be different from the other keys. + int AddNumber(Vector<const uint8_t> key, int value); + + private: + int AddSymbol(Vector<const uint8_t> key, bool is_one_byte, int value); + // Backs up the key and its length in the backing store. + // The backup is stored with a base 127 encoding of the + // length (plus a bit saying whether the string is one byte), + // followed by the bytes of the key. + uint8_t* BackupKey(Vector<const uint8_t> key, bool is_one_byte); + + // Compare two encoded keys (both pointing into the backing store) + // for having the same base-127 encoded lengths and representation. + // and then having the same 'length' bytes following. + static bool Match(void* first, void* second); + // Creates a hash from a sequence of bytes. + static uint32_t Hash(Vector<const uint8_t> key, bool is_one_byte); + // Checks whether a string containing a JS number is its canonical + // form. + static bool IsNumberCanonical(Vector<const uint8_t> key); + + // Size of buffer. Sufficient for using it to call DoubleToCString in + // from conversions.h. + static const int kBufferSize = 100; + + UnicodeCache* unicode_constants_; + // Backing store used to store strings used as hashmap keys. + SequenceCollector<unsigned char> backing_store_; + HashMap map_; + // Buffer used for string->number->canonical string conversions. + char number_buffer_[kBufferSize]; +}; + + +// ---------------------------------------------------------------------------- +// LiteralBuffer - Collector of chars of literals. + +class LiteralBuffer { + public: + LiteralBuffer() : is_one_byte_(true), position_(0), backing_store_() { } + + ~LiteralBuffer() { backing_store_.Dispose(); } + + INLINE(void AddChar(uint32_t code_unit)) { + if (position_ >= backing_store_.length()) ExpandBuffer(); + if (is_one_byte_) { + if (code_unit <= unibrow::Latin1::kMaxChar) { + backing_store_[position_] = static_cast<byte>(code_unit); + position_ += kOneByteSize; + return; + } + ConvertToTwoByte(); + } + if (code_unit <= unibrow::Utf16::kMaxNonSurrogateCharCode) { + *reinterpret_cast<uint16_t*>(&backing_store_[position_]) = code_unit; + position_ += kUC16Size; + } else { + *reinterpret_cast<uint16_t*>(&backing_store_[position_]) = + unibrow::Utf16::LeadSurrogate(code_unit); + position_ += kUC16Size; + if (position_ >= backing_store_.length()) ExpandBuffer(); + *reinterpret_cast<uint16_t*>(&backing_store_[position_]) = + unibrow::Utf16::TrailSurrogate(code_unit); + position_ += kUC16Size; + } + } + + bool is_one_byte() const { return is_one_byte_; } + + bool is_contextual_keyword(Vector<const char> keyword) const { + return is_one_byte() && keyword.length() == position_ && + (memcmp(keyword.start(), backing_store_.start(), position_) == 0); + } + + Vector<const uint16_t> two_byte_literal() const { + DCHECK(!is_one_byte_); + DCHECK((position_ & 0x1) == 0); + return Vector<const uint16_t>( + reinterpret_cast<const uint16_t*>(backing_store_.start()), + position_ >> 1); + } + + Vector<const uint8_t> one_byte_literal() const { + DCHECK(is_one_byte_); + return Vector<const uint8_t>( + reinterpret_cast<const uint8_t*>(backing_store_.start()), + position_); + } + + int length() const { + return is_one_byte_ ? position_ : (position_ >> 1); + } + + void ReduceLength(int delta) { + position_ -= delta * (is_one_byte_ ? kOneByteSize : kUC16Size); + } + + void Reset() { + position_ = 0; + is_one_byte_ = true; + } + + Handle<String> Internalize(Isolate* isolate) const; + + void CopyFrom(const LiteralBuffer* other) { + if (other == nullptr) { + Reset(); + } else { + is_one_byte_ = other->is_one_byte_; + position_ = other->position_; + backing_store_.Dispose(); + backing_store_ = other->backing_store_.Clone(); + } + } + + private: + static const int kInitialCapacity = 16; + static const int kGrowthFactory = 4; + static const int kMinConversionSlack = 256; + static const int kMaxGrowth = 1 * MB; + inline int NewCapacity(int min_capacity) { + int capacity = Max(min_capacity, backing_store_.length()); + int new_capacity = Min(capacity * kGrowthFactory, capacity + kMaxGrowth); + return new_capacity; + } + + void ExpandBuffer() { + Vector<byte> new_store = Vector<byte>::New(NewCapacity(kInitialCapacity)); + MemCopy(new_store.start(), backing_store_.start(), position_); + backing_store_.Dispose(); + backing_store_ = new_store; + } + + void ConvertToTwoByte() { + DCHECK(is_one_byte_); + Vector<byte> new_store; + int new_content_size = position_ * kUC16Size; + if (new_content_size >= backing_store_.length()) { + // Ensure room for all currently read code units as UC16 as well + // as the code unit about to be stored. + new_store = Vector<byte>::New(NewCapacity(new_content_size)); + } else { + new_store = backing_store_; + } + uint8_t* src = backing_store_.start(); + uint16_t* dst = reinterpret_cast<uint16_t*>(new_store.start()); + for (int i = position_ - 1; i >= 0; i--) { + dst[i] = src[i]; + } + if (new_store.start() != backing_store_.start()) { + backing_store_.Dispose(); + backing_store_ = new_store; + } + position_ = new_content_size; + is_one_byte_ = false; + } + + bool is_one_byte_; + int position_; + Vector<byte> backing_store_; + + DISALLOW_COPY_AND_ASSIGN(LiteralBuffer); +}; + + +// ---------------------------------------------------------------------------- +// JavaScript Scanner. + +class Scanner { + public: + // Scoped helper for literal recording. Automatically drops the literal + // if aborting the scanning before it's complete. + class LiteralScope { + public: + explicit LiteralScope(Scanner* self) : scanner_(self), complete_(false) { + scanner_->StartLiteral(); + } + ~LiteralScope() { + if (!complete_) scanner_->DropLiteral(); + } + void Complete() { + complete_ = true; + } + + private: + Scanner* scanner_; + bool complete_; + }; + + // Scoped helper for a re-settable bookmark. + class BookmarkScope { + public: + explicit BookmarkScope(Scanner* scanner) : scanner_(scanner) { + DCHECK_NOT_NULL(scanner_); + } + ~BookmarkScope() { scanner_->DropBookmark(); } + + bool Set() { return scanner_->SetBookmark(); } + void Reset() { scanner_->ResetToBookmark(); } + bool HasBeenSet() { return scanner_->BookmarkHasBeenSet(); } + bool HasBeenReset() { return scanner_->BookmarkHasBeenReset(); } + + private: + Scanner* scanner_; + + DISALLOW_COPY_AND_ASSIGN(BookmarkScope); + }; + + // Representation of an interval of source positions. + struct Location { + Location(int b, int e) : beg_pos(b), end_pos(e) { } + Location() : beg_pos(0), end_pos(0) { } + + bool IsValid() const { + return beg_pos >= 0 && end_pos >= beg_pos; + } + + static Location invalid() { return Location(-1, -1); } + + int beg_pos; + int end_pos; + }; + + // -1 is outside of the range of any real source code. + static const int kNoOctalLocation = -1; + + explicit Scanner(UnicodeCache* scanner_contants); + + void Initialize(Utf16CharacterStream* source); + + // Returns the next token and advances input. + Token::Value Next(); + // Returns the token following peek() + Token::Value PeekAhead(); + // Returns the current token again. + Token::Value current_token() { return current_.token; } + // Returns the location information for the current token + // (the token last returned by Next()). + Location location() const { return current_.location; } + + // Similar functions for the upcoming token. + + // One token look-ahead (past the token returned by Next()). + Token::Value peek() const { return next_.token; } + + Location peek_location() const { return next_.location; } + + bool literal_contains_escapes() const { + return LiteralContainsEscapes(current_); + } + bool next_literal_contains_escapes() const { + return LiteralContainsEscapes(next_); + } + bool is_literal_contextual_keyword(Vector<const char> keyword) { + DCHECK_NOT_NULL(current_.literal_chars); + return current_.literal_chars->is_contextual_keyword(keyword); + } + bool is_next_contextual_keyword(Vector<const char> keyword) { + DCHECK_NOT_NULL(next_.literal_chars); + return next_.literal_chars->is_contextual_keyword(keyword); + } + + const AstRawString* CurrentSymbol(AstValueFactory* ast_value_factory); + const AstRawString* NextSymbol(AstValueFactory* ast_value_factory); + const AstRawString* CurrentRawSymbol(AstValueFactory* ast_value_factory); + + double DoubleValue(); + bool ContainsDot(); + bool LiteralMatches(const char* data, int length, bool allow_escapes = true) { + if (is_literal_one_byte() && + literal_length() == length && + (allow_escapes || !literal_contains_escapes())) { + const char* token = + reinterpret_cast<const char*>(literal_one_byte_string().start()); + return !strncmp(token, data, length); + } + return false; + } + inline bool UnescapedLiteralMatches(const char* data, int length) { + return LiteralMatches(data, length, false); + } + + void IsGetOrSet(bool* is_get, bool* is_set) { + if (is_literal_one_byte() && + literal_length() == 3 && + !literal_contains_escapes()) { + const char* token = + reinterpret_cast<const char*>(literal_one_byte_string().start()); + *is_get = strncmp(token, "get", 3) == 0; + *is_set = !*is_get && strncmp(token, "set", 3) == 0; + } + } + + int FindSymbol(DuplicateFinder* finder, int value); + + UnicodeCache* unicode_cache() { return unicode_cache_; } + + // Returns the location of the last seen octal literal. + Location octal_position() const { return octal_pos_; } + void clear_octal_position() { octal_pos_ = Location::invalid(); } + + // Returns the value of the last smi that was scanned. + int smi_value() const { return current_.smi_value_; } + + // Seek forward to the given position. This operation does not + // work in general, for instance when there are pushed back + // characters, but works for seeking forward until simple delimiter + // tokens, which is what it is used for. + void SeekForward(int pos); + + // Returns true if there was a line terminator before the peek'ed token, + // possibly inside a multi-line comment. + bool HasAnyLineTerminatorBeforeNext() const { + return has_line_terminator_before_next_ || + has_multiline_comment_before_next_; + } + + // Scans the input as a regular expression pattern, previous + // character(s) must be /(=). Returns true if a pattern is scanned. + bool ScanRegExpPattern(bool seen_equal); + // Scans the input as regular expression flags. Returns the flags on success. + Maybe<RegExp::Flags> ScanRegExpFlags(); + + // Scans the input as a template literal + Token::Value ScanTemplateStart(); + Token::Value ScanTemplateContinuation(); + + const LiteralBuffer* source_url() const { return &source_url_; } + const LiteralBuffer* source_mapping_url() const { + return &source_mapping_url_; + } + + bool IdentifierIsFutureStrictReserved(const AstRawString* string) const; + + private: + // The current and look-ahead token. + struct TokenDesc { + Token::Value token; + Location location; + LiteralBuffer* literal_chars; + LiteralBuffer* raw_literal_chars; + int smi_value_; + }; + + static const int kCharacterLookaheadBufferSize = 1; + + // Scans octal escape sequence. Also accepts "\0" decimal escape sequence. + template <bool capture_raw> + uc32 ScanOctalEscape(uc32 c, int length); + + // Call this after setting source_ to the input. + void Init() { + // Set c0_ (one character ahead) + STATIC_ASSERT(kCharacterLookaheadBufferSize == 1); + Advance(); + // Initialize current_ to not refer to a literal. + current_.literal_chars = NULL; + current_.raw_literal_chars = NULL; + next_next_.token = Token::UNINITIALIZED; + } + + // Support BookmarkScope functionality. + bool SetBookmark(); + void ResetToBookmark(); + bool BookmarkHasBeenSet(); + bool BookmarkHasBeenReset(); + void DropBookmark(); + static void CopyTokenDesc(TokenDesc* to, TokenDesc* from); + + // Literal buffer support + inline void StartLiteral() { + LiteralBuffer* free_buffer = + (current_.literal_chars == &literal_buffer0_) + ? &literal_buffer1_ + : (current_.literal_chars == &literal_buffer1_) ? &literal_buffer2_ + : &literal_buffer0_; + free_buffer->Reset(); + next_.literal_chars = free_buffer; + } + + inline void StartRawLiteral() { + LiteralBuffer* free_buffer = + (current_.raw_literal_chars == &raw_literal_buffer0_) + ? &raw_literal_buffer1_ + : (current_.raw_literal_chars == &raw_literal_buffer1_) + ? &raw_literal_buffer2_ + : &raw_literal_buffer0_; + free_buffer->Reset(); + next_.raw_literal_chars = free_buffer; + } + + INLINE(void AddLiteralChar(uc32 c)) { + DCHECK_NOT_NULL(next_.literal_chars); + next_.literal_chars->AddChar(c); + } + + INLINE(void AddRawLiteralChar(uc32 c)) { + DCHECK_NOT_NULL(next_.raw_literal_chars); + next_.raw_literal_chars->AddChar(c); + } + + INLINE(void ReduceRawLiteralLength(int delta)) { + DCHECK_NOT_NULL(next_.raw_literal_chars); + next_.raw_literal_chars->ReduceLength(delta); + } + + // Stops scanning of a literal and drop the collected characters, + // e.g., due to an encountered error. + inline void DropLiteral() { + next_.literal_chars = NULL; + next_.raw_literal_chars = NULL; + } + + inline void AddLiteralCharAdvance() { + AddLiteralChar(c0_); + Advance(); + } + + // Low-level scanning support. + template <bool capture_raw = false, bool check_surrogate = true> + void Advance() { + if (capture_raw) { + AddRawLiteralChar(c0_); + } + c0_ = source_->Advance(); + if (check_surrogate) HandleLeadSurrogate(); + } + + void HandleLeadSurrogate() { + if (unibrow::Utf16::IsLeadSurrogate(c0_)) { + uc32 c1 = source_->Advance(); + if (!unibrow::Utf16::IsTrailSurrogate(c1)) { + source_->PushBack(c1); + } else { + c0_ = unibrow::Utf16::CombineSurrogatePair(c0_, c1); + } + } + } + + void PushBack(uc32 ch) { + if (ch > static_cast<uc32>(unibrow::Utf16::kMaxNonSurrogateCharCode)) { + source_->PushBack(unibrow::Utf16::TrailSurrogate(c0_)); + source_->PushBack(unibrow::Utf16::LeadSurrogate(c0_)); + } else { + source_->PushBack(c0_); + } + c0_ = ch; + } + + inline Token::Value Select(Token::Value tok) { + Advance(); + return tok; + } + + inline Token::Value Select(uc32 next, Token::Value then, Token::Value else_) { + Advance(); + if (c0_ == next) { + Advance(); + return then; + } else { + return else_; + } + } + + // Returns the literal string, if any, for the current token (the + // token last returned by Next()). The string is 0-terminated. + // Literal strings are collected for identifiers, strings, numbers as well + // as for template literals. For template literals we also collect the raw + // form. + // These functions only give the correct result if the literal was scanned + // when a LiteralScope object is alive. + Vector<const uint8_t> literal_one_byte_string() { + DCHECK_NOT_NULL(current_.literal_chars); + return current_.literal_chars->one_byte_literal(); + } + Vector<const uint16_t> literal_two_byte_string() { + DCHECK_NOT_NULL(current_.literal_chars); + return current_.literal_chars->two_byte_literal(); + } + bool is_literal_one_byte() { + DCHECK_NOT_NULL(current_.literal_chars); + return current_.literal_chars->is_one_byte(); + } + int literal_length() const { + DCHECK_NOT_NULL(current_.literal_chars); + return current_.literal_chars->length(); + } + // Returns the literal string for the next token (the token that + // would be returned if Next() were called). + Vector<const uint8_t> next_literal_one_byte_string() { + DCHECK_NOT_NULL(next_.literal_chars); + return next_.literal_chars->one_byte_literal(); + } + Vector<const uint16_t> next_literal_two_byte_string() { + DCHECK_NOT_NULL(next_.literal_chars); + return next_.literal_chars->two_byte_literal(); + } + bool is_next_literal_one_byte() { + DCHECK_NOT_NULL(next_.literal_chars); + return next_.literal_chars->is_one_byte(); + } + Vector<const uint8_t> raw_literal_one_byte_string() { + DCHECK_NOT_NULL(current_.raw_literal_chars); + return current_.raw_literal_chars->one_byte_literal(); + } + Vector<const uint16_t> raw_literal_two_byte_string() { + DCHECK_NOT_NULL(current_.raw_literal_chars); + return current_.raw_literal_chars->two_byte_literal(); + } + bool is_raw_literal_one_byte() { + DCHECK_NOT_NULL(current_.raw_literal_chars); + return current_.raw_literal_chars->is_one_byte(); + } + + template <bool capture_raw> + uc32 ScanHexNumber(int expected_length); + // Scan a number of any length but not bigger than max_value. For example, the + // number can be 000000001, so it's very long in characters but its value is + // small. + template <bool capture_raw> + uc32 ScanUnlimitedLengthHexNumber(int max_value); + + // Scans a single JavaScript token. + void Scan(); + + bool SkipWhiteSpace(); + Token::Value SkipSingleLineComment(); + Token::Value SkipSourceURLComment(); + void TryToParseSourceURLComment(); + Token::Value SkipMultiLineComment(); + // Scans a possible HTML comment -- begins with '<!'. + Token::Value ScanHtmlComment(); + + void ScanDecimalDigits(); + Token::Value ScanNumber(bool seen_period); + Token::Value ScanIdentifierOrKeyword(); + Token::Value ScanIdentifierSuffix(LiteralScope* literal, bool escaped); + + Token::Value ScanString(); + + // Scans an escape-sequence which is part of a string and adds the + // decoded character to the current literal. Returns true if a pattern + // is scanned. + template <bool capture_raw, bool in_template_literal> + bool ScanEscape(); + + // Decodes a Unicode escape-sequence which is part of an identifier. + // If the escape sequence cannot be decoded the result is kBadChar. + uc32 ScanIdentifierUnicodeEscape(); + // Helper for the above functions. + template <bool capture_raw> + uc32 ScanUnicodeEscape(); + + Token::Value ScanTemplateSpan(); + + // Return the current source position. + int source_pos() { + return static_cast<int>(source_->pos()) - kCharacterLookaheadBufferSize; + } + + static bool LiteralContainsEscapes(const TokenDesc& token) { + Location location = token.location; + int source_length = (location.end_pos - location.beg_pos); + if (token.token == Token::STRING) { + // Subtract delimiters. + source_length -= 2; + } + return token.literal_chars->length() != source_length; + } + + UnicodeCache* unicode_cache_; + + // Buffers collecting literal strings, numbers, etc. + LiteralBuffer literal_buffer0_; + LiteralBuffer literal_buffer1_; + LiteralBuffer literal_buffer2_; + + // Values parsed from magic comments. + LiteralBuffer source_url_; + LiteralBuffer source_mapping_url_; + + // Buffer to store raw string values + LiteralBuffer raw_literal_buffer0_; + LiteralBuffer raw_literal_buffer1_; + LiteralBuffer raw_literal_buffer2_; + + TokenDesc current_; // desc for current token (as returned by Next()) + TokenDesc next_; // desc for next token (one token look-ahead) + TokenDesc next_next_; // desc for the token after next (after PeakAhead()) + + // Variables for Scanner::BookmarkScope and the *Bookmark implementation. + // These variables contain the scanner state when a bookmark is set. + // + // We will use bookmark_c0_ as a 'control' variable, where: + // - bookmark_c0_ >= 0: A bookmark has been set and this contains c0_. + // - bookmark_c0_ == -1: No bookmark has been set. + // - bookmark_c0_ == -2: The bookmark has been applied (ResetToBookmark). + // + // Which state is being bookmarked? The parser state is distributed over + // several variables, roughly like this: + // ... 1234 + 5678 ..... [character stream] + // [current_] [next_] c0_ | [scanner state] + // So when the scanner is logically at the beginning of an expression + // like "1234 + 4567", then: + // - current_ contains "1234" + // - next_ contains "+" + // - c0_ contains ' ' (the space between "+" and "5678", + // - the source_ character stream points to the beginning of "5678". + // To be able to restore this state, we will keep copies of current_, next_, + // and c0_; we'll ask the stream to bookmark itself, and we'll copy the + // contents of current_'s and next_'s literal buffers to bookmark_*_literal_. + static const uc32 kNoBookmark = -1; + static const uc32 kBookmarkWasApplied = -2; + uc32 bookmark_c0_; + TokenDesc bookmark_current_; + TokenDesc bookmark_next_; + LiteralBuffer bookmark_current_literal_; + LiteralBuffer bookmark_current_raw_literal_; + LiteralBuffer bookmark_next_literal_; + LiteralBuffer bookmark_next_raw_literal_; + + // Input stream. Must be initialized to an Utf16CharacterStream. + Utf16CharacterStream* source_; + + + // Start position of the octal literal last scanned. + Location octal_pos_; + + // One Unicode character look-ahead; c0_ < 0 at the end of the input. + uc32 c0_; + + // Whether there is a line terminator whitespace character after + // the current token, and before the next. Does not count newlines + // inside multiline comments. + bool has_line_terminator_before_next_; + // Whether there is a multi-line comment that contains a + // line-terminator after the current token, and before the next. + bool has_multiline_comment_before_next_; +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_PARSING_SCANNER_H_ diff --git a/deps/v8/src/parsing/token.cc b/deps/v8/src/parsing/token.cc new file mode 100644 index 0000000000..7edfefa821 --- /dev/null +++ b/deps/v8/src/parsing/token.cc @@ -0,0 +1,42 @@ +// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stdint.h> + +#include "src/parsing/token.h" + +namespace v8 { +namespace internal { + +#define T(name, string, precedence) #name, +const char* const Token::name_[NUM_TOKENS] = { + TOKEN_LIST(T, T) +}; +#undef T + + +#define T(name, string, precedence) string, +const char* const Token::string_[NUM_TOKENS] = { + TOKEN_LIST(T, T) +}; +#undef T + + +#define T(name, string, precedence) precedence, +const int8_t Token::precedence_[NUM_TOKENS] = { + TOKEN_LIST(T, T) +}; +#undef T + + +#define KT(a, b, c) 'T', +#define KK(a, b, c) 'K', +const char Token::token_type[] = { + TOKEN_LIST(KT, KK) +}; +#undef KT +#undef KK + +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/parsing/token.h b/deps/v8/src/parsing/token.h new file mode 100644 index 0000000000..fee1f7e85a --- /dev/null +++ b/deps/v8/src/parsing/token.h @@ -0,0 +1,324 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_PARSING_TOKEN_H_ +#define V8_PARSING_TOKEN_H_ + +#include "src/base/logging.h" +#include "src/globals.h" + +namespace v8 { +namespace internal { + +// TOKEN_LIST takes a list of 3 macros M, all of which satisfy the +// same signature M(name, string, precedence), where name is the +// symbolic token name, string is the corresponding syntactic symbol +// (or NULL, for literals), and precedence is the precedence (or 0). +// The parameters are invoked for token categories as follows: +// +// T: Non-keyword tokens +// K: Keyword tokens + +// IGNORE_TOKEN is a convenience macro that can be supplied as +// an argument (at any position) for a TOKEN_LIST call. It does +// nothing with tokens belonging to the respective category. + +#define IGNORE_TOKEN(name, string, precedence) + +#define TOKEN_LIST(T, K) \ + /* End of source indicator. */ \ + T(EOS, "EOS", 0) \ + \ + /* Punctuators (ECMA-262, section 7.7, page 15). */ \ + T(LPAREN, "(", 0) \ + T(RPAREN, ")", 0) \ + T(LBRACK, "[", 0) \ + T(RBRACK, "]", 0) \ + T(LBRACE, "{", 0) \ + T(RBRACE, "}", 0) \ + T(COLON, ":", 0) \ + T(SEMICOLON, ";", 0) \ + T(PERIOD, ".", 0) \ + T(ELLIPSIS, "...", 0) \ + T(CONDITIONAL, "?", 3) \ + T(INC, "++", 0) \ + T(DEC, "--", 0) \ + T(ARROW, "=>", 0) \ + \ + /* Assignment operators. */ \ + /* IsAssignmentOp() and Assignment::is_compound() relies on */ \ + /* this block of enum values being contiguous and sorted in the */ \ + /* same order! */ \ + T(INIT, "=init", 2) /* AST-use only. */ \ + T(ASSIGN, "=", 2) \ + T(ASSIGN_BIT_OR, "|=", 2) \ + T(ASSIGN_BIT_XOR, "^=", 2) \ + T(ASSIGN_BIT_AND, "&=", 2) \ + T(ASSIGN_SHL, "<<=", 2) \ + T(ASSIGN_SAR, ">>=", 2) \ + T(ASSIGN_SHR, ">>>=", 2) \ + T(ASSIGN_ADD, "+=", 2) \ + T(ASSIGN_SUB, "-=", 2) \ + T(ASSIGN_MUL, "*=", 2) \ + T(ASSIGN_DIV, "/=", 2) \ + T(ASSIGN_MOD, "%=", 2) \ + \ + /* Binary operators sorted by precedence. */ \ + /* IsBinaryOp() relies on this block of enum values */ \ + /* being contiguous and sorted in the same order! */ \ + T(COMMA, ",", 1) \ + T(OR, "||", 4) \ + T(AND, "&&", 5) \ + T(BIT_OR, "|", 6) \ + T(BIT_XOR, "^", 7) \ + T(BIT_AND, "&", 8) \ + T(SHL, "<<", 11) \ + T(SAR, ">>", 11) \ + T(SHR, ">>>", 11) \ + T(ROR, "rotate right", 11) /* only used by Crankshaft */ \ + T(ADD, "+", 12) \ + T(SUB, "-", 12) \ + T(MUL, "*", 13) \ + T(DIV, "/", 13) \ + T(MOD, "%", 13) \ + \ + /* Compare operators sorted by precedence. */ \ + /* IsCompareOp() relies on this block of enum values */ \ + /* being contiguous and sorted in the same order! */ \ + T(EQ, "==", 9) \ + T(NE, "!=", 9) \ + T(EQ_STRICT, "===", 9) \ + T(NE_STRICT, "!==", 9) \ + T(LT, "<", 10) \ + T(GT, ">", 10) \ + T(LTE, "<=", 10) \ + T(GTE, ">=", 10) \ + K(INSTANCEOF, "instanceof", 10) \ + K(IN, "in", 10) \ + \ + /* Unary operators. */ \ + /* IsUnaryOp() relies on this block of enum values */ \ + /* being contiguous and sorted in the same order! */ \ + T(NOT, "!", 0) \ + T(BIT_NOT, "~", 0) \ + K(DELETE, "delete", 0) \ + K(TYPEOF, "typeof", 0) \ + K(VOID, "void", 0) \ + \ + /* Keywords (ECMA-262, section 7.5.2, page 13). */ \ + K(BREAK, "break", 0) \ + K(CASE, "case", 0) \ + K(CATCH, "catch", 0) \ + K(CONTINUE, "continue", 0) \ + K(DEBUGGER, "debugger", 0) \ + K(DEFAULT, "default", 0) \ + /* DELETE */ \ + K(DO, "do", 0) \ + K(ELSE, "else", 0) \ + K(FINALLY, "finally", 0) \ + K(FOR, "for", 0) \ + K(FUNCTION, "function", 0) \ + K(IF, "if", 0) \ + /* IN */ \ + /* INSTANCEOF */ \ + K(NEW, "new", 0) \ + K(RETURN, "return", 0) \ + K(SWITCH, "switch", 0) \ + K(THIS, "this", 0) \ + K(THROW, "throw", 0) \ + K(TRY, "try", 0) \ + /* TYPEOF */ \ + K(VAR, "var", 0) \ + /* VOID */ \ + K(WHILE, "while", 0) \ + K(WITH, "with", 0) \ + \ + /* Literals (ECMA-262, section 7.8, page 16). */ \ + K(NULL_LITERAL, "null", 0) \ + K(TRUE_LITERAL, "true", 0) \ + K(FALSE_LITERAL, "false", 0) \ + T(NUMBER, NULL, 0) \ + T(SMI, NULL, 0) \ + T(STRING, NULL, 0) \ + \ + /* Identifiers (not keywords or future reserved words). */ \ + T(IDENTIFIER, NULL, 0) \ + \ + /* Future reserved words (ECMA-262, section 7.6.1.2). */ \ + T(FUTURE_RESERVED_WORD, NULL, 0) \ + T(FUTURE_STRICT_RESERVED_WORD, NULL, 0) \ + K(CLASS, "class", 0) \ + K(CONST, "const", 0) \ + K(EXPORT, "export", 0) \ + K(EXTENDS, "extends", 0) \ + K(IMPORT, "import", 0) \ + K(LET, "let", 0) \ + K(STATIC, "static", 0) \ + K(YIELD, "yield", 0) \ + K(SUPER, "super", 0) \ + \ + /* Illegal token - not able to scan. */ \ + T(ILLEGAL, "ILLEGAL", 0) \ + T(ESCAPED_KEYWORD, NULL, 0) \ + T(ESCAPED_STRICT_RESERVED_WORD, NULL, 0) \ + \ + /* Scanner-internal use only. */ \ + T(WHITESPACE, NULL, 0) \ + T(UNINITIALIZED, NULL, 0) \ + \ + /* ES6 Template Literals */ \ + T(TEMPLATE_SPAN, NULL, 0) \ + T(TEMPLATE_TAIL, NULL, 0) + + +class Token { + public: + // All token values. +#define T(name, string, precedence) name, + enum Value { + TOKEN_LIST(T, T) + NUM_TOKENS + }; +#undef T + + // Returns a string corresponding to the C++ token name + // (e.g. "LT" for the token LT). + static const char* Name(Value tok) { + DCHECK(tok < NUM_TOKENS); // tok is unsigned + return name_[tok]; + } + + // Predicates + static bool IsKeyword(Value tok) { + return token_type[tok] == 'K'; + } + + static bool IsIdentifier(Value tok, LanguageMode language_mode, + bool is_generator) { + switch (tok) { + case IDENTIFIER: + return true; + case ESCAPED_STRICT_RESERVED_WORD: + case FUTURE_STRICT_RESERVED_WORD: + case LET: + case STATIC: + return is_sloppy(language_mode); + case YIELD: + return !is_generator && is_sloppy(language_mode); + default: + return false; + } + UNREACHABLE(); + return false; + } + + static bool IsAssignmentOp(Value tok) { + return INIT <= tok && tok <= ASSIGN_MOD; + } + + static bool IsBinaryOp(Value op) { + return COMMA <= op && op <= MOD; + } + + static bool IsTruncatingBinaryOp(Value op) { + return BIT_OR <= op && op <= ROR; + } + + static bool IsCompareOp(Value op) { + return EQ <= op && op <= IN; + } + + static bool IsOrderedRelationalCompareOp(Value op) { + return op == LT || op == LTE || op == GT || op == GTE; + } + + static bool IsEqualityOp(Value op) { + return op == EQ || op == EQ_STRICT; + } + + static bool IsInequalityOp(Value op) { + return op == NE || op == NE_STRICT; + } + + static bool IsArithmeticCompareOp(Value op) { + return IsOrderedRelationalCompareOp(op) || + IsEqualityOp(op) || IsInequalityOp(op); + } + + static Value NegateCompareOp(Value op) { + DCHECK(IsArithmeticCompareOp(op)); + switch (op) { + case EQ: return NE; + case NE: return EQ; + case EQ_STRICT: return NE_STRICT; + case NE_STRICT: return EQ_STRICT; + case LT: return GTE; + case GT: return LTE; + case LTE: return GT; + case GTE: return LT; + default: + UNREACHABLE(); + return op; + } + } + + static Value ReverseCompareOp(Value op) { + DCHECK(IsArithmeticCompareOp(op)); + switch (op) { + case EQ: return EQ; + case NE: return NE; + case EQ_STRICT: return EQ_STRICT; + case NE_STRICT: return NE_STRICT; + case LT: return GT; + case GT: return LT; + case LTE: return GTE; + case GTE: return LTE; + default: + UNREACHABLE(); + return op; + } + } + + static bool IsBitOp(Value op) { + return (BIT_OR <= op && op <= SHR) || op == BIT_NOT; + } + + static bool IsUnaryOp(Value op) { + return (NOT <= op && op <= VOID) || op == ADD || op == SUB; + } + + static bool IsCountOp(Value op) { + return op == INC || op == DEC; + } + + static bool IsShiftOp(Value op) { + return (SHL <= op) && (op <= SHR); + } + + // Returns a string corresponding to the JS token string + // (.e., "<" for the token LT) or NULL if the token doesn't + // have a (unique) string (e.g. an IDENTIFIER). + static const char* String(Value tok) { + DCHECK(tok < NUM_TOKENS); // tok is unsigned. + return string_[tok]; + } + + // Returns the precedence > 0 for binary and compare + // operators; returns 0 otherwise. + static int Precedence(Value tok) { + DCHECK(tok < NUM_TOKENS); // tok is unsigned. + return precedence_[tok]; + } + + private: + static const char* const name_[NUM_TOKENS]; + static const char* const string_[NUM_TOKENS]; + static const int8_t precedence_[NUM_TOKENS]; + static const char token_type[NUM_TOKENS]; +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_PARSING_TOKEN_H_ |