// 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/bailout-reason.h" #include "src/globals.h" #include "src/heap/factory.h" #include "src/isolate.h" #include "src/label.h" #include "src/objects/literal-objects.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(InitializeClassFieldsStatement) #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(GetIterator) \ V(GetTemplateObject) \ V(ImportCallExpression) \ V(Literal) \ V(NativeFunctionLiteral) \ V(Property) \ V(ResolvedProperty) \ V(RewritableExpression) \ V(Spread) \ V(StoreInArrayLiteral) \ V(SuperCallReference) \ V(SuperPropertyReference) \ V(TemplateLiteral) \ V(ThisFunction) \ V(Throw) \ V(UnaryOperation) \ V(VariableProxy) \ V(Yield) \ V(YieldStar) #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 ProducedPreParsedScopeData; class Statement; #define DEF_FORWARD_DECLARATION(type) class type; AST_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) }; #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(); 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) #undef DECLARE_NODE_FUNCTIONS BreakableStatement* AsBreakableStatement(); 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_; class NodeTypeField : public BitField {}; protected: uint32_t bit_field_; static const uint8_t kNextBitFieldIndex = NodeTypeField::kNext; AstNode(int position, NodeType type) : position_(position), bit_field_(NodeTypeField::encode(type)) {} }; class Statement : public AstNode { public: bool IsEmpty() { return AsEmptyStatement() != nullptr; } bool IsJump() const; protected: Statement(int position, NodeType type) : AstNode(position, type) {} static const uint8_t kNextBitFieldIndex = AstNode::kNextBitFieldIndex; }; 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; // 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. 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; bool IsCompileTimeValue(); protected: Expression(int pos, NodeType type) : AstNode(pos, type) {} static const uint8_t kNextBitFieldIndex = AstNode::kNextBitFieldIndex; }; // 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: class BreakableTypeField : public BitField {}; protected: BreakableStatement(BreakableType breakable_type, int position, NodeType type) : Statement(position, type) { bit_field_ |= BreakableTypeField::encode(breakable_type); } static const uint8_t kNextBitFieldIndex = BreakableTypeField::kNext; }; class Block : public BreakableStatement { public: ZonePtrList* statements() { return &statements_; } bool ignore_completion_value() const { return IgnoreCompletionField::decode(bit_field_); } inline ZonePtrList* labels() const; bool IsJump() const { return !statements_.is_empty() && statements_.last()->IsJump() && labels() == nullptr; // Good enough as an approximation... } Scope* scope() const { return scope_; } void set_scope(Scope* scope) { scope_ = scope; } private: friend class AstNodeFactory; ZonePtrList statements_; Scope* scope_; class IgnoreCompletionField : public BitField {}; class IsLabeledField : public BitField {}; 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); } }; 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); } 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: typedef ThreadedList List; VariableProxy* proxy() const { return proxy_; } protected: Declaration(VariableProxy* proxy, int pos, NodeType type) : AstNode(pos, type), proxy_(proxy), next_(nullptr) {} private: VariableProxy* proxy_; // Declarations list threaded through the declarations. Declaration** next() { return &next_; } Declaration* next_; friend List; }; class VariableDeclaration : public Declaration { public: inline NestedVariableDeclaration* AsNested(); private: friend class AstNodeFactory; class IsNestedField : public BitField {}; protected: VariableDeclaration(VariableProxy* proxy, int pos, bool is_nested = false) : Declaration(proxy, pos, kVariableDeclaration) { bit_field_ = IsNestedField::update(bit_field_, is_nested); } static const uint8_t kNextBitFieldIndex = IsNestedField::kNext; }; // 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(VariableProxy* proxy, Scope* scope, int pos) : VariableDeclaration(proxy, 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(VariableProxy* proxy, FunctionLiteral* fun, int pos) : Declaration(proxy, pos, kFunctionDeclaration), fun_(fun) { DCHECK_NOT_NULL(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; } static const uint8_t kNextBitFieldIndex = BreakableStatement::kNextBitFieldIndex; 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_; }; 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"; } protected: ForEachStatement(ZonePtrList* labels, ZonePtrList* own_labels, int pos, NodeType type) : IterationStatement(labels, own_labels, pos, type) {} }; class ForInStatement final : public ForEachStatement { public: void Initialize(Expression* each, Expression* subject, Statement* body) { ForEachStatement::Initialize(body); each_ = each; subject_ = subject; } Expression* enumerable() const { return subject(); } Expression* each() const { return each_; } Expression* subject() const { return subject_; } enum ForInType { FAST_FOR_IN, SLOW_FOR_IN }; ForInType for_in_type() const { return ForInTypeField::decode(bit_field_); } private: friend class AstNodeFactory; ForInStatement(ZonePtrList* labels, ZonePtrList* own_labels, int pos) : ForEachStatement(labels, own_labels, pos, kForInStatement), each_(nullptr), subject_(nullptr) { bit_field_ = ForInTypeField::update(bit_field_, SLOW_FOR_IN); } Expression* each_; Expression* subject_; class ForInTypeField : public BitField {}; }; class ForOfStatement final : public ForEachStatement { public: void Initialize(Statement* body, Variable* iterator, Expression* assign_iterator, Expression* assign_next, Expression* next_result, Expression* result_done, Expression* assign_each) { ForEachStatement::Initialize(body); iterator_ = iterator; assign_iterator_ = assign_iterator; assign_next_ = assign_next; next_result_ = next_result; result_done_ = result_done; assign_each_ = assign_each; } Variable* iterator() const { return iterator_; } // iterator = subject[Symbol.iterator]() Expression* assign_iterator() const { return assign_iterator_; } // iteratorRecord.next = iterator.next Expression* assign_next() const { return assign_next_; } // result = iterator.next() // with type check Expression* next_result() const { return next_result_; } // result.done Expression* result_done() const { return result_done_; } // each = result.value Expression* assign_each() const { return assign_each_; } void set_assign_iterator(Expression* e) { assign_iterator_ = e; } void set_assign_next(Expression* e) { assign_next_ = e; } void set_next_result(Expression* e) { next_result_ = e; } void set_result_done(Expression* e) { result_done_ = e; } void set_assign_each(Expression* e) { assign_each_ = e; } private: friend class AstNodeFactory; ForOfStatement(ZonePtrList* labels, ZonePtrList* own_labels, int pos) : ForEachStatement(labels, own_labels, pos, kForOfStatement), iterator_(nullptr), assign_iterator_(nullptr), next_result_(nullptr), result_done_(nullptr), assign_each_(nullptr) {} Variable* iterator_; Expression* assign_iterator_; Expression* assign_next_; Expression* next_result_; Expression* result_done_; Expression* assign_each_; }; class ExpressionStatement final : public Statement { public: void set_expression(Expression* e) { expression_ = e; } Expression* expression() const { return expression_; } bool IsJump() const { return expression_->IsThrow(); } private: friend class AstNodeFactory; ExpressionStatement(Expression* expression, int pos) : Statement(pos, kExpressionStatement), expression_(expression) {} Expression* expression_; }; class JumpStatement : public Statement { public: bool IsJump() const { return true; } 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 }; Expression* expression() const { return expression_; } Type type() const { return TypeField::decode(bit_field_); } bool is_async_return() const { return type() == kAsyncReturn; } 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_; class TypeField : public BitField {}; }; 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() const { return statements_; } private: friend class AstNodeFactory; CaseClause(Expression* label, ZonePtrList* 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()->IsEmpty(); } bool HasElseStatement() const { return !else_statement()->IsEmpty(); } 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; } bool IsJump() const { return HasThenStatement() && then_statement()->IsJump() && HasElseStatement() && else_statement()->IsJump(); } 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; } 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; explicit EmptyStatement(int pos) : Statement(pos, 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; } private: friend class AstNodeFactory; explicit SloppyBlockFunctionStatement(Statement* statement) : Statement(kNoSourcePosition, kSloppyBlockFunctionStatement), statement_(statement) {} Statement* statement_; }; 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; class TypeField : public BitField {}; 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; class NeedsInitialAllocationSiteField : public BitField {}; class IsSimpleField : public BitField {}; 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); } static const uint8_t kNextBitFieldIndex = IsSimpleField::kNext; }; // Common supertype for ObjectLiteralProperty and ClassLiteralProperty class LiteralProperty : public ZoneObject { public: Expression* key() const { return key_; } Expression* value() const { return value_; } bool is_computed_name() const { return is_computed_name_; } bool NeedsSetFunctionName() const; protected: LiteralProperty(Expression* key, Expression* value, bool is_computed_name) : key_(key), value_(value), is_computed_name_(is_computed_name) {} Expression* key_; Expression* value_; bool is_computed_name_; }; // 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: typedef ObjectLiteralProperty Property; Handle boilerplate_description() const { DCHECK(!boilerplate_description_.is_null()); return boilerplate_description_; } int properties_count() const { return boilerplate_properties_; } 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)); struct Accessors: public ZoneObject { Accessors() : getter(nullptr), setter(nullptr) {} ObjectLiteralProperty* getter; ObjectLiteralProperty* setter; }; private: friend class AstNodeFactory; ObjectLiteral(ZonePtrList* properties, uint32_t boilerplate_properties, int pos, bool has_rest_property) : AggregateLiteral(pos, kObjectLiteral), boilerplate_properties_(boilerplate_properties), properties_(properties) { bit_field_ |= HasElementsField::encode(false) | HasRestPropertyField::encode(has_rest_property) | FastElementsField::encode(false) | HasNullPrototypeField::encode(false); } 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_; class HasElementsField : public BitField {}; class HasRestPropertyField : public BitField {}; class FastElementsField : public BitField {}; class HasNullPrototypeField : public BitField {}; }; // A map from property names to getter/setter pairs allocated in the zone. class AccessorTable : public base::TemplateHashMap { public: explicit AccessorTable(Zone* zone) : base::TemplateHashMap( Literal::Match, ZoneAllocationPolicy(zone)), zone_(zone) {} Iterator lookup(Literal* literal) { Iterator it = find(literal, true, ZoneAllocationPolicy(zone_)); if (it->second == nullptr) { it->second = new (zone_) ObjectLiteral::Accessors(); } return it; } private: Zone* zone_; }; // 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_; } ZonePtrList* values() const { return values_; } int first_spread_index() const { return first_spread_index_; } bool is_empty() const; // 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(ZonePtrList* values, int first_spread_index, int pos) : AggregateLiteral(pos, kArrayLiteral), first_spread_index_(first_spread_index), values_(values) {} int first_spread_index_; Handle boilerplate_description_; ZonePtrList* values_; }; enum class HoleCheckMode { kRequired, kElided }; class VariableProxy final : public Expression { public: bool IsValidReferenceExpression() const { return !is_this() && !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; } bool is_this() const { return IsThisField::decode(bit_field_); } bool is_assigned() const { return IsAssignedField::decode(bit_field_); } void set_is_assigned() { bit_field_ = IsAssignedField::update(bit_field_, true); if (is_resolved()) { var()->set_maybe_assigned(); } } 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 is_private_field() const { return IsPrivateField::decode(bit_field_); } void set_is_private_field() { bit_field_ = IsPrivateField::update(bit_field_, true); } // Bind this proxy to the variable var. void BindTo(Variable* var); void set_next_unresolved(VariableProxy* next) { next_unresolved_ = next; } VariableProxy* next_unresolved() { return next_unresolved_; } 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) { bit_field_ |= IsThisField::encode(variable_kind == THIS_VARIABLE) | IsAssignedField::encode(false) | IsResolvedField::encode(false) | HoleCheckModeField::encode(HoleCheckMode::kElided) | IsPrivateField::encode(false); } explicit VariableProxy(const VariableProxy* copy_from); class IsThisField : public BitField { }; class IsAssignedField : public BitField {}; class IsResolvedField : public BitField {}; class IsNewTargetField : public BitField {}; class HoleCheckModeField : public BitField {}; class IsPrivateField : public BitField {}; union { const AstRawString* raw_name_; // if !is_resolved_ Variable* var_; // if is_resolved_ }; VariableProxy* next_unresolved_; }; // Left-hand side can only be a property, a global or a (parameter or local) // slot. enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY, NAMED_SUPER_PROPERTY, KEYED_SUPER_PROPERTY }; class Property final : public Expression { public: bool IsValidReferenceExpression() const { return true; } Expression* obj() const { return obj_; } Expression* key() const { return key_; } bool IsSuperAccess() { return obj()->IsSuperPropertyReference(); } // Returns the properties assign type. static LhsKind GetAssignType(Property* property) { if (property == nullptr) return VARIABLE; 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) : Expression(pos, kProperty), obj_(obj), key_(key) { } 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_; } 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 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, 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(Expression* expression, ZonePtrList* arguments, int pos, PossiblyEval possibly_eval) : Expression(pos, kCall), expression_(expression), arguments_(arguments) { bit_field_ |= IsPossiblyEvalField::encode(possibly_eval == IS_POSSIBLY_EVAL) | IsTaggedTemplateField::encode(false); } Call(Expression* expression, ZonePtrList* arguments, int pos, TaggedTemplateTag tag) : Expression(pos, kCall), expression_(expression), arguments_(arguments) { bit_field_ |= IsPossiblyEvalField::encode(false) | IsTaggedTemplateField::encode(true); } class IsPossiblyEvalField : public BitField {}; class IsTaggedTemplateField : public BitField {}; Expression* expression_; ZonePtrList* arguments_; }; class CallNew final : public Expression { public: Expression* expression() const { return expression_; } ZonePtrList* arguments() const { return arguments_; } bool only_last_arg_is_spread() { return !arguments_->is_empty() && arguments_->last()->IsSpread(); } private: friend class AstNodeFactory; CallNew(Expression* expression, ZonePtrList* arguments, int pos) : Expression(pos, kCallNew), expression_(expression), arguments_(arguments) {} 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: 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(const Runtime::Function* function, ZonePtrList* arguments, int pos) : Expression(pos, kCallRuntime), function_(function), arguments_(arguments) {} CallRuntime(int context_index, ZonePtrList* arguments, int pos) : Expression(pos, kCallRuntime), context_index_(context_index), function_(nullptr), arguments_(arguments) {} 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_; class OperatorField : public BitField {}; }; 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_; class OperatorField : public BitField {}; }; 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_; class OperatorField : public BitField {}; }; 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); } class IsPrefixField : public BitField {}; class TokenField : public BitField {}; 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_; class OperatorField : public BitField {}; }; 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; class TokenField : public BitField {}; class LookupHoistingModeField : public BitField { }; 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_; }; // The RewritableExpression class is a wrapper for AST nodes that wait // for some potential rewriting. However, even if such nodes are indeed // rewritten, the RewritableExpression wrapper nodes will survive in the // final AST and should be just ignored, i.e., they should be treated as // equivalent to the wrapped nodes. For this reason and to simplify later // phases, RewritableExpressions are considered as exceptions of AST nodes // in the following sense: // // 1. IsRewritableExpression and AsRewritableExpression behave as usual. // 2. All other Is* and As* methods are practically delegated to the // wrapped node, i.e. IsArrayLiteral() will return true iff the // wrapped node is an array literal. // // Furthermore, an invariant that should be respected is that the wrapped // node is not a RewritableExpression. class RewritableExpression final : public Expression { public: Expression* expression() const { return expr_; } bool is_rewritten() const { return IsRewrittenField::decode(bit_field_); } void set_rewritten() { bit_field_ = IsRewrittenField::update(bit_field_, true); } void Rewrite(Expression* new_expression) { DCHECK(!is_rewritten()); DCHECK_NOT_NULL(new_expression); DCHECK(!new_expression->IsRewritableExpression()); expr_ = new_expression; set_rewritten(); } Scope* scope() const { return scope_; } void set_scope(Scope* scope) { scope_ = scope; } private: friend class AstNodeFactory; RewritableExpression(Expression* expression, Scope* scope) : Expression(expression->position(), kRewritableExpression), expr_(expression), scope_(scope) { bit_field_ |= IsRewrittenField::encode(false); DCHECK(!expression->IsRewritableExpression()); } Expression* expr_; Scope* scope_; class IsRewrittenField : public BitField {}; }; // 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_; class OnAbruptResumeField : public BitField {}; }; 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 FunctionType { kAnonymousExpression, kNamedExpression, kDeclaration, kAccessorOrMethod, kWrapped, }; enum IdType { kIdTypeInvalid = -1, kIdTypeTopLevel = 0 }; enum ParameterFlag { kNoDuplicateParameters, kHasDuplicateParameters }; enum EagerCompileHint { 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() const { 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_declaration() const { return function_type() == kDeclaration; } bool is_named_expression() const { return function_type() == kNamedExpression; } bool is_anonymous_expression() const { return function_type() == kAnonymousExpression; } void mark_as_iife() { bit_field_ = IIFEBit::update(bit_field_, true); } bool is_iife() const { return IIFEBit::decode(bit_field_); } bool is_top_level() const { return function_literal_id() == FunctionLiteral::kIdTypeTopLevel; } bool is_wrapped() const { return function_type() == kWrapped; } LanguageMode language_mode() const; static bool NeedsHomeObject(Expression* expr); int expected_property_count() { // Not valid for lazy functions. DCHECK_NOT_NULL(body_); 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; } // 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_NOT_NULL(body_); 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() { ... }(); bool ShouldEagerCompile() const; void SetShouldEagerCompile(); FunctionType function_type() const { return FunctionTypeBits::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_fields_initializer(bool value) { bit_field_ = RequiresInstanceFieldsInitializer::update(bit_field_, value); } bool requires_instance_fields_initializer() const { return RequiresInstanceFieldsInitializer::decode(bit_field_); } ProducedPreParsedScopeData* produced_preparsed_scope_data() const { return produced_preparsed_scope_data_; } private: friend class AstNodeFactory; FunctionLiteral( Zone* zone, const AstRawString* name, AstValueFactory* ast_value_factory, DeclarationScope* scope, ZonePtrList* body, int expected_property_count, int parameter_count, int function_length, FunctionType function_type, ParameterFlag has_duplicate_parameters, EagerCompileHint eager_compile_hint, int position, bool has_braces, int function_literal_id, ProducedPreParsedScopeData* produced_preparsed_scope_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_(body), raw_inferred_name_(ast_value_factory->empty_cons_string()), produced_preparsed_scope_data_(produced_preparsed_scope_data) { bit_field_ |= FunctionTypeBits::encode(function_type) | Pretenure::encode(false) | HasDuplicateParameters::encode(has_duplicate_parameters == kHasDuplicateParameters) | DontOptimizeReasonField::encode(BailoutReason::kNoReason) | RequiresInstanceFieldsInitializer::encode(false) | HasBracesField::encode(has_braces) | IIFEBit::encode(false); if (eager_compile_hint == kShouldEagerCompile) SetShouldEagerCompile(); DCHECK_EQ(body == nullptr, expected_property_count < 0); } class FunctionTypeBits : public BitField {}; class Pretenure : public BitField {}; class HasDuplicateParameters : public BitField {}; class DontOptimizeReasonField : public BitField {}; class RequiresInstanceFieldsInitializer : public BitField {}; class HasBracesField : public BitField {}; class IIFEBit : public BitField {}; 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_; ProducedPreParsedScopeData* produced_preparsed_scope_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, PUBLIC_FIELD, PRIVATE_FIELD }; Kind kind() const { return kind_; } bool is_static() const { return is_static_; } void set_computed_name_var(Variable* var) { DCHECK_EQ(PUBLIC_FIELD, kind()); private_or_computed_name_var_ = var; } Variable* computed_name_var() const { DCHECK_EQ(PUBLIC_FIELD, kind()); return private_or_computed_name_var_; } void set_private_field_name_var(Variable* var) { DCHECK_EQ(PRIVATE_FIELD, kind()); private_or_computed_name_var_ = var; } Variable* private_field_name_var() const { DCHECK_EQ(PRIVATE_FIELD, kind()); return private_or_computed_name_var_; } private: friend class AstNodeFactory; ClassLiteralProperty(Expression* key, Expression* value, Kind kind, bool is_static, bool is_computed_name); Kind kind_; bool is_static_; Variable* private_or_computed_name_var_; }; class InitializeClassFieldsStatement final : public Statement { public: typedef ClassLiteralProperty Property; ZonePtrList* fields() const { return fields_; } private: friend class AstNodeFactory; InitializeClassFieldsStatement(ZonePtrList* fields, int pos) : Statement(pos, kInitializeClassFieldsStatement), fields_(fields) {} ZonePtrList* fields_; }; class ClassLiteral final : public Expression { public: typedef ClassLiteralProperty Property; Scope* scope() const { return scope_; } Variable* class_variable() const { return class_variable_; } Expression* extends() const { return extends_; } FunctionLiteral* constructor() const { return constructor_; } ZonePtrList* properties() const { return properties_; } 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 IsAnonymousFunctionDefinition() const { return is_anonymous_expression(); } FunctionLiteral* static_fields_initializer() const { return static_fields_initializer_; } FunctionLiteral* instance_fields_initializer_function() const { return instance_fields_initializer_function_; } private: friend class AstNodeFactory; ClassLiteral(Scope* scope, Variable* class_variable, Expression* extends, FunctionLiteral* constructor, ZonePtrList* properties, FunctionLiteral* static_fields_initializer, FunctionLiteral* instance_fields_initializer_function, int start_position, int end_position, bool has_name_static_property, bool has_static_computed_names, bool is_anonymous) : Expression(start_position, kClassLiteral), end_position_(end_position), scope_(scope), class_variable_(class_variable), extends_(extends), constructor_(constructor), properties_(properties), static_fields_initializer_(static_fields_initializer), instance_fields_initializer_function_( instance_fields_initializer_function) { bit_field_ |= HasNameStaticProperty::encode(has_name_static_property) | HasStaticComputedNames::encode(has_static_computed_names) | IsAnonymousExpression::encode(is_anonymous); } int end_position_; Scope* scope_; Variable* class_variable_; Expression* extends_; FunctionLiteral* constructor_; ZonePtrList* properties_; FunctionLiteral* static_fields_initializer_; FunctionLiteral* instance_fields_initializer_function_; class HasNameStaticProperty : public BitField {}; class HasStaticComputedNames : public BitField {}; class IsAnonymousExpression : public BitField {}; }; 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 ThisFunction final : public Expression { private: friend class AstNodeFactory; explicit ThisFunction(int pos) : Expression(pos, kThisFunction) {} }; class SuperPropertyReference final : public Expression { public: VariableProxy* this_var() const { return this_var_; } Expression* home_object() const { return home_object_; } private: friend class AstNodeFactory; SuperPropertyReference(VariableProxy* this_var, Expression* home_object, int pos) : Expression(pos, kSuperPropertyReference), this_var_(this_var), home_object_(home_object) { DCHECK(this_var->is_this()); DCHECK(home_object->IsProperty()); } VariableProxy* this_var_; Expression* home_object_; }; class SuperCallReference final : public Expression { public: VariableProxy* this_var() const { return this_var_; } VariableProxy* new_target_var() const { return new_target_var_; } VariableProxy* this_function_var() const { return this_function_var_; } private: friend class AstNodeFactory; SuperCallReference(VariableProxy* this_var, VariableProxy* new_target_var, VariableProxy* this_function_var, int pos) : Expression(pos, kSuperCallReference), this_var_(this_var), new_target_var_(new_target_var), this_function_var_(this_function_var) { DCHECK(this_var->is_this()); DCHECK(new_target_var->raw_name()->IsOneByteEqualTo(".new.target")); DCHECK(this_function_var->raw_name()->IsOneByteEqualTo(".this_function")); } VariableProxy* this_var_; 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) {} }; // Represents the spec operation `GetIterator()` // (defined at https://tc39.github.io/ecma262/#sec-getiterator). Ignition // desugars this into a LoadIC / JSLoadNamed, CallIC, and a type-check to // validate return value of the Symbol.iterator() call. enum class IteratorType { kNormal, kAsync }; class GetIterator final : public Expression { public: IteratorType hint() const { return hint_; } Expression* iterable() const { return iterable_; } Expression* iterable_for_call_printer() const { return destructured_iterable_ != nullptr ? destructured_iterable_ : iterable_; } private: friend class AstNodeFactory; GetIterator(Expression* iterable, Expression* destructured_iterable, IteratorType hint, int pos) : Expression(pos, kGetIterator), hint_(hint), iterable_(iterable), destructured_iterable_(destructured_iterable) {} GetIterator(Expression* iterable, IteratorType hint, int pos) : Expression(pos, kGetIterator), hint_(hint), iterable_(iterable), destructured_iterable_(nullptr) {} IteratorType hint_; Expression* iterable_; // iterable_ is the variable proxy, while destructured_iterable_ points to // the raw value stored in the variable proxy. This is only used for // pretty printing error messages. Expression* destructured_iterable_; }; // 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 BASE_EMBEDDED { public: void Visit(AstNode* node) { impl()->Visit(node); } void VisitDeclarations(Declaration::List* declarations) { for (Declaration* decl : *declarations) Visit(decl); } void VisitStatements(ZonePtrList* statements) { for (int i = 0; i < statements->length(); i++) { Statement* stmt = statements->at(i); Visit(stmt); if (stmt->IsJump()) break; } } void VisitExpressions(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_AST_VISITOR_SWITCH() \ switch (node->node_type()) { \ AST_NODE_LIST(GENERATE_VISIT_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; \ } \ \ 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 BASE_EMBEDDED { public: AstNodeFactory(AstValueFactory* ast_value_factory, Zone* zone) : zone_(zone), ast_value_factory_(ast_value_factory) {} AstValueFactory* ast_value_factory() const { return ast_value_factory_; } VariableDeclaration* NewVariableDeclaration(VariableProxy* proxy, int pos) { return new (zone_) VariableDeclaration(proxy, pos); } NestedVariableDeclaration* NewNestedVariableDeclaration(VariableProxy* proxy, Scope* scope, int pos) { return new (zone_) NestedVariableDeclaration(proxy, scope, pos); } FunctionDeclaration* NewFunctionDeclaration(VariableProxy* proxy, FunctionLiteral* fun, int pos) { return new (zone_) FunctionDeclaration(proxy, fun, pos); } Block* NewBlock(int capacity, bool ignore_completion_value, ZonePtrList* labels = nullptr) { return labels != nullptr ? new (zone_) LabeledBlock(zone_, labels, capacity, ignore_completion_value) : new (zone_) Block(zone_, labels, capacity, ignore_completion_value); } #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); } } UNREACHABLE(); } ForOfStatement* NewForOfStatement(ZonePtrList* labels, ZonePtrList* own_labels, int pos) { return new (zone_) ForOfStatement(labels, own_labels, pos); } 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); } 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); } EmptyStatement* NewEmptyStatement(int pos) { return new (zone_) EmptyStatement(pos); } SloppyBlockFunctionStatement* NewSloppyBlockFunctionStatement() { return new (zone_) SloppyBlockFunctionStatement(NewEmptyStatement(kNoSourcePosition)); } CaseClause* NewCaseClause(Expression* label, ZonePtrList* statements) { return new (zone_) CaseClause(label, statements); } Literal* NewStringLiteral(const AstRawString* string, int pos) { 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( ZonePtrList* properties, uint32_t boilerplate_properties, int pos, bool has_rest_property) { return new (zone_) ObjectLiteral(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(ZonePtrList* values, int pos) { return new (zone_) ArrayLiteral(values, -1, pos); } ArrayLiteral* NewArrayLiteral(ZonePtrList* values, int first_spread_index, int pos) { return new (zone_) ArrayLiteral(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); } Property* NewProperty(Expression* obj, Expression* key, int pos) { return new (zone_) Property(obj, key, pos); } ResolvedProperty* NewResolvedProperty(VariableProxy* obj, VariableProxy* property, int pos = kNoSourcePosition) { return new (zone_) ResolvedProperty(obj, property, pos); } Call* NewCall(Expression* expression, ZonePtrList* arguments, int pos, Call::PossiblyEval possibly_eval = Call::NOT_EVAL) { return new (zone_) Call(expression, arguments, pos, possibly_eval); } Call* NewTaggedTemplate(Expression* expression, ZonePtrList* arguments, int pos) { return new (zone_) Call(expression, arguments, pos, Call::TaggedTemplateTag::kTrue); } CallNew* NewCallNew(Expression* expression, ZonePtrList* arguments, int pos) { return new (zone_) CallNew(expression, arguments, pos); } CallRuntime* NewCallRuntime(Runtime::FunctionId id, ZonePtrList* arguments, int pos) { return new (zone_) CallRuntime(Runtime::FunctionForId(id), arguments, pos); } CallRuntime* NewCallRuntime(const Runtime::Function* function, ZonePtrList* arguments, int pos) { return new (zone_) CallRuntime(function, arguments, pos); } CallRuntime* NewCallRuntime(int context_index, ZonePtrList* arguments, int pos) { return new (zone_) CallRuntime(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); } RewritableExpression* NewRewritableExpression(Expression* expression, Scope* scope) { DCHECK_NOT_NULL(expression); return new (zone_) RewritableExpression(expression, scope); } Assignment* NewAssignment(Token::Value op, Expression* target, Expression* value, int pos) { DCHECK(Token::IsAssignmentOp(op)); 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) { DCHECK_NOT_NULL(expression); 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, ZonePtrList* body, int expected_property_count, int parameter_count, int function_length, FunctionLiteral::ParameterFlag has_duplicate_parameters, FunctionLiteral::FunctionType function_type, FunctionLiteral::EagerCompileHint eager_compile_hint, int position, bool has_braces, int function_literal_id, ProducedPreParsedScopeData* produced_preparsed_scope_data = nullptr) { return new (zone_) FunctionLiteral( zone_, name, ast_value_factory_, scope, body, expected_property_count, parameter_count, function_length, function_type, has_duplicate_parameters, eager_compile_hint, position, has_braces, function_literal_id, produced_preparsed_scope_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, ZonePtrList* 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, FunctionLiteral::kAnonymousExpression, FunctionLiteral::kNoDuplicateParameters, FunctionLiteral::kShouldLazyCompile, 0, /* has_braces */ false, FunctionLiteral::kIdTypeTopLevel); } ClassLiteral::Property* NewClassLiteralProperty( Expression* key, Expression* value, ClassLiteralProperty::Kind kind, bool is_static, bool is_computed_name) { return new (zone_) ClassLiteral::Property(key, value, kind, is_static, is_computed_name); } ClassLiteral* NewClassLiteral( Scope* scope, Variable* variable, Expression* extends, FunctionLiteral* constructor, ZonePtrList* properties, FunctionLiteral* static_fields_initializer, FunctionLiteral* instance_fields_initializer_function, int start_position, int end_position, bool has_name_static_property, bool has_static_computed_names, bool is_anonymous) { return new (zone_) ClassLiteral( scope, variable, extends, constructor, properties, static_fields_initializer, instance_fields_initializer_function, start_position, end_position, has_name_static_property, has_static_computed_names, is_anonymous); } 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); } ThisFunction* NewThisFunction(int pos) { return new (zone_) ThisFunction(pos); } SuperPropertyReference* NewSuperPropertyReference(VariableProxy* this_var, Expression* home_object, int pos) { return new (zone_) SuperPropertyReference(this_var, home_object, pos); } SuperCallReference* NewSuperCallReference(VariableProxy* this_var, VariableProxy* new_target_var, VariableProxy* this_function_var, int pos) { return new (zone_) SuperCallReference(this_var, new_target_var, this_function_var, pos); } EmptyParentheses* NewEmptyParentheses(int pos) { return new (zone_) EmptyParentheses(pos); } GetIterator* NewGetIterator(Expression* iterable, Expression* destructured_iterable, IteratorType hint, int pos) { return new (zone_) GetIterator(iterable, destructured_iterable, hint, pos); } GetIterator* NewGetIterator(Expression* iterable, IteratorType hint, int pos) { return new (zone_) GetIterator(iterable, hint, 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); } InitializeClassFieldsStatement* NewInitializeClassFieldsStatement( ZonePtrList* args, int pos) { return new (zone_) InitializeClassFieldsStatement(args, pos); } Zone* zone() const { return zone_; } void set_zone(Zone* zone) { zone_ = 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_; }; // Type testing & conversion functions overridden by concrete subclasses. // Inline functions for AstNode. #define DECLARE_NODE_FUNCTIONS(type) \ bool AstNode::Is##type() const { \ NodeType mine = node_type(); \ if (mine == AstNode::kRewritableExpression && \ AstNode::k##type != AstNode::kRewritableExpression) \ mine = reinterpret_cast(this) \ ->expression() \ ->node_type(); \ return mine == AstNode::k##type; \ } \ type* AstNode::As##type() { \ NodeType mine = node_type(); \ AstNode* result = this; \ if (mine == AstNode::kRewritableExpression && \ AstNode::k##type != AstNode::kRewritableExpression) { \ result = \ reinterpret_cast(this)->expression(); \ mine = result->node_type(); \ } \ return mine == AstNode::k##type ? reinterpret_cast(result) \ : nullptr; \ } \ const type* AstNode::As##type() const { \ NodeType mine = node_type(); \ const AstNode* result = this; \ if (mine == AstNode::kRewritableExpression && \ AstNode::k##type != AstNode::kRewritableExpression) { \ result = \ reinterpret_cast(this)->expression(); \ mine = result->node_type(); \ } \ return mine == AstNode::k##type ? reinterpret_cast(result) \ : nullptr; \ } AST_NODE_LIST(DECLARE_NODE_FUNCTIONS) #undef DECLARE_NODE_FUNCTIONS } // namespace internal } // namespace v8 #endif // V8_AST_AST_H_