Merge V8 5.5.372.32
Test: Manual, built and ran D8
Change-Id: I831a5491f74342c2675bb6fe1e24a2258e493758
diff --git a/src/parsing/duplicate-finder.cc b/src/parsing/duplicate-finder.cc
new file mode 100644
index 0000000..6b57153
--- /dev/null
+++ b/src/parsing/duplicate-finder.cc
@@ -0,0 +1,145 @@
+// 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/duplicate-finder.h"
+
+#include "src/conversions.h"
+#include "src/unicode-cache.h"
+
+namespace v8 {
+namespace internal {
+
+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);
+ base::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/src/parsing/duplicate-finder.h b/src/parsing/duplicate-finder.h
new file mode 100644
index 0000000..a3858e7
--- /dev/null
+++ b/src/parsing/duplicate-finder.h
@@ -0,0 +1,64 @@
+// 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_DUPLICATE_FINDER_H_
+#define V8_PARSING_DUPLICATE_FINDER_H_
+
+#include "src/base/hashmap.h"
+#include "src/collector.h"
+
+namespace v8 {
+namespace internal {
+
+class UnicodeCache;
+
+// 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_;
+ base::CustomMatcherHashMap map_;
+ // Buffer used for string->number->canonical string conversions.
+ char number_buffer_[kBufferSize];
+};
+
+} // namespace internal
+} // namespace v8
+
+#endif // V8_PARSING_DUPLICATE_FINDER_H_
diff --git a/src/parsing/expression-classifier.h b/src/parsing/expression-classifier.h
index 9190e18..6a1fbac 100644
--- a/src/parsing/expression-classifier.h
+++ b/src/parsing/expression-classifier.h
@@ -7,11 +7,12 @@
#include "src/messages.h"
#include "src/parsing/scanner.h"
-#include "src/parsing/token.h"
namespace v8 {
namespace internal {
+class DuplicateFinder;
+
#define ERROR_CODES(T) \
T(ExpressionProduction, 0) \
T(FormalParameterInitializerProduction, 1) \
@@ -21,11 +22,32 @@
T(StrictModeFormalParametersProduction, 5) \
T(ArrowFormalParametersProduction, 6) \
T(LetPatternProduction, 7) \
- T(ObjectLiteralProduction, 8) \
- T(TailCallExpressionProduction, 9) \
- T(AsyncArrowFormalParametersProduction, 10)
+ T(TailCallExpressionProduction, 8) \
+ T(AsyncArrowFormalParametersProduction, 9)
-template <typename Traits>
+// Expression classifiers serve two purposes:
+//
+// 1) They keep track of error messages that are pending (and other
+// related information), waiting for the parser to decide whether
+// the parsed expression is a pattern or not.
+// 2) They keep track of expressions that may need to be rewritten, if
+// the parser decides that they are not patterns. (A different
+// mechanism implements the rewriting of patterns.)
+//
+// Expression classifiers are used by the parser in a stack fashion.
+// Each new classifier is pushed on top of the stack. This happens
+// automatically by the class's constructor. While on top of the
+// stack, the classifier records pending error messages and tracks the
+// pending non-patterns of the expression that is being parsed.
+//
+// At the end of its life, a classifier is either "accumulated" to the
+// one that is below it on the stack, or is "discarded". The former
+// is achieved by calling the method Accumulate. The latter is
+// achieved automatically by the destructor, but it can happen earlier
+// by calling the method Discard. Both actions result in removing the
+// classifier from the parser's stack.
+
+template <typename Types>
class ExpressionClassifier {
public:
enum ErrorKind : unsigned {
@@ -55,51 +77,41 @@
const char* arg;
};
+ // clang-format off
enum TargetProduction : unsigned {
#define DEFINE_PRODUCTION(NAME, CODE) NAME = 1 << CODE,
ERROR_CODES(DEFINE_PRODUCTION)
#undef DEFINE_PRODUCTION
- ExpressionProductions =
- (ExpressionProduction | FormalParameterInitializerProduction |
- TailCallExpressionProduction),
- PatternProductions = (BindingPatternProduction |
- AssignmentPatternProduction | LetPatternProduction),
- FormalParametersProductions = (DistinctFormalParametersProduction |
- StrictModeFormalParametersProduction),
- AllProductions =
- (ExpressionProductions | PatternProductions |
- FormalParametersProductions | ArrowFormalParametersProduction |
- ObjectLiteralProduction | AsyncArrowFormalParametersProduction)
+#define DEFINE_ALL_PRODUCTIONS(NAME, CODE) NAME |
+ AllProductions = ERROR_CODES(DEFINE_ALL_PRODUCTIONS) /* | */ 0
+#undef DEFINE_ALL_PRODUCTIONS
};
+ // clang-format on
enum FunctionProperties : unsigned {
NonSimpleParameter = 1 << 0
};
- explicit ExpressionClassifier(const Traits* t)
- : zone_(t->zone()),
- non_patterns_to_rewrite_(t->GetNonPatternList()),
- reported_errors_(t->GetReportedErrorList()),
- duplicate_finder_(nullptr),
- invalid_productions_(0),
- function_properties_(0) {
- reported_errors_begin_ = reported_errors_end_ = reported_errors_->length();
- non_pattern_begin_ = non_patterns_to_rewrite_->length();
- }
-
- ExpressionClassifier(const Traits* t, DuplicateFinder* duplicate_finder)
- : zone_(t->zone()),
- non_patterns_to_rewrite_(t->GetNonPatternList()),
- reported_errors_(t->GetReportedErrorList()),
+ explicit ExpressionClassifier(typename Types::Base* base,
+ DuplicateFinder* duplicate_finder = nullptr)
+ : base_(base),
+ previous_(base->classifier_),
+ zone_(base->impl()->zone()),
+ non_patterns_to_rewrite_(base->impl()->GetNonPatternList()),
+ reported_errors_(base->impl()->GetReportedErrorList()),
duplicate_finder_(duplicate_finder),
invalid_productions_(0),
function_properties_(0) {
+ base->classifier_ = this;
reported_errors_begin_ = reported_errors_end_ = reported_errors_->length();
non_pattern_begin_ = non_patterns_to_rewrite_->length();
}
- ~ExpressionClassifier() { Discard(); }
+ V8_INLINE ~ExpressionClassifier() {
+ Discard();
+ if (base_->classifier_ == this) base_->classifier_ = previous_;
+ }
V8_INLINE bool is_valid(unsigned productions) const {
return (invalid_productions_ & productions) == 0;
@@ -179,14 +191,6 @@
return reported_error(kLetPatternProduction);
}
- V8_INLINE bool has_object_literal_error() const {
- return !is_valid(ObjectLiteralProduction);
- }
-
- V8_INLINE const Error& object_literal_error() const {
- return reported_error(kObjectLiteralProduction);
- }
-
V8_INLINE bool has_tail_call_expression() const {
return !is_valid(TailCallExpressionProduction);
}
@@ -295,14 +299,6 @@
Add(Error(loc, message, kLetPatternProduction, arg));
}
- void RecordObjectLiteralError(const Scanner::Location& loc,
- MessageTemplate::Template message,
- const char* arg = nullptr) {
- if (has_object_literal_error()) return;
- invalid_productions_ |= ObjectLiteralProduction;
- Add(Error(loc, message, kObjectLiteralProduction, arg));
- }
-
void RecordTailCallExpressionError(const Scanner::Location& loc,
MessageTemplate::Template message,
const char* arg = nullptr) {
@@ -316,7 +312,14 @@
DCHECK_EQ(inner->reported_errors_, reported_errors_);
DCHECK_EQ(inner->reported_errors_begin_, reported_errors_end_);
DCHECK_EQ(inner->reported_errors_end_, reported_errors_->length());
- if (merge_non_patterns) MergeNonPatterns(inner);
+ DCHECK_EQ(inner->non_patterns_to_rewrite_, non_patterns_to_rewrite_);
+ DCHECK_LE(non_pattern_begin_, inner->non_pattern_begin_);
+ DCHECK_LE(inner->non_pattern_begin_, non_patterns_to_rewrite_->length());
+ // Merge non-patterns from the inner classifier, or discard them.
+ if (merge_non_patterns)
+ inner->non_pattern_begin_ = non_patterns_to_rewrite_->length();
+ else
+ non_patterns_to_rewrite_->Rewind(inner->non_pattern_begin_);
// Propagate errors from inner, but don't overwrite already recorded
// errors.
unsigned non_arrow_inner_invalid_productions =
@@ -393,10 +396,7 @@
non_patterns_to_rewrite_->Rewind(non_pattern_begin_);
}
- V8_INLINE void MergeNonPatterns(ExpressionClassifier* inner) {
- DCHECK_LE(non_pattern_begin_, inner->non_pattern_begin_);
- inner->non_pattern_begin_ = inner->non_patterns_to_rewrite_->length();
- }
+ ExpressionClassifier* previous() const { return previous_; }
private:
V8_INLINE const Error& reported_error(ErrorKind kind) const {
@@ -410,6 +410,9 @@
// We should only be looking for an error when we know that one has
// been reported. But we're not... So this is to make sure we have
// the same behaviour.
+ UNREACHABLE();
+
+ // Make MSVC happy by returning an error from this inaccessible path.
static Error none;
return none;
}
@@ -434,8 +437,10 @@
reported_errors_end_++;
}
+ typename Types::Base* base_;
+ ExpressionClassifier* previous_;
Zone* zone_;
- ZoneList<typename Traits::Type::Expression>* non_patterns_to_rewrite_;
+ ZoneList<typename Types::Expression>* non_patterns_to_rewrite_;
ZoneList<Error>* reported_errors_;
DuplicateFinder* duplicate_finder_;
// The uint16_t for non_pattern_begin_ will not be enough in the case,
@@ -456,6 +461,8 @@
// stack overflow while parsing.
uint16_t reported_errors_begin_;
uint16_t reported_errors_end_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExpressionClassifier);
};
diff --git a/src/parsing/func-name-inferrer.cc b/src/parsing/func-name-inferrer.cc
index 0821be0..a86e1c2 100644
--- a/src/parsing/func-name-inferrer.cc
+++ b/src/parsing/func-name-inferrer.cc
@@ -45,9 +45,11 @@
}
void FuncNameInferrer::RemoveAsyncKeywordFromEnd() {
- DCHECK(names_stack_.length() > 0);
- DCHECK(names_stack_.last().name->IsOneByteEqualTo("async"));
- names_stack_.RemoveLast();
+ if (IsOpen()) {
+ DCHECK(names_stack_.length() > 0);
+ DCHECK(names_stack_.last().name->IsOneByteEqualTo("async"));
+ names_stack_.RemoveLast();
+ }
}
const AstString* FuncNameInferrer::MakeNameFromStack() {
diff --git a/src/parsing/func-name-inferrer.h b/src/parsing/func-name-inferrer.h
index cffd8a8..cc9204b 100644
--- a/src/parsing/func-name-inferrer.h
+++ b/src/parsing/func-name-inferrer.h
@@ -6,7 +6,7 @@
#define V8_PARSING_FUNC_NAME_INFERRER_H_
#include "src/handles.h"
-#include "src/zone.h"
+#include "src/zone/zone.h"
namespace v8 {
namespace internal {
diff --git a/src/parsing/parameter-initializer-rewriter.cc b/src/parsing/parameter-initializer-rewriter.cc
index b12a80f..73224a2 100644
--- a/src/parsing/parameter-initializer-rewriter.cc
+++ b/src/parsing/parameter-initializer-rewriter.cc
@@ -47,9 +47,9 @@
}
// No need to visit the constructor since it will have the class
// scope on its scope chain.
- ZoneList<ObjectLiteralProperty*>* props = class_literal->properties();
+ ZoneList<ClassLiteralProperty*>* props = class_literal->properties();
for (int i = 0; i < props->length(); ++i) {
- ObjectLiteralProperty* prop = props->at(i);
+ ClassLiteralProperty* prop = props->at(i);
if (!prop->key()->IsLiteral()) {
Visit(prop->key());
}
diff --git a/src/parsing/parameter-initializer-rewriter.h b/src/parsing/parameter-initializer-rewriter.h
index a0ff7d2..5e409b4 100644
--- a/src/parsing/parameter-initializer-rewriter.h
+++ b/src/parsing/parameter-initializer-rewriter.h
@@ -5,7 +5,7 @@
#ifndef V8_PARSING_PARAMETER_EXPRESSION_REWRITER_H_
#define V8_PARSING_PARAMETER_EXPRESSION_REWRITER_H_
-#include "src/types.h"
+#include "src/ast/ast-types.h"
namespace v8 {
namespace internal {
diff --git a/src/parsing/parse-info.cc b/src/parsing/parse-info.cc
index dfec061..5b9b5e4 100644
--- a/src/parsing/parse-info.cc
+++ b/src/parsing/parse-info.cc
@@ -33,7 +33,9 @@
ParseInfo::ParseInfo(Zone* zone, Handle<JSFunction> function)
: ParseInfo(zone, Handle<SharedFunctionInfo>(function->shared())) {
- set_context(Handle<Context>(function->context()));
+ if (!function->context()->IsNativeContext()) {
+ set_outer_scope_info(handle(function->context()->scope_info()));
+ }
}
ParseInfo::ParseInfo(Zone* zone, Handle<SharedFunctionInfo> shared)
@@ -86,17 +88,13 @@
return (compiler_hints_ & (1 << SharedFunctionInfo::kIsDeclaration)) != 0;
}
-bool ParseInfo::is_arrow() const {
- return (compiler_hints_ & (1 << SharedFunctionInfo::kIsArrow)) != 0;
+bool ParseInfo::requires_class_field_init() const {
+ return (compiler_hints_ &
+ (1 << SharedFunctionInfo::kRequiresClassFieldInit)) != 0;
}
-
-bool ParseInfo::is_async() const {
- return (compiler_hints_ & (1 << SharedFunctionInfo::kIsAsyncFunction)) != 0;
-}
-
-bool ParseInfo::is_default_constructor() const {
- return (compiler_hints_ & (1 << SharedFunctionInfo::kIsDefaultConstructor)) !=
- 0;
+bool ParseInfo::is_class_field_initializer() const {
+ return (compiler_hints_ &
+ (1 << SharedFunctionInfo::kIsClassFieldInitializer)) != 0;
}
FunctionKind ParseInfo::function_kind() const {
diff --git a/src/parsing/parse-info.h b/src/parsing/parse-info.h
index 6176135..4aedae4 100644
--- a/src/parsing/parse-info.h
+++ b/src/parsing/parse-info.h
@@ -148,9 +148,8 @@
// Getters for individual compiler hints.
bool is_declaration() const;
- bool is_arrow() const;
- bool is_async() const;
- bool is_default_constructor() const;
+ bool requires_class_field_init() const;
+ bool is_class_field_initializer() const;
FunctionKind function_kind() const;
//--------------------------------------------------------------------------
@@ -159,11 +158,15 @@
Isolate* isolate() const { return isolate_; }
Handle<SharedFunctionInfo> shared_info() const { return shared_; }
Handle<Script> script() const { return script_; }
- Handle<Context> context() const { return context_; }
+ MaybeHandle<ScopeInfo> maybe_outer_scope_info() const {
+ return maybe_outer_scope_info_;
+ }
void clear_script() { script_ = Handle<Script>::null(); }
void set_isolate(Isolate* isolate) { isolate_ = isolate; }
void set_shared_info(Handle<SharedFunctionInfo> shared) { shared_ = shared; }
- void set_context(Handle<Context> context) { context_ = context; }
+ void set_outer_scope_info(Handle<ScopeInfo> outer_scope_info) {
+ maybe_outer_scope_info_ = outer_scope_info;
+ }
void set_script(Handle<Script> script) { script_ = script; }
//--------------------------------------------------------------------------
@@ -178,7 +181,10 @@
void ReopenHandlesInNewHandleScope() {
shared_ = Handle<SharedFunctionInfo>(*shared_);
script_ = Handle<Script>(*script_);
- context_ = Handle<Context>(*context_);
+ Handle<ScopeInfo> outer_scope_info;
+ if (maybe_outer_scope_info_.ToHandle(&outer_scope_info)) {
+ maybe_outer_scope_info_ = Handle<ScopeInfo>(*outer_scope_info);
+ }
}
#ifdef DEBUG
@@ -224,7 +230,7 @@
Isolate* isolate_;
Handle<SharedFunctionInfo> shared_;
Handle<Script> script_;
- Handle<Context> context_;
+ MaybeHandle<ScopeInfo> maybe_outer_scope_info_;
//----------- Inputs+Outputs of parsing and scope analysis -----------------
ScriptData** cached_data_; // used if available, populated if requested.
diff --git a/src/parsing/parser-base.h b/src/parsing/parser-base.h
index b8703d0..1ebbee4 100644
--- a/src/parsing/parser-base.h
+++ b/src/parsing/parser-base.h
@@ -5,6 +5,7 @@
#ifndef V8_PARSING_PARSER_BASE_H
#define V8_PARSING_PARSER_BASE_H
+#include "src/ast/ast.h"
#include "src/ast/scopes.h"
#include "src/bailout-reason.h"
#include "src/base/hashmap.h"
@@ -56,59 +57,6 @@
return static_cast<T>(bitfield) & static_cast<T>(mask);
}
-enum class MethodKind {
- kNormal = 0,
- kStatic = 1 << 0,
- kGenerator = 1 << 1,
- kStaticGenerator = kStatic | kGenerator,
- kAsync = 1 << 2,
- kStaticAsync = kStatic | kAsync,
-
- /* Any non-ordinary method kinds */
- kSpecialMask = kGenerator | kAsync
-};
-
-inline bool IsValidMethodKind(MethodKind kind) {
- return kind == MethodKind::kNormal || kind == MethodKind::kStatic ||
- kind == MethodKind::kGenerator ||
- kind == MethodKind::kStaticGenerator || kind == MethodKind::kAsync ||
- kind == MethodKind::kStaticAsync;
-}
-
-static inline MethodKind operator|(MethodKind lhs, MethodKind rhs) {
- typedef unsigned char T;
- return static_cast<MethodKind>(static_cast<T>(lhs) | static_cast<T>(rhs));
-}
-
-static inline MethodKind& operator|=(MethodKind& lhs, const MethodKind& rhs) {
- lhs = lhs | rhs;
- DCHECK(IsValidMethodKind(lhs));
- return lhs;
-}
-
-static inline bool operator&(MethodKind bitfield, MethodKind mask) {
- typedef unsigned char T;
- return static_cast<T>(bitfield) & static_cast<T>(mask);
-}
-
-inline bool IsNormalMethod(MethodKind kind) {
- return kind == MethodKind::kNormal;
-}
-
-inline bool IsSpecialMethod(MethodKind kind) {
- return kind & MethodKind::kSpecialMask;
-}
-
-inline bool IsStaticMethod(MethodKind kind) {
- return kind & MethodKind::kStatic;
-}
-
-inline bool IsGeneratorMethod(MethodKind kind) {
- return kind & MethodKind::kGenerator;
-}
-
-inline bool IsAsyncMethod(MethodKind kind) { return kind & MethodKind::kAsync; }
-
struct FormalParametersBase {
explicit FormalParametersBase(DeclarationScope* scope) : scope(scope) {}
DeclarationScope* scope;
@@ -126,8 +74,8 @@
// 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_CUSTOM(x) ok); \
- if (!*ok) return this->x(); \
+#define CHECK_OK_CUSTOM(x, ...) ok); \
+ if (!*ok) return impl()->x(__VA_ARGS__); \
((void)0
#define DUMMY ) // to make indentation work
#undef DUMMY
@@ -140,93 +88,86 @@
// following the Curiously Recurring Template Pattern (CRTP).
// The structure of the parser objects is roughly the following:
//
-// // Common denominator, needed to avoid cyclic dependency.
-// // Instances of this template will end up with very minimal
-// // definitions, ideally containing just typedefs.
+// // A structure template containing type definitions, needed to
+// // avoid a cyclic dependency.
// template <typename Impl>
-// class ParserBaseTraits;
-
+// struct ParserTypes;
+//
// // The parser base object, which should just implement pure
// // parser behavior. The Impl parameter is the actual derived
// // class (according to CRTP), which implements impure parser
// // behavior.
// template <typename Impl>
-// class ParserBase : public ParserBaseTraits<Impl> { ... };
+// class ParserBase { ... };
//
// // And then, for each parser variant (e.g., parser, preparser, etc):
// class Parser;
//
// template <>
-// class ParserBaseTraits<Parser> { ... };
+// class ParserTypes<Parser> { ... };
//
// class Parser : public ParserBase<Parser> { ... };
//
-// TODO(nikolaos): Currently the traits objects contain many things
-// that will be moved to the implementation objects or to the parser
-// base. The following comments will have to change, when this happens.
+// The parser base object implements pure parsing, according to the
+// language grammar. Different parser implementations may exhibit
+// different parser-driven behavior that is not considered as pure
+// parsing, e.g., early error detection and reporting, AST generation, etc.
-// The traits class template encapsulates the differences between
-// parser/pre-parser implementations. In particular:
-
-// - 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:
+// The ParserTypes structure encapsulates the differences in the
+// types used in parsing methods. E.g., Parser methods use Expression*
+// and PreParser methods use PreParserExpression. For any given parser
+// implementation class Impl, it is expected to contain the following typedefs:
+//
// template <>
-// class ParserBaseTraits<Impl> {
-// // In particular...
-// struct Type {
-// typedef GeneratorVariable;
-// typedef AstProperties;
-// typedef ExpressionClassifier;
-// // Return types for traversing functions.
-// typedef Identifier;
-// typedef Expression;
-// typedef YieldExpression;
-// typedef FunctionLiteral;
-// typedef ClassLiteral;
-// typedef Literal;
-// typedef ObjectLiteralProperty;
-// typedef ExpressionList;
-// typedef PropertyList;
-// typedef FormalParameter;
-// typedef FormalParameters;
-// typedef StatementList;
-// // For constructing objects returned by the traversing functions.
-// typedef Factory;
-// };
-// // ...
+// struct ParserTypes<Impl> {
+// // Synonyms for ParserBase<Impl> and Impl, respectively.
+// typedef Base;
+// typedef Impl;
+// // TODO(nikolaos): this one will probably go away, as it is
+// // not related to pure parsing.
+// typedef Variable;
+// // Return types for traversing functions.
+// typedef Identifier;
+// typedef Expression;
+// typedef FunctionLiteral;
+// typedef ObjectLiteralProperty;
+// typedef ClassLiteralProperty;
+// typedef ExpressionList;
+// typedef ObjectPropertyList;
+// typedef ClassPropertyList;
+// typedef FormalParameters;
+// typedef Statement;
+// typedef StatementList;
+// typedef Block;
+// typedef BreakableStatement;
+// typedef IterationStatement;
+// // For constructing objects returned by the traversing functions.
+// typedef Factory;
+// // For other implementation-specific tasks.
+// typedef Target;
+// typedef TargetScope;
// };
template <typename Impl>
-class ParserBaseTraits;
+struct ParserTypes;
template <typename Impl>
-class ParserBase : public ParserBaseTraits<Impl> {
+class ParserBase {
public:
- // Shorten type names defined by Traits.
- typedef ParserBaseTraits<Impl> 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;
- typedef typename Traits::Type::ExpressionClassifier ExpressionClassifier;
+ // Shorten type names defined by ParserTypes<Impl>.
+ typedef ParserTypes<Impl> Types;
+ typedef typename Types::Identifier IdentifierT;
+ typedef typename Types::Expression ExpressionT;
+ typedef typename Types::FunctionLiteral FunctionLiteralT;
+ typedef typename Types::ObjectLiteralProperty ObjectLiteralPropertyT;
+ typedef typename Types::ClassLiteralProperty ClassLiteralPropertyT;
+ typedef typename Types::ExpressionList ExpressionListT;
+ typedef typename Types::FormalParameters FormalParametersT;
+ typedef typename Types::Statement StatementT;
+ typedef typename Types::StatementList StatementListT;
+ typedef typename Types::Block BlockT;
+ typedef typename v8::internal::ExpressionClassifier<Types>
+ ExpressionClassifier;
// All implementation-specific methods must be called through this.
Impl* impl() { return static_cast<Impl*>(this); }
@@ -246,6 +187,7 @@
parsing_module_(false),
stack_limit_(stack_limit),
zone_(zone),
+ classifier_(nullptr),
scanner_(scanner),
stack_overflow_(false),
allow_lazy_(false),
@@ -257,7 +199,8 @@
allow_harmony_function_sent_(false),
allow_harmony_async_await_(false),
allow_harmony_restrictive_generators_(false),
- allow_harmony_trailing_commas_(false) {}
+ allow_harmony_trailing_commas_(false),
+ allow_harmony_class_fields_(false) {}
#define ALLOW_ACCESSORS(name) \
bool allow_##name() const { return allow_##name##_; } \
@@ -273,6 +216,7 @@
ALLOW_ACCESSORS(harmony_async_await);
ALLOW_ACCESSORS(harmony_restrictive_generators);
ALLOW_ACCESSORS(harmony_trailing_commas);
+ ALLOW_ACCESSORS(harmony_class_fields);
#undef ALLOW_ACCESSORS
@@ -280,7 +224,12 @@
void set_stack_limit(uintptr_t stack_limit) { stack_limit_ = stack_limit; }
+ Zone* zone() const { return zone_; }
+
protected:
+ friend class v8::internal::ExpressionClassifier<ParserTypes<Impl>>;
+
+ // clang-format off
enum AllowRestrictedIdentifiers {
kAllowRestrictedIdentifiers,
kDontAllowRestrictedIdentifiers
@@ -291,14 +240,26 @@
PARSE_EAGERLY
};
+ enum LazyParsingResult {
+ kLazyParsingComplete,
+ kLazyParsingAborted
+ };
+
enum VariableDeclarationContext {
kStatementListItem,
kStatement,
kForStatement
};
+ enum class FunctionBodyType {
+ kNormal,
+ kSingleExpression
+ };
+ // clang-format on
+
class Checkpoint;
- class ObjectLiteralCheckerBase;
+ class ClassLiteralChecker;
+ class ObjectLiteralChecker;
// ---------------------------------------------------------------------------
// ScopeState and its subclasses implement the parser's scope stack.
@@ -333,8 +294,8 @@
// allocation.
// TODO(verwaest): Move to LazyBlockState class that only allocates the
// scope when needed.
- explicit BlockState(ScopeState** scope_stack)
- : ScopeState(scope_stack, NewScope(*scope_stack)) {}
+ explicit BlockState(Zone* zone, ScopeState** scope_stack)
+ : ScopeState(scope_stack, NewScope(zone, *scope_stack)) {}
void SetNonlinear() { this->scope()->SetNonlinear(); }
void set_start_position(int pos) { this->scope()->set_start_position(pos); }
@@ -348,9 +309,8 @@
}
private:
- Scope* NewScope(ScopeState* outer_state) {
+ Scope* NewScope(Zone* zone, ScopeState* outer_state) {
Scope* parent = outer_state->scope();
- Zone* zone = outer_state->zone();
return new (zone) Scope(zone, parent, BLOCK_SCOPE);
}
};
@@ -384,14 +344,6 @@
expressions_.Add(expr, zone_);
}
- void AddExplicitTailCall(ExpressionT expr, const Scanner::Location& loc) {
- if (!has_explicit_tail_calls()) {
- loc_ = loc;
- has_explicit_tail_calls_ = true;
- }
- expressions_.Add(expr, zone_);
- }
-
void Append(const TailCallExpressionList& other) {
if (!has_explicit_tail_calls()) {
loc_ = other.loc_;
@@ -425,9 +377,13 @@
class FunctionState final : public ScopeState {
public:
FunctionState(FunctionState** function_state_stack,
- ScopeState** scope_stack, Scope* scope, FunctionKind kind);
+ ScopeState** scope_stack, DeclarationScope* scope);
~FunctionState();
+ DeclarationScope* scope() const {
+ return ScopeState::scope()->AsDeclarationScope();
+ }
+
int NextMaterializedLiteralIndex() {
return next_materialized_literal_index_++;
}
@@ -442,24 +398,27 @@
void AddProperty() { expected_property_count_++; }
int expected_property_count() { return expected_property_count_; }
- bool is_generator() const { return IsGeneratorFunction(kind_); }
- bool is_async_function() const { return IsAsyncFunction(kind_); }
- bool is_resumable() const { return is_generator() || is_async_function(); }
-
- FunctionKind kind() const { return kind_; }
+ FunctionKind kind() const { return scope()->function_kind(); }
FunctionState* outer() const { return outer_function_state_; }
- void set_generator_object_variable(
- typename Traits::Type::GeneratorVariable* variable) {
+ void set_generator_object_variable(typename Types::Variable* variable) {
DCHECK(variable != NULL);
- DCHECK(is_resumable());
+ DCHECK(IsResumableFunction(kind()));
generator_object_variable_ = variable;
}
- typename Traits::Type::GeneratorVariable* generator_object_variable()
- const {
+ typename Types::Variable* generator_object_variable() const {
return generator_object_variable_;
}
+ void set_promise_variable(typename Types::Variable* variable) {
+ DCHECK(variable != NULL);
+ DCHECK(IsAsyncFunction(kind()));
+ promise_variable_ = variable;
+ }
+ typename Types::Variable* promise_variable() const {
+ return promise_variable_;
+ }
+
const ZoneList<DestructuringAssignment>&
destructuring_assignments_to_rewrite() const {
return destructuring_assignments_to_rewrite_;
@@ -474,14 +433,6 @@
tail_call_expressions_.AddImplicitTailCall(expression);
}
}
- void AddExplicitTailCallExpression(ExpressionT expression,
- const Scanner::Location& loc) {
- DCHECK(expression->IsCall());
- if (return_expr_context() ==
- ReturnExprContext::kInsideValidReturnStatement) {
- tail_call_expressions_.AddExplicitTailCall(expression, loc);
- }
- }
ZoneList<typename ExpressionClassifier::Error>* GetReportedErrorList() {
return &reported_errors_;
@@ -530,11 +481,13 @@
// Properties count estimation.
int expected_property_count_;
- 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_;
+ // For async functions, this variable holds a temporary for the Promise
+ // being created as output of the async function.
+ Variable* promise_variable_;
FunctionState** function_state_stack_;
FunctionState* outer_function_state_;
@@ -644,8 +597,97 @@
Mode old_mode_;
};
+ struct DeclarationDescriptor {
+ enum Kind { NORMAL, PARAMETER };
+ Scope* scope;
+ Scope* hoist_scope;
+ VariableMode mode;
+ int declaration_pos;
+ int initialization_pos;
+ Kind declaration_kind;
+ };
+
+ struct DeclarationParsingResult {
+ struct Declaration {
+ Declaration(ExpressionT pattern, int initializer_position,
+ ExpressionT initializer)
+ : pattern(pattern),
+ initializer_position(initializer_position),
+ initializer(initializer) {}
+
+ ExpressionT pattern;
+ int initializer_position;
+ ExpressionT initializer;
+ };
+
+ DeclarationParsingResult()
+ : declarations(4),
+ first_initializer_loc(Scanner::Location::invalid()),
+ bindings_loc(Scanner::Location::invalid()) {}
+
+ DeclarationDescriptor descriptor;
+ List<Declaration> declarations;
+ Scanner::Location first_initializer_loc;
+ Scanner::Location bindings_loc;
+ };
+
+ struct CatchInfo {
+ public:
+ explicit CatchInfo(ParserBase* parser)
+ : name(parser->impl()->EmptyIdentifier()),
+ variable(nullptr),
+ pattern(parser->impl()->EmptyExpression()),
+ scope(nullptr),
+ init_block(parser->impl()->NullBlock()),
+ inner_block(parser->impl()->NullBlock()),
+ for_promise_reject(false),
+ bound_names(1, parser->zone()),
+ tail_call_expressions(parser->zone()) {}
+ IdentifierT name;
+ Variable* variable;
+ ExpressionT pattern;
+ Scope* scope;
+ BlockT init_block;
+ BlockT inner_block;
+ bool for_promise_reject;
+ ZoneList<const AstRawString*> bound_names;
+ TailCallExpressionList tail_call_expressions;
+ };
+
+ struct ForInfo {
+ public:
+ explicit ForInfo(ParserBase* parser)
+ : bound_names(1, parser->zone()),
+ mode(ForEachStatement::ENUMERATE),
+ each_loc(),
+ parsing_result() {}
+ ZoneList<const AstRawString*> bound_names;
+ ForEachStatement::VisitMode mode;
+ Scanner::Location each_loc;
+ DeclarationParsingResult parsing_result;
+ };
+
+ struct ClassInfo {
+ public:
+ explicit ClassInfo(ParserBase* parser)
+ : proxy(nullptr),
+ extends(parser->impl()->EmptyExpression()),
+ properties(parser->impl()->NewClassPropertyList(4)),
+ instance_field_initializers(parser->impl()->NewExpressionList(0)),
+ constructor(parser->impl()->EmptyFunctionLiteral()),
+ has_seen_constructor(false),
+ static_initializer_var(nullptr) {}
+ VariableProxy* proxy;
+ ExpressionT extends;
+ typename Types::ClassPropertyList properties;
+ ExpressionListT instance_field_initializers;
+ FunctionLiteralT constructor;
+ bool has_seen_constructor;
+ Variable* static_initializer_var;
+ };
+
DeclarationScope* NewScriptScope() const {
- return new (zone()) DeclarationScope(zone());
+ return new (zone()) DeclarationScope(zone(), ast_value_factory());
}
DeclarationScope* NewVarblockScope() const {
@@ -653,7 +695,7 @@
}
ModuleScope* NewModuleScope(DeclarationScope* parent) const {
- return new (zone()) ModuleScope(zone(), parent, ast_value_factory());
+ return new (zone()) ModuleScope(parent, ast_value_factory());
}
DeclarationScope* NewEvalScope(Scope* parent) const {
@@ -683,12 +725,18 @@
new (zone()) DeclarationScope(zone(), scope(), FUNCTION_SCOPE, kind);
// TODO(verwaest): Move into the DeclarationScope constructor.
if (!IsArrowFunction(kind)) {
- result->DeclareThis(ast_value_factory());
result->DeclareDefaultFunctionVariables(ast_value_factory());
}
return result;
}
+ V8_INLINE DeclarationScope* GetDeclarationScope() const {
+ return scope()->GetDeclarationScope();
+ }
+ V8_INLINE DeclarationScope* GetClosureScope() const {
+ return scope()->GetClosureScope();
+ }
+
Scanner* scanner() const { return scanner_; }
AstValueFactory* ast_value_factory() const { return ast_value_factory_; }
int position() const { return scanner_->location().beg_pos; }
@@ -696,7 +744,6 @@
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;
@@ -761,8 +808,12 @@
Expect(Token::SEMICOLON, ok);
}
- // A dummy function, just useful as an argument to CHECK_OK_CUSTOM.
+ // Dummy functions, just useful as arguments to CHECK_OK_CUSTOM.
static void Void() {}
+ template <typename T>
+ static T Return(T result) {
+ return result;
+ }
bool is_any_identifier(Token::Value token) {
return token == Token::IDENTIFIER || token == Token::ENUM ||
@@ -796,7 +847,7 @@
}
}
- bool CheckInOrOf(ForEachStatement::VisitMode* visit_mode, bool* ok) {
+ bool CheckInOrOf(ForEachStatement::VisitMode* visit_mode) {
if (Check(Token::IN)) {
*visit_mode = ForEachStatement::ENUMERATE;
return true;
@@ -818,21 +869,19 @@
Scanner::Location octal = scanner()->octal_position();
if (octal.IsValid() && beg_pos <= octal.beg_pos &&
octal.end_pos <= end_pos) {
- ReportMessageAt(octal, message);
+ impl()->ReportMessageAt(octal, message);
scanner()->clear_octal_position();
*ok = false;
}
}
// for now, this check just collects statistics.
- void CheckDecimalLiteralWithLeadingZero(int* use_counts, int beg_pos,
- int end_pos) {
+ void CheckDecimalLiteralWithLeadingZero(int beg_pos, int end_pos) {
Scanner::Location token_location =
scanner()->decimal_with_leading_zero_position();
if (token_location.IsValid() && beg_pos <= token_location.beg_pos &&
token_location.end_pos <= end_pos) {
scanner()->clear_decimal_with_leading_zero_position();
- if (use_counts != nullptr)
- ++use_counts[v8::Isolate::kDecimalWithLeadingZeroInStrictMode];
+ impl()->CountUsage(v8::Isolate::kDecimalWithLeadingZeroInStrictMode);
}
}
@@ -846,9 +895,7 @@
ok);
}
- void CheckDestructuringElement(ExpressionT element,
- ExpressionClassifier* classifier, int beg_pos,
- int end_pos);
+ void CheckDestructuringElement(ExpressionT element, 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.
@@ -859,14 +906,14 @@
// 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,
+ if (impl()->IsEvalOrArguments(function_name)) {
+ impl()->ReportMessageAt(function_name_loc,
MessageTemplate::kStrictEvalArguments);
*ok = false;
return;
}
if (function_name_validity == kFunctionNameIsStrictReserved) {
- Traits::ReportMessageAt(function_name_loc,
+ impl()->ReportMessageAt(function_name_loc,
MessageTemplate::kUnexpectedStrictReserved);
*ok = false;
return;
@@ -880,50 +927,45 @@
return Token::Precedence(token);
}
- typename Traits::Type::Factory* factory() { return &ast_node_factory_; }
+ typename Types::Factory* factory() { return &ast_node_factory_; }
DeclarationScope* GetReceiverScope() const {
return scope()->GetReceiverScope();
}
LanguageMode language_mode() { return scope()->language_mode(); }
- bool is_generator() const { return function_state_->is_generator(); }
- bool is_async_function() const {
- return function_state_->is_async_function();
+ void RaiseLanguageMode(LanguageMode mode) {
+ LanguageMode old = scope()->language_mode();
+ impl()->SetLanguageMode(scope(), old > mode ? old : mode);
}
- bool is_resumable() const { return function_state_->is_resumable(); }
+ bool is_generator() const {
+ return IsGeneratorFunction(function_state_->kind());
+ }
+ bool is_async_function() const {
+ return IsAsyncFunction(function_state_->kind());
+ }
+ bool is_resumable() const {
+ return IsResumableFunction(function_state_->kind());
+ }
// Report syntax errors.
- void ReportMessage(MessageTemplate::Template message, const char* arg = NULL,
+ void ReportMessage(MessageTemplate::Template message) {
+ Scanner::Location source_location = scanner()->location();
+ impl()->ReportMessageAt(source_location, message,
+ static_cast<const char*>(nullptr), kSyntaxError);
+ }
+
+ template <typename T>
+ void ReportMessage(MessageTemplate::Template message, T arg,
ParseErrorType error_type = kSyntaxError) {
Scanner::Location source_location = scanner()->location();
- Traits::ReportMessageAt(source_location, message, arg, error_type);
- }
-
- void ReportMessage(MessageTemplate::Template message, const AstRawString* arg,
- 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,
- const char* arg = NULL,
- ParseErrorType error_type = kSyntaxError) {
- Traits::ReportMessageAt(location, message, arg, error_type);
- }
-
- void ReportMessageAt(Scanner::Location location,
- MessageTemplate::Template message,
- const AstRawString* arg,
- ParseErrorType error_type = kSyntaxError) {
- Traits::ReportMessageAt(location, message, arg, error_type);
+ impl()->ReportMessageAt(source_location, message, arg, error_type);
}
void ReportMessageAt(Scanner::Location location,
MessageTemplate::Template message,
ParseErrorType error_type) {
- ReportMessageAt(location, message, static_cast<const char*>(nullptr),
- error_type);
+ impl()->ReportMessageAt(location, message,
+ static_cast<const char*>(nullptr), error_type);
}
void GetUnexpectedTokenMessage(
@@ -938,59 +980,47 @@
void ReportClassifierError(
const typename ExpressionClassifier::Error& error) {
- Traits::ReportMessageAt(error.location, error.message, error.arg,
+ impl()->ReportMessageAt(error.location, error.message, error.arg,
error.type);
}
- void ValidateExpression(const ExpressionClassifier* classifier, bool* ok) {
- if (!classifier->is_valid_expression() ||
- classifier->has_object_literal_error()) {
- const Scanner::Location& a = classifier->expression_error().location;
- const Scanner::Location& b =
- classifier->object_literal_error().location;
- if (a.beg_pos < 0 || (b.beg_pos >= 0 && a.beg_pos > b.beg_pos)) {
- ReportClassifierError(classifier->object_literal_error());
- } else {
- ReportClassifierError(classifier->expression_error());
- }
+ void ValidateExpression(bool* ok) {
+ if (!classifier()->is_valid_expression()) {
+ 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());
+ void ValidateFormalParameterInitializer(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());
+ void ValidateBindingPattern(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());
+ void ValidateAssignmentPattern(bool* ok) {
+ if (!classifier()->is_valid_assignment_pattern()) {
+ ReportClassifierError(classifier()->assignment_pattern_error());
*ok = false;
}
}
- void ValidateFormalParameters(const ExpressionClassifier* classifier,
- LanguageMode language_mode,
+ void ValidateFormalParameters(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());
+ !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());
+ !classifier()->is_valid_strict_mode_formal_parameters()) {
+ ReportClassifierError(classifier()->strict_mode_formal_parameter_error());
*ok = false;
}
}
@@ -999,78 +1029,73 @@
return is_any_identifier(token) || token == Token::LPAREN;
}
- void ValidateArrowFormalParameters(const ExpressionClassifier* classifier,
- ExpressionT expr,
+ void ValidateArrowFormalParameters(ExpressionT expr,
bool parenthesized_formals, bool is_async,
bool* ok) {
- if (classifier->is_valid_binding_pattern()) {
+ if (classifier()->is_valid_binding_pattern()) {
// A simple arrow formal parameter: IDENTIFIER => BODY.
- if (!this->IsIdentifier(expr)) {
- Traits::ReportMessageAt(scanner()->location(),
+ if (!impl()->IsIdentifier(expr)) {
+ impl()->ReportMessageAt(scanner()->location(),
MessageTemplate::kUnexpectedToken,
Token::String(scanner()->current_token()));
*ok = false;
}
- } else if (!classifier->is_valid_arrow_formal_parameters()) {
+ } 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 typename ExpressionClassifier::Error& error =
- parenthesized_formals ? classifier->arrow_formal_parameters_error()
- : classifier->binding_pattern_error();
+ parenthesized_formals ? classifier()->arrow_formal_parameters_error()
+ : classifier()->binding_pattern_error();
ReportClassifierError(error);
*ok = false;
}
- if (is_async && !classifier->is_valid_async_arrow_formal_parameters()) {
+ if (is_async && !classifier()->is_valid_async_arrow_formal_parameters()) {
const typename ExpressionClassifier::Error& error =
- classifier->async_arrow_formal_parameters_error();
+ classifier()->async_arrow_formal_parameters_error();
ReportClassifierError(error);
*ok = false;
}
}
- void ValidateLetPattern(const ExpressionClassifier* classifier, bool* ok) {
- if (!classifier->is_valid_let_pattern()) {
- ReportClassifierError(classifier->let_pattern_error());
+ void ValidateLetPattern(bool* ok) {
+ if (!classifier()->is_valid_let_pattern()) {
+ ReportClassifierError(classifier()->let_pattern_error());
*ok = false;
}
}
- void CheckNoTailCallExpressions(const ExpressionClassifier* classifier,
- bool* ok) {
- if (FLAG_harmony_explicit_tailcalls &&
- classifier->has_tail_call_expression()) {
- ReportClassifierError(classifier->tail_call_expression_error());
- *ok = false;
- }
- }
-
- void ExpressionUnexpectedToken(ExpressionClassifier* classifier) {
+ void ExpressionUnexpectedToken() {
MessageTemplate::Template message = MessageTemplate::kUnexpectedToken;
const char* arg;
Scanner::Location location = scanner()->peek_location();
GetUnexpectedTokenMessage(peek(), &message, &location, &arg);
- classifier->RecordExpressionError(location, message, arg);
+ classifier()->RecordExpressionError(location, message, arg);
}
- void BindingPatternUnexpectedToken(ExpressionClassifier* classifier) {
+ void BindingPatternUnexpectedToken() {
MessageTemplate::Template message = MessageTemplate::kUnexpectedToken;
const char* arg;
Scanner::Location location = scanner()->peek_location();
GetUnexpectedTokenMessage(peek(), &message, &location, &arg);
- classifier->RecordBindingPatternError(location, message, arg);
+ classifier()->RecordBindingPatternError(location, message, arg);
}
- void ArrowFormalParametersUnexpectedToken(ExpressionClassifier* classifier) {
+ void ArrowFormalParametersUnexpectedToken() {
MessageTemplate::Template message = MessageTemplate::kUnexpectedToken;
const char* arg;
Scanner::Location location = scanner()->peek_location();
GetUnexpectedTokenMessage(peek(), &message, &location, &arg);
- classifier->RecordArrowFormalParametersError(location, message, arg);
+ classifier()->RecordArrowFormalParametersError(location, message, arg);
}
- // Recursive descent functions:
+ // Recursive descent functions.
+ // 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. The family of CHECK_OK* macros can
+ // be useful for this.
// 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
@@ -1078,8 +1103,7 @@
// "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);
+ IdentifierT ParseAndClassifyIdentifier(bool* ok);
// Parses an identifier or a strict mode future reserved word, and indicate
// whether it is strict mode future reserved. Allows passing in function_kind
// for the case of parsing the identifier in a function expression, where the
@@ -1098,76 +1122,173 @@
ExpressionT ParseRegExpLiteral(bool* ok);
- ExpressionT ParsePrimaryExpression(ExpressionClassifier* classifier,
- bool* is_async, bool* ok);
- ExpressionT ParsePrimaryExpression(ExpressionClassifier* classifier,
- bool* ok) {
+ ExpressionT ParsePrimaryExpression(bool* is_async, bool* ok);
+ ExpressionT ParsePrimaryExpression(bool* ok) {
bool is_async;
- return ParsePrimaryExpression(classifier, &is_async, ok);
- }
- ExpressionT ParseExpression(bool accept_IN, bool* ok);
- ExpressionT ParseExpression(bool accept_IN, ExpressionClassifier* classifier,
- bool* ok);
- ExpressionT ParseArrayLiteral(ExpressionClassifier* classifier, bool* ok);
- ExpressionT ParsePropertyName(IdentifierT* name, bool* is_get, bool* is_set,
- bool* is_computed_name,
- ExpressionClassifier* classifier, bool* ok);
- ExpressionT ParseObjectLiteral(ExpressionClassifier* classifier, bool* ok);
- ObjectLiteralPropertyT ParsePropertyDefinition(
- ObjectLiteralCheckerBase* checker, bool in_class, bool has_extends,
- MethodKind kind, bool* is_computed_name, bool* has_seen_constructor,
- ExpressionClassifier* classifier, IdentifierT* name, bool* ok);
- typename Traits::Type::ExpressionList ParseArguments(
- Scanner::Location* first_spread_pos, bool maybe_arrow,
- ExpressionClassifier* classifier, bool* ok);
- typename Traits::Type::ExpressionList ParseArguments(
- Scanner::Location* first_spread_pos, ExpressionClassifier* classifier,
- bool* ok) {
- return ParseArguments(first_spread_pos, false, classifier, ok);
+ return ParsePrimaryExpression(&is_async, ok);
}
- ExpressionT ParseAssignmentExpression(bool accept_IN,
- ExpressionClassifier* classifier,
- bool* ok);
- ExpressionT ParseYieldExpression(bool accept_IN,
- ExpressionClassifier* classifier, bool* ok);
- ExpressionT ParseTailCallExpression(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* is_async, bool* ok);
- ExpressionT ParseMemberExpression(ExpressionClassifier* classifier,
- bool* is_async, bool* ok);
- ExpressionT ParseMemberExpressionContinuation(
- ExpressionT expression, bool* is_async, ExpressionClassifier* classifier,
- bool* ok);
+ // This method wraps the parsing of the expression inside a new expression
+ // classifier and calls RewriteNonPattern if parsing is successful.
+ // It should be used whenever we're parsing an expression that will be
+ // used as a non-pattern (i.e., in most cases).
+ V8_INLINE ExpressionT ParseExpression(bool accept_IN, bool* ok);
+
+ // This method does not wrap the parsing of the expression inside a
+ // new expression classifier; it uses the top-level classifier instead.
+ // It should be used whenever we're parsing something with the "cover"
+ // grammar that recognizes both patterns and non-patterns (which roughly
+ // corresponds to what's inside the parentheses generated by the symbol
+ // "CoverParenthesizedExpressionAndArrowParameterList" in the ES 2017
+ // specification).
+ ExpressionT ParseExpressionCoverGrammar(bool accept_IN, bool* ok);
+
+ ExpressionT ParseArrayLiteral(bool* ok);
+
+ enum class PropertyKind {
+ kAccessorProperty,
+ kValueProperty,
+ kShorthandProperty,
+ kMethodProperty,
+ kClassField,
+ kNotSet
+ };
+
+ bool SetPropertyKindFromToken(Token::Value token, PropertyKind* kind);
+ ExpressionT ParsePropertyName(IdentifierT* name, PropertyKind* kind,
+ bool* is_generator, bool* is_get, bool* is_set,
+ bool* is_async, bool* is_computed_name,
+ bool* ok);
+ ExpressionT ParseObjectLiteral(bool* ok);
+ ClassLiteralPropertyT ParseClassPropertyDefinition(
+ ClassLiteralChecker* checker, bool has_extends, bool* is_computed_name,
+ bool* has_seen_constructor, bool* ok);
+ FunctionLiteralT ParseClassFieldForInitializer(bool has_initializer,
+ bool* ok);
+ ObjectLiteralPropertyT ParseObjectPropertyDefinition(
+ ObjectLiteralChecker* checker, bool* is_computed_name, bool* ok);
+ ExpressionListT ParseArguments(Scanner::Location* first_spread_pos,
+ bool maybe_arrow, bool* ok);
+ ExpressionListT ParseArguments(Scanner::Location* first_spread_pos,
+ bool* ok) {
+ return ParseArguments(first_spread_pos, false, ok);
+ }
+
+ ExpressionT ParseAssignmentExpression(bool accept_IN, bool* ok);
+ ExpressionT ParseYieldExpression(bool accept_IN, bool* ok);
+ ExpressionT ParseConditionalExpression(bool accept_IN, bool* ok);
+ ExpressionT ParseBinaryExpression(int prec, bool accept_IN, bool* ok);
+ ExpressionT ParseUnaryExpression(bool* ok);
+ ExpressionT ParsePostfixExpression(bool* ok);
+ ExpressionT ParseLeftHandSideExpression(bool* ok);
+ ExpressionT ParseMemberWithNewPrefixesExpression(bool* is_async, bool* ok);
+ ExpressionT ParseMemberExpression(bool* is_async, bool* ok);
+ ExpressionT ParseMemberExpressionContinuation(ExpressionT expression,
+ bool* is_async, bool* ok);
ExpressionT ParseArrowFunctionLiteral(bool accept_IN,
const FormalParametersT& parameters,
- bool is_async,
- const ExpressionClassifier& classifier,
bool* ok);
- ExpressionT ParseTemplateLiteral(ExpressionT tag, int start,
- ExpressionClassifier* classifier, bool* ok);
+ void ParseAsyncFunctionBody(Scope* scope, StatementListT body,
+ FunctionKind kind, FunctionBodyType type,
+ bool accept_IN, int pos, bool* ok);
+ ExpressionT ParseAsyncFunctionLiteral(bool* ok);
+ ExpressionT ParseClassLiteral(IdentifierT name,
+ Scanner::Location class_name_location,
+ bool name_is_strict_reserved,
+ int class_token_pos, bool* ok);
+ ExpressionT ParseTemplateLiteral(ExpressionT tag, int start, bool* ok);
ExpressionT ParseSuperExpression(bool is_new, bool* ok);
ExpressionT ParseNewTargetExpression(bool* ok);
- void ParseFormalParameter(FormalParametersT* parameters,
- ExpressionClassifier* classifier, bool* ok);
- void ParseFormalParameterList(FormalParametersT* parameters,
- ExpressionClassifier* classifier, bool* ok);
+ void ParseFormalParameter(FormalParametersT* parameters, bool* ok);
+ void ParseFormalParameterList(FormalParametersT* parameters, bool* ok);
void CheckArityRestrictions(int param_count, FunctionKind function_type,
bool has_rest, int formals_start_pos,
int formals_end_pos, bool* ok);
+ BlockT ParseVariableDeclarations(VariableDeclarationContext var_context,
+ DeclarationParsingResult* parsing_result,
+ ZoneList<const AstRawString*>* names,
+ bool* ok);
+ StatementT ParseAsyncFunctionDeclaration(ZoneList<const AstRawString*>* names,
+ bool default_export, bool* ok);
+ StatementT ParseFunctionDeclaration(bool* ok);
+ StatementT ParseHoistableDeclaration(ZoneList<const AstRawString*>* names,
+ bool default_export, bool* ok);
+ StatementT ParseHoistableDeclaration(int pos, ParseFunctionFlags flags,
+ ZoneList<const AstRawString*>* names,
+ bool default_export, bool* ok);
+ StatementT ParseClassDeclaration(ZoneList<const AstRawString*>* names,
+ bool default_export, bool* ok);
+ StatementT ParseNativeDeclaration(bool* ok);
+
+ // Under some circumstances, we allow preparsing to abort if the preparsed
+ // function is "long and trivial", and fully parse instead. Our current
+ // definition of "long and trivial" is:
+ // - over kLazyParseTrialLimit statements
+ // - all starting with an identifier (i.e., no if, for, while, etc.)
+ static const int kLazyParseTrialLimit = 200;
+
+ // TODO(nikolaos, marja): The first argument should not really be passed
+ // by value. The method is expected to add the parsed statements to the
+ // list. This works because in the case of the parser, StatementListT is
+ // a pointer whereas the preparser does not really modify the body.
+ V8_INLINE void ParseStatementList(StatementListT body, int end_token,
+ bool* ok) {
+ LazyParsingResult result = ParseStatementList(body, end_token, false, ok);
+ USE(result);
+ DCHECK_EQ(result, kLazyParsingComplete);
+ }
+ LazyParsingResult ParseStatementList(StatementListT body, int end_token,
+ bool may_abort, bool* ok);
+ StatementT ParseStatementListItem(bool* ok);
+ StatementT ParseStatement(ZoneList<const AstRawString*>* labels,
+ AllowLabelledFunctionStatement allow_function,
+ bool* ok);
+ StatementT ParseStatementAsUnlabelled(ZoneList<const AstRawString*>* labels,
+ bool* ok);
+ BlockT ParseBlock(ZoneList<const AstRawString*>* labels, bool* ok);
+
+ // Parse a SubStatement in strict mode, or with an extra block scope in
+ // sloppy mode to handle
+ // ES#sec-functiondeclarations-in-ifstatement-statement-clauses
+ // The legacy parameter indicates whether function declarations are
+ // banned by the ES2015 specification in this location, and they are being
+ // permitted here to match previous V8 behavior.
+ StatementT ParseScopedStatement(ZoneList<const AstRawString*>* labels,
+ bool legacy, bool* ok);
+
+ StatementT ParseVariableStatement(VariableDeclarationContext var_context,
+ ZoneList<const AstRawString*>* names,
+ bool* ok);
+
+ // Magical syntax support.
+ ExpressionT ParseV8Intrinsic(bool* ok);
+
+ ExpressionT ParseDoExpression(bool* ok);
+
+ StatementT ParseDebuggerStatement(bool* ok);
+
+ StatementT ParseExpressionOrLabelledStatement(
+ ZoneList<const AstRawString*>* labels,
+ AllowLabelledFunctionStatement allow_function, bool* ok);
+ StatementT ParseIfStatement(ZoneList<const AstRawString*>* labels, bool* ok);
+ StatementT ParseContinueStatement(bool* ok);
+ StatementT ParseBreakStatement(ZoneList<const AstRawString*>* labels,
+ bool* ok);
+ StatementT ParseReturnStatement(bool* ok);
+ StatementT ParseWithStatement(ZoneList<const AstRawString*>* labels,
+ bool* ok);
+ StatementT ParseDoWhileStatement(ZoneList<const AstRawString*>* labels,
+ bool* ok);
+ StatementT ParseWhileStatement(ZoneList<const AstRawString*>* labels,
+ bool* ok);
+ StatementT ParseThrowStatement(bool* ok);
+ StatementT ParseSwitchStatement(ZoneList<const AstRawString*>* labels,
+ bool* ok);
+ StatementT ParseTryStatement(bool* ok);
+ StatementT ParseForStatement(ZoneList<const AstRawString*>* labels, bool* ok);
+
bool IsNextLetKeyword();
bool IsTrivialExpression();
@@ -1184,9 +1305,9 @@
bool IsValidReferenceExpression(ExpressionT expression);
bool IsAssignableIdentifier(ExpressionT expression) {
- if (!Traits::IsIdentifier(expression)) return false;
+ if (!impl()->IsIdentifier(expression)) return false;
if (is_strict(language_mode()) &&
- Traits::IsEvalOrArguments(Traits::AsIdentifier(expression))) {
+ impl()->IsEvalOrArguments(impl()->AsIdentifier(expression))) {
return false;
}
return true;
@@ -1201,8 +1322,8 @@
// forwards the information to scope.
Call::PossiblyEval CheckPossibleEvalCall(ExpressionT expression,
Scope* scope) {
- if (Traits::IsIdentifier(expression) &&
- Traits::IsEval(Traits::AsIdentifier(expression))) {
+ if (impl()->IsIdentifier(expression) &&
+ impl()->IsEval(impl()->AsIdentifier(expression))) {
scope->RecordEvalCall();
if (is_sloppy(scope->language_mode())) {
// For sloppy scopes we also have to record the call at function level,
@@ -1214,56 +1335,33 @@
return Call::NOT_EVAL;
}
- // 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,
- MethodKind method_type,
- ExpressionClassifier* classifier, 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 {
+ class ObjectLiteralChecker {
public:
explicit ObjectLiteralChecker(ParserBase* parser)
- : ObjectLiteralCheckerBase(parser), has_seen_proto_(false) {}
+ : parser_(parser), has_seen_proto_(false) {}
- void CheckProperty(Token::Value property, PropertyKind type,
- MethodKind method_type, ExpressionClassifier* classifier,
- bool* ok) override;
+ void CheckDuplicateProto(Token::Value property);
private:
bool IsProto() { return this->scanner()->LiteralMatches("__proto__", 9); }
+ ParserBase* parser() const { return parser_; }
+ Scanner* scanner() const { return parser_->scanner(); }
+
+ ParserBase* parser_;
bool has_seen_proto_;
};
// Validation per ES6 class literals.
- class ClassLiteralChecker : public ObjectLiteralCheckerBase {
+ class ClassLiteralChecker {
public:
explicit ClassLiteralChecker(ParserBase* parser)
- : ObjectLiteralCheckerBase(parser), has_seen_constructor_(false) {}
+ : parser_(parser), has_seen_constructor_(false) {}
- void CheckProperty(Token::Value property, PropertyKind type,
- MethodKind method_type, ExpressionClassifier* classifier,
- bool* ok) override;
+ void CheckClassMethodName(Token::Value property, PropertyKind type,
+ bool is_generator, bool is_async, bool is_static,
+ bool* ok);
private:
bool IsConstructor() {
@@ -1273,6 +1371,10 @@
return this->scanner()->LiteralMatches("prototype", 9);
}
+ ParserBase* parser() const { return parser_; }
+ Scanner* scanner() const { return parser_->scanner(); }
+
+ ParserBase* parser_;
bool has_seen_constructor_;
};
@@ -1281,19 +1383,63 @@
}
Scope* scope() const { return scope_state_->scope(); }
+ // Stack of expression classifiers.
+ // The top of the stack is always pointed to by classifier().
+ V8_INLINE ExpressionClassifier* classifier() const {
+ DCHECK_NOT_NULL(classifier_);
+ return classifier_;
+ }
+
+ // Accumulates the classifier that is on top of the stack (inner) to
+ // the one that is right below (outer) and pops the inner.
+ V8_INLINE void Accumulate(unsigned productions,
+ bool merge_non_patterns = true) {
+ DCHECK_NOT_NULL(classifier_);
+ ExpressionClassifier* previous = classifier_->previous();
+ DCHECK_NOT_NULL(previous);
+ previous->Accumulate(classifier_, productions, merge_non_patterns);
+ classifier_ = previous;
+ }
+
+ // Pops and discards the classifier that is on top of the stack
+ // without accumulating.
+ V8_INLINE void Discard() {
+ DCHECK_NOT_NULL(classifier_);
+ classifier_->Discard();
+ classifier_ = classifier_->previous();
+ }
+
+ // Accumulate errors that can be arbitrarily deep in an expression.
+ // These correspond to the ECMAScript spec's 'Contains' operation
+ // on productions. This includes:
+ //
+ // - YieldExpression is disallowed in arrow parameters in a generator.
+ // - AwaitExpression is disallowed in arrow parameters in an async function.
+ // - AwaitExpression is disallowed in async arrow parameters.
+ //
+ V8_INLINE void AccumulateFormalParameterContainmentErrors() {
+ Accumulate(ExpressionClassifier::FormalParameterInitializerProduction |
+ ExpressionClassifier::AsyncArrowFormalParametersProduction);
+ }
+
+ // Parser base's protected field members.
+
ScopeState* scope_state_; // Scope stack.
FunctionState* function_state_; // Function state stack.
v8::Extension* extension_;
FuncNameInferrer* fni_;
AstValueFactory* ast_value_factory_; // Not owned.
- typename Traits::Type::Factory ast_node_factory_;
+ typename Types::Factory ast_node_factory_;
ParserRecorder* log_;
Mode mode_;
bool parsing_module_;
uintptr_t stack_limit_;
+ // Parser base's private field members.
+
private:
Zone* zone_;
+ ExpressionClassifier* classifier_;
Scanner* scanner_;
bool stack_overflow_;
@@ -1308,6 +1454,7 @@
bool allow_harmony_async_await_;
bool allow_harmony_restrictive_generators_;
bool allow_harmony_trailing_commas_;
+ bool allow_harmony_class_fields_;
friend class DiscardableZoneScope;
};
@@ -1315,12 +1462,12 @@
template <typename Impl>
ParserBase<Impl>::FunctionState::FunctionState(
FunctionState** function_state_stack, ScopeState** scope_stack,
- Scope* scope, FunctionKind kind)
+ DeclarationScope* scope)
: ScopeState(scope_stack, scope),
next_materialized_literal_index_(0),
expected_property_count_(0),
- kind_(kind),
- generator_object_variable_(NULL),
+ generator_object_variable_(nullptr),
+ promise_variable_(nullptr),
function_state_stack_(function_state_stack),
outer_function_state_(*function_state_stack),
destructuring_assignments_to_rewrite_(16, scope->zone()),
@@ -1413,19 +1560,18 @@
MessageTemplate::Template message) {
const char* arg;
GetUnexpectedTokenMessage(token, &message, &source_location, &arg);
- Traits::ReportMessageAt(source_location, message, arg);
+ impl()->ReportMessageAt(source_location, message, arg);
}
template <typename Impl>
typename ParserBase<Impl>::IdentifierT ParserBase<Impl>::ParseIdentifier(
AllowRestrictedIdentifiers allow_restricted_identifiers, bool* ok) {
ExpressionClassifier classifier(this);
- auto result =
- ParseAndClassifyIdentifier(&classifier, CHECK_OK_CUSTOM(EmptyIdentifier));
+ auto result = ParseAndClassifyIdentifier(CHECK_OK_CUSTOM(EmptyIdentifier));
if (allow_restricted_identifiers == kDontAllowRestrictedIdentifiers) {
- ValidateAssignmentPattern(&classifier, CHECK_OK_CUSTOM(EmptyIdentifier));
- ValidateBindingPattern(&classifier, CHECK_OK_CUSTOM(EmptyIdentifier));
+ ValidateAssignmentPattern(CHECK_OK_CUSTOM(EmptyIdentifier));
+ ValidateBindingPattern(CHECK_OK_CUSTOM(EmptyIdentifier));
}
return result;
@@ -1433,33 +1579,32 @@
template <typename Impl>
typename ParserBase<Impl>::IdentifierT
-ParserBase<Impl>::ParseAndClassifyIdentifier(ExpressionClassifier* classifier,
- bool* ok) {
+ParserBase<Impl>::ParseAndClassifyIdentifier(bool* ok) {
Token::Value next = Next();
if (next == Token::IDENTIFIER || next == Token::ASYNC ||
(next == Token::AWAIT && !parsing_module_ && !is_async_function())) {
- IdentifierT name = this->GetSymbol(scanner());
+ IdentifierT name = impl()->GetSymbol();
// 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->IsEvalOrArguments(name)) {
- classifier->RecordStrictModeFormalParameterError(
+ if (impl()->IsEvalOrArguments(name)) {
+ classifier()->RecordStrictModeFormalParameterError(
scanner()->location(), MessageTemplate::kStrictEvalArguments);
if (is_strict(language_mode())) {
- classifier->RecordBindingPatternError(
+ classifier()->RecordBindingPatternError(
scanner()->location(), MessageTemplate::kStrictEvalArguments);
}
} else if (next == Token::AWAIT) {
- classifier->RecordAsyncArrowFormalParametersError(
+ classifier()->RecordAsyncArrowFormalParametersError(
scanner()->location(), MessageTemplate::kAwaitBindingIdentifier);
}
- if (classifier->duplicate_finder() != nullptr &&
- scanner()->FindSymbol(classifier->duplicate_finder(), 1) != 0) {
- classifier->RecordDuplicateFormalParameterError(scanner()->location());
+ if (classifier()->duplicate_finder() != nullptr &&
+ scanner()->FindSymbol(classifier()->duplicate_finder(), 1) != 0) {
+ classifier()->RecordDuplicateFormalParameterError(scanner()->location());
}
return name;
} else if (is_sloppy(language_mode()) &&
@@ -1467,25 +1612,25 @@
next == Token::ESCAPED_STRICT_RESERVED_WORD ||
next == Token::LET || next == Token::STATIC ||
(next == Token::YIELD && !is_generator()))) {
- classifier->RecordStrictModeFormalParameterError(
+ classifier()->RecordStrictModeFormalParameterError(
scanner()->location(), MessageTemplate::kUnexpectedStrictReserved);
if (next == Token::ESCAPED_STRICT_RESERVED_WORD &&
is_strict(language_mode())) {
ReportUnexpectedToken(next);
*ok = false;
- return Traits::EmptyIdentifier();
+ return impl()->EmptyIdentifier();
}
if (next == Token::LET ||
(next == Token::ESCAPED_STRICT_RESERVED_WORD &&
scanner()->is_literal_contextual_keyword(CStrVector("let")))) {
- classifier->RecordLetPatternError(scanner()->location(),
- MessageTemplate::kLetInLexicalBinding);
+ classifier()->RecordLetPatternError(
+ scanner()->location(), MessageTemplate::kLetInLexicalBinding);
}
- return this->GetSymbol(scanner());
+ return impl()->GetSymbol();
} else {
- this->ReportUnexpectedToken(next);
+ ReportUnexpectedToken(next);
*ok = false;
- return Traits::EmptyIdentifier();
+ return impl()->EmptyIdentifier();
}
}
@@ -1505,10 +1650,10 @@
} else {
ReportUnexpectedToken(next);
*ok = false;
- return Traits::EmptyIdentifier();
+ return impl()->EmptyIdentifier();
}
- return this->GetSymbol(scanner());
+ return impl()->GetSymbol();
}
template <typename Impl>
@@ -1521,12 +1666,12 @@
next != Token::FUTURE_STRICT_RESERVED_WORD &&
next != Token::ESCAPED_KEYWORD &&
next != Token::ESCAPED_STRICT_RESERVED_WORD && !Token::IsKeyword(next)) {
- this->ReportUnexpectedToken(next);
+ ReportUnexpectedToken(next);
*ok = false;
- return Traits::EmptyIdentifier();
+ return impl()->EmptyIdentifier();
}
- return this->GetSymbol(scanner());
+ return impl()->GetSymbol();
}
template <typename Impl>
@@ -1537,18 +1682,18 @@
Next();
ReportMessage(MessageTemplate::kUnterminatedRegExp);
*ok = false;
- return Traits::EmptyExpression();
+ return impl()->EmptyExpression();
}
int literal_index = function_state_->NextMaterializedLiteralIndex();
- IdentifierT js_pattern = this->GetNextSymbol(scanner());
+ IdentifierT js_pattern = impl()->GetNextSymbol();
Maybe<RegExp::Flags> flags = scanner()->ScanRegExpFlags();
if (flags.IsNothing()) {
Next();
ReportMessage(MessageTemplate::kMalformedRegExpFlags);
*ok = false;
- return Traits::EmptyExpression();
+ return impl()->EmptyExpression();
}
int js_flags = flags.FromJust();
Next();
@@ -1557,7 +1702,7 @@
template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePrimaryExpression(
- ExpressionClassifier* classifier, bool* is_async, bool* ok) {
+ bool* is_async, bool* ok) {
// PrimaryExpression ::
// 'this'
// 'null'
@@ -1573,14 +1718,14 @@
// '(' Expression ')'
// TemplateLiteral
// do Block
- // AsyncFunctionExpression
+ // AsyncFunctionLiteral
int beg_pos = peek_position();
switch (peek()) {
case Token::THIS: {
- BindingPatternUnexpectedToken(classifier);
+ BindingPatternUnexpectedToken();
Consume(Token::THIS);
- return this->ThisExpression(beg_pos);
+ return impl()->ThisExpression(beg_pos);
}
case Token::NULL_LITERAL:
@@ -1588,15 +1733,15 @@
case Token::FALSE_LITERAL:
case Token::SMI:
case Token::NUMBER:
- BindingPatternUnexpectedToken(classifier);
- return this->ExpressionFromLiteral(Next(), beg_pos, scanner(), factory());
+ BindingPatternUnexpectedToken();
+ return impl()->ExpressionFromLiteral(Next(), beg_pos);
case Token::ASYNC:
if (allow_harmony_async_await() &&
!scanner()->HasAnyLineTerminatorAfterNext() &&
PeekAhead() == Token::FUNCTION) {
Consume(Token::ASYNC);
- return impl()->ParseAsyncFunctionExpression(CHECK_OK);
+ return ParseAsyncFunctionLiteral(CHECK_OK);
}
// CoverCallExpressionAndAsyncArrowHead
*is_async = true;
@@ -1609,28 +1754,28 @@
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);
+ IdentifierT name = ParseAndClassifyIdentifier(CHECK_OK);
+ return impl()->ExpressionFromIdentifier(name, beg_pos,
+ scanner()->location().end_pos);
}
case Token::STRING: {
- BindingPatternUnexpectedToken(classifier);
+ BindingPatternUnexpectedToken();
Consume(Token::STRING);
- return this->ExpressionFromString(beg_pos, scanner(), factory());
+ return impl()->ExpressionFromString(beg_pos);
}
case Token::ASSIGN_DIV:
case Token::DIV:
- classifier->RecordBindingPatternError(
+ classifier()->RecordBindingPatternError(
scanner()->peek_location(), MessageTemplate::kUnexpectedTokenRegExp);
- return this->ParseRegExpLiteral(ok);
+ return ParseRegExpLiteral(ok);
case Token::LBRACK:
- return this->ParseArrayLiteral(classifier, ok);
+ return ParseArrayLiteral(ok);
case Token::LBRACE:
- return this->ParseObjectLiteral(classifier, ok);
+ return ParseObjectLiteral(ok);
case Token::LPAREN: {
// Arrow function formal parameters are either a single identifier or a
@@ -1638,61 +1783,34 @@
// 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.
- bool pattern_error = !classifier->is_valid_binding_pattern();
- classifier->RecordPatternError(scanner()->peek_location(),
- MessageTemplate::kUnexpectedToken,
- Token::String(Token::LPAREN));
- if (pattern_error) ArrowFormalParametersUnexpectedToken(classifier);
+ bool pattern_error = !classifier()->is_valid_binding_pattern();
+ classifier()->RecordPatternError(scanner()->peek_location(),
+ MessageTemplate::kUnexpectedToken,
+ Token::String(Token::LPAREN));
+ if (pattern_error) ArrowFormalParametersUnexpectedToken();
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()->RecordExpressionError(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();
- int expr_pos = peek_position();
- classifier->RecordExpressionError(scanner()->location(),
- MessageTemplate::kUnexpectedToken,
- Token::String(Token::ELLIPSIS));
- classifier->RecordNonSimpleParameter();
- ExpressionClassifier binding_classifier(this);
- ExpressionT expr = this->ParseAssignmentExpression(
- true, &binding_classifier, CHECK_OK);
- classifier->Accumulate(&binding_classifier,
- ExpressionClassifier::AllProductions);
- if (!this->IsIdentifier(expr) && !IsValidPattern(expr)) {
- classifier->RecordArrowFormalParametersError(
- Scanner::Location(ellipsis_pos, scanner()->location().end_pos),
- MessageTemplate::kInvalidRestParameter);
- }
- 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, expr_pos);
}
// Heuristically try to detect immediately called functions before
// seeing the call parentheses.
function_state_->set_next_function_is_parenthesized(peek() ==
Token::FUNCTION);
- ExpressionT expr = this->ParseExpression(true, classifier, CHECK_OK);
+ ExpressionT expr = ParseExpressionCoverGrammar(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
return expr;
}
case Token::CLASS: {
- BindingPatternUnexpectedToken(classifier);
+ BindingPatternUnexpectedToken();
Consume(Token::CLASS);
- int class_token_position = position();
- IdentifierT name = this->EmptyIdentifier();
+ int class_token_pos = position();
+ IdentifierT name = impl()->EmptyIdentifier();
bool is_strict_reserved_name = false;
Scanner::Location class_name_location = Scanner::Location::invalid();
if (peek_any_identifier()) {
@@ -1700,28 +1818,26 @@
CHECK_OK);
class_name_location = scanner()->location();
}
- return impl()->ParseClassLiteral(classifier, name, class_name_location,
- is_strict_reserved_name,
- class_token_position, ok);
+ return ParseClassLiteral(name, class_name_location,
+ is_strict_reserved_name, class_token_pos, ok);
}
case Token::TEMPLATE_SPAN:
case Token::TEMPLATE_TAIL:
- BindingPatternUnexpectedToken(classifier);
- return this->ParseTemplateLiteral(Traits::NoTemplateTag(), beg_pos,
- classifier, ok);
+ BindingPatternUnexpectedToken();
+ return ParseTemplateLiteral(impl()->NoTemplateTag(), beg_pos, ok);
case Token::MOD:
if (allow_natives() || extension_ != NULL) {
- BindingPatternUnexpectedToken(classifier);
- return impl()->ParseV8Intrinsic(ok);
+ BindingPatternUnexpectedToken();
+ return ParseV8Intrinsic(ok);
}
break;
case Token::DO:
if (allow_harmony_do_expressions()) {
- BindingPatternUnexpectedToken(classifier);
- return impl()->ParseDoExpression(ok);
+ BindingPatternUnexpectedToken();
+ return ParseDoExpression(ok);
}
break;
@@ -1731,78 +1847,71 @@
ReportUnexpectedToken(Next());
*ok = false;
- return this->EmptyExpression();
+ return impl()->EmptyExpression();
}
template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseExpression(
bool accept_IN, bool* ok) {
ExpressionClassifier classifier(this);
- ExpressionT result = ParseExpression(accept_IN, &classifier, CHECK_OK);
- impl()->RewriteNonPattern(&classifier, CHECK_OK);
+ ExpressionT result = ParseExpressionCoverGrammar(accept_IN, CHECK_OK);
+ impl()->RewriteNonPattern(CHECK_OK);
return result;
}
template <typename Impl>
-typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseExpression(
- bool accept_IN, ExpressionClassifier* classifier, bool* ok) {
+typename ParserBase<Impl>::ExpressionT
+ParserBase<Impl>::ParseExpressionCoverGrammar(bool accept_IN, bool* ok) {
// Expression ::
// AssignmentExpression
// Expression ',' AssignmentExpression
- ExpressionT result;
- {
+ ExpressionT result = impl()->EmptyExpression();
+ while (true) {
+ int comma_pos = position();
ExpressionClassifier binding_classifier(this);
- result = this->ParseAssignmentExpression(accept_IN, &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) {
- CheckNoTailCallExpressions(classifier, CHECK_OK);
- 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);
+ ExpressionT right;
+ if (Check(Token::ELLIPSIS)) {
+ // 'x, y, ...z' in CoverParenthesizedExpressionAndArrowParameterList only
+ // as the formal parameters of'(x, y, ...z) => foo', and is not itself a
+ // valid expression.
+ classifier()->RecordExpressionError(scanner()->location(),
+ MessageTemplate::kUnexpectedToken,
+ Token::String(Token::ELLIPSIS));
+ int ellipsis_pos = position();
+ int pattern_pos = peek_position();
+ ExpressionT pattern = ParsePrimaryExpression(CHECK_OK);
+ ValidateBindingPattern(CHECK_OK);
+ right = factory()->NewSpread(pattern, ellipsis_pos, pattern_pos);
+ } else {
+ right = ParseAssignmentExpression(accept_IN, CHECK_OK);
}
- Consume(Token::COMMA);
- bool is_rest = false;
+ // No need to accumulate binding pattern-related errors, since
+ // an Expression can't be a binding pattern anyway.
+ impl()->Accumulate(ExpressionClassifier::AllProductions &
+ ~(ExpressionClassifier::BindingPatternProduction |
+ ExpressionClassifier::LetPatternProduction));
+ if (!impl()->IsIdentifier(right)) classifier()->RecordNonSimpleParameter();
+ if (impl()->IsEmptyExpression(result)) {
+ // First time through the loop.
+ result = right;
+ } else {
+ result =
+ factory()->NewBinaryOperation(Token::COMMA, result, right, comma_pos);
+ }
+
+ if (!Check(Token::COMMA)) break;
+
+ if (right->IsSpread()) {
+ classifier()->RecordArrowFormalParametersError(
+ scanner()->location(), MessageTemplate::kParamAfterRest);
+ }
+
if (allow_harmony_trailing_commas() && peek() == Token::RPAREN &&
PeekAhead() == Token::ARROW) {
// a trailing comma is allowed at the end of an arrow parameter list
break;
- } else 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(), expr_pos = peek_position();
- ExpressionClassifier binding_classifier(this);
- ExpressionT right = this->ParseAssignmentExpression(
- accept_IN, &binding_classifier, CHECK_OK);
- classifier->Accumulate(&binding_classifier,
- ExpressionClassifier::AllProductions);
- if (is_rest) {
- if (!this->IsIdentifier(right) && !IsValidPattern(right)) {
- classifier->RecordArrowFormalParametersError(
- Scanner::Location(pos, scanner()->location().end_pos),
- MessageTemplate::kInvalidRestParameter);
- }
- right = factory()->NewSpread(right, pos, expr_pos);
- }
- is_simple_parameter_list =
- is_simple_parameter_list && this->IsIdentifier(right);
- result = factory()->NewBinaryOperation(Token::COMMA, result, right, pos);
- }
- if (!is_simple_parameter_list || seen_rest) {
- classifier->RecordNonSimpleParameter();
}
return result;
@@ -1810,26 +1919,23 @@
template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseArrayLiteral(
- ExpressionClassifier* classifier, bool* ok) {
+ bool* ok) {
// ArrayLiteral ::
// '[' Expression? (',' Expression?)* ']'
int pos = peek_position();
- typename Traits::Type::ExpressionList values =
- this->NewExpressionList(4, zone_);
+ ExpressionListT values = impl()->NewExpressionList(4);
int first_spread_index = -1;
Expect(Token::LBRACK, CHECK_OK);
while (peek() != Token::RBRACK) {
ExpressionT elem;
if (peek() == Token::COMMA) {
- elem = this->GetLiteralTheHole(peek_position(), factory());
+ elem = impl()->GetLiteralTheHole(peek_position());
} else if (peek() == Token::ELLIPSIS) {
int start_pos = peek_position();
Consume(Token::ELLIPSIS);
int expr_pos = peek_position();
- ExpressionT argument =
- this->ParseAssignmentExpression(true, classifier, CHECK_OK);
- CheckNoTailCallExpressions(classifier, CHECK_OK);
+ ExpressionT argument = ParseAssignmentExpression(true, CHECK_OK);
elem = factory()->NewSpread(argument, start_pos, expr_pos);
if (first_spread_index < 0) {
@@ -1837,25 +1943,23 @@
}
if (argument->IsAssignment()) {
- classifier->RecordPatternError(
+ classifier()->RecordPatternError(
Scanner::Location(start_pos, scanner()->location().end_pos),
MessageTemplate::kInvalidDestructuringTarget);
} else {
- CheckDestructuringElement(argument, classifier, start_pos,
+ CheckDestructuringElement(argument, start_pos,
scanner()->location().end_pos);
}
if (peek() == Token::COMMA) {
- classifier->RecordPatternError(
+ classifier()->RecordPatternError(
Scanner::Location(start_pos, scanner()->location().end_pos),
MessageTemplate::kElementAfterRest);
}
} else {
int beg_pos = peek_position();
- elem = this->ParseAssignmentExpression(true, classifier, CHECK_OK);
- CheckNoTailCallExpressions(classifier, CHECK_OK);
- CheckDestructuringElement(elem, classifier, beg_pos,
- scanner()->location().end_pos);
+ elem = ParseAssignmentExpression(true, CHECK_OK);
+ CheckDestructuringElement(elem, beg_pos, scanner()->location().end_pos);
}
values->Add(elem, zone_);
if (peek() != Token::RBRACK) {
@@ -1878,19 +1982,87 @@
// to change. Also, this error message will never appear while pre-
// parsing (this is OK, as it is an implementation limitation).
ReportMessage(MessageTemplate::kTooManySpreads);
- return this->EmptyExpression();
+ return impl()->EmptyExpression();
}
}
return result;
}
template <class Impl>
+bool ParserBase<Impl>::SetPropertyKindFromToken(Token::Value token,
+ PropertyKind* kind) {
+ // This returns true, setting the property kind, iff the given token is one
+ // which must occur after a property name, indicating that the previous token
+ // was in fact a name and not a modifier (like the "get" in "get x").
+ switch (token) {
+ case Token::COLON:
+ *kind = PropertyKind::kValueProperty;
+ return true;
+ case Token::COMMA:
+ case Token::RBRACE:
+ case Token::ASSIGN:
+ *kind = PropertyKind::kShorthandProperty;
+ return true;
+ case Token::LPAREN:
+ *kind = PropertyKind::kMethodProperty;
+ return true;
+ case Token::MUL:
+ case Token::SEMICOLON:
+ *kind = PropertyKind::kClassField;
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+template <class Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePropertyName(
- IdentifierT* name, bool* is_get, bool* is_set, bool* is_computed_name,
- ExpressionClassifier* classifier, bool* ok) {
+ IdentifierT* name, PropertyKind* kind, bool* is_generator, bool* is_get,
+ bool* is_set, bool* is_async, bool* is_computed_name, bool* ok) {
+ DCHECK(*kind == PropertyKind::kNotSet);
+ DCHECK(!*is_generator);
+ DCHECK(!*is_get);
+ DCHECK(!*is_set);
+ DCHECK(!*is_async);
+ DCHECK(!*is_computed_name);
+
+ *is_generator = Check(Token::MUL);
+ if (*is_generator) {
+ *kind = PropertyKind::kMethodProperty;
+ }
+
Token::Value token = peek();
int pos = peek_position();
+ if (allow_harmony_async_await() && !*is_generator && token == Token::ASYNC &&
+ !scanner()->HasAnyLineTerminatorAfterNext()) {
+ Consume(Token::ASYNC);
+ token = peek();
+ if (SetPropertyKindFromToken(token, kind)) {
+ *name = impl()->GetSymbol(); // TODO(bakkot) specialize on 'async'
+ impl()->PushLiteralName(*name);
+ return factory()->NewStringLiteral(*name, pos);
+ }
+ *kind = PropertyKind::kMethodProperty;
+ *is_async = true;
+ pos = peek_position();
+ }
+
+ if (token == Token::IDENTIFIER && !*is_generator && !*is_async) {
+ // This is checking for 'get' and 'set' in particular.
+ Consume(Token::IDENTIFIER);
+ token = peek();
+ if (SetPropertyKindFromToken(token, kind) ||
+ !scanner()->IsGetOrSet(is_get, is_set)) {
+ *name = impl()->GetSymbol();
+ impl()->PushLiteralName(*name);
+ return factory()->NewStringLiteral(*name, pos);
+ }
+ *kind = PropertyKind::kAccessorProperty;
+ pos = peek_position();
+ }
+
// For non computed property names we normalize the name a bit:
//
// "12" -> 12
@@ -1900,274 +2072,417 @@
//
// This is important because we use the property name as a key in a hash
// table when we compute constant properties.
+ ExpressionT expression = impl()->EmptyExpression();
switch (token) {
case Token::STRING:
Consume(Token::STRING);
- *name = this->GetSymbol(scanner());
+ *name = impl()->GetSymbol();
break;
case Token::SMI:
Consume(Token::SMI);
- *name = this->GetNumberAsSymbol(scanner());
+ *name = impl()->GetNumberAsSymbol();
break;
case Token::NUMBER:
Consume(Token::NUMBER);
- *name = this->GetNumberAsSymbol(scanner());
+ *name = impl()->GetNumberAsSymbol();
break;
case Token::LBRACK: {
+ *name = impl()->EmptyIdentifier();
*is_computed_name = true;
Consume(Token::LBRACK);
ExpressionClassifier computed_name_classifier(this);
- ExpressionT expression =
- ParseAssignmentExpression(true, &computed_name_classifier, CHECK_OK);
- impl()->RewriteNonPattern(&computed_name_classifier, CHECK_OK);
- classifier->Accumulate(&computed_name_classifier,
- ExpressionClassifier::ExpressionProductions);
+ expression = ParseAssignmentExpression(true, CHECK_OK);
+ impl()->RewriteNonPattern(CHECK_OK);
+ impl()->AccumulateFormalParameterContainmentErrors();
Expect(Token::RBRACK, CHECK_OK);
- return expression;
+ break;
}
default:
*name = ParseIdentifierName(CHECK_OK);
- scanner()->IsGetOrSet(is_get, is_set);
break;
}
+ if (*kind == PropertyKind::kNotSet) {
+ SetPropertyKindFromToken(peek(), kind);
+ }
+
+ if (*is_computed_name) {
+ return expression;
+ }
+
+ impl()->PushLiteralName(*name);
+
uint32_t index;
- return this->IsArrayIndex(*name, &index)
+ return impl()->IsArrayIndex(*name, &index)
? factory()->NewNumberLiteral(index, pos)
: factory()->NewStringLiteral(*name, pos);
}
template <typename Impl>
-typename ParserBase<Impl>::ObjectLiteralPropertyT
-ParserBase<Impl>::ParsePropertyDefinition(
- ObjectLiteralCheckerBase* checker, bool in_class, bool has_extends,
- MethodKind method_kind, bool* is_computed_name, bool* has_seen_constructor,
- ExpressionClassifier* classifier, IdentifierT* name, bool* ok) {
- DCHECK(!in_class || IsStaticMethod(method_kind) ||
- has_seen_constructor != nullptr);
+typename ParserBase<Impl>::ClassLiteralPropertyT
+ParserBase<Impl>::ParseClassPropertyDefinition(ClassLiteralChecker* checker,
+ bool has_extends,
+ bool* is_computed_name,
+ bool* has_seen_constructor,
+ bool* ok) {
+ DCHECK(has_seen_constructor != nullptr);
bool is_get = false;
bool is_set = false;
- bool is_generator = Check(Token::MUL);
+ bool is_generator = false;
bool is_async = false;
- const bool is_static = IsStaticMethod(method_kind);
+ bool is_static = false;
+ PropertyKind kind = PropertyKind::kNotSet;
Token::Value name_token = peek();
- if (is_generator) {
- method_kind |= MethodKind::kGenerator;
- } else if (allow_harmony_async_await() && name_token == Token::ASYNC &&
- !scanner()->HasAnyLineTerminatorAfterNext() &&
- PeekAhead() != Token::LPAREN && PeekAhead()) {
- is_async = true;
+ IdentifierT name = impl()->EmptyIdentifier();
+ ExpressionT name_expression;
+ if (name_token == Token::STATIC) {
+ Consume(Token::STATIC);
+ if (peek() == Token::LPAREN) {
+ kind = PropertyKind::kMethodProperty;
+ name = impl()->GetSymbol(); // TODO(bakkot) specialize on 'static'
+ name_expression = factory()->NewStringLiteral(name, position());
+ } else if (peek() == Token::ASSIGN || peek() == Token::SEMICOLON ||
+ peek() == Token::RBRACE) {
+ name = impl()->GetSymbol(); // TODO(bakkot) specialize on 'static'
+ name_expression = factory()->NewStringLiteral(name, position());
+ } else {
+ is_static = true;
+ name_expression = ParsePropertyName(
+ &name, &kind, &is_generator, &is_get, &is_set, &is_async,
+ is_computed_name, CHECK_OK_CUSTOM(EmptyClassLiteralProperty));
+ }
+ } else {
+ name_expression = ParsePropertyName(
+ &name, &kind, &is_generator, &is_get, &is_set, &is_async,
+ is_computed_name, CHECK_OK_CUSTOM(EmptyClassLiteralProperty));
}
+ switch (kind) {
+ case PropertyKind::kClassField:
+ case PropertyKind::kNotSet: // This case is a name followed by a name or
+ // other property. Here we have to assume
+ // that's an uninitialized field followed by a
+ // linebreak followed by a property, with ASI
+ // adding the semicolon. If not, there will be
+ // a syntax error after parsing the first name
+ // as an uninitialized field.
+ case PropertyKind::kShorthandProperty:
+ case PropertyKind::kValueProperty:
+ if (allow_harmony_class_fields()) {
+ bool has_initializer = Check(Token::ASSIGN);
+ ExpressionT function_literal = ParseClassFieldForInitializer(
+ has_initializer, CHECK_OK_CUSTOM(EmptyClassLiteralProperty));
+ ExpectSemicolon(CHECK_OK_CUSTOM(EmptyClassLiteralProperty));
+ return factory()->NewClassLiteralProperty(
+ name_expression, function_literal, ClassLiteralProperty::FIELD,
+ is_static, *is_computed_name);
+ } else {
+ ReportUnexpectedToken(Next());
+ *ok = false;
+ return impl()->EmptyClassLiteralProperty();
+ }
+
+ case PropertyKind::kMethodProperty: {
+ DCHECK(!is_get && !is_set);
+
+ // MethodDefinition
+ // PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}'
+ // '*' PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}'
+
+ if (!*is_computed_name) {
+ checker->CheckClassMethodName(
+ name_token, PropertyKind::kMethodProperty, is_generator, is_async,
+ is_static, CHECK_OK_CUSTOM(EmptyClassLiteralProperty));
+ }
+
+ FunctionKind kind = is_generator
+ ? FunctionKind::kConciseGeneratorMethod
+ : is_async ? FunctionKind::kAsyncConciseMethod
+ : FunctionKind::kConciseMethod;
+
+ if (!is_static && impl()->IsConstructor(name)) {
+ *has_seen_constructor = true;
+ kind = has_extends ? FunctionKind::kSubclassConstructor
+ : FunctionKind::kBaseConstructor;
+ }
+
+ ExpressionT value = impl()->ParseFunctionLiteral(
+ name, scanner()->location(), kSkipFunctionNameCheck, kind,
+ kNoSourcePosition, FunctionLiteral::kAccessorOrMethod,
+ language_mode(), CHECK_OK_CUSTOM(EmptyClassLiteralProperty));
+
+ return factory()->NewClassLiteralProperty(name_expression, value,
+ ClassLiteralProperty::METHOD,
+ is_static, *is_computed_name);
+ }
+
+ case PropertyKind::kAccessorProperty: {
+ DCHECK((is_get || is_set) && !is_generator && !is_async);
+
+ if (!*is_computed_name) {
+ checker->CheckClassMethodName(
+ name_token, PropertyKind::kAccessorProperty, false, false,
+ is_static, CHECK_OK_CUSTOM(EmptyClassLiteralProperty));
+ // 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.
+ name_expression =
+ factory()->NewStringLiteral(name, name_expression->position());
+ }
+
+ FunctionKind kind = is_get ? FunctionKind::kGetterFunction
+ : FunctionKind::kSetterFunction;
+
+ FunctionLiteralT value = impl()->ParseFunctionLiteral(
+ name, scanner()->location(), kSkipFunctionNameCheck, kind,
+ kNoSourcePosition, FunctionLiteral::kAccessorOrMethod,
+ language_mode(), CHECK_OK_CUSTOM(EmptyClassLiteralProperty));
+
+ if (!*is_computed_name) {
+ impl()->AddAccessorPrefixToFunctionName(is_get, value, name);
+ }
+
+ return factory()->NewClassLiteralProperty(
+ name_expression, value,
+ is_get ? ClassLiteralProperty::GETTER : ClassLiteralProperty::SETTER,
+ is_static, *is_computed_name);
+ }
+ }
+ UNREACHABLE();
+ return impl()->EmptyClassLiteralProperty();
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::FunctionLiteralT
+ParserBase<Impl>::ParseClassFieldForInitializer(bool has_initializer,
+ bool* ok) {
+ // Makes a concise method which evaluates and returns the initialized value
+ // (or undefined if absent).
+ FunctionKind kind = FunctionKind::kConciseMethod;
+ DeclarationScope* initializer_scope = NewFunctionScope(kind);
+ initializer_scope->set_start_position(scanner()->location().end_pos);
+ FunctionState initializer_state(&function_state_, &scope_state_,
+ initializer_scope);
+ DCHECK(scope() == initializer_scope);
+ scope()->SetLanguageMode(STRICT);
+ ExpressionClassifier expression_classifier(this);
+ ExpressionT value;
+ if (has_initializer) {
+ value = this->ParseAssignmentExpression(
+ true, CHECK_OK_CUSTOM(EmptyFunctionLiteral));
+ impl()->RewriteNonPattern(CHECK_OK_CUSTOM(EmptyFunctionLiteral));
+ } else {
+ value = factory()->NewUndefinedLiteral(kNoSourcePosition);
+ }
+ initializer_scope->set_end_position(scanner()->location().end_pos);
+ typename Types::StatementList body = impl()->NewStatementList(1);
+ body->Add(factory()->NewReturnStatement(value, kNoSourcePosition), zone());
+ FunctionLiteralT function_literal = factory()->NewFunctionLiteral(
+ impl()->EmptyIdentifierString(), initializer_scope, body,
+ initializer_state.materialized_literal_count(),
+ initializer_state.expected_property_count(), 0,
+ FunctionLiteral::kNoDuplicateParameters,
+ FunctionLiteral::kAnonymousExpression,
+ FunctionLiteral::kShouldLazyCompile, initializer_scope->start_position());
+ function_literal->set_is_class_field_initializer(true);
+ return function_literal;
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::ObjectLiteralPropertyT
+ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker,
+ bool* is_computed_name,
+ bool* ok) {
+ bool is_get = false;
+ bool is_set = false;
+ bool is_generator = false;
+ bool is_async = false;
+ PropertyKind kind = PropertyKind::kNotSet;
+
+ IdentifierT name = impl()->EmptyIdentifier();
+ Token::Value name_token = peek();
int next_beg_pos = scanner()->peek_location().beg_pos;
int next_end_pos = scanner()->peek_location().end_pos;
- ExpressionT name_expression =
- ParsePropertyName(name, &is_get, &is_set, is_computed_name, classifier,
- CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
- if (fni_ != nullptr && !*is_computed_name) {
- this->PushLiteralName(fni_, *name);
- }
+ ExpressionT name_expression = ParsePropertyName(
+ &name, &kind, &is_generator, &is_get, &is_set, &is_async,
+ is_computed_name, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
- if (!in_class && !is_generator) {
- DCHECK(!IsStaticMethod(method_kind));
- if (peek() == Token::COLON) {
- // PropertyDefinition
- // PropertyName ':' AssignmentExpression
+ switch (kind) {
+ case PropertyKind::kValueProperty: {
+ DCHECK(!is_get && !is_set && !is_generator && !is_async);
+
if (!*is_computed_name) {
- checker->CheckProperty(name_token, kValueProperty, MethodKind::kNormal,
- classifier,
- CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
+ checker->CheckDuplicateProto(name_token);
}
Consume(Token::COLON);
int beg_pos = peek_position();
- ExpressionT value = this->ParseAssignmentExpression(
- true, classifier, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
- CheckDestructuringElement(value, classifier, beg_pos,
- scanner()->location().end_pos);
+ ExpressionT value = ParseAssignmentExpression(
+ true, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
+ CheckDestructuringElement(value, beg_pos, scanner()->location().end_pos);
- return factory()->NewObjectLiteralProperty(name_expression, value,
- is_static, *is_computed_name);
+ ObjectLiteralPropertyT result = factory()->NewObjectLiteralProperty(
+ name_expression, value, *is_computed_name);
+
+ if (!*is_computed_name) {
+ impl()->SetFunctionNameFromPropertyName(result, name);
+ }
+
+ return result;
}
- if (Token::IsIdentifier(name_token, language_mode(), this->is_generator(),
- parsing_module_ || is_async_function()) &&
- (peek() == Token::COMMA || peek() == Token::RBRACE ||
- peek() == Token::ASSIGN)) {
+ case PropertyKind::kShorthandProperty: {
// PropertyDefinition
// IdentifierReference
// CoverInitializedName
//
// CoverInitializedName
// IdentifierReference Initializer?
- if (classifier->duplicate_finder() != nullptr &&
- scanner()->FindSymbol(classifier->duplicate_finder(), 1) != 0) {
- classifier->RecordDuplicateFormalParameterError(scanner()->location());
+ DCHECK(!is_get && !is_set && !is_generator && !is_async);
+
+ if (!Token::IsIdentifier(name_token, language_mode(),
+ this->is_generator(),
+ parsing_module_ || is_async_function())) {
+ ReportUnexpectedToken(Next());
+ *ok = false;
+ return impl()->EmptyObjectLiteralProperty();
}
- if (this->IsEvalOrArguments(*name) && is_strict(language_mode())) {
- classifier->RecordBindingPatternError(
+ DCHECK(!*is_computed_name);
+
+ if (classifier()->duplicate_finder() != nullptr &&
+ scanner()->FindSymbol(classifier()->duplicate_finder(), 1) != 0) {
+ classifier()->RecordDuplicateFormalParameterError(
+ scanner()->location());
+ }
+
+ if (impl()->IsEvalOrArguments(name) && is_strict(language_mode())) {
+ classifier()->RecordBindingPatternError(
scanner()->location(), MessageTemplate::kStrictEvalArguments);
}
if (name_token == Token::LET) {
- classifier->RecordLetPatternError(
+ classifier()->RecordLetPatternError(
scanner()->location(), MessageTemplate::kLetInLexicalBinding);
}
if (name_token == Token::AWAIT) {
DCHECK(!is_async_function());
- classifier->RecordAsyncArrowFormalParametersError(
+ classifier()->RecordAsyncArrowFormalParametersError(
Scanner::Location(next_beg_pos, next_end_pos),
MessageTemplate::kAwaitBindingIdentifier);
}
ExpressionT lhs =
- this->ExpressionFromIdentifier(*name, next_beg_pos, next_end_pos);
- CheckDestructuringElement(lhs, classifier, next_beg_pos, next_end_pos);
+ impl()->ExpressionFromIdentifier(name, next_beg_pos, next_end_pos);
+ CheckDestructuringElement(lhs, next_beg_pos, next_end_pos);
ExpressionT value;
if (peek() == Token::ASSIGN) {
Consume(Token::ASSIGN);
ExpressionClassifier rhs_classifier(this);
- ExpressionT rhs = this->ParseAssignmentExpression(
- true, &rhs_classifier, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
- impl()->RewriteNonPattern(&rhs_classifier,
- CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
- classifier->Accumulate(&rhs_classifier,
- ExpressionClassifier::ExpressionProductions);
+ ExpressionT rhs = ParseAssignmentExpression(
+ true, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
+ impl()->RewriteNonPattern(CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
+ impl()->AccumulateFormalParameterContainmentErrors();
value = factory()->NewAssignment(Token::ASSIGN, lhs, rhs,
kNoSourcePosition);
- classifier->RecordObjectLiteralError(
+ classifier()->RecordExpressionError(
Scanner::Location(next_beg_pos, scanner()->location().end_pos),
MessageTemplate::kInvalidCoverInitializedName);
- Traits::SetFunctionNameFromIdentifierRef(rhs, lhs);
+ impl()->SetFunctionNameFromIdentifierRef(rhs, lhs);
} else {
value = lhs;
}
return factory()->NewObjectLiteralProperty(
- name_expression, value, ObjectLiteralProperty::COMPUTED, is_static,
- false);
- }
- }
-
- // Method definitions are never valid in patterns.
- classifier->RecordPatternError(
- Scanner::Location(next_beg_pos, scanner()->location().end_pos),
- MessageTemplate::kInvalidDestructuringTarget);
-
- if (is_async && !IsSpecialMethod(method_kind)) {
- DCHECK(!is_get);
- DCHECK(!is_set);
- bool dont_care;
- name_expression = ParsePropertyName(
- name, &dont_care, &dont_care, is_computed_name, classifier,
- CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
- method_kind |= MethodKind::kAsync;
- }
-
- if (is_generator || peek() == Token::LPAREN) {
- // MethodDefinition
- // PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}'
- // '*' PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}'
- if (!*is_computed_name) {
- checker->CheckProperty(name_token, kMethodProperty, method_kind,
- classifier,
- CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
+ name_expression, value, ObjectLiteralProperty::COMPUTED, false);
}
- FunctionKind kind = is_generator
- ? FunctionKind::kConciseGeneratorMethod
- : is_async ? FunctionKind::kAsyncConciseMethod
- : FunctionKind::kConciseMethod;
+ case PropertyKind::kMethodProperty: {
+ DCHECK(!is_get && !is_set);
- if (in_class && !IsStaticMethod(method_kind) &&
- this->IsConstructor(*name)) {
- *has_seen_constructor = true;
- kind = has_extends ? FunctionKind::kSubclassConstructor
- : FunctionKind::kBaseConstructor;
+ // MethodDefinition
+ // PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}'
+ // '*' PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}'
+
+ classifier()->RecordPatternError(
+ Scanner::Location(next_beg_pos, scanner()->location().end_pos),
+ MessageTemplate::kInvalidDestructuringTarget);
+
+ FunctionKind kind = is_generator
+ ? FunctionKind::kConciseGeneratorMethod
+ : is_async ? FunctionKind::kAsyncConciseMethod
+ : FunctionKind::kConciseMethod;
+
+ ExpressionT value = impl()->ParseFunctionLiteral(
+ name, scanner()->location(), kSkipFunctionNameCheck, kind,
+ kNoSourcePosition, FunctionLiteral::kAccessorOrMethod,
+ language_mode(), CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
+
+ return factory()->NewObjectLiteralProperty(
+ name_expression, value, ObjectLiteralProperty::COMPUTED,
+ *is_computed_name);
}
- ExpressionT value = impl()->ParseFunctionLiteral(
- *name, scanner()->location(), kSkipFunctionNameCheck, kind,
- kNoSourcePosition, FunctionLiteral::kAccessorOrMethod, language_mode(),
- CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
+ case PropertyKind::kAccessorProperty: {
+ DCHECK((is_get || is_set) && !(is_set && is_get) && !is_generator &&
+ !is_async);
- return factory()->NewObjectLiteralProperty(name_expression, value,
- ObjectLiteralProperty::COMPUTED,
- is_static, *is_computed_name);
- }
+ classifier()->RecordPatternError(
+ Scanner::Location(next_beg_pos, scanner()->location().end_pos),
+ MessageTemplate::kInvalidDestructuringTarget);
- if (in_class && name_token == Token::STATIC && IsNormalMethod(method_kind)) {
- // ClassElement (static)
- // 'static' MethodDefinition
- *name = this->EmptyIdentifier();
- ObjectLiteralPropertyT property = ParsePropertyDefinition(
- checker, true, has_extends, MethodKind::kStatic, is_computed_name,
- nullptr, classifier, name, ok);
- impl()->RewriteNonPattern(classifier, ok);
- return property;
- }
+ if (!*is_computed_name) {
+ // 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.
+ name_expression =
+ factory()->NewStringLiteral(name, name_expression->position());
+ }
- if (is_get || is_set) {
- // MethodDefinition (Accessors)
- // get PropertyName '(' ')' '{' FunctionBody '}'
- // set PropertyName '(' PropertySetParameterList ')' '{' FunctionBody '}'
- *name = this->EmptyIdentifier();
- bool dont_care = false;
- name_token = peek();
+ FunctionKind kind = is_get ? FunctionKind::kGetterFunction
+ : FunctionKind::kSetterFunction;
- name_expression = ParsePropertyName(
- name, &dont_care, &dont_care, is_computed_name, classifier,
- CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
+ FunctionLiteralT value = impl()->ParseFunctionLiteral(
+ name, scanner()->location(), kSkipFunctionNameCheck, kind,
+ kNoSourcePosition, FunctionLiteral::kAccessorOrMethod,
+ language_mode(), CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
- if (!*is_computed_name) {
- checker->CheckProperty(name_token, kAccessorProperty, method_kind,
- classifier,
- CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
+ if (!*is_computed_name) {
+ impl()->AddAccessorPrefixToFunctionName(is_get, value, name);
+ }
+
+ return factory()->NewObjectLiteralProperty(
+ name_expression, value, is_get ? ObjectLiteralProperty::GETTER
+ : ObjectLiteralProperty::SETTER,
+ *is_computed_name);
}
- typename Traits::Type::FunctionLiteral value = impl()->ParseFunctionLiteral(
- *name, scanner()->location(), kSkipFunctionNameCheck,
- is_get ? FunctionKind::kGetterFunction : FunctionKind::kSetterFunction,
- kNoSourcePosition, FunctionLiteral::kAccessorOrMethod, 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);
+ case PropertyKind::kClassField:
+ case PropertyKind::kNotSet:
+ ReportUnexpectedToken(Next());
+ *ok = false;
+ return impl()->EmptyObjectLiteralProperty();
}
-
- Token::Value next = Next();
- ReportUnexpectedToken(next);
- *ok = false;
- return this->EmptyObjectLiteralProperty();
+ UNREACHABLE();
+ return impl()->EmptyObjectLiteralProperty();
}
template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseObjectLiteral(
- ExpressionClassifier* classifier, bool* ok) {
+ bool* ok) {
// ObjectLiteral ::
// '{' (PropertyDefinition (',' PropertyDefinition)* ','? )? '}'
int pos = peek_position();
- typename Traits::Type::PropertyList properties =
- this->NewPropertyList(4, zone_);
+ typename Types::ObjectPropertyList properties =
+ impl()->NewObjectPropertyList(4);
int number_of_boilerplate_properties = 0;
bool has_computed_names = false;
ObjectLiteralChecker checker(this);
@@ -2177,20 +2492,16 @@
while (peek() != Token::RBRACE) {
FuncNameInferrer::State fni_state(fni_);
- const bool in_class = false;
- const bool has_extends = false;
bool is_computed_name = false;
- IdentifierT name = this->EmptyIdentifier();
- ObjectLiteralPropertyT property = this->ParsePropertyDefinition(
- &checker, in_class, has_extends, MethodKind::kNormal, &is_computed_name,
- NULL, classifier, &name, CHECK_OK);
+ ObjectLiteralPropertyT property =
+ ParseObjectPropertyDefinition(&checker, &is_computed_name, CHECK_OK);
if (is_computed_name) {
has_computed_names = true;
}
// Count CONSTANT or COMPUTED properties to maintain the enumeration order.
- if (!has_computed_names && this->IsBoilerplateProperty(property)) {
+ if (!has_computed_names && impl()->IsBoilerplateProperty(property)) {
number_of_boilerplate_properties++;
}
properties->Add(property, zone());
@@ -2201,8 +2512,6 @@
}
if (fni_ != nullptr) fni_->Infer();
-
- Traits::SetFunctionNameFromPropertyName(property, name);
}
Expect(Token::RBRACE, CHECK_OK);
@@ -2216,16 +2525,13 @@
}
template <typename Impl>
-typename ParserBase<Impl>::Traits::Type::ExpressionList
-ParserBase<Impl>::ParseArguments(Scanner::Location* first_spread_arg_loc,
- bool maybe_arrow,
- ExpressionClassifier* classifier, bool* ok) {
+typename ParserBase<Impl>::ExpressionListT ParserBase<Impl>::ParseArguments(
+ Scanner::Location* first_spread_arg_loc, bool maybe_arrow, bool* ok) {
// Arguments ::
// '(' (AssignmentExpression)*[','] ')'
Scanner::Location spread_arg = Scanner::Location::invalid();
- typename Traits::Type::ExpressionList result =
- this->NewExpressionList(4, zone_);
+ ExpressionListT result = impl()->NewExpressionList(4);
Expect(Token::LPAREN, CHECK_OK_CUSTOM(NullExpressionList));
bool done = (peek() == Token::RPAREN);
bool was_unspread = false;
@@ -2235,12 +2541,10 @@
bool is_spread = Check(Token::ELLIPSIS);
int expr_pos = peek_position();
- ExpressionT argument = this->ParseAssignmentExpression(
- true, classifier, CHECK_OK_CUSTOM(NullExpressionList));
- CheckNoTailCallExpressions(classifier, CHECK_OK_CUSTOM(NullExpressionList));
+ ExpressionT argument =
+ ParseAssignmentExpression(true, CHECK_OK_CUSTOM(NullExpressionList));
if (!maybe_arrow) {
- impl()->RewriteNonPattern(classifier,
- CHECK_OK_CUSTOM(NullExpressionList));
+ impl()->RewriteNonPattern(CHECK_OK_CUSTOM(NullExpressionList));
}
if (is_spread) {
if (!spread_arg.IsValid()) {
@@ -2263,7 +2567,7 @@
if (result->length() > Code::kMaxArguments) {
ReportMessage(MessageTemplate::kTooManyArguments);
*ok = false;
- return this->NullExpressionList();
+ return impl()->NullExpressionList();
}
done = (peek() != Token::COMMA);
if (!done) {
@@ -2276,22 +2580,21 @@
}
Scanner::Location location = scanner_->location();
if (Token::RPAREN != Next()) {
- ReportMessageAt(location, MessageTemplate::kUnterminatedArgList);
+ impl()->ReportMessageAt(location, MessageTemplate::kUnterminatedArgList);
*ok = false;
- return this->NullExpressionList();
+ return impl()->NullExpressionList();
}
*first_spread_arg_loc = spread_arg;
if (!maybe_arrow || peek() != Token::ARROW) {
if (maybe_arrow) {
- impl()->RewriteNonPattern(classifier,
- CHECK_OK_CUSTOM(NullExpressionList));
+ impl()->RewriteNonPattern(CHECK_OK_CUSTOM(NullExpressionList));
}
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);
+ impl()->MaterializeUnspreadArgumentsLiterals(unspread_sequences_count);
}
}
@@ -2301,9 +2604,7 @@
// Precedence = 2
template <typename Impl>
typename ParserBase<Impl>::ExpressionT
-ParserBase<Impl>::ParseAssignmentExpression(bool accept_IN,
- ExpressionClassifier* classifier,
- bool* ok) {
+ParserBase<Impl>::ParseAssignmentExpression(bool accept_IN, bool* ok) {
// AssignmentExpression ::
// ConditionalExpression
// ArrowFunction
@@ -2312,13 +2613,13 @@
int lhs_beg_pos = peek_position();
if (peek() == Token::YIELD && is_generator()) {
- return this->ParseYieldExpression(accept_IN, classifier, ok);
+ return ParseYieldExpression(accept_IN, ok);
}
FuncNameInferrer::State fni_state(fni_);
Checkpoint checkpoint(this);
- ExpressionClassifier arrow_formals_classifier(this,
- classifier->duplicate_finder());
+ ExpressionClassifier arrow_formals_classifier(
+ this, classifier()->duplicate_finder());
Scope::Snapshot scope_snapshot(scope());
@@ -2328,26 +2629,23 @@
bool parenthesized_formals = peek() == Token::LPAREN;
if (!is_async && !parenthesized_formals) {
- ArrowFormalParametersUnexpectedToken(&arrow_formals_classifier);
+ ArrowFormalParametersUnexpectedToken();
}
// Parse a simple, faster sub-grammar (primary expression) if it's evident
// that we have only a trivial expression to parse.
ExpressionT expression;
if (IsTrivialExpression()) {
- expression = this->ParsePrimaryExpression(&arrow_formals_classifier,
- &is_async, CHECK_OK);
+ expression = ParsePrimaryExpression(&is_async, CHECK_OK);
} else {
- expression = this->ParseConditionalExpression(
- accept_IN, &arrow_formals_classifier, CHECK_OK);
+ expression = ParseConditionalExpression(accept_IN, CHECK_OK);
}
- if (is_async && this->IsIdentifier(expression) && peek_any_identifier() &&
+ if (is_async && impl()->IsIdentifier(expression) && peek_any_identifier() &&
PeekAhead() == Token::ARROW) {
// async Identifier => AsyncConciseBody
- IdentifierT name =
- ParseAndClassifyIdentifier(&arrow_formals_classifier, CHECK_OK);
- expression = this->ExpressionFromIdentifier(
+ IdentifierT name = ParseAndClassifyIdentifier(CHECK_OK);
+ expression = impl()->ExpressionFromIdentifier(
name, position(), scanner()->location().end_pos, InferName::kNo);
if (fni_) {
// Remove `async` keyword from inferred name stack.
@@ -2357,26 +2655,29 @@
if (peek() == Token::ARROW) {
Scanner::Location arrow_loc = scanner()->peek_location();
- ValidateArrowFormalParameters(&arrow_formals_classifier, expression,
- parenthesized_formals, is_async, CHECK_OK);
+ ValidateArrowFormalParameters(expression, parenthesized_formals, is_async,
+ CHECK_OK);
// This reads strangely, but is correct: it checks whether any
// sub-expression of the parameter list failed to be a valid formal
// parameter initializer. Since YieldExpressions are banned anywhere
// in an arrow parameter list, this is correct.
// TODO(adamk): Rename "FormalParameterInitializerError" to refer to
// "YieldExpression", which is its only use.
- ValidateFormalParameterInitializer(&arrow_formals_classifier, ok);
+ ValidateFormalParameterInitializer(ok);
Scanner::Location loc(lhs_beg_pos, scanner()->location().end_pos);
DeclarationScope* scope =
- this->NewFunctionScope(is_async ? FunctionKind::kAsyncArrowFunction
- : FunctionKind::kArrowFunction);
+ NewFunctionScope(is_async ? FunctionKind::kAsyncArrowFunction
+ : 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.
this->scope()->PropagateUsageFlagsToScope(scope);
+
+ scope_snapshot.Reparent(scope);
+
FormalParametersT parameters(scope);
- if (!arrow_formals_classifier.is_simple_parameter_list()) {
+ if (!classifier()->is_simple_parameter_list()) {
scope->SetHasNonSimpleParameters();
parameters.is_simple = false;
}
@@ -2385,18 +2686,16 @@
scope->set_start_position(lhs_beg_pos);
Scanner::Location duplicate_loc = Scanner::Location::invalid();
- this->ParseArrowFunctionFormalParameterList(
- ¶meters, expression, loc, &duplicate_loc, scope_snapshot, CHECK_OK);
+ impl()->DeclareArrowFunctionFormalParameters(¶meters, expression, loc,
+ &duplicate_loc, CHECK_OK);
if (duplicate_loc.IsValid()) {
- arrow_formals_classifier.RecordDuplicateFormalParameterError(
- duplicate_loc);
+ classifier()->RecordDuplicateFormalParameterError(duplicate_loc);
}
- expression = this->ParseArrowFunctionLiteral(
- accept_IN, parameters, is_async, arrow_formals_classifier, CHECK_OK);
- arrow_formals_classifier.Discard();
- classifier->RecordPatternError(arrow_loc,
- MessageTemplate::kUnexpectedToken,
- Token::String(Token::ARROW));
+ expression = ParseArrowFunctionLiteral(accept_IN, parameters, CHECK_OK);
+ impl()->Discard();
+ classifier()->RecordPatternError(arrow_loc,
+ MessageTemplate::kUnexpectedToken,
+ Token::String(Token::ARROW));
if (fni_ != nullptr) fni_->Infer();
@@ -2407,87 +2706,70 @@
// form part of one. Propagate speculative formal parameter error locations
// (including those for binding patterns, since formal parameters can
// themselves contain binding patterns).
- // Do not merge pending non-pattern expressions yet!
- unsigned productions =
- ExpressionClassifier::FormalParametersProductions |
- ExpressionClassifier::AsyncArrowFormalParametersProduction |
- ExpressionClassifier::FormalParameterInitializerProduction;
+ unsigned productions = ExpressionClassifier::AllProductions &
+ ~ExpressionClassifier::ArrowFormalParametersProduction;
// Parenthesized identifiers and property references are allowed as part
- // of a larger binding pattern, even though parenthesized patterns
+ // of a larger assignment pattern, even though parenthesized patterns
// themselves are not allowed, e.g., "[(x)] = []". Only accumulate
// assignment pattern errors if the parsed expression is more complex.
- if (this->IsValidReferenceExpression(expression)) {
- productions |= ExpressionClassifier::PatternProductions &
- ~ExpressionClassifier::AssignmentPatternProduction;
- } else {
- productions |= ExpressionClassifier::PatternProductions;
+ if (IsValidReferenceExpression(expression)) {
+ productions &= ~ExpressionClassifier::AssignmentPatternProduction;
}
const bool is_destructuring_assignment =
IsValidPattern(expression) && peek() == Token::ASSIGN;
- if (!is_destructuring_assignment) {
- // This may be an expression or a pattern, so we must continue to
- // accumulate expression-related errors.
- productions |= ExpressionClassifier::ExpressionProduction |
- ExpressionClassifier::TailCallExpressionProduction |
- ExpressionClassifier::ObjectLiteralProduction;
+ if (is_destructuring_assignment) {
+ // This is definitely not an expression so don't accumulate
+ // expression-related errors.
+ productions &= ~(ExpressionClassifier::ExpressionProduction |
+ ExpressionClassifier::TailCallExpressionProduction);
}
- classifier->Accumulate(&arrow_formals_classifier, productions, false);
-
if (!Token::IsAssignmentOp(peek())) {
// Parsed conditional expression only (no assignment).
- // Now pending non-pattern expressions must be merged.
- classifier->MergeNonPatterns(&arrow_formals_classifier);
+ // Pending non-pattern expressions must be merged.
+ impl()->Accumulate(productions);
return expression;
+ } else {
+ // Pending non-pattern expressions must be discarded.
+ impl()->Accumulate(productions, false);
}
- // Now pending non-pattern expressions must be discarded.
- arrow_formals_classifier.Discard();
-
- CheckNoTailCallExpressions(classifier, CHECK_OK);
-
if (is_destructuring_assignment) {
- ValidateAssignmentPattern(classifier, CHECK_OK);
+ ValidateAssignmentPattern(CHECK_OK);
} else {
- expression = this->CheckAndRewriteReferenceExpression(
+ expression = CheckAndRewriteReferenceExpression(
expression, lhs_beg_pos, scanner()->location().end_pos,
MessageTemplate::kInvalidLhsInAssignment, CHECK_OK);
}
- expression = this->MarkExpressionAsAssigned(expression);
+ expression = impl()->MarkExpressionAsAssigned(expression);
Token::Value op = Next(); // Get assignment operator.
if (op != Token::ASSIGN) {
- classifier->RecordPatternError(scanner()->location(),
- MessageTemplate::kUnexpectedToken,
- Token::String(op));
+ classifier()->RecordPatternError(scanner()->location(),
+ MessageTemplate::kUnexpectedToken,
+ Token::String(op));
}
int pos = position();
ExpressionClassifier rhs_classifier(this);
- ExpressionT right =
- this->ParseAssignmentExpression(accept_IN, &rhs_classifier, CHECK_OK);
- CheckNoTailCallExpressions(&rhs_classifier, CHECK_OK);
- impl()->RewriteNonPattern(&rhs_classifier, CHECK_OK);
- classifier->Accumulate(
- &rhs_classifier,
- ExpressionClassifier::ExpressionProductions |
- ExpressionClassifier::ObjectLiteralProduction |
- ExpressionClassifier::AsyncArrowFormalParametersProduction);
+ ExpressionT right = ParseAssignmentExpression(accept_IN, CHECK_OK);
+ impl()->RewriteNonPattern(CHECK_OK);
+ impl()->AccumulateFormalParameterContainmentErrors();
// 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)) {
+ if (op == Token::ASSIGN && impl()->IsThisProperty(expression)) {
function_state_->AddProperty();
}
- this->CheckAssigningFunctionLiteralToProperty(expression, right);
+ impl()->CheckAssigningFunctionLiteralToProperty(expression, right);
if (fni_ != NULL) {
// Check if the right hand side is a call to avoid inferring a
@@ -2502,7 +2784,7 @@
}
if (op == Token::ASSIGN) {
- Traits::SetFunctionNameFromIdentifierRef(right, expression);
+ impl()->SetFunctionNameFromIdentifierRef(right, expression);
}
if (op == Token::ASSIGN_EXP) {
@@ -2522,19 +2804,19 @@
template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseYieldExpression(
- bool accept_IN, ExpressionClassifier* classifier, bool* ok) {
+ bool accept_IN, bool* ok) {
// YieldExpression ::
// 'yield' ([no line terminator] '*'? AssignmentExpression)?
int pos = peek_position();
- classifier->RecordPatternError(scanner()->peek_location(),
- MessageTemplate::kInvalidDestructuringTarget);
- classifier->RecordFormalParameterInitializerError(
+ classifier()->RecordPatternError(
+ scanner()->peek_location(), MessageTemplate::kInvalidDestructuringTarget);
+ classifier()->RecordFormalParameterInitializerError(
scanner()->peek_location(), MessageTemplate::kYieldInParameter);
Expect(Token::YIELD, CHECK_OK);
ExpressionT generator_object =
factory()->NewVariableProxy(function_state_->generator_object_variable());
// The following initialization is necessary.
- ExpressionT expression = Traits::EmptyExpression();
+ ExpressionT expression = impl()->EmptyExpression();
bool delegating = false; // yield*
if (!scanner()->HasAnyLineTerminatorBeforeNext()) {
if (Check(Token::MUL)) delegating = true;
@@ -2553,8 +2835,8 @@
if (!delegating) break;
// Delegating yields require an RHS; fall through.
default:
- expression = ParseAssignmentExpression(accept_IN, classifier, CHECK_OK);
- impl()->RewriteNonPattern(classifier, CHECK_OK);
+ expression = ParseAssignmentExpression(accept_IN, CHECK_OK);
+ impl()->RewriteNonPattern(CHECK_OK);
break;
}
}
@@ -2563,87 +2845,18 @@
return impl()->RewriteYieldStar(generator_object, expression, pos);
}
- expression = Traits::BuildIteratorResult(expression, false);
+ expression = impl()->BuildIteratorResult(expression, false);
// 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, pos, Yield::kOnExceptionThrow);
+ ExpressionT yield = factory()->NewYield(generator_object, expression, pos,
+ Yield::kOnExceptionThrow);
return yield;
}
-template <typename Impl>
-typename ParserBase<Impl>::ExpressionT
-ParserBase<Impl>::ParseTailCallExpression(ExpressionClassifier* classifier,
- bool* ok) {
- // TailCallExpression::
- // 'continue' MemberExpression Arguments
- // 'continue' CallExpression Arguments
- // 'continue' MemberExpression TemplateLiteral
- // 'continue' CallExpression TemplateLiteral
- Expect(Token::CONTINUE, CHECK_OK);
- int pos = position();
- int sub_expression_pos = peek_position();
- ExpressionT expression =
- this->ParseLeftHandSideExpression(classifier, CHECK_OK);
- CheckNoTailCallExpressions(classifier, CHECK_OK);
-
- Scanner::Location loc(pos, scanner()->location().end_pos);
- if (!expression->IsCall()) {
- Scanner::Location sub_loc(sub_expression_pos, loc.end_pos);
- ReportMessageAt(sub_loc, MessageTemplate::kUnexpectedInsideTailCall);
- *ok = false;
- return Traits::EmptyExpression();
- }
- if (Traits::IsDirectEvalCall(expression)) {
- Scanner::Location sub_loc(sub_expression_pos, loc.end_pos);
- ReportMessageAt(sub_loc, MessageTemplate::kUnexpectedTailCallOfEval);
- *ok = false;
- return Traits::EmptyExpression();
- }
- if (!is_strict(language_mode())) {
- ReportMessageAt(loc, MessageTemplate::kUnexpectedSloppyTailCall);
- *ok = false;
- return Traits::EmptyExpression();
- }
- if (is_resumable()) {
- Scanner::Location sub_loc(sub_expression_pos, loc.end_pos);
- ReportMessageAt(sub_loc, MessageTemplate::kUnexpectedTailCall);
- *ok = false;
- return Traits::EmptyExpression();
- }
- ReturnExprContext return_expr_context =
- function_state_->return_expr_context();
- if (return_expr_context != ReturnExprContext::kInsideValidReturnStatement) {
- MessageTemplate::Template msg = MessageTemplate::kNone;
- switch (return_expr_context) {
- case ReturnExprContext::kInsideValidReturnStatement:
- UNREACHABLE();
- return Traits::EmptyExpression();
- case ReturnExprContext::kInsideValidBlock:
- msg = MessageTemplate::kUnexpectedTailCall;
- break;
- case ReturnExprContext::kInsideTryBlock:
- msg = MessageTemplate::kUnexpectedTailCallInTryBlock;
- break;
- case ReturnExprContext::kInsideForInOfBody:
- msg = MessageTemplate::kUnexpectedTailCallInForInOf;
- break;
- }
- ReportMessageAt(loc, msg);
- *ok = false;
- return Traits::EmptyExpression();
- }
- classifier->RecordTailCallExpressionError(
- loc, MessageTemplate::kUnexpectedTailCall);
- function_state_->AddExplicitTailCallExpression(expression, loc);
- return expression;
-}
-
// Precedence = 3
template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseConditionalExpression(bool accept_IN,
- ExpressionClassifier* classifier,
bool* ok) {
// ConditionalExpression ::
// LogicalOrExpression
@@ -2651,23 +2864,20 @@
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);
+ ExpressionT expression = ParseBinaryExpression(4, accept_IN, CHECK_OK);
if (peek() != Token::CONDITIONAL) return expression;
- CheckNoTailCallExpressions(classifier, CHECK_OK);
- impl()->RewriteNonPattern(classifier, CHECK_OK);
- BindingPatternUnexpectedToken(classifier);
- ArrowFormalParametersUnexpectedToken(classifier);
+ impl()->RewriteNonPattern(CHECK_OK);
+ BindingPatternUnexpectedToken();
+ ArrowFormalParametersUnexpectedToken();
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);
- impl()->RewriteNonPattern(classifier, CHECK_OK);
+ ExpressionT left = ParseAssignmentExpression(true, CHECK_OK);
+ impl()->RewriteNonPattern(CHECK_OK);
Expect(Token::COLON, CHECK_OK);
- ExpressionT right =
- ParseAssignmentExpression(accept_IN, classifier, CHECK_OK);
- impl()->RewriteNonPattern(classifier, CHECK_OK);
+ ExpressionT right = ParseAssignmentExpression(accept_IN, CHECK_OK);
+ impl()->RewriteNonPattern(CHECK_OK);
return factory()->NewConditional(expression, left, right, pos);
}
@@ -2675,30 +2885,24 @@
// Precedence >= 4
template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseBinaryExpression(
- int prec, bool accept_IN, ExpressionClassifier* classifier, bool* ok) {
+ int prec, bool accept_IN, bool* ok) {
DCHECK(prec >= 4);
- ExpressionT x = this->ParseUnaryExpression(classifier, CHECK_OK);
+ ExpressionT x = ParseUnaryExpression(CHECK_OK);
for (int prec1 = Precedence(peek(), accept_IN); prec1 >= prec; prec1--) {
// prec1 >= 4
while (Precedence(peek(), accept_IN) == prec1) {
- CheckNoTailCallExpressions(classifier, CHECK_OK);
- impl()->RewriteNonPattern(classifier, CHECK_OK);
- BindingPatternUnexpectedToken(classifier);
- ArrowFormalParametersUnexpectedToken(classifier);
+ impl()->RewriteNonPattern(CHECK_OK);
+ BindingPatternUnexpectedToken();
+ ArrowFormalParametersUnexpectedToken();
Token::Value op = Next();
int pos = position();
const bool is_right_associative = op == Token::EXP;
const int next_prec = is_right_associative ? prec1 : prec1 + 1;
- ExpressionT y =
- ParseBinaryExpression(next_prec, accept_IN, classifier, CHECK_OK);
- if (op != Token::OR && op != Token::AND) {
- CheckNoTailCallExpressions(classifier, CHECK_OK);
- }
- impl()->RewriteNonPattern(classifier, CHECK_OK);
+ ExpressionT y = ParseBinaryExpression(next_prec, accept_IN, CHECK_OK);
+ impl()->RewriteNonPattern(CHECK_OK);
- if (this->ShortcutNumericLiteralBinaryExpression(&x, y, op, pos,
- factory())) {
+ if (impl()->ShortcutNumericLiteralBinaryExpression(&x, y, op, pos)) {
continue;
}
@@ -2731,7 +2935,7 @@
template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseUnaryExpression(
- ExpressionClassifier* classifier, bool* ok) {
+ bool* ok) {
// UnaryExpression ::
// PostfixExpression
// 'delete' UnaryExpression
@@ -2747,44 +2951,42 @@
Token::Value op = peek();
if (Token::IsUnaryOp(op)) {
- BindingPatternUnexpectedToken(classifier);
- ArrowFormalParametersUnexpectedToken(classifier);
+ BindingPatternUnexpectedToken();
+ ArrowFormalParametersUnexpectedToken();
op = Next();
int pos = position();
- ExpressionT expression = ParseUnaryExpression(classifier, CHECK_OK);
- CheckNoTailCallExpressions(classifier, CHECK_OK);
- impl()->RewriteNonPattern(classifier, CHECK_OK);
+ ExpressionT expression = ParseUnaryExpression(CHECK_OK);
+ impl()->RewriteNonPattern(CHECK_OK);
if (op == Token::DELETE && is_strict(language_mode())) {
- if (this->IsIdentifier(expression)) {
+ if (impl()->IsIdentifier(expression)) {
// "delete identifier" is a syntax error in strict mode.
ReportMessage(MessageTemplate::kStrictDelete);
*ok = false;
- return this->EmptyExpression();
+ return impl()->EmptyExpression();
}
}
if (peek() == Token::EXP) {
ReportUnexpectedToken(Next());
*ok = false;
- return this->EmptyExpression();
+ return impl()->EmptyExpression();
}
- // Allow Traits do rewrite the expression.
- return this->BuildUnaryExpression(expression, op, pos, factory());
+ // Allow the parser's implementation to rewrite the expression.
+ return impl()->BuildUnaryExpression(expression, op, pos);
} else if (Token::IsCountOp(op)) {
- BindingPatternUnexpectedToken(classifier);
- ArrowFormalParametersUnexpectedToken(classifier);
+ BindingPatternUnexpectedToken();
+ ArrowFormalParametersUnexpectedToken();
op = Next();
int beg_pos = peek_position();
- ExpressionT expression = this->ParseUnaryExpression(classifier, CHECK_OK);
- CheckNoTailCallExpressions(classifier, CHECK_OK);
- expression = this->CheckAndRewriteReferenceExpression(
+ ExpressionT expression = ParseUnaryExpression(CHECK_OK);
+ expression = CheckAndRewriteReferenceExpression(
expression, beg_pos, scanner()->location().end_pos,
MessageTemplate::kInvalidLhsInPrefixOp, CHECK_OK);
- this->MarkExpressionAsAssigned(expression);
- impl()->RewriteNonPattern(classifier, CHECK_OK);
+ expression = impl()->MarkExpressionAsAssigned(expression);
+ impl()->RewriteNonPattern(CHECK_OK);
return factory()->NewCountOperation(op,
true /* prefix */,
@@ -2792,41 +2994,39 @@
position());
} else if (is_async_function() && peek() == Token::AWAIT) {
- classifier->RecordFormalParameterInitializerError(
+ classifier()->RecordFormalParameterInitializerError(
scanner()->peek_location(),
MessageTemplate::kAwaitExpressionFormalParameter);
int await_pos = peek_position();
Consume(Token::AWAIT);
- ExpressionT value = ParseUnaryExpression(classifier, CHECK_OK);
+ ExpressionT value = ParseUnaryExpression(CHECK_OK);
return impl()->RewriteAwaitExpression(value, await_pos);
} else {
- return this->ParsePostfixExpression(classifier, ok);
+ return ParsePostfixExpression(ok);
}
}
template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePostfixExpression(
- ExpressionClassifier* classifier, bool* ok) {
+ bool* ok) {
// PostfixExpression ::
// LeftHandSideExpression ('++' | '--')?
int lhs_beg_pos = peek_position();
- ExpressionT expression =
- this->ParseLeftHandSideExpression(classifier, CHECK_OK);
+ ExpressionT expression = ParseLeftHandSideExpression(CHECK_OK);
if (!scanner()->HasAnyLineTerminatorBeforeNext() &&
Token::IsCountOp(peek())) {
- CheckNoTailCallExpressions(classifier, CHECK_OK);
- BindingPatternUnexpectedToken(classifier);
- ArrowFormalParametersUnexpectedToken(classifier);
+ BindingPatternUnexpectedToken();
+ ArrowFormalParametersUnexpectedToken();
- expression = this->CheckAndRewriteReferenceExpression(
+ expression = CheckAndRewriteReferenceExpression(
expression, lhs_beg_pos, scanner()->location().end_pos,
MessageTemplate::kInvalidLhsInPostfixOp, CHECK_OK);
- expression = this->MarkExpressionAsAssigned(expression);
- impl()->RewriteNonPattern(classifier, CHECK_OK);
+ expression = impl()->MarkExpressionAsAssigned(expression);
+ impl()->RewriteNonPattern(CHECK_OK);
Token::Value next = Next();
expression =
@@ -2840,40 +3040,33 @@
template <typename Impl>
typename ParserBase<Impl>::ExpressionT
-ParserBase<Impl>::ParseLeftHandSideExpression(ExpressionClassifier* classifier,
- bool* ok) {
+ParserBase<Impl>::ParseLeftHandSideExpression(bool* ok) {
// LeftHandSideExpression ::
// (NewExpression | MemberExpression) ...
- if (FLAG_harmony_explicit_tailcalls && peek() == Token::CONTINUE) {
- return this->ParseTailCallExpression(classifier, ok);
- }
-
bool is_async = false;
- ExpressionT result = this->ParseMemberWithNewPrefixesExpression(
- classifier, &is_async, CHECK_OK);
+ ExpressionT result =
+ ParseMemberWithNewPrefixesExpression(&is_async, CHECK_OK);
while (true) {
switch (peek()) {
case Token::LBRACK: {
- CheckNoTailCallExpressions(classifier, CHECK_OK);
- impl()->RewriteNonPattern(classifier, CHECK_OK);
- BindingPatternUnexpectedToken(classifier);
- ArrowFormalParametersUnexpectedToken(classifier);
+ impl()->RewriteNonPattern(CHECK_OK);
+ BindingPatternUnexpectedToken();
+ ArrowFormalParametersUnexpectedToken();
Consume(Token::LBRACK);
int pos = position();
- ExpressionT index = ParseExpression(true, classifier, CHECK_OK);
- impl()->RewriteNonPattern(classifier, CHECK_OK);
+ ExpressionT index = ParseExpressionCoverGrammar(true, CHECK_OK);
+ impl()->RewriteNonPattern(CHECK_OK);
result = factory()->NewProperty(result, index, pos);
Expect(Token::RBRACK, CHECK_OK);
break;
}
case Token::LPAREN: {
- CheckNoTailCallExpressions(classifier, CHECK_OK);
int pos;
- impl()->RewriteNonPattern(classifier, CHECK_OK);
- BindingPatternUnexpectedToken(classifier);
+ impl()->RewriteNonPattern(CHECK_OK);
+ BindingPatternUnexpectedToken();
if (scanner()->current_token() == Token::IDENTIFIER ||
scanner()->current_token() == Token::SUPER ||
scanner()->current_token() == Token::ASYNC) {
@@ -2895,36 +3088,36 @@
}
}
Scanner::Location spread_pos;
- typename Traits::Type::ExpressionList args;
- if (V8_UNLIKELY(is_async && this->IsIdentifier(result))) {
+ ExpressionListT args;
+ if (V8_UNLIKELY(is_async && impl()->IsIdentifier(result))) {
ExpressionClassifier async_classifier(this);
- args = ParseArguments(&spread_pos, true, &async_classifier, CHECK_OK);
+ args = ParseArguments(&spread_pos, true, CHECK_OK);
if (peek() == Token::ARROW) {
if (fni_) {
fni_->RemoveAsyncKeywordFromEnd();
}
- ValidateBindingPattern(&async_classifier, CHECK_OK);
- if (!async_classifier.is_valid_async_arrow_formal_parameters()) {
+ ValidateBindingPattern(CHECK_OK);
+ ValidateFormalParameterInitializer(CHECK_OK);
+ if (!classifier()->is_valid_async_arrow_formal_parameters()) {
ReportClassifierError(
- async_classifier.async_arrow_formal_parameters_error());
+ classifier()->async_arrow_formal_parameters_error());
*ok = false;
- return this->EmptyExpression();
+ return impl()->EmptyExpression();
}
if (args->length()) {
// async ( Arguments ) => ...
- return Traits::ExpressionListToExpression(args);
+ return impl()->ExpressionListToExpression(args);
}
// async () => ...
return factory()->NewEmptyParentheses(pos);
} else {
- classifier->Accumulate(&async_classifier,
- ExpressionClassifier::AllProductions);
+ impl()->AccumulateFormalParameterContainmentErrors();
}
} else {
- args = ParseArguments(&spread_pos, false, classifier, CHECK_OK);
+ args = ParseArguments(&spread_pos, false, CHECK_OK);
}
- ArrowFormalParametersUnexpectedToken(classifier);
+ ArrowFormalParametersUnexpectedToken();
// Keep track of eval() calls since they disable all local variable
// optimizations.
@@ -2947,7 +3140,8 @@
// 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(pos);
+ result = impl()->RewriteSuperCall(result);
+ ExpressionT this_expr = impl()->ThisExpression(pos);
result =
factory()->NewAssignment(Token::INIT, this_expr, result, pos);
}
@@ -2957,26 +3151,24 @@
}
case Token::PERIOD: {
- CheckNoTailCallExpressions(classifier, CHECK_OK);
- impl()->RewriteNonPattern(classifier, CHECK_OK);
- BindingPatternUnexpectedToken(classifier);
- ArrowFormalParametersUnexpectedToken(classifier);
+ impl()->RewriteNonPattern(CHECK_OK);
+ BindingPatternUnexpectedToken();
+ ArrowFormalParametersUnexpectedToken();
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);
+ impl()->PushLiteralName(name);
break;
}
case Token::TEMPLATE_SPAN:
case Token::TEMPLATE_TAIL: {
- CheckNoTailCallExpressions(classifier, CHECK_OK);
- impl()->RewriteNonPattern(classifier, CHECK_OK);
- BindingPatternUnexpectedToken(classifier);
- ArrowFormalParametersUnexpectedToken(classifier);
- result = ParseTemplateLiteral(result, position(), classifier, CHECK_OK);
+ impl()->RewriteNonPattern(CHECK_OK);
+ BindingPatternUnexpectedToken();
+ ArrowFormalParametersUnexpectedToken();
+ result = ParseTemplateLiteral(result, position(), CHECK_OK);
break;
}
@@ -2988,8 +3180,8 @@
template <typename Impl>
typename ParserBase<Impl>::ExpressionT
-ParserBase<Impl>::ParseMemberWithNewPrefixesExpression(
- ExpressionClassifier* classifier, bool* is_async, bool* ok) {
+ParserBase<Impl>::ParseMemberWithNewPrefixesExpression(bool* is_async,
+ bool* ok) {
// NewExpression ::
// ('new')+ MemberExpression
//
@@ -3011,8 +3203,8 @@
// new new foo().bar().baz means (new (new foo()).bar()).baz
if (peek() == Token::NEW) {
- BindingPatternUnexpectedToken(classifier);
- ArrowFormalParametersUnexpectedToken(classifier);
+ BindingPatternUnexpectedToken();
+ ArrowFormalParametersUnexpectedToken();
Consume(Token::NEW);
int new_pos = position();
ExpressionT result;
@@ -3022,15 +3214,13 @@
} else if (peek() == Token::PERIOD) {
return ParseNewTargetExpression(CHECK_OK);
} else {
- result = this->ParseMemberWithNewPrefixesExpression(classifier, is_async,
- CHECK_OK);
+ result = ParseMemberWithNewPrefixesExpression(is_async, CHECK_OK);
}
- impl()->RewriteNonPattern(classifier, CHECK_OK);
+ impl()->RewriteNonPattern(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);
+ ExpressionListT args = ParseArguments(&spread_pos, CHECK_OK);
if (spread_pos.IsValid()) {
args = impl()->PrepareSpreadArguments(args);
@@ -3039,21 +3229,19 @@
result = factory()->NewCallNew(result, args, new_pos);
}
// The expression can still continue with . or [ after the arguments.
- result = this->ParseMemberExpressionContinuation(result, is_async,
- classifier, CHECK_OK);
+ result = ParseMemberExpressionContinuation(result, is_async, CHECK_OK);
return result;
}
// NewExpression without arguments.
- return factory()->NewCallNew(result, this->NewExpressionList(0, zone_),
- new_pos);
+ return factory()->NewCallNew(result, impl()->NewExpressionList(0), new_pos);
}
// No 'new' or 'super' keyword.
- return this->ParseMemberExpression(classifier, is_async, ok);
+ return ParseMemberExpression(is_async, ok);
}
template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseMemberExpression(
- ExpressionClassifier* classifier, bool* is_async, bool* ok) {
+ bool* is_async, bool* ok) {
// MemberExpression ::
// (PrimaryExpression | FunctionLiteral | ClassLiteral)
// ('[' Expression ']' | '.' Identifier | Arguments | TemplateLiteral)*
@@ -3065,8 +3253,8 @@
// Parse the initial primary or function expression.
ExpressionT result;
if (peek() == Token::FUNCTION) {
- BindingPatternUnexpectedToken(classifier);
- ArrowFormalParametersUnexpectedToken(classifier);
+ BindingPatternUnexpectedToken();
+ ArrowFormalParametersUnexpectedToken();
Consume(Token::FUNCTION);
int function_token_position = position();
@@ -3078,19 +3266,19 @@
if (!is_generator()) {
// TODO(neis): allow escaping into closures?
- ReportMessageAt(scanner()->location(),
- MessageTemplate::kUnexpectedFunctionSent);
+ impl()->ReportMessageAt(scanner()->location(),
+ MessageTemplate::kUnexpectedFunctionSent);
*ok = false;
- return this->EmptyExpression();
+ return impl()->EmptyExpression();
}
- return this->FunctionSentExpression(factory(), pos);
+ return impl()->FunctionSentExpression(pos);
}
FunctionKind function_kind = Check(Token::MUL)
? FunctionKind::kGeneratorFunction
: FunctionKind::kNormalFunction;
- IdentifierT name = this->EmptyIdentifier();
+ IdentifierT name = impl()->EmptyIdentifier();
bool is_strict_reserved_name = false;
Scanner::Location function_name_location = Scanner::Location::invalid();
FunctionLiteral::FunctionType function_type =
@@ -3111,11 +3299,10 @@
const bool is_new = false;
result = ParseSuperExpression(is_new, CHECK_OK);
} else {
- result = ParsePrimaryExpression(classifier, is_async, CHECK_OK);
+ result = ParsePrimaryExpression(is_async, CHECK_OK);
}
- result =
- ParseMemberExpressionContinuation(result, is_async, classifier, CHECK_OK);
+ result = ParseMemberExpressionContinuation(result, is_async, CHECK_OK);
return result;
}
@@ -3131,20 +3318,21 @@
IsClassConstructor(kind)) {
if (peek() == Token::PERIOD || peek() == Token::LBRACK) {
scope->RecordSuperPropertyUsage();
- return this->NewSuperPropertyReference(factory(), pos);
+ return impl()->NewSuperPropertyReference(pos);
}
// new super() is never allowed.
// super() is only allowed in derived constructor
if (!is_new && peek() == Token::LPAREN && IsSubclassConstructor(kind)) {
// TODO(rossberg): This might not be the correct FunctionState for the
// method here.
- return this->NewSuperCallReference(factory(), pos);
+ return impl()->NewSuperCallReference(pos);
}
}
- ReportMessageAt(scanner()->location(), MessageTemplate::kUnexpectedSuper);
+ impl()->ReportMessageAt(scanner()->location(),
+ MessageTemplate::kUnexpectedSuper);
*ok = false;
- return this->EmptyExpression();
+ return impl()->EmptyExpression();
}
template <typename Impl>
@@ -3154,7 +3342,7 @@
Consume(Token::PERIOD);
ExpectContextualKeyword(property_name, CHECK_OK_CUSTOM(Void));
if (scanner()->literal_contains_escapes()) {
- Traits::ReportMessageAt(
+ impl()->ReportMessageAt(
Scanner::Location(pos, scanner()->location().end_pos),
MessageTemplate::kInvalidEscapedMetaProperty, full_name);
*ok = false;
@@ -3168,63 +3356,58 @@
ExpectMetaProperty(CStrVector("target"), "new.target", pos, CHECK_OK);
if (!GetReceiverScope()->is_function_scope()) {
- ReportMessageAt(scanner()->location(),
- MessageTemplate::kUnexpectedNewTarget);
+ impl()->ReportMessageAt(scanner()->location(),
+ MessageTemplate::kUnexpectedNewTarget);
*ok = false;
- return this->EmptyExpression();
+ return impl()->EmptyExpression();
}
- return this->NewTargetExpression(pos);
+ return impl()->NewTargetExpression(pos);
}
template <typename Impl>
typename ParserBase<Impl>::ExpressionT
-ParserBase<Impl>::ParseMemberExpressionContinuation(
- ExpressionT expression, bool* is_async, ExpressionClassifier* classifier,
- bool* ok) {
+ParserBase<Impl>::ParseMemberExpressionContinuation(ExpressionT expression,
+ bool* is_async, bool* ok) {
// Parses this part of MemberExpression:
// ('[' Expression ']' | '.' Identifier | TemplateLiteral)*
while (true) {
switch (peek()) {
case Token::LBRACK: {
*is_async = false;
- impl()->RewriteNonPattern(classifier, CHECK_OK);
- BindingPatternUnexpectedToken(classifier);
- ArrowFormalParametersUnexpectedToken(classifier);
+ impl()->RewriteNonPattern(CHECK_OK);
+ BindingPatternUnexpectedToken();
+ ArrowFormalParametersUnexpectedToken();
Consume(Token::LBRACK);
int pos = position();
- ExpressionT index = this->ParseExpression(true, classifier, CHECK_OK);
- impl()->RewriteNonPattern(classifier, CHECK_OK);
+ ExpressionT index = ParseExpressionCoverGrammar(true, CHECK_OK);
+ impl()->RewriteNonPattern(CHECK_OK);
expression = factory()->NewProperty(expression, index, pos);
- if (fni_ != NULL) {
- this->PushPropertyName(fni_, index);
- }
+ impl()->PushPropertyName(index);
Expect(Token::RBRACK, CHECK_OK);
break;
}
case Token::PERIOD: {
*is_async = false;
- impl()->RewriteNonPattern(classifier, CHECK_OK);
- BindingPatternUnexpectedToken(classifier);
- ArrowFormalParametersUnexpectedToken(classifier);
+ impl()->RewriteNonPattern(CHECK_OK);
+ BindingPatternUnexpectedToken();
+ ArrowFormalParametersUnexpectedToken();
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);
- }
+ impl()->PushLiteralName(name);
break;
}
case Token::TEMPLATE_SPAN:
case Token::TEMPLATE_TAIL: {
*is_async = false;
- impl()->RewriteNonPattern(classifier, CHECK_OK);
- BindingPatternUnexpectedToken(classifier);
- ArrowFormalParametersUnexpectedToken(classifier);
+ impl()->RewriteNonPattern(CHECK_OK);
+ BindingPatternUnexpectedToken();
+ ArrowFormalParametersUnexpectedToken();
int pos;
if (scanner()->current_token() == Token::IDENTIFIER) {
pos = position();
@@ -3236,62 +3419,58 @@
expression->AsFunctionLiteral()->set_should_eager_compile();
}
}
- expression =
- ParseTemplateLiteral(expression, pos, classifier, CHECK_OK);
+ expression = ParseTemplateLiteral(expression, pos, CHECK_OK);
break;
}
case Token::ILLEGAL: {
ReportUnexpectedTokenAt(scanner()->peek_location(), Token::ILLEGAL);
*ok = false;
- return this->EmptyExpression();
+ return impl()->EmptyExpression();
}
default:
return expression;
}
}
DCHECK(false);
- return this->EmptyExpression();
+ return impl()->EmptyExpression();
}
template <typename Impl>
void ParserBase<Impl>::ParseFormalParameter(FormalParametersT* parameters,
- ExpressionClassifier* classifier,
bool* ok) {
// FormalParameter[Yield,GeneratorParameter] :
// BindingElement[?Yield, ?GeneratorParameter]
bool is_rest = parameters->has_rest;
- ExpressionT pattern =
- ParsePrimaryExpression(classifier, CHECK_OK_CUSTOM(Void));
- ValidateBindingPattern(classifier, CHECK_OK_CUSTOM(Void));
+ ExpressionT pattern = ParsePrimaryExpression(CHECK_OK_CUSTOM(Void));
+ ValidateBindingPattern(CHECK_OK_CUSTOM(Void));
- if (!Traits::IsIdentifier(pattern)) {
+ if (!impl()->IsIdentifier(pattern)) {
parameters->is_simple = false;
- ValidateFormalParameterInitializer(classifier, CHECK_OK_CUSTOM(Void));
- classifier->RecordNonSimpleParameter();
+ ValidateFormalParameterInitializer(CHECK_OK_CUSTOM(Void));
+ classifier()->RecordNonSimpleParameter();
}
- ExpressionT initializer = Traits::EmptyExpression();
+ ExpressionT initializer = impl()->EmptyExpression();
if (!is_rest && Check(Token::ASSIGN)) {
ExpressionClassifier init_classifier(this);
- initializer = ParseAssignmentExpression(true, &init_classifier,
- CHECK_OK_CUSTOM(Void));
- impl()->RewriteNonPattern(&init_classifier, CHECK_OK_CUSTOM(Void));
- ValidateFormalParameterInitializer(&init_classifier, CHECK_OK_CUSTOM(Void));
+ initializer = ParseAssignmentExpression(true, CHECK_OK_CUSTOM(Void));
+ impl()->RewriteNonPattern(CHECK_OK_CUSTOM(Void));
+ ValidateFormalParameterInitializer(CHECK_OK_CUSTOM(Void));
parameters->is_simple = false;
- init_classifier.Discard();
- classifier->RecordNonSimpleParameter();
+ impl()->Discard();
+ classifier()->RecordNonSimpleParameter();
- Traits::SetFunctionNameFromIdentifierRef(initializer, pattern);
+ impl()->SetFunctionNameFromIdentifierRef(initializer, pattern);
}
- Traits::AddFormalParameter(parameters, pattern, initializer,
+ impl()->AddFormalParameter(parameters, pattern, initializer,
scanner()->location().end_pos, is_rest);
}
template <typename Impl>
-void ParserBase<Impl>::ParseFormalParameterList(
- FormalParametersT* parameters, ExpressionClassifier* classifier, bool* ok) {
+void ParserBase<Impl>::ParseFormalParameterList(FormalParametersT* parameters,
+ bool* ok) {
// FormalParameters[Yield] :
// [empty]
// FunctionRestParameter[?Yield]
@@ -3313,14 +3492,14 @@
return;
}
parameters->has_rest = Check(Token::ELLIPSIS);
- ParseFormalParameter(parameters, classifier, CHECK_OK_CUSTOM(Void));
+ ParseFormalParameter(parameters, CHECK_OK_CUSTOM(Void));
if (parameters->has_rest) {
parameters->is_simple = false;
- classifier->RecordNonSimpleParameter();
+ classifier()->RecordNonSimpleParameter();
if (peek() == Token::COMMA) {
- ReportMessageAt(scanner()->peek_location(),
- MessageTemplate::kParamAfterRest);
+ impl()->ReportMessageAt(scanner()->peek_location(),
+ MessageTemplate::kParamAfterRest);
*ok = false;
return;
}
@@ -3336,11 +3515,321 @@
for (int i = 0; i < parameters->Arity(); ++i) {
auto parameter = parameters->at(i);
- Traits::DeclareFormalParameter(parameters->scope, parameter, classifier);
+ impl()->DeclareFormalParameter(parameters->scope, parameter);
}
}
template <typename Impl>
+typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseVariableDeclarations(
+ VariableDeclarationContext var_context,
+ DeclarationParsingResult* parsing_result,
+ ZoneList<const AstRawString*>* names, bool* ok) {
+ // VariableDeclarations ::
+ // ('var' | 'const' | 'let') (Identifier ('=' AssignmentExpression)?)+[',']
+ //
+ // ES6:
+ // FIXME(marja, nikolaos): Add an up-to-date comment about ES6 variable
+ // declaration syntax.
+
+ DCHECK_NOT_NULL(parsing_result);
+ parsing_result->descriptor.declaration_kind = DeclarationDescriptor::NORMAL;
+ parsing_result->descriptor.declaration_pos = peek_position();
+ parsing_result->descriptor.initialization_pos = peek_position();
+
+ BlockT init_block = impl()->NullBlock();
+ if (var_context != kForStatement) {
+ init_block = factory()->NewBlock(
+ nullptr, 1, true, parsing_result->descriptor.declaration_pos);
+ }
+
+ switch (peek()) {
+ case Token::VAR:
+ parsing_result->descriptor.mode = VAR;
+ Consume(Token::VAR);
+ break;
+ case Token::CONST:
+ Consume(Token::CONST);
+ DCHECK(var_context != kStatement);
+ parsing_result->descriptor.mode = CONST;
+ break;
+ case Token::LET:
+ Consume(Token::LET);
+ DCHECK(var_context != kStatement);
+ parsing_result->descriptor.mode = LET;
+ break;
+ default:
+ UNREACHABLE(); // by current callers
+ break;
+ }
+
+ parsing_result->descriptor.scope = scope();
+ parsing_result->descriptor.hoist_scope = nullptr;
+
+ // 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 bindings_start = peek_position();
+ do {
+ // Parse binding pattern.
+ FuncNameInferrer::State fni_state(fni_);
+
+ ExpressionT pattern = impl()->EmptyExpression();
+ int decl_pos = peek_position();
+ {
+ ExpressionClassifier pattern_classifier(this);
+ pattern = ParsePrimaryExpression(CHECK_OK_CUSTOM(NullBlock));
+
+ ValidateBindingPattern(CHECK_OK_CUSTOM(NullBlock));
+ if (IsLexicalVariableMode(parsing_result->descriptor.mode)) {
+ ValidateLetPattern(CHECK_OK_CUSTOM(NullBlock));
+ }
+ }
+
+ Scanner::Location variable_loc = scanner()->location();
+ bool single_name = impl()->IsIdentifier(pattern);
+
+ if (single_name) {
+ impl()->PushVariableName(impl()->AsIdentifier(pattern));
+ }
+
+ ExpressionT value = impl()->EmptyExpression();
+ int initializer_position = kNoSourcePosition;
+ if (Check(Token::ASSIGN)) {
+ ExpressionClassifier classifier(this);
+ value = ParseAssignmentExpression(var_context != kForStatement,
+ CHECK_OK_CUSTOM(NullBlock));
+ impl()->RewriteNonPattern(CHECK_OK_CUSTOM(NullBlock));
+ 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 && fni_ != nullptr) {
+ if (!value->IsCall() && !value->IsCallNew()) {
+ fni_->Infer();
+ } else {
+ fni_->RemoveLastFunction();
+ }
+ }
+
+ impl()->SetFunctionNameFromIdentifierRef(value, pattern);
+
+ // End position of the initializer is after the assignment expression.
+ initializer_position = scanner()->location().end_pos;
+ } else {
+ if (var_context != kForStatement || !PeekInOrOf()) {
+ // ES6 'const' and binding patterns require initializers.
+ if (parsing_result->descriptor.mode == CONST ||
+ !impl()->IsIdentifier(pattern)) {
+ impl()->ReportMessageAt(
+ Scanner::Location(decl_pos, scanner()->location().end_pos),
+ MessageTemplate::kDeclarationMissingInitializer,
+ !impl()->IsIdentifier(pattern) ? "destructuring" : "const");
+ *ok = false;
+ return impl()->NullBlock();
+ }
+ // 'let x' initializes 'x' to undefined.
+ if (parsing_result->descriptor.mode == LET) {
+ value = impl()->GetLiteralUndefined(position());
+ }
+ }
+
+ // End position of the initializer is after the variable.
+ initializer_position = position();
+ }
+
+ typename DeclarationParsingResult::Declaration decl(
+ pattern, initializer_position, value);
+ if (var_context == kForStatement) {
+ // Save the declaration for further handling in ParseForStatement.
+ parsing_result->declarations.Add(decl);
+ } else {
+ // Immediately declare the variable otherwise. This avoids O(N^2)
+ // behavior (where N is the number of variables in a single
+ // declaration) in the PatternRewriter having to do with removing
+ // and adding VariableProxies to the Scope (see bug 4699).
+ impl()->DeclareAndInitializeVariables(init_block,
+ &parsing_result->descriptor, &decl,
+ names, CHECK_OK_CUSTOM(NullBlock));
+ }
+ } while (Check(Token::COMMA));
+
+ parsing_result->bindings_loc =
+ Scanner::Location(bindings_start, scanner()->location().end_pos);
+
+ DCHECK(*ok);
+ return init_block;
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::StatementT
+ParserBase<Impl>::ParseFunctionDeclaration(bool* ok) {
+ Consume(Token::FUNCTION);
+ int pos = position();
+ ParseFunctionFlags flags = ParseFunctionFlags::kIsNormal;
+ if (Check(Token::MUL)) {
+ flags |= ParseFunctionFlags::kIsGenerator;
+ if (allow_harmony_restrictive_declarations()) {
+ impl()->ReportMessageAt(scanner()->location(),
+ MessageTemplate::kGeneratorInLegacyContext);
+ *ok = false;
+ return impl()->NullStatement();
+ }
+ }
+ return ParseHoistableDeclaration(pos, flags, nullptr, false, ok);
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::StatementT
+ParserBase<Impl>::ParseHoistableDeclaration(
+ ZoneList<const AstRawString*>* names, bool default_export, bool* ok) {
+ Expect(Token::FUNCTION, CHECK_OK_CUSTOM(NullStatement));
+ int pos = position();
+ ParseFunctionFlags flags = ParseFunctionFlags::kIsNormal;
+ if (Check(Token::MUL)) {
+ flags |= ParseFunctionFlags::kIsGenerator;
+ }
+ return ParseHoistableDeclaration(pos, flags, names, default_export, ok);
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::StatementT
+ParserBase<Impl>::ParseHoistableDeclaration(
+ int pos, ParseFunctionFlags flags, ZoneList<const AstRawString*>* names,
+ bool default_export, bool* ok) {
+ // FunctionDeclaration ::
+ // 'function' Identifier '(' FormalParameters ')' '{' FunctionBody '}'
+ // 'function' '(' FormalParameters ')' '{' FunctionBody '}'
+ // GeneratorDeclaration ::
+ // 'function' '*' Identifier '(' FormalParameters ')' '{' FunctionBody '}'
+ // 'function' '*' '(' FormalParameters ')' '{' FunctionBody '}'
+ //
+ // The anonymous forms are allowed iff [default_export] is true.
+ //
+ // 'function' and '*' (if present) have been consumed by the caller.
+
+ const bool is_generator = flags & ParseFunctionFlags::kIsGenerator;
+ const bool is_async = flags & ParseFunctionFlags::kIsAsync;
+ DCHECK(!is_generator || !is_async);
+
+ IdentifierT name;
+ FunctionNameValidity name_validity;
+ IdentifierT variable_name;
+ if (default_export && peek() == Token::LPAREN) {
+ impl()->GetDefaultStrings(&name, &variable_name);
+ name_validity = kSkipFunctionNameCheck;
+ } else {
+ bool is_strict_reserved;
+ name = ParseIdentifierOrStrictReservedWord(&is_strict_reserved,
+ CHECK_OK_CUSTOM(NullStatement));
+ name_validity = is_strict_reserved ? kFunctionNameIsStrictReserved
+ : kFunctionNameValidityUnknown;
+ variable_name = name;
+ }
+
+ FuncNameInferrer::State fni_state(fni_);
+ impl()->PushEnclosingName(name);
+ FunctionLiteralT function = impl()->ParseFunctionLiteral(
+ name, scanner()->location(), name_validity,
+ is_generator ? FunctionKind::kGeneratorFunction
+ : is_async ? FunctionKind::kAsyncFunction
+ : FunctionKind::kNormalFunction,
+ pos, FunctionLiteral::kDeclaration, language_mode(),
+ CHECK_OK_CUSTOM(NullStatement));
+
+ return impl()->DeclareFunction(variable_name, function, pos, is_generator,
+ is_async, names, ok);
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseClassDeclaration(
+ ZoneList<const AstRawString*>* names, bool default_export, bool* ok) {
+ // ClassDeclaration ::
+ // 'class' Identifier ('extends' LeftHandExpression)? '{' ClassBody '}'
+ // 'class' ('extends' LeftHandExpression)? '{' ClassBody '}'
+ //
+ // The anonymous form is allowed iff [default_export] is true.
+ //
+ // 'class' is expected to be consumed by the caller.
+ //
+ // A ClassDeclaration
+ //
+ // class C { ... }
+ //
+ // has the same semantics as:
+ //
+ // let C = class C { ... };
+ //
+ // so rewrite it as such.
+
+ int class_token_pos = position();
+ IdentifierT name = impl()->EmptyIdentifier();
+ bool is_strict_reserved = false;
+ IdentifierT variable_name = impl()->EmptyIdentifier();
+ if (default_export && (peek() == Token::EXTENDS || peek() == Token::LBRACE)) {
+ impl()->GetDefaultStrings(&name, &variable_name);
+ } else {
+ name = ParseIdentifierOrStrictReservedWord(&is_strict_reserved,
+ CHECK_OK_CUSTOM(NullStatement));
+ variable_name = name;
+ }
+
+ ExpressionClassifier no_classifier(this);
+ ExpressionT value =
+ ParseClassLiteral(name, scanner()->location(), is_strict_reserved,
+ class_token_pos, CHECK_OK_CUSTOM(NullStatement));
+ int end_pos = position();
+ return impl()->DeclareClass(variable_name, value, names, class_token_pos,
+ end_pos, ok);
+}
+
+// Language extension which is only enabled for source files loaded
+// through the API's extension mechanism. A native function
+// declaration is resolved by looking up the function through a
+// callback provided by the extension.
+template <typename Impl>
+typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseNativeDeclaration(
+ bool* ok) {
+ int pos = peek_position();
+ Expect(Token::FUNCTION, CHECK_OK_CUSTOM(NullStatement));
+ // Allow "eval" or "arguments" for backward compatibility.
+ IdentifierT name = ParseIdentifier(kAllowRestrictedIdentifiers,
+ CHECK_OK_CUSTOM(NullStatement));
+ Expect(Token::LPAREN, CHECK_OK_CUSTOM(NullStatement));
+ if (peek() != Token::RPAREN) {
+ do {
+ ParseIdentifier(kAllowRestrictedIdentifiers,
+ CHECK_OK_CUSTOM(NullStatement));
+ } while (Check(Token::COMMA));
+ }
+ Expect(Token::RPAREN, CHECK_OK_CUSTOM(NullStatement));
+ Expect(Token::SEMICOLON, CHECK_OK_CUSTOM(NullStatement));
+ return impl()->DeclareNative(name, pos, ok);
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::StatementT
+ParserBase<Impl>::ParseAsyncFunctionDeclaration(
+ ZoneList<const AstRawString*>* names, bool default_export, bool* ok) {
+ // AsyncFunctionDeclaration ::
+ // async [no LineTerminator here] function BindingIdentifier[Await]
+ // ( FormalParameters[Await] ) { AsyncFunctionBody }
+ DCHECK_EQ(scanner()->current_token(), Token::ASYNC);
+ int pos = position();
+ if (scanner()->HasAnyLineTerminatorBeforeNext()) {
+ *ok = false;
+ impl()->ReportUnexpectedToken(scanner()->current_token());
+ return impl()->NullStatement();
+ }
+ Expect(Token::FUNCTION, CHECK_OK_CUSTOM(NullStatement));
+ ParseFunctionFlags flags = ParseFunctionFlags::kIsAsync;
+ return ParseHoistableDeclaration(pos, flags, names, default_export, ok);
+}
+
+template <typename Impl>
void ParserBase<Impl>::CheckArityRestrictions(int param_count,
FunctionKind function_kind,
bool has_rest,
@@ -3348,19 +3837,22 @@
int formals_end_pos, bool* ok) {
if (IsGetterFunction(function_kind)) {
if (param_count != 0) {
- ReportMessageAt(Scanner::Location(formals_start_pos, formals_end_pos),
- MessageTemplate::kBadGetterArity);
+ impl()->ReportMessageAt(
+ Scanner::Location(formals_start_pos, formals_end_pos),
+ MessageTemplate::kBadGetterArity);
*ok = false;
}
} else if (IsSetterFunction(function_kind)) {
if (param_count != 1) {
- ReportMessageAt(Scanner::Location(formals_start_pos, formals_end_pos),
- MessageTemplate::kBadSetterArity);
+ impl()->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);
+ impl()->ReportMessageAt(
+ Scanner::Location(formals_start_pos, formals_end_pos),
+ MessageTemplate::kBadSetterRestParameter);
*ok = false;
}
}
@@ -3412,31 +3904,33 @@
template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseArrowFunctionLiteral(
- bool accept_IN, const FormalParametersT& formal_parameters, bool is_async,
- const ExpressionClassifier& formals_classifier, bool* ok) {
+ bool accept_IN, const FormalParametersT& formal_parameters, 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();
+ return impl()->EmptyExpression();
}
- typename Traits::Type::StatementList body;
+ StatementListT body = impl()->NullStatementList();
int num_parameters = formal_parameters.scope->num_parameters();
int materialized_literal_count = -1;
int expected_property_count = -1;
- FunctionKind arrow_kind = is_async ? kAsyncArrowFunction : kArrowFunction;
+ FunctionKind kind = formal_parameters.scope->function_kind();
+ FunctionLiteral::EagerCompileHint eager_compile_hint =
+ FunctionLiteral::kShouldLazyCompile;
+ bool should_be_used_once_hint = false;
{
FunctionState function_state(&function_state_, &scope_state_,
- formal_parameters.scope, arrow_kind);
+ formal_parameters.scope);
function_state.SkipMaterializedLiterals(
formal_parameters.materialized_literals_count);
- this->ReindexLiterals(formal_parameters);
+ impl()->ReindexLiterals(formal_parameters);
Expect(Token::ARROW, CHECK_OK);
@@ -3444,20 +3938,42 @@
// Multiple statement body
Consume(Token::LBRACE);
DCHECK_EQ(scope(), formal_parameters.scope);
- bool is_lazily_parsed = (mode() == PARSE_LAZILY &&
- formal_parameters.scope->AllowsLazyParsing());
+ bool is_lazily_parsed =
+ (mode() == PARSE_LAZILY &&
+ formal_parameters.scope
+ ->AllowsLazyParsingWithoutUnresolvedVariables());
+ // TODO(marja): consider lazy-parsing inner arrow functions too. is_this
+ // handling in Scope::ResolveVariable needs to change.
if (is_lazily_parsed) {
- body = this->NewStatementList(0, zone());
- impl()->SkipLazyFunctionBody(&materialized_literal_count,
- &expected_property_count, CHECK_OK);
+ Scanner::BookmarkScope bookmark(scanner());
+ bookmark.Set();
+ LazyParsingResult result = impl()->SkipLazyFunctionBody(
+ &materialized_literal_count, &expected_property_count, false, true,
+ CHECK_OK);
+ formal_parameters.scope->ResetAfterPreparsing(
+ ast_value_factory_, result == kLazyParsingAborted);
+
if (formal_parameters.materialized_literals_count > 0) {
materialized_literal_count +=
formal_parameters.materialized_literals_count;
}
- } else {
+
+ if (result == kLazyParsingAborted) {
+ bookmark.Apply();
+ // 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) {
body = impl()->ParseEagerFunctionBody(
- this->EmptyIdentifier(), kNoSourcePosition, formal_parameters,
- arrow_kind, FunctionLiteral::kAnonymousExpression, CHECK_OK);
+ impl()->EmptyIdentifier(), kNoSourcePosition, formal_parameters,
+ kind, FunctionLiteral::kAnonymousExpression, CHECK_OK);
materialized_literal_count =
function_state.materialized_literal_count();
expected_property_count = function_state.expected_property_count();
@@ -3469,18 +3985,18 @@
function_state_->return_expr_context());
ReturnExprScope allow_tail_calls(
function_state_, ReturnExprContext::kInsideValidReturnStatement);
- body = this->NewStatementList(1, zone());
- this->AddParameterInitializationBlock(formal_parameters, body, is_async,
- CHECK_OK);
+ body = impl()->NewStatementList(1);
+ impl()->AddParameterInitializationBlock(
+ formal_parameters, body, kind == kAsyncArrowFunction, CHECK_OK);
ExpressionClassifier classifier(this);
- if (is_async) {
- impl()->ParseAsyncArrowSingleExpressionBody(body, accept_IN,
- &classifier, pos, CHECK_OK);
- impl()->RewriteNonPattern(&classifier, CHECK_OK);
+ if (kind == kAsyncArrowFunction) {
+ ParseAsyncFunctionBody(scope(), body, kAsyncArrowFunction,
+ FunctionBodyType::kSingleExpression, accept_IN,
+ pos, CHECK_OK);
+ impl()->RewriteNonPattern(CHECK_OK);
} else {
- ExpressionT expression =
- ParseAssignmentExpression(accept_IN, &classifier, CHECK_OK);
- impl()->RewriteNonPattern(&classifier, CHECK_OK);
+ ExpressionT expression = ParseAssignmentExpression(accept_IN, CHECK_OK);
+ impl()->RewriteNonPattern(CHECK_OK);
body->Add(factory()->NewReturnStatement(expression, pos), zone());
if (allow_tailcalls() && !is_sloppy(language_mode())) {
// ES6 14.6.1 Static Semantics: IsInTailPosition
@@ -3499,8 +4015,8 @@
// 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);
+ ValidateFormalParameters(language_mode(), allow_duplicate_parameters,
+ CHECK_OK);
// Validate strict mode.
if (is_strict(language_mode())) {
@@ -3513,24 +4029,141 @@
}
FunctionLiteralT function_literal = factory()->NewFunctionLiteral(
- this->EmptyIdentifierString(), formal_parameters.scope, body,
+ impl()->EmptyIdentifierString(), formal_parameters.scope, body,
materialized_literal_count, expected_property_count, num_parameters,
FunctionLiteral::kNoDuplicateParameters,
- FunctionLiteral::kAnonymousExpression,
- FunctionLiteral::kShouldLazyCompile, arrow_kind,
+ FunctionLiteral::kAnonymousExpression, eager_compile_hint,
formal_parameters.scope->start_position());
function_literal->set_function_token_position(
formal_parameters.scope->start_position());
+ if (should_be_used_once_hint) {
+ function_literal->set_should_be_used_once_hint();
+ }
- if (fni_ != NULL) this->InferFunctionName(fni_, function_literal);
+ impl()->AddFunctionForNameInference(function_literal);
return function_literal;
}
template <typename Impl>
+typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseClassLiteral(
+ IdentifierT name, Scanner::Location class_name_location,
+ bool name_is_strict_reserved, int class_token_pos, bool* ok) {
+ // All parts of a ClassDeclaration and ClassExpression are strict code.
+ if (name_is_strict_reserved) {
+ impl()->ReportMessageAt(class_name_location,
+ MessageTemplate::kUnexpectedStrictReserved);
+ *ok = false;
+ return impl()->EmptyExpression();
+ }
+ if (impl()->IsEvalOrArguments(name)) {
+ impl()->ReportMessageAt(class_name_location,
+ MessageTemplate::kStrictEvalArguments);
+ *ok = false;
+ return impl()->EmptyExpression();
+ }
+
+ BlockState block_state(zone(), &scope_state_);
+ RaiseLanguageMode(STRICT);
+
+ ClassInfo class_info(this);
+ impl()->DeclareClassVariable(name, block_state.scope(), &class_info,
+ class_token_pos, CHECK_OK);
+
+ if (Check(Token::EXTENDS)) {
+ block_state.set_start_position(scanner()->location().end_pos);
+ ExpressionClassifier extends_classifier(this);
+ class_info.extends = ParseLeftHandSideExpression(CHECK_OK);
+ impl()->RewriteNonPattern(CHECK_OK);
+ impl()->AccumulateFormalParameterContainmentErrors();
+ } else {
+ block_state.set_start_position(scanner()->location().end_pos);
+ }
+
+ ClassLiteralChecker checker(this);
+
+ Expect(Token::LBRACE, CHECK_OK);
+
+ const bool has_extends = !impl()->IsEmptyExpression(class_info.extends);
+ while (peek() != Token::RBRACE) {
+ if (Check(Token::SEMICOLON)) continue;
+ FuncNameInferrer::State fni_state(fni_);
+ bool is_computed_name = false; // Classes do not care about computed
+ // property names here.
+ ExpressionClassifier property_classifier(this);
+ ClassLiteralPropertyT property = ParseClassPropertyDefinition(
+ &checker, has_extends, &is_computed_name,
+ &class_info.has_seen_constructor, CHECK_OK);
+ impl()->RewriteNonPattern(CHECK_OK);
+ impl()->AccumulateFormalParameterContainmentErrors();
+
+ impl()->DeclareClassProperty(name, property, &class_info, CHECK_OK);
+ impl()->InferFunctionName();
+ }
+
+ Expect(Token::RBRACE, CHECK_OK);
+ return impl()->RewriteClassLiteral(name, &class_info, class_token_pos, ok);
+}
+
+template <typename Impl>
+void ParserBase<Impl>::ParseAsyncFunctionBody(Scope* scope, StatementListT body,
+ FunctionKind kind,
+ FunctionBodyType body_type,
+ bool accept_IN, int pos,
+ bool* ok) {
+ scope->ForceContextAllocation();
+
+ impl()->PrepareAsyncFunctionBody(body, kind, pos);
+
+ BlockT block = factory()->NewBlock(nullptr, 8, true, kNoSourcePosition);
+
+ ExpressionT return_value = impl()->EmptyExpression();
+ if (body_type == FunctionBodyType::kNormal) {
+ ParseStatementList(block->statements(), Token::RBRACE,
+ CHECK_OK_CUSTOM(Void));
+ return_value = factory()->NewUndefinedLiteral(kNoSourcePosition);
+ } else {
+ return_value = ParseAssignmentExpression(accept_IN, CHECK_OK_CUSTOM(Void));
+ impl()->RewriteNonPattern(CHECK_OK_CUSTOM(Void));
+ }
+
+ impl()->RewriteAsyncFunctionBody(body, block, return_value,
+ CHECK_OK_CUSTOM(Void));
+ scope->set_end_position(scanner()->location().end_pos);
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::ExpressionT
+ParserBase<Impl>::ParseAsyncFunctionLiteral(bool* ok) {
+ // AsyncFunctionLiteral ::
+ // async [no LineTerminator here] function ( FormalParameters[Await] )
+ // { AsyncFunctionBody }
+ //
+ // async [no LineTerminator here] function BindingIdentifier[Await]
+ // ( FormalParameters[Await] ) { AsyncFunctionBody }
+ DCHECK_EQ(scanner()->current_token(), Token::ASYNC);
+ int pos = position();
+ Expect(Token::FUNCTION, CHECK_OK);
+ bool is_strict_reserved = false;
+ IdentifierT name = impl()->EmptyIdentifier();
+ FunctionLiteral::FunctionType type = FunctionLiteral::kAnonymousExpression;
+
+ if (peek_any_identifier()) {
+ type = FunctionLiteral::kNamedExpression;
+ name = ParseIdentifierOrStrictReservedWord(FunctionKind::kAsyncFunction,
+ &is_strict_reserved, CHECK_OK);
+ }
+ return impl()->ParseFunctionLiteral(
+ name, scanner()->location(),
+ is_strict_reserved ? kFunctionNameIsStrictReserved
+ : kFunctionNameValidityUnknown,
+ FunctionKind::kAsyncFunction, pos, type, language_mode(), CHECK_OK);
+}
+
+template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseTemplateLiteral(
- ExpressionT tag, int start, ExpressionClassifier* classifier, bool* ok) {
+ ExpressionT tag, int start, 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.
@@ -3569,29 +4202,28 @@
CheckTemplateOctalLiteral(pos, peek_position(), CHECK_OK);
next = peek();
if (next == Token::EOS) {
- ReportMessageAt(Scanner::Location(start, peek_position()),
- MessageTemplate::kUnterminatedTemplate);
+ impl()->ReportMessageAt(Scanner::Location(start, peek_position()),
+ MessageTemplate::kUnterminatedTemplate);
*ok = false;
- return Traits::EmptyExpression();
+ return impl()->EmptyExpression();
} else if (next == Token::ILLEGAL) {
- Traits::ReportMessageAt(
+ impl()->ReportMessageAt(
Scanner::Location(position() + 1, peek_position()),
MessageTemplate::kUnexpectedToken, "ILLEGAL", kSyntaxError);
*ok = false;
- return Traits::EmptyExpression();
+ return impl()->EmptyExpression();
}
int expr_pos = peek_position();
- ExpressionT expression = this->ParseExpression(true, classifier, CHECK_OK);
- CheckNoTailCallExpressions(classifier, CHECK_OK);
- impl()->RewriteNonPattern(classifier, CHECK_OK);
+ ExpressionT expression = ParseExpressionCoverGrammar(true, CHECK_OK);
+ impl()->RewriteNonPattern(CHECK_OK);
impl()->AddTemplateExpression(&ts, expression);
if (peek() != Token::RBRACE) {
- ReportMessageAt(Scanner::Location(expr_pos, peek_position()),
- MessageTemplate::kUnterminatedTemplateExpr);
+ impl()->ReportMessageAt(Scanner::Location(expr_pos, peek_position()),
+ MessageTemplate::kUnterminatedTemplateExpr);
*ok = false;
- return Traits::EmptyExpression();
+ return impl()->EmptyExpression();
}
// If we didn't die parsing that expression, our next token should be a
@@ -3601,16 +4233,16 @@
pos = position();
if (next == Token::EOS) {
- ReportMessageAt(Scanner::Location(start, pos),
- MessageTemplate::kUnterminatedTemplate);
+ impl()->ReportMessageAt(Scanner::Location(start, pos),
+ MessageTemplate::kUnterminatedTemplate);
*ok = false;
- return Traits::EmptyExpression();
+ return impl()->EmptyExpression();
} else if (next == Token::ILLEGAL) {
- Traits::ReportMessageAt(
+ impl()->ReportMessageAt(
Scanner::Location(position() + 1, peek_position()),
MessageTemplate::kUnexpectedToken, "ILLEGAL", kSyntaxError);
*ok = false;
- return Traits::EmptyExpression();
+ return impl()->EmptyExpression();
}
impl()->AddTemplateSpan(&ts, next == Token::TEMPLATE_TAIL);
@@ -3627,8 +4259,8 @@
ParserBase<Impl>::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);
+ return CheckAndRewriteReferenceExpression(expression, beg_pos, end_pos,
+ message, kReferenceError, ok);
}
template <typename Impl>
@@ -3636,12 +4268,12 @@
ParserBase<Impl>::CheckAndRewriteReferenceExpression(
ExpressionT expression, int beg_pos, int end_pos,
MessageTemplate::Template message, ParseErrorType type, bool* ok) {
- if (this->IsIdentifier(expression) && is_strict(language_mode()) &&
- this->IsEvalOrArguments(this->AsIdentifier(expression))) {
+ if (impl()->IsIdentifier(expression) && is_strict(language_mode()) &&
+ impl()->IsEvalOrArguments(impl()->AsIdentifier(expression))) {
ReportMessageAt(Scanner::Location(beg_pos, end_pos),
MessageTemplate::kStrictEvalArguments, kSyntaxError);
*ok = false;
- return this->EmptyExpression();
+ return impl()->EmptyExpression();
}
if (expression->IsValidReferenceExpression()) {
return expression;
@@ -3649,47 +4281,1140 @@
if (expression->IsCall()) {
// If it is a call, make it a runtime error for legacy web compatibility.
// Rewrite `expr' to `expr[throw ReferenceError]'.
- ExpressionT error = this->NewThrowReferenceError(message, beg_pos);
+ ExpressionT error = impl()->NewThrowReferenceError(message, beg_pos);
return factory()->NewProperty(expression, error, beg_pos);
}
ReportMessageAt(Scanner::Location(beg_pos, end_pos), message, type);
*ok = false;
- return this->EmptyExpression();
+ return impl()->EmptyExpression();
}
template <typename Impl>
bool ParserBase<Impl>::IsValidReferenceExpression(ExpressionT expression) {
- return this->IsAssignableIdentifier(expression) || expression->IsProperty();
+ return IsAssignableIdentifier(expression) || expression->IsProperty();
}
template <typename Impl>
-void ParserBase<Impl>::CheckDestructuringElement(
- ExpressionT expression, ExpressionClassifier* classifier, int begin,
- int end) {
+void ParserBase<Impl>::CheckDestructuringElement(ExpressionT expression,
+ int begin, int end) {
if (!IsValidPattern(expression) && !expression->IsAssignment() &&
!IsValidReferenceExpression(expression)) {
- classifier->RecordAssignmentPatternError(
+ classifier()->RecordAssignmentPatternError(
Scanner::Location(begin, end),
MessageTemplate::kInvalidDestructuringTarget);
}
}
+template <typename Impl>
+typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseV8Intrinsic(
+ bool* ok) {
+ // CallRuntime ::
+ // '%' Identifier Arguments
+
+ int pos = peek_position();
+ Expect(Token::MOD, CHECK_OK);
+ // Allow "eval" or "arguments" for backward compatibility.
+ IdentifierT name = ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK);
+ Scanner::Location spread_pos;
+ ExpressionClassifier classifier(this);
+ ExpressionListT args = ParseArguments(&spread_pos, CHECK_OK);
+
+ DCHECK(!spread_pos.IsValid());
+
+ return impl()->NewV8Intrinsic(name, args, pos, ok);
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseDoExpression(
+ bool* ok) {
+ // AssignmentExpression ::
+ // do '{' StatementList '}'
+
+ int pos = peek_position();
+ Expect(Token::DO, CHECK_OK);
+ BlockT block = ParseBlock(nullptr, CHECK_OK);
+ return impl()->RewriteDoExpression(block, pos, ok);
+}
+
+// Redefinition of CHECK_OK for parsing statements.
+#undef CHECK_OK
+#define CHECK_OK CHECK_OK_CUSTOM(NullStatement)
+
+template <typename Impl>
+typename ParserBase<Impl>::LazyParsingResult
+ParserBase<Impl>::ParseStatementList(StatementListT body, int end_token,
+ bool may_abort, 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.
+ typename Types::TargetScope target_scope(this);
+ int count_statements = 0;
+
+ DCHECK(!impl()->IsNullStatementList(body));
+ bool directive_prologue = true; // Parsing directive prologue.
+
+ 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();
+ StatementT stat =
+ ParseStatementListItem(CHECK_OK_CUSTOM(Return, kLazyParsingComplete));
+
+ if (impl()->IsNullStatement(stat) || impl()->IsEmptyStatement(stat)) {
+ directive_prologue = false; // End of directive prologue.
+ continue;
+ }
+
+ if (directive_prologue) {
+ // The length of the token is used to distinguish between strings literals
+ // that evaluate equal to directives but contain either escape sequences
+ // (e.g., "use \x73trict") or line continuations (e.g., "use \(newline)
+ // strict").
+ if (impl()->IsUseStrictDirective(stat) &&
+ token_loc.end_pos - token_loc.beg_pos == sizeof("use strict") + 1) {
+ // Directive "use strict" (ES5 14.1).
+ RaiseLanguageMode(STRICT);
+ 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
+ impl()->ReportMessageAt(
+ token_loc, MessageTemplate::kIllegalLanguageModeDirective,
+ "use strict");
+ *ok = false;
+ return kLazyParsingComplete;
+ }
+ // 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 (impl()->IsUseAsmDirective(stat) &&
+ token_loc.end_pos - token_loc.beg_pos ==
+ sizeof("use asm") + 1) {
+ // Directive "use asm".
+ impl()->SetAsmModule();
+ } else if (impl()->IsStringLiteral(stat)) {
+ // Possibly an unknown directive.
+ // Should not change mode, but will increment usage counters
+ // as appropriate. Ditto usages below.
+ RaiseLanguageMode(SLOPPY);
+ } else {
+ // End of the directive prologue.
+ directive_prologue = false;
+ RaiseLanguageMode(SLOPPY);
+ }
+ } else {
+ RaiseLanguageMode(SLOPPY);
+ }
+
+ // If we're allowed to abort, we will do so when we see a "long and
+ // trivial" function. Our current definition of "long and trivial" is:
+ // - over kLazyParseTrialLimit statements
+ // - all starting with an identifier (i.e., no if, for, while, etc.)
+ if (may_abort) {
+ if (!starts_with_identifier) {
+ may_abort = false;
+ } else if (++count_statements > kLazyParseTrialLimit) {
+ return kLazyParsingAborted;
+ }
+ }
+
+ body->Add(stat, zone());
+ }
+ return kLazyParsingComplete;
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::StatementT ParserBase<Impl>::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 ParseHoistableDeclaration(nullptr, false, ok);
+ case Token::CLASS:
+ Consume(Token::CLASS);
+ return ParseClassDeclaration(nullptr, false, ok);
+ case Token::VAR:
+ case Token::CONST:
+ return ParseVariableStatement(kStatementListItem, nullptr, ok);
+ case Token::LET:
+ if (IsNextLetKeyword()) {
+ return ParseVariableStatement(kStatementListItem, nullptr, ok);
+ }
+ break;
+ case Token::ASYNC:
+ if (allow_harmony_async_await() && PeekAhead() == Token::FUNCTION &&
+ !scanner()->HasAnyLineTerminatorAfterNext()) {
+ Consume(Token::ASYNC);
+ return ParseAsyncFunctionDeclaration(nullptr, false, ok);
+ }
+ /* falls through */
+ default:
+ break;
+ }
+ return ParseStatement(nullptr, kAllowLabelledFunctionStatement, ok);
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStatement(
+ ZoneList<const AstRawString*>* labels,
+ AllowLabelledFunctionStatement allow_function, 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:
+ Next();
+ return factory()->NewEmptyStatement(kNoSourcePosition);
+ 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, as the corresponding AST nodes do not currently store their
+ // labels.
+ // TODO(nikolaos, marja): Consider adding the labels to the AST nodes.
+ if (labels == nullptr) {
+ return ParseStatementAsUnlabelled(labels, ok);
+ } else {
+ BlockT result =
+ factory()->NewBlock(labels, 1, false, kNoSourcePosition);
+ typename Types::Target target(this, result);
+ StatementT statement = ParseStatementAsUnlabelled(labels, CHECK_OK);
+ 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 only allowed as a StatementListItem, not in
+ // an arbitrary Statement position. Exceptions such as
+ // ES#sec-functiondeclarations-in-ifstatement-statement-clauses
+ // are handled by calling ParseScopedStatement rather than
+ // ParseStatement directly.
+ impl()->ReportMessageAt(scanner()->peek_location(),
+ is_strict(language_mode())
+ ? MessageTemplate::kStrictFunction
+ : MessageTemplate::kSloppyFunction);
+ *ok = false;
+ return impl()->NullStatement();
+ case Token::DEBUGGER:
+ return ParseDebuggerStatement(ok);
+ case Token::VAR:
+ return ParseVariableStatement(kStatement, nullptr, ok);
+ default:
+ return ParseExpressionOrLabelledStatement(labels, allow_function, ok);
+ }
+}
+
+// This method parses a subset of statements (break, continue, return, throw,
+// try) which are to be grouped because they all require their labeles to be
+// preserved in an enclosing block.
+template <typename Impl>
+typename ParserBase<Impl>::StatementT
+ParserBase<Impl>::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 impl()->NullStatement();
+ }
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseBlock(
+ ZoneList<const AstRawString*>* labels, bool* ok) {
+ // Block ::
+ // '{' StatementList '}'
+
+ // Construct block expecting 16 statements.
+ BlockT body = factory()->NewBlock(labels, 16, false, kNoSourcePosition);
+
+ // Parse the statements and collect escaping labels.
+ Expect(Token::LBRACE, CHECK_OK_CUSTOM(NullBlock));
+ {
+ BlockState block_state(zone(), &scope_state_);
+ block_state.set_start_position(scanner()->location().beg_pos);
+ typename Types::Target target(this, body);
+
+ while (peek() != Token::RBRACE) {
+ StatementT stat = ParseStatementListItem(CHECK_OK_CUSTOM(NullBlock));
+ if (!impl()->IsNullStatement(stat) && !impl()->IsEmptyStatement(stat)) {
+ body->statements()->Add(stat, zone());
+ }
+ }
+
+ Expect(Token::RBRACE, CHECK_OK_CUSTOM(NullBlock));
+ block_state.set_end_position(scanner()->location().end_pos);
+ body->set_scope(block_state.FinalizedBlockScope());
+ }
+ return body;
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseScopedStatement(
+ ZoneList<const AstRawString*>* labels, bool legacy, bool* ok) {
+ if (is_strict(language_mode()) || peek() != Token::FUNCTION ||
+ (legacy && allow_harmony_restrictive_declarations())) {
+ return ParseStatement(labels, kDisallowLabelledFunctionStatement, ok);
+ } else {
+ if (legacy) {
+ impl()->CountUsage(v8::Isolate::kLegacyFunctionDeclaration);
+ }
+ // Make a block around the statement for a lexical binding
+ // is introduced by a FunctionDeclaration.
+ BlockState block_state(zone(), &scope_state_);
+ block_state.set_start_position(scanner()->location().beg_pos);
+ BlockT block = factory()->NewBlock(NULL, 1, false, kNoSourcePosition);
+ StatementT body = ParseFunctionDeclaration(CHECK_OK);
+ block->statements()->Add(body, zone());
+ block_state.set_end_position(scanner()->location().end_pos);
+ block->set_scope(block_state.FinalizedBlockScope());
+ return block;
+ }
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseVariableStatement(
+ VariableDeclarationContext var_context,
+ ZoneList<const AstRawString*>* names, bool* ok) {
+ // VariableStatement ::
+ // VariableDeclarations ';'
+
+ // The scope of a var 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 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;
+ StatementT result =
+ ParseVariableDeclarations(var_context, &parsing_result, names, CHECK_OK);
+ ExpectSemicolon(CHECK_OK);
+ return result;
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::StatementT ParserBase<Impl>::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);
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::StatementT
+ParserBase<Impl>::ParseExpressionOrLabelledStatement(
+ ZoneList<const AstRawString*>* labels,
+ AllowLabelledFunctionStatement allow_function, 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 impl()->NullStatement();
+ default:
+ break;
+ }
+
+ bool starts_with_identifier = peek_any_identifier();
+ ExpressionT expr = ParseExpression(true, CHECK_OK);
+ if (peek() == Token::COLON && starts_with_identifier &&
+ impl()->IsIdentifier(expr)) {
+ // The whole expression was a single identifier, and not, e.g.,
+ // something starting with an identifier or a parenthesized identifier.
+ labels = impl()->DeclareLabel(labels, impl()->AsIdentifierExpression(expr),
+ CHECK_OK);
+ Consume(Token::COLON);
+ // ES#sec-labelled-function-declarations Labelled Function Declarations
+ if (peek() == Token::FUNCTION && is_sloppy(language_mode())) {
+ if (allow_function == kAllowLabelledFunctionStatement) {
+ return ParseFunctionDeclaration(ok);
+ } else {
+ return ParseScopedStatement(labels, true, ok);
+ }
+ }
+ return ParseStatement(labels, kDisallowLabelledFunctionStatement, 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_ != nullptr && peek() == Token::FUNCTION &&
+ !scanner()->HasAnyLineTerminatorBeforeNext() && impl()->IsNative(expr) &&
+ !scanner()->literal_contains_escapes()) {
+ return ParseNativeDeclaration(ok);
+ }
+
+ // Parsed expression statement, followed by semicolon.
+ ExpectSemicolon(CHECK_OK);
+ return factory()->NewExpressionStatement(expr, pos);
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::StatementT ParserBase<Impl>::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);
+ ExpressionT condition = ParseExpression(true, CHECK_OK);
+ Expect(Token::RPAREN, CHECK_OK);
+ StatementT then_statement = ParseScopedStatement(labels, false, CHECK_OK);
+ StatementT else_statement = impl()->NullStatement();
+ if (Check(Token::ELSE)) {
+ else_statement = ParseScopedStatement(labels, false, CHECK_OK);
+ } else {
+ else_statement = factory()->NewEmptyStatement(kNoSourcePosition);
+ }
+ return factory()->NewIfStatement(condition, then_statement, else_statement,
+ pos);
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseContinueStatement(
+ bool* ok) {
+ // ContinueStatement ::
+ // 'continue' Identifier? ';'
+
+ int pos = peek_position();
+ Expect(Token::CONTINUE, CHECK_OK);
+ IdentifierT label = impl()->EmptyIdentifier();
+ 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);
+ }
+ typename Types::IterationStatement target =
+ impl()->LookupContinueTarget(label, CHECK_OK);
+ if (impl()->IsNullStatement(target)) {
+ // Illegal continue statement.
+ MessageTemplate::Template message = MessageTemplate::kIllegalContinue;
+ if (!impl()->IsEmptyIdentifier(label)) {
+ message = MessageTemplate::kUnknownLabel;
+ }
+ ReportMessage(message, label);
+ *ok = false;
+ return impl()->NullStatement();
+ }
+ ExpectSemicolon(CHECK_OK);
+ return factory()->NewContinueStatement(target, pos);
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseBreakStatement(
+ ZoneList<const AstRawString*>* labels, bool* ok) {
+ // BreakStatement ::
+ // 'break' Identifier? ';'
+
+ int pos = peek_position();
+ Expect(Token::BREAK, CHECK_OK);
+ IdentifierT label = impl()->EmptyIdentifier();
+ 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 (!impl()->IsEmptyIdentifier(label) &&
+ impl()->ContainsLabel(labels, label)) {
+ ExpectSemicolon(CHECK_OK);
+ return factory()->NewEmptyStatement(pos);
+ }
+ typename Types::BreakableStatement target =
+ impl()->LookupBreakTarget(label, CHECK_OK);
+ if (impl()->IsNullStatement(target)) {
+ // Illegal break statement.
+ MessageTemplate::Template message = MessageTemplate::kIllegalBreak;
+ if (!impl()->IsEmptyIdentifier(label)) {
+ message = MessageTemplate::kUnknownLabel;
+ }
+ ReportMessage(message, label);
+ *ok = false;
+ return impl()->NullStatement();
+ }
+ ExpectSemicolon(CHECK_OK);
+ return factory()->NewBreakStatement(target, pos);
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseReturnStatement(
+ bool* ok) {
+ // ReturnStatement ::
+ // 'return' [no line terminator] 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();
+
+ switch (GetDeclarationScope()->scope_type()) {
+ case SCRIPT_SCOPE:
+ case EVAL_SCOPE:
+ case MODULE_SCOPE:
+ impl()->ReportMessageAt(loc, MessageTemplate::kIllegalReturn);
+ *ok = false;
+ return impl()->NullStatement();
+ default:
+ break;
+ }
+
+ Token::Value tok = peek();
+ ExpressionT return_value = impl()->EmptyExpression();
+ if (scanner()->HasAnyLineTerminatorBeforeNext() || tok == Token::SEMICOLON ||
+ tok == Token::RBRACE || tok == Token::EOS) {
+ if (IsSubclassConstructor(function_state_->kind())) {
+ return_value = impl()->ThisExpression(loc.beg_pos);
+ } else {
+ return_value = impl()->GetLiteralUndefined(position());
+ }
+ } else {
+ if (IsSubclassConstructor(function_state_->kind())) {
+ // Because of the return code rewriting that happens in case of a subclass
+ // constructor we don't want to accept tail calls, therefore we don't set
+ // ReturnExprScope to kInsideValidReturnStatement here.
+ return_value = ParseExpression(true, CHECK_OK);
+ } else {
+ ReturnExprScope maybe_allow_tail_calls(
+ function_state_, ReturnExprContext::kInsideValidReturnStatement);
+ return_value = ParseExpression(true, CHECK_OK);
+
+ if (allow_tailcalls() && !is_sloppy(language_mode()) && !is_resumable()) {
+ // ES6 14.6.1 Static Semantics: IsInTailPosition
+ function_state_->AddImplicitTailCallExpression(return_value);
+ }
+ }
+ }
+ ExpectSemicolon(CHECK_OK);
+ return_value = impl()->RewriteReturn(return_value, loc.beg_pos);
+ return factory()->NewReturnStatement(return_value, loc.beg_pos);
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::StatementT ParserBase<Impl>::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 impl()->NullStatement();
+ }
+
+ Expect(Token::LPAREN, CHECK_OK);
+ ExpressionT expr = ParseExpression(true, CHECK_OK);
+ Expect(Token::RPAREN, CHECK_OK);
+
+ Scope* with_scope = NewScope(WITH_SCOPE);
+ StatementT body = impl()->NullStatement();
+ {
+ BlockState block_state(&scope_state_, with_scope);
+ with_scope->set_start_position(scanner()->peek_location().beg_pos);
+ body = ParseScopedStatement(labels, true, CHECK_OK);
+ with_scope->set_end_position(scanner()->location().end_pos);
+ }
+ return factory()->NewWithStatement(with_scope, expr, body, pos);
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseDoWhileStatement(
+ ZoneList<const AstRawString*>* labels, bool* ok) {
+ // DoStatement ::
+ // 'do' Statement 'while' '(' Expression ')' ';'
+
+ auto loop = factory()->NewDoWhileStatement(labels, peek_position());
+ typename Types::Target target(this, loop);
+
+ Expect(Token::DO, CHECK_OK);
+ StatementT body = ParseScopedStatement(nullptr, true, CHECK_OK);
+ Expect(Token::WHILE, CHECK_OK);
+ Expect(Token::LPAREN, CHECK_OK);
+
+ ExpressionT 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.
+ Check(Token::SEMICOLON);
+
+ loop->Initialize(cond, body);
+ return loop;
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseWhileStatement(
+ ZoneList<const AstRawString*>* labels, bool* ok) {
+ // WhileStatement ::
+ // 'while' '(' Expression ')' Statement
+
+ auto loop = factory()->NewWhileStatement(labels, peek_position());
+ typename Types::Target target(this, loop);
+
+ Expect(Token::WHILE, CHECK_OK);
+ Expect(Token::LPAREN, CHECK_OK);
+ ExpressionT cond = ParseExpression(true, CHECK_OK);
+ Expect(Token::RPAREN, CHECK_OK);
+ StatementT body = ParseScopedStatement(nullptr, true, CHECK_OK);
+
+ loop->Initialize(cond, body);
+ return loop;
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseThrowStatement(
+ bool* ok) {
+ // ThrowStatement ::
+ // 'throw' Expression ';'
+
+ Expect(Token::THROW, CHECK_OK);
+ int pos = position();
+ if (scanner()->HasAnyLineTerminatorBeforeNext()) {
+ ReportMessage(MessageTemplate::kNewlineAfterThrow);
+ *ok = false;
+ return impl()->NullStatement();
+ }
+ ExpressionT exception = ParseExpression(true, CHECK_OK);
+ ExpectSemicolon(CHECK_OK);
+
+ return impl()->NewThrowStatement(exception, pos);
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseSwitchStatement(
+ ZoneList<const AstRawString*>* labels, bool* ok) {
+ // SwitchStatement ::
+ // 'switch' '(' Expression ')' '{' CaseClause* '}'
+ // CaseClause ::
+ // 'case' Expression ':' StatementList
+ // 'default' ':' StatementList
+
+ int switch_pos = peek_position();
+
+ Expect(Token::SWITCH, CHECK_OK);
+ Expect(Token::LPAREN, CHECK_OK);
+ ExpressionT tag = ParseExpression(true, CHECK_OK);
+ Expect(Token::RPAREN, CHECK_OK);
+
+ auto switch_statement = factory()->NewSwitchStatement(labels, switch_pos);
+
+ {
+ BlockState cases_block_state(zone(), &scope_state_);
+ cases_block_state.set_start_position(scanner()->location().beg_pos);
+ cases_block_state.SetNonlinear();
+ typename Types::Target target(this, switch_statement);
+
+ bool default_seen = false;
+ auto cases = impl()->NewCaseClauseList(4);
+ Expect(Token::LBRACE, CHECK_OK);
+ while (peek() != Token::RBRACE) {
+ // An empty label indicates the default case.
+ ExpressionT label = impl()->EmptyExpression();
+ if (Check(Token::CASE)) {
+ label = ParseExpression(true, CHECK_OK);
+ } else {
+ Expect(Token::DEFAULT, CHECK_OK);
+ if (default_seen) {
+ ReportMessage(MessageTemplate::kMultipleDefaultsInSwitch);
+ *ok = false;
+ return impl()->NullStatement();
+ }
+ default_seen = true;
+ }
+ Expect(Token::COLON, CHECK_OK);
+ int clause_pos = position();
+ StatementListT statements = impl()->NewStatementList(5);
+ while (peek() != Token::CASE && peek() != Token::DEFAULT &&
+ peek() != Token::RBRACE) {
+ StatementT stat = ParseStatementListItem(CHECK_OK);
+ statements->Add(stat, zone());
+ }
+ auto clause = factory()->NewCaseClause(label, statements, clause_pos);
+ cases->Add(clause, zone());
+ }
+ Expect(Token::RBRACE, CHECK_OK);
+
+ cases_block_state.set_end_position(scanner()->location().end_pos);
+ return impl()->RewriteSwitchStatement(
+ tag, switch_statement, cases, cases_block_state.FinalizedBlockScope());
+ }
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::StatementT ParserBase<Impl>::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();
+
+ BlockT try_block = impl()->NullBlock();
+ {
+ ReturnExprScope no_tail_calls(function_state_,
+ ReturnExprContext::kInsideTryBlock);
+ try_block = ParseBlock(nullptr, CHECK_OK);
+ }
+
+ CatchInfo catch_info(this);
+ catch_info.for_promise_reject = allow_natives() && Check(Token::MOD);
+
+ if (peek() != Token::CATCH && peek() != Token::FINALLY) {
+ ReportMessage(MessageTemplate::kNoCatchOrFinally);
+ *ok = false;
+ return impl()->NullStatement();
+ }
+
+ BlockT catch_block = impl()->NullBlock();
+ if (Check(Token::CATCH)) {
+ Expect(Token::LPAREN, CHECK_OK);
+ catch_info.scope = NewScope(CATCH_SCOPE);
+ catch_info.scope->set_start_position(scanner()->location().beg_pos);
+
+ {
+ CollectExpressionsInTailPositionToListScope
+ collect_tail_call_expressions_scope(
+ function_state_, &catch_info.tail_call_expressions);
+ BlockState catch_block_state(&scope_state_, catch_info.scope);
+
+ catch_block = factory()->NewBlock(nullptr, 16, false, kNoSourcePosition);
+
+ // Create a block scope to hold any lexical declarations created
+ // as part of destructuring the catch parameter.
+ {
+ BlockState catch_variable_block_state(zone(), &scope_state_);
+ catch_variable_block_state.set_start_position(
+ scanner()->location().beg_pos);
+ typename Types::Target target(this, catch_block);
+
+ // This does not simply call ParsePrimaryExpression to avoid
+ // ExpressionFromIdentifier from being called in the first
+ // branch, which would introduce an unresolved symbol and mess
+ // with arrow function names.
+ if (peek_any_identifier()) {
+ catch_info.name =
+ ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK);
+ } else {
+ ExpressionClassifier pattern_classifier(this);
+ catch_info.pattern = ParsePrimaryExpression(CHECK_OK);
+ ValidateBindingPattern(CHECK_OK);
+ }
+
+ Expect(Token::RPAREN, CHECK_OK);
+ impl()->RewriteCatchPattern(&catch_info, CHECK_OK);
+ if (!impl()->IsNullStatement(catch_info.init_block)) {
+ catch_block->statements()->Add(catch_info.init_block, zone());
+ }
+
+ catch_info.inner_block = ParseBlock(nullptr, CHECK_OK);
+ catch_block->statements()->Add(catch_info.inner_block, zone());
+ impl()->ValidateCatchBlock(catch_info, CHECK_OK);
+ catch_variable_block_state.set_end_position(
+ scanner()->location().end_pos);
+ catch_block->set_scope(
+ catch_variable_block_state.FinalizedBlockScope());
+ }
+ }
+
+ catch_info.scope->set_end_position(scanner()->location().end_pos);
+ }
+
+ BlockT finally_block = impl()->NullBlock();
+ DCHECK(peek() == Token::FINALLY || !impl()->IsNullStatement(catch_block));
+ if (Check(Token::FINALLY)) {
+ finally_block = ParseBlock(nullptr, CHECK_OK);
+ }
+
+ return impl()->RewriteTryStatement(try_block, catch_block, finally_block,
+ catch_info, pos);
+}
+
+template <typename Impl>
+typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForStatement(
+ ZoneList<const AstRawString*>* labels, bool* ok) {
+ int stmt_pos = peek_position();
+ ForInfo for_info(this);
+ bool bound_names_are_lexical = false;
+
+ // Create an in-between scope for let-bound iteration variables.
+ BlockState for_state(zone(), &scope_state_);
+ Expect(Token::FOR, CHECK_OK);
+ Expect(Token::LPAREN, CHECK_OK);
+ for_state.set_start_position(scanner()->location().beg_pos);
+ for_state.set_is_hidden();
+
+ StatementT init = impl()->NullStatement();
+ if (peek() != Token::SEMICOLON) {
+ // An initializer is present.
+ if (peek() == Token::VAR || peek() == Token::CONST ||
+ (peek() == Token::LET && IsNextLetKeyword())) {
+ // The initializer contains declarations.
+ ParseVariableDeclarations(kForStatement, &for_info.parsing_result,
+ nullptr, CHECK_OK);
+ bound_names_are_lexical =
+ IsLexicalVariableMode(for_info.parsing_result.descriptor.mode);
+ for_info.each_loc = scanner()->location();
+
+ if (CheckInOrOf(&for_info.mode)) {
+ // Just one declaration followed by in/of.
+ if (for_info.parsing_result.declarations.length() != 1) {
+ impl()->ReportMessageAt(
+ for_info.parsing_result.bindings_loc,
+ MessageTemplate::kForInOfLoopMultiBindings,
+ ForEachStatement::VisitModeString(for_info.mode));
+ *ok = false;
+ return impl()->NullStatement();
+ }
+ if (for_info.parsing_result.first_initializer_loc.IsValid() &&
+ (is_strict(language_mode()) ||
+ for_info.mode == ForEachStatement::ITERATE ||
+ bound_names_are_lexical ||
+ !impl()->IsIdentifier(
+ for_info.parsing_result.declarations[0].pattern) ||
+ allow_harmony_for_in())) {
+ // Only increment the use count if we would have let this through
+ // without the flag.
+ if (allow_harmony_for_in()) {
+ impl()->CountUsage(v8::Isolate::kForInInitializer);
+ }
+ impl()->ReportMessageAt(
+ for_info.parsing_result.first_initializer_loc,
+ MessageTemplate::kForInOfLoopInitializer,
+ ForEachStatement::VisitModeString(for_info.mode));
+ *ok = false;
+ return impl()->NullStatement();
+ }
+
+ BlockT init_block = impl()->RewriteForVarInLegacy(for_info);
+
+ auto loop =
+ factory()->NewForEachStatement(for_info.mode, labels, stmt_pos);
+ typename Types::Target target(this, loop);
+
+ int each_keyword_pos = scanner()->location().beg_pos;
+
+ ExpressionT enumerable = impl()->EmptyExpression();
+ if (for_info.mode == ForEachStatement::ITERATE) {
+ ExpressionClassifier classifier(this);
+ enumerable = ParseAssignmentExpression(true, CHECK_OK);
+ impl()->RewriteNonPattern(CHECK_OK);
+ } else {
+ enumerable = ParseExpression(true, CHECK_OK);
+ }
+
+ Expect(Token::RPAREN, CHECK_OK);
+
+ StatementT final_loop = impl()->NullStatement();
+ {
+ ReturnExprScope no_tail_calls(function_state_,
+ ReturnExprContext::kInsideForInOfBody);
+ BlockState block_state(zone(), &scope_state_);
+ block_state.set_start_position(scanner()->location().beg_pos);
+
+ StatementT body = ParseScopedStatement(nullptr, true, CHECK_OK);
+
+ BlockT body_block = impl()->NullBlock();
+ ExpressionT each_variable = impl()->EmptyExpression();
+ impl()->DesugarBindingInForEachStatement(&for_info, &body_block,
+ &each_variable, CHECK_OK);
+ body_block->statements()->Add(body, zone());
+ final_loop = impl()->InitializeForEachStatement(
+ loop, each_variable, enumerable, body_block, each_keyword_pos);
+
+ block_state.set_end_position(scanner()->location().end_pos);
+ body_block->set_scope(block_state.FinalizedBlockScope());
+ }
+
+ init_block =
+ impl()->CreateForEachStatementTDZ(init_block, for_info, ok);
+
+ for_state.set_end_position(scanner()->location().end_pos);
+ Scope* for_scope = for_state.FinalizedBlockScope();
+ // Parsed for-in loop w/ variable declarations.
+ if (!impl()->IsNullStatement(init_block)) {
+ init_block->statements()->Add(final_loop, zone());
+ init_block->set_scope(for_scope);
+ return init_block;
+ } else {
+ DCHECK_NULL(for_scope);
+ return final_loop;
+ }
+ } else {
+ // One or more declaration not followed by in/of.
+ init = impl()->BuildInitializationBlock(
+ &for_info.parsing_result,
+ bound_names_are_lexical ? &for_info.bound_names : nullptr,
+ CHECK_OK);
+ }
+ } else {
+ // The initializer does not contain declarations.
+ int lhs_beg_pos = peek_position();
+ ExpressionClassifier classifier(this);
+ ExpressionT expression = ParseExpressionCoverGrammar(false, CHECK_OK);
+ int lhs_end_pos = scanner()->location().end_pos;
+
+ bool is_for_each = CheckInOrOf(&for_info.mode);
+ bool is_destructuring = is_for_each && (expression->IsArrayLiteral() ||
+ expression->IsObjectLiteral());
+
+ if (is_destructuring) {
+ ValidateAssignmentPattern(CHECK_OK);
+ } else {
+ impl()->RewriteNonPattern(CHECK_OK);
+ }
+
+ if (is_for_each) {
+ // Initializer is reference followed by in/of.
+ if (!is_destructuring) {
+ expression = impl()->CheckAndRewriteReferenceExpression(
+ expression, lhs_beg_pos, lhs_end_pos,
+ MessageTemplate::kInvalidLhsInFor, kSyntaxError, CHECK_OK);
+ }
+
+ auto loop =
+ factory()->NewForEachStatement(for_info.mode, labels, stmt_pos);
+ typename Types::Target target(this, loop);
+
+ int each_keyword_pos = scanner()->location().beg_pos;
+
+ ExpressionT enumerable = impl()->EmptyExpression();
+ if (for_info.mode == ForEachStatement::ITERATE) {
+ ExpressionClassifier classifier(this);
+ enumerable = ParseAssignmentExpression(true, CHECK_OK);
+ impl()->RewriteNonPattern(CHECK_OK);
+ } else {
+ enumerable = ParseExpression(true, CHECK_OK);
+ }
+
+ Expect(Token::RPAREN, CHECK_OK);
+
+ {
+ ReturnExprScope no_tail_calls(function_state_,
+ ReturnExprContext::kInsideForInOfBody);
+ BlockState block_state(zone(), &scope_state_);
+ block_state.set_start_position(scanner()->location().beg_pos);
+
+ // For legacy compat reasons, give for loops similar treatment to
+ // if statements in allowing a function declaration for a body
+ StatementT body = ParseScopedStatement(nullptr, true, CHECK_OK);
+ block_state.set_end_position(scanner()->location().end_pos);
+ StatementT final_loop = impl()->InitializeForEachStatement(
+ loop, expression, enumerable, body, each_keyword_pos);
+
+ Scope* for_scope = for_state.FinalizedBlockScope();
+ DCHECK_NULL(for_scope);
+ USE(for_scope);
+ Scope* block_scope = block_state.FinalizedBlockScope();
+ DCHECK_NULL(block_scope);
+ USE(block_scope);
+ return final_loop;
+ }
+ } else {
+ // Initializer is just an expression.
+ init = factory()->NewExpressionStatement(expression, lhs_beg_pos);
+ }
+ }
+ }
+
+ // Standard 'for' loop, we have parsed the initializer at this point.
+ auto loop = factory()->NewForStatement(labels, stmt_pos);
+ typename Types::Target target(this, loop);
+
+ Expect(Token::SEMICOLON, CHECK_OK);
+
+ ExpressionT cond = impl()->EmptyExpression();
+ StatementT next = impl()->NullStatement();
+ StatementT body = impl()->NullStatement();
+
+ // 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 = scope();
+ // TODO(verwaest): Allocate this through a ScopeState as well.
+ if (bound_names_are_lexical && for_info.bound_names.length() > 0) {
+ inner_scope = NewScopeWithParent(inner_scope, BLOCK_SCOPE);
+ inner_scope->set_start_position(scanner()->location().beg_pos);
+ }
+ {
+ BlockState block_state(&scope_state_, inner_scope);
+
+ if (peek() != Token::SEMICOLON) {
+ cond = ParseExpression(true, CHECK_OK);
+ }
+ Expect(Token::SEMICOLON, CHECK_OK);
+
+ if (peek() != Token::RPAREN) {
+ ExpressionT exp = ParseExpression(true, CHECK_OK);
+ next = factory()->NewExpressionStatement(exp, exp->position());
+ }
+ Expect(Token::RPAREN, CHECK_OK);
+
+ body = ParseScopedStatement(nullptr, true, CHECK_OK);
+ }
+
+ if (bound_names_are_lexical && for_info.bound_names.length() > 0) {
+ auto result = impl()->DesugarLexicalBindingsInForStatement(
+ loop, init, cond, next, body, inner_scope, for_info, CHECK_OK);
+ for_state.set_end_position(scanner()->location().end_pos);
+ return result;
+ } else {
+ for_state.set_end_position(scanner()->location().end_pos);
+ Scope* for_scope = for_state.FinalizedBlockScope();
+ if (for_scope != nullptr) {
+ // 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.
+ BlockT block = factory()->NewBlock(nullptr, 2, false, kNoSourcePosition);
+ if (!impl()->IsNullStatement(init)) {
+ block->statements()->Add(init, zone());
+ }
+ block->statements()->Add(loop, zone());
+ block->set_scope(for_scope);
+ loop->Initialize(init, cond, next, body);
+ return block;
+ } else {
+ loop->Initialize(init, cond, next, body);
+ return loop;
+ }
+ }
+}
#undef CHECK_OK
#undef CHECK_OK_CUSTOM
template <typename Impl>
-void ParserBase<Impl>::ObjectLiteralChecker::CheckProperty(
- Token::Value property, PropertyKind type, MethodKind method_type,
- ExpressionClassifier* classifier, bool* ok) {
- DCHECK(!IsStaticMethod(method_type));
- DCHECK(!IsSpecialMethod(method_type) || type == kMethodProperty);
-
+void ParserBase<Impl>::ObjectLiteralChecker::CheckDuplicateProto(
+ Token::Value property) {
if (property == Token::SMI || property == Token::NUMBER) return;
- if (type == kValueProperty && IsProto()) {
+ if (IsProto()) {
if (has_seen_proto_) {
- classifier->RecordObjectLiteralError(
+ this->parser()->classifier()->RecordExpressionError(
this->scanner()->location(), MessageTemplate::kDuplicateProto);
return;
}
@@ -3698,23 +5423,22 @@
}
template <typename Impl>
-void ParserBase<Impl>::ClassLiteralChecker::CheckProperty(
- Token::Value property, PropertyKind type, MethodKind method_type,
- ExpressionClassifier* classifier, bool* ok) {
- DCHECK(type == kMethodProperty || type == kAccessorProperty);
+void ParserBase<Impl>::ClassLiteralChecker::CheckClassMethodName(
+ Token::Value property, PropertyKind type, bool is_generator, bool is_async,
+ bool is_static, bool* ok) {
+ DCHECK(type == PropertyKind::kMethodProperty ||
+ type == PropertyKind::kAccessorProperty);
if (property == Token::SMI || property == Token::NUMBER) return;
- if (IsStaticMethod(method_type)) {
+ if (is_static) {
if (IsPrototype()) {
this->parser()->ReportMessage(MessageTemplate::kStaticPrototype);
*ok = false;
return;
}
} else if (IsConstructor()) {
- const bool is_generator = IsGeneratorMethod(method_type);
- const bool is_async = IsAsyncMethod(method_type);
- if (is_generator || is_async || type == kAccessorProperty) {
+ if (is_generator || is_async || type == PropertyKind::kAccessorProperty) {
MessageTemplate::Template msg =
is_generator ? MessageTemplate::kConstructorIsGenerator
: is_async ? MessageTemplate::kConstructorIsAsync
diff --git a/src/parsing/parser.cc b/src/parsing/parser.cc
index cfc2de8..7b88695 100644
--- a/src/parsing/parser.cc
+++ b/src/parsing/parser.cc
@@ -15,6 +15,7 @@
#include "src/base/platform/platform.h"
#include "src/char-predicates-inl.h"
#include "src/messages.h"
+#include "src/parsing/duplicate-finder.h"
#include "src/parsing/parameter-initializer-rewriter.h"
#include "src/parsing/parse-info.h"
#include "src/parsing/rewriter.h"
@@ -121,12 +122,20 @@
if (use_temp_zone) {
parser_->fni_ = &fni_;
parser_->zone_ = temp_zone;
+ if (parser_->reusable_preparser_ != nullptr) {
+ parser_->reusable_preparser_->zone_ = temp_zone;
+ }
}
}
- ~DiscardableZoneScope() {
+ void Reset() {
parser_->fni_ = prev_fni_;
parser_->zone_ = prev_zone_;
+ if (parser_->reusable_preparser_ != nullptr) {
+ parser_->reusable_preparser_->zone_ = prev_zone_;
+ }
+ ast_node_factory_scope_.Reset();
}
+ ~DiscardableZoneScope() { Reset(); }
private:
AstNodeFactory::BodyScope ast_node_factory_scope_;
@@ -149,9 +158,64 @@
}
}
+Expression* Parser::CallClassFieldInitializer(Scope* scope,
+ Expression* this_expr) {
+ // This produces the expression
+ // `.class_field_intializer(this_expr)`, where '.class_field_intializer' is
+ // the name
+ // of a synthetic variable.
+ // 'this_expr' will be 'this' in a base constructor and the result of calling
+ // 'super' in a derived one.
+ const AstRawString* init_fn_name =
+ ast_value_factory()->dot_class_field_init_string();
+ VariableProxy* init_fn_proxy = scope->NewUnresolved(factory(), init_fn_name);
+ ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(2, zone());
+ args->Add(init_fn_proxy, zone());
+ args->Add(this_expr, zone());
+ return factory()->NewCallRuntime(Runtime::kInlineCall, args,
+ kNoSourcePosition);
+}
+
+Expression* Parser::RewriteSuperCall(Expression* super_call) {
+ // TODO(bakkot) find a way to avoid this for classes without fields.
+ if (!allow_harmony_class_fields()) {
+ return super_call;
+ }
+ // This turns a super call `super()` into a do expression of the form
+ // do {
+ // tmp x = super();
+ // if (.class-field-init)
+ // .class-field-init(x)
+ // x; // This isn't actually present; our do-expression representation
+ // allows specifying that the expression returns x directly.
+ // }
+ Variable* var_tmp =
+ scope()->NewTemporary(ast_value_factory()->empty_string());
+ Block* block = factory()->NewBlock(nullptr, 1, false, kNoSourcePosition);
+ Assignment* assignment = factory()->NewAssignment(
+ Token::ASSIGN, factory()->NewVariableProxy(var_tmp), super_call,
+ kNoSourcePosition);
+ block->statements()->Add(
+ factory()->NewExpressionStatement(assignment, kNoSourcePosition), zone());
+ const AstRawString* init_fn_name =
+ ast_value_factory()->dot_class_field_init_string();
+ VariableProxy* init_fn_proxy =
+ scope()->NewUnresolved(factory(), init_fn_name);
+ Expression* condition = init_fn_proxy;
+ Statement* initialize = factory()->NewExpressionStatement(
+ CallClassFieldInitializer(scope(), factory()->NewVariableProxy(var_tmp)),
+ kNoSourcePosition);
+ IfStatement* if_statement = factory()->NewIfStatement(
+ condition, initialize, factory()->NewEmptyStatement(kNoSourcePosition),
+ kNoSourcePosition);
+ block->statements()->Add(if_statement, zone());
+ return factory()->NewDoExpression(block, var_tmp, kNoSourcePosition);
+}
+
FunctionLiteral* Parser::DefaultConstructor(const AstRawString* name,
- bool call_super, int pos,
- int end_pos,
+ bool call_super,
+ bool requires_class_field_init,
+ int pos, int end_pos,
LanguageMode language_mode) {
int materialized_literal_count = -1;
int expected_property_count = -1;
@@ -170,7 +234,7 @@
{
FunctionState function_state(&function_state_, &scope_state_,
- function_scope, kind);
+ function_scope);
body = new (zone()) ZoneList<Statement*>(call_super ? 2 : 1, zone());
if (call_super) {
@@ -204,8 +268,11 @@
VariableProxy* new_target_proxy =
NewUnresolved(ast_value_factory()->new_target_string(), pos);
args->Add(new_target_proxy, zone());
- CallRuntime* call = factory()->NewCallRuntime(
+ Expression* call = factory()->NewCallRuntime(
Context::REFLECT_CONSTRUCT_INDEX, args, pos);
+ if (requires_class_field_init) {
+ call = CallClassFieldInitializer(scope(), call);
+ }
body->Add(factory()->NewReturnStatement(call, pos), zone());
}
@@ -218,7 +285,9 @@
expected_property_count, parameter_count,
FunctionLiteral::kNoDuplicateParameters,
FunctionLiteral::kAnonymousExpression,
- FunctionLiteral::kShouldLazyCompile, kind, pos);
+ FunctionLiteral::kShouldLazyCompile, pos);
+
+ function_literal->set_requires_class_field_init(requires_class_field_init);
return function_literal;
}
@@ -230,41 +299,39 @@
// 'continue' statement targets). Upon construction, a new target is
// added; it is removed upon destruction.
-class Target BASE_EMBEDDED {
+class ParserTarget BASE_EMBEDDED {
public:
- Target(Target** variable, BreakableStatement* statement)
- : variable_(variable), statement_(statement), previous_(*variable) {
- *variable = this;
+ ParserTarget(ParserBase<Parser>* parser, BreakableStatement* statement)
+ : variable_(&parser->impl()->target_stack_),
+ statement_(statement),
+ previous_(parser->impl()->target_stack_) {
+ parser->impl()->target_stack_ = this;
}
- ~Target() {
- *variable_ = previous_;
- }
+ ~ParserTarget() { *variable_ = previous_; }
- Target* previous() { return previous_; }
+ ParserTarget* previous() { return previous_; }
BreakableStatement* statement() { return statement_; }
private:
- Target** variable_;
+ ParserTarget** variable_;
BreakableStatement* statement_;
- Target* previous_;
+ ParserTarget* previous_;
};
-
-class TargetScope BASE_EMBEDDED {
+class ParserTargetScope BASE_EMBEDDED {
public:
- explicit TargetScope(Target** variable)
- : variable_(variable), previous_(*variable) {
- *variable = NULL;
+ explicit ParserTargetScope(ParserBase<Parser>* parser)
+ : variable_(&parser->impl()->target_stack_),
+ previous_(parser->impl()->target_stack_) {
+ parser->impl()->target_stack_ = nullptr;
}
- ~TargetScope() {
- *variable_ = previous_;
- }
+ ~ParserTargetScope() { *variable_ = previous_; }
private:
- Target** variable_;
- Target* previous_;
+ ParserTarget** variable_;
+ ParserTarget* previous_;
};
@@ -276,17 +343,14 @@
// 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 nullptr; \
+#define CHECK_OK_VALUE(x) ok); \
+ if (!*ok) return x; \
((void)0
#define DUMMY ) // to make indentation work
#undef DUMMY
-#define CHECK_OK_VOID ok); \
- if (!*ok) return; \
- ((void)0
-#define DUMMY ) // to make indentation work
-#undef DUMMY
+#define CHECK_OK CHECK_OK_VALUE(nullptr)
+#define CHECK_OK_VOID CHECK_OK_VALUE(this->Void())
#define CHECK_FAILED /**/); \
if (failed_) return nullptr; \
@@ -297,76 +361,9 @@
// ----------------------------------------------------------------------------
// Implementation of Parser
-bool ParserBaseTraits<Parser>::IsEval(const AstRawString* identifier) const {
- return identifier == delegate()->ast_value_factory()->eval_string();
-}
-
-bool ParserBaseTraits<Parser>::IsArguments(
- const AstRawString* identifier) const {
- return identifier == delegate()->ast_value_factory()->arguments_string();
-}
-
-bool ParserBaseTraits<Parser>::IsEvalOrArguments(
- const AstRawString* identifier) const {
- return IsEval(identifier) || IsArguments(identifier);
-}
-
-bool ParserBaseTraits<Parser>::IsUndefined(
- const AstRawString* identifier) const {
- return identifier == delegate()->ast_value_factory()->undefined_string();
-}
-
-bool ParserBaseTraits<Parser>::IsPrototype(
- const AstRawString* identifier) const {
- return identifier == delegate()->ast_value_factory()->prototype_string();
-}
-
-bool ParserBaseTraits<Parser>::IsConstructor(
- const AstRawString* identifier) const {
- return identifier == delegate()->ast_value_factory()->constructor_string();
-}
-
-bool ParserBaseTraits<Parser>::IsThisProperty(Expression* expression) {
- DCHECK(expression != NULL);
- Property* property = expression->AsProperty();
- return property != NULL && property->obj()->IsVariableProxy() &&
- property->obj()->AsVariableProxy()->is_this();
-}
-
-bool ParserBaseTraits<Parser>::IsIdentifier(Expression* expression) {
- VariableProxy* operand = expression->AsVariableProxy();
- return operand != NULL && !operand->is_this();
-}
-
-void ParserBaseTraits<Parser>::PushPropertyName(FuncNameInferrer* fni,
- Expression* expression) {
- if (expression->IsPropertyName()) {
- fni->PushLiteralName(expression->AsLiteral()->AsRawPropertyName());
- } else {
- fni->PushLiteralName(
- delegate()->ast_value_factory()->anonymous_function_string());
- }
-}
-
-void ParserBaseTraits<Parser>::CheckAssigningFunctionLiteralToProperty(
- Expression* left, Expression* right) {
- DCHECK(left != NULL);
- if (left->IsProperty() && right->IsFunctionLiteral()) {
- right->AsFunctionLiteral()->set_pretenure();
- }
-}
-
-Expression* ParserBaseTraits<Parser>::MarkExpressionAsAssigned(
- Expression* expression) {
- VariableProxy* proxy =
- expression != NULL ? expression->AsVariableProxy() : NULL;
- if (proxy != NULL) proxy->set_is_assigned();
- return expression;
-}
-
-bool ParserBaseTraits<Parser>::ShortcutNumericLiteralBinaryExpression(
- Expression** x, Expression* y, Token::Value op, int pos,
- AstNodeFactory* factory) {
+bool Parser::ShortcutNumericLiteralBinaryExpression(Expression** x,
+ Expression* y,
+ Token::Value op, int pos) {
if ((*x)->AsLiteral() && (*x)->AsLiteral()->raw_value()->IsNumber() &&
y->AsLiteral() && y->AsLiteral()->raw_value()->IsNumber()) {
double x_val = (*x)->AsLiteral()->raw_value()->AsNumber();
@@ -376,53 +373,53 @@
bool has_dot = x_has_dot || y_has_dot;
switch (op) {
case Token::ADD:
- *x = factory->NewNumberLiteral(x_val + y_val, pos, has_dot);
+ *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);
+ *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);
+ *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);
+ *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);
+ *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);
+ *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);
+ *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);
+ *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);
+ *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);
+ *x = factory()->NewNumberLiteral(value, pos, has_dot);
return true;
}
case Token::EXP: {
double value = Pow(x_val, y_val);
int int_value = static_cast<int>(value);
- *x = factory->NewNumberLiteral(
+ *x = factory()->NewNumberLiteral(
int_value == value && value != -0.0 ? int_value : value, pos,
has_dot);
return true;
@@ -434,15 +431,15 @@
return false;
}
-Expression* ParserBaseTraits<Parser>::BuildUnaryExpression(
- Expression* expression, Token::Value op, int pos, AstNodeFactory* factory) {
+Expression* Parser::BuildUnaryExpression(Expression* expression,
+ Token::Value op, int pos) {
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);
+ return factory()->NewBooleanLiteral(!condition, pos);
} else if (literal->IsNumber()) {
// Compute some expressions involving only number literals.
double value = literal->AsNumber();
@@ -451,9 +448,10 @@
case Token::ADD:
return expression;
case Token::SUB:
- return factory->NewNumberLiteral(-value, pos, has_dot);
+ return factory()->NewNumberLiteral(-value, pos, has_dot);
case Token::BIT_NOT:
- return factory->NewNumberLiteral(~DoubleToInt32(value), pos, has_dot);
+ return factory()->NewNumberLiteral(~DoubleToInt32(value), pos,
+ has_dot);
default:
break;
}
@@ -461,53 +459,33 @@
}
// Desugar '+foo' => 'foo*1'
if (op == Token::ADD) {
- return factory->NewBinaryOperation(
- Token::MUL, expression, factory->NewNumberLiteral(1, pos, true), pos);
+ 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);
+ 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()->NewBinaryOperation(
+ Token::BIT_XOR, expression, factory()->NewNumberLiteral(~0, pos), pos);
}
- return factory->NewUnaryOperation(op, expression, pos);
+ return factory()->NewUnaryOperation(op, expression, pos);
}
-Expression* ParserBaseTraits<Parser>::BuildIteratorResult(Expression* value,
- bool done) {
+Expression* Parser::BuildIteratorResult(Expression* value, bool done) {
int pos = kNoSourcePosition;
- AstNodeFactory* factory = delegate()->factory();
- Zone* zone = delegate()->zone();
- if (value == nullptr) value = factory->NewUndefinedLiteral(pos);
+ if (value == nullptr) value = factory()->NewUndefinedLiteral(pos);
- auto args = new (zone) ZoneList<Expression*>(2, zone);
- args->Add(value, zone);
- args->Add(factory->NewBooleanLiteral(done, pos), zone);
+ auto args = new (zone()) ZoneList<Expression*>(2, zone());
+ args->Add(value, zone());
+ args->Add(factory()->NewBooleanLiteral(done, pos), zone());
- return factory->NewCallRuntime(Runtime::kInlineCreateIterResultObject, args,
- pos);
-}
-
-Expression* ParserBaseTraits<Parser>::NewThrowReferenceError(
- MessageTemplate::Template message, int pos) {
- return delegate()->NewThrowError(
- Runtime::kNewReferenceError, message,
- delegate()->ast_value_factory()->empty_string(), pos);
-}
-
-Expression* ParserBaseTraits<Parser>::NewThrowSyntaxError(
- MessageTemplate::Template message, const AstRawString* arg, int pos) {
- return delegate()->NewThrowError(Runtime::kNewSyntaxError, message, arg, pos);
-}
-
-Expression* ParserBaseTraits<Parser>::NewThrowTypeError(
- MessageTemplate::Template message, const AstRawString* arg, int pos) {
- return delegate()->NewThrowError(Runtime::kNewTypeError, message, arg, pos);
+ return factory()->NewCallRuntime(Runtime::kInlineCreateIterResultObject, args,
+ pos);
}
Expression* Parser::NewThrowError(Runtime::FunctionId id,
@@ -520,124 +498,62 @@
return factory()->NewThrow(call_constructor, pos);
}
-void ParserBaseTraits<Parser>::ReportMessageAt(
- Scanner::Location source_location, MessageTemplate::Template message,
- const char* arg, ParseErrorType error_type) {
- if (delegate()->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;
- }
- delegate()->pending_error_handler_.ReportMessageAt(source_location.beg_pos,
- source_location.end_pos,
- message, arg, error_type);
-}
-
-void ParserBaseTraits<Parser>::ReportMessageAt(
- Scanner::Location source_location, MessageTemplate::Template message,
- const AstRawString* arg, ParseErrorType error_type) {
- if (delegate()->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;
- }
- delegate()->pending_error_handler_.ReportMessageAt(source_location.beg_pos,
- source_location.end_pos,
- message, arg, error_type);
-}
-
-const AstRawString* ParserBaseTraits<Parser>::GetSymbol(
- Scanner* scanner) const {
- const AstRawString* result =
- delegate()->scanner()->CurrentSymbol(delegate()->ast_value_factory());
- DCHECK(result != NULL);
- return result;
-}
-
-const AstRawString* ParserBaseTraits<Parser>::GetNumberAsSymbol(
- Scanner* scanner) const {
- double double_value = delegate()->scanner()->DoubleValue();
- char array[100];
- const char* string = DoubleToCString(double_value, ArrayVector(array));
- return delegate()->ast_value_factory()->GetOneByteString(string);
-}
-
-const AstRawString* ParserBaseTraits<Parser>::GetNextSymbol(
- Scanner* scanner) const {
- return delegate()->scanner()->NextSymbol(delegate()->ast_value_factory());
-}
-
-Expression* ParserBaseTraits<Parser>::ThisExpression(int pos) {
- return delegate()->NewUnresolved(
- delegate()->ast_value_factory()->this_string(), pos, pos + 4,
- Variable::THIS);
-}
-
-Expression* ParserBaseTraits<Parser>::NewSuperPropertyReference(
- AstNodeFactory* factory, int pos) {
+Expression* Parser::NewSuperPropertyReference(int pos) {
// this_function[home_object_symbol]
- VariableProxy* this_function_proxy = delegate()->NewUnresolved(
- delegate()->ast_value_factory()->this_function_string(), pos);
+ VariableProxy* this_function_proxy =
+ NewUnresolved(ast_value_factory()->this_function_string(), pos);
Expression* home_object_symbol_literal =
- factory->NewSymbolLiteral("home_object_symbol", kNoSourcePosition);
- Expression* home_object = factory->NewProperty(
+ factory()->NewSymbolLiteral("home_object_symbol", kNoSourcePosition);
+ Expression* home_object = factory()->NewProperty(
this_function_proxy, home_object_symbol_literal, pos);
- return factory->NewSuperPropertyReference(
+ return factory()->NewSuperPropertyReference(
ThisExpression(pos)->AsVariableProxy(), home_object, pos);
}
-Expression* ParserBaseTraits<Parser>::NewSuperCallReference(
- AstNodeFactory* factory, int pos) {
- VariableProxy* new_target_proxy = delegate()->NewUnresolved(
- delegate()->ast_value_factory()->new_target_string(), pos);
- VariableProxy* this_function_proxy = delegate()->NewUnresolved(
- delegate()->ast_value_factory()->this_function_string(), pos);
- return factory->NewSuperCallReference(ThisExpression(pos)->AsVariableProxy(),
- new_target_proxy, this_function_proxy,
- pos);
+Expression* Parser::NewSuperCallReference(int pos) {
+ VariableProxy* new_target_proxy =
+ NewUnresolved(ast_value_factory()->new_target_string(), pos);
+ VariableProxy* this_function_proxy =
+ NewUnresolved(ast_value_factory()->this_function_string(), pos);
+ return factory()->NewSuperCallReference(
+ ThisExpression(pos)->AsVariableProxy(), new_target_proxy,
+ this_function_proxy, pos);
}
-Expression* ParserBaseTraits<Parser>::NewTargetExpression(int pos) {
+Expression* Parser::NewTargetExpression(int pos) {
static const int kNewTargetStringLength = 10;
- auto proxy = delegate()->NewUnresolved(
- delegate()->ast_value_factory()->new_target_string(), pos,
- pos + kNewTargetStringLength);
+ auto proxy = NewUnresolved(ast_value_factory()->new_target_string(), pos,
+ pos + kNewTargetStringLength);
proxy->set_is_new_target();
return proxy;
}
-Expression* ParserBaseTraits<Parser>::FunctionSentExpression(
- AstNodeFactory* factory, int pos) const {
+Expression* Parser::FunctionSentExpression(int pos) {
// We desugar function.sent into %_GeneratorGetInputOrDebugPos(generator).
- Zone* zone = delegate()->zone();
- ZoneList<Expression*>* args = new (zone) ZoneList<Expression*>(1, zone);
- VariableProxy* generator = factory->NewVariableProxy(
- delegate()->function_state_->generator_object_variable());
- args->Add(generator, zone);
- return factory->NewCallRuntime(Runtime::kInlineGeneratorGetInputOrDebugPos,
- args, pos);
+ ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(1, zone());
+ VariableProxy* generator =
+ factory()->NewVariableProxy(function_state_->generator_object_variable());
+ args->Add(generator, zone());
+ return factory()->NewCallRuntime(Runtime::kInlineGeneratorGetInputOrDebugPos,
+ args, pos);
}
-Literal* ParserBaseTraits<Parser>::ExpressionFromLiteral(
- Token::Value token, int pos, Scanner* scanner,
- AstNodeFactory* factory) const {
+Literal* Parser::ExpressionFromLiteral(Token::Value token, int pos) {
switch (token) {
case Token::NULL_LITERAL:
- return factory->NewNullLiteral(pos);
+ return factory()->NewNullLiteral(pos);
case Token::TRUE_LITERAL:
- return factory->NewBooleanLiteral(true, pos);
+ return factory()->NewBooleanLiteral(true, pos);
case Token::FALSE_LITERAL:
- return factory->NewBooleanLiteral(false, pos);
+ return factory()->NewBooleanLiteral(false, pos);
case Token::SMI: {
- int value = scanner->smi_value();
- return factory->NewSmiLiteral(value, pos);
+ 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);
+ bool has_dot = scanner()->ContainsDot();
+ double value = scanner()->DoubleValue();
+ return factory()->NewNumberLiteral(value, pos, has_dot);
}
default:
DCHECK(false);
@@ -645,43 +561,74 @@
return NULL;
}
-Expression* ParserBaseTraits<Parser>::ExpressionFromIdentifier(
- const AstRawString* name, int start_position, int end_position,
- InferName infer) {
- if (infer == InferName::kYes && delegate()->fni_ != NULL) {
- delegate()->fni_->PushVariableName(name);
- }
- return delegate()->NewUnresolved(name, start_position, end_position);
-}
-
-Expression* ParserBaseTraits<Parser>::ExpressionFromString(
- int pos, Scanner* scanner, AstNodeFactory* factory) const {
- const AstRawString* symbol = GetSymbol(scanner);
- if (delegate()->fni_ != NULL) delegate()->fni_->PushLiteralName(symbol);
- return factory->NewStringLiteral(symbol, pos);
-}
-
-Expression* ParserBaseTraits<Parser>::GetIterator(Expression* iterable,
- AstNodeFactory* factory,
- int pos) {
+Expression* Parser::GetIterator(Expression* iterable, int pos) {
Expression* iterator_symbol_literal =
- factory->NewSymbolLiteral("iterator_symbol", kNoSourcePosition);
+ factory()->NewSymbolLiteral("iterator_symbol", kNoSourcePosition);
Expression* prop =
- factory->NewProperty(iterable, iterator_symbol_literal, pos);
- Zone* zone = delegate()->zone();
- ZoneList<Expression*>* args = new (zone) ZoneList<Expression*>(0, zone);
- return factory->NewCall(prop, args, pos);
-}
-
-Literal* ParserBaseTraits<Parser>::GetLiteralTheHole(
- int position, AstNodeFactory* factory) const {
- return factory->NewTheHoleLiteral(kNoSourcePosition);
+ factory()->NewProperty(iterable, iterator_symbol_literal, pos);
+ ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(0, zone());
+ return factory()->NewCall(prop, args, pos);
}
void Parser::MarkTailPosition(Expression* expression) {
expression->MarkTail();
}
+Expression* Parser::NewV8Intrinsic(const AstRawString* name,
+ ZoneList<Expression*>* args, int pos,
+ bool* ok) {
+ if (extension_ != nullptr) {
+ // The extension structures are only accessible while parsing the
+ // very first time, not when reparsing because of lazy compilation.
+ GetClosureScope()->ForceEagerCompilation();
+ }
+
+ DCHECK(name->is_one_byte());
+ const Runtime::Function* function =
+ Runtime::FunctionForName(name->raw_data(), name->length());
+
+ if (function != nullptr) {
+ // Check for possible name clash.
+ DCHECK_EQ(Context::kNotFound,
+ Context::IntrinsicIndexForName(name->raw_data(), name->length()));
+ // 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() != nullptr) {
+ return args->at(0);
+ } else {
+ ReportMessage(MessageTemplate::kNotIsvar);
+ *ok = false;
+ return nullptr;
+ }
+ }
+
+ // Check that the expected number of arguments are being passed.
+ if (function->nargs != -1 && function->nargs != args->length()) {
+ ReportMessage(MessageTemplate::kRuntimeWrongNumArgs);
+ *ok = false;
+ return nullptr;
+ }
+
+ return factory()->NewCallRuntime(function, args, pos);
+ }
+
+ int context_index =
+ Context::IntrinsicIndexForName(name->raw_data(), name->length());
+
+ // Check that the function is defined.
+ if (context_index == Context::kNotFound) {
+ ReportMessage(MessageTemplate::kNotDefined, name);
+ *ok = false;
+ return nullptr;
+ }
+
+ return factory()->NewCallRuntime(context_index, args, pos);
+}
+
Parser::Parser(ParseInfo* info)
: ParserBase<Parser>(info->zone(), &scanner_, info->stack_limit(),
info->extension(), info->ast_value_factory(), NULL),
@@ -699,7 +646,8 @@
// ParseInfo during background parsing.
DCHECK(!info->script().is_null() || info->source_stream() != nullptr ||
info->character_stream() != nullptr);
- set_allow_lazy(info->allow_lazy_parsing());
+ set_allow_lazy(FLAG_lazy && info->allow_lazy_parsing() &&
+ !info->is_native() && info->extension() == nullptr);
set_allow_natives(FLAG_allow_natives_syntax || info->is_native());
set_allow_tailcalls(FLAG_harmony_tailcalls && !info->is_native() &&
info->isolate()->is_tail_call_elimination_enabled());
@@ -711,6 +659,7 @@
set_allow_harmony_async_await(FLAG_harmony_async_await);
set_allow_harmony_restrictive_generators(FLAG_harmony_restrictive_generators);
set_allow_harmony_trailing_commas(FLAG_harmony_trailing_commas);
+ set_allow_harmony_class_fields(FLAG_harmony_class_fields);
for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
++feature) {
use_counts_[feature] = 0;
@@ -725,29 +674,19 @@
}
void Parser::DeserializeScopeChain(
- ParseInfo* info, Handle<Context> context,
- Scope::DeserializationMode deserialization_mode) {
+ ParseInfo* info, MaybeHandle<ScopeInfo> maybe_outer_scope_info) {
DCHECK(ThreadId::Current().Equals(info->isolate()->thread_id()));
// TODO(wingo): Add an outer SCRIPT_SCOPE corresponding to the native
// context, which will have the "this" binding for script scopes.
DeclarationScope* script_scope = NewScriptScope();
info->set_script_scope(script_scope);
Scope* scope = script_scope;
- if (!context.is_null() && !context->IsNativeContext()) {
- scope = Scope::DeserializeScopeChain(info->isolate(), zone(), *context,
- script_scope, ast_value_factory(),
- deserialization_mode);
- if (info->context().is_null()) {
- DCHECK(deserialization_mode ==
- Scope::DeserializationMode::kDeserializeOffHeap);
- } else {
- // 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());
- }
+ Handle<ScopeInfo> outer_scope_info;
+ if (maybe_outer_scope_info.ToHandle(&outer_scope_info)) {
+ scope = Scope::DeserializeScopeChain(
+ info->isolate(), zone(), *outer_scope_info, script_scope,
+ ast_value_factory(), Scope::DeserializationMode::kScopesOnly);
+ DCHECK(!info->is_module() || scope->is_module_scope());
}
original_scope_ = scope;
}
@@ -762,8 +701,7 @@
HistogramTimerScope timer_scope(isolate->counters()->parse(), true);
RuntimeCallTimerScope runtime_timer(isolate, &RuntimeCallStats::Parse);
- TRACE_EVENT_RUNTIME_CALL_STATS_TRACING_SCOPED(
- isolate, &tracing::TraceEventStatsTable::Parse);
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.Parse");
Handle<String> source(String::cast(info->script()->source()));
isolate->counters()->total_parse_size()->Increment(source->length());
base::ElapsedTimer timer;
@@ -781,24 +719,13 @@
cached_parse_data_->Initialize();
}
- DeserializeScopeChain(info, info->context(),
- Scope::DeserializationMode::kKeepScopeInfo);
+ DeserializeScopeChain(info, info->maybe_outer_scope_info());
source = String::Flatten(source);
FunctionLiteral* result;
{
- std::unique_ptr<Utf16CharacterStream> stream;
- if (source->IsExternalTwoByteString()) {
- stream.reset(new ExternalTwoByteStringUtf16CharacterStream(
- Handle<ExternalTwoByteString>::cast(source), 0, source->length()));
- } else if (source->IsExternalOneByteString()) {
- stream.reset(new ExternalOneByteStringUtf16CharacterStream(
- Handle<ExternalOneByteString>::cast(source), 0, source->length()));
- } else {
- stream.reset(
- new GenericStringUtf16CharacterStream(source, 0, source->length()));
- }
+ std::unique_ptr<Utf16CharacterStream> stream(ScannerStream::For(source));
scanner_.Initialize(stream.get());
result = DoParseProgram(info);
}
@@ -835,27 +762,25 @@
DCHECK_NULL(scope_state_);
DCHECK_NULL(target_stack_);
- Mode parsing_mode = FLAG_lazy && allow_lazy() ? PARSE_LAZILY : PARSE_EAGERLY;
- if (allow_natives() || extension_ != NULL) parsing_mode = PARSE_EAGERLY;
+ Mode parsing_mode = allow_lazy() ? PARSE_LAZILY : PARSE_EAGERLY;
FunctionLiteral* result = NULL;
{
Scope* outer = original_scope_;
- // If there's a chance that there's a reference to global 'this', predeclare
- // it as a dynamic global on the script scope.
- if (outer->GetReceiverScope()->is_script_scope()) {
- info->script_scope()->DeclareDynamicGlobal(
- ast_value_factory()->this_string(), Variable::THIS);
- }
- DCHECK(outer);
+ DCHECK_NOT_NULL(outer);
+ parsing_module_ = info->is_module();
if (info->is_eval()) {
if (!outer->is_script_scope() || is_strict(info->language_mode())) {
parsing_mode = PARSE_EAGERLY;
}
outer = NewEvalScope(outer);
- } else if (info->is_module()) {
+ } else if (parsing_module_) {
DCHECK_EQ(outer, info->script_scope());
outer = NewModuleScope(info->script_scope());
+ // Never do lazy parsing in modules. If we want to support this in the
+ // future, we must force context-allocation for all variables that are
+ // declared at the module level but not MODULE-allocated.
+ parsing_mode = PARSE_EAGERLY;
}
DeclarationScope* scope = outer->AsDeclarationScope();
@@ -864,14 +789,29 @@
// Enter 'scope' with the given parsing mode.
ParsingModeScope parsing_mode_scope(this, parsing_mode);
- FunctionState function_state(&function_state_, &scope_state_, scope,
- kNormalFunction);
+ FunctionState function_state(&function_state_, &scope_state_, scope);
ZoneList<Statement*>* body = new(zone()) ZoneList<Statement*>(16, zone());
bool ok = true;
int beg_pos = scanner()->location().beg_pos;
- parsing_module_ = info->is_module();
if (parsing_module_) {
+ // Declare the special module parameter.
+ auto name = ast_value_factory()->empty_string();
+ bool is_duplicate;
+ bool is_rest = false;
+ bool is_optional = false;
+ auto var = scope->DeclareParameter(name, VAR, is_optional, is_rest,
+ &is_duplicate, ast_value_factory());
+ DCHECK(!is_duplicate);
+ var->AllocateTo(VariableLocation::PARAMETER, 0);
+
+ PrepareGeneratorVariables(&function_state);
+ Expression* initial_yield =
+ BuildInitialYield(kNoSourcePosition, kGeneratorFunction);
+ body->Add(
+ factory()->NewExpressionStatement(initial_yield, kNoSourcePosition),
+ zone());
+
ParseModuleItemList(body, &ok);
ok = ok &&
module()->Validate(this->scope()->AsModuleScope(),
@@ -889,7 +829,7 @@
if (ok && is_strict(language_mode())) {
CheckStrictOctalLiteral(beg_pos, scanner()->location().end_pos, &ok);
- CheckDecimalLiteralWithLeadingZero(use_counts_, beg_pos,
+ CheckDecimalLiteralWithLeadingZero(beg_pos,
scanner()->location().end_pos);
}
if (ok && is_sloppy(language_mode())) {
@@ -897,7 +837,7 @@
// 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, nullptr, &ok);
+ InsertSloppyBlockFunctionVarBindings(scope);
}
if (ok) {
CheckConflictingVarDeclarations(scope, &ok);
@@ -915,9 +855,10 @@
if (ok) {
RewriteDestructuringAssignments();
+ int parameter_count = parsing_module_ ? 1 : 0;
result = factory()->NewScriptOrEvalFunctionLiteral(
scope, body, function_state.materialized_literal_count(),
- function_state.expected_property_count());
+ function_state.expected_property_count(), parameter_count);
}
}
@@ -934,8 +875,7 @@
DCHECK(parsing_on_main_thread_);
RuntimeCallTimerScope runtime_timer(isolate, &RuntimeCallStats::ParseLazy);
HistogramTimerScope timer_scope(isolate->counters()->parse_lazy());
- TRACE_EVENT_RUNTIME_CALL_STATS_TRACING_SCOPED(
- isolate, &tracing::TraceEventStatsTable::ParseLazy);
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.ParseLazy");
Handle<String> source(String::cast(info->script()->source()));
isolate->counters()->total_parse_size()->Increment(source->length());
base::ElapsedTimer timer;
@@ -943,26 +883,14 @@
timer.Start();
}
Handle<SharedFunctionInfo> shared_info = info->shared_info();
- DeserializeScopeChain(info, info->context(),
- Scope::DeserializationMode::kKeepScopeInfo);
+ DeserializeScopeChain(info, info->maybe_outer_scope_info());
// Initialize parser state.
source = String::Flatten(source);
FunctionLiteral* result;
{
- std::unique_ptr<Utf16CharacterStream> stream;
- if (source->IsExternalTwoByteString()) {
- stream.reset(new ExternalTwoByteStringUtf16CharacterStream(
- Handle<ExternalTwoByteString>::cast(source),
- shared_info->start_position(), shared_info->end_position()));
- } else if (source->IsExternalOneByteString()) {
- stream.reset(new ExternalOneByteStringUtf16CharacterStream(
- Handle<ExternalOneByteString>::cast(source),
- shared_info->start_position(), shared_info->end_position()));
- } else {
- stream.reset(new GenericStringUtf16CharacterStream(
- source, shared_info->start_position(), shared_info->end_position()));
- }
+ std::unique_ptr<Utf16CharacterStream> stream(ScannerStream::For(
+ source, shared_info->start_position(), shared_info->end_position()));
Handle<String> name(String::cast(shared_info->name()));
result =
DoParseLazy(info, ast_value_factory()->GetString(name), stream.get());
@@ -974,6 +902,8 @@
if (FLAG_trace_parse && result != NULL) {
double ms = timer.Elapsed().InMillisecondsF();
+ // We need to make sure that the debug-name is available.
+ ast_value_factory()->Internalize(isolate);
std::unique_ptr<char[]> name_chars = result->debug_name()->ToCString();
PrintF("[parsing function: %s - took %0.3f ms]\n", name_chars.get(), ms);
}
@@ -1010,24 +940,20 @@
{
// Parse the function literal.
- Scope* scope = original_scope_;
- DCHECK(scope);
- // If there's a chance that there's a reference to global 'this', predeclare
- // it as a dynamic global on the script scope.
- if (info->is_arrow() && scope->GetReceiverScope()->is_script_scope()) {
- info->script_scope()->DeclareDynamicGlobal(
- ast_value_factory()->this_string(), Variable::THIS);
- }
- FunctionState function_state(&function_state_, &scope_state_, scope,
- info->function_kind());
- DCHECK(is_sloppy(scope->language_mode()) ||
+ Scope* outer = original_scope_;
+ DeclarationScope* outer_function = outer->GetClosureScope();
+ DCHECK(outer);
+ FunctionState function_state(&function_state_, &scope_state_,
+ outer_function);
+ BlockState block_state(&scope_state_, outer);
+ DCHECK(is_sloppy(outer->language_mode()) ||
is_strict(info->language_mode()));
FunctionLiteral::FunctionType function_type = ComputeFunctionType(info);
+ FunctionKind kind = info->function_kind();
bool ok = true;
- if (info->is_arrow()) {
- bool is_async = allow_harmony_async_await() && info->is_async();
- if (is_async) {
+ if (IsArrowFunction(kind)) {
+ if (allow_harmony_async_await() && IsAsyncFunction(kind)) {
DCHECK(!scanner()->HasAnyLineTerminatorAfterNext());
if (!Check(Token::ASYNC)) {
CHECK(stack_overflow());
@@ -1040,7 +966,7 @@
}
// TODO(adamk): We should construct this scope from the ScopeInfo.
- DeclarationScope* scope = NewFunctionScope(FunctionKind::kArrowFunction);
+ DeclarationScope* scope = NewFunctionScope(kind);
// These two bits only need to be explicitly set because we're
// not passing the ScopeInfo to the Scope constructor.
@@ -1062,15 +988,12 @@
BlockState block_state(&scope_state_, scope);
if (Check(Token::LPAREN)) {
// '(' StrictFormalParameters ')'
- ParseFormalParameterList(&formals, &formals_classifier, &ok);
+ ParseFormalParameterList(&formals, &ok);
if (ok) ok = Check(Token::RPAREN);
} else {
// BindingIdentifier
- ParseFormalParameter(&formals, &formals_classifier, &ok);
- if (ok) {
- DeclareFormalParameter(formals.scope, formals.at(0),
- &formals_classifier);
- }
+ ParseFormalParameter(&formals, &ok);
+ if (ok) DeclareFormalParameter(formals.scope, formals.at(0));
}
}
@@ -1078,8 +1001,7 @@
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, is_async, formals_classifier, &ok);
+ Expression* expression = ParseArrowFunctionLiteral(true, formals, &ok);
if (ok) {
// Scanning must end at the same position that was recorded
// previously. If not, parsing has been interrupted due to a stack
@@ -1097,16 +1019,31 @@
}
}
}
- } else if (info->is_default_constructor()) {
- DCHECK_EQ(this->scope(), scope);
+ } else if (IsDefaultConstructor(kind)) {
+ DCHECK_EQ(scope(), outer);
+ bool is_subclass_constructor = IsSubclassConstructor(kind);
result = DefaultConstructor(
- raw_name, IsSubclassConstructor(info->function_kind()),
+ raw_name, is_subclass_constructor, info->requires_class_field_init(),
info->start_position(), info->end_position(), info->language_mode());
+ if (!is_subclass_constructor && info->requires_class_field_init()) {
+ result = InsertClassFieldInitializer(result);
+ }
+ } else if (info->is_class_field_initializer()) {
+ Handle<SharedFunctionInfo> shared_info = info->shared_info();
+ DCHECK(!shared_info.is_null());
+ if (shared_info->length() == 0) {
+ result = ParseClassFieldForInitializer(
+ info->start_position() != info->end_position(), &ok);
+ } else {
+ result = SynthesizeClassFieldInitializer(shared_info->length());
+ }
} else {
- result = ParseFunctionLiteral(raw_name, Scanner::Location::invalid(),
- kSkipFunctionNameCheck,
- info->function_kind(), kNoSourcePosition,
- function_type, info->language_mode(), &ok);
+ result = ParseFunctionLiteral(
+ raw_name, Scanner::Location::invalid(), kSkipFunctionNameCheck, kind,
+ kNoSourcePosition, function_type, info->language_mode(), &ok);
+ if (info->requires_class_field_init()) {
+ result = InsertClassFieldInitializer(result);
+ }
}
// Make sure the results agree.
DCHECK(ok == (result != nullptr));
@@ -1117,131 +1054,6 @@
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();
- Statement* stat = ParseStatementListItem(CHECK_OK_VOID);
- 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.
- 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;
- if (use_strict_found) {
- if (is_sloppy(language_mode())) {
- RaiseLanguageMode(STRICT);
- }
-
- if (!this->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
- const AstRawString* string = literal->raw_value()->AsString();
- ReportMessageAt(token_loc,
- MessageTemplate::kIllegalLanguageModeDirective,
- string);
- *ok = false;
- return;
- }
- // 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 (this->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];
- DCHECK(this->scope()->is_declaration_scope());
- this->scope()->AsDeclarationScope()->set_asm_module();
- } 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());
- }
-}
-
-
-Statement* Parser::ParseStatementListItem(bool* ok) {
- // (Ecma 262 6th Edition, 13.1):
- // StatementListItem:
- // Statement
- // Declaration
- const Token::Value peeked = peek();
- switch (peeked) {
- case Token::FUNCTION:
- return ParseHoistableDeclaration(NULL, false, ok);
- case Token::CLASS:
- Consume(Token::CLASS);
- return ParseClassDeclaration(NULL, false, ok);
- case Token::CONST:
- return ParseVariableStatement(kStatementListItem, NULL, ok);
- case Token::VAR:
- return ParseVariableStatement(kStatementListItem, NULL, ok);
- case Token::LET:
- if (IsNextLetKeyword()) {
- return ParseVariableStatement(kStatementListItem, NULL, ok);
- }
- break;
- case Token::ASYNC:
- if (allow_harmony_async_await() && PeekAhead() == Token::FUNCTION &&
- !scanner()->HasAnyLineTerminatorAfterNext()) {
- Consume(Token::ASYNC);
- return ParseAsyncFunctionDeclaration(NULL, false, ok);
- }
- /* falls through */
- default:
- break;
- }
- return ParseStatement(NULL, kAllowLabelledFunctionStatement, ok);
-}
-
-
Statement* Parser::ParseModuleItem(bool* ok) {
// ecma262/#prod-ModuleItem
// ModuleItem :
@@ -1285,7 +1097,7 @@
// StringLiteral
Expect(Token::STRING, CHECK_OK);
- return GetSymbol(scanner());
+ return GetSymbol();
}
@@ -1413,7 +1225,7 @@
if (tok == Token::STRING) {
const AstRawString* module_specifier = ParseModuleSpecifier(CHECK_OK_VOID);
ExpectSemicolon(CHECK_OK_VOID);
- module()->AddEmptyImport(module_specifier, scanner()->location(), zone());
+ module()->AddEmptyImport(module_specifier);
return;
}
@@ -1481,7 +1293,7 @@
if (named_imports != nullptr) {
if (named_imports->length() == 0) {
- module()->AddEmptyImport(module_specifier, scanner()->location(), zone());
+ module()->AddEmptyImport(module_specifier);
} else {
for (int i = 0; i < named_imports->length(); ++i) {
const NamedImport* import = named_imports->at(i);
@@ -1526,9 +1338,8 @@
default: {
int pos = position();
ExpressionClassifier classifier(this);
- Expression* value =
- ParseAssignmentExpression(true, &classifier, CHECK_OK);
- RewriteNonPattern(&classifier, CHECK_OK);
+ Expression* value = ParseAssignmentExpression(true, CHECK_OK);
+ RewriteNonPattern(CHECK_OK);
SetFunctionName(value, ast_value_factory()->default_string());
const AstRawString* local_name =
@@ -1621,8 +1432,7 @@
export_locations[i], zone());
}
} else if (length == 0) {
- module()->AddEmptyImport(module_specifier, scanner()->location(),
- zone());
+ module()->AddEmptyImport(module_specifier);
} else {
for (int i = 0; i < length; ++i) {
module()->AddExport(original_names[i], export_names[i],
@@ -1673,141 +1483,8 @@
return result;
}
-Statement* Parser::ParseStatement(ZoneList<const AstRawString*>* labels,
- AllowLabelledFunctionStatement allow_function,
- bool* ok) {
- // Statement ::
- // EmptyStatement
- // ...
-
- if (peek() == Token::SEMICOLON) {
- Next();
- return factory()->NewEmptyStatement(kNoSourcePosition);
- }
- return ParseSubStatement(labels, allow_function, ok);
-}
-
-Statement* Parser::ParseSubStatement(
- ZoneList<const AstRawString*>* labels,
- AllowLabelledFunctionStatement allow_function, 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:
- Next();
- return factory()->NewEmptyStatement(kNoSourcePosition);
-
- 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, kNoSourcePosition);
- 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 only allowed as a StatementListItem, not in
- // an arbitrary Statement position. Exceptions such as
- // ES#sec-functiondeclarations-in-ifstatement-statement-clauses
- // are handled by calling ParseScopedStatement rather than
- // ParseSubStatement directly.
- ReportMessageAt(scanner()->peek_location(),
- is_strict(language_mode())
- ? MessageTemplate::kStrictFunction
- : MessageTemplate::kSloppyFunction);
- *ok = false;
- return nullptr;
-
- case Token::DEBUGGER:
- return ParseDebuggerStatement(ok);
-
- case Token::VAR:
- return ParseVariableStatement(kStatement, NULL, ok);
-
- default:
- return ParseExpressionOrLabelledStatement(labels, allow_function, 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, int begin_pos,
- int end_pos, Variable::Kind kind) {
+ int end_pos, VariableKind kind) {
return scope()->NewUnresolved(factory(), name, begin_pos, end_pos, kind);
}
@@ -1816,25 +1493,19 @@
scanner()->location().end_pos);
}
-InitializationFlag Parser::DefaultInitializationFlag(VariableMode mode) {
- DCHECK(IsDeclaredVariableMode(mode));
- return mode == VAR ? kCreatedInitialized : kNeedsInitialization;
-}
-
Declaration* Parser::DeclareVariable(const AstRawString* name,
VariableMode mode, int pos, bool* ok) {
- return DeclareVariable(name, mode, DefaultInitializationFlag(mode), pos, ok);
+ return DeclareVariable(name, mode, Variable::DefaultInitializationFlag(mode),
+ pos, ok);
}
Declaration* Parser::DeclareVariable(const AstRawString* name,
VariableMode mode, InitializationFlag init,
int pos, bool* ok) {
DCHECK_NOT_NULL(name);
- Scope* scope =
- IsLexicalVariableMode(mode) ? this->scope() : GetDeclarationScope();
- VariableProxy* proxy =
- scope->NewUnresolved(factory(), name, scanner()->location().beg_pos,
- scanner()->location().end_pos);
+ VariableProxy* proxy = factory()->NewVariableProxy(
+ name, NORMAL_VARIABLE, scanner()->location().beg_pos,
+ scanner()->location().end_pos);
Declaration* declaration =
factory()->NewVariableDeclaration(proxy, this->scope(), pos);
Declare(declaration, DeclarationDescriptor::NORMAL, mode, init, CHECK_OK);
@@ -1845,132 +1516,99 @@
DeclarationDescriptor::Kind declaration_kind,
VariableMode mode, InitializationFlag init, bool* ok,
Scope* scope) {
- DCHECK(IsDeclaredVariableMode(mode) && mode != CONST_LEGACY);
-
- VariableProxy* proxy = declaration->proxy();
- DCHECK(proxy->raw_name() != NULL);
- const AstRawString* name = proxy->raw_name();
-
- if (scope == nullptr) scope = this->scope();
- if (mode == VAR) scope = scope->GetDeclarationScope();
- DCHECK(!scope->is_catch_scope());
- DCHECK(!scope->is_with_scope());
- DCHECK(scope->is_declaration_scope() ||
- (IsLexicalVariableMode(mode) && scope->is_block_scope()));
-
- bool is_function_declaration = declaration->IsFunctionDeclaration();
-
- Variable* var = NULL;
- if (scope->is_eval_scope() && is_sloppy(scope->language_mode()) &&
- mode == VAR) {
- // 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 DeclareEvalVar or DeclareEvalFunction runtime functions.
- Variable::Kind kind = Variable::NORMAL;
- // TODO(sigurds) figure out if kNotAssigned is OK here
- var = new (zone()) Variable(scope, name, mode, kind, init, kNotAssigned);
- var->AllocateTo(VariableLocation::LOOKUP, -1);
- } else {
- // Declare the variable in the declaration scope.
- var = scope->LookupLocal(name);
- if (var == NULL) {
- // Declare the name.
- Variable::Kind kind = Variable::NORMAL;
- if (is_function_declaration) {
- kind = Variable::FUNCTION;
- }
- var = scope->DeclareLocal(name, mode, init, kind, kNotAssigned);
- } else if (IsLexicalVariableMode(mode) ||
- IsLexicalVariableMode(var->mode())) {
- // Allow duplicate function decls for web compat, see bug 4693.
- bool duplicate_allowed = false;
- if (is_sloppy(scope->language_mode()) && is_function_declaration &&
- var->is_function()) {
- DCHECK(IsLexicalVariableMode(mode) &&
- IsLexicalVariableMode(var->mode()));
- // If the duplication is allowed, then the var will show up
- // in the SloppyBlockFunctionMap and the new FunctionKind
- // will be a permitted duplicate.
- FunctionKind function_kind =
- declaration->AsFunctionDeclaration()->fun()->kind();
- duplicate_allowed =
- scope->GetDeclarationScope()->sloppy_block_function_map()->Lookup(
- const_cast<AstRawString*>(name), name->hash()) != nullptr &&
- !IsAsyncFunction(function_kind) &&
- !(allow_harmony_restrictive_generators() &&
- IsGeneratorFunction(function_kind));
- }
- if (duplicate_allowed) {
- ++use_counts_[v8::Isolate::kSloppyModeBlockScopedFunctionRedefinition];
- } else {
- // 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()));
- // In harmony we treat re-declarations as early errors. See
- // ES5 16 for a definition of early errors.
- if (declaration_kind == DeclarationDescriptor::NORMAL) {
- ReportMessage(MessageTemplate::kVarRedeclaration, name);
- } else {
- ReportMessage(MessageTemplate::kParamDupe);
- }
- *ok = false;
- return nullptr;
- }
- } else if (mode == VAR) {
- var->set_maybe_assigned();
- }
+ if (scope == nullptr) {
+ scope = this->scope();
}
- DCHECK_NOT_NULL(var);
-
- // 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.
- //
- // This will lead to multiple declaration nodes for the
- // same variable if it is declared several times. This is not a
- // semantic issue, but it may be a performance issue since it may
- // lead to repeated DeclareEvalVar or DeclareEvalFunction calls.
- scope->AddDeclaration(declaration);
- proxy->BindTo(var);
- return var;
+ bool sloppy_mode_block_scope_function_redefinition = false;
+ Variable* variable = scope->DeclareVariable(
+ declaration, mode, init, allow_harmony_restrictive_generators(),
+ &sloppy_mode_block_scope_function_redefinition, ok);
+ if (!*ok) {
+ if (declaration_kind == DeclarationDescriptor::NORMAL) {
+ ReportMessage(MessageTemplate::kVarRedeclaration,
+ declaration->proxy()->raw_name());
+ } else {
+ ReportMessage(MessageTemplate::kParamDupe);
+ }
+ return nullptr;
+ }
+ if (sloppy_mode_block_scope_function_redefinition) {
+ ++use_counts_[v8::Isolate::kSloppyModeBlockScopedFunctionRedefinition];
+ }
+ return variable;
}
-
-// 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);
- }
+Block* Parser::BuildInitializationBlock(
+ DeclarationParsingResult* parsing_result,
+ ZoneList<const AstRawString*>* names, bool* ok) {
+ Block* result = factory()->NewBlock(
+ NULL, 1, true, parsing_result->descriptor.declaration_pos);
+ for (auto declaration : parsing_result->declarations) {
+ PatternRewriter::DeclareAndInitializeVariables(
+ this, result, &(parsing_result->descriptor), &declaration, names,
+ CHECK_OK);
}
- Expect(Token::RPAREN, CHECK_OK);
- Expect(Token::SEMICOLON, CHECK_OK);
+ return result;
+}
+void Parser::DeclareAndInitializeVariables(
+ Block* block, const DeclarationDescriptor* declaration_descriptor,
+ const DeclarationParsingResult::Declaration* declaration,
+ ZoneList<const AstRawString*>* names, bool* ok) {
+ DCHECK_NOT_NULL(block);
+ PatternRewriter::DeclareAndInitializeVariables(
+ this, block, declaration_descriptor, declaration, names, ok);
+}
+
+Statement* Parser::DeclareFunction(const AstRawString* variable_name,
+ FunctionLiteral* function, int pos,
+ bool is_generator, bool is_async,
+ ZoneList<const AstRawString*>* names,
+ bool* ok) {
+ // 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 =
+ (!scope()->is_declaration_scope() || scope()->is_module_scope()) ? LET
+ : VAR;
+ VariableProxy* proxy =
+ factory()->NewVariableProxy(variable_name, NORMAL_VARIABLE);
+ Declaration* declaration =
+ factory()->NewFunctionDeclaration(proxy, function, scope(), pos);
+ Declare(declaration, DeclarationDescriptor::NORMAL, mode, kCreatedInitialized,
+ CHECK_OK);
+ if (names) names->Add(variable_name, zone());
+ // Async functions don't undergo sloppy mode block scoped hoisting, and don't
+ // allow duplicates in a block. Both are represented by the
+ // sloppy_block_function_map. Don't add them to the map for async functions.
+ // Generators are also supposed to be prohibited; currently doing this behind
+ // a flag and UseCounting violations to assess web compatibility.
+ if (is_sloppy(language_mode()) && !scope()->is_declaration_scope() &&
+ !is_async && !(allow_harmony_restrictive_generators() && is_generator)) {
+ SloppyBlockFunctionStatement* delegate =
+ factory()->NewSloppyBlockFunctionStatement(scope());
+ DeclarationScope* target_scope = GetDeclarationScope();
+ target_scope->DeclareSloppyBlockFunction(variable_name, delegate);
+ return delegate;
+ }
+ return factory()->NewEmptyStatement(kNoSourcePosition);
+}
+
+Statement* Parser::DeclareClass(const AstRawString* variable_name,
+ Expression* value,
+ ZoneList<const AstRawString*>* names,
+ int class_token_pos, int end_pos, bool* ok) {
+ Declaration* decl =
+ DeclareVariable(variable_name, LET, class_token_pos, CHECK_OK);
+ decl->proxy()->var()->set_initializer_position(end_pos);
+ Assignment* assignment = factory()->NewAssignment(Token::INIT, decl->proxy(),
+ value, class_token_pos);
+ Statement* assignment_statement =
+ factory()->NewExpressionStatement(assignment, kNoSourcePosition);
+ if (names) names->Add(variable_name, zone());
+ return assignment_statement;
+}
+
+Statement* Parser::DeclareNative(const AstRawString* name, int pos, bool* 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
@@ -1989,734 +1627,102 @@
pos);
}
-Statement* Parser::ParseHoistableDeclaration(
- ZoneList<const AstRawString*>* names, bool default_export, bool* ok) {
- Expect(Token::FUNCTION, CHECK_OK);
- int pos = position();
- ParseFunctionFlags flags = ParseFunctionFlags::kIsNormal;
- if (Check(Token::MUL)) {
- flags |= ParseFunctionFlags::kIsGenerator;
- }
- return ParseHoistableDeclaration(pos, flags, names, default_export, ok);
-}
-
-Statement* Parser::ParseAsyncFunctionDeclaration(
- ZoneList<const AstRawString*>* names, bool default_export, bool* ok) {
- DCHECK_EQ(scanner()->current_token(), Token::ASYNC);
- int pos = position();
- if (scanner()->HasAnyLineTerminatorBeforeNext()) {
+ZoneList<const AstRawString*>* Parser::DeclareLabel(
+ ZoneList<const AstRawString*>* labels, VariableProxy* var, bool* ok) {
+ 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)) {
+ ReportMessage(MessageTemplate::kLabelRedeclaration, label);
*ok = false;
- ReportUnexpectedToken(scanner()->current_token());
return nullptr;
}
- Expect(Token::FUNCTION, CHECK_OK);
- ParseFunctionFlags flags = ParseFunctionFlags::kIsAsync;
- return ParseHoistableDeclaration(pos, flags, names, default_export, ok);
+ if (labels == nullptr) {
+ labels = new (zone()) ZoneList<const AstRawString*>(1, 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);
+ return labels;
}
-Statement* Parser::ParseHoistableDeclaration(
- int pos, ParseFunctionFlags flags, ZoneList<const AstRawString*>* names,
- bool default_export, bool* ok) {
- // FunctionDeclaration ::
- // 'function' Identifier '(' FormalParameters ')' '{' FunctionBody '}'
- // 'function' '(' FormalParameters ')' '{' FunctionBody '}'
- // GeneratorDeclaration ::
- // 'function' '*' Identifier '(' FormalParameters ')' '{' FunctionBody '}'
- // 'function' '*' '(' FormalParameters ')' '{' FunctionBody '}'
- //
- // The anonymous forms are allowed iff [default_export] is true.
- //
- // 'function' and '*' (if present) have been consumed by the caller.
-
- const bool is_generator = flags & ParseFunctionFlags::kIsGenerator;
- const bool is_async = flags & ParseFunctionFlags::kIsAsync;
- DCHECK(!is_generator || !is_async);
-
- const AstRawString* name;
- FunctionNameValidity name_validity;
- const AstRawString* variable_name;
- if (default_export && peek() == Token::LPAREN) {
- name = ast_value_factory()->default_string();
- name_validity = kSkipFunctionNameCheck;
- variable_name = ast_value_factory()->star_default_star_string();
- } else {
- bool is_strict_reserved;
- name = ParseIdentifierOrStrictReservedWord(&is_strict_reserved, CHECK_OK);
- name_validity = is_strict_reserved ? kFunctionNameIsStrictReserved
- : kFunctionNameValidityUnknown;
- variable_name = name;
- }
-
- FuncNameInferrer::State fni_state(fni_);
- if (fni_ != NULL) fni_->PushEnclosingName(name);
- FunctionLiteral* fun = ParseFunctionLiteral(
- name, scanner()->location(), name_validity,
- is_generator ? FunctionKind::kGeneratorFunction
- : is_async ? FunctionKind::kAsyncFunction
- : FunctionKind::kNormalFunction,
- pos, FunctionLiteral::kDeclaration, language_mode(), CHECK_OK);
-
- // 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 =
- (!scope()->is_declaration_scope() || scope()->is_module_scope()) ? LET
- : VAR;
- VariableProxy* proxy = NewUnresolved(variable_name);
- Declaration* declaration =
- factory()->NewFunctionDeclaration(proxy, fun, scope(), pos);
- Declare(declaration, DeclarationDescriptor::NORMAL, mode, kCreatedInitialized,
- CHECK_OK);
- if (names) names->Add(variable_name, zone());
- EmptyStatement* empty = factory()->NewEmptyStatement(kNoSourcePosition);
- // Async functions don't undergo sloppy mode block scoped hoisting, and don't
- // allow duplicates in a block. Both are represented by the
- // sloppy_block_function_map. Don't add them to the map for async functions.
- // Generators are also supposed to be prohibited; currently doing this behind
- // a flag and UseCounting violations to assess web compatibility.
- if (is_sloppy(language_mode()) && !scope()->is_declaration_scope() &&
- !is_async && !(allow_harmony_restrictive_generators() && is_generator)) {
- SloppyBlockFunctionStatement* delegate =
- factory()->NewSloppyBlockFunctionStatement(empty, scope());
- DeclarationScope* target_scope = GetDeclarationScope();
- target_scope->DeclareSloppyBlockFunction(variable_name, delegate);
- return delegate;
- }
- return empty;
-}
-
-Statement* Parser::ParseClassDeclaration(ZoneList<const AstRawString*>* names,
- bool default_export, bool* ok) {
- // ClassDeclaration ::
- // 'class' Identifier ('extends' LeftHandExpression)? '{' ClassBody '}'
- // 'class' ('extends' LeftHandExpression)? '{' ClassBody '}'
- //
- // The anonymous form is allowed iff [default_export] is true.
- //
- // 'class' is expected to be consumed by the caller.
- //
- // A ClassDeclaration
- //
- // class C { ... }
- //
- // has the same semantics as:
- //
- // let C = class C { ... };
- //
- // so rewrite it as such.
-
- int pos = position();
-
- const AstRawString* name;
- bool is_strict_reserved;
- const AstRawString* variable_name;
- if (default_export && (peek() == Token::EXTENDS || peek() == Token::LBRACE)) {
- name = ast_value_factory()->default_string();
- is_strict_reserved = false;
- variable_name = ast_value_factory()->star_default_star_string();
- } else {
- name = ParseIdentifierOrStrictReservedWord(&is_strict_reserved, CHECK_OK);
- variable_name = name;
- }
-
- Expression* value = ParseClassLiteral(nullptr, name, scanner()->location(),
- is_strict_reserved, pos, CHECK_OK);
-
- Declaration* decl = DeclareVariable(variable_name, LET, pos, CHECK_OK);
- decl->proxy()->var()->set_initializer_position(position());
- Assignment* assignment =
- factory()->NewAssignment(Token::INIT, decl->proxy(), value, pos);
- Statement* assignment_statement =
- factory()->NewExpressionStatement(assignment, kNoSourcePosition);
- if (names) names->Add(variable_name, zone());
- return assignment_statement;
-}
-
-Block* Parser::ParseBlock(ZoneList<const AstRawString*>* labels, 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, kNoSourcePosition);
-
- // Parse the statements and collect escaping labels.
- Expect(Token::LBRACE, CHECK_OK);
- {
- BlockState block_state(&scope_state_);
- block_state.set_start_position(scanner()->location().beg_pos);
- 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_state.set_end_position(scanner()->location().end_pos);
- body->set_scope(block_state.FinalizedBlockScope());
- }
- return body;
-}
-
-
-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 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 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;
- Block* result =
- ParseVariableDeclarations(var_context, &parsing_result, names, CHECK_OK);
- ExpectSemicolon(CHECK_OK);
- return result;
-}
-
-Block* Parser::ParseVariableDeclarations(
- VariableDeclarationContext var_context,
- DeclarationParsingResult* parsing_result,
- ZoneList<const AstRawString*>* names, 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;
-
- Block* init_block = nullptr;
- if (var_context != kForStatement) {
- init_block = factory()->NewBlock(
- NULL, 1, true, parsing_result->descriptor.declaration_pos);
- }
-
- if (peek() == Token::VAR) {
- Consume(Token::VAR);
- } else if (peek() == Token::CONST) {
- Consume(Token::CONST);
- DCHECK(var_context != kStatement);
- parsing_result->descriptor.mode = CONST;
- } else if (peek() == Token::LET) {
- Consume(Token::LET);
- DCHECK(var_context != kStatement);
- parsing_result->descriptor.mode = LET;
- } 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();
- do {
- FuncNameInferrer::State fni_state(fni_);
-
- // Parse name.
- if (!first_declaration) Consume(Token::COMMA);
-
- Expression* pattern;
- int decl_pos = peek_position();
- {
- ExpressionClassifier pattern_classifier(this);
- pattern = ParsePrimaryExpression(&pattern_classifier, CHECK_OK);
- ValidateBindingPattern(&pattern_classifier, CHECK_OK);
- if (IsLexicalVariableMode(parsing_result->descriptor.mode)) {
- ValidateLetPattern(&pattern_classifier, CHECK_OK);
- }
- }
-
- 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);
- }
-
- Expression* value = NULL;
- int initializer_position = kNoSourcePosition;
- if (Check(Token::ASSIGN)) {
- ExpressionClassifier classifier(this);
- value = ParseAssignmentExpression(var_context != kForStatement,
- &classifier, CHECK_OK);
- RewriteNonPattern(&classifier, CHECK_OK);
- 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();
- }
- }
-
- ParserBaseTraits<Parser>::SetFunctionNameFromIdentifierRef(value,
- pattern);
-
- // End position of the initializer is after the assignment expression.
- initializer_position = scanner()->location().end_pos;
- } else {
- // Initializers may be either required or implied unless this is a
- // for-in/of iteration variable.
- if (var_context != kForStatement || !PeekInOrOf()) {
- // ES6 'const' and binding patterns require initializers.
- if (parsing_result->descriptor.mode == CONST ||
- !pattern->IsVariableProxy()) {
- ReportMessageAt(
- Scanner::Location(decl_pos, scanner()->location().end_pos),
- MessageTemplate::kDeclarationMissingInitializer,
- !pattern->IsVariableProxy() ? "destructuring" : "const");
- *ok = false;
- return nullptr;
- }
-
- // 'let x' initializes 'x' to undefined.
- if (parsing_result->descriptor.mode == LET) {
- value = GetLiteralUndefined(position());
- }
- }
-
- // End position of the initializer is after the variable.
- initializer_position = position();
- }
-
- DeclarationParsingResult::Declaration decl(pattern, initializer_position,
- value);
- if (var_context == kForStatement) {
- // Save the declaration for further handling in ParseForStatement.
- parsing_result->declarations.Add(decl);
- } else {
- // Immediately declare the variable otherwise. This avoids O(N^2)
- // behavior (where N is the number of variables in a single
- // declaration) in the PatternRewriter having to do with removing
- // and adding VariableProxies to the Scope (see bug 4699).
- DCHECK_NOT_NULL(init_block);
- PatternRewriter::DeclareAndInitializeVariables(
- init_block, &parsing_result->descriptor, &decl, names, CHECK_OK);
- }
- first_declaration = false;
- } while (peek() == Token::COMMA);
-
- parsing_result->bindings_loc =
- Scanner::Location(bindings_start, scanner()->location().end_pos);
-
- DCHECK(*ok);
- return init_block;
-}
-
-
-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;
- }
+bool Parser::ContainsLabel(ZoneList<const AstRawString*>* labels,
+ const AstRawString* label) {
+ DCHECK_NOT_NULL(label);
+ if (labels != nullptr) {
+ for (int i = labels->length(); i-- > 0;) {
+ if (labels->at(i) == label) return true;
}
}
return false;
}
-Statement* Parser::ParseFunctionDeclaration(bool* ok) {
- Consume(Token::FUNCTION);
- int pos = position();
- ParseFunctionFlags flags = ParseFunctionFlags::kIsNormal;
- if (Check(Token::MUL)) {
- flags |= ParseFunctionFlags::kIsGenerator;
- if (allow_harmony_restrictive_declarations()) {
- ReportMessageAt(scanner()->location(),
- MessageTemplate::kGeneratorInLegacyContext);
- *ok = false;
- return nullptr;
- }
+Expression* Parser::RewriteReturn(Expression* return_value, int pos) {
+ if (IsSubclassConstructor(function_state_->kind())) {
+ // For subclass constructors we need to return this in case of undefined
+ // return a Smi (transformed into an exception in the ConstructStub)
+ // for a non object.
+ //
+ // return expr;
+ //
+ // Is rewritten as:
+ //
+ // return (temp = expr) === undefined ? this :
+ // %_IsJSReceiver(temp) ? temp : 1;
+
+ // temp = expr
+ Variable* temp = NewTemporary(ast_value_factory()->empty_string());
+ Assignment* assign = factory()->NewAssignment(
+ Token::ASSIGN, factory()->NewVariableProxy(temp), return_value, 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 : 1;
+ Expression* is_object_conditional = factory()->NewConditional(
+ is_spec_object_call, factory()->NewVariableProxy(temp),
+ factory()->NewSmiLiteral(1, pos), pos);
+
+ // temp === undefined
+ Expression* is_undefined = factory()->NewCompareOperation(
+ Token::EQ_STRICT, assign,
+ factory()->NewUndefinedLiteral(kNoSourcePosition), pos);
+
+ // is_undefined ? this : is_object_conditional
+ return_value = factory()->NewConditional(is_undefined, ThisExpression(pos),
+ is_object_conditional, pos);
}
-
- return ParseHoistableDeclaration(pos, flags, nullptr, false, CHECK_OK);
-}
-
-Statement* Parser::ParseExpressionOrLabelledStatement(
- ZoneList<const AstRawString*>* labels,
- AllowLabelledFunctionStatement allow_function, 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;
- 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)) {
- 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);
- // ES#sec-labelled-function-declarations Labelled Function Declarations
- if (peek() == Token::FUNCTION && is_sloppy(language_mode())) {
- if (allow_function == kAllowLabelledFunctionStatement) {
- return ParseFunctionDeclaration(ok);
- } else {
- return ParseScopedStatement(labels, true, ok);
- }
- }
- return ParseStatement(labels, kDisallowLabelledFunctionStatement, 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.
- 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 = ParseScopedStatement(labels, false, CHECK_OK);
- Statement* else_statement = NULL;
- if (peek() == Token::ELSE) {
- Next();
- else_statement = ParseScopedStatement(labels, false, CHECK_OK);
- } else {
- else_statement = factory()->NewEmptyStatement(kNoSourcePosition);
- }
- 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;
- }
- 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;
- }
- 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();
-
- 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(loc.beg_pos);
- } else {
- return_value = GetLiteralUndefined(position());
- }
- } else {
- int pos = peek_position();
-
- if (IsSubclassConstructor(function_state_->kind())) {
- // Because of the return code rewriting that happens in case of a subclass
- // constructor we don't want to accept tail calls, therefore we don't set
- // ReturnExprScope to kInsideValidReturnStatement here.
- return_value = ParseExpression(true, CHECK_OK);
-
- // For subclass constructors we need to return this in case of undefined
- // return a Smi (transformed into an exception in the ConstructStub)
- // for a non object.
- //
- // return expr;
- //
- // Is rewritten as:
- //
- // return (temp = expr) === undefined ? this :
- // %_IsJSReceiver(temp) ? temp : 1;
-
- // temp = expr
- Variable* temp = NewTemporary(ast_value_factory()->empty_string());
- Assignment* assign = factory()->NewAssignment(
- Token::ASSIGN, factory()->NewVariableProxy(temp), return_value, 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 : 1;
- Expression* is_object_conditional = factory()->NewConditional(
- is_spec_object_call, factory()->NewVariableProxy(temp),
- factory()->NewSmiLiteral(1, pos), pos);
-
- // temp === undefined
- Expression* is_undefined = factory()->NewCompareOperation(
- Token::EQ_STRICT, assign,
- factory()->NewUndefinedLiteral(kNoSourcePosition), pos);
-
- // is_undefined ? this : is_object_conditional
- return_value = factory()->NewConditional(
- is_undefined, ThisExpression(pos), is_object_conditional, pos);
- } else {
- ReturnExprScope maybe_allow_tail_calls(
- function_state_, ReturnExprContext::kInsideValidReturnStatement);
- return_value = ParseExpression(true, CHECK_OK);
-
- if (allow_tailcalls() && !is_sloppy(language_mode()) && !is_resumable()) {
- // ES6 14.6.1 Static Semantics: IsInTailPosition
- function_state_->AddImplicitTailCallExpression(return_value);
- }
- }
- }
- ExpectSemicolon(CHECK_OK);
-
if (is_generator()) {
return_value = BuildIteratorResult(return_value, true);
} else if (is_async_function()) {
- return_value = BuildPromiseResolve(return_value, return_value->position());
+ return_value = BuildResolvePromise(return_value, return_value->position());
}
+ return return_value;
+}
- result = factory()->NewReturnStatement(return_value, loc.beg_pos);
-
- DeclarationScope* decl_scope = GetDeclarationScope();
- if (decl_scope->is_script_scope() || decl_scope->is_eval_scope()) {
- ReportMessageAt(loc, MessageTemplate::kIllegalReturn);
+Expression* Parser::RewriteDoExpression(Block* body, int pos, bool* ok) {
+ Variable* result = NewTemporary(ast_value_factory()->dot_result_string());
+ DoExpression* expr = factory()->NewDoExpression(body, result, pos);
+ if (!Rewriter::Rewrite(this, GetClosureScope(), expr, ast_value_factory())) {
*ok = false;
- return NULL;
+ return nullptr;
}
- return result;
+ return expr;
}
-
-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* with_scope = NewScope(WITH_SCOPE);
- Statement* body;
- {
- BlockState block_state(&scope_state_, with_scope);
- with_scope->set_start_position(scanner()->peek_location().beg_pos);
- body = ParseScopedStatement(labels, true, CHECK_OK);
- 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());
- }
- return factory()->NewCaseClause(label, statements, pos);
-}
-
-
-Statement* Parser::ParseSwitchStatement(ZoneList<const AstRawString*>* labels,
- bool* ok) {
- // SwitchStatement ::
- // 'switch' '(' Expression ')' '{' CaseClause* '}'
+Statement* Parser::RewriteSwitchStatement(Expression* tag,
+ SwitchStatement* switch_statement,
+ ZoneList<CaseClause*>* cases,
+ Scope* scope) {
// 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:
@@ -2728,12 +1734,6 @@
// }
Block* switch_block = factory()->NewBlock(NULL, 2, false, kNoSourcePosition);
- 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 =
NewTemporary(ast_value_factory()->dot_switch_tag_string());
@@ -2752,301 +1752,112 @@
factory()->NewUndefinedLiteral(kNoSourcePosition), kNoSourcePosition),
zone());
+ Expression* tag_read = factory()->NewVariableProxy(tag_variable);
+ switch_statement->Initialize(tag_read, cases);
Block* cases_block = factory()->NewBlock(NULL, 1, false, kNoSourcePosition);
-
- SwitchStatement* switch_statement =
- factory()->NewSwitchStatement(labels, switch_pos);
-
- {
- BlockState cases_block_state(&scope_state_);
- cases_block_state.set_start_position(scanner()->location().beg_pos);
- cases_block_state.SetNonlinear();
- 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_block_state.set_end_position(scanner()->location().end_pos);
- cases_block->set_scope(cases_block_state.FinalizedBlockScope());
- }
-
+ cases_block->statements()->Add(switch_statement, zone());
+ cases_block->set_scope(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;
+void Parser::RewriteCatchPattern(CatchInfo* catch_info, bool* ok) {
+ if (catch_info->name == nullptr) {
+ DCHECK_NOT_NULL(catch_info->pattern);
+ catch_info->name = ast_value_factory()->dot_catch_string();
}
- Expression* exception = ParseExpression(true, CHECK_OK);
- ExpectSemicolon(CHECK_OK);
+ catch_info->variable = catch_info->scope->DeclareLocal(
+ catch_info->name, VAR, kCreatedInitialized, NORMAL_VARIABLE);
+ if (catch_info->pattern != nullptr) {
+ DeclarationDescriptor descriptor;
+ descriptor.declaration_kind = DeclarationDescriptor::NORMAL;
+ descriptor.scope = scope();
+ descriptor.hoist_scope = nullptr;
+ descriptor.mode = LET;
+ descriptor.declaration_pos = catch_info->pattern->position();
+ descriptor.initialization_pos = catch_info->pattern->position();
- return factory()->NewExpressionStatement(
- factory()->NewThrow(exception, pos), pos);
+ // Initializer position for variables declared by the pattern.
+ const int initializer_position = position();
+
+ DeclarationParsingResult::Declaration decl(
+ catch_info->pattern, initializer_position,
+ factory()->NewVariableProxy(catch_info->variable));
+
+ catch_info->init_block =
+ factory()->NewBlock(nullptr, 8, true, kNoSourcePosition);
+ PatternRewriter::DeclareAndInitializeVariables(
+ this, catch_info->init_block, &descriptor, &decl,
+ &catch_info->bound_names, ok);
+ } else {
+ catch_info->bound_names.Add(catch_info->name, zone());
+ }
}
-
-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;
- {
- ReturnExprScope no_tail_calls(function_state_,
- ReturnExprContext::kInsideTryBlock);
- try_block = ParseBlock(NULL, CHECK_OK);
- }
-
- Token::Value tok = peek();
-
- bool catch_for_promise_reject = false;
- if (allow_natives() && tok == Token::MOD) {
- Consume(Token::MOD);
- catch_for_promise_reject = true;
- 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;
- TailCallExpressionList tail_call_expressions_in_catch_block(zone());
- if (tok == Token::CATCH) {
- Consume(Token::CATCH);
-
- Expect(Token::LPAREN, CHECK_OK);
- catch_scope = NewScope(CATCH_SCOPE);
- catch_scope->set_start_position(scanner()->location().beg_pos);
-
- {
- CollectExpressionsInTailPositionToListScope
- collect_tail_call_expressions_scope(
- function_state_, &tail_call_expressions_in_catch_block);
- BlockState block_state(&scope_state_, catch_scope);
-
- catch_block = factory()->NewBlock(nullptr, 16, false, kNoSourcePosition);
-
- // Create a block scope to hold any lexical declarations created
- // as part of destructuring the catch parameter.
- {
- BlockState block_state(&scope_state_);
- block_state.set_start_position(scanner()->location().beg_pos);
- Target target(&this->target_stack_, catch_block);
-
- const AstRawString* name = ast_value_factory()->dot_catch_string();
- Expression* pattern = nullptr;
- if (peek_any_identifier()) {
- name = ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK);
- } else {
- ExpressionClassifier pattern_classifier(this);
- pattern = ParsePrimaryExpression(&pattern_classifier, CHECK_OK);
- ValidateBindingPattern(&pattern_classifier, CHECK_OK);
- }
- catch_variable = catch_scope->DeclareLocal(
- name, VAR, kCreatedInitialized, Variable::NORMAL);
-
- Expect(Token::RPAREN, CHECK_OK);
-
- ZoneList<const AstRawString*> bound_names(1, zone());
- if (pattern != nullptr) {
- DeclarationDescriptor descriptor;
- descriptor.declaration_kind = DeclarationDescriptor::NORMAL;
- descriptor.parser = this;
- descriptor.scope = scope();
- descriptor.hoist_scope = nullptr;
- descriptor.mode = LET;
- descriptor.declaration_pos = pattern->position();
- descriptor.initialization_pos = pattern->position();
-
- // Initializer position for variables declared by the pattern.
- const int initializer_position = position();
-
- DeclarationParsingResult::Declaration decl(
- pattern, initializer_position,
- factory()->NewVariableProxy(catch_variable));
-
- Block* init_block =
- factory()->NewBlock(nullptr, 8, true, kNoSourcePosition);
- PatternRewriter::DeclareAndInitializeVariables(
- init_block, &descriptor, &decl, &bound_names, CHECK_OK);
- catch_block->statements()->Add(init_block, zone());
- } else {
- bound_names.Add(name, zone());
- }
-
- Block* inner_block = ParseBlock(nullptr, CHECK_OK);
- catch_block->statements()->Add(inner_block, zone());
-
- // Check for `catch(e) { let e; }` and similar errors.
- Scope* inner_block_scope = inner_block->scope();
- if (inner_block_scope != nullptr) {
- Declaration* decl =
- inner_block_scope->CheckLexDeclarationsConflictingWith(
- bound_names);
- if (decl != nullptr) {
- const AstRawString* name = decl->proxy()->raw_name();
- int position = decl->proxy()->position();
- Scanner::Location location =
- position == kNoSourcePosition
- ? Scanner::Location::invalid()
- : Scanner::Location(position, position + 1);
- ReportMessageAt(location, MessageTemplate::kVarRedeclaration, name);
- *ok = false;
- return nullptr;
- }
- }
- block_state.set_end_position(scanner()->location().end_pos);
- catch_block->set_scope(block_state.FinalizedBlockScope());
- }
+void Parser::ValidateCatchBlock(const CatchInfo& catch_info, bool* ok) {
+ // Check for `catch(e) { let e; }` and similar errors.
+ Scope* inner_block_scope = catch_info.inner_block->scope();
+ if (inner_block_scope != nullptr) {
+ Declaration* decl = inner_block_scope->CheckLexDeclarationsConflictingWith(
+ catch_info.bound_names);
+ if (decl != nullptr) {
+ const AstRawString* name = decl->proxy()->raw_name();
+ int position = decl->proxy()->position();
+ Scanner::Location location =
+ position == kNoSourcePosition
+ ? Scanner::Location::invalid()
+ : Scanner::Location(position, position + 1);
+ ReportMessageAt(location, MessageTemplate::kVarRedeclaration, name);
+ *ok = false;
}
-
- 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);
- }
-
+Statement* Parser::RewriteTryStatement(Block* try_block, Block* catch_block,
+ Block* finally_block,
+ const CatchInfo& catch_info, int pos) {
// 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 (catch_block != nullptr && finally_block != nullptr) {
// If we have both, create an inner try/catch.
- DCHECK(catch_scope != NULL && catch_variable != NULL);
+ DCHECK_NOT_NULL(catch_info.scope);
+ DCHECK_NOT_NULL(catch_info.variable);
TryCatchStatement* statement;
- if (catch_for_promise_reject) {
+ if (catch_info.for_promise_reject) {
statement = factory()->NewTryCatchStatementForPromiseReject(
- try_block, catch_scope, catch_variable, catch_block,
+ try_block, catch_info.scope, catch_info.variable, catch_block,
kNoSourcePosition);
} else {
- statement = factory()->NewTryCatchStatement(try_block, catch_scope,
- catch_variable, catch_block,
- kNoSourcePosition);
+ statement = factory()->NewTryCatchStatement(
+ try_block, catch_info.scope, catch_info.variable, catch_block,
+ kNoSourcePosition);
}
- try_block = factory()->NewBlock(NULL, 1, false, kNoSourcePosition);
+ try_block = factory()->NewBlock(nullptr, 1, false, kNoSourcePosition);
try_block->statements()->Add(statement, zone());
- catch_block = NULL; // Clear to indicate it's been handled.
+ catch_block = nullptr; // Clear to indicate it's been handled.
}
- TryStatement* result = NULL;
- if (catch_block != NULL) {
+ if (catch_block != nullptr) {
// For a try-catch construct append return expressions from the catch block
// to the list of return expressions.
function_state_->tail_call_expressions().Append(
- tail_call_expressions_in_catch_block);
+ catch_info.tail_call_expressions);
- DCHECK(finally_block == NULL);
- DCHECK(catch_scope != NULL && catch_variable != NULL);
- result = factory()->NewTryCatchStatement(try_block, catch_scope,
- catch_variable, catch_block, pos);
+ DCHECK_NULL(finally_block);
+ DCHECK_NOT_NULL(catch_info.scope);
+ DCHECK_NOT_NULL(catch_info.variable);
+ return factory()->NewTryCatchStatement(
+ try_block, catch_info.scope, catch_info.variable, catch_block, pos);
} else {
- if (FLAG_harmony_explicit_tailcalls &&
- tail_call_expressions_in_catch_block.has_explicit_tail_calls()) {
- // TODO(ishell): update chapter number.
- // ES8 XX.YY.ZZ
- ReportMessageAt(tail_call_expressions_in_catch_block.location(),
- MessageTemplate::kUnexpectedTailCallInCatchBlock);
- *ok = false;
- return NULL;
- }
- DCHECK(finally_block != NULL);
- result = factory()->NewTryFinallyStatement(try_block, finally_block, pos);
+ DCHECK_NOT_NULL(finally_block);
+ return 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 = ParseScopedStatement(NULL, true, 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 = ParseScopedStatement(NULL, true, CHECK_OK);
-
- if (loop != NULL) loop->Initialize(cond, body);
- return loop;
-}
-
-
// !%_IsJSReceiver(result = iterator.next()) &&
// %ThrowIteratorResultNotAnObject(result)
Expression* Parser::BuildIteratorNextResult(Expression* iterator,
@@ -3115,6 +1926,138 @@
return stmt;
}
+// Special case for legacy for
+//
+// for (var x = initializer in enumerable) body
+//
+// An initialization block of the form
+//
+// {
+// x = initializer;
+// }
+//
+// is returned in this case. It has reserved space for two statements,
+// so that (later on during parsing), the equivalent of
+//
+// for (x in enumerable) body
+//
+// is added as a second statement to it.
+Block* Parser::RewriteForVarInLegacy(const ForInfo& for_info) {
+ const DeclarationParsingResult::Declaration& decl =
+ for_info.parsing_result.declarations[0];
+ if (!IsLexicalVariableMode(for_info.parsing_result.descriptor.mode) &&
+ decl.pattern->IsVariableProxy() && decl.initializer != nullptr) {
+ DCHECK(!allow_harmony_for_in());
+ ++use_counts_[v8::Isolate::kForInInitializer];
+ const AstRawString* name = decl.pattern->AsVariableProxy()->raw_name();
+ VariableProxy* single_var = NewUnresolved(name);
+ Block* init_block = factory()->NewBlock(
+ nullptr, 2, true, for_info.parsing_result.descriptor.declaration_pos);
+ init_block->statements()->Add(
+ factory()->NewExpressionStatement(
+ factory()->NewAssignment(Token::ASSIGN, single_var,
+ decl.initializer, kNoSourcePosition),
+ kNoSourcePosition),
+ zone());
+ return init_block;
+ }
+ return nullptr;
+}
+
+// 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
+// }
+void Parser::DesugarBindingInForEachStatement(ForInfo* for_info,
+ Block** body_block,
+ Expression** each_variable,
+ bool* ok) {
+ DeclarationParsingResult::Declaration& decl =
+ for_info->parsing_result.declarations[0];
+ Variable* temp = NewTemporary(ast_value_factory()->dot_for_string());
+ auto each_initialization_block =
+ factory()->NewBlock(nullptr, 1, true, kNoSourcePosition);
+ {
+ auto descriptor = for_info->parsing_result.descriptor;
+ descriptor.declaration_pos = kNoSourcePosition;
+ descriptor.initialization_pos = kNoSourcePosition;
+ decl.initializer = factory()->NewVariableProxy(temp);
+
+ bool is_for_var_of =
+ for_info->mode == ForEachStatement::ITERATE &&
+ for_info->parsing_result.descriptor.mode == VariableMode::VAR;
+
+ PatternRewriter::DeclareAndInitializeVariables(
+ this, each_initialization_block, &descriptor, &decl,
+ (IsLexicalVariableMode(for_info->parsing_result.descriptor.mode) ||
+ is_for_var_of)
+ ? &for_info->bound_names
+ : nullptr,
+ CHECK_OK_VOID);
+
+ // Annex B.3.5 prohibits the form
+ // `try {} catch(e) { for (var e of {}); }`
+ // So if we are parsing a statement like `for (var ... of ...)`
+ // we need to walk up the scope chain and look for catch scopes
+ // which have a simple binding, then compare their binding against
+ // all of the names declared in the init of the for-of we're
+ // parsing.
+ if (is_for_var_of) {
+ Scope* catch_scope = scope();
+ while (catch_scope != nullptr && !catch_scope->is_declaration_scope()) {
+ if (catch_scope->is_catch_scope()) {
+ auto name = catch_scope->catch_variable_name();
+ // If it's a simple binding and the name is declared in the for loop.
+ if (name != ast_value_factory()->dot_catch_string() &&
+ for_info->bound_names.Contains(name)) {
+ ReportMessageAt(for_info->parsing_result.bindings_loc,
+ MessageTemplate::kVarRedeclaration, name);
+ *ok = false;
+ return;
+ }
+ }
+ catch_scope = catch_scope->outer_scope();
+ }
+ }
+ }
+
+ *body_block = factory()->NewBlock(nullptr, 3, false, kNoSourcePosition);
+ (*body_block)->statements()->Add(each_initialization_block, zone());
+ *each_variable = factory()->NewVariableProxy(temp, for_info->each_loc.beg_pos,
+ for_info->each_loc.end_pos);
+}
+
+// Create a TDZ for any lexically-bound names in for in/of statements.
+Block* Parser::CreateForEachStatementTDZ(Block* init_block,
+ const ForInfo& for_info, bool* ok) {
+ if (IsLexicalVariableMode(for_info.parsing_result.descriptor.mode)) {
+ DCHECK_NULL(init_block);
+
+ init_block = factory()->NewBlock(nullptr, 1, false, kNoSourcePosition);
+
+ for (int i = 0; i < for_info.bound_names.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.
+ Declaration* tdz_decl = DeclareVariable(for_info.bound_names[i], LET,
+ kNoSourcePosition, CHECK_OK);
+ tdz_decl->proxy()->var()->set_initializer_position(position());
+ }
+ }
+ return init_block;
+}
+
Statement* Parser::InitializeForOfStatement(ForOfStatement* for_of,
Expression* each,
Expression* iterable,
@@ -3138,8 +2081,7 @@
{
assign_iterator = factory()->NewAssignment(
Token::ASSIGN, factory()->NewVariableProxy(iterator),
- GetIterator(iterable, factory(), iterable->position()),
- iterable->position());
+ GetIterator(iterable, iterable->position()), iterable->position());
}
// !%_IsJSReceiver(result = iterator.next()) &&
@@ -3240,9 +2182,8 @@
}
Statement* Parser::DesugarLexicalBindingsInForStatement(
- Scope* inner_scope, VariableMode mode, ZoneList<const AstRawString*>* names,
ForStatement* loop, Statement* init, Expression* cond, Statement* next,
- Statement* body, bool* ok) {
+ Statement* body, Scope* inner_scope, const ForInfo& for_info, 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
@@ -3280,11 +2221,11 @@
// }
// }
- DCHECK(names->length() > 0);
- ZoneList<Variable*> temps(names->length(), zone());
+ DCHECK(for_info.bound_names.length() > 0);
+ ZoneList<Variable*> temps(for_info.bound_names.length(), zone());
- Block* outer_block =
- factory()->NewBlock(NULL, names->length() + 4, false, kNoSourcePosition);
+ Block* outer_block = factory()->NewBlock(
+ nullptr, for_info.bound_names.length() + 4, false, kNoSourcePosition);
// Add statement: let/const x = i.
outer_block->statements()->Add(init, zone());
@@ -3293,8 +2234,8 @@
// For each lexical variable x:
// make statement: temp_x = x.
- for (int i = 0; i < names->length(); i++) {
- VariableProxy* proxy = NewUnresolved(names->at(i));
+ for (int i = 0; i < for_info.bound_names.length(); i++) {
+ VariableProxy* proxy = NewUnresolved(for_info.bound_names[i]);
Variable* temp = NewTemporary(temp_name);
VariableProxy* temp_proxy = factory()->NewVariableProxy(temp);
Assignment* assignment = factory()->NewAssignment(Token::ASSIGN, temp_proxy,
@@ -3338,14 +2279,15 @@
{
BlockState block_state(&scope_state_, inner_scope);
- Block* ignore_completion_block =
- factory()->NewBlock(NULL, names->length() + 3, true, kNoSourcePosition);
- ZoneList<Variable*> inner_vars(names->length(), zone());
+ Block* ignore_completion_block = factory()->NewBlock(
+ nullptr, for_info.bound_names.length() + 3, true, kNoSourcePosition);
+ ZoneList<Variable*> inner_vars(for_info.bound_names.length(), zone());
// For each let variable x:
// make statement: let/const x = temp_x.
- for (int i = 0; i < names->length(); i++) {
- Declaration* decl =
- DeclareVariable(names->at(i), mode, kNoSourcePosition, CHECK_OK);
+ for (int i = 0; i < for_info.bound_names.length(); i++) {
+ Declaration* decl = DeclareVariable(
+ for_info.bound_names[i], for_info.parsing_result.descriptor.mode,
+ kNoSourcePosition, CHECK_OK);
inner_vars.Add(decl->proxy()->var(), zone());
VariableProxy* temp_proxy = factory()->NewVariableProxy(temps.at(i));
Assignment* assignment = factory()->NewAssignment(
@@ -3429,7 +2371,7 @@
// 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++) {
+ for (int i = 0; i < for_info.bound_names.length(); i++) {
VariableProxy* temp_proxy = factory()->NewVariableProxy(temps.at(i));
VariableProxy* proxy =
factory()->NewVariableProxy(inner_vars.at(i), inner_var_proxy_pos);
@@ -3479,433 +2421,7 @@
return outer_block;
}
-Statement* Parser::ParseScopedStatement(ZoneList<const AstRawString*>* labels,
- bool legacy, bool* ok) {
- if (is_strict(language_mode()) || peek() != Token::FUNCTION ||
- (legacy && allow_harmony_restrictive_declarations())) {
- return ParseSubStatement(labels, kDisallowLabelledFunctionStatement, ok);
- } else {
- if (legacy) {
- ++use_counts_[v8::Isolate::kLegacyFunctionDeclaration];
- }
- // Make a block around the statement for a lexical binding
- // is introduced by a FunctionDeclaration.
- BlockState block_state(&scope_state_);
- block_state.set_start_position(scanner()->location().beg_pos);
- Block* block = factory()->NewBlock(NULL, 1, false, kNoSourcePosition);
- Statement* body = ParseFunctionDeclaration(CHECK_OK);
- block->statements()->Add(body, zone());
- block_state.set_end_position(scanner()->location().end_pos);
- block->set_scope(block_state.FinalizedBlockScope());
- return block;
- }
-}
-
-Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
- bool* ok) {
- int stmt_pos = peek_position();
- Statement* init = NULL;
- ZoneList<const AstRawString*> bound_names(1, zone());
- bool bound_names_are_lexical = false;
-
- // Create an in-between scope for let-bound iteration variables.
- BlockState for_state(&scope_state_);
- Expect(Token::FOR, CHECK_OK);
- Expect(Token::LPAREN, CHECK_OK);
- for_state.set_start_position(scanner()->location().beg_pos);
- for_state.set_is_hidden();
- DeclarationParsingResult parsing_result;
- if (peek() != Token::SEMICOLON) {
- if (peek() == Token::VAR || peek() == Token::CONST ||
- (peek() == Token::LET && IsNextLetKeyword())) {
- ParseVariableDeclarations(kForStatement, &parsing_result, nullptr,
- CHECK_OK);
-
- ForEachStatement::VisitMode mode = ForEachStatement::ENUMERATE;
- int each_beg_pos = scanner()->location().beg_pos;
- int each_end_pos = scanner()->location().end_pos;
-
- if (CheckInOrOf(&mode, ok)) {
- if (!*ok) return nullptr;
- if (parsing_result.declarations.length() != 1) {
- ReportMessageAt(parsing_result.bindings_loc,
- MessageTemplate::kForInOfLoopMultiBindings,
- ForEachStatement::VisitModeString(mode));
- *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() || allow_harmony_for_in())) {
- // Only increment the use count if we would have let this through
- // without the flag.
- if (allow_harmony_for_in()) {
- ++use_counts_[v8::Isolate::kForInInitializer];
- }
- ReportMessageAt(parsing_result.first_initializer_loc,
- MessageTemplate::kForInOfLoopInitializer,
- ForEachStatement::VisitModeString(mode));
- *ok = false;
- return nullptr;
- }
-
- Block* init_block = nullptr;
- bound_names_are_lexical =
- IsLexicalVariableMode(parsing_result.descriptor.mode);
-
- // special case for legacy for (var ... = ... in ...)
- if (!bound_names_are_lexical && decl.pattern->IsVariableProxy() &&
- decl.initializer != nullptr) {
- DCHECK(!allow_harmony_for_in());
- ++use_counts_[v8::Isolate::kForInInitializer];
- const AstRawString* name =
- decl.pattern->AsVariableProxy()->raw_name();
- VariableProxy* single_var = NewUnresolved(name);
- 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, kNoSourcePosition),
- kNoSourcePosition),
- 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 = NewTemporary(ast_value_factory()->dot_for_string());
- ForEachStatement* loop =
- factory()->NewForEachStatement(mode, labels, stmt_pos);
- Target target(&this->target_stack_, loop);
-
- int each_keyword_position = scanner()->location().beg_pos;
-
- Expression* enumerable;
- if (mode == ForEachStatement::ITERATE) {
- ExpressionClassifier classifier(this);
- enumerable = ParseAssignmentExpression(true, &classifier, CHECK_OK);
- RewriteNonPattern(&classifier, CHECK_OK);
- } else {
- enumerable = ParseExpression(true, CHECK_OK);
- }
-
- Expect(Token::RPAREN, CHECK_OK);
-
-
- Block* body_block =
- factory()->NewBlock(NULL, 3, false, kNoSourcePosition);
-
- Statement* final_loop;
- {
- ReturnExprScope no_tail_calls(function_state_,
- ReturnExprContext::kInsideForInOfBody);
- BlockState block_state(&scope_state_);
- block_state.set_start_position(scanner()->location().beg_pos);
-
- Statement* body = ParseScopedStatement(NULL, true, CHECK_OK);
-
- auto each_initialization_block =
- factory()->NewBlock(nullptr, 1, true, kNoSourcePosition);
- {
- auto descriptor = parsing_result.descriptor;
- descriptor.declaration_pos = kNoSourcePosition;
- descriptor.initialization_pos = kNoSourcePosition;
- decl.initializer = factory()->NewVariableProxy(temp);
-
- bool is_for_var_of =
- mode == ForEachStatement::ITERATE &&
- parsing_result.descriptor.mode == VariableMode::VAR;
-
- PatternRewriter::DeclareAndInitializeVariables(
- each_initialization_block, &descriptor, &decl,
- bound_names_are_lexical || is_for_var_of ? &bound_names
- : nullptr,
- CHECK_OK);
-
- // Annex B.3.5 prohibits the form
- // `try {} catch(e) { for (var e of {}); }`
- // So if we are parsing a statement like `for (var ... of ...)`
- // we need to walk up the scope chain and look for catch scopes
- // which have a simple binding, then compare their binding against
- // all of the names declared in the init of the for-of we're
- // parsing.
- if (is_for_var_of) {
- Scope* catch_scope = scope();
- while (catch_scope != nullptr &&
- !catch_scope->is_declaration_scope()) {
- if (catch_scope->is_catch_scope()) {
- auto name = catch_scope->catch_variable_name();
- if (name !=
- ast_value_factory()
- ->dot_catch_string()) { // i.e. is a simple binding
- if (bound_names.Contains(name)) {
- ReportMessageAt(parsing_result.bindings_loc,
- MessageTemplate::kVarRedeclaration, name);
- *ok = false;
- return nullptr;
- }
- }
- }
- catch_scope = catch_scope->outer_scope();
- }
- }
- }
-
- 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);
- final_loop = InitializeForEachStatement(
- loop, temp_proxy, enumerable, body_block, each_keyword_position);
- block_state.set_end_position(scanner()->location().end_pos);
- body_block->set_scope(block_state.FinalizedBlockScope());
- }
-
- // Create a TDZ for any lexically-bound names.
- if (bound_names_are_lexical) {
- DCHECK_NULL(init_block);
-
- init_block =
- factory()->NewBlock(nullptr, 1, false, kNoSourcePosition);
-
- for (int i = 0; i < bound_names.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.
- Declaration* tdz_decl = DeclareVariable(
- bound_names[i], LET, kNoSourcePosition, CHECK_OK);
- tdz_decl->proxy()->var()->set_initializer_position(position());
- }
- }
-
- for_state.set_end_position(scanner()->location().end_pos);
- Scope* for_scope = for_state.FinalizedBlockScope();
- // Parsed for-in loop w/ variable declarations.
- if (init_block != nullptr) {
- init_block->statements()->Add(final_loop, zone());
- init_block->set_scope(for_scope);
- return init_block;
- } else {
- DCHECK_NULL(for_scope);
- return final_loop;
- }
- } else {
- bound_names_are_lexical =
- IsLexicalVariableMode(parsing_result.descriptor.mode);
- init = parsing_result.BuildInitializationBlock(
- bound_names_are_lexical ? &bound_names : nullptr, CHECK_OK);
- }
- } else {
- int lhs_beg_pos = peek_position();
- ExpressionClassifier classifier(this);
- Expression* expression = ParseExpression(false, &classifier, CHECK_OK);
- int lhs_end_pos = scanner()->location().end_pos;
- ForEachStatement::VisitMode mode = ForEachStatement::ENUMERATE;
-
- bool is_for_each = CheckInOrOf(&mode, CHECK_OK);
- bool is_destructuring = is_for_each && (expression->IsArrayLiteral() ||
- expression->IsObjectLiteral());
-
- if (is_destructuring) {
- ValidateAssignmentPattern(&classifier, CHECK_OK);
- } else {
- RewriteNonPattern(&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);
-
- int each_keyword_position = scanner()->location().beg_pos;
-
- Expression* enumerable;
- if (mode == ForEachStatement::ITERATE) {
- ExpressionClassifier classifier(this);
- enumerable = ParseAssignmentExpression(true, &classifier, CHECK_OK);
- RewriteNonPattern(&classifier, CHECK_OK);
- } else {
- enumerable = ParseExpression(true, CHECK_OK);
- }
-
- Expect(Token::RPAREN, CHECK_OK);
-
- // For legacy compat reasons, give for loops similar treatment to
- // if statements in allowing a function declaration for a body
- Statement* body = ParseScopedStatement(NULL, true, CHECK_OK);
- Statement* final_loop = InitializeForEachStatement(
- loop, expression, enumerable, body, each_keyword_position);
-
- DCHECK_NULL(for_state.FinalizedBlockScope());
- return final_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.
- Expect(Token::SEMICOLON, CHECK_OK);
-
- Expression* cond = NULL;
- Statement* next = NULL;
- Statement* body = NULL;
-
- // 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 = scope();
- // TODO(verwaest): Allocate this through a ScopeState as well.
- if (bound_names_are_lexical && bound_names.length() > 0) {
- inner_scope = NewScopeWithParent(inner_scope, BLOCK_SCOPE);
- inner_scope->set_start_position(scanner()->location().beg_pos);
- }
- {
- BlockState block_state(&scope_state_, inner_scope);
-
- if (peek() != Token::SEMICOLON) {
- cond = ParseExpression(true, CHECK_OK);
- }
- Expect(Token::SEMICOLON, CHECK_OK);
-
- if (peek() != Token::RPAREN) {
- Expression* exp = ParseExpression(true, CHECK_OK);
- next = factory()->NewExpressionStatement(exp, exp->position());
- }
- Expect(Token::RPAREN, CHECK_OK);
-
- body = ParseScopedStatement(NULL, true, CHECK_OK);
- }
-
- Statement* result = NULL;
- if (bound_names_are_lexical && bound_names.length() > 0) {
- result = DesugarLexicalBindingsInForStatement(
- inner_scope, parsing_result.descriptor.mode, &bound_names, loop, init,
- cond, next, body, CHECK_OK);
- for_state.set_end_position(scanner()->location().end_pos);
- } else {
- for_state.set_end_position(scanner()->location().end_pos);
- Scope* for_scope = for_state.FinalizedBlockScope();
- 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, kNoSourcePosition);
- 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 Parser::ParseArrowFunctionFormalParameters(
+void Parser::AddArrowFunctionFormalParameters(
ParserFormalParameters* parameters, Expression* expr, int end_pos,
bool* ok) {
// ArrowFunctionFormals ::
@@ -3929,8 +2445,8 @@
Expression* left = binop->left();
Expression* right = binop->right();
int comma_pos = binop->position();
- ParseArrowFunctionFormalParameters(parameters, left, comma_pos,
- CHECK_OK_VOID);
+ AddArrowFunctionFormalParameters(parameters, left, comma_pos,
+ CHECK_OK_VOID);
// LHS of comma expression should be unparenthesized.
expr = right;
}
@@ -3958,80 +2474,14 @@
AddFormalParameter(parameters, expr, initializer, end_pos, is_rest);
}
-void Parser::DesugarAsyncFunctionBody(const AstRawString* function_name,
- Scope* scope, ZoneList<Statement*>* body,
- ExpressionClassifier* classifier,
- FunctionKind kind,
- FunctionBodyType body_type,
- bool accept_IN, int pos, bool* ok) {
- // function async_function() {
- // try {
- // .generator_object = %CreateGeneratorObject();
- // ... function body ...
- // } catch (e) {
- // return Promise.reject(e);
- // }
- // }
- scope->ForceContextAllocation();
- Variable* temp =
- NewTemporary(ast_value_factory()->dot_generator_object_string());
- function_state_->set_generator_object_variable(temp);
-
- Expression* init_generator_variable = factory()->NewAssignment(
- Token::INIT, factory()->NewVariableProxy(temp),
- BuildCreateJSGeneratorObject(pos, kind), kNoSourcePosition);
- body->Add(factory()->NewExpressionStatement(init_generator_variable,
- kNoSourcePosition),
- zone());
-
- Block* try_block = factory()->NewBlock(NULL, 8, true, kNoSourcePosition);
-
- ZoneList<Statement*>* inner_body = try_block->statements();
-
- Expression* return_value = nullptr;
- if (body_type == FunctionBodyType::kNormal) {
- ParseStatementList(inner_body, Token::RBRACE, CHECK_OK_VOID);
- return_value = factory()->NewUndefinedLiteral(kNoSourcePosition);
- } else {
- return_value =
- ParseAssignmentExpression(accept_IN, classifier, CHECK_OK_VOID);
- RewriteNonPattern(classifier, CHECK_OK_VOID);
- }
-
- return_value = BuildPromiseResolve(return_value, return_value->position());
- inner_body->Add(
- factory()->NewReturnStatement(return_value, return_value->position()),
- zone());
- body->Add(BuildRejectPromiseOnException(try_block), zone());
- scope->set_end_position(scanner()->location().end_pos);
-}
-
-DoExpression* Parser::ParseDoExpression(bool* ok) {
- // AssignmentExpression ::
- // do '{' StatementList '}'
- int pos = peek_position();
-
- Expect(Token::DO, CHECK_OK);
- Variable* result = NewTemporary(ast_value_factory()->dot_result_string());
- Block* block = ParseBlock(nullptr, CHECK_OK);
- DoExpression* expr = factory()->NewDoExpression(block, result, pos);
- if (!Rewriter::Rewrite(this, GetClosureScope(), expr, ast_value_factory())) {
- *ok = false;
- return nullptr;
- }
- return expr;
-}
-
-void ParserBaseTraits<Parser>::ParseArrowFunctionFormalParameterList(
+void Parser::DeclareArrowFunctionFormalParameters(
ParserFormalParameters* parameters, Expression* expr,
const Scanner::Location& params_loc, Scanner::Location* duplicate_loc,
- const Scope::Snapshot& scope_snapshot, bool* ok) {
+ bool* ok) {
if (expr->IsEmptyParentheses()) return;
- delegate()->ParseArrowFunctionFormalParameters(
- parameters, expr, params_loc.end_pos, CHECK_OK_VOID);
-
- scope_snapshot.Reparent(parameters->scope);
+ AddArrowFunctionFormalParameters(parameters, expr, params_loc.end_pos,
+ CHECK_OK_VOID);
if (parameters->Arity() > Code::kMaxArguments) {
ReportMessageAt(params_loc, MessageTemplate::kMalformedArrowFunParamList);
@@ -4039,23 +2489,25 @@
return;
}
- Type::ExpressionClassifier classifier(delegate());
+ ExpressionClassifier classifier(this);
if (!parameters->is_simple) {
- classifier.RecordNonSimpleParameter();
+ this->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;
+ DeclareFormalParameter(parameters->scope, parameter);
+ if (!this->classifier()
+ ->is_valid_formal_parameter_list_without_duplicates() &&
+ !duplicate_loc->IsValid()) {
+ *duplicate_loc =
+ this->classifier()->duplicate_formal_parameter_error().location;
}
}
DCHECK_EQ(parameters->is_simple, parameters->scope->has_simple_parameters());
}
-void ParserBaseTraits<Parser>::ReindexLiterals(
- const ParserFormalParameters& parameters) {
- if (delegate()->function_state_->materialized_literal_count() > 0) {
+void Parser::ReindexLiterals(const ParserFormalParameters& parameters) {
+ if (function_state_->materialized_literal_count() > 0) {
AstLiteralReindexer reindexer;
for (const auto p : parameters.params) {
@@ -4063,11 +2515,24 @@
if (p.initializer != nullptr) reindexer.Reindex(p.initializer);
}
- DCHECK(reindexer.count() <=
- delegate()->function_state_->materialized_literal_count());
+ DCHECK(reindexer.count() <= function_state_->materialized_literal_count());
}
}
+void Parser::PrepareGeneratorVariables(FunctionState* function_state) {
+ // For generators, allocating variables in contexts is currently a win
+ // because it minimizes the work needed to suspend and resume an
+ // activation. The machine code produced for generators (by full-codegen)
+ // relies on this forced context allocation, but not in an essential way.
+ 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.
+ Variable* temp =
+ NewTemporary(ast_value_factory()->dot_generator_object_string());
+ function_state->set_generator_object_variable(temp);
+}
FunctionLiteral* Parser::ParseFunctionLiteral(
const AstRawString* function_name, Scanner::Location function_name_location,
@@ -4119,7 +2584,9 @@
// 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:
+ // We separate between lazy parsing top level functions and lazy parsing inner
+ // functions, because the latter needs to do more work. In particular, we need
+ // to track unresolved variables to distinguish between these cases:
// (function foo() {
// bar = function() { return 1; }
// })();
@@ -4131,17 +2598,18 @@
// 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.
+ // immediately). bar can be parsed lazily, but we need to parse it in a mode
+ // that tracks unresolved variables.
+ DCHECK_IMPLIES(mode() == PARSE_LAZILY, FLAG_lazy);
+ DCHECK_IMPLIES(mode() == PARSE_LAZILY, allow_lazy());
+ DCHECK_IMPLIES(mode() == PARSE_LAZILY, extension_ == nullptr);
- // 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 &&
- this->scope()->AllowsLazyParsing() &&
- !function_state_->next_function_is_parenthesized();
+ bool is_lazy_top_level_function =
+ mode() == PARSE_LAZILY &&
+ eager_compile_hint == FunctionLiteral::kShouldLazyCompile &&
+ scope()->AllowsLazyParsingWithoutUnresolvedVariables();
- // Determine whether the function body can be discarded after parsing.
+ // Determine whether we can still lazy parse the inner function.
// The preconditions are:
// - Lazy compilation has to be enabled.
// - Neither V8 natives nor native function declarations can be allowed,
@@ -4156,18 +2624,20 @@
// - The function literal shouldn't be hinted to eagerly compile.
// - For asm.js functions the body needs to be available when module
// validation is active, because we examine the entire module at once.
+
+ // Inner functions will be parsed using a temporary Zone. After parsing, we
+ // will migrate unresolved variable into a Scope in the main Zone.
+ // TODO(marja): Refactor parsing modes: simplify this.
bool use_temp_zone =
- !is_lazily_parsed && FLAG_lazy && !allow_natives() &&
- extension_ == NULL && allow_lazy() &&
- function_type == FunctionLiteral::kDeclaration &&
+ allow_lazy() && function_type == FunctionLiteral::kDeclaration &&
eager_compile_hint != FunctionLiteral::kShouldEagerCompile &&
!(FLAG_validate_asm && scope()->IsAsmModule());
+ bool is_lazy_inner_function =
+ use_temp_zone && FLAG_lazy_inner_functions && !is_lazy_top_level_function;
- DeclarationScope* main_scope = nullptr;
- if (use_temp_zone) {
- // This Scope lives in the main Zone; we'll migrate data into it later.
- main_scope = NewFunctionScope(kind);
- }
+ // This Scope lives in the main zone. We'll migrate data into that zone later.
+ DeclarationScope* scope = NewFunctionScope(kind);
+ SetLanguageMode(scope, language_mode);
ZoneList<Statement*>* body = nullptr;
int arity = -1;
@@ -4177,6 +2647,32 @@
bool should_be_used_once_hint = false;
bool has_duplicate_parameters;
+ FunctionState function_state(&function_state_, &scope_state_, scope);
+#ifdef DEBUG
+ scope->SetScopeName(function_name);
+#endif
+
+ ExpressionClassifier formals_classifier(this, &duplicate_finder);
+
+ if (is_generator) PrepareGeneratorVariables(&function_state);
+
+ Expect(Token::LPAREN, CHECK_OK);
+ int start_position = scanner()->location().beg_pos;
+ this->scope()->set_start_position(start_position);
+ ParserFormalParameters formals(scope);
+ ParseFormalParameterList(&formals, CHECK_OK);
+ arity = formals.Arity();
+ Expect(Token::RPAREN, CHECK_OK);
+ int formals_end_position = scanner()->location().end_pos;
+
+ CheckArityRestrictions(arity, kind, formals.has_rest, start_position,
+ formals_end_position, CHECK_OK);
+ Expect(Token::LBRACE, CHECK_OK);
+ // Don't include the rest parameter into the function's formal parameter
+ // count (esp. the SharedFunctionInfo::internal_formal_parameter_count,
+ // which says whether we need to create an arguments adaptor frame).
+ if (formals.has_rest) arity--;
+
{
// Temporary zones can nest. When we migrate free variables (see below), we
// need to recreate them in the previous Zone.
@@ -4187,94 +2683,58 @@
// new temporary zone if the preconditions are satisfied, and ensures that
// the previous zone is always restored after parsing the body. To be able
// to do scope analysis correctly after full parsing, we migrate needed
- // information from scope into main_scope when the function has been parsed.
+ // information when the function is parsed.
Zone temp_zone(zone()->allocator());
DiscardableZoneScope zone_scope(this, &temp_zone, use_temp_zone);
-
- DeclarationScope* scope = NewFunctionScope(kind);
- SetLanguageMode(scope, language_mode);
- if (!use_temp_zone) {
- main_scope = scope;
- } else {
- DCHECK(main_scope->zone() != scope->zone());
- }
-
- FunctionState function_state(&function_state_, &scope_state_, scope, kind);
#ifdef DEBUG
- scope->SetScopeName(function_name);
+ if (use_temp_zone) scope->set_needs_migration();
#endif
- ExpressionClassifier formals_classifier(this, &duplicate_finder);
- 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. The machine code produced for generators (by full-codegen)
- // relies on this forced context allocation, but not in an essential way.
- this->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 =
- 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;
- this->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, kind, formals.has_rest, start_position,
- formals_end_position, CHECK_OK);
- Expect(Token::LBRACE, CHECK_OK);
- // Don't include the rest parameter into the function's formal parameter
- // count (esp. the SharedFunctionInfo::internal_formal_parameter_count,
- // which says whether we need to create an arguments adaptor frame).
- if (formals.has_rest) arity--;
-
- // 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);
+ // Eager or lazy parse? If is_lazy_top_level_function, we'll parse
+ // lazily. We'll call SkipLazyFunctionBody, which may decide to abort lazy
+ // parsing if it suspects that wasn't a good idea. If so (in which case the
+ // parser is expected to have backtracked), or if we didn't try to lazy
+ // parse in the first place, we'll have to parse eagerly.
+ if (is_lazy_top_level_function || is_lazy_inner_function) {
+ Scanner::BookmarkScope bookmark(scanner());
+ bookmark.Set();
+ LazyParsingResult result = SkipLazyFunctionBody(
+ &materialized_literal_count, &expected_property_count,
+ is_lazy_inner_function, is_lazy_top_level_function, CHECK_OK);
materialized_literal_count += formals.materialized_literals_count +
function_state.materialized_literal_count();
- if (bookmark.HasBeenReset()) {
+ if (result == kLazyParsingAborted) {
+ DCHECK(is_lazy_top_level_function);
+ bookmark.Apply();
// Trigger eager (re-)parsing, just below this block.
- is_lazily_parsed = false;
+ is_lazy_top_level_function = 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;
+ scope->ResetAfterPreparsing(ast_value_factory(), true);
+ zone_scope.Reset();
+ use_temp_zone = false;
}
}
- if (!is_lazily_parsed) {
+
+ if (!is_lazy_top_level_function && !is_lazy_inner_function) {
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 = nullptr;
- }
+ }
+
+ if (use_temp_zone || is_lazy_top_level_function) {
+ // If the preconditions are correct the function body should never be
+ // accessed, but do this anyway for better behaviour if they're wrong.
+ body = nullptr;
+ scope->AnalyzePartially(&previous_zone_ast_node_factory);
}
// Parsing the body may change the language mode in our scope.
@@ -4286,13 +2746,13 @@
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);
+ ValidateFormalParameters(language_mode, allow_duplicate_parameters,
+ CHECK_OK);
if (is_strict(language_mode)) {
CheckStrictOctalLiteral(scope->start_position(), scope->end_position(),
CHECK_OK);
- CheckDecimalLiteralWithLeadingZero(use_counts_, scope->start_position(),
+ CheckDecimalLiteralWithLeadingZero(scope->start_position(),
scope->end_position());
}
CheckConflictingVarDeclarations(scope, CHECK_OK);
@@ -4302,12 +2762,7 @@
RewriteDestructuringAssignments();
}
has_duplicate_parameters =
- !formals_classifier.is_valid_formal_parameter_list_without_duplicates();
-
- if (use_temp_zone) {
- DCHECK(main_scope != scope);
- scope->AnalyzePartially(main_scope, &previous_zone_ast_node_factory);
- }
+ !classifier()->is_valid_formal_parameter_list_without_duplicates();
} // DiscardableZoneScope goes out of scope.
FunctionLiteral::ParameterFlag duplicate_parameters =
@@ -4316,53 +2771,31 @@
// Note that the FunctionLiteral needs to be created in the main Zone again.
FunctionLiteral* function_literal = factory()->NewFunctionLiteral(
- function_name, main_scope, body, materialized_literal_count,
+ function_name, scope, body, materialized_literal_count,
expected_property_count, arity, duplicate_parameters, function_type,
- eager_compile_hint, kind, pos);
+ eager_compile_hint, 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 (fni_ != NULL && should_infer_name) fni_->AddFunction(function_literal);
+ if (should_infer_name) {
+ DCHECK_NOT_NULL(fni_);
+ fni_->AddFunction(function_literal);
+ }
return function_literal;
}
-Expression* Parser::ParseAsyncFunctionExpression(bool* ok) {
- // AsyncFunctionDeclaration ::
- // async [no LineTerminator here] function ( FormalParameters[Await] )
- // { AsyncFunctionBody }
- //
- // async [no LineTerminator here] function BindingIdentifier[Await]
- // ( FormalParameters[Await] ) { AsyncFunctionBody }
- DCHECK_EQ(scanner()->current_token(), Token::ASYNC);
- int pos = position();
- Expect(Token::FUNCTION, CHECK_OK);
- bool is_strict_reserved = false;
- const AstRawString* name = nullptr;
- FunctionLiteral::FunctionType type = FunctionLiteral::kAnonymousExpression;
-
- if (peek_any_identifier()) {
- type = FunctionLiteral::kNamedExpression;
- name = ParseIdentifierOrStrictReservedWord(FunctionKind::kAsyncFunction,
- &is_strict_reserved, CHECK_OK);
- }
- return ParseFunctionLiteral(name, scanner()->location(),
- is_strict_reserved ? kFunctionNameIsStrictReserved
- : kFunctionNameValidityUnknown,
- FunctionKind::kAsyncFunction, pos, type,
- language_mode(), CHECK_OK);
-}
-
-void Parser::SkipLazyFunctionBody(int* materialized_literal_count,
- int* expected_property_count, bool* ok,
- Scanner::BookmarkScope* bookmark) {
- DCHECK_IMPLIES(bookmark, bookmark->HasBeenSet());
+Parser::LazyParsingResult Parser::SkipLazyFunctionBody(
+ int* materialized_literal_count, int* expected_property_count,
+ bool is_inner_function, bool may_abort, bool* ok) {
if (produce_cached_parse_data()) CHECK(log_);
int function_block_pos = position();
- DeclarationScope* scope = this->scope()->AsDeclarationScope();
+ DeclarationScope* scope = function_state_->scope();
DCHECK(scope->is_function_scope());
- if (consume_cached_parse_data() && !cached_parse_data_->rejected()) {
+ // Inner functions are not part of the cached data.
+ if (!is_inner_function && 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 =
@@ -4374,14 +2807,14 @@
scanner()->SeekForward(entry.end_pos() - 1);
scope->set_end_position(entry.end_pos());
- Expect(Token::RBRACE, CHECK_OK_VOID);
+ Expect(Token::RBRACE, CHECK_OK_VALUE(kLazyParsingComplete));
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;
+ return kLazyParsingComplete;
}
cached_parse_data_->Reject();
}
@@ -4389,32 +2822,32 @@
// 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.
- }
+ ParseLazyFunctionBodyWithPreParser(&logger, is_inner_function, may_abort);
+
+ // Return immediately if pre-parser decided to abort parsing.
+ if (result == PreParser::kPreParseAbort) return kLazyParsingAborted;
if (result == PreParser::kPreParseStackOverflow) {
// Propagate stack overflow.
set_stack_overflow();
*ok = false;
- return;
+ return kLazyParsingComplete;
}
if (logger.has_error()) {
ReportMessageAt(Scanner::Location(logger.start(), logger.end()),
logger.message(), logger.argument_opt(),
logger.error_type());
*ok = false;
- return;
+ return kLazyParsingComplete;
}
scope->set_end_position(logger.end());
- Expect(Token::RBRACE, CHECK_OK_VOID);
+ Expect(Token::RBRACE, CHECK_OK_VALUE(kLazyParsingComplete));
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()) {
+ if (!is_inner_function && produce_cached_parse_data()) {
DCHECK(log_);
// Position right after terminal '}'.
int body_end = scanner()->location().end_pos;
@@ -4422,6 +2855,7 @@
*expected_property_count, language_mode(),
scope->uses_super_property(), scope->calls_eval());
}
+ return kLazyParsingComplete;
}
@@ -4438,9 +2872,9 @@
Token::EQ_STRICT, factory()->NewVariableProxy(var),
factory()->NewNullLiteral(kNoSourcePosition), kNoSourcePosition),
kNoSourcePosition);
- Expression* throw_type_error = this->NewThrowTypeError(
- MessageTemplate::kNonCoercible, ast_value_factory()->empty_string(),
- kNoSourcePosition);
+ Expression* throw_type_error =
+ NewThrowTypeError(MessageTemplate::kNonCoercible,
+ ast_value_factory()->empty_string(), kNoSourcePosition);
IfStatement* if_statement = factory()->NewIfStatement(
condition,
factory()->NewExpressionStatement(throw_type_error, kNoSourcePosition),
@@ -4495,7 +2929,6 @@
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;
@@ -4544,8 +2977,8 @@
BlockState block_state(&scope_state_, param_scope);
DeclarationParsingResult::Declaration decl(
parameter.pattern, parameter.initializer_end_position, initial_value);
- PatternRewriter::DeclareAndInitializeVariables(param_block, &descriptor,
- &decl, nullptr, CHECK_OK);
+ PatternRewriter::DeclareAndInitializeVariables(
+ this, param_block, &descriptor, &decl, nullptr, CHECK_OK);
if (param_block != init_block) {
param_scope = block_state.FinalizedBlockScope();
@@ -4558,28 +2991,74 @@
return init_block;
}
-Block* Parser::BuildRejectPromiseOnException(Block* block) {
- // try { <block> } catch (error) { return Promise.reject(error); }
- Block* try_block = block;
+Block* Parser::BuildRejectPromiseOnException(Block* inner_block, bool* ok) {
+ // .promise = %AsyncFunctionPromiseCreate();
+ // try {
+ // <inner_block>
+ // } catch (.catch) {
+ // %RejectPromise(.promise, .catch);
+ // return .promise;
+ // } finally {
+ // %AsyncFunctionPromiseRelease(.promise);
+ // }
+ Block* result = factory()->NewBlock(nullptr, 2, true, kNoSourcePosition);
+
+ // .promise = %AsyncFunctionPromiseCreate();
+ Statement* set_promise;
+ {
+ Expression* create_promise = factory()->NewCallRuntime(
+ Context::ASYNC_FUNCTION_PROMISE_CREATE_INDEX,
+ new (zone()) ZoneList<Expression*>(0, zone()), kNoSourcePosition);
+ Assignment* assign_promise = factory()->NewAssignment(
+ Token::INIT, factory()->NewVariableProxy(PromiseVariable()),
+ create_promise, kNoSourcePosition);
+ set_promise =
+ factory()->NewExpressionStatement(assign_promise, kNoSourcePosition);
+ }
+ result->statements()->Add(set_promise, zone());
+
+ // catch (.catch) { return %RejectPromise(.promise, .catch), .promise }
Scope* catch_scope = NewScope(CATCH_SCOPE);
catch_scope->set_is_hidden();
Variable* catch_variable =
catch_scope->DeclareLocal(ast_value_factory()->dot_catch_string(), VAR,
- kCreatedInitialized, Variable::NORMAL);
+ kCreatedInitialized, NORMAL_VARIABLE);
Block* catch_block = factory()->NewBlock(nullptr, 1, true, kNoSourcePosition);
- Expression* promise_reject = BuildPromiseReject(
+ Expression* promise_reject = BuildRejectPromise(
factory()->NewVariableProxy(catch_variable), kNoSourcePosition);
-
ReturnStatement* return_promise_reject =
factory()->NewReturnStatement(promise_reject, kNoSourcePosition);
catch_block->statements()->Add(return_promise_reject, zone());
- TryStatement* try_catch_statement = factory()->NewTryCatchStatement(
- try_block, catch_scope, catch_variable, catch_block, kNoSourcePosition);
- block = factory()->NewBlock(nullptr, 1, true, kNoSourcePosition);
- block->statements()->Add(try_catch_statement, zone());
- return block;
+ TryStatement* try_catch_statement =
+ factory()->NewTryCatchStatementForAsyncAwait(inner_block, catch_scope,
+ catch_variable, catch_block,
+ kNoSourcePosition);
+
+ // There is no TryCatchFinally node, so wrap it in an outer try/finally
+ Block* outer_try_block =
+ factory()->NewBlock(nullptr, 1, true, kNoSourcePosition);
+ outer_try_block->statements()->Add(try_catch_statement, zone());
+
+ // finally { %AsyncFunctionPromiseRelease(.promise) }
+ Block* finally_block =
+ factory()->NewBlock(nullptr, 1, true, kNoSourcePosition);
+ {
+ ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(1, zone());
+ args->Add(factory()->NewVariableProxy(PromiseVariable()), zone());
+ Expression* call_promise_release = factory()->NewCallRuntime(
+ Context::ASYNC_FUNCTION_PROMISE_RELEASE_INDEX, args, kNoSourcePosition);
+ Statement* promise_release = factory()->NewExpressionStatement(
+ call_promise_release, kNoSourcePosition);
+ finally_block->statements()->Add(promise_release, zone());
+ }
+
+ Statement* try_finally_statement = factory()->NewTryFinallyStatement(
+ outer_try_block, finally_block, kNoSourcePosition);
+
+ result->statements()->Add(try_finally_statement, zone());
+ return result;
}
Expression* Parser::BuildCreateJSGeneratorObject(int pos, FunctionKind kind) {
@@ -4593,26 +3072,68 @@
pos);
}
-Expression* Parser::BuildPromiseResolve(Expression* value, int pos) {
- ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(1, zone());
+Expression* Parser::BuildResolvePromise(Expression* value, int pos) {
+ // %ResolvePromise(.promise, value), .promise
+ ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(2, zone());
+ args->Add(factory()->NewVariableProxy(PromiseVariable()), zone());
args->Add(value, zone());
- return factory()->NewCallRuntime(Context::PROMISE_CREATE_RESOLVED_INDEX, args,
- pos);
+ Expression* call_runtime =
+ factory()->NewCallRuntime(Context::PROMISE_RESOLVE_INDEX, args, pos);
+ return factory()->NewBinaryOperation(
+ Token::COMMA, call_runtime,
+ factory()->NewVariableProxy(PromiseVariable()), pos);
}
-Expression* Parser::BuildPromiseReject(Expression* value, int pos) {
- ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(1, zone());
+Expression* Parser::BuildRejectPromise(Expression* value, int pos) {
+ // %RejectPromiseNoDebugEvent(.promise, value, true), .promise
+ // The NoDebugEvent variant disables the additional debug event for the
+ // rejection since a debug event already happened for the exception that got
+ // us here.
+ ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(2, zone());
+ args->Add(factory()->NewVariableProxy(PromiseVariable()), zone());
args->Add(value, zone());
- return factory()->NewCallRuntime(Context::PROMISE_CREATE_REJECTED_INDEX, args,
- pos);
+ Expression* call_runtime = factory()->NewCallRuntime(
+ Context::REJECT_PROMISE_NO_DEBUG_EVENT_INDEX, args, pos);
+ return factory()->NewBinaryOperation(
+ Token::COMMA, call_runtime,
+ factory()->NewVariableProxy(PromiseVariable()), pos);
+}
+
+Variable* Parser::PromiseVariable() {
+ // Based on the various compilation paths, there are many different code
+ // paths which may be the first to access the Promise temporary. Whichever
+ // comes first should create it and stash it in the FunctionState.
+ Variable* promise = function_state_->promise_variable();
+ if (function_state_->promise_variable() == nullptr) {
+ promise = scope()->NewTemporary(ast_value_factory()->empty_string());
+ function_state_->set_promise_variable(promise);
+ }
+ return promise;
+}
+
+Expression* Parser::BuildInitialYield(int pos, FunctionKind kind) {
+ Expression* allocation = BuildCreateJSGeneratorObject(pos, kind);
+ VariableProxy* init_proxy =
+ factory()->NewVariableProxy(function_state_->generator_object_variable());
+ Assignment* assignment = factory()->NewAssignment(
+ Token::INIT, init_proxy, allocation, kNoSourcePosition);
+ VariableProxy* get_proxy =
+ factory()->NewVariableProxy(function_state_->generator_object_variable());
+ // The position of the yield is important for reporting the exception
+ // caused by calling the .throw method on a generator suspended at the
+ // initial yield (i.e. right after generator instantiation).
+ return factory()->NewYield(get_proxy, assignment, scope()->start_position(),
+ Yield::kOnExceptionThrow);
}
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).
+ // Everything inside an eagerly parsed function will be parsed eagerly (see
+ // comment above). Lazy inner functions are handled separately and they won't
+ // require the mode to be PARSE_LAZILY (see ParseFunctionLiteral).
+ // TODO(marja): Refactor parsing modes: remove this.
ParsingModeScope parsing_mode(this, PARSE_EAGERLY);
ZoneList<Statement*>* result = new(zone()) ZoneList<Statement*>(8, zone());
@@ -4657,26 +3178,10 @@
Block* try_block =
factory()->NewBlock(nullptr, 3, false, kNoSourcePosition);
-
- {
- Expression* allocation = BuildCreateJSGeneratorObject(pos, kind);
- VariableProxy* init_proxy = factory()->NewVariableProxy(
- function_state_->generator_object_variable());
- Assignment* assignment = factory()->NewAssignment(
- Token::INIT, init_proxy, allocation, kNoSourcePosition);
- VariableProxy* get_proxy = factory()->NewVariableProxy(
- function_state_->generator_object_variable());
- // The position of the yield is important for reporting the exception
- // caused by calling the .throw method on a generator suspended at the
- // initial yield (i.e. right after generator instantiation).
- Yield* yield = factory()->NewYield(get_proxy, assignment,
- scope()->start_position(),
- Yield::kOnExceptionThrow);
- try_block->statements()->Add(
- factory()->NewExpressionStatement(yield, kNoSourcePosition),
- zone());
- }
-
+ Expression* initial_yield = BuildInitialYield(pos, kind);
+ try_block->statements()->Add(
+ factory()->NewExpressionStatement(initial_yield, kNoSourcePosition),
+ zone());
ParseStatementList(try_block->statements(), Token::RBRACE, CHECK_OK);
Statement* final_return = factory()->NewReturnStatement(
@@ -4700,16 +3205,15 @@
zone());
} else if (IsAsyncFunction(kind)) {
const bool accept_IN = true;
- DesugarAsyncFunctionBody(function_name, inner_scope, body, nullptr, kind,
- FunctionBodyType::kNormal, accept_IN, pos,
- CHECK_OK);
+ ParseAsyncFunctionBody(inner_scope, body, kind, FunctionBodyType::kNormal,
+ accept_IN, pos, CHECK_OK);
} else {
ParseStatementList(body, Token::RBRACE, CHECK_OK);
}
if (IsSubclassConstructor(kind)) {
- body->Add(factory()->NewReturnStatement(
- this->ThisExpression(kNoSourcePosition), kNoSourcePosition),
+ body->Add(factory()->NewReturnStatement(ThisExpression(kNoSourcePosition),
+ kNoSourcePosition),
zone());
}
}
@@ -4726,12 +3230,12 @@
Block* init_block = BuildParameterInitializationBlock(parameters, CHECK_OK);
if (is_sloppy(inner_scope->language_mode())) {
- InsertSloppyBlockFunctionVarBindings(inner_scope, function_scope,
- CHECK_OK);
+ InsertSloppyBlockFunctionVarBindings(inner_scope);
}
+ // TODO(littledan): Merge the two rejection blocks into one
if (IsAsyncFunction(kind)) {
- init_block = BuildRejectPromiseOnException(init_block);
+ init_block = BuildRejectPromiseOnException(init_block, CHECK_OK);
}
DCHECK_NOT_NULL(init_block);
@@ -4748,31 +3252,42 @@
} else {
DCHECK_EQ(inner_scope, function_scope);
if (is_sloppy(function_scope->language_mode())) {
- InsertSloppyBlockFunctionVarBindings(function_scope, nullptr, CHECK_OK);
+ InsertSloppyBlockFunctionVarBindings(function_scope);
}
}
+ if (!IsArrowFunction(kind)) {
+ // Declare arguments after parsing the function since lexical 'arguments'
+ // masks the arguments object. Declare arguments before declaring the
+ // function var since the arguments object masks 'function arguments'.
+ function_scope->DeclareArguments(ast_value_factory());
+ }
+
if (function_type == FunctionLiteral::kNamedExpression) {
- // Now that we know the language mode, we can create the const assignment
- // in the previously reserved spot.
- DCHECK_EQ(function_scope, scope());
- Variable* fvar = function_scope->DeclareFunctionVar(function_name);
- VariableProxy* fproxy = factory()->NewVariableProxy(fvar);
- result->Set(kFunctionNameAssignmentIndex,
- factory()->NewExpressionStatement(
- factory()->NewAssignment(Token::INIT, fproxy,
- factory()->NewThisFunction(pos),
- kNoSourcePosition),
- kNoSourcePosition));
+ Statement* statement;
+ if (function_scope->LookupLocal(function_name) == nullptr) {
+ // Now that we know the language mode, we can create the const assignment
+ // in the previously reserved spot.
+ DCHECK_EQ(function_scope, scope());
+ Variable* fvar = function_scope->DeclareFunctionVar(function_name);
+ VariableProxy* fproxy = factory()->NewVariableProxy(fvar);
+ statement = factory()->NewExpressionStatement(
+ factory()->NewAssignment(Token::INIT, fproxy,
+ factory()->NewThisFunction(pos),
+ kNoSourcePosition),
+ kNoSourcePosition);
+ } else {
+ statement = factory()->NewEmptyStatement(kNoSourcePosition);
+ }
+ result->Set(kFunctionNameAssignmentIndex, statement);
}
MarkCollectedTailCallExpressions();
return result;
}
-
PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser(
- SingletonLogger* logger, Scanner::BookmarkScope* bookmark) {
+ SingletonLogger* logger, bool is_inner_function, bool may_abort) {
// This function may be called on a background thread too; record only the
// main thread preparse times.
if (pre_parse_timer_ != NULL) {
@@ -4794,209 +3309,337 @@
SET_ALLOW(harmony_restrictive_declarations);
SET_ALLOW(harmony_async_await);
SET_ALLOW(harmony_trailing_commas);
+ SET_ALLOW(harmony_class_fields);
#undef SET_ALLOW
}
+ // Aborting inner function preparsing would leave scopes in an inconsistent
+ // state; we don't parse inner functions in the abortable mode anyway.
+ DCHECK(!is_inner_function || !may_abort);
+
+ DeclarationScope* function_scope = function_state_->scope();
PreParser::PreParseResult result = reusable_preparser_->PreParseLazyFunction(
- language_mode(), function_state_->kind(),
- scope()->AsDeclarationScope()->has_simple_parameters(), parsing_module_,
- logger, bookmark, use_counts_);
+ function_scope, parsing_module_, logger, is_inner_function, may_abort,
+ use_counts_);
if (pre_parse_timer_ != NULL) {
pre_parse_timer_->Stop();
}
return result;
}
-Expression* Parser::ParseClassLiteral(ExpressionClassifier* classifier,
- 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 nullptr;
- }
- if (IsEvalOrArguments(name)) {
- ReportMessageAt(class_name_location, MessageTemplate::kStrictEvalArguments);
- *ok = false;
- return nullptr;
- }
+Expression* Parser::InstallHomeObject(Expression* function_literal,
+ Expression* home_object) {
+ Block* do_block = factory()->NewBlock(nullptr, 1, false, kNoSourcePosition);
+ Variable* result_var =
+ scope()->NewTemporary(ast_value_factory()->empty_string());
+ DoExpression* do_expr =
+ factory()->NewDoExpression(do_block, result_var, kNoSourcePosition);
+ Assignment* init = factory()->NewAssignment(
+ Token::ASSIGN, factory()->NewVariableProxy(result_var), function_literal,
+ kNoSourcePosition);
+ do_block->statements()->Add(
+ factory()->NewExpressionStatement(init, kNoSourcePosition), zone());
+ Property* home_object_property = factory()->NewProperty(
+ factory()->NewVariableProxy(result_var),
+ factory()->NewSymbolLiteral("home_object_symbol", kNoSourcePosition),
+ kNoSourcePosition);
+ Assignment* assignment = factory()->NewAssignment(
+ Token::ASSIGN, home_object_property, home_object, kNoSourcePosition);
+ do_block->statements()->Add(
+ factory()->NewExpressionStatement(assignment, kNoSourcePosition), zone());
+ return do_expr;
+}
- BlockState block_state(&scope_state_);
+const AstRawString* ClassFieldVariableName(bool is_name,
+ AstValueFactory* ast_value_factory,
+ int index) {
+ std::string name =
+ ".class-field-" + std::to_string(index) + (is_name ? "-name" : "-func");
+ return ast_value_factory->GetOneByteString(name.c_str());
+}
+
+FunctionLiteral* Parser::SynthesizeClassFieldInitializer(int count) {
+ DCHECK(count > 0);
+ // Makes a function which reads the names and initializers for each class
+ // field out of deterministically named local variables and sets each property
+ // to the result of evaluating its corresponding initializer in turn.
+
+ // This produces a function which looks like
+ // function () {
+ // this[.class-field-0-name] = .class-field-0-func();
+ // this[.class-field-1-name] = .class-field-1-func();
+ // [...]
+ // this[.class-field-n-name] = .class-field-n-func();
+ // return this;
+ // }
+ // except that it performs defineProperty, so that instead of '=' it has
+ // %DefineDataPropertyInLiteral(this, .class-field-0-name,
+ // .class-field-0-func(),
+ // DONT_ENUM, false)
+
RaiseLanguageMode(STRICT);
+ FunctionKind kind = FunctionKind::kConciseMethod;
+ DeclarationScope* initializer_scope = NewFunctionScope(kind);
+ SetLanguageMode(initializer_scope, language_mode());
+ initializer_scope->set_start_position(scanner()->location().end_pos);
+ initializer_scope->set_end_position(scanner()->location().end_pos);
+ FunctionState initializer_state(&function_state_, &scope_state_,
+ initializer_scope);
+ ZoneList<Statement*>* body = new (zone()) ZoneList<Statement*>(count, zone());
+ for (int i = 0; i < count; ++i) {
+ const AstRawString* name =
+ ClassFieldVariableName(true, ast_value_factory(), i);
+ VariableProxy* name_proxy = scope()->NewUnresolved(factory(), name);
+ const AstRawString* function_name =
+ ClassFieldVariableName(false, ast_value_factory(), i);
+ VariableProxy* function_proxy =
+ scope()->NewUnresolved(factory(), function_name);
+ ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(2, zone());
+ args->Add(function_proxy, zone());
+ args->Add(ThisExpression(kNoSourcePosition), zone());
+ Expression* call = factory()->NewCallRuntime(Runtime::kInlineCall, args,
+ kNoSourcePosition);
+ ZoneList<Expression*>* define_property_args =
+ new (zone()) ZoneList<Expression*>(5, zone());
+ define_property_args->Add(ThisExpression(kNoSourcePosition), zone());
+ define_property_args->Add(name_proxy, zone());
+ define_property_args->Add(call, zone());
+ define_property_args->Add(
+ factory()->NewNumberLiteral(DONT_ENUM, kNoSourcePosition), zone());
+ define_property_args->Add(
+ factory()->NewNumberLiteral(
+ false, // TODO(bakkot) function name inference a la class { x =
+ // function(){}; static y = function(){}; }
+ kNoSourcePosition),
+ zone());
+ body->Add(factory()->NewExpressionStatement(
+ factory()->NewCallRuntime(
+ Runtime::kDefineDataProperty,
+ define_property_args, // TODO(bakkot) verify that this is
+ // the same as object_define_property
+ kNoSourcePosition),
+ kNoSourcePosition),
+ zone());
+ }
+ body->Add(factory()->NewReturnStatement(ThisExpression(kNoSourcePosition),
+ kNoSourcePosition),
+ zone());
+ FunctionLiteral* function_literal = factory()->NewFunctionLiteral(
+ ast_value_factory()->empty_string(), initializer_scope, body,
+ initializer_state.materialized_literal_count(),
+ initializer_state.expected_property_count(), 0,
+ FunctionLiteral::kNoDuplicateParameters,
+ FunctionLiteral::kAnonymousExpression,
+ FunctionLiteral::kShouldLazyCompile, initializer_scope->start_position());
+ function_literal->set_is_class_field_initializer(true);
+ function_literal->scope()->set_arity(count);
+ return function_literal;
+}
+
+FunctionLiteral* Parser::InsertClassFieldInitializer(
+ FunctionLiteral* constructor) {
+ Statement* call_initializer = factory()->NewExpressionStatement(
+ CallClassFieldInitializer(
+ constructor->scope(),
+ constructor->scope()->NewUnresolved(
+ factory(), ast_value_factory()->this_string(), kNoSourcePosition,
+ kNoSourcePosition + 4, THIS_VARIABLE)),
+ kNoSourcePosition);
+ constructor->body()->InsertAt(0, call_initializer, zone());
+ return constructor;
+}
+
+// If a class name is specified, this method declares the class variable
+// and sets class_info->proxy to point to that name.
+void Parser::DeclareClassVariable(const AstRawString* name, Scope* block_scope,
+ ClassInfo* class_info, int class_token_pos,
+ bool* ok) {
#ifdef DEBUG
scope()->SetScopeName(name);
#endif
- VariableProxy* proxy = nullptr;
if (name != nullptr) {
- proxy = NewUnresolved(name);
- // TODO(verwaest): declare via block_state.
- Declaration* declaration =
- factory()->NewVariableDeclaration(proxy, block_state.scope(), pos);
+ class_info->proxy = factory()->NewVariableProxy(name, NORMAL_VARIABLE);
+ Declaration* declaration = factory()->NewVariableDeclaration(
+ class_info->proxy, block_scope, class_token_pos);
Declare(declaration, DeclarationDescriptor::NORMAL, CONST,
- DefaultInitializationFlag(CONST), CHECK_OK);
+ Variable::DefaultInitializationFlag(CONST), ok);
+ }
+}
+
+// This method declares a property of the given class. It updates the
+// following fields of class_info, as appropriate:
+// - constructor
+// - static_initializer_var
+// - instance_field_initializers
+// - properties
+void Parser::DeclareClassProperty(const AstRawString* class_name,
+ ClassLiteralProperty* property,
+ ClassInfo* class_info, bool* ok) {
+ if (class_info->has_seen_constructor && class_info->constructor == nullptr) {
+ class_info->constructor = GetPropertyValue(property)->AsFunctionLiteral();
+ DCHECK_NOT_NULL(class_info->constructor);
+ class_info->constructor->set_raw_name(
+ class_name != nullptr ? class_name
+ : ast_value_factory()->empty_string());
+ return;
}
- Expression* extends = nullptr;
- if (Check(Token::EXTENDS)) {
- block_state.set_start_position(scanner()->location().end_pos);
- ExpressionClassifier extends_classifier(this);
- extends = ParseLeftHandSideExpression(&extends_classifier, CHECK_OK);
- CheckNoTailCallExpressions(&extends_classifier, CHECK_OK);
- RewriteNonPattern(&extends_classifier, CHECK_OK);
- if (classifier != nullptr) {
- classifier->Accumulate(&extends_classifier,
- ExpressionClassifier::ExpressionProductions);
- }
- } else {
- block_state.set_start_position(scanner()->location().end_pos);
- }
-
-
- ClassLiteralChecker checker(this);
- ZoneList<ObjectLiteral::Property*>* properties = NewPropertyList(4, zone());
- FunctionLiteral* constructor = nullptr;
- 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;
- bool is_computed_name = false; // Classes do not care about computed
- // property names here.
- ExpressionClassifier property_classifier(this);
- const AstRawString* property_name = nullptr;
- ObjectLiteral::Property* property = ParsePropertyDefinition(
- &checker, in_class, has_extends, MethodKind::kNormal, &is_computed_name,
- &has_seen_constructor, &property_classifier, &property_name, CHECK_OK);
- RewriteNonPattern(&property_classifier, CHECK_OK);
- if (classifier != nullptr) {
- classifier->Accumulate(&property_classifier,
- ExpressionClassifier::ExpressionProductions);
- }
-
- if (has_seen_constructor && constructor == nullptr) {
- constructor = GetPropertyValue(property)->AsFunctionLiteral();
- DCHECK_NOT_NULL(constructor);
- constructor->set_raw_name(
- name != nullptr ? name : ast_value_factory()->empty_string());
+ if (property->kind() == ClassLiteralProperty::FIELD) {
+ DCHECK(allow_harmony_class_fields());
+ if (property->is_static()) {
+ if (class_info->static_initializer_var == nullptr) {
+ class_info->static_initializer_var =
+ NewTemporary(ast_value_factory()->empty_string());
+ }
+ // TODO(bakkot) only do this conditionally
+ Expression* function = InstallHomeObject(
+ property->value(),
+ factory()->NewVariableProxy(class_info->static_initializer_var));
+ ZoneList<Expression*>* args =
+ new (zone()) ZoneList<Expression*>(2, zone());
+ args->Add(function, zone());
+ args->Add(factory()->NewVariableProxy(class_info->static_initializer_var),
+ zone());
+ Expression* call = factory()->NewCallRuntime(Runtime::kInlineCall, args,
+ kNoSourcePosition);
+ property->set_value(call);
} else {
- properties->Add(property, zone());
- }
-
- if (fni_ != nullptr) fni_->Infer();
-
- if (property_name != ast_value_factory()->constructor_string()) {
- SetFunctionNameFromPropertyName(property, property_name);
+ // if (is_computed_name) { // TODO(bakkot) figure out why this is
+ // necessary for non-computed names in full-codegen
+ ZoneList<Expression*>* to_name_args =
+ new (zone()) ZoneList<Expression*>(1, zone());
+ to_name_args->Add(property->key(), zone());
+ property->set_key(factory()->NewCallRuntime(
+ Runtime::kToName, to_name_args, kNoSourcePosition));
+ //}
+ const AstRawString* name = ClassFieldVariableName(
+ true, ast_value_factory(),
+ class_info->instance_field_initializers->length());
+ VariableProxy* name_proxy =
+ factory()->NewVariableProxy(name, NORMAL_VARIABLE);
+ Declaration* name_declaration = factory()->NewVariableDeclaration(
+ name_proxy, scope(), kNoSourcePosition);
+ Variable* name_var =
+ Declare(name_declaration, DeclarationDescriptor::NORMAL, CONST,
+ kNeedsInitialization, ok, scope());
+ DCHECK(*ok);
+ if (!*ok) return;
+ class_info->instance_field_initializers->Add(property->value(), zone());
+ property->set_value(factory()->NewVariableProxy(name_var));
}
}
+ class_info->properties->Add(property, zone());
+}
- Expect(Token::RBRACE, CHECK_OK);
+// This method rewrites a class literal into a do-expression.
+// It uses the following fields of class_info:
+// - constructor (if missing, it updates it with a default constructor)
+// - proxy
+// - extends
+// - static_initializer_var
+// - instance_field_initializers
+// - properties
+Expression* Parser::RewriteClassLiteral(const AstRawString* name,
+ ClassInfo* class_info, int pos,
+ bool* ok) {
int end_pos = scanner()->location().end_pos;
-
- if (constructor == nullptr) {
- constructor = DefaultConstructor(name, has_extends, pos, end_pos,
- block_state.language_mode());
- }
-
- // Note that we do not finalize this block scope because it is
- // used as a sentinel value indicating an anonymous class.
- block_state.set_end_position(end_pos);
-
- if (name != nullptr) {
- DCHECK_NOT_NULL(proxy);
- proxy->var()->set_initializer_position(end_pos);
- }
-
Block* do_block = factory()->NewBlock(nullptr, 1, false, pos);
Variable* result_var = NewTemporary(ast_value_factory()->empty_string());
- do_block->set_scope(block_state.FinalizedBlockScope());
DoExpression* do_expr = factory()->NewDoExpression(do_block, result_var, pos);
+ bool has_extends = class_info->extends != nullptr;
+ bool has_instance_fields =
+ class_info->instance_field_initializers->length() > 0;
+ DCHECK(!has_instance_fields || allow_harmony_class_fields());
+ bool has_default_constructor = class_info->constructor == nullptr;
+ if (has_default_constructor) {
+ class_info->constructor =
+ DefaultConstructor(name, has_extends, has_instance_fields, pos, end_pos,
+ scope()->language_mode());
+ }
+
+ if (has_instance_fields && !has_extends) {
+ class_info->constructor =
+ InsertClassFieldInitializer(class_info->constructor);
+ class_info->constructor->set_requires_class_field_init(true);
+ } // The derived case is handled by rewriting super calls.
+
+ scope()->set_end_position(end_pos);
+
+ if (name != nullptr) {
+ DCHECK_NOT_NULL(class_info->proxy);
+ class_info->proxy->var()->set_initializer_position(end_pos);
+ }
+
ClassLiteral* class_literal = factory()->NewClassLiteral(
- proxy, extends, constructor, properties, pos, end_pos);
+ class_info->proxy, class_info->extends, class_info->constructor,
+ class_info->properties, pos, end_pos);
+
+ if (class_info->static_initializer_var != nullptr) {
+ class_literal->set_static_initializer_proxy(
+ factory()->NewVariableProxy(class_info->static_initializer_var));
+ }
do_block->statements()->Add(
- factory()->NewExpressionStatement(class_literal, pos), zone());
- do_expr->set_represented_function(constructor);
- Rewriter::Rewrite(this, GetClosureScope(), do_expr, ast_value_factory());
+ factory()->NewExpressionStatement(
+ factory()->NewAssignment(Token::ASSIGN,
+ factory()->NewVariableProxy(result_var),
+ class_literal, kNoSourcePosition),
+ pos),
+ zone());
+ if (allow_harmony_class_fields() &&
+ (has_instance_fields || (has_extends && !has_default_constructor))) {
+ // Default constructors for derived classes without fields will not try to
+ // read this variable, so there's no need to create it.
+ const AstRawString* init_fn_name =
+ ast_value_factory()->dot_class_field_init_string();
+ Variable* init_fn_var = scope()->DeclareLocal(
+ init_fn_name, CONST, kCreatedInitialized, NORMAL_VARIABLE);
+ Expression* initializer =
+ has_instance_fields
+ ? static_cast<Expression*>(SynthesizeClassFieldInitializer(
+ class_info->instance_field_initializers->length()))
+ : factory()->NewBooleanLiteral(false, kNoSourcePosition);
+ Assignment* assignment = factory()->NewAssignment(
+ Token::INIT, factory()->NewVariableProxy(init_fn_var), initializer,
+ kNoSourcePosition);
+ do_block->statements()->Add(
+ factory()->NewExpressionStatement(assignment, kNoSourcePosition),
+ zone());
+ }
+ for (int i = 0; i < class_info->instance_field_initializers->length(); ++i) {
+ const AstRawString* function_name =
+ ClassFieldVariableName(false, ast_value_factory(), i);
+ VariableProxy* function_proxy =
+ factory()->NewVariableProxy(function_name, NORMAL_VARIABLE);
+ Declaration* function_declaration = factory()->NewVariableDeclaration(
+ function_proxy, scope(), kNoSourcePosition);
+ Variable* function_var =
+ Declare(function_declaration, DeclarationDescriptor::NORMAL, CONST,
+ kNeedsInitialization, ok, scope());
+ if (!*ok) return nullptr;
+ Property* prototype_property = factory()->NewProperty(
+ factory()->NewVariableProxy(result_var),
+ factory()->NewStringLiteral(ast_value_factory()->prototype_string(),
+ kNoSourcePosition),
+ kNoSourcePosition);
+ Expression* function_value = InstallHomeObject(
+ class_info->instance_field_initializers->at(i),
+ prototype_property); // TODO(bakkot) ideally this would be conditional,
+ // especially in trivial cases
+ Assignment* function_assignment = factory()->NewAssignment(
+ Token::INIT, factory()->NewVariableProxy(function_var), function_value,
+ kNoSourcePosition);
+ do_block->statements()->Add(factory()->NewExpressionStatement(
+ function_assignment, kNoSourcePosition),
+ zone());
+ }
+ do_block->set_scope(scope()->FinalizeBlockScope());
+ do_expr->set_represented_function(class_info->constructor);
return do_expr;
}
-
-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(this);
- ZoneList<Expression*>* args =
- ParseArguments(&spread_pos, &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.
- GetClosureScope()->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::kRuntimeWrongNumArgs);
- *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) {
- ReportMessage(MessageTemplate::kNotDefined, name);
- *ok = false;
- return NULL;
- }
-
- return factory()->NewCallRuntime(context_index, args, pos);
-}
-
-
Literal* Parser::GetLiteralUndefined(int position) {
return factory()->NewUndefinedLiteral(position);
}
@@ -5045,100 +3688,22 @@
}
}
-void Parser::InsertSloppyBlockFunctionVarBindings(DeclarationScope* scope,
- Scope* complex_params_scope,
- bool* ok) {
- // For each variable which is used as a function declaration in a sloppy
- // block,
- 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
- // or parameter,
-
- // Check if there's a conflict with a parameter.
- // This depends on the fact that functions always have a scope solely to
- // hold complex parameters, and the names local to that scope are
- // precisely the names of the parameters. IsDeclaredParameter(name) does
- // not hold for names declared by complex parameters, nor are those
- // bindings necessarily declared lexically, so we have to check for them
- // explicitly. On the other hand, if there are not complex parameters,
- // it is sufficient to just check IsDeclaredParameter.
- if (complex_params_scope != nullptr) {
- if (complex_params_scope->LookupLocal(name) != nullptr) {
- continue;
- }
- } else {
- if (scope->IsDeclaredParameter(name)) {
- continue;
- }
- }
-
- bool var_created = false;
-
- // Write in assignments to var for each block-scoped function declaration
- auto delegates = static_cast<SloppyBlockFunctionStatement*>(p->value);
-
- DeclarationScope* decl_scope = scope;
- while (decl_scope->is_eval_scope()) {
- decl_scope = decl_scope->outer_scope()->GetDeclarationScope();
- }
- Scope* outer_scope = decl_scope->outer_scope();
-
- for (SloppyBlockFunctionStatement* delegate = delegates;
- delegate != nullptr; delegate = delegate->next()) {
- // Check if there's a conflict with a lexical declaration
- Scope* query_scope = delegate->scope()->outer_scope();
- Variable* var = nullptr;
- bool should_hoist = true;
-
- // Note that we perform this loop for each delegate named 'name',
- // which may duplicate work if those delegates share scopes.
- // It is not sufficient to just do a Lookup on query_scope: for
- // example, that does not prevent hoisting of the function in
- // `{ let e; try {} catch (e) { function e(){} } }`
- do {
- var = query_scope->LookupLocal(name);
- if (var != nullptr && IsLexicalVariableMode(var->mode())) {
- should_hoist = false;
- break;
- }
- query_scope = query_scope->outer_scope();
- } while (query_scope != outer_scope);
-
- if (!should_hoist) continue;
-
- // Declare a var-style binding for the function in the outer scope
- if (!var_created) {
- var_created = true;
- VariableProxy* proxy = scope->NewUnresolved(factory(), name);
- Declaration* declaration =
- factory()->NewVariableDeclaration(proxy, scope, kNoSourcePosition);
- Declare(declaration, DeclarationDescriptor::NORMAL, VAR,
- DefaultInitializationFlag(VAR), ok, scope);
- DCHECK(ok); // Based on the preceding check, this should not fail
- if (!ok) return;
- }
-
- // 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, kNoSourcePosition);
- Statement* statement =
- factory()->NewExpressionStatement(assignment, kNoSourcePosition);
- delegate->set_statement(statement);
- }
+void Parser::InsertSloppyBlockFunctionVarBindings(DeclarationScope* scope) {
+ // For the outermost eval scope, we cannot hoist during parsing: let
+ // declarations in the surrounding scope may prevent hoisting, but the
+ // information is unaccessible during parsing. In this case, we hoist later in
+ // DeclarationScope::Analyze.
+ if (scope->is_eval_scope() && scope->outer_scope() == original_scope_) {
+ return;
}
+ scope->HoistSloppyBlockFunctions(factory());
}
-
// ----------------------------------------------------------------------------
// Parser support
bool Parser::TargetStackContainsLabel(const AstRawString* label) {
- for (Target* t = target_stack_; t != NULL; t = t->previous()) {
+ for (ParserTarget* t = target_stack_; t != NULL; t = t->previous()) {
if (ContainsLabel(t->statement()->labels(), label)) return true;
}
return false;
@@ -5148,7 +3713,7 @@
BreakableStatement* Parser::LookupBreakTarget(const AstRawString* label,
bool* ok) {
bool anonymous = label == NULL;
- for (Target* t = target_stack_; t != NULL; t = t->previous()) {
+ for (ParserTarget* t = target_stack_; t != NULL; t = t->previous()) {
BreakableStatement* stat = t->statement();
if ((anonymous && stat->is_target_for_anonymous()) ||
(!anonymous && ContainsLabel(stat->labels(), label))) {
@@ -5162,7 +3727,7 @@
IterationStatement* Parser::LookupContinueTarget(const AstRawString* label,
bool* ok) {
bool anonymous = label == NULL;
- for (Target* t = target_stack_; t != NULL; t = t->previous()) {
+ for (ParserTarget* t = target_stack_; t != NULL; t = t->previous()) {
IterationStatement* stat = t->statement()->AsIterationStatement();
if (stat == NULL) continue;
@@ -5188,7 +3753,7 @@
void Parser::Internalize(Isolate* isolate, Handle<Script> script, bool error) {
- // Internalize strings.
+ // Internalize strings and values.
ast_value_factory()->Internalize(isolate);
// Error processing.
@@ -5240,12 +3805,6 @@
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());
@@ -5261,7 +3820,6 @@
info->set_literal(result);
Internalize(isolate, info->script(), result == NULL);
- DCHECK(ast_value_factory()->IsInternalized());
return (result != NULL);
}
@@ -5282,11 +3840,11 @@
stream_ptr = info->character_stream();
} else {
DCHECK(info->character_stream() == nullptr);
- stream.reset(new ExternalStreamingStream(info->source_stream(),
- info->source_stream_encoding()));
+ stream.reset(ScannerStream::For(info->source_stream(),
+ info->source_stream_encoding()));
stream_ptr = stream.get();
}
- DCHECK(info->context().is_null() || info->context()->IsNativeContext());
+ DCHECK(info->maybe_outer_scope_info().is_null());
DCHECK(original_scope_);
@@ -5431,11 +3989,9 @@
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());
+ZoneList<Expression*>* Parser::PrepareSpreadArguments(
+ ZoneList<Expression*>* list) {
+ ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(1, zone());
if (list->length() == 1) {
// Spread-call with single spread argument produces an InternalArray
// containing the values from the array.
@@ -5462,8 +4018,8 @@
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());
+ ZoneList<Expression*>* unspread =
+ new (zone()) ZoneList<Expression*>(1, zone());
// Push array of unspread parameters
while (i < n && !list->at(i)->IsSpread()) {
@@ -5478,15 +4034,15 @@
}
// Push eagerly spread argument
- ZoneList<v8::internal::Expression*>* spread_list =
- new (zone()) ZoneList<v8::internal::Expression*>(1, zone());
+ ZoneList<Expression*>* spread_list =
+ new (zone()) ZoneList<Expression*>(1, zone());
spread_list->Add(list->at(i++)->AsSpread()->expression(), zone());
args->Add(factory()->NewCallRuntime(Context::SPREAD_ITERABLE_INDEX,
spread_list, kNoSourcePosition),
zone());
}
- list = new (zone()) ZoneList<v8::internal::Expression*>(1, zone());
+ list = new (zone()) ZoneList<Expression*>(1, zone());
list->Add(factory()->NewCallRuntime(Context::SPREAD_ARGUMENTS_INDEX, args,
kNoSourcePosition),
zone());
@@ -5495,10 +4051,8 @@
UNREACHABLE();
}
-
Expression* Parser::SpreadCall(Expression* function,
- ZoneList<v8::internal::Expression*>* args,
- int pos) {
+ ZoneList<Expression*>* args, int pos) {
if (function->IsSuperCallReference()) {
// Super calls
// $super_constructor = %_GetSuperConstructor(<this-function>)
@@ -5540,10 +4094,8 @@
}
}
-
Expression* Parser::SpreadCallNew(Expression* function,
- ZoneList<v8::internal::Expression*>* args,
- int pos) {
+ ZoneList<Expression*>* args, int pos) {
args->InsertAt(0, function, zone());
return factory()->NewCallRuntime(Context::REFLECT_CONSTRUCT_INDEX, args, pos);
@@ -5562,90 +4114,141 @@
scope->SetLanguageMode(mode);
}
-
-void Parser::RaiseLanguageMode(LanguageMode mode) {
- LanguageMode old = scope()->language_mode();
- SetLanguageMode(scope(), old > mode ? old : mode);
+void Parser::SetAsmModule() {
+ // Store the usage count; The actual use counter on the isolate is
+ // incremented after parsing is done.
+ ++use_counts_[v8::Isolate::kUseAsm];
+ DCHECK(scope()->is_declaration_scope());
+ scope()->AsDeclarationScope()->set_asm_module();
}
void Parser::MarkCollectedTailCallExpressions() {
const ZoneList<Expression*>& tail_call_expressions =
function_state_->tail_call_expressions().expressions();
for (int i = 0; i < tail_call_expressions.length(); ++i) {
- Expression* expression = tail_call_expressions[i];
- // If only FLAG_harmony_explicit_tailcalls is enabled then expression
- // must be a Call expression.
- DCHECK(FLAG_harmony_tailcalls || !FLAG_harmony_explicit_tailcalls ||
- expression->IsCall());
- MarkTailPosition(expression);
+ MarkTailPosition(tail_call_expressions[i]);
}
}
-Expression* ParserBaseTraits<Parser>::ExpressionListToExpression(
- ZoneList<Expression*>* args) {
- AstNodeFactory* factory = delegate()->factory();
+Expression* Parser::ExpressionListToExpression(ZoneList<Expression*>* args) {
Expression* expr = args->at(0);
for (int i = 1; i < args->length(); ++i) {
- expr = factory->NewBinaryOperation(Token::COMMA, expr, args->at(i),
- expr->position());
+ expr = factory()->NewBinaryOperation(Token::COMMA, expr, args->at(i),
+ expr->position());
}
return expr;
}
+// This method intoduces the line initializing the generator object
+// when desugaring the body of async_function.
+void Parser::PrepareAsyncFunctionBody(ZoneList<Statement*>* body,
+ FunctionKind kind, int pos) {
+ // function async_function() {
+ // .generator_object = %CreateGeneratorObject();
+ // BuildRejectPromiseOnException({
+ // ... block ...
+ // return %ResolvePromise(.promise, expr), .promise;
+ // })
+ // }
+
+ Variable* temp =
+ NewTemporary(ast_value_factory()->dot_generator_object_string());
+ function_state_->set_generator_object_variable(temp);
+
+ Expression* init_generator_variable = factory()->NewAssignment(
+ Token::INIT, factory()->NewVariableProxy(temp),
+ BuildCreateJSGeneratorObject(pos, kind), kNoSourcePosition);
+ body->Add(factory()->NewExpressionStatement(init_generator_variable,
+ kNoSourcePosition),
+ zone());
+}
+
+// This method completes the desugaring of the body of async_function.
+void Parser::RewriteAsyncFunctionBody(ZoneList<Statement*>* body, Block* block,
+ Expression* return_value, bool* ok) {
+ // function async_function() {
+ // .generator_object = %CreateGeneratorObject();
+ // BuildRejectPromiseOnException({
+ // ... block ...
+ // return %ResolvePromise(.promise, expr), .promise;
+ // })
+ // }
+
+ return_value = BuildResolvePromise(return_value, return_value->position());
+ block->statements()->Add(
+ factory()->NewReturnStatement(return_value, return_value->position()),
+ zone());
+ block = BuildRejectPromiseOnException(block, CHECK_OK_VOID);
+ body->Add(block, zone());
+}
+
Expression* Parser::RewriteAwaitExpression(Expression* value, int await_pos) {
- // yield %AsyncFunctionAwait(.generator_object, <operand>)
+ // yield do {
+ // tmp = <operand>;
+ // %AsyncFunctionAwait(.generator_object, tmp, .promise);
+ // .promise
+ // }
+ // The value of the expression is returned to the caller of the async
+ // function for the first yield statement; for this, .promise is the
+ // appropriate return value, being a Promise that will be fulfilled or
+ // rejected with the appropriate value by the desugaring. Subsequent yield
+ // occurrences will return to the AsyncFunctionNext call within the
+ // implemementation of the intermediate throwaway Promise's then handler.
+ // This handler has nothing useful to do with the value, as the Promise is
+ // ignored. If we yielded the value of the throwawayPromise that
+ // AsyncFunctionAwait creates as an intermediate, it would create a memory
+ // leak; we must return .promise instead;
+ // The operand needs to be evaluated on a separate statement in order to get
+ // a break location, and the .promise needs to be read earlier so that it
+ // doesn't insert a false location.
+ // TODO(littledan): investigate why this ordering is needed in more detail.
Variable* generator_object_variable =
- delegate()->function_state_->generator_object_variable();
+ function_state_->generator_object_variable();
// If generator_object_variable is null,
+ // TODO(littledan): Is this necessary?
if (!generator_object_variable) return value;
- auto factory = delegate()->factory();
const int nopos = kNoSourcePosition;
- Variable* temp_var =
- delegate()->NewTemporary(delegate()->ast_value_factory()->empty_string());
- VariableProxy* temp_proxy = factory->NewVariableProxy(temp_var);
- Block* do_block = factory->NewBlock(nullptr, 2, false, nopos);
+ Block* do_block = factory()->NewBlock(nullptr, 2, false, nopos);
+
+ Variable* promise = PromiseVariable();
// Wrap value evaluation to provide a break location.
- Expression* value_assignment =
- factory->NewAssignment(Token::ASSIGN, temp_proxy, value, nopos);
+ Variable* temp_var = NewTemporary(ast_value_factory()->empty_string());
+ Expression* value_assignment = factory()->NewAssignment(
+ Token::ASSIGN, factory()->NewVariableProxy(temp_var), value, nopos);
do_block->statements()->Add(
- factory->NewExpressionStatement(value_assignment, value->position()),
+ factory()->NewExpressionStatement(value_assignment, value->position()),
zone());
ZoneList<Expression*>* async_function_await_args =
- new (zone()) ZoneList<Expression*>(2, zone());
+ new (zone()) ZoneList<Expression*>(3, zone());
Expression* generator_object =
- factory->NewVariableProxy(generator_object_variable);
+ factory()->NewVariableProxy(generator_object_variable);
async_function_await_args->Add(generator_object, zone());
- async_function_await_args->Add(temp_proxy, zone());
- Expression* async_function_await = delegate()->factory()->NewCallRuntime(
- Context::ASYNC_FUNCTION_AWAIT_INDEX, async_function_await_args, nopos);
- // Wrap await to provide a break location between value evaluation and yield.
- Expression* await_assignment = factory->NewAssignment(
- Token::ASSIGN, temp_proxy, async_function_await, nopos);
+ async_function_await_args->Add(factory()->NewVariableProxy(temp_var), zone());
+ async_function_await_args->Add(factory()->NewVariableProxy(promise), zone());
+
+ // The parser emits calls to AsyncFunctionAwaitCaught, but the
+ // AstNumberingVisitor will rewrite this to AsyncFunctionAwaitUncaught
+ // if there is no local enclosing try/catch block.
+ Expression* async_function_await =
+ factory()->NewCallRuntime(Context::ASYNC_FUNCTION_AWAIT_CAUGHT_INDEX,
+ async_function_await_args, nopos);
do_block->statements()->Add(
- factory->NewExpressionStatement(await_assignment, await_pos), zone());
- Expression* do_expr = factory->NewDoExpression(do_block, temp_var, nopos);
+ factory()->NewExpressionStatement(async_function_await, await_pos),
+ zone());
- generator_object = factory->NewVariableProxy(generator_object_variable);
- return factory->NewYield(generator_object, do_expr, nopos,
- Yield::kOnExceptionRethrow);
+ // Wrap await to provide a break location between value evaluation and yield.
+ Expression* do_expr = factory()->NewDoExpression(do_block, promise, nopos);
+
+ generator_object = factory()->NewVariableProxy(generator_object_variable);
+ return factory()->NewYield(generator_object, do_expr, nopos,
+ Yield::kOnExceptionRethrow);
}
-ZoneList<Expression*>* ParserBaseTraits<Parser>::GetNonPatternList() const {
- return delegate()->function_state_->non_patterns_to_rewrite();
-}
-
-ZoneList<typename ParserBaseTraits<Parser>::Type::ExpressionClassifier::Error>*
-ParserBaseTraits<Parser>::GetReportedErrorList() const {
- return delegate()->function_state_->GetReportedErrorList();
-}
-
-Zone* ParserBaseTraits<Parser>::zone() const { return delegate()->zone(); }
-
class NonPatternRewriter : public AstExpressionRewriter {
public:
NonPatternRewriter(uintptr_t stack_limit, Parser* parser)
@@ -5674,7 +4277,7 @@
return false;
}
- void VisitObjectLiteralProperty(ObjectLiteralProperty* property) override {
+ void VisitLiteralProperty(LiteralProperty* property) override {
if (property == nullptr) return;
// Do not rewrite (computed) key expressions
AST_REWRITE_PROPERTY(Expression, property, value);
@@ -5683,11 +4286,10 @@
Parser* parser_;
};
-
-void Parser::RewriteNonPattern(ExpressionClassifier* classifier, bool* ok) {
- ValidateExpression(classifier, CHECK_OK_VOID);
+void Parser::RewriteNonPattern(bool* ok) {
+ ValidateExpression(CHECK_OK_VOID);
auto non_patterns_to_rewrite = function_state_->non_patterns_to_rewrite();
- int begin = classifier->GetNonPatternBegin();
+ int begin = classifier()->GetNonPatternBegin();
int end = non_patterns_to_rewrite->length();
if (begin < end) {
NonPatternRewriter rewriter(stack_limit_, this);
@@ -5711,8 +4313,11 @@
pair.assignment->AsRewritableExpression();
DCHECK_NOT_NULL(to_rewrite);
if (!to_rewrite->is_rewritten()) {
- PatternRewriter::RewriteDestructuringAssignment(this, to_rewrite,
- pair.scope);
+ // Since this function is called at the end of parsing the program,
+ // pair.scope may already have been removed by FinalizeBlockScope in the
+ // meantime.
+ Scope* scope = pair.scope->GetUnremovedScope();
+ PatternRewriter::RewriteDestructuringAssignment(this, to_rewrite, scope);
}
}
}
@@ -5733,8 +4338,8 @@
Expression* result;
DCHECK_NOT_NULL(lhs->raw_name());
- result = this->ExpressionFromIdentifier(lhs->raw_name(), lhs->position(),
- lhs->end_position());
+ result = ExpressionFromIdentifier(lhs->raw_name(), lhs->position(),
+ lhs->end_position());
args->Add(left, zone());
args->Add(right, zone());
Expression* call =
@@ -5807,8 +4412,7 @@
// ++($R.length)
if (!value->IsLiteral() ||
!value->AsLiteral()->raw_value()->IsTheHole()) {
- ZoneList<Expression*>* append_element_args =
- NewExpressionList(2, zone());
+ ZoneList<Expression*>* append_element_args = NewExpressionList(2);
append_element_args->Add(factory()->NewVariableProxy(result), zone());
append_element_args->Add(value, zone());
do_block->statements()->Add(
@@ -5837,8 +4441,7 @@
// %AppendElement($R, each)
Statement* append_body;
{
- ZoneList<Expression*>* append_element_args =
- NewExpressionList(2, zone());
+ ZoneList<Expression*>* append_element_args = NewExpressionList(2);
append_element_args->Add(factory()->NewVariableProxy(result), zone());
append_element_args->Add(factory()->NewVariableProxy(each), zone());
append_body = factory()->NewExpressionStatement(
@@ -5865,7 +4468,7 @@
void Parser::QueueDestructuringAssignmentForRewriting(Expression* expr) {
DCHECK(expr->IsRewritableExpression());
function_state_->AddDestructuringAssignment(
- DestructuringAssignment(expr, delegate()->scope()));
+ DestructuringAssignment(expr, scope()));
}
void Parser::QueueNonPatternForRewriting(Expression* expr, bool* ok) {
@@ -5873,43 +4476,38 @@
function_state_->AddNonPatternForRewriting(expr, ok);
}
-void ParserBaseTraits<Parser>::SetFunctionNameFromPropertyName(
- ObjectLiteralProperty* property, const AstRawString* name) {
- Expression* value = property->value();
+void Parser::AddAccessorPrefixToFunctionName(bool is_get,
+ FunctionLiteral* function,
+ const AstRawString* name) {
+ DCHECK_NOT_NULL(name);
+ const AstRawString* prefix = is_get ? ast_value_factory()->get_space_string()
+ : ast_value_factory()->set_space_string();
+ function->set_raw_name(ast_value_factory()->NewConsString(prefix, name));
+}
+
+void Parser::SetFunctionNameFromPropertyName(ObjectLiteralProperty* property,
+ const AstRawString* name) {
+ DCHECK(property->kind() != ObjectLiteralProperty::GETTER);
+ DCHECK(property->kind() != ObjectLiteralProperty::SETTER);
// Computed name setting must happen at runtime.
- if (property->is_computed_name()) return;
-
- // Getter and setter names are handled here because their names
- // change in ES2015, even though they are not anonymous.
- auto function = value->AsFunctionLiteral();
- if (function != nullptr) {
- bool is_getter = property->kind() == ObjectLiteralProperty::GETTER;
- bool is_setter = property->kind() == ObjectLiteralProperty::SETTER;
- if (is_getter || is_setter) {
- DCHECK_NOT_NULL(name);
- const AstRawString* prefix =
- is_getter ? delegate()->ast_value_factory()->get_space_string()
- : delegate()->ast_value_factory()->set_space_string();
- function->set_raw_name(
- delegate()->ast_value_factory()->NewConsString(prefix, name));
- return;
- }
- }
+ DCHECK(!property->is_computed_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;
+ Expression* value = property->value();
+
DCHECK(!value->IsAnonymousFunctionDefinition() ||
property->kind() == ObjectLiteralProperty::COMPUTED);
- delegate()->SetFunctionName(value, name);
+ SetFunctionName(value, name);
}
-void ParserBaseTraits<Parser>::SetFunctionNameFromIdentifierRef(
- Expression* value, Expression* identifier) {
+void Parser::SetFunctionNameFromIdentifierRef(Expression* value,
+ Expression* identifier) {
if (!identifier->IsVariableProxy()) return;
- delegate()->SetFunctionName(value, identifier->AsVariableProxy()->raw_name());
+ SetFunctionName(value, identifier->AsVariableProxy()->raw_name());
}
void Parser::SetFunctionName(Expression* value, const AstRawString* name) {
@@ -6050,7 +4648,7 @@
Variable* var_iterator = NewTemporary(ast_value_factory()->empty_string());
Statement* get_iterator;
{
- Expression* iterator = GetIterator(iterable, factory(), nopos);
+ Expression* iterator = GetIterator(iterable, nopos);
Expression* iterator_proxy = factory()->NewVariableProxy(var_iterator);
Expression* assignment = factory()->NewAssignment(
Token::ASSIGN, iterator_proxy, iterator, nopos);
@@ -6155,7 +4753,7 @@
Block* then = factory()->NewBlock(nullptr, 4 + 1, false, nopos);
BuildIteratorCloseForCompletion(
- then->statements(), var_iterator,
+ scope(), then->statements(), var_iterator,
factory()->NewSmiLiteral(Parser::kNormalCompletion, nopos));
then->statements()->Add(throw_call, zone());
check_throw = factory()->NewIfStatement(
@@ -6259,7 +4857,7 @@
// input = function.sent;
Statement* get_input;
{
- Expression* function_sent = FunctionSentExpression(factory(), nopos);
+ Expression* function_sent = FunctionSentExpression(nopos);
Expression* input_proxy = factory()->NewVariableProxy(var_input);
Expression* assignment = factory()->NewAssignment(
Token::ASSIGN, input_proxy, function_sent, nopos);
@@ -6313,9 +4911,8 @@
Scope* catch_scope = NewScope(CATCH_SCOPE);
catch_scope->set_is_hidden();
const AstRawString* name = ast_value_factory()->dot_catch_string();
- Variable* catch_variable =
- catch_scope->DeclareLocal(name, VAR, kCreatedInitialized,
- Variable::NORMAL);
+ Variable* catch_variable = catch_scope->DeclareLocal(
+ name, VAR, kCreatedInitialized, NORMAL_VARIABLE);
try_catch = factory()->NewTryCatchStatementForDesugaring(
try_block, catch_scope, catch_variable, catch_block, nopos);
@@ -6524,9 +5121,9 @@
statements->Add(validate_output, zone());
}
-void Parser::FinalizeIteratorUse(Variable* completion, Expression* condition,
- Variable* iter, Block* iterator_use,
- Block* target) {
+void Parser::FinalizeIteratorUse(Scope* use_scope, Variable* completion,
+ Expression* condition, Variable* iter,
+ Block* iterator_use, Block* target) {
//
// This function adds two statements to [target], corresponding to the
// following code:
@@ -6582,7 +5179,8 @@
{
Block* block = factory()->NewBlock(nullptr, 2, true, nopos);
Expression* proxy = factory()->NewVariableProxy(completion);
- BuildIteratorCloseForCompletion(block->statements(), iter, proxy);
+ BuildIteratorCloseForCompletion(use_scope, block->statements(), iter,
+ proxy);
DCHECK(block->statements()->length() == 2);
maybe_close = factory()->NewBlock(nullptr, 1, true, nopos);
@@ -6599,10 +5197,10 @@
// }
Statement* try_catch;
{
- Scope* catch_scope = NewScopeWithParent(scope(), CATCH_SCOPE);
+ Scope* catch_scope = NewScopeWithParent(use_scope, CATCH_SCOPE);
Variable* catch_variable =
catch_scope->DeclareLocal(ast_value_factory()->dot_catch_string(), VAR,
- kCreatedInitialized, Variable::NORMAL);
+ kCreatedInitialized, NORMAL_VARIABLE);
catch_scope->set_is_hidden();
Statement* rethrow;
@@ -6639,7 +5237,8 @@
target->statements()->Add(try_finally, zone());
}
-void Parser::BuildIteratorCloseForCompletion(ZoneList<Statement*>* statements,
+void Parser::BuildIteratorCloseForCompletion(Scope* scope,
+ ZoneList<Statement*>* statements,
Variable* iterator,
Expression* completion) {
//
@@ -6705,10 +5304,10 @@
Block* catch_block = factory()->NewBlock(nullptr, 0, false, nopos);
- Scope* catch_scope = NewScope(CATCH_SCOPE);
+ Scope* catch_scope = NewScopeWithParent(scope, CATCH_SCOPE);
Variable* catch_variable =
catch_scope->DeclareLocal(ast_value_factory()->dot_catch_string(), VAR,
- kCreatedInitialized, Variable::NORMAL);
+ kCreatedInitialized, NORMAL_VARIABLE);
catch_scope->set_is_hidden();
try_call_return = factory()->NewTryCatchStatement(
@@ -6842,20 +5441,18 @@
Block* try_block = factory()->NewBlock(nullptr, 1, false, nopos);
try_block->statements()->Add(loop, zone());
- FinalizeIteratorUse(var_completion, closing_condition, loop->iterator(),
- try_block, final_loop);
+ // The scope in which the parser creates this loop.
+ Scope* loop_scope = scope()->outer_scope();
+ DCHECK_EQ(loop_scope->scope_type(), BLOCK_SCOPE);
+ DCHECK_EQ(scope()->scope_type(), BLOCK_SCOPE);
+
+ FinalizeIteratorUse(loop_scope, var_completion, closing_condition,
+ loop->iterator(), try_block, final_loop);
}
return final_loop;
}
-#ifdef DEBUG
-void Parser::Print(AstNode* node) {
- ast_value_factory()->Internalize(Isolate::Current());
- node->Print(Isolate::Current());
-}
-#endif // DEBUG
-
#undef CHECK_OK
#undef CHECK_OK_VOID
#undef CHECK_FAILED
diff --git a/src/parsing/parser.h b/src/parsing/parser.h
index b069f9a..418bedf 100644
--- a/src/parsing/parser.h
+++ b/src/parsing/parser.h
@@ -21,7 +21,8 @@
class ParseInfo;
class ScriptData;
-class Target;
+class ParserTarget;
+class ParserTargetScope;
class FunctionEntry BASE_EMBEDDED {
public:
@@ -138,239 +139,33 @@
};
template <>
-class ParserBaseTraits<Parser> {
- public:
- typedef ParserBaseTraits<Parser> ParserTraits;
+struct ParserTypes<Parser> {
+ typedef ParserBase<Parser> Base;
+ typedef Parser Impl;
- struct Type {
- typedef Variable GeneratorVariable;
+ typedef v8::internal::Variable Variable;
- typedef v8::internal::AstProperties AstProperties;
+ // Return types for traversing functions.
+ typedef const AstRawString* Identifier;
+ typedef v8::internal::Expression* Expression;
+ typedef v8::internal::FunctionLiteral* FunctionLiteral;
+ typedef ObjectLiteral::Property* ObjectLiteralProperty;
+ typedef ClassLiteral::Property* ClassLiteralProperty;
+ typedef ZoneList<v8::internal::Expression*>* ExpressionList;
+ typedef ZoneList<ObjectLiteral::Property*>* ObjectPropertyList;
+ typedef ZoneList<ClassLiteral::Property*>* ClassPropertyList;
+ typedef ParserFormalParameters FormalParameters;
+ typedef v8::internal::Statement* Statement;
+ typedef ZoneList<v8::internal::Statement*>* StatementList;
+ typedef v8::internal::Block* Block;
+ typedef v8::internal::BreakableStatement* BreakableStatement;
+ typedef v8::internal::IterationStatement* IterationStatement;
- typedef v8::internal::ExpressionClassifier<ParserTraits>
- ExpressionClassifier;
+ // For constructing objects returned by the traversing functions.
+ typedef AstNodeFactory Factory;
- // 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;
- };
-
- // TODO(nikolaos): The traits methods should not need to call methods
- // of the implementation object.
- Parser* delegate() { return reinterpret_cast<Parser*>(this); }
- const Parser* delegate() const {
- return reinterpret_cast<const Parser*>(this);
- }
-
- // 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);
-
- static const AstRawString* AsIdentifier(Expression* expression) {
- DCHECK(IsIdentifier(expression));
- return expression->AsVariableProxy()->raw_name();
- }
-
- bool IsPrototype(const AstRawString* identifier) const;
-
- bool IsConstructor(const AstRawString* identifier) const;
-
- bool IsDirectEvalCall(Expression* expression) const {
- if (!expression->IsCall()) return false;
- expression = expression->AsCall()->expression();
- return IsIdentifier(expression) && IsEval(AsIdentifier(expression));
- }
-
- 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);
- }
-
- // 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);
-
- Expression* BuildIteratorResult(Expression* value, bool done);
-
- // 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);
-
- // Reporting errors.
- void ReportMessageAt(Scanner::Location source_location,
- MessageTemplate::Template message,
- const char* arg = NULL,
- 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 nullptr; }
- static Expression* EmptyExpression() { return nullptr; }
- static Literal* EmptyLiteral() { return nullptr; }
- static ObjectLiteralProperty* EmptyObjectLiteralProperty() { return nullptr; }
- static FunctionLiteral* EmptyFunctionLiteral() { return nullptr; }
-
- // Used in error return values.
- static ZoneList<Expression*>* NullExpressionList() { return nullptr; }
-
- // Non-NULL empty string.
- V8_INLINE const AstRawString* EmptyIdentifierString() const;
-
- // Odd-ball literal creators.
- Literal* GetLiteralTheHole(int position, AstNodeFactory* factory) const;
-
- // Producing data during the recursive descent.
- const AstRawString* GetSymbol(Scanner* scanner) const;
- const AstRawString* GetNextSymbol(Scanner* scanner) const;
- const AstRawString* GetNumberAsSymbol(Scanner* scanner) const;
-
- Expression* ThisExpression(int pos = kNoSourcePosition);
- Expression* NewSuperPropertyReference(AstNodeFactory* factory, int pos);
- Expression* NewSuperCallReference(AstNodeFactory* factory, int pos);
- Expression* NewTargetExpression(int pos);
- Expression* FunctionSentExpression(AstNodeFactory* factory, int pos) const;
- Literal* ExpressionFromLiteral(Token::Value token, int pos, Scanner* scanner,
- AstNodeFactory* factory) const;
- Expression* ExpressionFromIdentifier(const AstRawString* name,
- int start_position, int end_position,
- InferName = InferName::kYes);
- Expression* ExpressionFromString(int pos, Scanner* scanner,
- AstNodeFactory* factory) const;
- Expression* GetIterator(Expression* iterable, AstNodeFactory* factory,
- int pos);
- ZoneList<v8::internal::Expression*>* NewExpressionList(int size,
- Zone* zone) const {
- return new(zone) ZoneList<v8::internal::Expression*>(size, zone);
- }
- ZoneList<ObjectLiteral::Property*>* NewPropertyList(int size,
- Zone* zone) const {
- return new(zone) ZoneList<ObjectLiteral::Property*>(size, zone);
- }
- ZoneList<v8::internal::Statement*>* NewStatementList(int size,
- Zone* zone) const {
- return new(zone) ZoneList<v8::internal::Statement*>(size, zone);
- }
-
- V8_INLINE void AddParameterInitializationBlock(
- const ParserFormalParameters& parameters,
- ZoneList<v8::internal::Statement*>* body, bool is_async, bool* ok);
-
- V8_INLINE void AddFormalParameter(ParserFormalParameters* parameters,
- Expression* pattern,
- Expression* initializer,
- int initializer_end_position, bool is_rest);
- V8_INLINE void DeclareFormalParameter(
- DeclarationScope* scope,
- const ParserFormalParameters::Parameter& parameter,
- Type::ExpressionClassifier* classifier);
- void ParseArrowFunctionFormalParameterList(
- ParserFormalParameters* parameters, Expression* params,
- const Scanner::Location& params_loc, Scanner::Location* duplicate_loc,
- const Scope::Snapshot& scope_snapshot, bool* ok);
-
- void ReindexLiterals(const ParserFormalParameters& parameters);
-
- V8_INLINE Expression* NoTemplateTag() { return NULL; }
- V8_INLINE static bool IsTaggedTemplate(const Expression* tag) {
- return tag != NULL;
- }
-
- V8_INLINE void MaterializeUnspreadArgumentsLiterals(int count) {}
-
- Expression* ExpressionListToExpression(ZoneList<Expression*>* args);
-
- void SetFunctionNameFromPropertyName(ObjectLiteralProperty* property,
- const AstRawString* name);
-
- void SetFunctionNameFromIdentifierRef(Expression* value,
- Expression* identifier);
-
- V8_INLINE ZoneList<typename Type::ExpressionClassifier::Error>*
- GetReportedErrorList() const;
- V8_INLINE Zone* zone() const;
-
- V8_INLINE ZoneList<Expression*>* GetNonPatternList() const;
+ typedef ParserTarget Target;
+ typedef ParserTargetScope TargetScope;
};
class Parser : public ParserBase<Parser> {
@@ -390,8 +185,16 @@
bool Parse(ParseInfo* info);
void ParseOnBackground(ParseInfo* info);
- void DeserializeScopeChain(ParseInfo* info, Handle<Context> context,
- Scope::DeserializationMode deserialization_mode);
+ // Deserialize the scope chain prior to parsing in which the script is going
+ // to be executed. If the script is a top-level script, or the scope chain
+ // consists of only a native context, maybe_outer_scope_info should be an
+ // empty handle.
+ //
+ // This only deserializes the scope chain, but doesn't connect the scopes to
+ // their corresponding scope infos. Therefore, looking up variables in the
+ // deserialized scopes is not possible.
+ void DeserializeScopeChain(ParseInfo* info,
+ MaybeHandle<ScopeInfo> maybe_outer_scope_info);
// Handle errors detected during parsing, move statistics to Isolate,
// internalize strings (move them to the heap).
@@ -400,9 +203,7 @@
private:
friend class ParserBase<Parser>;
- // TODO(nikolaos): This should not be necessary. It will be removed
- // when the traits object stops delegating to the implementation object.
- friend class ParserBaseTraits<Parser>;
+ friend class v8::internal::ExpressionClassifier<ParserTypes<Parser>>;
// Runtime encoding of different completion modes.
enum CompletionKind {
@@ -411,18 +212,12 @@
kAbruptCompletion
};
- enum class FunctionBodyType { kNormal, kSingleExpression };
-
- DeclarationScope* GetDeclarationScope() const {
- return scope()->GetDeclarationScope();
- }
- DeclarationScope* GetClosureScope() const {
- return scope()->GetClosureScope();
- }
Variable* NewTemporary(const AstRawString* name) {
return scope()->NewTemporary(name);
}
+ void PrepareGeneratorVariables(FunctionState* function_state);
+
// 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
@@ -455,12 +250,6 @@
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);
@@ -482,75 +271,52 @@
location(location) {}
};
ZoneList<const NamedImport*>* ParseNamedImports(int pos, bool* ok);
- Statement* ParseStatement(ZoneList<const AstRawString*>* labels,
- AllowLabelledFunctionStatement allow_function,
- bool* ok);
- Statement* ParseSubStatement(ZoneList<const AstRawString*>* labels,
- AllowLabelledFunctionStatement allow_function,
- bool* ok);
- Statement* ParseStatementAsUnlabelled(ZoneList<const AstRawString*>* labels,
- bool* ok);
- Statement* ParseFunctionDeclaration(bool* ok);
- Statement* ParseHoistableDeclaration(ZoneList<const AstRawString*>* names,
- bool default_export, bool* ok);
- Statement* ParseHoistableDeclaration(int pos, ParseFunctionFlags flags,
- ZoneList<const AstRawString*>* names,
- bool default_export, bool* ok);
- Statement* ParseAsyncFunctionDeclaration(ZoneList<const AstRawString*>* names,
- bool default_export, bool* ok);
- Expression* ParseAsyncFunctionExpression(bool* ok);
- Statement* ParseClassDeclaration(ZoneList<const AstRawString*>* names,
- bool default_export, bool* ok);
- Statement* ParseNativeDeclaration(bool* ok);
- Block* ParseBlock(ZoneList<const AstRawString*>* labels, bool* ok);
- Block* ParseVariableStatement(VariableDeclarationContext var_context,
- ZoneList<const AstRawString*>* names,
- bool* ok);
- DoExpression* ParseDoExpression(bool* ok);
- Expression* ParseYieldStarExpression(bool* ok);
+ Block* BuildInitializationBlock(DeclarationParsingResult* parsing_result,
+ ZoneList<const AstRawString*>* names,
+ bool* ok);
+ void DeclareAndInitializeVariables(
+ Block* block, const DeclarationDescriptor* declaration_descriptor,
+ const DeclarationParsingResult::Declaration* declaration,
+ ZoneList<const AstRawString*>* names, bool* ok);
+ ZoneList<const AstRawString*>* DeclareLabel(
+ ZoneList<const AstRawString*>* labels, VariableProxy* expr, bool* ok);
+ bool ContainsLabel(ZoneList<const AstRawString*>* labels,
+ const AstRawString* label);
+ Expression* RewriteReturn(Expression* return_value, int pos);
+ Statement* RewriteSwitchStatement(Expression* tag,
+ SwitchStatement* switch_statement,
+ ZoneList<CaseClause*>* cases, Scope* scope);
+ void RewriteCatchPattern(CatchInfo* catch_info, bool* ok);
+ void ValidateCatchBlock(const CatchInfo& catch_info, bool* ok);
+ Statement* RewriteTryStatement(Block* try_block, Block* catch_block,
+ Block* finally_block,
+ const CatchInfo& catch_info, int pos);
- struct DeclarationDescriptor {
- enum Kind { NORMAL, PARAMETER };
- Parser* parser;
- Scope* scope;
- Scope* hoist_scope;
- VariableMode mode;
- 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;
- };
+ Statement* DeclareFunction(const AstRawString* variable_name,
+ FunctionLiteral* function, int pos,
+ bool is_generator, bool is_async,
+ ZoneList<const AstRawString*>* names, bool* ok);
+ V8_INLINE Statement* DeclareClass(const AstRawString* variable_name,
+ Expression* value,
+ ZoneList<const AstRawString*>* names,
+ int class_token_pos, int end_pos, bool* ok);
+ V8_INLINE void DeclareClassVariable(const AstRawString* name,
+ Scope* block_scope, ClassInfo* class_info,
+ int class_token_pos, bool* ok);
+ V8_INLINE void DeclareClassProperty(const AstRawString* class_name,
+ ClassLiteralProperty* property,
+ ClassInfo* class_info, bool* ok);
+ V8_INLINE Expression* RewriteClassLiteral(const AstRawString* name,
+ ClassInfo* class_info, int pos,
+ bool* ok);
+ V8_INLINE Statement* DeclareNative(const AstRawString* name, int pos,
+ bool* ok);
class PatternRewriter final : public AstVisitor<PatternRewriter> {
public:
static void DeclareAndInitializeVariables(
- Block* block, const DeclarationDescriptor* declaration_descriptor,
+ Parser* parser, Block* block,
+ const DeclarationDescriptor* declaration_descriptor,
const DeclarationParsingResult::Declaration* declaration,
ZoneList<const AstRawString*>* names, bool* ok);
@@ -627,47 +393,12 @@
DEFINE_AST_VISITOR_MEMBERS_WITHOUT_STACKOVERFLOW()
};
- Block* ParseVariableDeclarations(VariableDeclarationContext var_context,
- DeclarationParsingResult* parsing_result,
- ZoneList<const AstRawString*>* names,
- bool* ok);
- Statement* ParseExpressionOrLabelledStatement(
- ZoneList<const AstRawString*>* labels,
- AllowLabelledFunctionStatement allow_function, 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);
- // Parse a SubStatement in strict mode, or with an extra block scope in
- // sloppy mode to handle
- // ES#sec-functiondeclarations-in-ifstatement-statement-clauses
- // The legacy parameter indicates whether function declarations are
- // banned by the ES2015 specification in this location, and they are being
- // permitted here to match previous V8 behavior.
- Statement* ParseScopedStatement(ZoneList<const AstRawString*>* labels,
- bool legacy, bool* ok);
-
// !%_IsJSReceiver(result = iterator.next()) &&
// %ThrowIteratorResultNotAnObject(result)
Expression* BuildIteratorNextResult(Expression* iterator, Variable* result,
int pos);
+ Expression* GetIterator(Expression* iterable, int pos);
// Initialize the components of a for-in / for-of statement.
Statement* InitializeForEachStatement(ForEachStatement* stmt,
@@ -677,18 +408,17 @@
Expression* iterable, Statement* body,
bool finalize,
int next_result_pos = kNoSourcePosition);
+ Block* RewriteForVarInLegacy(const ForInfo& for_info);
+ void DesugarBindingInForEachStatement(ForInfo* for_info, Block** body_block,
+ Expression** each_variable, bool* ok);
+ Block* CreateForEachStatementTDZ(Block* init_block, const ForInfo& for_info,
+ bool* ok);
+
Statement* DesugarLexicalBindingsInForStatement(
- Scope* inner_scope, VariableMode mode,
- ZoneList<const AstRawString*>* names, ForStatement* loop, Statement* init,
- Expression* cond, Statement* next, Statement* body, bool* ok);
+ ForStatement* loop, Statement* init, Expression* cond, Statement* next,
+ Statement* body, Scope* inner_scope, const ForInfo& for_info, bool* ok);
- void DesugarAsyncFunctionBody(const AstRawString* function_name, Scope* scope,
- ZoneList<Statement*>* body,
- Type::ExpressionClassifier* classifier,
- FunctionKind kind, FunctionBodyType type,
- bool accept_IN, int pos, bool* ok);
-
- void RewriteDoExpression(Expression* expr, bool* ok);
+ Expression* RewriteDoExpression(Block* body, int pos, bool* ok);
FunctionLiteral* ParseFunctionLiteral(
const AstRawString* name, Scanner::Location function_name_location,
@@ -696,14 +426,10 @@
int function_token_position, FunctionLiteral::FunctionType type,
LanguageMode language_mode, bool* ok);
- Expression* ParseClassLiteral(ExpressionClassifier* classifier,
- const AstRawString* name,
- Scanner::Location class_name_location,
- bool name_is_strict_reserved, int pos,
- bool* ok);
-
- // Magical syntax support.
- Expression* ParseV8Intrinsic(bool* ok);
+ Expression* InstallHomeObject(Expression* function_literal,
+ Expression* home_object);
+ FunctionLiteral* SynthesizeClassFieldInitializer(int count);
+ FunctionLiteral* InsertClassFieldInitializer(FunctionLiteral* constructor);
// Get odd-ball literals.
Literal* GetLiteralUndefined(int position);
@@ -724,14 +450,11 @@
void InsertShadowingVarBindingInitializers(Block* block);
// Implement sloppy block-scoped functions, ES2015 Annex B 3.3
- void InsertSloppyBlockFunctionVarBindings(DeclarationScope* scope,
- Scope* complex_params_scope,
- bool* ok);
+ void InsertSloppyBlockFunctionVarBindings(DeclarationScope* scope);
- static InitializationFlag DefaultInitializationFlag(VariableMode mode);
VariableProxy* NewUnresolved(const AstRawString* name, int begin_pos,
int end_pos = kNoSourcePosition,
- Variable::Kind kind = Variable::NORMAL);
+ VariableKind kind = NORMAL_VARIABLE);
VariableProxy* NewUnresolved(const AstRawString* name);
Variable* Declare(Declaration* declaration,
DeclarationDescriptor::Kind declaration_kind,
@@ -750,25 +473,24 @@
// Factory methods.
FunctionLiteral* DefaultConstructor(const AstRawString* name, bool call_super,
- int pos, int end_pos,
- LanguageMode language_mode);
+ bool requires_class_field_init, 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
+ // If may_abort == true, 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);
+ LazyParsingResult SkipLazyFunctionBody(int* materialized_literal_count,
+ int* expected_property_count,
+ bool is_inner_function, bool may_abort,
+ bool* ok);
PreParser::PreParseResult ParseLazyFunctionBodyWithPreParser(
- SingletonLogger* logger, Scanner::BookmarkScope* bookmark = nullptr);
+ SingletonLogger* logger, bool is_inner_function, bool may_abort);
Block* BuildParameterInitializationBlock(
const ParserFormalParameters& parameters, bool* ok);
- Block* BuildRejectPromiseOnException(Block* block);
+ Block* BuildRejectPromiseOnException(Block* block, bool* ok);
// Consumes the ending }.
ZoneList<Statement*>* ParseEagerFunctionBody(
@@ -817,25 +539,16 @@
Expression* tag);
uint32_t ComputeTemplateLiteralHash(const TemplateLiteral* lit);
- void ParseAsyncArrowSingleExpressionBody(ZoneList<Statement*>* body,
- bool accept_IN,
- ExpressionClassifier* classifier,
- int pos, bool* ok) {
- DesugarAsyncFunctionBody(ast_value_factory()->empty_string(), scope(), body,
- classifier, kAsyncArrowFunction,
- FunctionBodyType::kSingleExpression, accept_IN,
- pos, ok);
- }
-
- 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);
+ ZoneList<Expression*>* PrepareSpreadArguments(ZoneList<Expression*>* list);
+ Expression* SpreadCall(Expression* function, ZoneList<Expression*>* args,
+ int pos);
+ Expression* SpreadCallNew(Expression* function, ZoneList<Expression*>* args,
+ int pos);
+ Expression* CallClassFieldInitializer(Scope* scope, Expression* this_expr);
+ Expression* RewriteSuperCall(Expression* call_expression);
void SetLanguageMode(Scope* scope, LanguageMode mode);
- void RaiseLanguageMode(LanguageMode mode);
+ void SetAsmModule();
V8_INLINE void MarkCollectedTailCallExpressions();
V8_INLINE void MarkTailPosition(Expression* expression);
@@ -852,7 +565,7 @@
V8_INLINE Expression* RewriteSpreads(ArrayLiteral* lit);
// Rewrite expressions that are not used as patterns
- V8_INLINE void RewriteNonPattern(ExpressionClassifier* classifier, bool* ok);
+ V8_INLINE void RewriteNonPattern(bool* ok);
V8_INLINE void QueueDestructuringAssignmentForRewriting(
Expression* assignment);
@@ -861,41 +574,513 @@
friend class InitializerRewriter;
void RewriteParameterInitializer(Expression* expr, Scope* scope);
+ Expression* BuildInitialYield(int pos, FunctionKind kind);
Expression* BuildCreateJSGeneratorObject(int pos, FunctionKind kind);
- Expression* BuildPromiseResolve(Expression* value, int pos);
- Expression* BuildPromiseReject(Expression* value, int pos);
+ Expression* BuildResolvePromise(Expression* value, int pos);
+ Expression* BuildRejectPromise(Expression* value, int pos);
+ Variable* PromiseVariable();
// Generic AST generator for throwing errors from compiled code.
Expression* NewThrowError(Runtime::FunctionId function_id,
MessageTemplate::Template message,
const AstRawString* arg, int pos);
- void FinalizeIteratorUse(Variable* completion, Expression* condition,
- Variable* iter, Block* iterator_use, Block* result);
+ void FinalizeIteratorUse(Scope* use_scope, Variable* completion,
+ Expression* condition, Variable* iter,
+ Block* iterator_use, Block* result);
Statement* FinalizeForOfStatement(ForOfStatement* loop, Variable* completion,
int pos);
void BuildIteratorClose(ZoneList<Statement*>* statements, Variable* iterator,
Variable* input, Variable* output);
- void BuildIteratorCloseForCompletion(ZoneList<Statement*>* statements,
+ void BuildIteratorCloseForCompletion(Scope* scope,
+ ZoneList<Statement*>* statements,
Variable* iterator,
Expression* completion);
Statement* CheckCallable(Variable* var, Expression* error, int pos);
V8_INLINE Expression* RewriteAwaitExpression(Expression* value, int pos);
+ V8_INLINE void PrepareAsyncFunctionBody(ZoneList<Statement*>* body,
+ FunctionKind kind, int pos);
+ V8_INLINE void RewriteAsyncFunctionBody(ZoneList<Statement*>* body,
+ Block* block,
+ Expression* return_value, bool* ok);
Expression* RewriteYieldStar(Expression* generator, Expression* expression,
int pos);
- void ParseArrowFunctionFormalParameters(ParserFormalParameters* parameters,
- Expression* params, int end_pos,
- bool* ok);
+ void AddArrowFunctionFormalParameters(ParserFormalParameters* parameters,
+ Expression* params, int end_pos,
+ bool* ok);
void SetFunctionName(Expression* value, const AstRawString* name);
+ // Helper functions for recursive descent.
+ V8_INLINE bool IsEval(const AstRawString* identifier) const {
+ return identifier == ast_value_factory()->eval_string();
+ }
+
+ V8_INLINE bool IsArguments(const AstRawString* identifier) const {
+ return identifier == ast_value_factory()->arguments_string();
+ }
+
+ V8_INLINE bool IsEvalOrArguments(const AstRawString* identifier) const {
+ return IsEval(identifier) || IsArguments(identifier);
+ }
+
+ V8_INLINE bool IsUndefined(const AstRawString* identifier) const {
+ return identifier == ast_value_factory()->undefined_string();
+ }
+
+ V8_INLINE bool IsFutureStrictReserved(const AstRawString* identifier) const {
+ return scanner()->IdentifierIsFutureStrictReserved(identifier);
+ }
+
+ // Returns true if the expression is of type "this.foo".
+ V8_INLINE static bool IsThisProperty(Expression* expression) {
+ DCHECK(expression != NULL);
+ Property* property = expression->AsProperty();
+ return property != NULL && property->obj()->IsVariableProxy() &&
+ property->obj()->AsVariableProxy()->is_this();
+ }
+
+ // This returns true if the expression is an indentifier (wrapped
+ // inside a variable proxy). We exclude the case of 'this', which
+ // has been converted to a variable proxy.
+ V8_INLINE static bool IsIdentifier(Expression* expression) {
+ DCHECK_NOT_NULL(expression);
+ VariableProxy* operand = expression->AsVariableProxy();
+ return operand != nullptr && !operand->is_this();
+ }
+
+ V8_INLINE static const AstRawString* AsIdentifier(Expression* expression) {
+ DCHECK(IsIdentifier(expression));
+ return expression->AsVariableProxy()->raw_name();
+ }
+
+ V8_INLINE VariableProxy* AsIdentifierExpression(Expression* expression) {
+ return expression->AsVariableProxy();
+ }
+
+ V8_INLINE bool IsPrototype(const AstRawString* identifier) const {
+ return identifier == ast_value_factory()->prototype_string();
+ }
+
+ V8_INLINE bool IsConstructor(const AstRawString* identifier) const {
+ return identifier == ast_value_factory()->constructor_string();
+ }
+
+ V8_INLINE bool IsDirectEvalCall(Expression* expression) const {
+ if (!expression->IsCall()) return false;
+ expression = expression->AsCall()->expression();
+ return IsIdentifier(expression) && IsEval(AsIdentifier(expression));
+ }
+
+ V8_INLINE static bool IsBoilerplateProperty(
+ ObjectLiteral::Property* property) {
+ return ObjectLiteral::IsBoilerplateProperty(property);
+ }
+
+ V8_INLINE bool IsNative(Expression* expr) const {
+ DCHECK_NOT_NULL(expr);
+ return expr->IsVariableProxy() &&
+ expr->AsVariableProxy()->raw_name() ==
+ ast_value_factory()->native_string();
+ }
+
+ V8_INLINE static bool IsArrayIndex(const AstRawString* string,
+ uint32_t* index) {
+ return string->AsArrayIndex(index);
+ }
+
+ V8_INLINE bool IsUseStrictDirective(Statement* statement) const {
+ return IsStringLiteral(statement, ast_value_factory()->use_strict_string());
+ }
+
+ V8_INLINE bool IsUseAsmDirective(Statement* statement) const {
+ return IsStringLiteral(statement, ast_value_factory()->use_asm_string());
+ }
+
+ // Returns true if the statement is an expression statement containing
+ // a single string literal. If a second argument is given, the literal
+ // is also compared with it and the result is true only if they are equal.
+ V8_INLINE bool IsStringLiteral(Statement* statement,
+ const AstRawString* arg = nullptr) const {
+ ExpressionStatement* e_stat = statement->AsExpressionStatement();
+ if (e_stat == nullptr) return false;
+ Literal* literal = e_stat->expression()->AsLiteral();
+ if (literal == nullptr || !literal->raw_value()->IsString()) return false;
+ return arg == nullptr || literal->raw_value()->AsString() == arg;
+ }
+
+ V8_INLINE static Expression* GetPropertyValue(LiteralProperty* property) {
+ return property->value();
+ }
+
+ V8_INLINE void GetDefaultStrings(
+ const AstRawString** default_string,
+ const AstRawString** star_default_star_string) {
+ *default_string = ast_value_factory()->default_string();
+ *star_default_star_string = ast_value_factory()->star_default_star_string();
+ }
+
+ // Functions for encapsulating the differences between parsing and preparsing;
+ // operations interleaved with the recursive descent.
+ V8_INLINE void PushLiteralName(const AstRawString* id) {
+ DCHECK_NOT_NULL(fni_);
+ fni_->PushLiteralName(id);
+ }
+
+ V8_INLINE void PushVariableName(const AstRawString* id) {
+ DCHECK_NOT_NULL(fni_);
+ fni_->PushVariableName(id);
+ }
+
+ V8_INLINE void PushPropertyName(Expression* expression) {
+ DCHECK_NOT_NULL(fni_);
+ if (expression->IsPropertyName()) {
+ fni_->PushLiteralName(expression->AsLiteral()->AsRawPropertyName());
+ } else {
+ fni_->PushLiteralName(ast_value_factory()->anonymous_function_string());
+ }
+ }
+
+ V8_INLINE void PushEnclosingName(const AstRawString* name) {
+ DCHECK_NOT_NULL(fni_);
+ fni_->PushEnclosingName(name);
+ }
+
+ V8_INLINE void AddFunctionForNameInference(FunctionLiteral* func_to_infer) {
+ DCHECK_NOT_NULL(fni_);
+ fni_->AddFunction(func_to_infer);
+ }
+
+ V8_INLINE void InferFunctionName() {
+ DCHECK_NOT_NULL(fni_);
+ fni_->Infer();
+ }
+
+ // If we assign a function literal to a property we pretenure the
+ // literal so it can be added as a constant function property.
+ V8_INLINE static void CheckAssigningFunctionLiteralToProperty(
+ Expression* left, Expression* right) {
+ DCHECK(left != NULL);
+ if (left->IsProperty() && right->IsFunctionLiteral()) {
+ right->AsFunctionLiteral()->set_pretenure();
+ }
+ }
+
+ // Determine if the expression is a variable proxy and mark it as being used
+ // in an assignment or with a increment/decrement operator.
+ V8_INLINE static Expression* MarkExpressionAsAssigned(
+ Expression* expression) {
+ VariableProxy* proxy =
+ expression != NULL ? expression->AsVariableProxy() : NULL;
+ if (proxy != NULL) proxy->set_is_assigned();
+ return 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);
+
+ // 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);
+
+ Expression* BuildIteratorResult(Expression* value, bool done);
+
+ // Generate AST node that throws a ReferenceError with the given type.
+ V8_INLINE Expression* NewThrowReferenceError(
+ MessageTemplate::Template message, int pos) {
+ return NewThrowError(Runtime::kNewReferenceError, message,
+ ast_value_factory()->empty_string(), 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.
+ V8_INLINE Expression* NewThrowSyntaxError(MessageTemplate::Template message,
+ const AstRawString* arg, int pos) {
+ return NewThrowError(Runtime::kNewSyntaxError, message, arg, pos);
+ }
+
+ // Generate AST node that throws a TypeError with the given
+ // type. Both arguments must be non-null (in the handle sense).
+ V8_INLINE Expression* NewThrowTypeError(MessageTemplate::Template message,
+ const AstRawString* arg, int pos) {
+ return NewThrowError(Runtime::kNewTypeError, message, arg, pos);
+ }
+
+ // Reporting errors.
+ V8_INLINE void ReportMessageAt(Scanner::Location source_location,
+ MessageTemplate::Template message,
+ const char* arg = NULL,
+ ParseErrorType error_type = kSyntaxError) {
+ if (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;
+ }
+ pending_error_handler_.ReportMessageAt(source_location.beg_pos,
+ source_location.end_pos, message,
+ arg, error_type);
+ }
+
+ V8_INLINE void ReportMessageAt(Scanner::Location source_location,
+ MessageTemplate::Template message,
+ const AstRawString* arg,
+ ParseErrorType error_type = kSyntaxError) {
+ if (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;
+ }
+ pending_error_handler_.ReportMessageAt(source_location.beg_pos,
+ source_location.end_pos, message,
+ arg, error_type);
+ }
+
+ // "null" return type creators.
+ V8_INLINE static const AstRawString* EmptyIdentifier() { return nullptr; }
+ V8_INLINE static bool IsEmptyIdentifier(const AstRawString* name) {
+ return name == nullptr;
+ }
+ V8_INLINE static Expression* EmptyExpression() { return nullptr; }
+ V8_INLINE static Literal* EmptyLiteral() { return nullptr; }
+ V8_INLINE static ObjectLiteralProperty* EmptyObjectLiteralProperty() {
+ return nullptr;
+ }
+ V8_INLINE static ClassLiteralProperty* EmptyClassLiteralProperty() {
+ return nullptr;
+ }
+ V8_INLINE static FunctionLiteral* EmptyFunctionLiteral() { return nullptr; }
+ V8_INLINE static Block* NullBlock() { return nullptr; }
+
+ V8_INLINE static bool IsEmptyExpression(Expression* expr) {
+ return expr == nullptr;
+ }
+
+ // Used in error return values.
+ V8_INLINE static ZoneList<Expression*>* NullExpressionList() {
+ return nullptr;
+ }
+ V8_INLINE static bool IsNullExpressionList(ZoneList<Expression*>* exprs) {
+ return exprs == nullptr;
+ }
+ V8_INLINE static ZoneList<Statement*>* NullStatementList() { return nullptr; }
+ V8_INLINE static bool IsNullStatementList(ZoneList<Statement*>* stmts) {
+ return stmts == nullptr;
+ }
+ V8_INLINE static Statement* NullStatement() { return nullptr; }
+ V8_INLINE bool IsNullStatement(Statement* stmt) { return stmt == nullptr; }
+ V8_INLINE bool IsEmptyStatement(Statement* stmt) {
+ DCHECK_NOT_NULL(stmt);
+ return stmt->IsEmpty();
+ }
+
+ // Non-NULL empty string.
+ V8_INLINE const AstRawString* EmptyIdentifierString() const {
+ return ast_value_factory()->empty_string();
+ }
+
+ // Odd-ball literal creators.
+ V8_INLINE Literal* GetLiteralTheHole(int position) {
+ return factory()->NewTheHoleLiteral(kNoSourcePosition);
+ }
+
+ // Producing data during the recursive descent.
+ V8_INLINE const AstRawString* GetSymbol() const {
+ const AstRawString* result = scanner()->CurrentSymbol(ast_value_factory());
+ DCHECK(result != NULL);
+ return result;
+ }
+
+ V8_INLINE const AstRawString* GetNextSymbol() const {
+ return scanner()->NextSymbol(ast_value_factory());
+ }
+
+ V8_INLINE const AstRawString* GetNumberAsSymbol() const {
+ double double_value = scanner()->DoubleValue();
+ char array[100];
+ const char* string = DoubleToCString(double_value, ArrayVector(array));
+ return ast_value_factory()->GetOneByteString(string);
+ }
+
+ V8_INLINE Expression* ThisExpression(int pos = kNoSourcePosition) {
+ return NewUnresolved(ast_value_factory()->this_string(), pos, pos + 4,
+ THIS_VARIABLE);
+ }
+
+ Expression* NewSuperPropertyReference(int pos);
+ Expression* NewSuperCallReference(int pos);
+ Expression* NewTargetExpression(int pos);
+ Expression* FunctionSentExpression(int pos);
+
+ Literal* ExpressionFromLiteral(Token::Value token, int pos);
+
+ V8_INLINE Expression* ExpressionFromIdentifier(
+ const AstRawString* name, int start_position, int end_position,
+ InferName infer = InferName::kYes) {
+ if (infer == InferName::kYes) {
+ fni_->PushVariableName(name);
+ }
+ return NewUnresolved(name, start_position, end_position);
+ }
+
+ V8_INLINE Expression* ExpressionFromString(int pos) {
+ const AstRawString* symbol = GetSymbol();
+ fni_->PushLiteralName(symbol);
+ return factory()->NewStringLiteral(symbol, pos);
+ }
+
+ V8_INLINE ZoneList<Expression*>* NewExpressionList(int size) const {
+ return new (zone()) ZoneList<Expression*>(size, zone());
+ }
+ V8_INLINE ZoneList<ObjectLiteral::Property*>* NewObjectPropertyList(
+ int size) const {
+ return new (zone()) ZoneList<ObjectLiteral::Property*>(size, zone());
+ }
+ V8_INLINE ZoneList<ClassLiteral::Property*>* NewClassPropertyList(
+ int size) const {
+ return new (zone()) ZoneList<ClassLiteral::Property*>(size, zone());
+ }
+ V8_INLINE ZoneList<Statement*>* NewStatementList(int size) const {
+ return new (zone()) ZoneList<Statement*>(size, zone());
+ }
+ V8_INLINE ZoneList<CaseClause*>* NewCaseClauseList(int size) const {
+ return new (zone()) ZoneList<CaseClause*>(size, zone());
+ }
+
+ V8_INLINE Expression* NewV8Intrinsic(const AstRawString* name,
+ ZoneList<Expression*>* args, int pos,
+ bool* ok);
+
+ V8_INLINE Statement* NewThrowStatement(Expression* exception, int pos) {
+ return factory()->NewExpressionStatement(
+ factory()->NewThrow(exception, pos), pos);
+ }
+
+ V8_INLINE void AddParameterInitializationBlock(
+ const ParserFormalParameters& parameters, ZoneList<Statement*>* body,
+ bool is_async, bool* ok) {
+ if (parameters.is_simple) return;
+ auto* init_block = BuildParameterInitializationBlock(parameters, ok);
+ if (!*ok) return;
+ if (is_async) {
+ init_block = BuildRejectPromiseOnException(init_block, ok);
+ if (!*ok) return;
+ }
+ if (init_block != nullptr) body->Add(init_block, zone());
+ }
+
+ V8_INLINE void 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()
+ : ast_value_factory()->empty_string();
+ parameters->params.Add(
+ ParserFormalParameters::Parameter(name, pattern, initializer,
+ initializer_end_position, is_rest),
+ parameters->scope->zone());
+ }
+
+ V8_INLINE void DeclareFormalParameter(
+ DeclarationScope* scope,
+ const ParserFormalParameters::Parameter& parameter) {
+ bool is_duplicate = false;
+ bool is_simple = classifier()->is_simple_parameter_list();
+ auto name = is_simple || parameter.is_rest
+ ? parameter.name
+ : 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, ast_value_factory());
+ if (is_duplicate) {
+ classifier()->RecordDuplicateFormalParameterError(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 DeclareArrowFunctionFormalParameters(ParserFormalParameters* parameters,
+ Expression* params,
+ const Scanner::Location& params_loc,
+ Scanner::Location* duplicate_loc,
+ bool* ok);
+
+ void ReindexLiterals(const ParserFormalParameters& parameters);
+
+ V8_INLINE Expression* NoTemplateTag() { return NULL; }
+ V8_INLINE static bool IsTaggedTemplate(const Expression* tag) {
+ return tag != NULL;
+ }
+
+ V8_INLINE void MaterializeUnspreadArgumentsLiterals(int count) {}
+
+ Expression* ExpressionListToExpression(ZoneList<Expression*>* args);
+
+ void AddAccessorPrefixToFunctionName(bool is_get, FunctionLiteral* function,
+ const AstRawString* name);
+
+ void SetFunctionNameFromPropertyName(ObjectLiteralProperty* property,
+ const AstRawString* name);
+
+ void SetFunctionNameFromIdentifierRef(Expression* value,
+ Expression* identifier);
+
+ V8_INLINE ZoneList<typename ExpressionClassifier::Error>*
+ GetReportedErrorList() const {
+ return function_state_->GetReportedErrorList();
+ }
+
+ V8_INLINE ZoneList<Expression*>* GetNonPatternList() const {
+ return function_state_->non_patterns_to_rewrite();
+ }
+
+ V8_INLINE void CountUsage(v8::Isolate::UseCounterFeature feature) {
+ ++use_counts_[feature];
+ }
+
+ // Parser's private field members.
+ friend class DiscardableZoneScope; // Uses reusable_preparser_.
+ // FIXME(marja): Make reusable_preparser_ always use its own temp Zone (call
+ // DeleteAll after each function), so this won't be needed.
+
Scanner scanner_;
PreParser* reusable_preparser_;
Scope* original_scope_; // for ES5 function declarations in sloppy eval
- Target* target_stack_; // for break, continue statements
+
+ friend class ParserTarget;
+ friend class ParserTargetScope;
+ ParserTarget* target_stack_; // for break, continue statements
+
ScriptCompiler::CompileOptions compile_options_;
ParseData* cached_parse_data_;
@@ -908,107 +1093,8 @@
HistogramTimer* pre_parse_timer_;
bool parsing_on_main_thread_;
-
-#ifdef DEBUG
- void Print(AstNode* node);
-#endif // DEBUG
};
-bool ParserBaseTraits<Parser>::IsFutureStrictReserved(
- const AstRawString* identifier) const {
- return delegate()->scanner()->IdentifierIsFutureStrictReserved(identifier);
-}
-
-const AstRawString* ParserBaseTraits<Parser>::EmptyIdentifierString() const {
- return delegate()->ast_value_factory()->empty_string();
-}
-
-
-// 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);
-};
-
-void ParserBaseTraits<Parser>::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()
- : delegate()->ast_value_factory()->empty_string();
- parameters->params.Add(
- ParserFormalParameters::Parameter(name, pattern, initializer,
- initializer_end_position, is_rest),
- parameters->scope->zone());
-}
-
-void ParserBaseTraits<Parser>::DeclareFormalParameter(
- DeclarationScope* scope, const ParserFormalParameters::Parameter& parameter,
- Type::ExpressionClassifier* classifier) {
- bool is_duplicate = false;
- bool is_simple = classifier->is_simple_parameter_list();
- auto name = is_simple || parameter.is_rest
- ? parameter.name
- : delegate()->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, delegate()->ast_value_factory());
- if (is_duplicate) {
- classifier->RecordDuplicateFormalParameterError(
- delegate()->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 ParserBaseTraits<Parser>::AddParameterInitializationBlock(
- const ParserFormalParameters& parameters,
- ZoneList<v8::internal::Statement*>* body, bool is_async, bool* ok) {
- if (!parameters.is_simple) {
- auto* init_block =
- delegate()->BuildParameterInitializationBlock(parameters, ok);
- if (!*ok) return;
-
- if (is_async) {
- init_block = delegate()->BuildRejectPromiseOnException(init_block);
- }
-
- if (init_block != nullptr) {
- body->Add(init_block, delegate()->zone());
- }
- }
-}
-
} // namespace internal
} // namespace v8
diff --git a/src/parsing/pattern-rewriter.cc b/src/parsing/pattern-rewriter.cc
index 1831a29..7898f87 100644
--- a/src/parsing/pattern-rewriter.cc
+++ b/src/parsing/pattern-rewriter.cc
@@ -12,7 +12,8 @@
namespace internal {
void Parser::PatternRewriter::DeclareAndInitializeVariables(
- Block* block, const DeclarationDescriptor* declaration_descriptor,
+ Parser* parser, Block* block,
+ const DeclarationDescriptor* declaration_descriptor,
const DeclarationParsingResult::Declaration* declaration,
ZoneList<const AstRawString*>* names, bool* ok) {
PatternRewriter rewriter;
@@ -20,7 +21,7 @@
DCHECK(block->ignore_completion_value());
rewriter.scope_ = declaration_descriptor->scope;
- rewriter.parser_ = declaration_descriptor->parser;
+ rewriter.parser_ = parser;
rewriter.context_ = BINDING;
rewriter.pattern_ = declaration->pattern;
rewriter.initializer_position_ = declaration->initializer_position;
@@ -36,11 +37,12 @@
void Parser::PatternRewriter::RewriteDestructuringAssignment(
Parser* parser, RewritableExpression* to_rewrite, Scope* scope) {
- PatternRewriter rewriter;
-
+ DCHECK(!scope->HasBeenRemoved());
DCHECK(!to_rewrite->is_rewritten());
bool ok = true;
+
+ PatternRewriter rewriter;
rewriter.scope_ = scope;
rewriter.parser_ = parser;
rewriter.context_ = ASSIGNMENT;
@@ -139,23 +141,16 @@
// 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 = descriptor_->scope->NewUnresolved(
- factory(), name, parser_->scanner()->location().beg_pos,
+ VariableProxy* proxy = factory()->NewVariableProxy(
+ name, NORMAL_VARIABLE, parser_->scanner()->location().beg_pos,
parser_->scanner()->location().end_pos);
Declaration* declaration = factory()->NewVariableDeclaration(
proxy, descriptor_->scope, descriptor_->declaration_pos);
- Variable* var = parser_->Declare(declaration, descriptor_->declaration_kind,
- descriptor_->mode,
- DefaultInitializationFlag(descriptor_->mode),
- ok_, descriptor_->hoist_scope);
+ Variable* var = parser_->Declare(
+ declaration, descriptor_->declaration_kind, descriptor_->mode,
+ Variable::DefaultInitializationFlag(descriptor_->mode), ok_,
+ descriptor_->hoist_scope);
if (!*ok_) return;
DCHECK_NOT_NULL(var);
DCHECK(proxy->is_resolved());
@@ -267,12 +262,14 @@
void Parser::PatternRewriter::VisitRewritableExpression(
RewritableExpression* node) {
// If this is not a destructuring assignment...
- if (!IsAssignmentContext() || !node->expression()->IsAssignment()) {
+ if (!IsAssignmentContext()) {
// Mark the node as rewritten to prevent redundant rewriting, and
// perform BindingPattern rewriting
DCHECK(!node->is_rewritten());
node->Rewrite(node->expression());
return Visit(node->expression());
+ } else if (!node->expression()->IsAssignment()) {
+ return Visit(node->expression());
}
if (node->is_rewritten()) return;
@@ -374,7 +371,7 @@
auto temp = *temp_var = CreateTempVar(current_value_);
auto iterator = CreateTempVar(parser_->GetIterator(
- factory()->NewVariableProxy(temp), factory(), kNoSourcePosition));
+ factory()->NewVariableProxy(temp), kNoSourcePosition));
auto done =
CreateTempVar(factory()->NewBooleanLiteral(false, kNoSourcePosition));
auto result = CreateTempVar();
@@ -601,8 +598,9 @@
Expression* closing_condition = factory()->NewUnaryOperation(
Token::NOT, factory()->NewVariableProxy(done), nopos);
- parser_->FinalizeIteratorUse(completion, closing_condition, iterator, block_,
- target);
+
+ parser_->FinalizeIteratorUse(scope(), completion, closing_condition, iterator,
+ block_, target);
block_ = target;
}
diff --git a/src/parsing/preparser.cc b/src/parsing/preparser.cc
index b1bbbf6..88470f7 100644
--- a/src/parsing/preparser.cc
+++ b/src/parsing/preparser.cc
@@ -10,6 +10,7 @@
#include "src/conversions.h"
#include "src/globals.h"
#include "src/list.h"
+#include "src/parsing/duplicate-finder.h"
#include "src/parsing/parser-base.h"
#include "src/parsing/preparse-data-format.h"
#include "src/parsing/preparse-data.h"
@@ -28,34 +29,18 @@
// 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 Statement::Default(); \
+#define CHECK_OK_VALUE(x) ok); \
+ if (!*ok) return x; \
((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
+#define CHECK_OK CHECK_OK_VALUE(Expression::Default())
+#define CHECK_OK_VOID CHECK_OK_VALUE(this->Void())
-void ParserBaseTraits<PreParser>::ReportMessageAt(
- Scanner::Location source_location, MessageTemplate::Template message,
- const char* arg, ParseErrorType error_type) {
- delegate()->log_->LogMessage(source_location.beg_pos, source_location.end_pos,
- message, arg, error_type);
-}
+namespace {
-void ParserBaseTraits<PreParser>::ReportMessageAt(
- Scanner::Location source_location, MessageTemplate::Template message,
- const AstRawString* arg, ParseErrorType error_type) {
- UNREACHABLE();
-}
-
-PreParserIdentifier ParserBaseTraits<PreParser>::GetSymbol(
- Scanner* scanner) const {
+PreParserIdentifier GetSymbolHelper(Scanner* scanner) {
switch (scanner->current_token()) {
case Token::ENUM:
return PreParserIdentifier::Enum();
@@ -86,49 +71,51 @@
}
}
-PreParserExpression ParserBaseTraits<PreParser>::ExpressionFromString(
- int pos, Scanner* scanner, PreParserFactory* factory) const {
- if (scanner->UnescapedLiteralMatches("use strict", 10)) {
- return PreParserExpression::UseStrictStringLiteral();
+} // unnamed namespace
+
+PreParserIdentifier PreParser::GetSymbol() const {
+ PreParserIdentifier symbol = GetSymbolHelper(scanner());
+ if (track_unresolved_variables_) {
+ const AstRawString* result = scanner()->CurrentSymbol(ast_value_factory());
+ DCHECK_NOT_NULL(result);
+ symbol.string_ = result;
}
- return PreParserExpression::StringLiteral();
+ return symbol;
}
PreParser::PreParseResult PreParser::PreParseLazyFunction(
- LanguageMode language_mode, FunctionKind kind, bool has_simple_parameters,
- bool parsing_module, ParserRecorder* log, Scanner::BookmarkScope* bookmark,
- int* use_counts) {
+ DeclarationScope* function_scope, bool parsing_module, ParserRecorder* log,
+ bool is_inner_function, bool may_abort, int* use_counts) {
+ DCHECK_EQ(FUNCTION_SCOPE, function_scope->scope_type());
parsing_module_ = parsing_module;
log_ = log;
use_counts_ = use_counts;
- // Lazy functions always have trivial outer scopes (no with/catch scopes).
+ DCHECK(!track_unresolved_variables_);
+ track_unresolved_variables_ = is_inner_function;
+
+ // The caller passes the function_scope which is not yet inserted into the
+ // scope_state_. All scopes above the function_scope are ignored by the
+ // PreParser.
DCHECK_NULL(scope_state_);
- DeclarationScope* top_scope = NewScriptScope();
- FunctionState top_state(&function_state_, &scope_state_, top_scope,
- kNormalFunction);
- scope()->SetLanguageMode(language_mode);
- DeclarationScope* function_scope = NewFunctionScope(kind);
- if (!has_simple_parameters) function_scope->SetHasNonSimpleParameters();
- FunctionState function_state(&function_state_, &scope_state_, function_scope,
- kind);
+ FunctionState function_state(&function_state_, &scope_state_, function_scope);
DCHECK_EQ(Token::LBRACE, scanner()->current_token());
bool ok = true;
int start_position = peek_position();
- ParseLazyFunctionLiteralBody(&ok, bookmark);
+ LazyParsingResult result = ParseLazyFunctionLiteralBody(may_abort, &ok);
use_counts_ = nullptr;
- if (bookmark && bookmark->HasBeenReset()) {
- // Do nothing, as we've just aborted scanning this function.
+ track_unresolved_variables_ = false;
+ if (result == kLazyParsingAborted) {
+ return kPreParseAbort;
} 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())) {
+ if (is_strict(function_scope->language_mode())) {
int end_pos = scanner()->location().end_pos;
CheckStrictOctalLiteral(start_position, end_pos, &ok);
- CheckDecimalLiteralWithLeadingZero(use_counts, start_position, end_pos);
- if (!ok) return kPreParseSuccess;
+ CheckDecimalLiteralWithLeadingZero(start_position, end_pos);
}
}
return kPreParseSuccess;
@@ -148,908 +135,6 @@
// 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 ParseHoistableDeclaration(ok);
- case Token::CLASS:
- return ParseClassDeclaration(ok);
- case Token::CONST:
- return ParseVariableStatement(kStatementListItem, ok);
- case Token::LET:
- if (IsNextLetKeyword()) {
- return ParseVariableStatement(kStatementListItem, ok);
- }
- break;
- case Token::ASYNC:
- if (allow_harmony_async_await() && PeekAhead() == Token::FUNCTION &&
- !scanner()->HasAnyLineTerminatorAfterNext()) {
- Consume(Token::ASYNC);
- return ParseAsyncFunctionDeclaration(ok);
- }
- /* falls through */
- default:
- break;
- }
- return ParseStatement(kAllowLabelledFunctionStatement, 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();
- Statement statement = ParseStatementListItem(CHECK_OK_CUSTOM(Void));
-
- if (directive_prologue) {
- bool use_strict_found = statement.IsUseStrictLiteral();
-
- if (use_strict_found) {
- scope()->SetLanguageMode(
- static_cast<LanguageMode>(scope()->language_mode() | STRICT));
- } else if (!statement.IsStringLiteral()) {
- directive_prologue = false;
- }
-
- if (use_strict_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
- ReportMessageAt(token_loc,
- MessageTemplate::kIllegalLanguageModeDirective,
- "use strict");
- *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;
- }
- }
-}
-
-
-PreParser::Statement PreParser::ParseStatement(
- AllowLabelledFunctionStatement allow_function, bool* ok) {
- // Statement ::
- // EmptyStatement
- // ...
-
- if (peek() == Token::SEMICOLON) {
- Next();
- return Statement::Default();
- }
- return ParseSubStatement(allow_function, ok);
-}
-
-PreParser::Statement PreParser::ParseScopedStatement(bool legacy, bool* ok) {
- if (is_strict(language_mode()) || peek() != Token::FUNCTION ||
- (legacy && allow_harmony_restrictive_declarations())) {
- return ParseSubStatement(kDisallowLabelledFunctionStatement, ok);
- } else {
- BlockState block_state(&scope_state_);
- return ParseFunctionDeclaration(ok);
- }
-}
-
-PreParser::Statement PreParser::ParseSubStatement(
- AllowLabelledFunctionStatement allow_function, 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:
- 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:
- // FunctionDeclaration only allowed as a StatementListItem, not in
- // an arbitrary Statement position. Exceptions such as
- // ES#sec-functiondeclarations-in-ifstatement-statement-clauses
- // are handled by calling ParseScopedStatement rather than
- // ParseSubStatement directly.
- ReportMessageAt(scanner()->peek_location(),
- is_strict(language_mode())
- ? MessageTemplate::kStrictFunction
- : MessageTemplate::kSloppyFunction);
- *ok = false;
- return Statement::Default();
-
- case Token::DEBUGGER:
- return ParseDebuggerStatement(ok);
-
- case Token::VAR:
- return ParseVariableStatement(kStatement, ok);
-
- default:
- return ParseExpressionOrLabelledStatement(allow_function, ok);
- }
-}
-
-PreParser::Statement PreParser::ParseHoistableDeclaration(
- int pos, ParseFunctionFlags flags, bool* ok) {
- const bool is_generator = flags & ParseFunctionFlags::kIsGenerator;
- const bool is_async = flags & ParseFunctionFlags::kIsAsync;
- DCHECK(!is_generator || !is_async);
-
- 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
- : is_async ? FunctionKind::kAsyncFunction
- : FunctionKind::kNormalFunction,
- pos, FunctionLiteral::kDeclaration, language_mode(),
- CHECK_OK);
- return Statement::FunctionDeclaration();
-}
-
-PreParser::Statement PreParser::ParseAsyncFunctionDeclaration(bool* ok) {
- // AsyncFunctionDeclaration ::
- // async [no LineTerminator here] function BindingIdentifier[Await]
- // ( FormalParameters[Await] ) { AsyncFunctionBody }
- DCHECK_EQ(scanner()->current_token(), Token::ASYNC);
- int pos = position();
- Expect(Token::FUNCTION, CHECK_OK);
- ParseFunctionFlags flags = ParseFunctionFlags::kIsAsync;
- return ParseHoistableDeclaration(pos, flags, ok);
-}
-
-PreParser::Statement PreParser::ParseHoistableDeclaration(bool* ok) {
- // FunctionDeclaration ::
- // 'function' Identifier '(' FormalParameterListopt ')' '{' FunctionBody '}'
- // GeneratorDeclaration ::
- // 'function' '*' Identifier '(' FormalParameterListopt ')'
- // '{' FunctionBody '}'
-
- Expect(Token::FUNCTION, CHECK_OK);
- int pos = position();
- ParseFunctionFlags flags = ParseFunctionFlags::kIsNormal;
- if (Check(Token::MUL)) {
- flags |= ParseFunctionFlags::kIsGenerator;
- }
- return ParseHoistableDeclaration(pos, flags, ok);
-}
-
-
-PreParser::Statement PreParser::ParseClassDeclaration(bool* ok) {
- Expect(Token::CLASS, CHECK_OK);
-
- int pos = position();
- bool is_strict_reserved = false;
- Identifier name =
- ParseIdentifierOrStrictReservedWord(&is_strict_reserved, CHECK_OK);
- ParseClassLiteral(nullptr, 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();
- {
- BlockState block_state(&scope_state_);
- 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) {
- Consume(Token::VAR);
- } else if (peek() == Token::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);
- DCHECK(var_context != kStatement);
- require_initializer = true;
- lexical = true;
- } else if (peek() == Token::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(this);
- pattern = ParsePrimaryExpression(&pattern_classifier, CHECK_OK);
-
- ValidateBindingPattern(&pattern_classifier, CHECK_OK);
- if (lexical) {
- ValidateLetPattern(&pattern_classifier, CHECK_OK);
- }
- }
-
- is_pattern = pattern.IsObjectLiteral() || pattern.IsArrayLiteral();
-
- Scanner::Location variable_loc = scanner()->location();
- nvars++;
- if (Check(Token::ASSIGN)) {
- ExpressionClassifier classifier(this);
- 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) &&
- (var_context != kForStatement || !PeekInOrOf())) {
- 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::ParseFunctionDeclaration(bool* ok) {
- Consume(Token::FUNCTION);
- int pos = position();
- ParseFunctionFlags flags = ParseFunctionFlags::kIsNormal;
- if (Check(Token::MUL)) {
- flags |= ParseFunctionFlags::kIsGenerator;
- if (allow_harmony_restrictive_declarations()) {
- ReportMessageAt(scanner()->location(),
- MessageTemplate::kGeneratorInLegacyContext);
- *ok = false;
- return Statement::Default();
- }
- }
- return ParseHoistableDeclaration(pos, flags, ok);
-}
-
-PreParser::Statement PreParser::ParseExpressionOrLabelledStatement(
- AllowLabelledFunctionStatement allow_function, 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();
-
- default:
- break;
- }
-
- bool starts_with_identifier = peek_any_identifier();
- ExpressionClassifier classifier(this);
- 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().IsEnum());
- DCHECK(!parsing_module_ || !expr.AsIdentifier().IsAwait());
- DCHECK(is_sloppy(language_mode()) ||
- !IsFutureStrictReserved(expr.AsIdentifier()));
- Consume(Token::COLON);
- // ES#sec-labelled-function-declarations Labelled Function Declarations
- if (peek() == Token::FUNCTION && is_sloppy(language_mode())) {
- if (allow_function == kAllowLabelledFunctionStatement) {
- return ParseFunctionDeclaration(ok);
- } else {
- return ParseScopedStatement(true, ok);
- }
- }
- Statement statement =
- ParseStatement(kDisallowLabelledFunctionStatement, 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.
- 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 = ParseScopedStatement(false, CHECK_OK);
- if (peek() == Token::ELSE) {
- Next();
- Statement else_stat = ParseScopedStatement(false, 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);
-
- // 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) {
- // Because of the return code rewriting that happens in case of a subclass
- // constructor we don't want to accept tail calls, therefore we don't set
- // ReturnExprScope to kInsideValidReturnStatement here.
- ReturnExprContext return_expr_context =
- IsSubclassConstructor(function_state_->kind())
- ? function_state_->return_expr_context()
- : ReturnExprContext::kInsideValidReturnStatement;
-
- ReturnExprScope maybe_allow_tail_calls(function_state_,
- return_expr_context);
- 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(WITH_SCOPE);
- BlockState block_state(&scope_state_, with_scope);
- ParseScopedStatement(true, 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);
-
- {
- BlockState cases_block_state(&scope_state_);
- 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();
- }
- }
- }
- Expect(Token::RBRACE, ok);
- return Statement::Default();
-}
-
-
-PreParser::Statement PreParser::ParseDoWhileStatement(bool* ok) {
- // DoStatement ::
- // 'do' Statement 'while' '(' Expression ')' ';'
-
- Expect(Token::DO, CHECK_OK);
- ParseScopedStatement(true, 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);
- ParseScopedStatement(true, ok);
- return Statement::Default();
-}
-
-
-PreParser::Statement PreParser::ParseForStatement(bool* ok) {
- // ForStatement ::
- // 'for' '(' Expression? ';' Expression? ';' Expression? ')' Statement
-
- // Create an in-between scope for let-bound iteration variables.
- bool has_lexical = false;
-
- BlockState block_state(&scope_state_);
- Expect(Token::FOR, CHECK_OK);
- Expect(Token::LPAREN, CHECK_OK);
- if (peek() != Token::SEMICOLON) {
- ForEachStatement::VisitMode mode;
- if (peek() == Token::VAR || peek() == Token::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);
- if (is_lexical) has_lexical = true;
- if (CheckInOrOf(&mode, ok)) {
- if (!*ok) return Statement::Default();
- if (decl_count != 1) {
- ReportMessageAt(bindings_loc,
- MessageTemplate::kForInOfLoopMultiBindings,
- ForEachStatement::VisitModeString(mode));
- *ok = false;
- return Statement::Default();
- }
- if (first_initializer_loc.IsValid() &&
- (is_strict(language_mode()) || mode == ForEachStatement::ITERATE ||
- is_lexical || is_binding_pattern || allow_harmony_for_in())) {
- // Only increment the use count if we would have let this through
- // without the flag.
- if (use_counts_ != nullptr && allow_harmony_for_in()) {
- ++use_counts_[v8::Isolate::kForInInitializer];
- }
- ReportMessageAt(first_initializer_loc,
- MessageTemplate::kForInOfLoopInitializer,
- ForEachStatement::VisitModeString(mode));
- *ok = false;
- return Statement::Default();
- }
-
- if (mode == ForEachStatement::ITERATE) {
- ExpressionClassifier classifier(this);
- ParseAssignmentExpression(true, &classifier, CHECK_OK);
- RewriteNonPattern(&classifier, CHECK_OK);
- } else {
- ParseExpression(true, CHECK_OK);
- }
-
- Expect(Token::RPAREN, CHECK_OK);
- {
- ReturnExprScope no_tail_calls(function_state_,
- ReturnExprContext::kInsideForInOfBody);
- ParseScopedStatement(true, CHECK_OK);
- }
- return Statement::Default();
- }
- } else {
- int lhs_beg_pos = peek_position();
- ExpressionClassifier classifier(this);
- Expression lhs = ParseExpression(false, &classifier, CHECK_OK);
- int lhs_end_pos = scanner()->location().end_pos;
- bool is_for_each = CheckInOrOf(&mode, CHECK_OK);
- bool is_destructuring = is_for_each &&
- (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);
- }
-
- if (mode == ForEachStatement::ITERATE) {
- ExpressionClassifier classifier(this);
- ParseAssignmentExpression(true, &classifier, CHECK_OK);
- RewriteNonPattern(&classifier, CHECK_OK);
- } else {
- ParseExpression(true, CHECK_OK);
- }
-
- Expect(Token::RPAREN, CHECK_OK);
- {
- BlockState block_state(&scope_state_);
- ParseScopedStatement(true, CHECK_OK);
- }
- return Statement::Default();
- }
- }
- }
-
- // Parsed initializer at this point.
- 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 = scope();
- // TODO(verwaest): Allocate this through a ScopeState as well.
- if (has_lexical) inner_scope = NewScopeWithParent(inner_scope, BLOCK_SCOPE);
-
- {
- BlockState block_state(&scope_state_, inner_scope);
-
- 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);
-
- ParseScopedStatement(true, 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);
-
- {
- ReturnExprScope no_tail_calls(function_state_,
- ReturnExprContext::kInsideTryBlock);
- ParseBlock(CHECK_OK);
- }
-
- Token::Value tok = peek();
- if (tok != Token::CATCH && tok != Token::FINALLY) {
- ReportMessageAt(scanner()->location(), MessageTemplate::kNoCatchOrFinally);
- *ok = false;
- return Statement::Default();
- }
- TailCallExpressionList tail_call_expressions_in_catch_block(zone());
- bool catch_block_exists = false;
- if (tok == Token::CATCH) {
- Consume(Token::CATCH);
- Expect(Token::LPAREN, CHECK_OK);
- Scope* catch_scope = NewScope(CATCH_SCOPE);
- ExpressionClassifier pattern_classifier(this);
- ParsePrimaryExpression(&pattern_classifier, CHECK_OK);
- ValidateBindingPattern(&pattern_classifier, CHECK_OK);
- Expect(Token::RPAREN, CHECK_OK);
- {
- CollectExpressionsInTailPositionToListScope
- collect_tail_call_expressions_scope(
- function_state_, &tail_call_expressions_in_catch_block);
- BlockState block_state(&scope_state_, catch_scope);
- {
- BlockState block_state(&scope_state_);
- ParseBlock(CHECK_OK);
- }
- }
- catch_block_exists = true;
- tok = peek();
- }
- if (tok == Token::FINALLY) {
- Consume(Token::FINALLY);
- ParseBlock(CHECK_OK);
- if (FLAG_harmony_explicit_tailcalls && catch_block_exists &&
- tail_call_expressions_in_catch_block.has_explicit_tail_calls()) {
- // TODO(ishell): update chapter number.
- // ES8 XX.YY.ZZ
- ReportMessageAt(tail_call_expressions_in_catch_block.location(),
- MessageTemplate::kUnexpectedTailCallInCatchBlock);
- *ok = false;
- return Statement::Default();
- }
- }
- 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();
-}
-
-
-// Redefinition of CHECK_OK for parsing expressions.
-#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,
@@ -1059,11 +144,11 @@
// '(' FormalParameterList? ')' '{' FunctionBody '}'
// Parse function body.
+ PreParserStatementList body;
bool outer_is_script_scope = scope()->is_script_scope();
DeclarationScope* function_scope = NewFunctionScope(kind);
function_scope->SetLanguageMode(language_mode);
- FunctionState function_state(&function_state_, &scope_state_, function_scope,
- kind);
+ FunctionState function_state(&function_state_, &scope_state_, function_scope);
DuplicateFinder duplicate_finder(scanner()->unicode_cache());
ExpressionClassifier formals_classifier(this, &duplicate_finder);
@@ -1071,7 +156,7 @@
int start_position = scanner()->location().beg_pos;
function_scope->set_start_position(start_position);
PreParserFormalParameters formals(function_scope);
- ParseFormalParameterList(&formals, &formals_classifier, CHECK_OK);
+ ParseFormalParameterList(&formals, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
int formals_end_position = scanner()->location().end_pos;
@@ -1085,9 +170,9 @@
Expect(Token::LBRACE, CHECK_OK);
if (is_lazily_parsed) {
- ParseLazyFunctionLiteralBody(CHECK_OK);
+ ParseLazyFunctionLiteralBody(false, CHECK_OK);
} else {
- ParseStatementList(Token::RBRACE, CHECK_OK);
+ ParseStatementList(body, Token::RBRACE, CHECK_OK);
}
Expect(Token::RBRACE, CHECK_OK);
@@ -1100,52 +185,24 @@
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);
+ ValidateFormalParameters(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);
- CheckDecimalLiteralWithLeadingZero(use_counts_, start_position,
- end_position);
+ CheckDecimalLiteralWithLeadingZero(start_position, end_position);
}
return Expression::Default();
}
-PreParser::Expression PreParser::ParseAsyncFunctionExpression(bool* ok) {
- // AsyncFunctionDeclaration ::
- // async [no LineTerminator here] function ( FormalParameters[Await] )
- // { AsyncFunctionBody }
- //
- // async [no LineTerminator here] function BindingIdentifier[Await]
- // ( FormalParameters[Await] ) { AsyncFunctionBody }
- int pos = position();
- Expect(Token::FUNCTION, CHECK_OK);
- bool is_strict_reserved = false;
- Identifier name;
- FunctionLiteral::FunctionType type = FunctionLiteral::kAnonymousExpression;
-
- if (peek_any_identifier()) {
- type = FunctionLiteral::kNamedExpression;
- name = ParseIdentifierOrStrictReservedWord(FunctionKind::kAsyncFunction,
- &is_strict_reserved, CHECK_OK);
- }
-
- ParseFunctionLiteral(name, scanner()->location(),
- is_strict_reserved ? kFunctionNameIsStrictReserved
- : kFunctionNameValidityUnknown,
- FunctionKind::kAsyncFunction, pos, type, language_mode(),
- CHECK_OK);
- return Expression::Default();
-}
-
-void PreParser::ParseLazyFunctionLiteralBody(bool* ok,
- Scanner::BookmarkScope* bookmark) {
+PreParser::LazyParsingResult PreParser::ParseLazyFunctionLiteralBody(
+ bool may_abort, bool* ok) {
int body_start = position();
- ParseStatementList(Token::RBRACE, ok, bookmark);
- if (!*ok) return;
- if (bookmark && bookmark->HasBeenReset()) return;
+ PreParserStatementList body;
+ LazyParsingResult result = ParseStatementList(
+ body, Token::RBRACE, may_abort, CHECK_OK_VALUE(kLazyParsingComplete));
+ if (result == kLazyParsingAborted) return result;
// Position right after terminal '}'.
DCHECK_EQ(Token::RBRACE, scanner()->peek());
@@ -1156,113 +213,45 @@
function_state_->materialized_literal_count(),
function_state_->expected_property_count(), language_mode(),
scope->uses_super_property(), scope->calls_eval());
+ return kLazyParsingComplete;
}
-PreParserExpression PreParser::ParseClassLiteral(
- ExpressionClassifier* classifier, 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();
+PreParserExpression PreParser::ExpressionFromIdentifier(
+ PreParserIdentifier name, int start_position, int end_position,
+ InferName infer) {
+ if (track_unresolved_variables_) {
+ AstNodeFactory factory(ast_value_factory());
+ // Setting the Zone is necessary because zone_ might be the temp Zone, and
+ // AstValueFactory doesn't know about it.
+ factory.set_zone(zone());
+ DCHECK_NOT_NULL(name.string_);
+ scope()->NewUnresolved(&factory, name.string_, start_position, end_position,
+ NORMAL_VARIABLE);
}
- if (IsEvalOrArguments(name)) {
- ReportMessageAt(class_name_location, MessageTemplate::kStrictEvalArguments);
- *ok = false;
- return EmptyExpression();
- }
+ return PreParserExpression::FromIdentifier(name);
+}
- LanguageMode class_language_mode = language_mode();
- BlockState block_state(&scope_state_);
- scope()->SetLanguageMode(
- static_cast<LanguageMode>(class_language_mode | STRICT));
- // TODO(marja): Make PreParser use scope names too.
- // this->scope()->SetScopeName(name);
+void PreParser::DeclareAndInitializeVariables(
+ PreParserStatement block,
+ const DeclarationDescriptor* declaration_descriptor,
+ const DeclarationParsingResult::Declaration* declaration,
+ ZoneList<const AstRawString*>* names, bool* ok) {
+ if (declaration->pattern.string_) {
+ /* Mimic what Parser does when declaring variables (see
+ Parser::PatternRewriter::VisitVariableProxy).
- bool has_extends = Check(Token::EXTENDS);
- if (has_extends) {
- ExpressionClassifier extends_classifier(this);
- ParseLeftHandSideExpression(&extends_classifier, CHECK_OK);
- CheckNoTailCallExpressions(&extends_classifier, CHECK_OK);
- ValidateExpression(&extends_classifier, CHECK_OK);
- if (classifier != nullptr) {
- classifier->Accumulate(&extends_classifier,
- ExpressionClassifier::ExpressionProductions);
+ var + no initializer -> RemoveUnresolved
+ let + no initializer -> RemoveUnresolved
+ var + initializer -> RemoveUnresolved followed by NewUnresolved
+ let + initializer -> RemoveUnresolved
+ */
+
+ if (declaration->initializer.IsEmpty() ||
+ declaration_descriptor->mode == VariableMode::LET) {
+ declaration_descriptor->scope->RemoveUnresolved(
+ declaration->pattern.string_);
}
}
-
- 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;
- bool is_computed_name = false; // Classes do not care about computed
- // property names here.
- Identifier name;
- ExpressionClassifier property_classifier(this);
- ParsePropertyDefinition(
- &checker, in_class, has_extends, MethodKind::kNormal, &is_computed_name,
- &has_seen_constructor, &property_classifier, &name, CHECK_OK);
- ValidateExpression(&property_classifier, CHECK_OK);
- if (classifier != nullptr) {
- classifier->Accumulate(&property_classifier,
- ExpressionClassifier::ExpressionProductions);
- }
- }
-
- 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(this);
- 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);
- while (peek() != Token::RBRACE) {
- ParseStatementListItem(CHECK_OK);
- }
- Expect(Token::RBRACE, CHECK_OK);
- return PreParserExpression::Default();
-}
-
-void PreParser::ParseAsyncArrowSingleExpressionBody(
- PreParserStatementList body, bool accept_IN,
- ExpressionClassifier* classifier, int pos, bool* ok) {
- scope()->ForceContextAllocation();
-
- PreParserExpression return_value =
- ParseAssignmentExpression(accept_IN, classifier, CHECK_OK_CUSTOM(Void));
-
- body->Add(PreParserStatement::ExpressionStatement(return_value), zone());
}
#undef CHECK_OK
diff --git a/src/parsing/preparser.h b/src/parsing/preparser.h
index 3f268ee..4b54748 100644
--- a/src/parsing/preparser.h
+++ b/src/parsing/preparser.h
@@ -6,18 +6,18 @@
#define V8_PARSING_PREPARSER_H
#include "src/ast/scopes.h"
-#include "src/bailout-reason.h"
-#include "src/base/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 {
+// Whereas the Parser generates AST during the recursive descent,
+// the 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.
class PreParserIdentifier {
public:
@@ -25,6 +25,9 @@
static PreParserIdentifier Default() {
return PreParserIdentifier(kUnknownIdentifier);
}
+ static PreParserIdentifier Empty() {
+ return PreParserIdentifier(kEmptyIdentifier);
+ }
static PreParserIdentifier Eval() {
return PreParserIdentifier(kEvalIdentifier);
}
@@ -64,6 +67,7 @@
static PreParserIdentifier Async() {
return PreParserIdentifier(kAsyncIdentifier);
}
+ bool IsEmpty() const { return type_ == kEmptyIdentifier; }
bool IsEval() const { return type_ == kEvalIdentifier; }
bool IsArguments() const { return type_ == kArgumentsIdentifier; }
bool IsEvalOrArguments() const { return IsEval() || IsArguments(); }
@@ -91,6 +95,7 @@
private:
enum Type {
+ kEmptyIdentifier,
kUnknownIdentifier,
kFutureReservedIdentifier,
kFutureStrictReservedIdentifier,
@@ -107,19 +112,23 @@
kAsyncIdentifier
};
- explicit PreParserIdentifier(Type type) : type_(type) {}
+ explicit PreParserIdentifier(Type type) : type_(type), string_(nullptr) {}
Type type_;
-
+ // Only non-nullptr when PreParser.track_unresolved_variables_ is true.
+ const AstRawString* string_;
friend class PreParserExpression;
+ friend class PreParser;
};
class PreParserExpression {
public:
- PreParserExpression() : code_(TypeField::encode(kExpression)) {}
+ PreParserExpression() : code_(TypeField::encode(kEmpty)) {}
+
+ static PreParserExpression Empty() { return PreParserExpression(); }
static PreParserExpression Default() {
- return PreParserExpression();
+ return PreParserExpression(TypeField::encode(kExpression));
}
static PreParserExpression Spread(PreParserExpression expression) {
@@ -128,7 +137,8 @@
static PreParserExpression FromIdentifier(PreParserIdentifier id) {
return PreParserExpression(TypeField::encode(kIdentifierExpression) |
- IdentifierTypeField::encode(id.type_));
+ IdentifierTypeField::encode(id.type_),
+ id.string_);
}
static PreParserExpression BinaryOperation(PreParserExpression left,
@@ -159,6 +169,11 @@
IsUseStrictField::encode(true));
}
+ static PreParserExpression UseAsmStringLiteral() {
+ return PreParserExpression(TypeField::encode(kStringLiteralExpression) |
+ IsUseAsmField::encode(true));
+ }
+
static PreParserExpression This() {
return PreParserExpression(TypeField::encode(kExpression) |
ExpressionTypeField::encode(kThisExpression));
@@ -199,6 +214,8 @@
ExpressionTypeField::encode(kNoTemplateTagExpression));
}
+ bool IsEmpty() const { return TypeField::decode(code_) == kEmpty; }
+
bool IsIdentifier() const {
return TypeField::decode(code_) == kIdentifierExpression;
}
@@ -230,6 +247,11 @@
IsUseStrictField::decode(code_);
}
+ bool IsUseAsmLiteral() const {
+ return TypeField::decode(code_) == kStringLiteralExpression &&
+ IsUseAsmField::decode(code_);
+ }
+
bool IsThis() const {
return TypeField::decode(code_) == kExpression &&
ExpressionTypeField::decode(code_) == kThisExpression;
@@ -275,7 +297,7 @@
ExpressionTypeField::decode(code_) == kNoTemplateTagExpression;
}
- bool IsSpreadExpression() const {
+ bool IsSpread() const {
return TypeField::decode(code_) == kSpreadExpression;
}
@@ -292,12 +314,16 @@
// More dummy implementations of things PreParser doesn't need to track:
void set_index(int index) {} // For YieldExpressions
void set_should_eager_compile() {}
+ void set_should_be_used_once_hint() {}
int position() const { return kNoSourcePosition; }
void set_function_token_position(int position) {}
+ void set_is_class_field_initializer(bool is_class_field_initializer) {}
+
private:
enum Type {
+ kEmpty,
kExpression,
kIdentifierExpression,
kStringLiteralExpression,
@@ -318,8 +344,9 @@
kAssignment
};
- explicit PreParserExpression(uint32_t expression_code)
- : code_(expression_code) {}
+ explicit PreParserExpression(uint32_t expression_code,
+ const AstRawString* string = nullptr)
+ : code_(expression_code), string_(string) {}
// The first three bits are for the Type.
typedef BitField<Type, 0, 3> TypeField;
@@ -335,11 +362,16 @@
// 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> IsUseAsmField;
typedef BitField<PreParserIdentifier::Type, TypeField::kNext, 10>
IdentifierTypeField;
typedef BitField<bool, TypeField::kNext, 1> HasCoverInitializedNameField;
uint32_t code_;
+ // Non-nullptr if the expression is one identifier.
+ const AstRawString* string_;
+
+ friend class PreParser;
};
@@ -353,13 +385,18 @@
PreParserList* operator->() { return this; }
void Add(T, void*) { ++length_; }
int length() const { return length_; }
+ static PreParserList Null() { return PreParserList(-1); }
+ bool IsNull() const { return length_ == -1; }
+
private:
+ explicit PreParserList(int n) : length_(n) {}
int length_;
};
-
typedef PreParserList<PreParserExpression> PreParserExpressionList;
+class PreParserStatement;
+typedef PreParserList<PreParserStatement> PreParserStatementList;
class PreParserStatement {
public:
@@ -367,12 +404,16 @@
return PreParserStatement(kUnknownStatement);
}
- static PreParserStatement Jump() {
- return PreParserStatement(kJumpStatement);
+ static PreParserStatement Null() {
+ return PreParserStatement(kNullStatement);
}
- static PreParserStatement FunctionDeclaration() {
- return PreParserStatement(kFunctionDeclaration);
+ static PreParserStatement Empty() {
+ return PreParserStatement(kEmptyStatement);
+ }
+
+ static PreParserStatement Jump() {
+ return PreParserStatement(kJumpStatement);
}
// Creates expression statement from expression.
@@ -383,6 +424,9 @@
if (expression.IsUseStrictLiteral()) {
return PreParserStatement(kUseStrictExpressionStatement);
}
+ if (expression.IsUseAsmLiteral()) {
+ return PreParserStatement(kUseAsmExpressionStatement);
+ }
if (expression.IsStringLiteral()) {
return PreParserStatement(kStringLiteralExpressionStatement);
}
@@ -390,28 +434,43 @@
}
bool IsStringLiteral() {
- return code_ == kStringLiteralExpressionStatement || IsUseStrictLiteral();
+ return code_ == kStringLiteralExpressionStatement || IsUseStrictLiteral() ||
+ IsUseAsmLiteral();
}
bool IsUseStrictLiteral() {
return code_ == kUseStrictExpressionStatement;
}
- bool IsFunctionDeclaration() {
- return code_ == kFunctionDeclaration;
- }
+ bool IsUseAsmLiteral() { return code_ == kUseAsmExpressionStatement; }
bool IsJumpStatement() {
return code_ == kJumpStatement;
}
+ bool IsNullStatement() { return code_ == kNullStatement; }
+
+ bool IsEmptyStatement() { return code_ == kEmptyStatement; }
+
+ // Dummy implementation for making statement->somefunc() work in both Parser
+ // and PreParser.
+ PreParserStatement* operator->() { return this; }
+
+ PreParserStatementList statements() { return PreParserStatementList(); }
+ void set_scope(Scope* scope) {}
+ void Initialize(PreParserExpression cond, PreParserStatement body) {}
+ void Initialize(PreParserStatement init, PreParserExpression cond,
+ PreParserStatement next, PreParserStatement body) {}
+
private:
enum Type {
+ kNullStatement,
+ kEmptyStatement,
kUnknownStatement,
kJumpStatement,
kStringLiteralExpressionStatement,
kUseStrictExpressionStatement,
- kFunctionDeclaration
+ kUseAsmExpressionStatement,
};
explicit PreParserStatement(Type code) : code_(code) {}
@@ -419,9 +478,6 @@
};
-typedef PreParserList<PreParserStatement> PreParserStatementList;
-
-
class PreParserFactory {
public:
explicit PreParserFactory(void* unused_value_factory) {}
@@ -433,31 +489,34 @@
int pos) {
return PreParserExpression::Default();
}
+ PreParserExpression NewUndefinedLiteral(int pos) {
+ return PreParserExpression::Default();
+ }
PreParserExpression NewRegExpLiteral(PreParserIdentifier js_pattern,
int js_flags, int literal_index,
int pos) {
return PreParserExpression::Default();
}
PreParserExpression NewArrayLiteral(PreParserExpressionList values,
- int literal_index,
- int pos) {
- return PreParserExpression::ArrayLiteral();
- }
- PreParserExpression NewArrayLiteral(PreParserExpressionList values,
int first_spread_index, int literal_index,
int pos) {
return PreParserExpression::ArrayLiteral();
}
+ PreParserExpression NewClassLiteralProperty(PreParserExpression key,
+ PreParserExpression value,
+ ClassLiteralProperty::Kind kind,
+ bool is_static,
+ bool is_computed_name) {
+ return PreParserExpression::Default();
+ }
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();
}
@@ -533,15 +592,9 @@
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();
+ return PreParserStatement::Jump();
}
PreParserExpression NewFunctionLiteral(
PreParserIdentifier name, Scope* scope, PreParserStatementList body,
@@ -549,8 +602,7 @@
int parameter_count,
FunctionLiteral::ParameterFlag has_duplicate_parameters,
FunctionLiteral::FunctionType function_type,
- FunctionLiteral::EagerCompileHint eager_compile_hint, FunctionKind kind,
- int position) {
+ FunctionLiteral::EagerCompileHint eager_compile_hint, int position) {
return PreParserExpression::Default();
}
@@ -563,6 +615,77 @@
return PreParserExpression::Default();
}
+ PreParserStatement NewEmptyStatement(int pos) {
+ return PreParserStatement::Default();
+ }
+
+ PreParserStatement NewBlock(ZoneList<const AstRawString*>* labels,
+ int capacity, bool ignore_completion_value,
+ int pos) {
+ return PreParserStatement::Default();
+ }
+
+ PreParserStatement NewDebuggerStatement(int pos) {
+ return PreParserStatement::Default();
+ }
+
+ PreParserStatement NewExpressionStatement(PreParserExpression expr, int pos) {
+ return PreParserStatement::ExpressionStatement(expr);
+ }
+
+ PreParserStatement NewIfStatement(PreParserExpression condition,
+ PreParserStatement then_statement,
+ PreParserStatement else_statement,
+ int pos) {
+ // This must return a jump statement iff both clauses are jump statements.
+ return else_statement.IsJumpStatement() ? then_statement : else_statement;
+ }
+
+ PreParserStatement NewBreakStatement(PreParserStatement target, int pos) {
+ return PreParserStatement::Jump();
+ }
+
+ PreParserStatement NewContinueStatement(PreParserStatement target, int pos) {
+ return PreParserStatement::Jump();
+ }
+
+ PreParserStatement NewWithStatement(Scope* scope,
+ PreParserExpression expression,
+ PreParserStatement statement, int pos) {
+ return PreParserStatement::Default();
+ }
+
+ PreParserStatement NewDoWhileStatement(ZoneList<const AstRawString*>* labels,
+ int pos) {
+ return PreParserStatement::Default();
+ }
+
+ PreParserStatement NewWhileStatement(ZoneList<const AstRawString*>* labels,
+ int pos) {
+ return PreParserStatement::Default();
+ }
+
+ PreParserStatement NewSwitchStatement(ZoneList<const AstRawString*>* labels,
+ int pos) {
+ return PreParserStatement::Default();
+ }
+
+ PreParserStatement NewCaseClause(PreParserExpression label,
+ PreParserStatementList statements, int pos) {
+ return PreParserStatement::Default();
+ }
+
+ PreParserStatement NewForStatement(ZoneList<const AstRawString*>* labels,
+ int pos) {
+ return PreParserStatement::Default();
+ }
+
+ PreParserStatement NewForEachStatement(ForEachStatement::VisitMode visit_mode,
+ ZoneList<const AstRawString*>* labels,
+ int pos) {
+ return PreParserStatement::Default();
+ }
+
// Return the object itself as AstVisitor and implement the needed
// dummy method right in this class.
PreParserFactory* visitor() { return this; }
@@ -585,328 +708,46 @@
class PreParser;
-template <>
-class ParserBaseTraits<PreParser> {
+class PreParserTarget {
public:
- typedef ParserBaseTraits<PreParser> PreParserTraits;
+ PreParserTarget(ParserBase<PreParser>* preparser,
+ PreParserStatement statement) {}
+};
- struct Type {
- // PreParser doesn't need to store generator variables.
- typedef void GeneratorVariable;
+class PreParserTargetScope {
+ public:
+ explicit PreParserTargetScope(ParserBase<PreParser>* preparser) {}
+};
- typedef int AstProperties;
+template <>
+struct ParserTypes<PreParser> {
+ typedef ParserBase<PreParser> Base;
+ typedef PreParser Impl;
- typedef v8::internal::ExpressionClassifier<PreParserTraits>
- ExpressionClassifier;
+ // PreParser doesn't need to store generator variables.
+ typedef void Variable;
- // Return types for traversing functions.
- typedef PreParserIdentifier Identifier;
- typedef PreParserExpression Expression;
- typedef PreParserExpression YieldExpression;
- typedef PreParserExpression FunctionLiteral;
- typedef PreParserExpression ClassLiteral;
- typedef PreParserExpression Literal;
- typedef PreParserExpression ObjectLiteralProperty;
- typedef PreParserExpressionList ExpressionList;
- typedef PreParserExpressionList PropertyList;
- typedef PreParserIdentifier FormalParameter;
- typedef PreParserFormalParameters FormalParameters;
- typedef PreParserStatementList StatementList;
+ // Return types for traversing functions.
+ typedef PreParserIdentifier Identifier;
+ typedef PreParserExpression Expression;
+ typedef PreParserExpression FunctionLiteral;
+ typedef PreParserExpression ObjectLiteralProperty;
+ typedef PreParserExpression ClassLiteralProperty;
+ typedef PreParserExpressionList ExpressionList;
+ typedef PreParserExpressionList ObjectPropertyList;
+ typedef PreParserExpressionList ClassPropertyList;
+ typedef PreParserFormalParameters FormalParameters;
+ typedef PreParserStatement Statement;
+ typedef PreParserStatementList StatementList;
+ typedef PreParserStatement Block;
+ typedef PreParserStatement BreakableStatement;
+ typedef PreParserStatement IterationStatement;
- // For constructing objects returned by the traversing functions.
- typedef PreParserFactory Factory;
- };
+ // For constructing objects returned by the traversing functions.
+ typedef PreParserFactory Factory;
- // TODO(nikolaos): The traits methods should not need to call methods
- // of the implementation object.
- PreParser* delegate() { return reinterpret_cast<PreParser*>(this); }
- const PreParser* delegate() const {
- return reinterpret_cast<const PreParser*>(this);
- }
-
- // Helper functions for recursive descent.
- bool IsEval(PreParserIdentifier identifier) const {
- return identifier.IsEval();
- }
-
- bool IsArguments(PreParserIdentifier identifier) const {
- return identifier.IsArguments();
- }
-
- bool IsEvalOrArguments(PreParserIdentifier identifier) const {
- return identifier.IsEvalOrArguments();
- }
-
- bool IsUndefined(PreParserIdentifier identifier) const {
- return identifier.IsUndefined();
- }
-
- bool IsAwait(PreParserIdentifier identifier) const {
- return identifier.IsAwait();
- }
-
- bool IsFutureStrictReserved(PreParserIdentifier identifier) const {
- return identifier.IsFutureStrictReserved();
- }
-
- // 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();
- }
-
- bool IsPrototype(PreParserIdentifier identifier) const {
- return identifier.IsPrototype();
- }
-
- bool IsConstructor(PreParserIdentifier identifier) const {
- return identifier.IsConstructor();
- }
-
- bool IsDirectEvalCall(PreParserExpression expression) const {
- return expression.IsDirectEvalCall();
- }
-
- 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();
- }
-
- 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 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 BuildIteratorResult(PreParserExpression value,
- bool done) {
- return PreParserExpression::Default();
- }
-
- PreParserExpression NewThrowReferenceError(MessageTemplate::Template message,
- int pos) {
- return PreParserExpression::Default();
- }
-
- PreParserExpression NewThrowSyntaxError(MessageTemplate::Template message,
- PreParserIdentifier arg, int pos) {
- return PreParserExpression::Default();
- }
-
- PreParserExpression NewThrowTypeError(MessageTemplate::Template message,
- PreParserIdentifier arg, int pos) {
- return PreParserExpression::Default();
- }
-
- // Reporting errors.
- void ReportMessageAt(Scanner::Location source_location,
- MessageTemplate::Template message,
- const char* arg = NULL,
- ParseErrorType error_type = kSyntaxError);
- void ReportMessageAt(Scanner::Location source_location,
- MessageTemplate::Template message,
- const AstRawString* arg,
- ParseErrorType error_type = kSyntaxError);
-
- // A dummy function, just useful as an argument to CHECK_OK_CUSTOM.
- static void Void() {}
-
- // "null" return type creators.
- static PreParserIdentifier EmptyIdentifier() {
- 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();
- }
- PreParserIdentifier EmptyIdentifierString() const {
- return PreParserIdentifier::Default();
- }
-
- // Odd-ball literal creators.
- PreParserExpression GetLiteralTheHole(int position,
- PreParserFactory* factory) const {
- return PreParserExpression::Default();
- }
-
- // Producing data during the recursive descent.
- PreParserIdentifier GetSymbol(Scanner* scanner) const;
-
- PreParserIdentifier GetNextSymbol(Scanner* scanner) const {
- return PreParserIdentifier::Default();
- }
-
- PreParserIdentifier GetNumberAsSymbol(Scanner* scanner) const {
- return PreParserIdentifier::Default();
- }
-
- PreParserExpression ThisExpression(int pos = kNoSourcePosition) {
- return PreParserExpression::This();
- }
-
- PreParserExpression NewSuperPropertyReference(PreParserFactory* factory,
- int pos) {
- return PreParserExpression::Default();
- }
-
- PreParserExpression NewSuperCallReference(PreParserFactory* factory,
- int pos) {
- return PreParserExpression::SuperCallReference();
- }
-
- PreParserExpression NewTargetExpression(int pos) {
- return PreParserExpression::Default();
- }
-
- PreParserExpression FunctionSentExpression(PreParserFactory* factory,
- int pos) const {
- return PreParserExpression::Default();
- }
-
- PreParserExpression ExpressionFromLiteral(Token::Value token, int pos,
- Scanner* scanner,
- PreParserFactory* factory) const {
- return PreParserExpression::Default();
- }
-
- PreParserExpression ExpressionFromIdentifier(PreParserIdentifier name,
- int start_position,
- int end_position,
- InferName = InferName::kYes) {
- return PreParserExpression::FromIdentifier(name);
- }
-
- PreParserExpression ExpressionFromString(int pos, Scanner* scanner,
- PreParserFactory* factory) const;
-
- PreParserExpression GetIterator(PreParserExpression iterable,
- PreParserFactory* factory, int pos) {
- return PreParserExpression::Default();
- }
-
- PreParserExpressionList NewExpressionList(int size, Zone* zone) const {
- return PreParserExpressionList();
- }
-
- PreParserExpressionList NewPropertyList(int size, Zone* zone) const {
- return PreParserExpressionList();
- }
-
- PreParserStatementList NewStatementList(int size, Zone* zone) const {
- return PreParserStatementList();
- }
-
- void AddParameterInitializationBlock(
- const PreParserFormalParameters& parameters, PreParserStatementList body,
- bool is_async, bool* ok) {}
-
- void AddFormalParameter(PreParserFormalParameters* parameters,
- PreParserExpression pattern,
- PreParserExpression initializer,
- int initializer_end_position, bool is_rest) {
- ++parameters->arity;
- }
-
- void DeclareFormalParameter(DeclarationScope* scope,
- PreParserIdentifier parameter,
- Type::ExpressionClassifier* classifier) {
- if (!classifier->is_simple_parameter_list()) {
- scope->SetHasNonSimpleParameters();
- }
- }
-
- V8_INLINE void ParseArrowFunctionFormalParameterList(
- PreParserFormalParameters* parameters, PreParserExpression params,
- const Scanner::Location& params_loc, Scanner::Location* duplicate_loc,
- const Scope::Snapshot& scope_snapshot, bool* ok);
-
- void ReindexLiterals(const PreParserFormalParameters& parameters) {}
-
- V8_INLINE PreParserExpression NoTemplateTag() {
- return PreParserExpression::NoTemplateTag();
- }
- V8_INLINE static bool IsTaggedTemplate(const PreParserExpression tag) {
- return !tag.IsNoTemplateTag();
- }
-
- inline void MaterializeUnspreadArgumentsLiterals(int count);
-
- inline PreParserExpression ExpressionListToExpression(
- PreParserExpressionList args) {
- return PreParserExpression::Default();
- }
-
- void SetFunctionNameFromPropertyName(PreParserExpression property,
- PreParserIdentifier name) {}
- void SetFunctionNameFromIdentifierRef(PreParserExpression value,
- PreParserExpression identifier) {}
-
- V8_INLINE ZoneList<typename Type::ExpressionClassifier::Error>*
- GetReportedErrorList() const;
- V8_INLINE Zone* zone() const;
- V8_INLINE ZoneList<PreParserExpression>* GetNonPatternList() const;
+ typedef PreParserTarget Target;
+ typedef PreParserTargetScope TargetScope;
};
@@ -924,9 +765,7 @@
// it is used) are generally omitted.
class PreParser : public ParserBase<PreParser> {
friend class ParserBase<PreParser>;
- // TODO(nikolaos): This should not be necessary. It will be removed
- // when the traits object stops delegating to the implementation object.
- friend class ParserBaseTraits<PreParser>;
+ friend class v8::internal::ExpressionClassifier<ParserTypes<PreParser>>;
public:
typedef PreParserIdentifier Identifier;
@@ -935,6 +774,7 @@
enum PreParseResult {
kPreParseStackOverflow,
+ kPreParseAbort,
kPreParseSuccess
};
@@ -942,7 +782,8 @@
ParserRecorder* log, uintptr_t stack_limit)
: ParserBase<PreParser>(zone, scanner, stack_limit, NULL,
ast_value_factory, log),
- use_counts_(nullptr) {}
+ use_counts_(nullptr),
+ track_unresolved_variables_(false) {}
// Pre-parse the program from the character stream; returns true on
// success (even if parsing failed, the pre-parse data successfully
@@ -958,19 +799,19 @@
// the global scope.
if (is_module) scope = NewModuleScope(scope);
- FunctionState top_scope(&function_state_, &scope_state_, scope,
- kNormalFunction);
+ FunctionState top_scope(&function_state_, &scope_state_, scope);
bool ok = true;
int start_position = scanner()->peek_location().beg_pos;
parsing_module_ = is_module;
- ParseStatementList(Token::EOS, &ok);
+ PreParserStatementList body;
+ ParseStatementList(body, Token::EOS, &ok);
if (stack_overflow()) return kPreParseStackOverflow;
if (!ok) {
ReportUnexpectedToken(scanner()->current_token());
} else if (is_strict(this->scope()->language_mode())) {
CheckStrictOctalLiteral(start_position, scanner()->location().end_pos,
&ok);
- CheckDecimalLiteralWithLeadingZero(use_counts_, start_position,
+ CheckDecimalLiteralWithLeadingZero(start_position,
scanner()->location().end_pos);
}
if (materialized_literals) {
@@ -987,16 +828,12 @@
// 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,
+ PreParseResult PreParseLazyFunction(DeclarationScope* function_scope,
bool parsing_module, ParserRecorder* log,
- Scanner::BookmarkScope* bookmark,
- int* use_counts);
+ bool track_unresolved_variables,
+ bool may_abort, int* use_counts);
private:
- 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
@@ -1006,72 +843,24 @@
// 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(AllowLabelledFunctionStatement allow_function,
- bool* ok);
- Statement ParseSubStatement(AllowLabelledFunctionStatement allow_function,
- bool* ok);
- Statement ParseScopedStatement(bool legacy, bool* ok);
- Statement ParseHoistableDeclaration(bool* ok);
- Statement ParseHoistableDeclaration(int pos, ParseFunctionFlags flags,
- bool* ok);
- Statement ParseFunctionDeclaration(bool* ok);
- Statement ParseAsyncFunctionDeclaration(bool* ok);
- Expression ParseAsyncFunctionExpression(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(
- AllowLabelledFunctionStatement allow_function, 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 PreParserStatementList ParseEagerFunctionBody(
PreParserIdentifier function_name, int pos,
const PreParserFormalParameters& parameters, FunctionKind kind,
FunctionLiteral::FunctionType function_type, bool* ok);
- V8_INLINE void SkipLazyFunctionBody(
- int* materialized_literal_count, int* expected_property_count, bool* ok,
- Scanner::BookmarkScope* bookmark = nullptr) {
+ V8_INLINE LazyParsingResult SkipLazyFunctionBody(
+ int* materialized_literal_count, int* expected_property_count,
+ bool track_unresolved_variables, bool may_abort, bool* ok) {
UNREACHABLE();
+ return kLazyParsingComplete;
}
Expression ParseFunctionLiteral(
Identifier name, Scanner::Location function_name_location,
FunctionNameValidity function_name_validity, FunctionKind kind,
int function_token_pos, FunctionLiteral::FunctionType function_type,
LanguageMode language_mode, bool* ok);
- void ParseLazyFunctionLiteralBody(bool* ok,
- Scanner::BookmarkScope* bookmark = nullptr);
-
- PreParserExpression ParseClassLiteral(ExpressionClassifier* classifier,
- PreParserIdentifier name,
- Scanner::Location class_name_location,
- bool name_is_strict_reserved, int pos,
- bool* ok);
+ LazyParsingResult ParseLazyFunctionLiteralBody(bool may_abort, bool* ok);
struct TemplateLiteralState {};
@@ -1085,14 +874,14 @@
TemplateLiteralState* state, int start, PreParserExpression tag);
V8_INLINE void CheckConflictingVarDeclarations(Scope* scope, bool* ok) {}
+ V8_INLINE void SetLanguageMode(Scope* scope, LanguageMode mode) {
+ scope->SetLanguageMode(mode);
+ }
+ V8_INLINE void SetAsmModule() {}
+
V8_INLINE void MarkCollectedTailCallExpressions() {}
V8_INLINE void MarkTailPosition(PreParserExpression expression) {}
- void ParseAsyncArrowSingleExpressionBody(PreParserStatementList body,
- bool accept_IN,
- ExpressionClassifier* classifier,
- int pos, bool* ok);
-
V8_INLINE PreParserExpressionList
PrepareSpreadArguments(PreParserExpressionList list) {
return list;
@@ -1105,6 +894,11 @@
PreParserExpressionList args,
int pos);
+ V8_INLINE PreParserExpression
+ RewriteSuperCall(PreParserExpression call_expression) {
+ return call_expression;
+ }
+
V8_INLINE void RewriteDestructuringAssignments() {}
V8_INLINE PreParserExpression RewriteExponentiation(PreParserExpression left,
@@ -1121,14 +915,102 @@
RewriteAwaitExpression(PreParserExpression value, int pos) {
return value;
}
+ V8_INLINE void PrepareAsyncFunctionBody(PreParserStatementList body,
+ FunctionKind kind, int pos) {}
+ V8_INLINE void RewriteAsyncFunctionBody(PreParserStatementList body,
+ PreParserStatement block,
+ PreParserExpression return_value,
+ bool* ok) {}
V8_INLINE PreParserExpression RewriteYieldStar(PreParserExpression generator,
PreParserExpression expression,
int pos) {
return PreParserExpression::Default();
}
- V8_INLINE void RewriteNonPattern(Type::ExpressionClassifier* classifier,
- bool* ok) {
- ValidateExpression(classifier, ok);
+ V8_INLINE void RewriteNonPattern(bool* ok) { ValidateExpression(ok); }
+
+ void DeclareAndInitializeVariables(
+ PreParserStatement block,
+ const DeclarationDescriptor* declaration_descriptor,
+ const DeclarationParsingResult::Declaration* declaration,
+ ZoneList<const AstRawString*>* names, bool* ok);
+
+ V8_INLINE ZoneList<const AstRawString*>* DeclareLabel(
+ ZoneList<const AstRawString*>* labels, PreParserExpression expr,
+ bool* ok) {
+ DCHECK(!expr.AsIdentifier().IsEnum());
+ DCHECK(!parsing_module_ || !expr.AsIdentifier().IsAwait());
+ DCHECK(is_sloppy(language_mode()) ||
+ !IsFutureStrictReserved(expr.AsIdentifier()));
+ return labels;
+ }
+
+ // TODO(nikolaos): The preparser currently does not keep track of labels.
+ V8_INLINE bool ContainsLabel(ZoneList<const AstRawString*>* labels,
+ PreParserIdentifier label) {
+ return false;
+ }
+
+ V8_INLINE PreParserExpression RewriteReturn(PreParserExpression return_value,
+ int pos) {
+ return return_value;
+ }
+ V8_INLINE PreParserStatement RewriteSwitchStatement(
+ PreParserExpression tag, PreParserStatement switch_statement,
+ PreParserStatementList cases, Scope* scope) {
+ return PreParserStatement::Default();
+ }
+ V8_INLINE void RewriteCatchPattern(CatchInfo* catch_info, bool* ok) {}
+ V8_INLINE void ValidateCatchBlock(const CatchInfo& catch_info, bool* ok) {}
+ V8_INLINE PreParserStatement RewriteTryStatement(
+ PreParserStatement try_block, PreParserStatement catch_block,
+ PreParserStatement finally_block, const CatchInfo& catch_info, int pos) {
+ return PreParserStatement::Default();
+ }
+
+ V8_INLINE PreParserExpression RewriteDoExpression(PreParserStatement body,
+ int pos, bool* ok) {
+ return PreParserExpression::Default();
+ }
+
+ // TODO(nikolaos): The preparser currently does not keep track of labels
+ // and targets.
+ V8_INLINE PreParserStatement LookupBreakTarget(PreParserIdentifier label,
+ bool* ok) {
+ return PreParserStatement::Default();
+ }
+ V8_INLINE PreParserStatement LookupContinueTarget(PreParserIdentifier label,
+ bool* ok) {
+ return PreParserStatement::Default();
+ }
+
+ V8_INLINE PreParserStatement DeclareFunction(
+ PreParserIdentifier variable_name, PreParserExpression function, int pos,
+ bool is_generator, bool is_async, ZoneList<const AstRawString*>* names,
+ bool* ok) {
+ return Statement::Default();
+ }
+
+ V8_INLINE PreParserStatement
+ DeclareClass(PreParserIdentifier variable_name, PreParserExpression value,
+ ZoneList<const AstRawString*>* names, int class_token_pos,
+ int end_pos, bool* ok) {
+ return PreParserStatement::Default();
+ }
+ V8_INLINE void DeclareClassVariable(PreParserIdentifier name,
+ Scope* block_scope, ClassInfo* class_info,
+ int class_token_pos, bool* ok) {}
+ V8_INLINE void DeclareClassProperty(PreParserIdentifier class_name,
+ PreParserExpression property,
+ ClassInfo* class_info, bool* ok) {}
+ V8_INLINE PreParserExpression RewriteClassLiteral(PreParserIdentifier name,
+ ClassInfo* class_info,
+ int pos, bool* ok) {
+ return PreParserExpression::Default();
+ }
+
+ V8_INLINE PreParserStatement DeclareNative(PreParserIdentifier name, int pos,
+ bool* ok) {
+ return PreParserStatement::Default();
}
V8_INLINE void QueueDestructuringAssignmentForRewriting(
@@ -1136,15 +1018,425 @@
V8_INLINE void QueueNonPatternForRewriting(PreParserExpression expr,
bool* ok) {}
- int* use_counts_;
-};
-
-void ParserBaseTraits<PreParser>::MaterializeUnspreadArgumentsLiterals(
- int count) {
- for (int i = 0; i < count; ++i) {
- delegate()->function_state_->NextMaterializedLiteralIndex();
+ // Helper functions for recursive descent.
+ V8_INLINE bool IsEval(PreParserIdentifier identifier) const {
+ return identifier.IsEval();
}
-}
+
+ V8_INLINE bool IsArguments(PreParserIdentifier identifier) const {
+ return identifier.IsArguments();
+ }
+
+ V8_INLINE bool IsEvalOrArguments(PreParserIdentifier identifier) const {
+ return identifier.IsEvalOrArguments();
+ }
+
+ V8_INLINE bool IsUndefined(PreParserIdentifier identifier) const {
+ return identifier.IsUndefined();
+ }
+
+ V8_INLINE bool IsAwait(PreParserIdentifier identifier) const {
+ return identifier.IsAwait();
+ }
+
+ V8_INLINE bool IsFutureStrictReserved(PreParserIdentifier identifier) const {
+ return identifier.IsFutureStrictReserved();
+ }
+
+ // Returns true if the expression is of type "this.foo".
+ V8_INLINE static bool IsThisProperty(PreParserExpression expression) {
+ return expression.IsThisProperty();
+ }
+
+ V8_INLINE static bool IsIdentifier(PreParserExpression expression) {
+ return expression.IsIdentifier();
+ }
+
+ V8_INLINE static PreParserIdentifier AsIdentifier(
+ PreParserExpression expression) {
+ return expression.AsIdentifier();
+ }
+
+ V8_INLINE static PreParserExpression AsIdentifierExpression(
+ PreParserExpression expression) {
+ return expression;
+ }
+
+ V8_INLINE bool IsPrototype(PreParserIdentifier identifier) const {
+ return identifier.IsPrototype();
+ }
+
+ V8_INLINE bool IsConstructor(PreParserIdentifier identifier) const {
+ return identifier.IsConstructor();
+ }
+
+ V8_INLINE bool IsDirectEvalCall(PreParserExpression expression) const {
+ return expression.IsDirectEvalCall();
+ }
+
+ V8_INLINE static bool IsBoilerplateProperty(PreParserExpression property) {
+ // PreParser doesn't count boilerplate properties.
+ return false;
+ }
+
+ V8_INLINE bool IsNative(PreParserExpression expr) const {
+ // 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 and there is
+ // no need to keep track of "native".
+ return false;
+ }
+
+ V8_INLINE static bool IsArrayIndex(PreParserIdentifier string,
+ uint32_t* index) {
+ return false;
+ }
+
+ V8_INLINE bool IsUseStrictDirective(PreParserStatement statement) const {
+ return statement.IsUseStrictLiteral();
+ }
+
+ V8_INLINE bool IsUseAsmDirective(PreParserStatement statement) const {
+ return statement.IsUseAsmLiteral();
+ }
+
+ V8_INLINE bool IsStringLiteral(PreParserStatement statement) const {
+ return statement.IsStringLiteral();
+ }
+
+ V8_INLINE static PreParserExpression GetPropertyValue(
+ PreParserExpression property) {
+ return PreParserExpression::Default();
+ }
+
+ V8_INLINE static void GetDefaultStrings(
+ PreParserIdentifier* default_string,
+ PreParserIdentifier* star_default_star_string) {}
+
+ // Functions for encapsulating the differences between parsing and preparsing;
+ // operations interleaved with the recursive descent.
+ V8_INLINE static void PushLiteralName(PreParserIdentifier id) {}
+ V8_INLINE static void PushVariableName(PreParserIdentifier id) {}
+ V8_INLINE void PushPropertyName(PreParserExpression expression) {}
+ V8_INLINE void PushEnclosingName(PreParserIdentifier name) {}
+ V8_INLINE static void AddFunctionForNameInference(
+ PreParserExpression expression) {}
+ V8_INLINE static void InferFunctionName() {}
+
+ V8_INLINE static void CheckAssigningFunctionLiteralToProperty(
+ PreParserExpression left, PreParserExpression right) {}
+
+ V8_INLINE 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;
+ }
+
+ V8_INLINE bool ShortcutNumericLiteralBinaryExpression(PreParserExpression* x,
+ PreParserExpression y,
+ Token::Value op,
+ int pos) {
+ return false;
+ }
+
+ V8_INLINE PreParserExpression BuildUnaryExpression(
+ PreParserExpression expression, Token::Value op, int pos) {
+ return PreParserExpression::Default();
+ }
+
+ V8_INLINE PreParserExpression BuildIteratorResult(PreParserExpression value,
+ bool done) {
+ return PreParserExpression::Default();
+ }
+
+ V8_INLINE PreParserStatement
+ BuildInitializationBlock(DeclarationParsingResult* parsing_result,
+ ZoneList<const AstRawString*>* names, bool* ok) {
+ return PreParserStatement::Default();
+ }
+
+ V8_INLINE PreParserStatement
+ InitializeForEachStatement(PreParserStatement stmt, PreParserExpression each,
+ PreParserExpression subject,
+ PreParserStatement body, int each_keyword_pos) {
+ return stmt;
+ }
+
+ V8_INLINE PreParserStatement RewriteForVarInLegacy(const ForInfo& for_info) {
+ return PreParserStatement::Null();
+ }
+ V8_INLINE void DesugarBindingInForEachStatement(
+ ForInfo* for_info, PreParserStatement* body_block,
+ PreParserExpression* each_variable, bool* ok) {}
+ V8_INLINE PreParserStatement CreateForEachStatementTDZ(
+ PreParserStatement init_block, const ForInfo& for_info, bool* ok) {
+ return init_block;
+ }
+
+ V8_INLINE StatementT DesugarLexicalBindingsInForStatement(
+ PreParserStatement loop, PreParserStatement init,
+ PreParserExpression cond, PreParserStatement next,
+ PreParserStatement body, Scope* inner_scope, const ForInfo& for_info,
+ bool* ok) {
+ return loop;
+ }
+
+ V8_INLINE PreParserExpression
+ NewThrowReferenceError(MessageTemplate::Template message, int pos) {
+ return PreParserExpression::Default();
+ }
+
+ V8_INLINE PreParserExpression NewThrowSyntaxError(
+ MessageTemplate::Template message, PreParserIdentifier arg, int pos) {
+ return PreParserExpression::Default();
+ }
+
+ V8_INLINE PreParserExpression NewThrowTypeError(
+ MessageTemplate::Template message, PreParserIdentifier arg, int pos) {
+ return PreParserExpression::Default();
+ }
+
+ // Reporting errors.
+ V8_INLINE void ReportMessageAt(Scanner::Location source_location,
+ MessageTemplate::Template message,
+ const char* arg = NULL,
+ ParseErrorType error_type = kSyntaxError) {
+ log_->LogMessage(source_location.beg_pos, source_location.end_pos, message,
+ arg, error_type);
+ }
+
+ V8_INLINE void ReportMessageAt(Scanner::Location source_location,
+ MessageTemplate::Template message,
+ PreParserIdentifier arg,
+ ParseErrorType error_type = kSyntaxError) {
+ UNREACHABLE();
+ }
+
+ // "null" return type creators.
+ V8_INLINE static PreParserIdentifier EmptyIdentifier() {
+ return PreParserIdentifier::Empty();
+ }
+ V8_INLINE static bool IsEmptyIdentifier(PreParserIdentifier name) {
+ return name.IsEmpty();
+ }
+ V8_INLINE static PreParserExpression EmptyExpression() {
+ return PreParserExpression::Empty();
+ }
+ V8_INLINE static PreParserExpression EmptyLiteral() {
+ return PreParserExpression::Default();
+ }
+ V8_INLINE static PreParserExpression EmptyObjectLiteralProperty() {
+ return PreParserExpression::Default();
+ }
+ V8_INLINE static PreParserExpression EmptyClassLiteralProperty() {
+ return PreParserExpression::Default();
+ }
+ V8_INLINE static PreParserExpression EmptyFunctionLiteral() {
+ return PreParserExpression::Default();
+ }
+
+ V8_INLINE static bool IsEmptyExpression(PreParserExpression expr) {
+ return expr.IsEmpty();
+ }
+
+ V8_INLINE static PreParserExpressionList NullExpressionList() {
+ return PreParserExpressionList::Null();
+ }
+
+ V8_INLINE static bool IsNullExpressionList(PreParserExpressionList exprs) {
+ return exprs.IsNull();
+ }
+
+ V8_INLINE static PreParserStatementList NullStatementList() {
+ return PreParserStatementList::Null();
+ }
+
+ V8_INLINE static bool IsNullStatementList(PreParserStatementList stmts) {
+ return stmts.IsNull();
+ }
+
+ V8_INLINE static PreParserStatement NullStatement() {
+ return PreParserStatement::Null();
+ }
+
+ V8_INLINE bool IsNullStatement(PreParserStatement stmt) {
+ return stmt.IsNullStatement();
+ }
+
+ V8_INLINE bool IsEmptyStatement(PreParserStatement stmt) {
+ return stmt.IsEmptyStatement();
+ }
+
+ V8_INLINE static PreParserStatement NullBlock() {
+ return PreParserStatement::Null();
+ }
+
+ V8_INLINE PreParserIdentifier EmptyIdentifierString() const {
+ return PreParserIdentifier::Default();
+ }
+
+ // Odd-ball literal creators.
+ V8_INLINE PreParserExpression GetLiteralTheHole(int position) {
+ return PreParserExpression::Default();
+ }
+
+ V8_INLINE PreParserExpression GetLiteralUndefined(int position) {
+ return PreParserExpression::Default();
+ }
+
+ // Producing data during the recursive descent.
+ PreParserIdentifier GetSymbol() const;
+
+ V8_INLINE PreParserIdentifier GetNextSymbol() const {
+ return PreParserIdentifier::Default();
+ }
+
+ V8_INLINE PreParserIdentifier GetNumberAsSymbol() const {
+ return PreParserIdentifier::Default();
+ }
+
+ V8_INLINE PreParserExpression ThisExpression(int pos = kNoSourcePosition) {
+ return PreParserExpression::This();
+ }
+
+ V8_INLINE PreParserExpression NewSuperPropertyReference(int pos) {
+ return PreParserExpression::Default();
+ }
+
+ V8_INLINE PreParserExpression NewSuperCallReference(int pos) {
+ return PreParserExpression::SuperCallReference();
+ }
+
+ V8_INLINE PreParserExpression NewTargetExpression(int pos) {
+ return PreParserExpression::Default();
+ }
+
+ V8_INLINE PreParserExpression FunctionSentExpression(int pos) {
+ return PreParserExpression::Default();
+ }
+
+ V8_INLINE PreParserExpression ExpressionFromLiteral(Token::Value token,
+ int pos) {
+ return PreParserExpression::Default();
+ }
+
+ PreParserExpression ExpressionFromIdentifier(
+ PreParserIdentifier name, int start_position, int end_position,
+ InferName infer = InferName::kYes);
+
+ V8_INLINE PreParserExpression ExpressionFromString(int pos) {
+ if (scanner()->UnescapedLiteralMatches("use strict", 10)) {
+ return PreParserExpression::UseStrictStringLiteral();
+ }
+ return PreParserExpression::StringLiteral();
+ }
+
+ V8_INLINE PreParserExpressionList NewExpressionList(int size) const {
+ return PreParserExpressionList();
+ }
+
+ V8_INLINE PreParserExpressionList NewObjectPropertyList(int size) const {
+ return PreParserExpressionList();
+ }
+
+ V8_INLINE PreParserExpressionList NewClassPropertyList(int size) const {
+ return PreParserExpressionList();
+ }
+
+ V8_INLINE PreParserStatementList NewStatementList(int size) const {
+ return PreParserStatementList();
+ }
+
+ PreParserStatementList NewCaseClauseList(int size) {
+ return PreParserStatementList();
+ }
+
+ V8_INLINE PreParserExpression
+ NewV8Intrinsic(PreParserIdentifier name, PreParserExpressionList arguments,
+ int pos, bool* ok) {
+ return PreParserExpression::Default();
+ }
+
+ V8_INLINE PreParserStatement NewThrowStatement(PreParserExpression exception,
+ int pos) {
+ return PreParserStatement::Jump();
+ }
+
+ V8_INLINE void AddParameterInitializationBlock(
+ const PreParserFormalParameters& parameters, PreParserStatementList body,
+ bool is_async, bool* ok) {}
+
+ V8_INLINE void AddFormalParameter(PreParserFormalParameters* parameters,
+ PreParserExpression pattern,
+ PreParserExpression initializer,
+ int initializer_end_position,
+ bool is_rest) {
+ ++parameters->arity;
+ }
+
+ V8_INLINE void DeclareFormalParameter(DeclarationScope* scope,
+ PreParserIdentifier parameter) {
+ if (!classifier()->is_simple_parameter_list()) {
+ scope->SetHasNonSimpleParameters();
+ }
+ }
+
+ V8_INLINE void DeclareArrowFunctionFormalParameters(
+ 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.
+ }
+
+ V8_INLINE void ReindexLiterals(const PreParserFormalParameters& parameters) {}
+
+ V8_INLINE PreParserExpression NoTemplateTag() {
+ return PreParserExpression::NoTemplateTag();
+ }
+
+ V8_INLINE static bool IsTaggedTemplate(const PreParserExpression tag) {
+ return !tag.IsNoTemplateTag();
+ }
+
+ V8_INLINE void MaterializeUnspreadArgumentsLiterals(int count) {
+ for (int i = 0; i < count; ++i) {
+ function_state_->NextMaterializedLiteralIndex();
+ }
+ }
+
+ V8_INLINE PreParserExpression
+ ExpressionListToExpression(PreParserExpressionList args) {
+ return PreParserExpression::Default();
+ }
+
+ V8_INLINE void AddAccessorPrefixToFunctionName(bool is_get,
+ PreParserExpression function,
+ PreParserIdentifier name) {}
+ V8_INLINE void SetFunctionNameFromPropertyName(PreParserExpression property,
+ PreParserIdentifier name) {}
+ V8_INLINE void SetFunctionNameFromIdentifierRef(
+ PreParserExpression value, PreParserExpression identifier) {}
+
+ V8_INLINE ZoneList<typename ExpressionClassifier::Error>*
+ GetReportedErrorList() const {
+ return function_state_->GetReportedErrorList();
+ }
+
+ V8_INLINE ZoneList<PreParserExpression>* GetNonPatternList() const {
+ return function_state_->non_patterns_to_rewrite();
+ }
+
+ V8_INLINE void CountUsage(v8::Isolate::UseCounterFeature feature) {
+ if (use_counts_ != nullptr) ++use_counts_[feature];
+ }
+
+ // Preparser's private field members.
+
+ int* use_counts_;
+ bool track_unresolved_variables_;
+};
PreParserExpression PreParser::SpreadCall(PreParserExpression function,
PreParserExpressionList args,
@@ -1158,46 +1450,24 @@
return factory()->NewCallNew(function, args, pos);
}
-void ParserBaseTraits<PreParser>::ParseArrowFunctionFormalParameterList(
- PreParserFormalParameters* parameters, PreParserExpression params,
- const Scanner::Location& params_loc, Scanner::Location* duplicate_loc,
- const Scope::Snapshot& scope_snapshot, bool* ok) {
- // TODO(wingo): Detect duplicated identifiers in paramlists. Detect parameter
- // lists that are too long.
-}
-
-ZoneList<PreParserExpression>* ParserBaseTraits<PreParser>::GetNonPatternList()
- const {
- return delegate()->function_state_->non_patterns_to_rewrite();
-}
-
-ZoneList<
- typename ParserBaseTraits<PreParser>::Type::ExpressionClassifier::Error>*
-ParserBaseTraits<PreParser>::GetReportedErrorList() const {
- return delegate()->function_state_->GetReportedErrorList();
-}
-
-Zone* ParserBaseTraits<PreParser>::zone() const {
- return delegate()->function_state_->scope()->zone();
-}
-
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);
+ PreParserStatementList result;
Scope* inner_scope = scope();
if (!parameters.is_simple) inner_scope = NewScope(BLOCK_SCOPE);
{
BlockState block_state(&scope_state_, inner_scope);
- ParseStatementList(Token::RBRACE, ok);
+ ParseStatementList(result, Token::RBRACE, ok);
if (!*ok) return PreParserStatementList();
}
Expect(Token::RBRACE, ok);
- return PreParserStatementList();
+ return result;
}
PreParserExpression PreParser::CloseTemplateLiteral(TemplateLiteralState* state,
diff --git a/src/parsing/rewriter.cc b/src/parsing/rewriter.cc
index 51ff547..57009bd 100644
--- a/src/parsing/rewriter.cc
+++ b/src/parsing/rewriter.cc
@@ -347,10 +347,13 @@
Variable* result = closure_scope->NewTemporary(
info->ast_value_factory()->dot_result_string());
// The name string must be internalized at this point.
+ info->ast_value_factory()->Internalize(info->isolate());
DCHECK(!result->name().is_null());
Processor processor(info->isolate(), closure_scope, result,
info->ast_value_factory());
processor.Process(body);
+ // Internalize any values created during rewriting.
+ info->ast_value_factory()->Internalize(info->isolate());
if (processor.HasStackOverflow()) return false;
if (processor.result_assigned()) {
diff --git a/src/parsing/scanner-character-streams.cc b/src/parsing/scanner-character-streams.cc
index 7cdef87..3f10cfa 100644
--- a/src/parsing/scanner-character-streams.cc
+++ b/src/parsing/scanner-character-streams.cc
@@ -7,506 +7,677 @@
#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-inl.h"
+#include "src/parsing/scanner.h"
#include "src/unicode-inl.h"
namespace v8 {
namespace internal {
-namespace {
-
-size_t CopyUtf8CharsToUtf16Chars(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 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 CopyUtf8CharsToUtf16Chars(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
+//
+// 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();
+
+ protected:
+ static const size_t kBufferSize = 512;
+
+ bool ReadBlock() override;
+
+ // FillBuffer should read up to kBufferSize characters at position and store
+ // them into buffer_[0..]. It returns the number of characters stored.
+ virtual size_t FillBuffer(size_t position) = 0;
+
+ // Fixed sized buffer that this class reads from.
+ // The base class' buffer_start_ should always point to buffer_.
+ uc16 buffer_[kBufferSize];
+};
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_--;
-}
-
+ : Utf16CharacterStream(buffer_, buffer_, buffer_, 0) {}
bool BufferedUtf16CharacterStream::ReadBlock() {
+ DCHECK_EQ(buffer_start_, buffer_);
+
+ size_t position = pos();
+ buffer_pos_ = position;
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;
+ buffer_end_ = buffer_ + FillBuffer(position);
+ DCHECK_EQ(pos(), position);
+ DCHECK_LE(buffer_end_, buffer_start_ + kBufferSize);
+ return buffer_cursor_ < buffer_end_;
}
-
-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.
+//
+// A stream w/ a data source being a (flattened) Handle<String>.
+class GenericStringUtf16CharacterStream : public BufferedUtf16CharacterStream {
+ public:
+ GenericStringUtf16CharacterStream(Handle<String> data, size_t start_position,
+ size_t end_position);
+
+ protected:
+ size_t FillBuffer(size_t position) override;
+
+ Handle<String> string_;
+ size_t length_;
+};
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;
+ : string_(data), length_(end_position) {
+ DCHECK_GE(end_position, start_position);
+ DCHECK_GE(static_cast<size_t>(string_->length()),
+ end_position - start_position);
+ buffer_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;
- }
+
+ size_t length = i::Min(kBufferSize, length_ - from_pos);
String::WriteToFlat<uc16>(*string_, buffer_, static_cast<int>(from_pos),
static_cast<int>(from_pos + length));
return length;
}
-
// ----------------------------------------------------------------------------
-// ExternalStreamingStream
+// ExternalTwoByteStringUtf16CharacterStream.
+//
+// A stream whose data source is a Handle<ExternalTwoByteString>. It avoids
+// all data copying.
-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;
+class ExternalTwoByteStringUtf16CharacterStream : public Utf16CharacterStream {
+ public:
+ ExternalTwoByteStringUtf16CharacterStream(Handle<ExternalTwoByteString> data,
+ size_t start_position,
+ size_t end_position);
- // 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.
- }
- }
+ private:
+ bool ReadBlock() override;
- // 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() { }
+ const uc16* raw_data_; // Pointer to the actual array of characters.
+ size_t start_pos_;
+ size_t end_pos_;
+};
ExternalTwoByteStringUtf16CharacterStream::
ExternalTwoByteStringUtf16CharacterStream(
- Handle<ExternalTwoByteString> data, int start_position,
- int end_position)
- : raw_data_(data->GetTwoByteData(start_position)), bookmark_(kNoBookmark) {
- buffer_cursor_ = raw_data_,
- buffer_end_ = raw_data_ + (end_position - start_position);
- pos_ = start_position;
+ Handle<ExternalTwoByteString> data, size_t start_position,
+ size_t end_position)
+ : raw_data_(data->GetTwoByteData(static_cast<int>(start_position))),
+ start_pos_(start_position),
+ end_pos_(end_position) {
+ buffer_start_ = raw_data_;
+ buffer_cursor_ = raw_data_;
+ buffer_end_ = raw_data_ + (end_pos_ - start_pos_);
+ buffer_pos_ = start_pos_;
}
-
-bool ExternalTwoByteStringUtf16CharacterStream::SetBookmark() {
- bookmark_ = pos_;
- return true;
-}
-
-
-void ExternalTwoByteStringUtf16CharacterStream::ResetToBookmark() {
- DCHECK(bookmark_ != kNoBookmark);
- pos_ = bookmark_;
- buffer_cursor_ = raw_data_ + bookmark_;
+bool ExternalTwoByteStringUtf16CharacterStream::ReadBlock() {
+ size_t position = pos();
+ bool have_data = start_pos_ <= position && position < end_pos_;
+ if (have_data) {
+ buffer_pos_ = start_pos_;
+ buffer_cursor_ = raw_data_ + (position - start_pos_),
+ buffer_end_ = raw_data_ + (end_pos_ - start_pos_);
+ } else {
+ buffer_pos_ = position;
+ buffer_cursor_ = raw_data_;
+ buffer_end_ = raw_data_;
+ }
+ return have_data;
}
// ----------------------------------------------------------------------------
// ExternalOneByteStringUtf16CharacterStream
+//
+// A stream whose data source is a Handle<ExternalOneByteString>.
-ExternalOneByteStringUtf16CharacterStream::
- ~ExternalOneByteStringUtf16CharacterStream() {}
+class ExternalOneByteStringUtf16CharacterStream
+ : public BufferedUtf16CharacterStream {
+ public:
+ ExternalOneByteStringUtf16CharacterStream(Handle<ExternalOneByteString> data,
+ size_t start_position,
+ size_t end_position);
+
+ // For testing:
+ ExternalOneByteStringUtf16CharacterStream(const char* data, size_t length);
+
+ protected:
+ size_t FillBuffer(size_t position) override;
+
+ const uint8_t* raw_data_; // Pointer to the actual array of characters.
+ size_t length_;
+};
ExternalOneByteStringUtf16CharacterStream::
ExternalOneByteStringUtf16CharacterStream(
- Handle<ExternalOneByteString> data, int start_position,
- int end_position)
- : raw_data_(data->GetChars()),
- length_(end_position),
- bookmark_(kNoBookmark) {
+ Handle<ExternalOneByteString> data, size_t start_position,
+ size_t end_position)
+ : raw_data_(data->GetChars()), length_(end_position) {
DCHECK(end_position >= start_position);
- pos_ = start_position;
+ buffer_pos_ = start_position;
}
ExternalOneByteStringUtf16CharacterStream::
ExternalOneByteStringUtf16CharacterStream(const char* data, size_t length)
- : raw_data_(reinterpret_cast<const uint8_t*>(data)),
- length_(length),
- bookmark_(kNoBookmark) {}
-
-ExternalOneByteStringUtf16CharacterStream::
- ExternalOneByteStringUtf16CharacterStream(const char* data)
- : ExternalOneByteStringUtf16CharacterStream(data, strlen(data)) {}
-
-bool ExternalOneByteStringUtf16CharacterStream::SetBookmark() {
- bookmark_ = pos_;
- return true;
-}
-
-void ExternalOneByteStringUtf16CharacterStream::ResetToBookmark() {
- DCHECK(bookmark_ != kNoBookmark);
- pos_ = bookmark_;
- buffer_cursor_ = buffer_;
- buffer_end_ = buffer_ + FillBuffer(pos_);
-}
-
-size_t ExternalOneByteStringUtf16CharacterStream::BufferSeekForward(
- size_t delta) {
- size_t old_pos = pos_;
- pos_ = Min(pos_ + delta, length_);
- ReadBlock();
- return pos_ - old_pos;
-}
+ : raw_data_(reinterpret_cast<const uint8_t*>(data)), length_(length) {}
size_t ExternalOneByteStringUtf16CharacterStream::FillBuffer(size_t from_pos) {
if (from_pos >= length_) return 0;
+
size_t length = Min(kBufferSize, length_ - from_pos);
- for (size_t i = 0; i < length; ++i) {
- buffer_[i] = static_cast<uc16>(raw_data_[from_pos + i]);
- }
+ i::CopyCharsUnsigned(buffer_, raw_data_ + from_pos, length);
return length;
}
+// ----------------------------------------------------------------------------
+// Utf8ExternalStreamingStream - chunked streaming of Utf-8 data.
+//
+// This implementation is fairly complex, since data arrives in chunks which
+// may 'cut' arbitrarily into utf-8 characters. Also, seeking to a given
+// character position is tricky because the byte position cannot be dericed
+// from the character position.
+
+class Utf8ExternalStreamingStream : public BufferedUtf16CharacterStream {
+ public:
+ Utf8ExternalStreamingStream(
+ ScriptCompiler::ExternalSourceStream* source_stream)
+ : current_({0, {0, 0, unibrow::Utf8::Utf8IncrementalBuffer(0)}}),
+ source_stream_(source_stream) {}
+ ~Utf8ExternalStreamingStream() override {
+ for (size_t i = 0; i < chunks_.size(); i++) delete[] chunks_[i].data;
+ }
+
+ protected:
+ size_t FillBuffer(size_t position) override;
+
+ private:
+ // A position within the data stream. It stores:
+ // - The 'physical' position (# of bytes in the stream),
+ // - the 'logical' position (# of ucs-2 characters, also within the stream),
+ // - a possibly incomplete utf-8 char at the current 'physical' position.
+ struct StreamPosition {
+ size_t bytes;
+ size_t chars;
+ unibrow::Utf8::Utf8IncrementalBuffer incomplete_char;
+ };
+
+ // Position contains a StreamPosition and the index of the chunk the position
+ // points into. (The chunk_no could be derived from pos, but that'd be
+ // an expensive search through all chunks.)
+ struct Position {
+ size_t chunk_no;
+ StreamPosition pos;
+ };
+
+ // A chunk in the list of chunks, containing:
+ // - The chunk data (data pointer and length), and
+ // - the position at the first byte of the chunk.
+ struct Chunk {
+ const uint8_t* data;
+ size_t length;
+ StreamPosition start;
+ };
+
+ // Within the current chunk, skip forward from current_ towards position.
+ bool SkipToPosition(size_t position);
+ // Within the current chunk, fill the buffer_ (while it has capacity).
+ void FillBufferFromCurrentChunk();
+ // Fetch a new chunk (assuming current_ is at the end of the current data).
+ bool FetchChunk();
+ // Search through the chunks and set current_ to point to the given position.
+ // (This call is potentially expensive.)
+ void SearchPosition(size_t position);
+
+ std::vector<Chunk> chunks_;
+ Position current_;
+ ScriptCompiler::ExternalSourceStream* source_stream_;
+};
+
+bool Utf8ExternalStreamingStream::SkipToPosition(size_t position) {
+ DCHECK_LE(current_.pos.chars, position); // We can only skip forward.
+
+ // Already there? Then return immediately.
+ if (current_.pos.chars == position) return true;
+
+ const Chunk& chunk = chunks_[current_.chunk_no];
+ DCHECK(current_.pos.bytes >= chunk.start.bytes);
+
+ unibrow::Utf8::Utf8IncrementalBuffer incomplete_char =
+ chunk.start.incomplete_char;
+ size_t it = current_.pos.bytes - chunk.start.bytes;
+ size_t chars = chunk.start.chars;
+ while (it < chunk.length && chars < position) {
+ unibrow::uchar t =
+ unibrow::Utf8::ValueOfIncremental(chunk.data[it], &incomplete_char);
+ if (t != unibrow::Utf8::kIncomplete) {
+ chars++;
+ if (t > unibrow::Utf16::kMaxNonSurrogateCharCode) chars++;
+ }
+ it++;
+ }
+
+ current_.pos.bytes += it;
+ current_.pos.chars = chars;
+ current_.pos.incomplete_char = incomplete_char;
+ current_.chunk_no += (it == chunk.length);
+
+ return current_.pos.chars == position;
+}
+
+void Utf8ExternalStreamingStream::FillBufferFromCurrentChunk() {
+ DCHECK_LT(current_.chunk_no, chunks_.size());
+ DCHECK_EQ(buffer_start_, buffer_cursor_);
+ DCHECK_LT(buffer_end_ + 1, buffer_start_ + kBufferSize);
+
+ const Chunk& chunk = chunks_[current_.chunk_no];
+
+ // The buffer_ is writable, but buffer_*_ members are const. So we get a
+ // non-const pointer into buffer that points to the same char as buffer_end_.
+ uint16_t* cursor = buffer_ + (buffer_end_ - buffer_start_);
+ DCHECK_EQ(cursor, buffer_end_);
+
+ // If the current chunk is the last (empty) chunk we'll have to process
+ // any left-over, partial characters.
+ if (chunk.length == 0) {
+ unibrow::uchar t =
+ unibrow::Utf8::ValueOfIncrementalFinish(¤t_.pos.incomplete_char);
+ if (t != unibrow::Utf8::kBufferEmpty) {
+ DCHECK(t < unibrow::Utf16::kMaxNonSurrogateCharCode);
+ *cursor = static_cast<uc16>(t);
+ buffer_end_++;
+ current_.pos.chars++;
+ }
+ return;
+ }
+
+ static const unibrow::uchar kUtf8Bom = 0xfeff;
+
+ unibrow::Utf8::Utf8IncrementalBuffer incomplete_char =
+ current_.pos.incomplete_char;
+ size_t it;
+ for (it = current_.pos.bytes - chunk.start.bytes;
+ it < chunk.length && cursor + 1 < buffer_start_ + kBufferSize; it++) {
+ unibrow::uchar t =
+ unibrow::Utf8::ValueOfIncremental(chunk.data[it], &incomplete_char);
+ if (t == unibrow::Utf8::kIncomplete) continue;
+ if (V8_LIKELY(t < kUtf8Bom)) {
+ *(cursor++) = static_cast<uc16>(t); // The by most frequent case.
+ } else if (t == kUtf8Bom && current_.pos.bytes + it == 2) {
+ // BOM detected at beginning of the stream. Don't copy it.
+ } else if (t <= unibrow::Utf16::kMaxNonSurrogateCharCode) {
+ *(cursor++) = static_cast<uc16>(t);
+ } else {
+ *(cursor++) = unibrow::Utf16::LeadSurrogate(t);
+ *(cursor++) = unibrow::Utf16::TrailSurrogate(t);
+ }
+ }
+
+ current_.pos.bytes = chunk.start.bytes + it;
+ current_.pos.chars += (cursor - buffer_end_);
+ current_.pos.incomplete_char = incomplete_char;
+ current_.chunk_no += (it == chunk.length);
+
+ buffer_end_ = cursor;
+}
+
+bool Utf8ExternalStreamingStream::FetchChunk() {
+ DCHECK_EQ(current_.chunk_no, chunks_.size());
+ DCHECK(chunks_.empty() || chunks_.back().length != 0);
+
+ const uint8_t* chunk = nullptr;
+ size_t length = source_stream_->GetMoreData(&chunk);
+ chunks_.push_back({chunk, length, current_.pos});
+ return length > 0;
+}
+
+void Utf8ExternalStreamingStream::SearchPosition(size_t position) {
+ // If current_ already points to the right position, we're done.
+ //
+ // This is expected to be the common case, since we typically call
+ // FillBuffer right after the current buffer.
+ if (current_.pos.chars == position) return;
+
+ // No chunks. Fetch at least one, so we can assume !chunks_.empty() below.
+ if (chunks_.empty()) {
+ DCHECK_EQ(current_.chunk_no, 0);
+ DCHECK_EQ(current_.pos.bytes, 0);
+ DCHECK_EQ(current_.pos.chars, 0);
+ FetchChunk();
+ }
+
+ // Search for the last chunk whose start position is less or equal to
+ // position.
+ size_t chunk_no = chunks_.size() - 1;
+ while (chunk_no > 0 && chunks_[chunk_no].start.chars > position) {
+ chunk_no--;
+ }
+
+ // Did we find the terminating (zero-length) chunk? Then we're seeking
+ // behind the end of the data, and position does not exist.
+ // Set current_ to point to the terminating chunk.
+ if (chunks_[chunk_no].length == 0) {
+ current_ = {chunk_no, chunks_[chunk_no].start};
+ return;
+ }
+
+ // Did we find the non-last chunk? Then our position must be within chunk_no.
+ if (chunk_no + 1 < chunks_.size()) {
+ // Fancy-pants optimization for ASCII chunks within a utf-8 stream.
+ // (Many web sites declare utf-8 encoding, but use only (or almost only) the
+ // ASCII subset for their JavaScript sources. We can exploit this, by
+ // checking whether the # bytes in a chunk are equal to the # chars, and if
+ // so avoid the expensive SkipToPosition.)
+ bool ascii_only_chunk =
+ (chunks_[chunk_no + 1].start.bytes - chunks_[chunk_no].start.bytes) ==
+ (chunks_[chunk_no + 1].start.chars - chunks_[chunk_no].start.chars);
+ if (ascii_only_chunk) {
+ size_t skip = position - chunks_[chunk_no].start.chars;
+ current_ = {chunk_no,
+ {chunks_[chunk_no].start.bytes + skip,
+ chunks_[chunk_no].start.chars + skip,
+ unibrow::Utf8::Utf8IncrementalBuffer(0)}};
+ } else {
+ current_ = {chunk_no, chunks_[chunk_no].start};
+ SkipToPosition(position);
+ }
+
+ // Since position was within the chunk, SkipToPosition should have found
+ // something.
+ DCHECK_EQ(position, current_.pos.chars);
+ return;
+ }
+
+ // What's left: We're in the last, non-terminating chunk. Our position
+ // may be in the chunk, but it may also be in 'future' chunks, which we'll
+ // have to obtain.
+ DCHECK_EQ(chunk_no, chunks_.size() - 1);
+ current_ = {chunk_no, chunks_[chunk_no].start};
+ bool have_more_data = true;
+ bool found = SkipToPosition(position);
+ while (have_more_data && !found) {
+ DCHECK_EQ(current_.chunk_no, chunks_.size());
+ have_more_data = FetchChunk();
+ found = have_more_data && SkipToPosition(position);
+ }
+
+ // We'll return with a postion != the desired position only if we're out
+ // of data. In that case, we'll point to the terminating chunk.
+ DCHECK_EQ(found, current_.pos.chars == position);
+ DCHECK_EQ(have_more_data, chunks_.back().length != 0);
+ DCHECK_IMPLIES(!found, !have_more_data);
+ DCHECK_IMPLIES(!found, current_.chunk_no == chunks_.size() - 1);
+}
+
+size_t Utf8ExternalStreamingStream::FillBuffer(size_t position) {
+ buffer_cursor_ = buffer_;
+ buffer_end_ = buffer_;
+
+ SearchPosition(position);
+ bool out_of_data = current_.chunk_no != chunks_.size() &&
+ chunks_[current_.chunk_no].length == 0;
+ if (out_of_data) return 0;
+
+ // Fill the buffer, until we have at least one char (or are out of data).
+ // (The embedder might give us 1-byte blocks within a utf-8 char, so we
+ // can't guarantee progress with one chunk. Thus we iterate.)
+ while (!out_of_data && buffer_cursor_ == buffer_end_) {
+ // At end of current data, but there might be more? Then fetch it.
+ if (current_.chunk_no == chunks_.size()) {
+ out_of_data = !FetchChunk();
+ }
+ FillBufferFromCurrentChunk();
+ }
+
+ DCHECK_EQ(current_.pos.chars - position, buffer_end_ - buffer_cursor_);
+ return buffer_end_ - buffer_cursor_;
+}
+
+// ----------------------------------------------------------------------------
+// Chunks - helper for One- + TwoByteExternalStreamingStream
+namespace {
+
+struct Chunk {
+ const uint8_t* data;
+ size_t byte_length;
+ size_t byte_pos;
+};
+
+typedef std::vector<struct Chunk> Chunks;
+
+void DeleteChunks(Chunks& chunks) {
+ for (size_t i = 0; i < chunks.size(); i++) delete[] chunks[i].data;
+}
+
+// Return the chunk index for the chunk containing position.
+// If position is behind the end of the stream, the index of the last,
+// zero-length chunk is returned.
+size_t FindChunk(Chunks& chunks, ScriptCompiler::ExternalSourceStream* source_,
+ size_t position) {
+ size_t end_pos =
+ chunks.empty() ? 0 : (chunks.back().byte_pos + chunks.back().byte_length);
+
+ // Get more data if needed. We usually won't enter the loop body.
+ bool out_of_data = !chunks.empty() && chunks.back().byte_length == 0;
+ while (!out_of_data && end_pos <= position + 1) {
+ const uint8_t* chunk = nullptr;
+ size_t len = source_->GetMoreData(&chunk);
+
+ chunks.push_back({chunk, len, end_pos});
+ end_pos += len;
+ out_of_data = (len == 0);
+ }
+
+ // Here, we should always have at least one chunk, and we either have the
+ // chunk we were looking for, or we're out of data. Also, out_of_data and
+ // end_pos are current (and designate whether we have exhausted the stream,
+ // and the length of data received so far, respectively).
+ DCHECK(!chunks.empty());
+ DCHECK_EQ(end_pos, chunks.back().byte_pos + chunks.back().byte_length);
+ DCHECK_EQ(out_of_data, chunks.back().byte_length == 0);
+ DCHECK(position < end_pos || out_of_data);
+
+ // Edge case: position is behind the end of stream: Return the last (length 0)
+ // chunk to indicate the end of the stream.
+ if (position >= end_pos) {
+ DCHECK(out_of_data);
+ return chunks.size() - 1;
+ }
+
+ // We almost always 'stream', meaning we want data from the last chunk, so
+ // let's look at chunks back-to-front.
+ size_t chunk_no = chunks.size() - 1;
+ while (chunks[chunk_no].byte_pos > position) {
+ DCHECK_NE(chunk_no, 0);
+ chunk_no--;
+ }
+ DCHECK_LE(chunks[chunk_no].byte_pos, position);
+ DCHECK_LT(position, chunks[chunk_no].byte_pos + chunks[chunk_no].byte_length);
+ return chunk_no;
+}
+
+} // anonymous namespace
+
+// ----------------------------------------------------------------------------
+// OneByteExternalStreamingStream
+//
+// A stream of latin-1 encoded, chunked data.
+
+class OneByteExternalStreamingStream : public BufferedUtf16CharacterStream {
+ public:
+ explicit OneByteExternalStreamingStream(
+ ScriptCompiler::ExternalSourceStream* source)
+ : source_(source) {}
+ ~OneByteExternalStreamingStream() override { DeleteChunks(chunks_); }
+
+ protected:
+ size_t FillBuffer(size_t position) override;
+
+ private:
+ Chunks chunks_;
+ ScriptCompiler::ExternalSourceStream* source_;
+};
+
+size_t OneByteExternalStreamingStream::FillBuffer(size_t position) {
+ const Chunk& chunk = chunks_[FindChunk(chunks_, source_, position)];
+ if (chunk.byte_length == 0) return 0;
+
+ size_t start_pos = position - chunk.byte_pos;
+ size_t len = i::Min(kBufferSize, chunk.byte_length - start_pos);
+ i::CopyCharsUnsigned(buffer_, chunk.data + start_pos, len);
+ return len;
+}
+
+// ----------------------------------------------------------------------------
+// TwoByteExternalStreamingStream
+//
+// A stream of ucs-2 data, delivered in chunks. Chunks may be 'cut' into the
+// middle of characters (or even contain only one byte), which adds a bit
+// of complexity. This stream avoid all data copying, except for characters
+// that cross chunk boundaries.
+
+class TwoByteExternalStreamingStream : public Utf16CharacterStream {
+ public:
+ explicit TwoByteExternalStreamingStream(
+ ScriptCompiler::ExternalSourceStream* source);
+ ~TwoByteExternalStreamingStream() override;
+
+ protected:
+ bool ReadBlock() override;
+
+ Chunks chunks_;
+ ScriptCompiler::ExternalSourceStream* source_;
+ uc16 one_char_buffer_;
+};
+
+TwoByteExternalStreamingStream::TwoByteExternalStreamingStream(
+ ScriptCompiler::ExternalSourceStream* source)
+ : Utf16CharacterStream(&one_char_buffer_, &one_char_buffer_,
+ &one_char_buffer_, 0),
+ source_(source),
+ one_char_buffer_(0) {}
+
+TwoByteExternalStreamingStream::~TwoByteExternalStreamingStream() {
+ DeleteChunks(chunks_);
+}
+
+bool TwoByteExternalStreamingStream::ReadBlock() {
+ size_t position = pos();
+
+ // We'll search for the 2nd byte of our character, to make sure we
+ // have enough data for at least one character.
+ size_t chunk_no = FindChunk(chunks_, source_, 2 * position + 1);
+
+ // Out of data? Return 0.
+ if (chunks_[chunk_no].byte_length == 0) {
+ buffer_cursor_ = buffer_start_;
+ buffer_end_ = buffer_start_;
+ return false;
+ }
+
+ Chunk& current = chunks_[chunk_no];
+
+ // Annoying edge case: Chunks may not be 2-byte aligned, meaning that a
+ // character may be split between the previous and the current chunk.
+ // If we find such a lonely byte at the beginning of the chunk, we'll use
+ // one_char_buffer_ to hold the full character.
+ bool lonely_byte = (chunks_[chunk_no].byte_pos == (2 * position + 1));
+ if (lonely_byte) {
+ DCHECK_NE(chunk_no, 0);
+ Chunk& previous_chunk = chunks_[chunk_no - 1];
+#ifdef V8_TARGET_BIG_ENDIAN
+ uc16 character = current.data[0] |
+ previous_chunk.data[previous_chunk.byte_length - 1] << 8;
+#else
+ uc16 character = previous_chunk.data[previous_chunk.byte_length - 1] |
+ current.data[0] << 8;
+#endif
+
+ one_char_buffer_ = character;
+ buffer_pos_ = position;
+ buffer_start_ = &one_char_buffer_;
+ buffer_cursor_ = &one_char_buffer_;
+ buffer_end_ = &one_char_buffer_ + 1;
+ return true;
+ }
+
+ // Common case: character is in current chunk.
+ DCHECK_LE(current.byte_pos, 2 * position);
+ DCHECK_LT(2 * position + 1, current.byte_pos + current.byte_length);
+
+ // Determine # of full ucs-2 chars in stream, and whether we started on an odd
+ // byte boundary.
+ bool odd_start = (current.byte_pos % 2) == 1;
+ size_t number_chars = (current.byte_length - odd_start) / 2;
+
+ // Point the buffer_*_ members into the current chunk and set buffer_cursor_
+ // to point to position. Be careful when converting the byte positions (in
+ // Chunk) to the ucs-2 character positions (in buffer_*_ members).
+ buffer_start_ = reinterpret_cast<const uint16_t*>(current.data + odd_start);
+ buffer_end_ = buffer_start_ + number_chars;
+ buffer_pos_ = (current.byte_pos + odd_start) / 2;
+ buffer_cursor_ = buffer_start_ + (position - buffer_pos_);
+ DCHECK_EQ(position, pos());
+ return true;
+}
+
+// ----------------------------------------------------------------------------
+// ScannerStream: Create stream instances.
+
+Utf16CharacterStream* ScannerStream::For(Handle<String> data) {
+ return ScannerStream::For(data, 0, data->length());
+}
+
+Utf16CharacterStream* ScannerStream::For(Handle<String> data, int start_pos,
+ int end_pos) {
+ DCHECK(start_pos >= 0);
+ DCHECK(end_pos <= data->length());
+ if (data->IsExternalOneByteString()) {
+ return new ExternalOneByteStringUtf16CharacterStream(
+ Handle<ExternalOneByteString>::cast(data), start_pos, end_pos);
+ } else if (data->IsExternalTwoByteString()) {
+ return new ExternalTwoByteStringUtf16CharacterStream(
+ Handle<ExternalTwoByteString>::cast(data), start_pos, end_pos);
+ } else {
+ // TODO(vogelheim): Maybe call data.Flatten() first?
+ return new GenericStringUtf16CharacterStream(data, start_pos, end_pos);
+ }
+}
+
+std::unique_ptr<Utf16CharacterStream> ScannerStream::ForTesting(
+ const char* data) {
+ return ScannerStream::ForTesting(data, strlen(data));
+}
+
+std::unique_ptr<Utf16CharacterStream> ScannerStream::ForTesting(
+ const char* data, size_t length) {
+ return std::unique_ptr<Utf16CharacterStream>(
+ new ExternalOneByteStringUtf16CharacterStream(data, length));
+}
+
+Utf16CharacterStream* ScannerStream::For(
+ ScriptCompiler::ExternalSourceStream* source_stream,
+ v8::ScriptCompiler::StreamedSource::Encoding encoding) {
+ switch (encoding) {
+ case v8::ScriptCompiler::StreamedSource::TWO_BYTE:
+ return new TwoByteExternalStreamingStream(source_stream);
+ case v8::ScriptCompiler::StreamedSource::ONE_BYTE:
+ return new OneByteExternalStreamingStream(source_stream);
+ case v8::ScriptCompiler::StreamedSource::UTF8:
+ return new Utf8ExternalStreamingStream(source_stream);
+ }
+ UNREACHABLE();
+ return nullptr;
+}
+
} // namespace internal
} // namespace v8
diff --git a/src/parsing/scanner-character-streams.h b/src/parsing/scanner-character-streams.h
index 94d8284..ac81613 100644
--- a/src/parsing/scanner-character-streams.h
+++ b/src/parsing/scanner-character-streams.h
@@ -5,187 +5,27 @@
#ifndef V8_PARSING_SCANNER_CHARACTER_STREAMS_H_
#define V8_PARSING_SCANNER_CHARACTER_STREAMS_H_
+#include "include/v8.h" // for v8::ScriptCompiler
#include "src/handles.h"
-#include "src/parsing/scanner.h"
-#include "src/vector.h"
namespace v8 {
namespace internal {
-// Forward declarations.
-class ExternalTwoByteString;
-class ExternalOneByteString;
+class Utf16CharacterStream;
-// 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 {
+class ScannerStream {
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_;
-};
-
-
-// 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_);
- pos_--;
- if (character != kEndOfInput) {
- buffer_cursor_--;
- }
- }
-
- bool SetBookmark() override;
- void ResetToBookmark() override;
-
- private:
- 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;
- }
- const uc16* raw_data_; // Pointer to the actual array of characters.
-
- static const size_t kNoBookmark = -1;
-
- size_t bookmark_;
-};
-
-// UTF16 buffer to read characters from an external latin1 string.
-class ExternalOneByteStringUtf16CharacterStream
- : public BufferedUtf16CharacterStream {
- public:
- ExternalOneByteStringUtf16CharacterStream(Handle<ExternalOneByteString> data,
- int start_position,
- int end_position);
- ~ExternalOneByteStringUtf16CharacterStream() override;
+ static Utf16CharacterStream* For(Handle<String> data);
+ static Utf16CharacterStream* For(Handle<String> data, int start_pos,
+ int end_pos);
+ static Utf16CharacterStream* For(
+ ScriptCompiler::ExternalSourceStream* source_stream,
+ ScriptCompiler::StreamedSource::Encoding encoding);
// For testing:
- explicit ExternalOneByteStringUtf16CharacterStream(const char* data);
- ExternalOneByteStringUtf16CharacterStream(const char* data, size_t length);
-
- bool SetBookmark() override;
- void ResetToBookmark() override;
-
- private:
- static const size_t kNoBookmark = -1;
-
- size_t BufferSeekForward(size_t delta) override;
- size_t FillBuffer(size_t position) override;
-
- const uint8_t* raw_data_; // Pointer to the actual array of characters.
- size_t length_;
- size_t bookmark_;
+ static std::unique_ptr<Utf16CharacterStream> ForTesting(const char* data);
+ static std::unique_ptr<Utf16CharacterStream> ForTesting(const char* data,
+ size_t length);
};
} // namespace internal
diff --git a/src/parsing/scanner.cc b/src/parsing/scanner.cc
index 06ead2e..e41b56f 100644
--- a/src/parsing/scanner.cc
+++ b/src/parsing/scanner.cc
@@ -14,7 +14,7 @@
#include "src/char-predicates-inl.h"
#include "src/conversions-inl.h"
#include "src/list-inl.h"
-#include "src/parsing/parser.h"
+#include "src/parsing/duplicate-finder.h" // For Scanner::FindSymbol
namespace v8 {
namespace internal {
@@ -26,25 +26,60 @@
return isolate->factory()->InternalizeTwoByteString(two_byte_literal());
}
+// ----------------------------------------------------------------------------
+// Scanner::BookmarkScope
-// Default implementation for streams that do not support bookmarks.
-bool Utf16CharacterStream::SetBookmark() { return false; }
-void Utf16CharacterStream::ResetToBookmark() { UNREACHABLE(); }
+const size_t Scanner::BookmarkScope::kBookmarkAtFirstPos =
+ std::numeric_limits<size_t>::max() - 2;
+const size_t Scanner::BookmarkScope::kNoBookmark =
+ std::numeric_limits<size_t>::max() - 1;
+const size_t Scanner::BookmarkScope::kBookmarkWasApplied =
+ std::numeric_limits<size_t>::max();
+void Scanner::BookmarkScope::Set() {
+ DCHECK_EQ(bookmark_, kNoBookmark);
+ DCHECK_EQ(scanner_->next_next_.token, Token::UNINITIALIZED);
+
+ // The first token is a bit special, since current_ will still be
+ // uninitialized. In this case, store kBookmarkAtFirstPos and special-case it
+ // when
+ // applying the bookmark.
+ DCHECK_IMPLIES(
+ scanner_->current_.token == Token::UNINITIALIZED,
+ scanner_->current_.location.beg_pos == scanner_->next_.location.beg_pos);
+ bookmark_ = (scanner_->current_.token == Token::UNINITIALIZED)
+ ? kBookmarkAtFirstPos
+ : scanner_->location().beg_pos;
+}
+
+void Scanner::BookmarkScope::Apply() {
+ DCHECK(HasBeenSet()); // Caller hasn't called SetBookmark.
+ if (bookmark_ == kBookmarkAtFirstPos) {
+ scanner_->SeekNext(0);
+ } else {
+ scanner_->SeekNext(bookmark_);
+ scanner_->Next();
+ DCHECK_EQ(scanner_->location().beg_pos, bookmark_);
+ }
+ bookmark_ = kBookmarkWasApplied;
+}
+
+bool Scanner::BookmarkScope::HasBeenSet() {
+ return bookmark_ != kNoBookmark && bookmark_ != kBookmarkWasApplied;
+}
+
+bool Scanner::BookmarkScope::HasBeenApplied() {
+ return bookmark_ == kBookmarkWasApplied;
+}
// ----------------------------------------------------------------------------
// Scanner
Scanner::Scanner(UnicodeCache* unicode_cache)
: unicode_cache_(unicode_cache),
- bookmark_c0_(kNoBookmark),
octal_pos_(Location::invalid()),
decimal_with_leading_zero_pos_(Location::invalid()),
found_html_comment_(false) {
- 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_;
}
@@ -305,14 +340,14 @@
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;
+ // Don't skip behind the end of input.
+ if (c0_ == kEndOfInput) break;
+
// Advance as long as character is a WhiteSpace or LineTerminator.
// Remember if the latter is the case.
if (unicode_cache_->IsLineTerminator(c0_)) {
@@ -328,25 +363,27 @@
// 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;
- }
-}
+ if (c0_ != '-' || !has_line_terminator_before_next_) break;
+ Advance();
+ if (c0_ != '-') {
+ PushBack('-'); // undo Advance()
+ break;
+ }
+
+ Advance();
+ if (c0_ != '>') {
+ PushBack2('-', '-'); // undo 2x Advance();
+ break;
+ }
+
+ // Treat the rest of the line as a comment.
+ SkipSingleLineComment();
+ }
+
+ // Return whether or not we skipped any characters.
+ return source_pos() != start_position;
+}
Token::Value Scanner::SkipSingleLineComment() {
Advance();
@@ -356,7 +393,7 @@
// 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_)) {
+ while (c0_ != kEndOfInput && !unicode_cache_->IsLineTerminator(c0_)) {
Advance();
}
@@ -366,7 +403,7 @@
Token::Value Scanner::SkipSourceURLComment() {
TryToParseSourceURLComment();
- while (c0_ >= 0 && !unicode_cache_->IsLineTerminator(c0_)) {
+ while (c0_ != kEndOfInput && !unicode_cache_->IsLineTerminator(c0_)) {
Advance();
}
@@ -377,11 +414,11 @@
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;
+ if (c0_ == kEndOfInput || !unicode_cache_->IsWhiteSpace(c0_)) return;
Advance();
LiteralBuffer name;
- while (c0_ >= 0 && !unicode_cache_->IsWhiteSpaceOrLineTerminator(c0_) &&
- c0_ != '=') {
+ while (c0_ != kEndOfInput &&
+ !unicode_cache_->IsWhiteSpaceOrLineTerminator(c0_) && c0_ != '=') {
name.AddChar(c0_);
Advance();
}
@@ -399,10 +436,10 @@
return;
Advance();
value->Reset();
- while (c0_ >= 0 && unicode_cache_->IsWhiteSpace(c0_)) {
+ while (c0_ != kEndOfInput && unicode_cache_->IsWhiteSpace(c0_)) {
Advance();
}
- while (c0_ >= 0 && !unicode_cache_->IsLineTerminator(c0_)) {
+ while (c0_ != kEndOfInput && !unicode_cache_->IsLineTerminator(c0_)) {
// Disallowed characters.
if (c0_ == '"' || c0_ == '\'') {
value->Reset();
@@ -415,7 +452,7 @@
Advance();
}
// Allow whitespace at the end.
- while (c0_ >= 0 && !unicode_cache_->IsLineTerminator(c0_)) {
+ while (c0_ != kEndOfInput && !unicode_cache_->IsLineTerminator(c0_)) {
if (!unicode_cache_->IsWhiteSpace(c0_)) {
value->Reset();
break;
@@ -429,10 +466,10 @@
DCHECK(c0_ == '*');
Advance();
- while (c0_ >= 0) {
+ while (c0_ != kEndOfInput) {
uc32 ch = c0_;
Advance();
- if (c0_ >= 0 && unicode_cache_->IsLineTerminator(ch)) {
+ if (c0_ != kEndOfInput && 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;
@@ -450,24 +487,24 @@
return Token::ILLEGAL;
}
-
Token::Value Scanner::ScanHtmlComment() {
// Check for <!-- comments.
DCHECK(c0_ == '!');
Advance();
- if (c0_ == '-') {
- Advance();
- if (c0_ == '-') {
- found_html_comment_ = true;
- return SkipSingleLineComment();
- }
- PushBack('-'); // undo Advance()
+ if (c0_ != '-') {
+ PushBack('!'); // undo Advance()
+ return Token::LT;
}
- PushBack('!'); // undo Advance()
- DCHECK(c0_ == '!');
- return Token::LT;
-}
+ Advance();
+ if (c0_ != '-') {
+ PushBack2('-', '!'); // undo 2x Advance()
+ return Token::LT;
+ }
+
+ found_html_comment_ = true;
+ return SkipSingleLineComment();
+}
void Scanner::Scan() {
next_.literal_chars = NULL;
@@ -716,7 +753,7 @@
break;
default:
- if (c0_ < 0) {
+ if (c0_ == kEndOfInput) {
token = Token::EOS;
} else if (unicode_cache_->IsIdentifierStart(c0_)) {
token = ScanIdentifierOrKeyword();
@@ -790,7 +827,7 @@
// Positions inside the lookahead token aren't supported.
DCHECK(pos >= current_pos);
if (pos != current_pos) {
- source_->SeekForward(pos - source_->pos());
+ source_->Seek(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
@@ -808,7 +845,8 @@
Advance<capture_raw>();
// Skip escaped newlines.
- if (!in_template_literal && c0_ >= 0 && unicode_cache_->IsLineTerminator(c)) {
+ if (!in_template_literal && c0_ != kEndOfInput &&
+ 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.
@@ -894,7 +932,7 @@
HandleLeadSurrogate();
break;
}
- if (c0_ < 0 || c0_ == '\n' || c0_ == '\r') return Token::ILLEGAL;
+ if (c0_ == kEndOfInput || c0_ == '\n' || c0_ == '\r') return Token::ILLEGAL;
if (c0_ == quote) {
literal.Complete();
Advance<false, false>();
@@ -906,12 +944,12 @@
AddLiteralChar(c);
}
- while (c0_ != quote && c0_ >= 0
- && !unicode_cache_->IsLineTerminator(c0_)) {
+ while (c0_ != quote && c0_ != kEndOfInput &&
+ !unicode_cache_->IsLineTerminator(c0_)) {
uc32 c = c0_;
Advance();
if (c == '\\') {
- if (c0_ < 0 || !ScanEscape<false, false>()) {
+ if (c0_ == kEndOfInput || !ScanEscape<false, false>()) {
return Token::ILLEGAL;
}
} else {
@@ -957,7 +995,7 @@
ReduceRawLiteralLength(2);
break;
} else if (c == '\\') {
- if (c0_ > 0 && unicode_cache_->IsLineTerminator(c0_)) {
+ if (c0_ != kEndOfInput && unicode_cache_->IsLineTerminator(c0_)) {
// The TV of LineContinuation :: \ LineTerminatorSequence is the empty
// code unit sequence.
uc32 lastChar = c0_;
@@ -1155,7 +1193,7 @@
// 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_)))
+ (c0_ != kEndOfInput && unicode_cache_->IsIdentifierStart(c0_)))
return Token::ILLEGAL;
literal.Complete();
@@ -1382,7 +1420,7 @@
}
// Scan the rest of the identifier characters.
- while (c0_ >= 0 && unicode_cache_->IsIdentifierPart(c0_)) {
+ while (c0_ != kEndOfInput && unicode_cache_->IsIdentifierPart(c0_)) {
if (c0_ != '\\') {
uc32 next_char = c0_;
Advance();
@@ -1408,7 +1446,7 @@
Token::Value Scanner::ScanIdentifierSuffix(LiteralScope* literal,
bool escaped) {
// Scan the rest of the identifier characters.
- while (c0_ >= 0 && unicode_cache_->IsIdentifierPart(c0_)) {
+ while (c0_ != kEndOfInput && unicode_cache_->IsIdentifierPart(c0_)) {
if (c0_ == '\\') {
uc32 c = ScanIdentifierUnicodeEscape();
escaped = true;
@@ -1465,10 +1503,12 @@
}
while (c0_ != '/' || in_character_class) {
- if (c0_ < 0 || unicode_cache_->IsLineTerminator(c0_)) return false;
+ if (c0_ == kEndOfInput || unicode_cache_->IsLineTerminator(c0_))
+ return false;
if (c0_ == '\\') { // Escape sequence.
AddLiteralCharAdvance();
- if (c0_ < 0 || unicode_cache_->IsLineTerminator(c0_)) return false;
+ if (c0_ == kEndOfInput || 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),
@@ -1499,7 +1539,7 @@
// Scan regular expression flags.
int flags = 0;
- while (c0_ >= 0 && unicode_cache_->IsIdentifierPart(c0_)) {
+ while (c0_ != kEndOfInput && unicode_cache_->IsIdentifierPart(c0_)) {
RegExp::Flags flag = RegExp::kNone;
switch (c0_) {
case 'g':
@@ -1574,202 +1614,31 @@
int Scanner::FindSymbol(DuplicateFinder* finder, int value) {
+ // TODO(vogelheim): Move this logic into the calling class; this can be fully
+ // implemented using the public interface.
if (is_literal_one_byte()) {
return finder->AddOneByteSymbol(literal_one_byte_string(), value);
}
return finder->AddTwoByteSymbol(literal_two_byte_string(), value);
}
+void Scanner::SeekNext(size_t position) {
+ // Use with care: This cleanly resets most, but not all scanner state.
+ // TODO(vogelheim): Fix this, or at least DCHECK the relevant conditions.
-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_;
- CopyToNextTokenDesc(&bookmark_current_);
- current_ = next_;
- CopyToNextTokenDesc(&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::CopyToNextTokenDesc(TokenDesc* from) {
- StartLiteral();
- StartRawLiteral();
- CopyTokenDesc(&next_, from);
- if (next_.literal_chars->length() == 0) next_.literal_chars = nullptr;
- if (next_.raw_literal_chars->length() == 0) next_.raw_literal_chars = nullptr;
-}
-
-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);
- base::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();
+ // To re-scan from a given character position, we need to:
+ // 1, Reset the current_, next_ and next_next_ tokens
+ // (next_ + next_next_ will be overwrittem by Next(),
+ // current_ will remain unchanged, so overwrite it fully.)
+ current_ = {{0, 0}, nullptr, nullptr, 0, Token::UNINITIALIZED};
+ next_.token = Token::UNINITIALIZED;
+ next_next_.token = Token::UNINITIALIZED;
+ // 2, reset the source to the desired position,
+ source_->Seek(position);
+ // 3, re-scan, by scanning the look-ahead char + 1 token (next_).
+ c0_ = source_->Advance();
+ Next();
+ DCHECK_EQ(next_.location.beg_pos, position);
}
} // namespace internal
diff --git a/src/parsing/scanner.h b/src/parsing/scanner.h
index 66c6ce8..b2b1a8a 100644
--- a/src/parsing/scanner.h
+++ b/src/parsing/scanner.h
@@ -8,12 +8,9 @@
#define V8_PARSING_SCANNER_H_
#include "src/allocation.h"
-#include "src/base/hashmap.h"
#include "src/base/logging.h"
#include "src/char-predicates.h"
-#include "src/collector.h"
#include "src/globals.h"
-#include "src/list.h"
#include "src/messages.h"
#include "src/parsing/token.h"
#include "src/unicode-decoder.h"
@@ -25,127 +22,127 @@
class AstRawString;
class AstValueFactory;
+class DuplicateFinder;
+class ExternalOneByteString;
+class ExternalTwoByteString;
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) { }
+ static const uc32 kEndOfInput = -1;
+
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.
+ // stream. If there are no more code units it returns kEndOfInput.
inline uc32 Advance() {
- if (buffer_cursor_ < buffer_end_ || ReadBlock()) {
- pos_++;
+ if (V8_LIKELY(buffer_cursor_ < buffer_end_)) {
return static_cast<uc32>(*(buffer_cursor_++));
+ } else if (ReadBlock()) {
+ return static_cast<uc32>(*(buffer_cursor_++));
+ } else {
+ // 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.
+ buffer_cursor_++;
+ return kEndOfInput;
}
- // 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;
+ // Go back one by one character in the input stream.
+ // This undoes the most recent Advance().
+ inline void Back() {
+ // The common case - if the previous character is within
+ // buffer_start_ .. buffer_end_ will be handles locally.
+ // Otherwise, a new block is requested.
+ if (V8_LIKELY(buffer_cursor_ > buffer_start_)) {
+ buffer_cursor_--;
+ } else {
+ ReadBlockAt(pos() - 1);
}
- 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;
+ // Go back one by two characters in the input stream. (This is the same as
+ // calling Back() twice. But Back() may - in some instances - do substantial
+ // work. Back2() guarantees this work will be done only once.)
+ inline void Back2() {
+ if (V8_LIKELY(buffer_cursor_ - 2 >= buffer_start_)) {
+ buffer_cursor_ -= 2;
+ } else {
+ ReadBlockAt(pos() - 2);
+ }
+ }
- virtual bool SetBookmark();
- virtual void ResetToBookmark();
+ inline size_t pos() const {
+ return buffer_pos_ + (buffer_cursor_ - buffer_start_);
+ }
+
+ inline void Seek(size_t pos) {
+ if (V8_LIKELY(pos >= buffer_pos_ &&
+ pos < (buffer_pos_ + (buffer_end_ - buffer_start_)))) {
+ buffer_cursor_ = buffer_start_ + (pos - buffer_pos_);
+ } else {
+ ReadBlockAt(pos);
+ }
+ }
protected:
- static const uc32 kEndOfInput = -1;
+ Utf16CharacterStream(const uint16_t* buffer_start,
+ const uint16_t* buffer_cursor,
+ const uint16_t* buffer_end, size_t buffer_pos)
+ : buffer_start_(buffer_start),
+ buffer_cursor_(buffer_cursor),
+ buffer_end_(buffer_end),
+ buffer_pos_(buffer_pos) {}
+ Utf16CharacterStream() : Utf16CharacterStream(nullptr, nullptr, nullptr, 0) {}
- // 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.
+ void ReadBlockAt(size_t new_pos) {
+ // The callers of this method (Back/Back2/Seek) should handle the easy
+ // case (seeking within the current buffer), and we should only get here
+ // if we actually require new data.
+ // (This is really an efficiency check, not a correctness invariant.)
+ DCHECK(new_pos < buffer_pos_ ||
+ new_pos >= buffer_pos_ + (buffer_end_ - buffer_start_));
+
+ // Change pos() to point to new_pos.
+ buffer_pos_ = new_pos;
+ buffer_cursor_ = buffer_start_;
+ bool success = ReadBlock();
+ USE(success);
+
+ // Post-conditions: 1, on success, we should be at the right position.
+ // 2, success == we should have more characters available.
+ DCHECK_IMPLIES(success, pos() == new_pos);
+ DCHECK_EQ(success, buffer_cursor_ < buffer_end_);
+ DCHECK_EQ(success, buffer_start_ < buffer_end_);
+ }
+
+ // Read more data, and update buffer_*_ to point to it.
+ // Returns true if more data was available.
+ //
+ // ReadBlock() may modify any of the buffer_*_ members, but must sure that
+ // the result of pos() remains unaffected.
+ //
+ // Examples:
+ // - a stream could either fill a separate buffer. Then buffer_start_ and
+ // buffer_cursor_ would point to the beginning of the buffer, and
+ // buffer_pos would be the old pos().
+ // - a stream with existing buffer chunks would set buffer_start_ and
+ // buffer_end_ to cover the full chunk, and then buffer_cursor_ would
+ // point into the middle of the buffer, while buffer_pos_ would describe
+ // the start of the buffer.
virtual bool ReadBlock() = 0;
- virtual size_t SlowSeekForward(size_t code_unit_count) = 0;
+ const uint16_t* buffer_start_;
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_;
- base::HashMap map_;
- // Buffer used for string->number->canonical string conversions.
- char number_buffer_[kBufferSize];
+ size_t buffer_pos_;
};
@@ -157,18 +154,24 @@
// Scoped helper for a re-settable bookmark.
class BookmarkScope {
public:
- explicit BookmarkScope(Scanner* scanner) : scanner_(scanner) {
+ explicit BookmarkScope(Scanner* scanner)
+ : scanner_(scanner), bookmark_(kNoBookmark) {
DCHECK_NOT_NULL(scanner_);
}
- ~BookmarkScope() { scanner_->DropBookmark(); }
+ ~BookmarkScope() {}
- bool Set() { return scanner_->SetBookmark(); }
- void Reset() { scanner_->ResetToBookmark(); }
- bool HasBeenSet() { return scanner_->BookmarkHasBeenSet(); }
- bool HasBeenReset() { return scanner_->BookmarkHasBeenReset(); }
+ void Set();
+ void Apply();
+ bool HasBeenSet();
+ bool HasBeenApplied();
private:
+ static const size_t kNoBookmark;
+ static const size_t kBookmarkWasApplied;
+ static const size_t kBookmarkAtFirstPos;
+
Scanner* scanner_;
+ size_t bookmark_;
DISALLOW_COPY_AND_ASSIGN(BookmarkScope);
};
@@ -190,6 +193,7 @@
// -1 is outside of the range of any real source code.
static const int kNoOctalLocation = -1;
+ static const uc32 kEndOfInput = Utf16CharacterStream::kEndOfInput;
explicit Scanner(UnicodeCache* scanner_contants);
@@ -251,7 +255,7 @@
return LiteralMatches(data, length, false);
}
- void IsGetOrSet(bool* is_get, bool* is_set) {
+ bool IsGetOrSet(bool* is_get, bool* is_set) {
if (is_literal_one_byte() &&
literal_length() == 3 &&
!literal_contains_escapes()) {
@@ -259,7 +263,9 @@
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;
+ return *is_get || *is_set;
}
+ return false;
}
int FindSymbol(DuplicateFinder* finder, int value);
@@ -418,23 +424,6 @@
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_;
- if (position_ < backing_store_.length()) {
- std::copy(other->backing_store_.begin(),
- other->backing_store_.begin() + position_,
- backing_store_.begin());
- } else {
- backing_store_.Dispose();
- backing_store_ = other->backing_store_.Clone();
- }
- }
- }
-
private:
static const int kInitialCapacity = 16;
static const int kGrowthFactory = 4;
@@ -528,15 +517,6 @@
scanner_error_ = MessageTemplate::kNone;
}
- // Support BookmarkScope functionality.
- bool SetBookmark();
- void ResetToBookmark();
- bool BookmarkHasBeenSet();
- bool BookmarkHasBeenReset();
- void DropBookmark();
- void CopyToNextTokenDesc(TokenDesc* from);
- static void CopyTokenDesc(TokenDesc* to, TokenDesc* from);
-
void ReportScannerError(const Location& location,
MessageTemplate::Template error) {
if (has_error()) return;
@@ -550,6 +530,9 @@
scanner_error_location_ = Location(pos, pos + 1);
}
+ // Seek to the next_ token at the given position.
+ void SeekNext(size_t position);
+
// Literal buffer support
inline void StartLiteral() {
LiteralBuffer* free_buffer =
@@ -618,7 +601,7 @@
if (unibrow::Utf16::IsLeadSurrogate(c0_)) {
uc32 c1 = source_->Advance();
if (!unibrow::Utf16::IsTrailSurrogate(c1)) {
- source_->PushBack(c1);
+ source_->Back();
} else {
c0_ = unibrow::Utf16::CombineSurrogatePair(c0_, c1);
}
@@ -627,14 +610,22 @@
void PushBack(uc32 ch) {
if (c0_ > static_cast<uc32>(unibrow::Utf16::kMaxNonSurrogateCharCode)) {
- source_->PushBack(unibrow::Utf16::TrailSurrogate(c0_));
- source_->PushBack(unibrow::Utf16::LeadSurrogate(c0_));
+ source_->Back2();
} else {
- source_->PushBack(c0_);
+ source_->Back();
}
c0_ = ch;
}
+ // Same as PushBack(ch1); PushBack(ch2).
+ // - Potentially more efficient as it uses Back2() on the stream.
+ // - Uses char as parameters, since we're only calling it with ASCII chars in
+ // practice. This way, we can avoid a few edge cases.
+ void PushBack2(char ch1, char ch2) {
+ source_->Back2();
+ c0_ = ch2;
+ }
+
inline Token::Value Select(Token::Value tok) {
Advance();
return tok;
@@ -790,37 +781,6 @@
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_;