// 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_SCOPES_H_ #define V8_AST_SCOPES_H_ #include <numeric> #include "src/ast/ast.h" #include "src/base/compiler-specific.h" #include "src/base/hashmap.h" #include "src/base/threaded-list.h" #include "src/common/globals.h" #include "src/objects/function-kind.h" #include "src/objects/objects.h" #include "src/utils/pointer-with-payload.h" #include "src/utils/utils.h" #include "src/zone/zone.h" namespace v8 { namespace internal { class AstNodeFactory; class AstValueFactory; class AstRawString; class Declaration; class ParseInfo; class Parser; class PreparseDataBuilder; class SloppyBlockFunctionStatement; class Statement; class StringSet; class VariableProxy; using UnresolvedList = base::ThreadedList<VariableProxy, VariableProxy::UnresolvedNext>; // A hash map to support fast variable declaration and lookup. class VariableMap : public ZoneHashMap { public: explicit VariableMap(Zone* zone); Variable* Declare(Zone* zone, Scope* scope, const AstRawString* name, VariableMode mode, VariableKind kind, InitializationFlag initialization_flag, MaybeAssignedFlag maybe_assigned_flag, IsStaticFlag is_static_flag, bool* was_added); V8_EXPORT_PRIVATE Variable* Lookup(const AstRawString* name); void Remove(Variable* var); void Add(Zone* zone, Variable* var); }; class Scope; template <> struct PointerWithPayloadTraits<Scope> { static constexpr int value = 1; }; // Global invariants after AST construction: Each reference (i.e. identifier) // to a JavaScript variable (including global properties) is represented by a // VariableProxy node. Immediately after AST construction and before variable // allocation, most VariableProxy nodes are "unresolved", i.e. not bound to a // corresponding variable (though some are bound during parse time). Variable // allocation binds each unresolved VariableProxy to one Variable and assigns // a location. Note that many VariableProxy nodes may refer to the same Java- // Script variable. // JS environments are represented in the parser using Scope, DeclarationScope // and ModuleScope. DeclarationScope is used for any scope that hosts 'var' // declarations. This includes script, module, eval, varblock, and function // scope. ModuleScope further specializes DeclarationScope. class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) { public: // --------------------------------------------------------------------------- // Construction Scope(Zone* zone, Scope* outer_scope, ScopeType scope_type); #ifdef DEBUG // The scope name is only used for printing/debugging. void SetScopeName(const AstRawString* scope_name) { scope_name_ = scope_name; } #endif DeclarationScope* AsDeclarationScope(); const DeclarationScope* AsDeclarationScope() const; ModuleScope* AsModuleScope(); const ModuleScope* AsModuleScope() const; ClassScope* AsClassScope(); const ClassScope* AsClassScope() const; class Snapshot final { public: Snapshot() : outer_scope_and_calls_eval_(nullptr, false), top_unresolved_(), top_local_() { DCHECK(IsCleared()); } inline explicit Snapshot(Scope* scope); ~Snapshot() { // If we're still active, there was no arrow function. In that case outer // calls eval if it already called eval before this snapshot started, or // if the code during the snapshot called eval. if (!IsCleared() && outer_scope_and_calls_eval_.GetPayload()) { RestoreEvalFlag(); } } void RestoreEvalFlag() { if (outer_scope_and_calls_eval_.GetPayload()) { // This recreates both calls_eval and sloppy_eval_can_extend_vars. outer_scope_and_calls_eval_.GetPointer()->RecordEvalCall(); } } void Reparent(DeclarationScope* new_parent); bool IsCleared() const { return outer_scope_and_calls_eval_.GetPointer() == nullptr; } void Clear() { outer_scope_and_calls_eval_.SetPointer(nullptr); #ifdef DEBUG outer_scope_and_calls_eval_.SetPayload(false); top_inner_scope_ = nullptr; top_local_ = base::ThreadedList<Variable>::Iterator(); top_unresolved_ = UnresolvedList::Iterator(); #endif } private: // During tracking calls_eval caches whether the outer scope called eval. // Upon move assignment we store whether the new inner scope calls eval into // the move target calls_eval bit, and restore calls eval on the outer // scope. PointerWithPayload<Scope, bool, 1> outer_scope_and_calls_eval_; Scope* top_inner_scope_; UnresolvedList::Iterator top_unresolved_; base::ThreadedList<Variable>::Iterator top_local_; // Disallow copy and move. Snapshot(const Snapshot&) = delete; Snapshot(Snapshot&&) = delete; }; enum class DeserializationMode { kIncludingVariables, kScopesOnly }; static Scope* DeserializeScopeChain(Isolate* isolate, Zone* zone, ScopeInfo scope_info, DeclarationScope* script_scope, AstValueFactory* ast_value_factory, DeserializationMode deserialization_mode); // Checks if the block scope is redundant, i.e. it does not contain any // block scoped declarations. In that case it is removed from the scope // tree and its children are reparented. Scope* FinalizeBlockScope(); // Inserts outer_scope into this scope's scope chain (and removes this // from the current outer_scope_'s inner scope list). // Assumes outer_scope_ is non-null. void ReplaceOuterScope(Scope* outer_scope); Zone* zone() const { return zone_; } void SetMustUsePreparseData() { if (must_use_preparsed_scope_data_) { return; } must_use_preparsed_scope_data_ = true; if (outer_scope_) { outer_scope_->SetMustUsePreparseData(); } } bool must_use_preparsed_scope_data() const { return must_use_preparsed_scope_data_; } // --------------------------------------------------------------------------- // Declarations // Lookup a variable in this scope. Returns the variable or nullptr if not // found. Variable* LookupLocal(const AstRawString* name) { DCHECK(scope_info_.is_null()); return variables_.Lookup(name); } Variable* LookupInScopeInfo(const AstRawString* name, Scope* cache); // Declare a local variable in this scope. If the variable has been // declared before, the previously declared variable is returned. Variable* DeclareLocal(const AstRawString* name, VariableMode mode, VariableKind kind, bool* was_added, InitializationFlag init_flag = kCreatedInitialized); Variable* DeclareVariable(Declaration* declaration, const AstRawString* name, int pos, VariableMode mode, VariableKind kind, InitializationFlag init, bool* was_added, bool* sloppy_mode_block_scope_function_redefinition, bool* ok); // Returns nullptr if there was a declaration conflict. Variable* DeclareVariableName(const AstRawString* name, VariableMode mode, bool* was_added, VariableKind kind = NORMAL_VARIABLE); Variable* DeclareCatchVariableName(const AstRawString* name); // Declarations list. base::ThreadedList<Declaration>* declarations() { return &decls_; } base::ThreadedList<Variable>* locals() { return &locals_; } // Create a new unresolved variable. VariableProxy* NewUnresolved(AstNodeFactory* factory, const AstRawString* name, int start_pos, VariableKind kind = NORMAL_VARIABLE) { // Note that we must not share the unresolved variables with // the same name because they may be removed selectively via // RemoveUnresolved(). DCHECK(!already_resolved_); DCHECK_EQ(factory->zone(), zone()); VariableProxy* proxy = factory->NewVariableProxy(name, kind, start_pos); AddUnresolved(proxy); return proxy; } void AddUnresolved(VariableProxy* proxy); // Removes an unresolved variable from the list so it can be readded to // another list. This is used to reparent parameter initializers that contain // sloppy eval. bool RemoveUnresolved(VariableProxy* var); // Deletes an unresolved variable. The variable proxy cannot be reused for // another list later. During parsing, an unresolved variable may have been // added optimistically, but then only the variable name was used (typically // for labels and arrow function parameters). If the variable was not // declared, the addition introduced a new unresolved variable which may end // up being allocated globally as a "ghost" variable. DeleteUnresolved removes // such a variable again if it was added; otherwise this is a no-op. void DeleteUnresolved(VariableProxy* var); // Creates a new temporary variable in this scope's TemporaryScope. The // name is only used for printing and cannot be used to find the variable. // In particular, the only way to get hold of the temporary is by keeping the // Variable* around. The name should not clash with a legitimate variable // names. // TODO(verwaest): Move to DeclarationScope? Variable* NewTemporary(const AstRawString* name); // Find variable with (variable->mode() <= |mode_limit|) that was declared in // |scope|. This is used to catch patterns like `try{}catch(e){let e;}` and // function([e]) { let e }, which are errors even though the two 'e's are each // time declared in different scopes. Returns the first duplicate variable // name if there is one, nullptr otherwise. const AstRawString* FindVariableDeclaredIn(Scope* scope, VariableMode mode_limit); // --------------------------------------------------------------------------- // Scope-specific info. // Inform the scope and outer scopes that the corresponding code contains an // eval call. inline void RecordEvalCall(); void RecordInnerScopeEvalCall() { inner_scope_calls_eval_ = true; for (Scope* scope = outer_scope(); scope != nullptr; scope = scope->outer_scope()) { if (scope->inner_scope_calls_eval_) return; scope->inner_scope_calls_eval_ = true; } } // Set the language mode flag (unless disabled by a global flag). void SetLanguageMode(LanguageMode language_mode) { DCHECK(!is_module_scope() || is_strict(language_mode)); set_language_mode(language_mode); } // Inform the scope that the scope may execute declarations nonlinearly. // Currently, the only nonlinear scope is a switch statement. The name is // more general in case something else comes up with similar control flow, // for example the ability to break out of something which does not have // its own lexical scope. // The bit does not need to be stored on the ScopeInfo because none of // the three compilers will perform hole check elimination on a variable // located in VariableLocation::CONTEXT. So, direct eval and closures // will not expose holes. void SetNonlinear() { scope_nonlinear_ = true; } // Position in the source where this scope begins and ends. // // * For the scope of a with statement // with (obj) stmt // start position: start position of first token of 'stmt' // end position: end position of last token of 'stmt' // * For the scope of a block // { stmts } // start position: start position of '{' // end position: end position of '}' // * For the scope of a function literal or decalaration // function fun(a,b) { stmts } // start position: start position of '(' // end position: end position of '}' // * For the scope of a catch block // try { stms } catch(e) { stmts } // start position: start position of '(' // end position: end position of ')' // * For the scope of a for-statement // for (let x ...) stmt // start position: start position of '(' // end position: end position of last token of 'stmt' // * For the scope of a switch statement // switch (tag) { cases } // start position: start position of '{' // end position: end position of '}' int start_position() const { return start_position_; } void set_start_position(int statement_pos) { start_position_ = statement_pos; } int end_position() const { return end_position_; } void set_end_position(int statement_pos) { end_position_ = statement_pos; } // Scopes created for desugaring are hidden. I.e. not visible to the debugger. bool is_hidden() const { return is_hidden_; } void set_is_hidden() { is_hidden_ = true; } void ForceContextAllocationForParameters() { DCHECK(!already_resolved_); force_context_allocation_for_parameters_ = true; } bool has_forced_context_allocation_for_parameters() const { return force_context_allocation_for_parameters_; } // --------------------------------------------------------------------------- // Predicates. // Specific scope types. bool is_eval_scope() const { return scope_type_ == EVAL_SCOPE; } bool is_function_scope() const { return scope_type_ == FUNCTION_SCOPE; } bool is_module_scope() const { return scope_type_ == MODULE_SCOPE; } bool is_script_scope() const { return scope_type_ == SCRIPT_SCOPE; } bool is_catch_scope() const { return scope_type_ == CATCH_SCOPE; } bool is_block_scope() const { return scope_type_ == BLOCK_SCOPE || scope_type_ == CLASS_SCOPE; } bool is_with_scope() const { return scope_type_ == WITH_SCOPE; } bool is_declaration_scope() const { return is_declaration_scope_; } bool is_class_scope() const { return scope_type_ == CLASS_SCOPE; } bool inner_scope_calls_eval() const { return inner_scope_calls_eval_; } bool private_name_lookup_skips_outer_class() const { return private_name_lookup_skips_outer_class_; } bool IsAsmModule() const; // Returns true if this scope or any inner scopes that might be eagerly // compiled are asm modules. bool ContainsAsmModule() const; // Does this scope have the potential to execute declarations non-linearly? bool is_nonlinear() const { return scope_nonlinear_; } // Returns if we need to force a context because the current scope is stricter // than the outerscope. We need this to properly track the language mode using // the context. This is required in ICs where we lookup the language mode // from the context. bool ForceContextForLanguageMode() const { // For function scopes we need not force a context since the language mode // can be obtained from the closure. Script scopes always have a context. if (scope_type_ == FUNCTION_SCOPE || scope_type_ == SCRIPT_SCOPE) { return false; } DCHECK_NOT_NULL(outer_scope_); return (language_mode() > outer_scope_->language_mode()); } // Whether this needs to be represented by a runtime context. bool NeedsContext() const { // Catch scopes always have heap slots. DCHECK_IMPLIES(is_catch_scope(), num_heap_slots() > 0); DCHECK_IMPLIES(is_with_scope(), num_heap_slots() > 0); DCHECK_IMPLIES(ForceContextForLanguageMode(), num_heap_slots() > 0); return num_heap_slots() > 0; } // Use Scope::ForEach for depth first traversal of scopes. // Before: // void Scope::VisitRecursively() { // DoSomething(); // for (Scope* s = inner_scope_; s != nullptr; s = s->sibling_) { // if (s->ShouldContinue()) continue; // s->VisitRecursively(); // } // } // // After: // void Scope::VisitIteratively() { // this->ForEach([](Scope* s) { // s->DoSomething(); // return s->ShouldContinue() ? kContinue : kDescend; // }); // } template <typename FunctionType> V8_INLINE void ForEach(FunctionType callback); enum Iteration { // Continue the iteration on the same level, do not recurse/descent into // inner scopes. kContinue, // Recurse/descend into inner scopes. kDescend }; // --------------------------------------------------------------------------- // Accessors. // The type of this scope. ScopeType scope_type() const { return scope_type_; } // The language mode of this scope. LanguageMode language_mode() const { return is_strict_ ? LanguageMode::kStrict : LanguageMode::kSloppy; } // inner_scope() and sibling() together implement the inner scope list of a // scope. Inner scope points to the an inner scope of the function, and // "sibling" points to a next inner scope of the outer scope of this scope. Scope* inner_scope() const { return inner_scope_; } Scope* sibling() const { return sibling_; } // The scope immediately surrounding this scope, or nullptr. Scope* outer_scope() const { return outer_scope_; } Variable* catch_variable() const { DCHECK(is_catch_scope()); DCHECK_EQ(1, num_var()); return static_cast<Variable*>(variables_.Start()->value); } bool ShouldBanArguments(); // --------------------------------------------------------------------------- // Variable allocation. // Result of variable allocation. int num_stack_slots() const { return num_stack_slots_; } int num_heap_slots() const { return num_heap_slots_; } bool HasContextExtensionSlot() const { switch (scope_type_) { case MODULE_SCOPE: case WITH_SCOPE: // DebugEvaluateContext as well return true; default: DCHECK_IMPLIES(sloppy_eval_can_extend_vars_, scope_type_ == FUNCTION_SCOPE || scope_type_ == EVAL_SCOPE || scope_type_ == BLOCK_SCOPE); DCHECK_IMPLIES(sloppy_eval_can_extend_vars_, is_declaration_scope()); return sloppy_eval_can_extend_vars_; } UNREACHABLE(); } int ContextHeaderLength() const { return HasContextExtensionSlot() ? Context::MIN_CONTEXT_EXTENDED_SLOTS : Context::MIN_CONTEXT_SLOTS; } int ContextLocalCount() const; // Determine if we can parse a function literal in this scope lazily without // caring about the unresolved variables within. bool AllowsLazyParsingWithoutUnresolvedVariables(const Scope* outer) const; // The number of contexts between this and scope; zero if this == scope. int ContextChainLength(Scope* scope) const; // The number of contexts between this and the outermost context that has a // sloppy eval call. One if this->sloppy_eval_can_extend_vars(). int ContextChainLengthUntilOutermostSloppyEval() const; // Find the first function, script, eval or (declaration) block scope. This is // the scope where var declarations will be hoisted to in the implementation. DeclarationScope* GetDeclarationScope(); // Find the first non-block declaration scope. This should be either a script, // function, or eval scope. Same as DeclarationScope(), but skips declaration // "block" scopes. Used for differentiating associated function objects (i.e., // the scope for which a function prologue allocates a context) or declaring // temporaries. DeclarationScope* GetClosureScope(); const DeclarationScope* GetClosureScope() const; // Find the first (non-arrow) function or script scope. This is where // 'this' is bound, and what determines the function kind. DeclarationScope* GetReceiverScope(); DeclarationScope* GetScriptScope(); // Find the innermost outer scope that needs a context. Scope* GetOuterScopeWithContext(); bool HasThisReference() const; // Analyze() must have been called once to create the ScopeInfo. Handle<ScopeInfo> scope_info() const { DCHECK(!scope_info_.is_null()); return scope_info_; } int num_var() const { return variables_.occupancy(); } // --------------------------------------------------------------------------- // Debugging. #ifdef DEBUG void Print(int n = 0); // n = indentation; n < 0 => don't print recursively // Check that the scope has positions assigned. void CheckScopePositions(); // Check that all Scopes in the scope tree use the same Zone. void CheckZones(); #endif // Retrieve `IsSimpleParameterList` of current or outer function. bool HasSimpleParameters(); void set_is_debug_evaluate_scope() { is_debug_evaluate_scope_ = true; } bool is_debug_evaluate_scope() const { return is_debug_evaluate_scope_; } bool IsSkippableFunctionScope(); void set_is_repl_mode_scope() { is_repl_mode_scope_ = true; } bool is_repl_mode_scope() const { return is_repl_mode_scope_; } bool RemoveInnerScope(Scope* inner_scope) { DCHECK_NOT_NULL(inner_scope); if (inner_scope == inner_scope_) { inner_scope_ = inner_scope_->sibling_; return true; } for (Scope* scope = inner_scope_; scope != nullptr; scope = scope->sibling_) { if (scope->sibling_ == inner_scope) { scope->sibling_ = scope->sibling_->sibling_; return true; } } return false; } Variable* LookupInScopeOrScopeInfo(const AstRawString* name) { Variable* var = variables_.Lookup(name); if (var != nullptr || scope_info_.is_null()) return var; return LookupInScopeInfo(name, this); } Variable* LookupForTesting(const AstRawString* name) { for (Scope* scope = this; scope != nullptr; scope = scope->outer_scope()) { Variable* var = scope->LookupInScopeOrScopeInfo(name); if (var != nullptr) return var; } return nullptr; } protected: explicit Scope(Zone* zone); void set_language_mode(LanguageMode language_mode) { is_strict_ = is_strict(language_mode); } private: Variable* Declare(Zone* zone, const AstRawString* name, VariableMode mode, VariableKind kind, InitializationFlag initialization_flag, MaybeAssignedFlag maybe_assigned_flag, bool* was_added) { // Static variables can only be declared using ClassScope methods. Variable* result = variables_.Declare( zone, this, name, mode, kind, initialization_flag, maybe_assigned_flag, IsStaticFlag::kNotStatic, was_added); if (*was_added) locals_.Add(result); return result; } // This method should only be invoked on scopes created during parsing (i.e., // not deserialized from a context). Also, since NeedsContext() is only // returning a valid result after variables are resolved, NeedsScopeInfo() // should also be invoked after resolution. bool NeedsScopeInfo() const; Variable* NewTemporary(const AstRawString* name, MaybeAssignedFlag maybe_assigned); // Walk the scope chain to find DeclarationScopes; call // SavePreparseDataForDeclarationScope for each. void SavePreparseData(Parser* parser); // Create a non-local variable with a given name. // These variables are looked up dynamically at runtime. Variable* NonLocal(const AstRawString* name, VariableMode mode); enum ScopeLookupMode { kParsedScope, kDeserializedScope, }; // Variable resolution. // Lookup a variable reference given by name starting with this scope, and // stopping when reaching the outer_scope_end scope. If the code is executed // because of a call to 'eval', the context parameter should be set to the // calling context of 'eval'. template <ScopeLookupMode mode> static Variable* Lookup(VariableProxy* proxy, Scope* scope, Scope* outer_scope_end, Scope* entry_point = nullptr, bool force_context_allocation = false); static Variable* LookupWith(VariableProxy* proxy, Scope* scope, Scope* outer_scope_end, Scope* entry_point, bool force_context_allocation); static Variable* LookupSloppyEval(VariableProxy* proxy, Scope* scope, Scope* outer_scope_end, Scope* entry_point, bool force_context_allocation); static void ResolvePreparsedVariable(VariableProxy* proxy, Scope* scope, Scope* end); void ResolveTo(ParseInfo* info, VariableProxy* proxy, Variable* var); void ResolveVariable(ParseInfo* info, VariableProxy* proxy); V8_WARN_UNUSED_RESULT bool ResolveVariablesRecursively(ParseInfo* info); // Finds free variables of this scope. This mutates the unresolved variables // list along the way, so full resolution cannot be done afterwards. void AnalyzePartially(DeclarationScope* max_outer_scope, AstNodeFactory* ast_node_factory, UnresolvedList* new_unresolved_list, bool maybe_in_arrowhead); void CollectNonLocals(DeclarationScope* max_outer_scope, Isolate* isolate, ParseInfo* info, Handle<StringSet>* non_locals); // Predicates. bool MustAllocate(Variable* var); bool MustAllocateInContext(Variable* var); // Variable allocation. void AllocateStackSlot(Variable* var); V8_INLINE void AllocateHeapSlot(Variable* var); void AllocateNonParameterLocal(Variable* var); void AllocateDeclaredGlobal(Variable* var); V8_INLINE void AllocateNonParameterLocalsAndDeclaredGlobals(); void AllocateVariablesRecursively(); void AllocateScopeInfosRecursively(Isolate* isolate, MaybeHandle<ScopeInfo> outer_scope); void AllocateDebuggerScopeInfos(Isolate* isolate, MaybeHandle<ScopeInfo> outer_scope); // Construct a scope based on the scope info. Scope(Zone* zone, ScopeType type, Handle<ScopeInfo> scope_info); // Construct a catch scope with a binding for the name. Scope(Zone* zone, const AstRawString* catch_variable_name, MaybeAssignedFlag maybe_assigned, Handle<ScopeInfo> scope_info); void AddInnerScope(Scope* inner_scope) { inner_scope->sibling_ = inner_scope_; inner_scope_ = inner_scope; inner_scope->outer_scope_ = this; } void SetDefaults(); friend class DeclarationScope; friend class ClassScope; friend class ScopeTestHelper; Zone* zone_; // Scope tree. Scope* outer_scope_; // the immediately enclosing outer scope, or nullptr Scope* inner_scope_; // an inner scope of this scope Scope* sibling_; // a sibling inner scope of the outer scope of this scope. // The variables declared in this scope: // // All user-declared variables (incl. parameters). For script scopes // variables may be implicitly 'declared' by being used (possibly in // an inner scope) with no intervening with statements or eval calls. VariableMap variables_; // In case of non-scopeinfo-backed scopes, this contains the variables of the // map above in order of addition. base::ThreadedList<Variable> locals_; // Unresolved variables referred to from this scope. The proxies themselves // form a linked list of all unresolved proxies. UnresolvedList unresolved_list_; // Declarations. base::ThreadedList<Declaration> decls_; // Serialized scope info support. Handle<ScopeInfo> scope_info_; // Debugging support. #ifdef DEBUG const AstRawString* scope_name_; // True if it doesn't need scope resolution (e.g., if the scope was // constructed based on a serialized scope info or a catch context). bool already_resolved_; // True if this scope may contain objects from a temp zone that needs to be // fixed up. bool needs_migration_; #endif // Source positions. int start_position_; int end_position_; // Computed via AllocateVariables. int num_stack_slots_; int num_heap_slots_; // The scope type. const ScopeType scope_type_; // Scope-specific information computed during parsing. // // The language mode of this scope. STATIC_ASSERT(LanguageModeSize == 2); bool is_strict_ : 1; // This scope contains an 'eval' call. bool calls_eval_ : 1; // The context associated with this scope can be extended by a sloppy eval // called inside of it. bool sloppy_eval_can_extend_vars_ : 1; // This scope's declarations might not be executed in order (e.g., switch). bool scope_nonlinear_ : 1; bool is_hidden_ : 1; // Temporary workaround that allows masking of 'this' in debug-evaluate // scopes. bool is_debug_evaluate_scope_ : 1; // True if one of the inner scopes or the scope itself calls eval. bool inner_scope_calls_eval_ : 1; bool force_context_allocation_for_parameters_ : 1; // True if it holds 'var' declarations. bool is_declaration_scope_ : 1; // True if the outer scope is a class scope and should be skipped when // resolving private names, i.e. if the scope is in a class heritage // expression. bool private_name_lookup_skips_outer_class_ : 1; bool must_use_preparsed_scope_data_ : 1; // True if this is a script scope that originated from // DebugEvaluate::GlobalREPL(). bool is_repl_mode_scope_ : 1; }; class V8_EXPORT_PRIVATE DeclarationScope : public Scope { public: DeclarationScope(Zone* zone, Scope* outer_scope, ScopeType scope_type, FunctionKind function_kind = kNormalFunction); DeclarationScope(Zone* zone, ScopeType scope_type, Handle<ScopeInfo> scope_info); // Creates a script scope. DeclarationScope(Zone* zone, AstValueFactory* ast_value_factory); FunctionKind function_kind() const { return function_kind_; } bool is_arrow_scope() const { return is_function_scope() && IsArrowFunction(function_kind_); } // Inform the scope that the corresponding code uses "super". void RecordSuperPropertyUsage() { DCHECK(IsConciseMethod(function_kind()) || IsAccessorFunction(function_kind()) || IsClassConstructor(function_kind())); scope_uses_super_property_ = true; } // Does this scope access "super" property (super.foo). bool NeedsHomeObject() const { return scope_uses_super_property_ || (inner_scope_calls_eval_ && (IsConciseMethod(function_kind()) || IsAccessorFunction(function_kind()) || IsClassConstructor(function_kind()))); } // Inform the scope and outer scopes that the corresponding code contains an // eval call. void RecordDeclarationScopeEvalCall() { calls_eval_ = true; // If this isn't a sloppy eval, we don't care about it. if (language_mode() != LanguageMode::kSloppy) return; // Sloppy eval in script scopes can only introduce global variables anyway, // so we don't care that it calls sloppy eval. if (is_script_scope()) return; // Sloppy eval in a eval scope can only introduce variables into the outer // (non-eval) declaration scope, not into this eval scope. if (is_eval_scope()) { #ifdef DEBUG // One of three things must be true: // 1. The outer non-eval declaration scope should already be marked as // being extendable by sloppy eval, by the current sloppy eval rather // than the inner one, // 2. The outer non-eval declaration scope is a script scope and thus // isn't extendable anyway, or // 3. This is a debug evaluate and all bets are off. DeclarationScope* outer_decl_scope = outer_scope()->GetDeclarationScope(); while (outer_decl_scope->is_eval_scope()) { outer_decl_scope = outer_decl_scope->GetDeclarationScope(); } if (outer_decl_scope->is_debug_evaluate_scope()) { // Don't check anything. // TODO(9662): Figure out where variables declared by an eval inside a // debug-evaluate actually go. } else if (!outer_decl_scope->is_script_scope()) { DCHECK(outer_decl_scope->sloppy_eval_can_extend_vars_); } #endif return; } sloppy_eval_can_extend_vars_ = true; num_heap_slots_ = Context::MIN_CONTEXT_EXTENDED_SLOTS; } bool sloppy_eval_can_extend_vars() const { return sloppy_eval_can_extend_vars_; } bool was_lazily_parsed() const { return was_lazily_parsed_; } Variable* LookupInModule(const AstRawString* name) { DCHECK(is_module_scope()); Variable* var = variables_.Lookup(name); DCHECK_NOT_NULL(var); return var; } void DeserializeReceiver(AstValueFactory* ast_value_factory); #ifdef DEBUG void set_is_being_lazily_parsed(bool is_being_lazily_parsed) { is_being_lazily_parsed_ = is_being_lazily_parsed; } bool is_being_lazily_parsed() const { return is_being_lazily_parsed_; } #endif void set_zone(Zone* zone) { #ifdef DEBUG needs_migration_ = true; #endif zone_ = zone; } // --------------------------------------------------------------------------- // Illegal redeclaration support. // Check if the scope has conflicting var // declarations, i.e. a var declaration that has been hoisted from a nested // scope over a let binding of the same name. Declaration* CheckConflictingVarDeclarations(); void set_has_checked_syntax(bool has_checked_syntax) { has_checked_syntax_ = has_checked_syntax; } bool has_checked_syntax() const { return has_checked_syntax_; } bool ShouldEagerCompile() const { return force_eager_compilation_ || should_eager_compile_; } void set_should_eager_compile(); void SetScriptScopeInfo(Handle<ScopeInfo> scope_info) { DCHECK(is_script_scope()); DCHECK(scope_info_.is_null()); scope_info_ = scope_info; } bool is_asm_module() const { return is_asm_module_; } void set_is_asm_module(); bool should_ban_arguments() const { return IsClassMembersInitializerFunction(function_kind()); } void set_is_async_module() { DCHECK(IsModule(function_kind_)); function_kind_ = kAsyncModule; } void DeclareThis(AstValueFactory* ast_value_factory); void DeclareArguments(AstValueFactory* ast_value_factory); void DeclareDefaultFunctionVariables(AstValueFactory* ast_value_factory); // Declare the function variable for a function literal. This variable // is in an intermediate scope between this function scope and the the // outer scope. Only possible for function scopes; at most one variable. // // This function needs to be called after all other variables have been // declared in the scope. It will add a variable for {name} to {variables_}; // either the function variable itself, or a non-local in case the function // calls sloppy eval. Variable* DeclareFunctionVar(const AstRawString* name, Scope* cache = nullptr); // Declare some special internal variables which must be accessible to // Ignition without ScopeInfo. Variable* DeclareGeneratorObjectVar(const AstRawString* name); // Declare a parameter in this scope. When there are duplicated // parameters the rightmost one 'wins'. However, the implementation // expects all parameters to be declared and from left to right. Variable* DeclareParameter(const AstRawString* name, VariableMode mode, bool is_optional, bool is_rest, AstValueFactory* ast_value_factory, int position); // Makes sure that num_parameters_ and has_rest is correct for the preparser. void RecordParameter(bool is_rest); // Declare an implicit global variable in this scope which must be a // script scope. The variable was introduced (possibly from an inner // scope) by a reference to an unresolved variable with no intervening // with statements or eval calls. Variable* DeclareDynamicGlobal(const AstRawString* name, VariableKind variable_kind, Scope* cache); // The variable corresponding to the 'this' value. Variable* receiver() { DCHECK(has_this_declaration() || is_script_scope()); DCHECK_NOT_NULL(receiver_); return receiver_; } bool has_this_declaration() const { return has_this_declaration_; } // The variable corresponding to the 'new.target' value. Variable* new_target_var() { return new_target_; } // The variable holding the function literal for named function // literals, or nullptr. Only valid for function scopes. Variable* function_var() const { return function_; } // The variable holding the JSGeneratorObject for generator, async // and async generator functions, and modules. Only valid for // function and module scopes. Variable* generator_object_var() const { DCHECK(is_function_scope() || is_module_scope()); return GetRareVariable(RareVariable::kGeneratorObject); } // Parameters. The left-most parameter has index 0. // Only valid for function and module scopes. Variable* parameter(int index) const { DCHECK(is_function_scope() || is_module_scope()); DCHECK(!is_being_lazily_parsed_); return params_[index]; } // Returns the number of formal parameters, excluding a possible rest // parameter. Examples: // function foo(a, b) {} ==> 2 // function foo(a, b, ...c) {} ==> 2 // function foo(a, b, c = 1) {} ==> 3 int num_parameters() const { return num_parameters_; } // The function's rest parameter (nullptr if there is none). Variable* rest_parameter() const { return has_rest_ ? params_[params_.length() - 1] : nullptr; } bool has_simple_parameters() const { return has_simple_parameters_; } // TODO(caitp): manage this state in a better way. PreParser must be able to // communicate that the scope is non-simple, without allocating any parameters // as the Parser does. This is necessary to ensure that TC39's proposed early // error can be reported consistently regardless of whether lazily parsed or // not. void SetHasNonSimpleParameters() { DCHECK(is_function_scope()); has_simple_parameters_ = false; } void MakeParametersNonSimple() { SetHasNonSimpleParameters(); for (ZoneHashMap::Entry* p = variables_.Start(); p != nullptr; p = variables_.Next(p)) { Variable* var = reinterpret_cast<Variable*>(p->value); if (var->is_parameter()) var->MakeParameterNonSimple(); } } // Returns whether the arguments object aliases formal parameters. CreateArgumentsType GetArgumentsType() const { DCHECK(is_function_scope()); DCHECK(!is_arrow_scope()); DCHECK_NOT_NULL(arguments_); return is_sloppy(language_mode()) && has_simple_parameters() ? CreateArgumentsType::kMappedArguments : CreateArgumentsType::kUnmappedArguments; } // The local variable 'arguments' if we need to allocate it; nullptr // otherwise. Variable* arguments() const { DCHECK_IMPLIES(is_arrow_scope(), arguments_ == nullptr); return arguments_; } Variable* this_function_var() const { Variable* this_function = GetRareVariable(RareVariable::kThisFunction); // This is only used in derived constructors atm. DCHECK(this_function == nullptr || (is_function_scope() && (IsClassConstructor(function_kind()) || IsConciseMethod(function_kind()) || IsAccessorFunction(function_kind())))); return this_function; } // Adds a local variable in this scope's locals list. This is for adjusting // the scope of temporaries and do-expression vars when desugaring parameter // initializers. void AddLocal(Variable* var); void DeclareSloppyBlockFunction( SloppyBlockFunctionStatement* sloppy_block_function); // Go through sloppy_block_functions_ and hoist those (into this scope) // which should be hoisted. void HoistSloppyBlockFunctions(AstNodeFactory* factory); // Compute top scope and allocate variables. For lazy compilation the top // scope only contains the single lazily compiled function, so this // doesn't re-allocate variables repeatedly. // // Returns false if private names can not be resolved and // ParseInfo's pending_error_handler will be populated with an // error. Otherwise, returns true. V8_WARN_UNUSED_RESULT static bool Analyze(ParseInfo* info); // To be called during parsing. Do just enough scope analysis that we can // discard the Scope contents for lazily compiled functions. In particular, // this records variables which cannot be resolved inside the Scope (we don't // yet know what they will resolve to since the outer Scopes are incomplete) // and recreates them with the correct Zone with ast_node_factory. void AnalyzePartially(Parser* parser, AstNodeFactory* ast_node_factory, bool maybe_in_arrowhead); // Allocate ScopeInfos for top scope and any inner scopes that need them. // Does nothing if ScopeInfo is already allocated. static void AllocateScopeInfos(ParseInfo* info, Isolate* isolate); Handle<StringSet> CollectNonLocals(Isolate* isolate, ParseInfo* info, Handle<StringSet> non_locals); // Determine if we can use lazy compilation for this scope. bool AllowsLazyCompilation() const; // Make sure this closure and all outer closures are eagerly compiled. void ForceEagerCompilation() { DCHECK_EQ(this, GetClosureScope()); DeclarationScope* s; for (s = this; !s->is_script_scope(); s = s->outer_scope()->GetClosureScope()) { s->force_eager_compilation_ = true; } s->force_eager_compilation_ = true; } #ifdef DEBUG void PrintParameters(); #endif V8_INLINE void AllocateLocals(); V8_INLINE void AllocateParameterLocals(); V8_INLINE void AllocateReceiver(); void ResetAfterPreparsing(AstValueFactory* ast_value_factory, bool aborted); bool is_skipped_function() const { return is_skipped_function_; } void set_is_skipped_function(bool is_skipped_function) { is_skipped_function_ = is_skipped_function; } bool has_inferred_function_name() const { return has_inferred_function_name_; } void set_has_inferred_function_name(bool value) { DCHECK(is_function_scope()); has_inferred_function_name_ = value; } // Save data describing the context allocation of the variables in this scope // and its subscopes (except scopes at the laziness boundary). The data is // saved in produced_preparse_data_. void SavePreparseDataForDeclarationScope(Parser* parser); void set_preparse_data_builder(PreparseDataBuilder* preparse_data_builder) { preparse_data_builder_ = preparse_data_builder; } PreparseDataBuilder* preparse_data_builder() const { return preparse_data_builder_; } void set_has_this_reference() { has_this_reference_ = true; } bool has_this_reference() const { return has_this_reference_; } void UsesThis() { set_has_this_reference(); GetReceiverScope()->receiver()->ForceContextAllocation(); } bool needs_private_name_context_chain_recalc() const { return needs_private_name_context_chain_recalc_; } void RecordNeedsPrivateNameContextChainRecalc(); // Re-writes the {VariableLocation} of top-level 'let' bindings from CONTEXT // to REPL_GLOBAL. Should only be called on REPL scripts. void RewriteReplGlobalVariables(); private: V8_INLINE void AllocateParameter(Variable* var, int index); // Resolve and fill in the allocation information for all variables // in this scopes. Must be called *after* all scopes have been // processed (parsed) to ensure that unresolved variables can be // resolved properly. // // In the case of code compiled and run using 'eval', the context // parameter is the context in which eval was called. In all other // cases the context parameter is an empty handle. // // Returns false if private names can not be resolved. bool AllocateVariables(ParseInfo* info); void SetDefaults(); // Recalculate the private name context chain from the existing skip bit in // preparation for AllocateScopeInfos. Because the private name scope is // implemented with a skip bit for scopes in heritage position, that bit may // need to be recomputed due scopes that do not need contexts. void RecalcPrivateNameContextChain(); bool has_simple_parameters_ : 1; // This scope contains an "use asm" annotation. bool is_asm_module_ : 1; bool force_eager_compilation_ : 1; // This function scope has a rest parameter. bool has_rest_ : 1; // This scope has a parameter called "arguments". bool has_arguments_parameter_ : 1; // This scope uses "super" property ('super.foo'). bool scope_uses_super_property_ : 1; bool should_eager_compile_ : 1; // Set to true after we have finished lazy parsing the scope. bool was_lazily_parsed_ : 1; #if DEBUG bool is_being_lazily_parsed_ : 1; #endif bool is_skipped_function_ : 1; bool has_inferred_function_name_ : 1; bool has_checked_syntax_ : 1; bool has_this_reference_ : 1; bool has_this_declaration_ : 1; bool needs_private_name_context_chain_recalc_ : 1; // If the scope is a function scope, this is the function kind. FunctionKind function_kind_; int num_parameters_ = 0; // Parameter list in source order. ZonePtrList<Variable> params_; // Map of function names to lists of functions defined in sloppy blocks base::ThreadedList<SloppyBlockFunctionStatement> sloppy_block_functions_; // Convenience variable. Variable* receiver_; // Function variable, if any; function scopes only. Variable* function_; // new.target variable, function scopes only. Variable* new_target_; // Convenience variable; function scopes only. Variable* arguments_; // For producing the scope allocation data during preparsing. PreparseDataBuilder* preparse_data_builder_; struct RareData : public ZoneObject { // Convenience variable; Subclass constructor only Variable* this_function = nullptr; // Generator object, if any; generator function scopes and module scopes // only. Variable* generator_object = nullptr; }; enum class RareVariable { kThisFunction = offsetof(RareData, this_function), kGeneratorObject = offsetof(RareData, generator_object), }; V8_INLINE RareData* EnsureRareData() { if (rare_data_ == nullptr) { rare_data_ = new (zone_) RareData; } return rare_data_; } V8_INLINE Variable* GetRareVariable(RareVariable id) const { if (rare_data_ == nullptr) return nullptr; return *reinterpret_cast<Variable**>( reinterpret_cast<uint8_t*>(rare_data_) + static_cast<ptrdiff_t>(id)); } // Set `var` to null if it's non-null and Predicate (Variable*) -> bool // returns true. template <typename Predicate> V8_INLINE void NullifyRareVariableIf(RareVariable id, Predicate predicate) { if (V8_LIKELY(rare_data_ == nullptr)) return; Variable** var = reinterpret_cast<Variable**>( reinterpret_cast<uint8_t*>(rare_data_) + static_cast<ptrdiff_t>(id)); if (*var && predicate(*var)) *var = nullptr; } RareData* rare_data_ = nullptr; }; void Scope::RecordEvalCall() { calls_eval_ = true; GetDeclarationScope()->RecordDeclarationScopeEvalCall(); RecordInnerScopeEvalCall(); } Scope::Snapshot::Snapshot(Scope* scope) : outer_scope_and_calls_eval_(scope, scope->calls_eval_), top_inner_scope_(scope->inner_scope_), top_unresolved_(scope->unresolved_list_.end()), top_local_(scope->GetClosureScope()->locals_.end()) { // Reset in order to record eval calls during this Snapshot's lifetime. outer_scope_and_calls_eval_.GetPointer()->calls_eval_ = false; outer_scope_and_calls_eval_.GetPointer()->sloppy_eval_can_extend_vars_ = false; } class ModuleScope final : public DeclarationScope { public: ModuleScope(DeclarationScope* script_scope, AstValueFactory* avfactory); // Deserialization. Does not restore the module descriptor. ModuleScope(Isolate* isolate, Handle<ScopeInfo> scope_info, AstValueFactory* avfactory); // Returns nullptr in a deserialized scope. SourceTextModuleDescriptor* module() const { return module_descriptor_; } // Set MODULE as VariableLocation for all variables that will live in a // module's export table. void AllocateModuleVariables(); private: SourceTextModuleDescriptor* const module_descriptor_; }; class V8_EXPORT_PRIVATE ClassScope : public Scope { public: ClassScope(Zone* zone, Scope* outer_scope, bool is_anonymous); // Deserialization. ClassScope(Isolate* isolate, Zone* zone, AstValueFactory* ast_value_factory, Handle<ScopeInfo> scope_info); struct HeritageParsingScope { explicit HeritageParsingScope(ClassScope* class_scope) : class_scope_(class_scope) { class_scope_->SetIsParsingHeritage(true); } ~HeritageParsingScope() { class_scope_->SetIsParsingHeritage(false); } private: ClassScope* class_scope_; }; // Declare a private name in the private name map and add it to the // local variables of this scope. Variable* DeclarePrivateName(const AstRawString* name, VariableMode mode, IsStaticFlag is_static_flag, bool* was_added); // Try resolving all unresolved private names found in the current scope. // Called from DeclarationScope::AllocateVariables() when reparsing a // method to generate code or when eval() is called to access private names. // If there are any private names that cannot be resolved, returns false. V8_WARN_UNUSED_RESULT bool ResolvePrivateNames(ParseInfo* info); // Called after the entire class literal is parsed. // - If we are certain a private name cannot be resolve, return that // variable proxy. // - If we find the private name in the scope chain, return nullptr. // If the name is found in the current class scope, resolve it // immediately. // - If we are not sure if the private name can be resolved or not yet, // return nullptr. VariableProxy* ResolvePrivateNamesPartially(); // Get the current tail of unresolved private names to be used to // reset the tail. UnresolvedList::Iterator GetUnresolvedPrivateNameTail(); // Reset the tail of unresolved private names, discard everything // between the tail passed into this method and the current tail. void ResetUnresolvedPrivateNameTail(UnresolvedList::Iterator tail); // Migrate private names added between the tail passed into this method // and the current tail. void MigrateUnresolvedPrivateNameTail(AstNodeFactory* ast_node_factory, UnresolvedList::Iterator tail); Variable* DeclareBrandVariable(AstValueFactory* ast_value_factory, IsStaticFlag is_static_flag, int class_token_pos); Variable* DeclareClassVariable(AstValueFactory* ast_value_factory, const AstRawString* name, int class_token_pos); Variable* brand() { return GetRareData() == nullptr ? nullptr : GetRareData()->brand; } Variable* class_variable() { return class_variable_; } V8_INLINE bool IsParsingHeritage() { return rare_data_and_is_parsing_heritage_.GetPayload(); } // Only maintained when the scope is parsed, not when the scope is // deserialized. bool has_static_private_methods() const { return has_static_private_methods_; } // Returns whether the index of class variable of this class scope should be // recorded in the ScopeInfo. // If any inner scope accesses static private names directly, the class // variable will be forced to be context-allocated. // The inner scope may also calls eval which may results in access to // static private names. // Only maintained when the scope is parsed. bool should_save_class_variable_index() const { return should_save_class_variable_index_ || has_explicit_static_private_methods_access_ || (has_static_private_methods_ && inner_scope_calls_eval_); } // Only maintained when the scope is parsed. bool is_anonymous_class() const { return is_anonymous_class_; } // Overriden during reparsing void set_should_save_class_variable_index() { should_save_class_variable_index_ = true; } private: friend class Scope; friend class PrivateNameScopeIterator; // Find the private name declared in the private name map first, // if it cannot be found there, try scope info if there is any. // Returns nullptr if it cannot be found. Variable* LookupPrivateName(VariableProxy* proxy); // Lookup a private name from the local private name map of the current // scope. Variable* LookupLocalPrivateName(const AstRawString* name); // Lookup a private name from the scope info of the current scope. Variable* LookupPrivateNameInScopeInfo(const AstRawString* name); struct RareData : public ZoneObject { explicit RareData(Zone* zone) : private_name_map(zone) {} UnresolvedList unresolved_private_names; VariableMap private_name_map; Variable* brand = nullptr; }; V8_INLINE RareData* GetRareData() { return rare_data_and_is_parsing_heritage_.GetPointer(); } V8_INLINE RareData* EnsureRareData() { if (GetRareData() == nullptr) { rare_data_and_is_parsing_heritage_.SetPointer(new (zone_) RareData(zone_)); } return GetRareData(); } V8_INLINE void SetIsParsingHeritage(bool v) { rare_data_and_is_parsing_heritage_.SetPayload(v); } PointerWithPayload<RareData, bool, 1> rare_data_and_is_parsing_heritage_; Variable* class_variable_ = nullptr; // These are only maintained when the scope is parsed, not when the // scope is deserialized. bool has_static_private_methods_ = false; bool has_explicit_static_private_methods_access_ = false; bool is_anonymous_class_ = false; // This is only maintained during reparsing, restored from the // preparsed data. bool should_save_class_variable_index_ = false; }; // Iterate over the private name scope chain. The iteration proceeds from the // innermost private name scope outwards. class PrivateNameScopeIterator { public: explicit PrivateNameScopeIterator(Scope* start); bool Done() const { return current_scope_ == nullptr; } void Next(); // Add an unresolved private name to the current scope. void AddUnresolvedPrivateName(VariableProxy* proxy); ClassScope* GetScope() const { DCHECK(!Done()); return current_scope_->AsClassScope(); } private: bool skipped_any_scopes_ = false; Scope* start_scope_; Scope* current_scope_; }; } // namespace internal } // namespace v8 #endif // V8_AST_SCOPES_H_