// Copyright 2012 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_AST_AST_H_ #define V8_AST_AST_H_ #include #include "src/ast/ast-value-factory.h" #include "src/ast/modules.h" #include "src/ast/variables.h" #include "src/base/threaded-list.h" #include "src/codegen/bailout-reason.h" #include "src/codegen/label.h" #include "src/common/globals.h" #include "src/execution/isolate.h" #include "src/heap/factory.h" #include "src/objects/function-syntax-kind.h" #include "src/objects/literal-objects.h" #include "src/objects/smi.h" #include "src/parsing/token.h" #include "src/runtime/runtime.h" namespace v8 { namespace internal { // The abstract syntax tree is an intermediate, light-weight // representation of the parsed JavaScript code suitable for // compilation to native code. // Nodes are allocated in a separate zone, which allows faster // allocation and constant-time deallocation of the entire syntax // tree. // ---------------------------------------------------------------------------- // Nodes of the abstract syntax tree. Only concrete classes are // enumerated here. #define DECLARATION_NODE_LIST(V) \ V(VariableDeclaration) \ V(FunctionDeclaration) #define ITERATION_NODE_LIST(V) \ V(DoWhileStatement) \ V(WhileStatement) \ V(ForStatement) \ V(ForInStatement) \ V(ForOfStatement) #define BREAKABLE_NODE_LIST(V) \ V(Block) \ V(SwitchStatement) #define STATEMENT_NODE_LIST(V) \ ITERATION_NODE_LIST(V) \ BREAKABLE_NODE_LIST(V) \ V(ExpressionStatement) \ V(EmptyStatement) \ V(SloppyBlockFunctionStatement) \ V(IfStatement) \ V(ContinueStatement) \ V(BreakStatement) \ V(ReturnStatement) \ V(WithStatement) \ V(TryCatchStatement) \ V(TryFinallyStatement) \ V(DebuggerStatement) \ V(InitializeClassMembersStatement) #define LITERAL_NODE_LIST(V) \ V(RegExpLiteral) \ V(ObjectLiteral) \ V(ArrayLiteral) #define EXPRESSION_NODE_LIST(V) \ LITERAL_NODE_LIST(V) \ V(Assignment) \ V(Await) \ V(BinaryOperation) \ V(NaryOperation) \ V(Call) \ V(CallNew) \ V(CallRuntime) \ V(ClassLiteral) \ V(CompareOperation) \ V(CompoundAssignment) \ V(Conditional) \ V(CountOperation) \ V(DoExpression) \ V(EmptyParentheses) \ V(FunctionLiteral) \ V(GetTemplateObject) \ V(ImportCallExpression) \ V(Literal) \ V(NativeFunctionLiteral) \ V(OptionalChain) \ V(Property) \ V(ResolvedProperty) \ V(Spread) \ V(StoreInArrayLiteral) \ V(SuperCallReference) \ V(SuperPropertyReference) \ V(TemplateLiteral) \ V(ThisExpression) \ V(Throw) \ V(UnaryOperation) \ V(VariableProxy) \ V(Yield) \ V(YieldStar) #define FAILURE_NODE_LIST(V) V(FailureExpression) #define AST_NODE_LIST(V) \ DECLARATION_NODE_LIST(V) \ STATEMENT_NODE_LIST(V) \ EXPRESSION_NODE_LIST(V) // Forward declarations class AstNode; class AstNodeFactory; class Declaration; class BreakableStatement; class Expression; class IterationStatement; class MaterializedLiteral; class NestedVariableDeclaration; class ProducedPreparseData; class Statement; #define DEF_FORWARD_DECLARATION(type) class type; AST_NODE_LIST(DEF_FORWARD_DECLARATION) FAILURE_NODE_LIST(DEF_FORWARD_DECLARATION) #undef DEF_FORWARD_DECLARATION class AstNode: public ZoneObject { public: #define DECLARE_TYPE_ENUM(type) k##type, enum NodeType : uint8_t { AST_NODE_LIST(DECLARE_TYPE_ENUM) /* , */ FAILURE_NODE_LIST(DECLARE_TYPE_ENUM) }; #undef DECLARE_TYPE_ENUM void* operator new(size_t size, Zone* zone) { return zone->New(size); } NodeType node_type() const { return NodeTypeField::decode(bit_field_); } int position() const { return position_; } #ifdef DEBUG void Print(Isolate* isolate); #endif // DEBUG // Type testing & conversion functions overridden by concrete subclasses. #define DECLARE_NODE_FUNCTIONS(type) \ V8_INLINE bool Is##type() const; \ V8_INLINE type* As##type(); \ V8_INLINE const type* As##type() const; AST_NODE_LIST(DECLARE_NODE_FUNCTIONS) FAILURE_NODE_LIST(DECLARE_NODE_FUNCTIONS) #undef DECLARE_NODE_FUNCTIONS IterationStatement* AsIterationStatement(); MaterializedLiteral* AsMaterializedLiteral(); private: // Hidden to prevent accidental usage. It would have to load the // current zone from the TLS. void* operator new(size_t size); int position_; using NodeTypeField = BitField; protected: uint32_t bit_field_; template using NextBitField = NodeTypeField::Next; AstNode(int position, NodeType type) : position_(position), bit_field_(NodeTypeField::encode(type)) {} }; class Statement : public AstNode { protected: Statement(int position, NodeType type) : AstNode(position, type) {} }; class Expression : public AstNode { public: enum Context { // Not assigned a context yet, or else will not be visited during // code generation. kUninitialized, // Evaluated for its side effects. kEffect, // Evaluated for its value (and side effects). kValue, // Evaluated for control flow (and side effects). kTest }; // True iff the expression is a valid reference expression. bool IsValidReferenceExpression() const; // True iff the expression is a private name. bool IsPrivateName() const; // Helpers for ToBoolean conversion. bool ToBooleanIsTrue() const; bool ToBooleanIsFalse() const; // Symbols that cannot be parsed as array indices are considered property // names. We do not treat symbols that can be array indexes as property // names because [] for string objects is handled only by keyed ICs. bool IsPropertyName() const; // True iff the expression is a class or function expression without // a syntactic name. bool IsAnonymousFunctionDefinition() const; // True iff the expression is a concise method definition. bool IsConciseMethodDefinition() const; // True iff the expression is an accessor function definition. bool IsAccessorFunctionDefinition() const; // True iff the expression is a literal represented as a smi. bool IsSmiLiteral() const; // True iff the expression is a literal represented as a number. V8_EXPORT_PRIVATE bool IsNumberLiteral() const; // True iff the expression is a string literal. bool IsStringLiteral() const; // True iff the expression is the null literal. bool IsNullLiteral() const; // True iff the expression is the hole literal. bool IsTheHoleLiteral() const; // True if we can prove that the expression is the undefined literal. Note // that this also checks for loads of the global "undefined" variable. bool IsUndefinedLiteral() const; // True if either null literal or undefined literal. inline bool IsNullOrUndefinedLiteral() const { return IsNullLiteral() || IsUndefinedLiteral(); } // True if a literal and not null or undefined. bool IsLiteralButNotNullOrUndefined() const; bool IsCompileTimeValue(); bool IsPattern() { STATIC_ASSERT(kObjectLiteral + 1 == kArrayLiteral); return IsInRange(node_type(), kObjectLiteral, kArrayLiteral); } bool is_parenthesized() const { return IsParenthesizedField::decode(bit_field_); } void mark_parenthesized() { bit_field_ = IsParenthesizedField::update(bit_field_, true); } void clear_parenthesized() { bit_field_ = IsParenthesizedField::update(bit_field_, false); } private: using IsParenthesizedField = AstNode::NextBitField; protected: Expression(int pos, NodeType type) : AstNode(pos, type) { DCHECK(!is_parenthesized()); } template using NextBitField = IsParenthesizedField::Next; }; class FailureExpression : public Expression { private: friend class AstNodeFactory; FailureExpression() : Expression(kNoSourcePosition, kFailureExpression) {} }; // V8's notion of BreakableStatement does not correspond to the notion of // BreakableStatement in ECMAScript. In V8, the idea is that a // BreakableStatement is a statement that can be the target of a break // statement. The BreakableStatement AST node carries a list of labels, any of // which can be used as an argument to the break statement in order to target // it. // // Since we don't want to attach a list of labels to all kinds of statements, we // only declare switchs, loops, and blocks as BreakableStatements. This means // that we implement breaks targeting other statement forms as breaks targeting // a substatement thereof. For instance, in "foo: if (b) { f(); break foo; }" we // pretend that foo is the label of the inner block. That's okay because one // can't observe the difference. // // This optimization makes it harder to detect invalid continue labels, see the // need for own_labels in IterationStatement. // class BreakableStatement : public Statement { public: enum BreakableType { TARGET_FOR_ANONYMOUS, TARGET_FOR_NAMED_ONLY }; // A list of all labels declared on the path up to the previous // BreakableStatement (if any). // // Example: "l1: for (;;) l2: l3: { l4: if (b) l5: { s } }" // labels() of the ForStatement will be l1. // labels() of the Block { l4: ... } will be l2, l3. // labels() of the Block { s } will be l4, l5. ZonePtrList* labels() const; // Testers. bool is_target_for_anonymous() const { return BreakableTypeField::decode(bit_field_) == TARGET_FOR_ANONYMOUS; } private: using BreakableTypeField = Statement::NextBitField; protected: BreakableStatement(BreakableType breakable_type, int position, NodeType type) : Statement(position, type) { bit_field_ |= BreakableTypeField::encode(breakable_type); } template using NextBitField = BreakableTypeField::Next; }; class Block : public BreakableStatement { public: ZonePtrList* statements() { return &statements_; } bool ignore_completion_value() const { return IgnoreCompletionField::decode(bit_field_); } inline ZonePtrList* labels() const; Scope* scope() const { return scope_; } void set_scope(Scope* scope) { scope_ = scope; } void InitializeStatements(const ScopedPtrList& statements, Zone* zone) { DCHECK_EQ(0, statements_.length()); statements.CopyTo(&statements_, zone); } private: friend class AstNodeFactory; ZonePtrList statements_; Scope* scope_; using IgnoreCompletionField = BreakableStatement::NextBitField; using IsLabeledField = IgnoreCompletionField::Next; protected: Block(Zone* zone, ZonePtrList* labels, int capacity, bool ignore_completion_value) : BreakableStatement(TARGET_FOR_NAMED_ONLY, kNoSourcePosition, kBlock), statements_(capacity, zone), scope_(nullptr) { bit_field_ |= IgnoreCompletionField::encode(ignore_completion_value) | IsLabeledField::encode(labels != nullptr); } Block(ZonePtrList* labels, bool ignore_completion_value) : Block(nullptr, labels, 0, ignore_completion_value) {} }; class LabeledBlock final : public Block { private: friend class AstNodeFactory; friend class Block; LabeledBlock(Zone* zone, ZonePtrList* labels, int capacity, bool ignore_completion_value) : Block(zone, labels, capacity, ignore_completion_value), labels_(labels) { DCHECK_NOT_NULL(labels); DCHECK_GT(labels->length(), 0); } LabeledBlock(ZonePtrList* labels, bool ignore_completion_value) : LabeledBlock(nullptr, labels, 0, ignore_completion_value) {} ZonePtrList* labels_; }; inline ZonePtrList* Block::labels() const { if (IsLabeledField::decode(bit_field_)) { return static_cast(this)->labels_; } return nullptr; } class DoExpression final : public Expression { public: Block* block() { return block_; } VariableProxy* result() { return result_; } private: friend class AstNodeFactory; DoExpression(Block* block, VariableProxy* result, int pos) : Expression(pos, kDoExpression), block_(block), result_(result) { DCHECK_NOT_NULL(block_); DCHECK_NOT_NULL(result_); } Block* block_; VariableProxy* result_; }; class Declaration : public AstNode { public: using List = base::ThreadedList; Variable* var() const { return var_; } void set_var(Variable* var) { var_ = var; } protected: Declaration(int pos, NodeType type) : AstNode(pos, type), next_(nullptr) {} private: Variable* var_; // Declarations list threaded through the declarations. Declaration** next() { return &next_; } Declaration* next_; friend List; friend base::ThreadedListTraits; }; class VariableDeclaration : public Declaration { public: inline NestedVariableDeclaration* AsNested(); private: friend class AstNodeFactory; using IsNestedField = Declaration::NextBitField; protected: explicit VariableDeclaration(int pos, bool is_nested = false) : Declaration(pos, kVariableDeclaration) { bit_field_ = IsNestedField::update(bit_field_, is_nested); } template using NextBitField = IsNestedField::Next; }; // For var declarations that appear in a block scope. // Only distinguished from VariableDeclaration during Scope analysis, // so it doesn't get its own NodeType. class NestedVariableDeclaration final : public VariableDeclaration { public: Scope* scope() const { return scope_; } private: friend class AstNodeFactory; NestedVariableDeclaration(Scope* scope, int pos) : VariableDeclaration(pos, true), scope_(scope) {} // Nested scope from which the declaration originated. Scope* scope_; }; inline NestedVariableDeclaration* VariableDeclaration::AsNested() { return IsNestedField::decode(bit_field_) ? static_cast(this) : nullptr; } class FunctionDeclaration final : public Declaration { public: FunctionLiteral* fun() const { return fun_; } private: friend class AstNodeFactory; FunctionDeclaration(FunctionLiteral* fun, int pos) : Declaration(pos, kFunctionDeclaration), fun_(fun) {} FunctionLiteral* fun_; }; class IterationStatement : public BreakableStatement { public: Statement* body() const { return body_; } void set_body(Statement* s) { body_ = s; } ZonePtrList* labels() const { return labels_; } // A list of all labels that the iteration statement is directly prefixed // with, i.e. all the labels that a continue statement in the body can use to // continue this iteration statement. This is always a subset of {labels}. // // Example: "l1: { l2: if (b) l3: l4: for (;;) s }" // labels() of the Block will be l1. // labels() of the ForStatement will be l2, l3, l4. // own_labels() of the ForStatement will be l3, l4. ZonePtrList* own_labels() const { return own_labels_; } protected: IterationStatement(ZonePtrList* labels, ZonePtrList* own_labels, int pos, NodeType type) : BreakableStatement(TARGET_FOR_ANONYMOUS, pos, type), labels_(labels), own_labels_(own_labels), body_(nullptr) {} void Initialize(Statement* body) { body_ = body; } private: ZonePtrList* labels_; ZonePtrList* own_labels_; Statement* body_; }; class DoWhileStatement final : public IterationStatement { public: void Initialize(Expression* cond, Statement* body) { IterationStatement::Initialize(body); cond_ = cond; } Expression* cond() const { return cond_; } private: friend class AstNodeFactory; DoWhileStatement(ZonePtrList* labels, ZonePtrList* own_labels, int pos) : IterationStatement(labels, own_labels, pos, kDoWhileStatement), cond_(nullptr) {} Expression* cond_; }; class WhileStatement final : public IterationStatement { public: void Initialize(Expression* cond, Statement* body) { IterationStatement::Initialize(body); cond_ = cond; } Expression* cond() const { return cond_; } private: friend class AstNodeFactory; WhileStatement(ZonePtrList* labels, ZonePtrList* own_labels, int pos) : IterationStatement(labels, own_labels, pos, kWhileStatement), cond_(nullptr) {} Expression* cond_; }; class ForStatement final : public IterationStatement { public: void Initialize(Statement* init, Expression* cond, Statement* next, Statement* body) { IterationStatement::Initialize(body); init_ = init; cond_ = cond; next_ = next; } Statement* init() const { return init_; } Expression* cond() const { return cond_; } Statement* next() const { return next_; } private: friend class AstNodeFactory; ForStatement(ZonePtrList* labels, ZonePtrList* own_labels, int pos) : IterationStatement(labels, own_labels, pos, kForStatement), init_(nullptr), cond_(nullptr), next_(nullptr) {} Statement* init_; Expression* cond_; Statement* next_; }; // Shared class for for-in and for-of statements. class ForEachStatement : public IterationStatement { public: enum VisitMode { ENUMERATE, // for (each in subject) body; ITERATE // for (each of subject) body; }; using IterationStatement::Initialize; static const char* VisitModeString(VisitMode mode) { return mode == ITERATE ? "for-of" : "for-in"; } void Initialize(Expression* each, Expression* subject, Statement* body) { IterationStatement::Initialize(body); each_ = each; subject_ = subject; } Expression* each() const { return each_; } Expression* subject() const { return subject_; } protected: friend class AstNodeFactory; ForEachStatement(ZonePtrList* labels, ZonePtrList* own_labels, int pos, NodeType type) : IterationStatement(labels, own_labels, pos, type), each_(nullptr), subject_(nullptr) {} Expression* each_; Expression* subject_; }; class ForInStatement final : public ForEachStatement { private: friend class AstNodeFactory; ForInStatement(ZonePtrList* labels, ZonePtrList* own_labels, int pos) : ForEachStatement(labels, own_labels, pos, kForInStatement) {} }; enum class IteratorType { kNormal, kAsync }; class ForOfStatement final : public ForEachStatement { public: IteratorType type() const { return type_; } private: friend class AstNodeFactory; ForOfStatement(ZonePtrList* labels, ZonePtrList* own_labels, int pos, IteratorType type) : ForEachStatement(labels, own_labels, pos, kForOfStatement), type_(type) {} IteratorType type_; }; class ExpressionStatement final : public Statement { public: void set_expression(Expression* e) { expression_ = e; } Expression* expression() const { return expression_; } private: friend class AstNodeFactory; ExpressionStatement(Expression* expression, int pos) : Statement(pos, kExpressionStatement), expression_(expression) {} Expression* expression_; }; class JumpStatement : public Statement { protected: JumpStatement(int pos, NodeType type) : Statement(pos, type) {} }; class ContinueStatement final : public JumpStatement { public: IterationStatement* target() const { return target_; } private: friend class AstNodeFactory; ContinueStatement(IterationStatement* target, int pos) : JumpStatement(pos, kContinueStatement), target_(target) {} IterationStatement* target_; }; class BreakStatement final : public JumpStatement { public: BreakableStatement* target() const { return target_; } private: friend class AstNodeFactory; BreakStatement(BreakableStatement* target, int pos) : JumpStatement(pos, kBreakStatement), target_(target) {} BreakableStatement* target_; }; class ReturnStatement final : public JumpStatement { public: enum Type { kNormal, kAsyncReturn, kSyntheticAsyncReturn }; Expression* expression() const { return expression_; } Type type() const { return TypeField::decode(bit_field_); } bool is_async_return() const { return type() != kNormal; } bool is_synthetic_async_return() const { return type() == kSyntheticAsyncReturn; } int end_position() const { return end_position_; } private: friend class AstNodeFactory; ReturnStatement(Expression* expression, Type type, int pos, int end_position) : JumpStatement(pos, kReturnStatement), expression_(expression), end_position_(end_position) { bit_field_ |= TypeField::encode(type); } Expression* expression_; int end_position_; using TypeField = JumpStatement::NextBitField; }; class WithStatement final : public Statement { public: Scope* scope() { return scope_; } Expression* expression() const { return expression_; } Statement* statement() const { return statement_; } void set_statement(Statement* s) { statement_ = s; } private: friend class AstNodeFactory; WithStatement(Scope* scope, Expression* expression, Statement* statement, int pos) : Statement(pos, kWithStatement), scope_(scope), expression_(expression), statement_(statement) {} Scope* scope_; Expression* expression_; Statement* statement_; }; class CaseClause final : public ZoneObject { public: bool is_default() const { return label_ == nullptr; } Expression* label() const { DCHECK(!is_default()); return label_; } ZonePtrList* statements() { return &statements_; } private: friend class AstNodeFactory; CaseClause(Zone* zone, Expression* label, const ScopedPtrList& statements); Expression* label_; ZonePtrList statements_; }; class SwitchStatement final : public BreakableStatement { public: ZonePtrList* labels() const { return labels_; } Expression* tag() const { return tag_; } void set_tag(Expression* t) { tag_ = t; } ZonePtrList* cases() { return &cases_; } private: friend class AstNodeFactory; SwitchStatement(Zone* zone, ZonePtrList* labels, Expression* tag, int pos) : BreakableStatement(TARGET_FOR_ANONYMOUS, pos, kSwitchStatement), labels_(labels), tag_(tag), cases_(4, zone) {} ZonePtrList* labels_; Expression* tag_; ZonePtrList cases_; }; // If-statements always have non-null references to their then- and // else-parts. When parsing if-statements with no explicit else-part, // the parser implicitly creates an empty statement. Use the // HasThenStatement() and HasElseStatement() functions to check if a // given if-statement has a then- or an else-part containing code. class IfStatement final : public Statement { public: bool HasThenStatement() const { return !then_statement_->IsEmptyStatement(); } bool HasElseStatement() const { return !else_statement_->IsEmptyStatement(); } Expression* condition() const { return condition_; } Statement* then_statement() const { return then_statement_; } Statement* else_statement() const { return else_statement_; } void set_then_statement(Statement* s) { then_statement_ = s; } void set_else_statement(Statement* s) { else_statement_ = s; } private: friend class AstNodeFactory; IfStatement(Expression* condition, Statement* then_statement, Statement* else_statement, int pos) : Statement(pos, kIfStatement), condition_(condition), then_statement_(then_statement), else_statement_(else_statement) {} Expression* condition_; Statement* then_statement_; Statement* else_statement_; }; class TryStatement : public Statement { public: Block* try_block() const { return try_block_; } void set_try_block(Block* b) { try_block_ = b; } protected: TryStatement(Block* try_block, int pos, NodeType type) : Statement(pos, type), try_block_(try_block) {} private: Block* try_block_; }; class TryCatchStatement final : public TryStatement { public: Scope* scope() { return scope_; } Block* catch_block() const { return catch_block_; } void set_catch_block(Block* b) { catch_block_ = b; } // Prediction of whether exceptions thrown into the handler for this try block // will be caught. // // BytecodeGenerator tracks the state of catch prediction, which can change // with each TryCatchStatement encountered. The tracked catch prediction is // later compiled into the code's handler table. The runtime uses this // information to implement a feature that notifies the debugger when an // uncaught exception is thrown, _before_ the exception propagates to the top. // // If this try/catch statement is meant to rethrow (HandlerTable::UNCAUGHT), // the catch prediction value is set to the same value as the surrounding // catch prediction. // // Since it's generally undecidable whether an exception will be caught, our // prediction is only an approximation. // --------------------------------------------------------------------------- inline HandlerTable::CatchPrediction GetCatchPrediction( HandlerTable::CatchPrediction outer_catch_prediction) const { if (catch_prediction_ == HandlerTable::UNCAUGHT) { return outer_catch_prediction; } return catch_prediction_; } // Indicates whether or not code should be generated to clear the pending // exception. The pending exception is cleared for cases where the exception // is not guaranteed to be rethrown, indicated by the value // HandlerTable::UNCAUGHT. If both the current and surrounding catch handler's // are predicted uncaught, the exception is not cleared. // // If this handler is not going to simply rethrow the exception, this method // indicates that the isolate's pending exception message should be cleared // before executing the catch_block. // In the normal use case, this flag is always on because the message object // is not needed anymore when entering the catch block and should not be // kept alive. // The use case where the flag is off is when the catch block is guaranteed // to rethrow the caught exception (using %ReThrow), which reuses the // pending message instead of generating a new one. // (When the catch block doesn't rethrow but is guaranteed to perform an // ordinary throw, not clearing the old message is safe but not very // useful.) inline bool ShouldClearPendingException( HandlerTable::CatchPrediction outer_catch_prediction) const { return catch_prediction_ != HandlerTable::UNCAUGHT || outer_catch_prediction != HandlerTable::UNCAUGHT; } bool is_try_catch_for_async() { return catch_prediction_ == HandlerTable::ASYNC_AWAIT; } private: friend class AstNodeFactory; TryCatchStatement(Block* try_block, Scope* scope, Block* catch_block, HandlerTable::CatchPrediction catch_prediction, int pos) : TryStatement(try_block, pos, kTryCatchStatement), scope_(scope), catch_block_(catch_block), catch_prediction_(catch_prediction) {} Scope* scope_; Block* catch_block_; HandlerTable::CatchPrediction catch_prediction_; }; class TryFinallyStatement final : public TryStatement { public: Block* finally_block() const { return finally_block_; } void set_finally_block(Block* b) { finally_block_ = b; } private: friend class AstNodeFactory; TryFinallyStatement(Block* try_block, Block* finally_block, int pos) : TryStatement(try_block, pos, kTryFinallyStatement), finally_block_(finally_block) {} Block* finally_block_; }; class DebuggerStatement final : public Statement { private: friend class AstNodeFactory; explicit DebuggerStatement(int pos) : Statement(pos, kDebuggerStatement) {} }; class EmptyStatement final : public Statement { private: friend class AstNodeFactory; EmptyStatement() : Statement(kNoSourcePosition, kEmptyStatement) {} }; // Delegates to another statement, which may be overwritten. // This was introduced to implement ES2015 Annex B3.3 for conditionally making // sloppy-mode block-scoped functions have a var binding, which is changed // from one statement to another during parsing. class SloppyBlockFunctionStatement final : public Statement { public: Statement* statement() const { return statement_; } void set_statement(Statement* statement) { statement_ = statement; } Scope* scope() const { return var_->scope(); } Variable* var() const { return var_; } Token::Value init() const { return TokenField::decode(bit_field_); } const AstRawString* name() const { return var_->raw_name(); } SloppyBlockFunctionStatement** next() { return &next_; } private: friend class AstNodeFactory; using TokenField = Statement::NextBitField; SloppyBlockFunctionStatement(int pos, Variable* var, Token::Value init, Statement* statement) : Statement(pos, kSloppyBlockFunctionStatement), var_(var), statement_(statement), next_(nullptr) { bit_field_ = TokenField::update(bit_field_, init); } Variable* var_; Statement* statement_; SloppyBlockFunctionStatement* next_; }; class Literal final : public Expression { public: enum Type { kSmi, kHeapNumber, kBigInt, kString, kSymbol, kBoolean, kUndefined, kNull, kTheHole, }; Type type() const { return TypeField::decode(bit_field_); } // Returns true if literal represents a property name (i.e. cannot be parsed // as array indices). bool IsPropertyName() const; // Returns true if literal represents an array index. // Note, that in general the following statement is not true: // key->IsPropertyName() != key->AsArrayIndex(...) // but for non-computed LiteralProperty properties the following is true: // property->key()->IsPropertyName() != property->key()->AsArrayIndex(...) bool AsArrayIndex(uint32_t* index) const; const AstRawString* AsRawPropertyName() { DCHECK(IsPropertyName()); return string_; } Smi AsSmiLiteral() const { DCHECK_EQ(kSmi, type()); return Smi::FromInt(smi_); } // Returns true if literal represents a Number. bool IsNumber() const { return type() == kHeapNumber || type() == kSmi; } double AsNumber() const { DCHECK(IsNumber()); switch (type()) { case kSmi: return smi_; case kHeapNumber: return number_; default: UNREACHABLE(); } } AstBigInt AsBigInt() const { DCHECK_EQ(type(), kBigInt); return bigint_; } bool IsString() const { return type() == kString; } const AstRawString* AsRawString() { DCHECK_EQ(type(), kString); return string_; } AstSymbol AsSymbol() { DCHECK_EQ(type(), kSymbol); return symbol_; } V8_EXPORT_PRIVATE bool ToBooleanIsTrue() const; bool ToBooleanIsFalse() const { return !ToBooleanIsTrue(); } bool ToUint32(uint32_t* value) const; // Returns an appropriate Object representing this Literal, allocating // a heap object if needed. Handle BuildValue(Isolate* isolate) const; // Support for using Literal as a HashMap key. NOTE: Currently, this works // only for string and number literals! uint32_t Hash(); static bool Match(void* literal1, void* literal2); private: friend class AstNodeFactory; using TypeField = Expression::NextBitField; Literal(int smi, int position) : Expression(position, kLiteral), smi_(smi) { bit_field_ = TypeField::update(bit_field_, kSmi); } Literal(double number, int position) : Expression(position, kLiteral), number_(number) { bit_field_ = TypeField::update(bit_field_, kHeapNumber); } Literal(AstBigInt bigint, int position) : Expression(position, kLiteral), bigint_(bigint) { bit_field_ = TypeField::update(bit_field_, kBigInt); } Literal(const AstRawString* string, int position) : Expression(position, kLiteral), string_(string) { bit_field_ = TypeField::update(bit_field_, kString); } Literal(AstSymbol symbol, int position) : Expression(position, kLiteral), symbol_(symbol) { bit_field_ = TypeField::update(bit_field_, kSymbol); } Literal(bool boolean, int position) : Expression(position, kLiteral), boolean_(boolean) { bit_field_ = TypeField::update(bit_field_, kBoolean); } Literal(Type type, int position) : Expression(position, kLiteral) { DCHECK(type == kNull || type == kUndefined || type == kTheHole); bit_field_ = TypeField::update(bit_field_, type); } union { const AstRawString* string_; int smi_; double number_; AstSymbol symbol_; AstBigInt bigint_; bool boolean_; }; }; // Base class for literals that need space in the type feedback vector. class MaterializedLiteral : public Expression { public: // A Materializedliteral is simple if the values consist of only // constants and simple object and array literals. bool IsSimple() const; protected: MaterializedLiteral(int pos, NodeType type) : Expression(pos, type) {} friend class CompileTimeValue; friend class ArrayLiteral; friend class ObjectLiteral; // Populate the depth field and any flags the literal has, returns the depth. int InitDepthAndFlags(); bool NeedsInitialAllocationSite(); // Populate the constant properties/elements fixed array. void BuildConstants(Isolate* isolate); // If the expression is a literal, return the literal value; // if the expression is a materialized literal and is_simple // then return an Array or Object Boilerplate Description // Otherwise, return undefined literal as the placeholder // in the object literal boilerplate. Handle GetBoilerplateValue(Expression* expression, Isolate* isolate); }; // Node for capturing a regexp literal. class RegExpLiteral final : public MaterializedLiteral { public: Handle pattern() const { return pattern_->string(); } const AstRawString* raw_pattern() const { return pattern_; } int flags() const { return flags_; } private: friend class AstNodeFactory; RegExpLiteral(const AstRawString* pattern, int flags, int pos) : MaterializedLiteral(pos, kRegExpLiteral), flags_(flags), pattern_(pattern) {} int const flags_; const AstRawString* const pattern_; }; // Base class for Array and Object literals, providing common code for handling // nested subliterals. class AggregateLiteral : public MaterializedLiteral { public: enum Flags { kNoFlags = 0, kIsShallow = 1, kDisableMementos = 1 << 1, kNeedsInitialAllocationSite = 1 << 2, }; bool is_initialized() const { return 0 < depth_; } int depth() const { DCHECK(is_initialized()); return depth_; } bool is_shallow() const { return depth() == 1; } bool needs_initial_allocation_site() const { return NeedsInitialAllocationSiteField::decode(bit_field_); } int ComputeFlags(bool disable_mementos = false) const { int flags = kNoFlags; if (is_shallow()) flags |= kIsShallow; if (disable_mementos) flags |= kDisableMementos; if (needs_initial_allocation_site()) flags |= kNeedsInitialAllocationSite; return flags; } // An AggregateLiteral is simple if the values consist of only // constants and simple object and array literals. bool is_simple() const { return IsSimpleField::decode(bit_field_); } private: int depth_ : 31; using NeedsInitialAllocationSiteField = MaterializedLiteral::NextBitField; using IsSimpleField = NeedsInitialAllocationSiteField::Next; protected: friend class AstNodeFactory; AggregateLiteral(int pos, NodeType type) : MaterializedLiteral(pos, type), depth_(0) { bit_field_ |= NeedsInitialAllocationSiteField::encode(false) | IsSimpleField::encode(false); } void set_is_simple(bool is_simple) { bit_field_ = IsSimpleField::update(bit_field_, is_simple); } void set_depth(int depth) { DCHECK(!is_initialized()); depth_ = depth; } void set_needs_initial_allocation_site(bool required) { bit_field_ = NeedsInitialAllocationSiteField::update(bit_field_, required); } template using NextBitField = IsSimpleField::Next; }; // Common supertype for ObjectLiteralProperty and ClassLiteralProperty class LiteralProperty : public ZoneObject { public: Expression* key() const { return key_and_is_computed_name_.GetPointer(); } Expression* value() const { return value_; } bool is_computed_name() const { return key_and_is_computed_name_.GetPayload(); } bool NeedsSetFunctionName() const; protected: LiteralProperty(Expression* key, Expression* value, bool is_computed_name) : key_and_is_computed_name_(key, is_computed_name), value_(value) {} PointerWithPayload key_and_is_computed_name_; Expression* value_; }; // Property is used for passing information // about an object literal's properties from the parser // to the code generator. class ObjectLiteralProperty final : public LiteralProperty { public: enum Kind : uint8_t { CONSTANT, // Property with constant value (compile time). COMPUTED, // Property with computed value (execution time). MATERIALIZED_LITERAL, // Property value is a materialized literal. GETTER, SETTER, // Property is an accessor function. PROTOTYPE, // Property is __proto__. SPREAD }; Kind kind() const { return kind_; } bool IsCompileTimeValue() const; void set_emit_store(bool emit_store); bool emit_store() const; bool IsNullPrototype() const { return IsPrototype() && value()->IsNullLiteral(); } bool IsPrototype() const { return kind() == PROTOTYPE; } private: friend class AstNodeFactory; ObjectLiteralProperty(Expression* key, Expression* value, Kind kind, bool is_computed_name); ObjectLiteralProperty(AstValueFactory* ast_value_factory, Expression* key, Expression* value, bool is_computed_name); Kind kind_; bool emit_store_; }; // An object literal has a boilerplate object that is used // for minimizing the work when constructing it at runtime. class ObjectLiteral final : public AggregateLiteral { public: using Property = ObjectLiteralProperty; Handle boilerplate_description() const { DCHECK(!boilerplate_description_.is_null()); return boilerplate_description_; } int properties_count() const { return boilerplate_properties_; } const ZonePtrList* properties() const { return &properties_; } bool has_elements() const { return HasElementsField::decode(bit_field_); } bool has_rest_property() const { return HasRestPropertyField::decode(bit_field_); } bool fast_elements() const { return FastElementsField::decode(bit_field_); } bool has_null_prototype() const { return HasNullPrototypeField::decode(bit_field_); } bool is_empty() const { DCHECK(is_initialized()); return !has_elements() && properties_count() == 0 && properties()->length() == 0; } bool IsEmptyObjectLiteral() const { return is_empty() && !has_null_prototype(); } // Populate the depth field and flags, returns the depth. int InitDepthAndFlags(); // Get the boilerplate description, populating it if necessary. Handle GetOrBuildBoilerplateDescription( Isolate* isolate) { if (boilerplate_description_.is_null()) { BuildBoilerplateDescription(isolate); } return boilerplate_description(); } // Populate the boilerplate description. void BuildBoilerplateDescription(Isolate* isolate); // Mark all computed expressions that are bound to a key that // is shadowed by a later occurrence of the same key. For the // marked expressions, no store code is emitted. void CalculateEmitStore(Zone* zone); // Determines whether the {CreateShallowObjectLiteratal} builtin can be used. bool IsFastCloningSupported() const; // Assemble bitfield of flags for the CreateObjectLiteral helper. int ComputeFlags(bool disable_mementos = false) const { int flags = AggregateLiteral::ComputeFlags(disable_mementos); if (fast_elements()) flags |= kFastElements; if (has_null_prototype()) flags |= kHasNullPrototype; return flags; } int EncodeLiteralType() { int flags = kNoFlags; if (fast_elements()) flags |= kFastElements; if (has_null_prototype()) flags |= kHasNullPrototype; return flags; } enum Flags { kFastElements = 1 << 3, kHasNullPrototype = 1 << 4, }; STATIC_ASSERT( static_cast(AggregateLiteral::kNeedsInitialAllocationSite) < static_cast(kFastElements)); private: friend class AstNodeFactory; ObjectLiteral(Zone* zone, const ScopedPtrList& properties, uint32_t boilerplate_properties, int pos, bool has_rest_property) : AggregateLiteral(pos, kObjectLiteral), boilerplate_properties_(boilerplate_properties), properties_(0, nullptr) { bit_field_ |= HasElementsField::encode(false) | HasRestPropertyField::encode(has_rest_property) | FastElementsField::encode(false) | HasNullPrototypeField::encode(false); properties.CopyTo(&properties_, zone); } void InitFlagsForPendingNullPrototype(int i); void set_has_elements(bool has_elements) { bit_field_ = HasElementsField::update(bit_field_, has_elements); } void set_fast_elements(bool fast_elements) { bit_field_ = FastElementsField::update(bit_field_, fast_elements); } void set_has_null_protoype(bool has_null_prototype) { bit_field_ = HasNullPrototypeField::update(bit_field_, has_null_prototype); } uint32_t boilerplate_properties_; Handle boilerplate_description_; ZoneList properties_; using HasElementsField = AggregateLiteral::NextBitField; using HasRestPropertyField = HasElementsField::Next; using FastElementsField = HasRestPropertyField::Next; using HasNullPrototypeField = FastElementsField::Next; }; // An array literal has a literals object that is used // for minimizing the work when constructing it at runtime. class ArrayLiteral final : public AggregateLiteral { public: Handle boilerplate_description() const { return boilerplate_description_; } const ZonePtrList* values() const { return &values_; } int first_spread_index() const { return first_spread_index_; } // Populate the depth field and flags, returns the depth. int InitDepthAndFlags(); // Get the boilerplate description, populating it if necessary. Handle GetOrBuildBoilerplateDescription( Isolate* isolate) { if (boilerplate_description_.is_null()) { BuildBoilerplateDescription(isolate); } return boilerplate_description(); } // Populate the boilerplate description. void BuildBoilerplateDescription(Isolate* isolate); // Determines whether the {CreateShallowArrayLiteral} builtin can be used. bool IsFastCloningSupported() const; // Assemble bitfield of flags for the CreateArrayLiteral helper. int ComputeFlags(bool disable_mementos = false) const { return AggregateLiteral::ComputeFlags(disable_mementos); } private: friend class AstNodeFactory; ArrayLiteral(Zone* zone, const ScopedPtrList& values, int first_spread_index, int pos) : AggregateLiteral(pos, kArrayLiteral), first_spread_index_(first_spread_index), values_(0, nullptr) { values.CopyTo(&values_, zone); } int first_spread_index_; Handle boilerplate_description_; ZonePtrList values_; }; enum class HoleCheckMode { kRequired, kElided }; class ThisExpression final : public Expression { private: friend class AstNodeFactory; ThisExpression() : Expression(kNoSourcePosition, kThisExpression) {} }; class VariableProxy final : public Expression { public: bool IsValidReferenceExpression() const { return !is_new_target(); } Handle name() const { return raw_name()->string(); } const AstRawString* raw_name() const { return is_resolved() ? var_->raw_name() : raw_name_; } Variable* var() const { DCHECK(is_resolved()); return var_; } void set_var(Variable* v) { DCHECK(!is_resolved()); DCHECK_NOT_NULL(v); var_ = v; } Scanner::Location location() { return Scanner::Location(position(), position() + raw_name()->length()); } bool is_assigned() const { return IsAssignedField::decode(bit_field_); } void set_is_assigned() { bit_field_ = IsAssignedField::update(bit_field_, true); if (is_resolved()) { var()->SetMaybeAssigned(); } } void clear_is_assigned() { bit_field_ = IsAssignedField::update(bit_field_, false); } bool is_resolved() const { return IsResolvedField::decode(bit_field_); } void set_is_resolved() { bit_field_ = IsResolvedField::update(bit_field_, true); } bool is_new_target() const { return IsNewTargetField::decode(bit_field_); } void set_is_new_target() { bit_field_ = IsNewTargetField::update(bit_field_, true); } HoleCheckMode hole_check_mode() const { HoleCheckMode mode = HoleCheckModeField::decode(bit_field_); DCHECK_IMPLIES(mode == HoleCheckMode::kRequired, var()->binding_needs_init() || var()->local_if_not_shadowed()->binding_needs_init()); return mode; } void set_needs_hole_check() { bit_field_ = HoleCheckModeField::update(bit_field_, HoleCheckMode::kRequired); } bool IsPrivateName() const { return raw_name()->length() > 0 && raw_name()->FirstCharacter() == '#'; } // Bind this proxy to the variable var. void BindTo(Variable* var); V8_INLINE VariableProxy* next_unresolved() { return next_unresolved_; } V8_INLINE bool is_removed_from_unresolved() const { return IsRemovedFromUnresolvedField::decode(bit_field_); } void mark_removed_from_unresolved() { bit_field_ = IsRemovedFromUnresolvedField::update(bit_field_, true); } // Provides filtered access to the unresolved variable proxy threaded list. struct UnresolvedNext { static VariableProxy** filter(VariableProxy** t) { VariableProxy** n = t; // Skip over possibly removed values. while (*n != nullptr && (*n)->is_removed_from_unresolved()) { n = (*n)->next(); } return n; } static VariableProxy** start(VariableProxy** head) { return filter(head); } static VariableProxy** next(VariableProxy* t) { return filter(t->next()); } }; private: friend class AstNodeFactory; VariableProxy(Variable* var, int start_position); VariableProxy(const AstRawString* name, VariableKind variable_kind, int start_position) : Expression(start_position, kVariableProxy), raw_name_(name), next_unresolved_(nullptr) { DCHECK_NE(THIS_VARIABLE, variable_kind); bit_field_ |= IsAssignedField::encode(false) | IsResolvedField::encode(false) | IsRemovedFromUnresolvedField::encode(false) | HoleCheckModeField::encode(HoleCheckMode::kElided); } explicit VariableProxy(const VariableProxy* copy_from); using IsAssignedField = Expression::NextBitField; using IsResolvedField = IsAssignedField::Next; using IsRemovedFromUnresolvedField = IsResolvedField::Next; using IsNewTargetField = IsRemovedFromUnresolvedField::Next; using HoleCheckModeField = IsNewTargetField::Next; union { const AstRawString* raw_name_; // if !is_resolved_ Variable* var_; // if is_resolved_ }; V8_INLINE VariableProxy** next() { return &next_unresolved_; } VariableProxy* next_unresolved_; friend base::ThreadedListTraits; }; // Wraps an optional chain to provide a wrapper for jump labels. class OptionalChain final : public Expression { public: Expression* expression() const { return expression_; } private: friend class AstNodeFactory; explicit OptionalChain(Expression* expression) : Expression(0, kOptionalChain), expression_(expression) {} Expression* expression_; }; // Assignments to a property will use one of several types of property access. // Otherwise, the assignment is to a non-property (a global, a local slot, a // parameter slot, or a destructuring pattern). enum AssignType { NON_PROPERTY, // destructuring NAMED_PROPERTY, // obj.key KEYED_PROPERTY, // obj[key] NAMED_SUPER_PROPERTY, // super.key KEYED_SUPER_PROPERTY, // super[key] PRIVATE_METHOD, // obj.#key: #key is a private method PRIVATE_GETTER_ONLY, // obj.#key: #key only has a getter defined PRIVATE_SETTER_ONLY, // obj.#key: #key only has a setter defined PRIVATE_GETTER_AND_SETTER // obj.#key: #key has both accessors defined }; class Property final : public Expression { public: bool is_optional_chain_link() const { return IsOptionalChainLinkField::decode(bit_field_); } bool IsValidReferenceExpression() const { return true; } Expression* obj() const { return obj_; } Expression* key() const { return key_; } bool IsSuperAccess() { return obj()->IsSuperPropertyReference(); } bool IsPrivateReference() const { return key()->IsPrivateName(); } // Returns the properties assign type. static AssignType GetAssignType(Property* property) { if (property == nullptr) return NON_PROPERTY; if (property->IsPrivateReference()) { DCHECK(!property->IsSuperAccess()); VariableProxy* proxy = property->key()->AsVariableProxy(); DCHECK_NOT_NULL(proxy); Variable* var = proxy->var(); switch (var->mode()) { case VariableMode::kPrivateMethod: return PRIVATE_METHOD; case VariableMode::kConst: return KEYED_PROPERTY; // Use KEYED_PROPERTY for private fields. case VariableMode::kPrivateGetterOnly: return PRIVATE_GETTER_ONLY; case VariableMode::kPrivateSetterOnly: return PRIVATE_SETTER_ONLY; case VariableMode::kPrivateGetterAndSetter: return PRIVATE_GETTER_AND_SETTER; default: UNREACHABLE(); } } bool super_access = property->IsSuperAccess(); return (property->key()->IsPropertyName()) ? (super_access ? NAMED_SUPER_PROPERTY : NAMED_PROPERTY) : (super_access ? KEYED_SUPER_PROPERTY : KEYED_PROPERTY); } private: friend class AstNodeFactory; Property(Expression* obj, Expression* key, int pos, bool optional_chain) : Expression(pos, kProperty), obj_(obj), key_(key) { bit_field_ |= IsOptionalChainLinkField::encode(optional_chain); } using IsOptionalChainLinkField = Expression::NextBitField; Expression* obj_; Expression* key_; }; // ResolvedProperty pairs a receiver field with a value field. It allows Call // to support arbitrary receivers while still taking advantage of TypeFeedback. class ResolvedProperty final : public Expression { public: VariableProxy* object() const { return object_; } VariableProxy* property() const { return property_; } void set_object(VariableProxy* e) { object_ = e; } void set_property(VariableProxy* e) { property_ = e; } private: friend class AstNodeFactory; ResolvedProperty(VariableProxy* obj, VariableProxy* property, int pos) : Expression(pos, kResolvedProperty), object_(obj), property_(property) {} VariableProxy* object_; VariableProxy* property_; }; class Call final : public Expression { public: Expression* expression() const { return expression_; } const ZonePtrList* arguments() const { return &arguments_; } bool is_possibly_eval() const { return IsPossiblyEvalField::decode(bit_field_); } bool is_tagged_template() const { return IsTaggedTemplateField::decode(bit_field_); } bool is_optional_chain_link() const { return IsOptionalChainLinkField::decode(bit_field_); } bool only_last_arg_is_spread() { return !arguments_.is_empty() && arguments_.last()->IsSpread(); } enum CallType { GLOBAL_CALL, WITH_CALL, NAMED_PROPERTY_CALL, KEYED_PROPERTY_CALL, NAMED_SUPER_PROPERTY_CALL, KEYED_SUPER_PROPERTY_CALL, PRIVATE_CALL, SUPER_CALL, RESOLVED_PROPERTY_CALL, OTHER_CALL }; enum PossiblyEval { IS_POSSIBLY_EVAL, NOT_EVAL, }; // Helpers to determine how to handle the call. CallType GetCallType() const; enum class TaggedTemplateTag { kTrue }; private: friend class AstNodeFactory; Call(Zone* zone, Expression* expression, const ScopedPtrList& arguments, int pos, PossiblyEval possibly_eval, bool optional_chain) : Expression(pos, kCall), expression_(expression), arguments_(0, nullptr) { bit_field_ |= IsPossiblyEvalField::encode(possibly_eval == IS_POSSIBLY_EVAL) | IsTaggedTemplateField::encode(false) | IsOptionalChainLinkField::encode(optional_chain); arguments.CopyTo(&arguments_, zone); } Call(Zone* zone, Expression* expression, const ScopedPtrList& arguments, int pos, TaggedTemplateTag tag) : Expression(pos, kCall), expression_(expression), arguments_(0, nullptr) { bit_field_ |= IsPossiblyEvalField::encode(false) | IsTaggedTemplateField::encode(true) | IsOptionalChainLinkField::encode(false); arguments.CopyTo(&arguments_, zone); } using IsPossiblyEvalField = Expression::NextBitField; using IsTaggedTemplateField = IsPossiblyEvalField::Next; using IsOptionalChainLinkField = IsTaggedTemplateField::Next; Expression* expression_; ZonePtrList arguments_; }; class CallNew final : public Expression { public: Expression* expression() const { return expression_; } const ZonePtrList* arguments() const { return &arguments_; } bool only_last_arg_is_spread() { return !arguments_.is_empty() && arguments_.last()->IsSpread(); } private: friend class AstNodeFactory; CallNew(Zone* zone, Expression* expression, const ScopedPtrList& arguments, int pos) : Expression(pos, kCallNew), expression_(expression), arguments_(0, nullptr) { arguments.CopyTo(&arguments_, zone); } Expression* expression_; ZonePtrList arguments_; }; // The CallRuntime class does not represent any official JavaScript // language construct. Instead it is used to call a C or JS function // with a set of arguments. This is used from the builtins that are // implemented in JavaScript. class CallRuntime final : public Expression { public: const ZonePtrList* arguments() const { return &arguments_; } bool is_jsruntime() const { return function_ == nullptr; } int context_index() const { DCHECK(is_jsruntime()); return context_index_; } const Runtime::Function* function() const { DCHECK(!is_jsruntime()); return function_; } const char* debug_name(); private: friend class AstNodeFactory; CallRuntime(Zone* zone, const Runtime::Function* function, const ScopedPtrList& arguments, int pos) : Expression(pos, kCallRuntime), function_(function), arguments_(0, nullptr) { arguments.CopyTo(&arguments_, zone); } CallRuntime(Zone* zone, int context_index, const ScopedPtrList& arguments, int pos) : Expression(pos, kCallRuntime), context_index_(context_index), function_(nullptr), arguments_(0, nullptr) { arguments.CopyTo(&arguments_, zone); } int context_index_; const Runtime::Function* function_; ZonePtrList arguments_; }; class UnaryOperation final : public Expression { public: Token::Value op() const { return OperatorField::decode(bit_field_); } Expression* expression() const { return expression_; } private: friend class AstNodeFactory; UnaryOperation(Token::Value op, Expression* expression, int pos) : Expression(pos, kUnaryOperation), expression_(expression) { bit_field_ |= OperatorField::encode(op); DCHECK(Token::IsUnaryOp(op)); } Expression* expression_; using OperatorField = Expression::NextBitField; }; class BinaryOperation final : public Expression { public: Token::Value op() const { return OperatorField::decode(bit_field_); } Expression* left() const { return left_; } Expression* right() const { return right_; } // Returns true if one side is a Smi literal, returning the other side's // sub-expression in |subexpr| and the literal Smi in |literal|. bool IsSmiLiteralOperation(Expression** subexpr, Smi* literal); private: friend class AstNodeFactory; BinaryOperation(Token::Value op, Expression* left, Expression* right, int pos) : Expression(pos, kBinaryOperation), left_(left), right_(right) { bit_field_ |= OperatorField::encode(op); DCHECK(Token::IsBinaryOp(op)); } Expression* left_; Expression* right_; using OperatorField = Expression::NextBitField; }; class NaryOperation final : public Expression { public: Token::Value op() const { return OperatorField::decode(bit_field_); } Expression* first() const { return first_; } Expression* subsequent(size_t index) const { return subsequent_[index].expression; } size_t subsequent_length() const { return subsequent_.size(); } int subsequent_op_position(size_t index) const { return subsequent_[index].op_position; } void AddSubsequent(Expression* expr, int pos) { subsequent_.emplace_back(expr, pos); } private: friend class AstNodeFactory; NaryOperation(Zone* zone, Token::Value op, Expression* first, size_t initial_subsequent_size) : Expression(first->position(), kNaryOperation), first_(first), subsequent_(zone) { bit_field_ |= OperatorField::encode(op); DCHECK(Token::IsBinaryOp(op)); DCHECK_NE(op, Token::EXP); subsequent_.reserve(initial_subsequent_size); } // Nary operations store the first (lhs) child expression inline, and the // child expressions (rhs of each op) are stored out-of-line, along with // their operation's position. Note that the Nary operation expression's // position has no meaning. // // So an nary add: // // expr + expr + expr + ... // // is stored as: // // (expr) [(+ expr), (+ expr), ...] // '-.--' '-----------.-----------' // first subsequent entry list Expression* first_; struct NaryOperationEntry { Expression* expression; int op_position; NaryOperationEntry(Expression* e, int pos) : expression(e), op_position(pos) {} }; ZoneVector subsequent_; using OperatorField = Expression::NextBitField; }; class CountOperation final : public Expression { public: bool is_prefix() const { return IsPrefixField::decode(bit_field_); } bool is_postfix() const { return !is_prefix(); } Token::Value op() const { return TokenField::decode(bit_field_); } Expression* expression() const { return expression_; } private: friend class AstNodeFactory; CountOperation(Token::Value op, bool is_prefix, Expression* expr, int pos) : Expression(pos, kCountOperation), expression_(expr) { bit_field_ |= IsPrefixField::encode(is_prefix) | TokenField::encode(op); } using IsPrefixField = Expression::NextBitField; using TokenField = IsPrefixField::Next; Expression* expression_; }; class CompareOperation final : public Expression { public: Token::Value op() const { return OperatorField::decode(bit_field_); } Expression* left() const { return left_; } Expression* right() const { return right_; } // Match special cases. bool IsLiteralCompareTypeof(Expression** expr, Literal** literal); bool IsLiteralCompareUndefined(Expression** expr); bool IsLiteralCompareNull(Expression** expr); private: friend class AstNodeFactory; CompareOperation(Token::Value op, Expression* left, Expression* right, int pos) : Expression(pos, kCompareOperation), left_(left), right_(right) { bit_field_ |= OperatorField::encode(op); DCHECK(Token::IsCompareOp(op)); } Expression* left_; Expression* right_; using OperatorField = Expression::NextBitField; }; class Spread final : public Expression { public: Expression* expression() const { return expression_; } int expression_position() const { return expr_pos_; } private: friend class AstNodeFactory; Spread(Expression* expression, int pos, int expr_pos) : Expression(pos, kSpread), expr_pos_(expr_pos), expression_(expression) {} int expr_pos_; Expression* expression_; }; // The StoreInArrayLiteral node corresponds to the StaInArrayLiteral bytecode. // It is used in the rewriting of destructuring assignments that contain an // array rest pattern. class StoreInArrayLiteral final : public Expression { public: Expression* array() const { return array_; } Expression* index() const { return index_; } Expression* value() const { return value_; } private: friend class AstNodeFactory; StoreInArrayLiteral(Expression* array, Expression* index, Expression* value, int position) : Expression(position, kStoreInArrayLiteral), array_(array), index_(index), value_(value) {} Expression* array_; Expression* index_; Expression* value_; }; class Conditional final : public Expression { public: Expression* condition() const { return condition_; } Expression* then_expression() const { return then_expression_; } Expression* else_expression() const { return else_expression_; } private: friend class AstNodeFactory; Conditional(Expression* condition, Expression* then_expression, Expression* else_expression, int position) : Expression(position, kConditional), condition_(condition), then_expression_(then_expression), else_expression_(else_expression) {} Expression* condition_; Expression* then_expression_; Expression* else_expression_; }; class Assignment : public Expression { public: Token::Value op() const { return TokenField::decode(bit_field_); } Expression* target() const { return target_; } Expression* value() const { return value_; } // The assignment was generated as part of block-scoped sloppy-mode // function hoisting, see // ES#sec-block-level-function-declarations-web-legacy-compatibility-semantics LookupHoistingMode lookup_hoisting_mode() const { return static_cast( LookupHoistingModeField::decode(bit_field_)); } void set_lookup_hoisting_mode(LookupHoistingMode mode) { bit_field_ = LookupHoistingModeField::update(bit_field_, static_cast(mode)); } protected: Assignment(NodeType type, Token::Value op, Expression* target, Expression* value, int pos); private: friend class AstNodeFactory; using TokenField = Expression::NextBitField; using LookupHoistingModeField = TokenField::Next; Expression* target_; Expression* value_; }; class CompoundAssignment final : public Assignment { public: BinaryOperation* binary_operation() const { return binary_operation_; } private: friend class AstNodeFactory; CompoundAssignment(Token::Value op, Expression* target, Expression* value, int pos, BinaryOperation* binary_operation) : Assignment(kCompoundAssignment, op, target, value, pos), binary_operation_(binary_operation) {} BinaryOperation* binary_operation_; }; // There are several types of Suspend node: // // Yield // YieldStar // Await // // Our Yield is different from the JS yield in that it "returns" its argument as // is, without wrapping it in an iterator result object. Such wrapping, if // desired, must be done beforehand (see the parser). class Suspend : public Expression { public: // With {kNoControl}, the {Suspend} behaves like yield, except that it never // throws and never causes the current generator to return. This is used to // desugar yield*. // TODO(caitp): remove once yield* desugaring for async generators is handled // in BytecodeGenerator. enum OnAbruptResume { kOnExceptionThrow, kNoControl }; Expression* expression() const { return expression_; } OnAbruptResume on_abrupt_resume() const { return OnAbruptResumeField::decode(bit_field_); } private: friend class AstNodeFactory; friend class Yield; friend class YieldStar; friend class Await; Suspend(NodeType node_type, Expression* expression, int pos, OnAbruptResume on_abrupt_resume) : Expression(pos, node_type), expression_(expression) { bit_field_ |= OnAbruptResumeField::encode(on_abrupt_resume); } Expression* expression_; using OnAbruptResumeField = Expression::NextBitField; }; class Yield final : public Suspend { private: friend class AstNodeFactory; Yield(Expression* expression, int pos, OnAbruptResume on_abrupt_resume) : Suspend(kYield, expression, pos, on_abrupt_resume) {} }; class YieldStar final : public Suspend { private: friend class AstNodeFactory; YieldStar(Expression* expression, int pos) : Suspend(kYieldStar, expression, pos, Suspend::OnAbruptResume::kNoControl) {} }; class Await final : public Suspend { private: friend class AstNodeFactory; Await(Expression* expression, int pos) : Suspend(kAwait, expression, pos, Suspend::kOnExceptionThrow) {} }; class Throw final : public Expression { public: Expression* exception() const { return exception_; } private: friend class AstNodeFactory; Throw(Expression* exception, int pos) : Expression(pos, kThrow), exception_(exception) {} Expression* exception_; }; class FunctionLiteral final : public Expression { public: enum ParameterFlag : uint8_t { kNoDuplicateParameters, kHasDuplicateParameters }; enum EagerCompileHint : uint8_t { kShouldEagerCompile, kShouldLazyCompile }; // Empty handle means that the function does not have a shared name (i.e. // the name will be set dynamically after creation of the function closure). MaybeHandle name() const { return raw_name_ ? raw_name_->string() : MaybeHandle(); } Handle name(Isolate* isolate) const; bool has_shared_name() const { return raw_name_ != nullptr; } const AstConsString* raw_name() const { return raw_name_; } void set_raw_name(const AstConsString* name) { raw_name_ = name; } DeclarationScope* scope() const { return scope_; } ZonePtrList* body() { return &body_; } void set_function_token_position(int pos) { function_token_position_ = pos; } int function_token_position() const { return function_token_position_; } int start_position() const; int end_position() const; bool is_anonymous_expression() const { return syntax_kind() == FunctionSyntaxKind::kAnonymousExpression; } void mark_as_oneshot_iife() { bit_field_ = OneshotIIFEBit::update(bit_field_, true); } bool is_oneshot_iife() const { return OneshotIIFEBit::decode(bit_field_); } bool is_toplevel() const { return function_literal_id() == kFunctionLiteralIdTopLevel; } V8_EXPORT_PRIVATE LanguageMode language_mode() const; static bool NeedsHomeObject(Expression* expr); void add_expected_properties(int number_properties) { expected_property_count_ += number_properties; } int expected_property_count() { return expected_property_count_; } int parameter_count() { return parameter_count_; } int function_length() { return function_length_; } bool AllowsLazyCompilation(); bool CanSuspend() { if (suspend_count() > 0) { DCHECK(IsResumableFunction(kind())); return true; } return false; } // We can safely skip the arguments adaptor frame setup even // in case of arguments mismatches for strict mode functions, // as long as there's // // 1. no use of the arguments object (either explicitly or // potentially implicitly via a direct eval() call), and // 2. rest parameters aren't being used in the function. // // See http://bit.ly/v8-faster-calls-with-arguments-mismatch // for the details here (https://crbug.com/v8/8895). bool SafeToSkipArgumentsAdaptor() const; // Returns either name or inferred name as a cstring. std::unique_ptr GetDebugName() const; Handle inferred_name() const { if (!inferred_name_.is_null()) { DCHECK_NULL(raw_inferred_name_); return inferred_name_; } if (raw_inferred_name_ != nullptr) { return raw_inferred_name_->string(); } UNREACHABLE(); } const AstConsString* raw_inferred_name() { return raw_inferred_name_; } // Only one of {set_inferred_name, set_raw_inferred_name} should be called. void set_inferred_name(Handle inferred_name); void set_raw_inferred_name(const AstConsString* raw_inferred_name); bool pretenure() const { return Pretenure::decode(bit_field_); } void set_pretenure() { bit_field_ = Pretenure::update(bit_field_, true); } bool has_duplicate_parameters() const { // Not valid for lazy functions. DCHECK(ShouldEagerCompile()); return HasDuplicateParameters::decode(bit_field_); } // This is used as a heuristic on when to eagerly compile a function // literal. We consider the following constructs as hints that the // function will be called immediately: // - (function() { ... })(); // - var x = function() { ... }(); V8_EXPORT_PRIVATE bool ShouldEagerCompile() const; V8_EXPORT_PRIVATE void SetShouldEagerCompile(); FunctionSyntaxKind syntax_kind() const { return FunctionSyntaxKindBits::decode(bit_field_); } FunctionKind kind() const; bool dont_optimize() { return dont_optimize_reason() != BailoutReason::kNoReason; } BailoutReason dont_optimize_reason() { return DontOptimizeReasonField::decode(bit_field_); } void set_dont_optimize_reason(BailoutReason reason) { bit_field_ = DontOptimizeReasonField::update(bit_field_, reason); } bool IsAnonymousFunctionDefinition() const { return is_anonymous_expression(); } int suspend_count() { return suspend_count_; } void set_suspend_count(int suspend_count) { suspend_count_ = suspend_count; } int return_position() { return std::max( start_position(), end_position() - (HasBracesField::decode(bit_field_) ? 1 : 0)); } int function_literal_id() const { return function_literal_id_; } void set_function_literal_id(int function_literal_id) { function_literal_id_ = function_literal_id; } void set_requires_instance_members_initializer(bool value) { bit_field_ = RequiresInstanceMembersInitializer::update(bit_field_, value); } bool requires_instance_members_initializer() const { return RequiresInstanceMembersInitializer::decode(bit_field_); } bool requires_brand_initialization() const; bool private_name_lookup_skips_outer_class() const; ProducedPreparseData* produced_preparse_data() const { return produced_preparse_data_; } private: friend class AstNodeFactory; FunctionLiteral(Zone* zone, const AstRawString* name, AstValueFactory* ast_value_factory, DeclarationScope* scope, const ScopedPtrList& body, int expected_property_count, int parameter_count, int function_length, FunctionSyntaxKind function_syntax_kind, ParameterFlag has_duplicate_parameters, EagerCompileHint eager_compile_hint, int position, bool has_braces, int function_literal_id, ProducedPreparseData* produced_preparse_data = nullptr) : Expression(position, kFunctionLiteral), expected_property_count_(expected_property_count), parameter_count_(parameter_count), function_length_(function_length), function_token_position_(kNoSourcePosition), suspend_count_(0), function_literal_id_(function_literal_id), raw_name_(name ? ast_value_factory->NewConsString(name) : nullptr), scope_(scope), body_(0, nullptr), raw_inferred_name_(ast_value_factory->empty_cons_string()), produced_preparse_data_(produced_preparse_data) { bit_field_ |= FunctionSyntaxKindBits::encode(function_syntax_kind) | Pretenure::encode(false) | HasDuplicateParameters::encode(has_duplicate_parameters == kHasDuplicateParameters) | DontOptimizeReasonField::encode(BailoutReason::kNoReason) | RequiresInstanceMembersInitializer::encode(false) | HasBracesField::encode(has_braces) | OneshotIIFEBit::encode(false); if (eager_compile_hint == kShouldEagerCompile) SetShouldEagerCompile(); body.CopyTo(&body_, zone); } using FunctionSyntaxKindBits = Expression::NextBitField; using Pretenure = FunctionSyntaxKindBits::Next; using HasDuplicateParameters = Pretenure::Next; using DontOptimizeReasonField = HasDuplicateParameters::Next; using RequiresInstanceMembersInitializer = DontOptimizeReasonField::Next; using HasBracesField = RequiresInstanceMembersInitializer::Next; using OneshotIIFEBit = HasBracesField::Next; // expected_property_count_ is the sum of instance fields and properties. // It can vary depending on whether a function is lazily or eagerly parsed. int expected_property_count_; int parameter_count_; int function_length_; int function_token_position_; int suspend_count_; int function_literal_id_; const AstConsString* raw_name_; DeclarationScope* scope_; ZonePtrList body_; const AstConsString* raw_inferred_name_; Handle inferred_name_; ProducedPreparseData* produced_preparse_data_; }; // Property is used for passing information // about a class literal's properties from the parser to the code generator. class ClassLiteralProperty final : public LiteralProperty { public: enum Kind : uint8_t { METHOD, GETTER, SETTER, FIELD }; Kind kind() const { return kind_; } bool is_static() const { return is_static_; } bool is_private() const { return is_private_; } void set_computed_name_var(Variable* var) { DCHECK_EQ(FIELD, kind()); DCHECK(!is_private()); private_or_computed_name_var_ = var; } Variable* computed_name_var() const { DCHECK_EQ(FIELD, kind()); DCHECK(!is_private()); return private_or_computed_name_var_; } void set_private_name_var(Variable* var) { DCHECK(is_private()); private_or_computed_name_var_ = var; } Variable* private_name_var() const { DCHECK(is_private()); return private_or_computed_name_var_; } bool NeedsHomeObjectOnClassPrototype() const { return is_private() && kind_ == METHOD && FunctionLiteral::NeedsHomeObject(value_); } private: friend class AstNodeFactory; ClassLiteralProperty(Expression* key, Expression* value, Kind kind, bool is_static, bool is_computed_name, bool is_private); Kind kind_; bool is_static_; bool is_private_; Variable* private_or_computed_name_var_; }; class InitializeClassMembersStatement final : public Statement { public: using Property = ClassLiteralProperty; ZonePtrList* fields() const { return fields_; } private: friend class AstNodeFactory; InitializeClassMembersStatement(ZonePtrList* fields, int pos) : Statement(pos, kInitializeClassMembersStatement), fields_(fields) {} ZonePtrList* fields_; }; class ClassLiteral final : public Expression { public: using Property = ClassLiteralProperty; ClassScope* scope() const { return scope_; } Expression* extends() const { return extends_; } FunctionLiteral* constructor() const { return constructor_; } ZonePtrList* public_members() const { return public_members_; } ZonePtrList* private_members() const { return private_members_; } int start_position() const { return position(); } int end_position() const { return end_position_; } bool has_name_static_property() const { return HasNameStaticProperty::decode(bit_field_); } bool has_static_computed_names() const { return HasStaticComputedNames::decode(bit_field_); } bool is_anonymous_expression() const { return IsAnonymousExpression::decode(bit_field_); } bool has_private_methods() const { return HasPrivateMethods::decode(bit_field_); } bool IsAnonymousFunctionDefinition() const { return is_anonymous_expression(); } FunctionLiteral* static_fields_initializer() const { return static_fields_initializer_; } FunctionLiteral* instance_members_initializer_function() const { return instance_members_initializer_function_; } private: friend class AstNodeFactory; ClassLiteral(ClassScope* scope, Expression* extends, FunctionLiteral* constructor, ZonePtrList* public_members, ZonePtrList* private_members, FunctionLiteral* static_fields_initializer, FunctionLiteral* instance_members_initializer_function, int start_position, int end_position, bool has_name_static_property, bool has_static_computed_names, bool is_anonymous, bool has_private_methods) : Expression(start_position, kClassLiteral), end_position_(end_position), scope_(scope), extends_(extends), constructor_(constructor), public_members_(public_members), private_members_(private_members), static_fields_initializer_(static_fields_initializer), instance_members_initializer_function_( instance_members_initializer_function) { bit_field_ |= HasNameStaticProperty::encode(has_name_static_property) | HasStaticComputedNames::encode(has_static_computed_names) | IsAnonymousExpression::encode(is_anonymous) | HasPrivateMethods::encode(has_private_methods); } int end_position_; ClassScope* scope_; Expression* extends_; FunctionLiteral* constructor_; ZonePtrList* public_members_; ZonePtrList* private_members_; FunctionLiteral* static_fields_initializer_; FunctionLiteral* instance_members_initializer_function_; using HasNameStaticProperty = Expression::NextBitField; using HasStaticComputedNames = HasNameStaticProperty::Next; using IsAnonymousExpression = HasStaticComputedNames::Next; using HasPrivateMethods = IsAnonymousExpression::Next; }; class NativeFunctionLiteral final : public Expression { public: Handle name() const { return name_->string(); } const AstRawString* raw_name() const { return name_; } v8::Extension* extension() const { return extension_; } private: friend class AstNodeFactory; NativeFunctionLiteral(const AstRawString* name, v8::Extension* extension, int pos) : Expression(pos, kNativeFunctionLiteral), name_(name), extension_(extension) {} const AstRawString* name_; v8::Extension* extension_; }; class SuperPropertyReference final : public Expression { public: Expression* home_object() const { return home_object_; } private: friend class AstNodeFactory; // We take in ThisExpression* only as a proof that it was accessed. SuperPropertyReference(Expression* home_object, int pos) : Expression(pos, kSuperPropertyReference), home_object_(home_object) { DCHECK(home_object->IsProperty()); } Expression* home_object_; }; class SuperCallReference final : public Expression { public: VariableProxy* new_target_var() const { return new_target_var_; } VariableProxy* this_function_var() const { return this_function_var_; } private: friend class AstNodeFactory; // We take in ThisExpression* only as a proof that it was accessed. SuperCallReference(VariableProxy* new_target_var, VariableProxy* this_function_var, int pos) : Expression(pos, kSuperCallReference), new_target_var_(new_target_var), this_function_var_(this_function_var) { DCHECK(new_target_var->raw_name()->IsOneByteEqualTo(".new.target")); DCHECK(this_function_var->raw_name()->IsOneByteEqualTo(".this_function")); } VariableProxy* new_target_var_; VariableProxy* this_function_var_; }; // This AST Node is used to represent a dynamic import call -- // import(argument). class ImportCallExpression final : public Expression { public: Expression* argument() const { return argument_; } private: friend class AstNodeFactory; ImportCallExpression(Expression* argument, int pos) : Expression(pos, kImportCallExpression), argument_(argument) {} Expression* argument_; }; // This class is produced when parsing the () in arrow functions without any // arguments and is not actually a valid expression. class EmptyParentheses final : public Expression { private: friend class AstNodeFactory; explicit EmptyParentheses(int pos) : Expression(pos, kEmptyParentheses) { mark_parenthesized(); } }; // Represents the spec operation `GetTemplateObject(templateLiteral)` // (defined at https://tc39.github.io/ecma262/#sec-gettemplateobject). class GetTemplateObject final : public Expression { public: const ZonePtrList* cooked_strings() const { return cooked_strings_; } const ZonePtrList* raw_strings() const { return raw_strings_; } Handle GetOrBuildDescription(Isolate* isolate); private: friend class AstNodeFactory; GetTemplateObject(const ZonePtrList* cooked_strings, const ZonePtrList* raw_strings, int pos) : Expression(pos, kGetTemplateObject), cooked_strings_(cooked_strings), raw_strings_(raw_strings) {} const ZonePtrList* cooked_strings_; const ZonePtrList* raw_strings_; }; class TemplateLiteral final : public Expression { public: const ZonePtrList* string_parts() const { return string_parts_; } const ZonePtrList* substitutions() const { return substitutions_; } private: friend class AstNodeFactory; TemplateLiteral(const ZonePtrList* parts, const ZonePtrList* substitutions, int pos) : Expression(pos, kTemplateLiteral), string_parts_(parts), substitutions_(substitutions) {} const ZonePtrList* string_parts_; const ZonePtrList* substitutions_; }; // ---------------------------------------------------------------------------- // Basic visitor // Sub-class should parametrize AstVisitor with itself, e.g.: // class SpecificVisitor : public AstVisitor { ... } template class AstVisitor { public: void Visit(AstNode* node) { impl()->Visit(node); } void VisitDeclarations(Declaration::List* declarations) { for (Declaration* decl : *declarations) Visit(decl); } void VisitStatements(const ZonePtrList* statements) { for (int i = 0; i < statements->length(); i++) { Statement* stmt = statements->at(i); Visit(stmt); } } void VisitExpressions(const ZonePtrList* expressions) { for (int i = 0; i < expressions->length(); i++) { // The variable statement visiting code may pass null expressions // to this code. Maybe this should be handled by introducing an // undefined expression or literal? Revisit this code if this // changes. Expression* expression = expressions->at(i); if (expression != nullptr) Visit(expression); } } protected: Subclass* impl() { return static_cast(this); } }; #define GENERATE_VISIT_CASE(NodeType) \ case AstNode::k##NodeType: \ return this->impl()->Visit##NodeType(static_cast(node)); #define GENERATE_FAILURE_CASE(NodeType) \ case AstNode::k##NodeType: \ UNREACHABLE(); #define GENERATE_AST_VISITOR_SWITCH() \ switch (node->node_type()) { \ AST_NODE_LIST(GENERATE_VISIT_CASE) \ FAILURE_NODE_LIST(GENERATE_FAILURE_CASE) \ } #define DEFINE_AST_VISITOR_SUBCLASS_MEMBERS() \ public: \ void VisitNoStackOverflowCheck(AstNode* node) { \ GENERATE_AST_VISITOR_SWITCH() \ } \ \ void Visit(AstNode* node) { \ if (CheckStackOverflow()) return; \ VisitNoStackOverflowCheck(node); \ } \ \ void SetStackOverflow() { stack_overflow_ = true; } \ void ClearStackOverflow() { stack_overflow_ = false; } \ bool HasStackOverflow() const { return stack_overflow_; } \ \ bool CheckStackOverflow() { \ if (stack_overflow_) return true; \ if (GetCurrentStackPosition() < stack_limit_) { \ stack_overflow_ = true; \ return true; \ } \ return false; \ } \ \ protected: \ uintptr_t stack_limit() const { return stack_limit_; } \ \ private: \ void InitializeAstVisitor(Isolate* isolate) { \ stack_limit_ = isolate->stack_guard()->real_climit(); \ stack_overflow_ = false; \ } \ \ void InitializeAstVisitor(uintptr_t stack_limit) { \ stack_limit_ = stack_limit; \ stack_overflow_ = false; \ } \ \ uintptr_t stack_limit_; \ bool stack_overflow_ #define DEFINE_AST_VISITOR_MEMBERS_WITHOUT_STACKOVERFLOW() \ public: \ void Visit(AstNode* node) { GENERATE_AST_VISITOR_SWITCH() } \ \ private: // ---------------------------------------------------------------------------- // AstNode factory class AstNodeFactory final { public: AstNodeFactory(AstValueFactory* ast_value_factory, Zone* zone) : zone_(zone), ast_value_factory_(ast_value_factory), empty_statement_(new (zone) class EmptyStatement()), this_expression_(new (zone) class ThisExpression()), failure_expression_(new (zone) class FailureExpression()) {} AstNodeFactory* ast_node_factory() { return this; } AstValueFactory* ast_value_factory() const { return ast_value_factory_; } VariableDeclaration* NewVariableDeclaration(int pos) { return new (zone_) VariableDeclaration(pos); } NestedVariableDeclaration* NewNestedVariableDeclaration(Scope* scope, int pos) { return new (zone_) NestedVariableDeclaration(scope, pos); } FunctionDeclaration* NewFunctionDeclaration(FunctionLiteral* fun, int pos) { return new (zone_) FunctionDeclaration(fun, pos); } Block* NewBlock(int capacity, bool ignore_completion_value) { return new (zone_) Block(zone_, nullptr, capacity, ignore_completion_value); } Block* NewBlock(bool ignore_completion_value, ZonePtrList* labels) { return labels != nullptr ? new (zone_) LabeledBlock(labels, ignore_completion_value) : new (zone_) Block(labels, ignore_completion_value); } Block* NewBlock(bool ignore_completion_value, const ScopedPtrList& statements) { Block* result = NewBlock(ignore_completion_value, nullptr); result->InitializeStatements(statements, zone_); return result; } #define STATEMENT_WITH_LABELS(NodeType) \ NodeType* New##NodeType(ZonePtrList* labels, \ ZonePtrList* own_labels, \ int pos) { \ return new (zone_) NodeType(labels, own_labels, pos); \ } STATEMENT_WITH_LABELS(DoWhileStatement) STATEMENT_WITH_LABELS(WhileStatement) STATEMENT_WITH_LABELS(ForStatement) #undef STATEMENT_WITH_LABELS SwitchStatement* NewSwitchStatement(ZonePtrList* labels, Expression* tag, int pos) { return new (zone_) SwitchStatement(zone_, labels, tag, pos); } ForEachStatement* NewForEachStatement( ForEachStatement::VisitMode visit_mode, ZonePtrList* labels, ZonePtrList* own_labels, int pos) { switch (visit_mode) { case ForEachStatement::ENUMERATE: { return new (zone_) ForInStatement(labels, own_labels, pos); } case ForEachStatement::ITERATE: { return new (zone_) ForOfStatement(labels, own_labels, pos, IteratorType::kNormal); } } UNREACHABLE(); } ForOfStatement* NewForOfStatement(ZonePtrList* labels, ZonePtrList* own_labels, int pos, IteratorType type) { return new (zone_) ForOfStatement(labels, own_labels, pos, type); } ExpressionStatement* NewExpressionStatement(Expression* expression, int pos) { return new (zone_) ExpressionStatement(expression, pos); } ContinueStatement* NewContinueStatement(IterationStatement* target, int pos) { return new (zone_) ContinueStatement(target, pos); } BreakStatement* NewBreakStatement(BreakableStatement* target, int pos) { return new (zone_) BreakStatement(target, pos); } ReturnStatement* NewReturnStatement(Expression* expression, int pos, int end_position = kNoSourcePosition) { return new (zone_) ReturnStatement(expression, ReturnStatement::kNormal, pos, end_position); } ReturnStatement* NewAsyncReturnStatement( Expression* expression, int pos, int end_position = kNoSourcePosition) { return new (zone_) ReturnStatement( expression, ReturnStatement::kAsyncReturn, pos, end_position); } ReturnStatement* NewSyntheticAsyncReturnStatement( Expression* expression, int pos, int end_position = kNoSourcePosition) { return new (zone_) ReturnStatement( expression, ReturnStatement::kSyntheticAsyncReturn, pos, end_position); } WithStatement* NewWithStatement(Scope* scope, Expression* expression, Statement* statement, int pos) { return new (zone_) WithStatement(scope, expression, statement, pos); } IfStatement* NewIfStatement(Expression* condition, Statement* then_statement, Statement* else_statement, int pos) { return new (zone_) IfStatement(condition, then_statement, else_statement, pos); } TryCatchStatement* NewTryCatchStatement(Block* try_block, Scope* scope, Block* catch_block, int pos) { return new (zone_) TryCatchStatement(try_block, scope, catch_block, HandlerTable::CAUGHT, pos); } TryCatchStatement* NewTryCatchStatementForReThrow(Block* try_block, Scope* scope, Block* catch_block, int pos) { return new (zone_) TryCatchStatement(try_block, scope, catch_block, HandlerTable::UNCAUGHT, pos); } TryCatchStatement* NewTryCatchStatementForDesugaring(Block* try_block, Scope* scope, Block* catch_block, int pos) { return new (zone_) TryCatchStatement(try_block, scope, catch_block, HandlerTable::DESUGARING, pos); } TryCatchStatement* NewTryCatchStatementForAsyncAwait(Block* try_block, Scope* scope, Block* catch_block, int pos) { return new (zone_) TryCatchStatement(try_block, scope, catch_block, HandlerTable::ASYNC_AWAIT, pos); } TryFinallyStatement* NewTryFinallyStatement(Block* try_block, Block* finally_block, int pos) { return new (zone_) TryFinallyStatement(try_block, finally_block, pos); } DebuggerStatement* NewDebuggerStatement(int pos) { return new (zone_) DebuggerStatement(pos); } class EmptyStatement* EmptyStatement() { return empty_statement_; } class ThisExpression* ThisExpression() { // Clear any previously set "parenthesized" flag on this_expression_ so this // particular token does not inherit the it. The flag is used to check // during arrow function head parsing whether we came from parenthesized // exprssion parsing, since additional arrow function verification was done // there. It does not matter whether a flag is unset after arrow head // verification, so clearing at this point is fine. this_expression_->clear_parenthesized(); return this_expression_; } class FailureExpression* FailureExpression() { return failure_expression_; } SloppyBlockFunctionStatement* NewSloppyBlockFunctionStatement( int pos, Variable* var, Token::Value init) { return new (zone_) SloppyBlockFunctionStatement(pos, var, init, EmptyStatement()); } CaseClause* NewCaseClause(Expression* label, const ScopedPtrList& statements) { return new (zone_) CaseClause(zone_, label, statements); } Literal* NewStringLiteral(const AstRawString* string, int pos) { DCHECK_NOT_NULL(string); return new (zone_) Literal(string, pos); } // A JavaScript symbol (ECMA-262 edition 6). Literal* NewSymbolLiteral(AstSymbol symbol, int pos) { return new (zone_) Literal(symbol, pos); } Literal* NewNumberLiteral(double number, int pos); Literal* NewSmiLiteral(int number, int pos) { return new (zone_) Literal(number, pos); } Literal* NewBigIntLiteral(AstBigInt bigint, int pos) { return new (zone_) Literal(bigint, pos); } Literal* NewBooleanLiteral(bool b, int pos) { return new (zone_) Literal(b, pos); } Literal* NewNullLiteral(int pos) { return new (zone_) Literal(Literal::kNull, pos); } Literal* NewUndefinedLiteral(int pos) { return new (zone_) Literal(Literal::kUndefined, pos); } Literal* NewTheHoleLiteral() { return new (zone_) Literal(Literal::kTheHole, kNoSourcePosition); } ObjectLiteral* NewObjectLiteral( const ScopedPtrList& properties, uint32_t boilerplate_properties, int pos, bool has_rest_property) { return new (zone_) ObjectLiteral(zone_, properties, boilerplate_properties, pos, has_rest_property); } ObjectLiteral::Property* NewObjectLiteralProperty( Expression* key, Expression* value, ObjectLiteralProperty::Kind kind, bool is_computed_name) { return new (zone_) ObjectLiteral::Property(key, value, kind, is_computed_name); } ObjectLiteral::Property* NewObjectLiteralProperty(Expression* key, Expression* value, bool is_computed_name) { return new (zone_) ObjectLiteral::Property(ast_value_factory_, key, value, is_computed_name); } RegExpLiteral* NewRegExpLiteral(const AstRawString* pattern, int flags, int pos) { return new (zone_) RegExpLiteral(pattern, flags, pos); } ArrayLiteral* NewArrayLiteral(const ScopedPtrList& values, int pos) { return new (zone_) ArrayLiteral(zone_, values, -1, pos); } ArrayLiteral* NewArrayLiteral(const ScopedPtrList& values, int first_spread_index, int pos) { return new (zone_) ArrayLiteral(zone_, values, first_spread_index, pos); } VariableProxy* NewVariableProxy(Variable* var, int start_position = kNoSourcePosition) { return new (zone_) VariableProxy(var, start_position); } VariableProxy* NewVariableProxy(const AstRawString* name, VariableKind variable_kind, int start_position = kNoSourcePosition) { DCHECK_NOT_NULL(name); return new (zone_) VariableProxy(name, variable_kind, start_position); } // Recreates the VariableProxy in this Zone. VariableProxy* CopyVariableProxy(VariableProxy* proxy) { return new (zone_) VariableProxy(proxy); } Variable* CopyVariable(Variable* variable) { return new (zone_) Variable(variable); } OptionalChain* NewOptionalChain(Expression* expression) { return new (zone_) OptionalChain(expression); } Property* NewProperty(Expression* obj, Expression* key, int pos, bool optional_chain = false) { return new (zone_) Property(obj, key, pos, optional_chain); } ResolvedProperty* NewResolvedProperty(VariableProxy* obj, VariableProxy* property, int pos = kNoSourcePosition) { return new (zone_) ResolvedProperty(obj, property, pos); } Call* NewCall(Expression* expression, const ScopedPtrList& arguments, int pos, Call::PossiblyEval possibly_eval = Call::NOT_EVAL, bool optional_chain = false) { return new (zone_) Call(zone_, expression, arguments, pos, possibly_eval, optional_chain); } Call* NewTaggedTemplate(Expression* expression, const ScopedPtrList& arguments, int pos) { return new (zone_) Call(zone_, expression, arguments, pos, Call::TaggedTemplateTag::kTrue); } CallNew* NewCallNew(Expression* expression, const ScopedPtrList& arguments, int pos) { return new (zone_) CallNew(zone_, expression, arguments, pos); } CallRuntime* NewCallRuntime(Runtime::FunctionId id, const ScopedPtrList& arguments, int pos) { return new (zone_) CallRuntime(zone_, Runtime::FunctionForId(id), arguments, pos); } CallRuntime* NewCallRuntime(const Runtime::Function* function, const ScopedPtrList& arguments, int pos) { return new (zone_) CallRuntime(zone_, function, arguments, pos); } CallRuntime* NewCallRuntime(int context_index, const ScopedPtrList& arguments, int pos) { return new (zone_) CallRuntime(zone_, context_index, arguments, pos); } UnaryOperation* NewUnaryOperation(Token::Value op, Expression* expression, int pos) { return new (zone_) UnaryOperation(op, expression, pos); } BinaryOperation* NewBinaryOperation(Token::Value op, Expression* left, Expression* right, int pos) { return new (zone_) BinaryOperation(op, left, right, pos); } NaryOperation* NewNaryOperation(Token::Value op, Expression* first, size_t initial_subsequent_size) { return new (zone_) NaryOperation(zone_, op, first, initial_subsequent_size); } CountOperation* NewCountOperation(Token::Value op, bool is_prefix, Expression* expr, int pos) { return new (zone_) CountOperation(op, is_prefix, expr, pos); } CompareOperation* NewCompareOperation(Token::Value op, Expression* left, Expression* right, int pos) { return new (zone_) CompareOperation(op, left, right, pos); } Spread* NewSpread(Expression* expression, int pos, int expr_pos) { return new (zone_) Spread(expression, pos, expr_pos); } StoreInArrayLiteral* NewStoreInArrayLiteral(Expression* array, Expression* index, Expression* value, int pos) { return new (zone_) StoreInArrayLiteral(array, index, value, pos); } Conditional* NewConditional(Expression* condition, Expression* then_expression, Expression* else_expression, int position) { return new (zone_) Conditional(condition, then_expression, else_expression, position); } Assignment* NewAssignment(Token::Value op, Expression* target, Expression* value, int pos) { DCHECK(Token::IsAssignmentOp(op)); DCHECK_NOT_NULL(target); DCHECK_NOT_NULL(value); if (op != Token::INIT && target->IsVariableProxy()) { target->AsVariableProxy()->set_is_assigned(); } if (op == Token::ASSIGN || op == Token::INIT) { return new (zone_) Assignment(AstNode::kAssignment, op, target, value, pos); } else { return new (zone_) CompoundAssignment( op, target, value, pos, NewBinaryOperation(Token::BinaryOpForAssignment(op), target, value, pos + 1)); } } Suspend* NewYield(Expression* expression, int pos, Suspend::OnAbruptResume on_abrupt_resume) { if (!expression) expression = NewUndefinedLiteral(pos); return new (zone_) Yield(expression, pos, on_abrupt_resume); } YieldStar* NewYieldStar(Expression* expression, int pos) { return new (zone_) YieldStar(expression, pos); } Await* NewAwait(Expression* expression, int pos) { if (!expression) expression = NewUndefinedLiteral(pos); return new (zone_) Await(expression, pos); } Throw* NewThrow(Expression* exception, int pos) { return new (zone_) Throw(exception, pos); } FunctionLiteral* NewFunctionLiteral( const AstRawString* name, DeclarationScope* scope, const ScopedPtrList& body, int expected_property_count, int parameter_count, int function_length, FunctionLiteral::ParameterFlag has_duplicate_parameters, FunctionSyntaxKind function_syntax_kind, FunctionLiteral::EagerCompileHint eager_compile_hint, int position, bool has_braces, int function_literal_id, ProducedPreparseData* produced_preparse_data = nullptr) { return new (zone_) FunctionLiteral( zone_, name, ast_value_factory_, scope, body, expected_property_count, parameter_count, function_length, function_syntax_kind, has_duplicate_parameters, eager_compile_hint, position, has_braces, function_literal_id, produced_preparse_data); } // Creates a FunctionLiteral representing a top-level script, the // result of an eval (top-level or otherwise), or the result of calling // the Function constructor. FunctionLiteral* NewScriptOrEvalFunctionLiteral( DeclarationScope* scope, const ScopedPtrList& body, int expected_property_count, int parameter_count) { return new (zone_) FunctionLiteral( zone_, ast_value_factory_->empty_string(), ast_value_factory_, scope, body, expected_property_count, parameter_count, parameter_count, FunctionSyntaxKind::kAnonymousExpression, FunctionLiteral::kNoDuplicateParameters, FunctionLiteral::kShouldLazyCompile, 0, /* has_braces */ false, kFunctionLiteralIdTopLevel); } ClassLiteral::Property* NewClassLiteralProperty( Expression* key, Expression* value, ClassLiteralProperty::Kind kind, bool is_static, bool is_computed_name, bool is_private) { return new (zone_) ClassLiteral::Property(key, value, kind, is_static, is_computed_name, is_private); } ClassLiteral* NewClassLiteral( ClassScope* scope, Expression* extends, FunctionLiteral* constructor, ZonePtrList* public_members, ZonePtrList* private_members, FunctionLiteral* static_fields_initializer, FunctionLiteral* instance_members_initializer_function, int start_position, int end_position, bool has_name_static_property, bool has_static_computed_names, bool is_anonymous, bool has_private_methods) { return new (zone_) ClassLiteral( scope, extends, constructor, public_members, private_members, static_fields_initializer, instance_members_initializer_function, start_position, end_position, has_name_static_property, has_static_computed_names, is_anonymous, has_private_methods); } NativeFunctionLiteral* NewNativeFunctionLiteral(const AstRawString* name, v8::Extension* extension, int pos) { return new (zone_) NativeFunctionLiteral(name, extension, pos); } DoExpression* NewDoExpression(Block* block, Variable* result_var, int pos) { VariableProxy* result = NewVariableProxy(result_var, pos); return new (zone_) DoExpression(block, result, pos); } SuperPropertyReference* NewSuperPropertyReference(Expression* home_object, int pos) { return new (zone_) SuperPropertyReference(home_object, pos); } SuperCallReference* NewSuperCallReference(VariableProxy* new_target_var, VariableProxy* this_function_var, int pos) { return new (zone_) SuperCallReference(new_target_var, this_function_var, pos); } EmptyParentheses* NewEmptyParentheses(int pos) { return new (zone_) EmptyParentheses(pos); } GetTemplateObject* NewGetTemplateObject( const ZonePtrList* cooked_strings, const ZonePtrList* raw_strings, int pos) { return new (zone_) GetTemplateObject(cooked_strings, raw_strings, pos); } TemplateLiteral* NewTemplateLiteral( const ZonePtrList* string_parts, const ZonePtrList* substitutions, int pos) { return new (zone_) TemplateLiteral(string_parts, substitutions, pos); } ImportCallExpression* NewImportCallExpression(Expression* args, int pos) { return new (zone_) ImportCallExpression(args, pos); } InitializeClassMembersStatement* NewInitializeClassMembersStatement( ZonePtrList* args, int pos) { return new (zone_) InitializeClassMembersStatement(args, pos); } Zone* zone() const { return zone_; } private: // This zone may be deallocated upon returning from parsing a function body // which we can guarantee is not going to be compiled or have its AST // inspected. // See ParseFunctionLiteral in parser.cc for preconditions. Zone* zone_; AstValueFactory* ast_value_factory_; class EmptyStatement* empty_statement_; class ThisExpression* this_expression_; class FailureExpression* failure_expression_; }; // Type testing & conversion functions overridden by concrete subclasses. // Inline functions for AstNode. #define DECLARE_NODE_FUNCTIONS(type) \ bool AstNode::Is##type() const { return node_type() == AstNode::k##type; } \ type* AstNode::As##type() { \ return node_type() == AstNode::k##type ? reinterpret_cast(this) \ : nullptr; \ } \ const type* AstNode::As##type() const { \ return node_type() == AstNode::k##type \ ? reinterpret_cast(this) \ : nullptr; \ } AST_NODE_LIST(DECLARE_NODE_FUNCTIONS) FAILURE_NODE_LIST(DECLARE_NODE_FUNCTIONS) #undef DECLARE_NODE_FUNCTIONS } // namespace internal } // namespace v8 #endif // V8_AST_AST_H_