1. third_party/blink/renderer/core/execution_context/execution_context.h — ExecutionContext

    网络上有个误区,其实我们以前理解的所谓的 “执行上下文” 不是 chromium 里的 ExecutionContext
    而是 v8::Context 及其相关内容!!很容易想到啊,js是由v8控制解释的,肯定v8说的算啊
    这个文件下那大段注释英文也说了,这俩不是同一个东西

    1. // An environment in which script can execute. This class exposes the common
    2. // properties of script execution environments on the web (i.e, common between
    3. // script executing in a window and script executing in a worker), such as:
    4. //
    5. // - a base URL for the resolution of relative URLs
    6. // - a security context that defines the privileges associated with the
    7. // environment (note, however, that specific isolated script contexts may
    8. // still enjoy elevated privileges)
    9. // - affordances for the activity (including script and active DOM objects) to
    10. // be paused or terminated, e.g. because a frame has entered the background or
    11. // been closed permanently
    12. // - a console logging facility for debugging
    13. //
    14. // Typically, the ExecutionContext is an instance of LocalDOMWindow or of
    15. // WorkerOrWorkletGlobalScope.
    16. //
    17. // Note that this is distinct from the notion of a ScriptState or v8::Context,
    18. // which are associated with a single script context (with a single global
    19. // object). For example, there are separate JavaScript globals for "main world"
    20. // script written by a web author and an "isolated world" content script written
    21. // by an extension developer, but these share an ExecutionContext (the window)
    22. // in common.
    23. 可以执行脚本的环境。该类公开了网络上脚本执行环境的公共属性
    24. (即在窗口中执行的脚本和在工作器中执行的脚本之间的公共属性),例如:
    25. - 用于解析相对 URL 的基本 URL
    26. - 一个安全上下文,它定义了与这个环境关联的权限
    27. (但是请注意,特定的隔离脚本上下文可能仍然享有更高的权限)
    28. - 要暂停或终止的活动(包括脚本和活动 DOM 对象)的可供性,例如因为框架已进入背景或永久关闭
    29. - 用于调试的控制台日志记录工具
    30. 通常, ExecutionContext LocalDOMWindow
    31. WorkerOrWorkletGlobalScope
    32. 请注意,这与 ScriptState v8::Context 的概念不同,后者与单个脚本上下文(具有单个全局对象)
    33. 相关联。例如,对于由网络作者编写的“主世界”脚本和由扩展开发人员编写的“孤立世界”内容脚本,
    34. 有单独的 JavaScript 全局变量,但它们共享一个共同的 ExecutionContext(窗口)。
    1. v8/include/v8-local-handle.h ```cpp /**
      • An object reference managed by the v8 garbage collector. *
      • All objects returned from v8 have to be tracked by the garbage collector so
      • that it knows that the objects are still alive. Also, because the garbage
      • collector may move objects, it is unsafe to point directly to an object.
      • Instead, all objects are stored in handles which are known by the garbage
      • collector and updated whenever an object moves. Handles should always be
      • passed by value (except in cases like out-parameters) and they should never
      • be allocated on the heap. *
      • There are two types of handles: local and persistent handles. *
      • Local handles are light-weight and transient and typically used in local
      • operations. They are managed by HandleScopes. That means that a HandleScope
      • must exist on the stack when they are created and that they are only valid
      • inside of the HandleScope active during their creation. For passing a local
      • handle to an outer HandleScope, an EscapableHandleScope and its Escape()
      • method must be used. *
      • Persistent handles can be used when storing objects across several
      • independent operations and have to be explicitly deallocated when they’re no
      • longer used. *
      • It is safe to extract the object stored in the handle by dereferencing the
      • handle (for instance, to extract the Object* from a Local); the value
      • will still be governed by a handle behind the scenes and the same rules apply
      • to these values as to their handles. */
      • 从 v8 返回的所有对象都必须由垃圾收集器跟踪,因此它知道这些对象还活着。 也因为垃圾收集器可能会移动对象,直接指向一个对象是不安全的。 相反,所有对象都存储在垃圾收集器所知道的句柄中,并在对象移动时更新句柄。Handles应该总是 按值传递(地址就是值)(除了像out-parameters这样的情况),他们不应该在堆上分配。

        有两种类型的句柄:local(局部)句柄和persistent(持久)句柄。

        局部句柄是轻量级和瞬态的,通常用于局部操作。它们由 HandleScopes 管理。 这意味着一个 HandleScope创建时必须存在于stack中, 并且它们仅在创建期间处于活动状态的 HandleScope 内有效。 要将本地句柄传递给外部 HandleScope,必须使用 EscapableHandleScope 及其 Escape() 方法。

        当跨多个对象存储对象时,可以使用持久句柄独立操作,并且在不再使用时必须明确释放。

        通过取消引用句柄来提取存储在句柄中的对象是安全的(例如,从 Local 中提取 Object*); 该值仍将由幕后的句柄控制,并且相同的规则适用于这些值的句柄。

        1. > Note: We are storing the handle locations as key values in the hash map.
        2. > When inserting a new variable via Declare(), we rely on the fact that the handle location remains alive for the duration of that variable use. Because a Variable holding a handle with the same location exists this is ensured.
        3. >
        4. > 注意:我们将句柄位置存储为哈希映射中的键值。
        5. > 当通过 Declare() 插入新变量时,我们依赖于句柄位置在该变量使用期间保持活动的事实。 因为一个变量的存在总是持有相同位置的句柄,这是肯定的。
        6. **所以!所有从v8分配的object都是通过句柄(智能指针)控制!!**
        7. 这个是重载 == 操作符, 之后的判断。
        8. ```javascript
        9. 检查两个句柄是否相同。
        10. 如果两者都为空,则返回 true,
        11. 或者它们所指的对象是否相同。
        12. 如果两个句柄都引用 JS 对象,则这与严格相等相同。
        13. 对于基元,例如数字或字符串,`false` 返回值并不表示这些值在 JavaScript 意义上不相等。
        14. 使用 `Value::StrictEquals()` 来检查原语是否相等。

        image.png
        然后从v8/src/api/api.cc(有很多有意思的api接口,typeof、DefineOwnProperty、。。) 跳到 v8/src/objects/objects.cc,有关于===的判断

        1. //v8/src/api/api.cc
        2. bool Value::StrictEquals(Local<Value> that) const {
        3. auto self = Utils::OpenHandle(this);
        4. auto other = Utils::OpenHandle(*that);
        5. return self->StrictEquals(*other);
        6. }
        7. //v8/src/objects/objects.cc
        8. bool Object::StrictEquals(Object that) {
        9. if (this->IsNumber()) {
        10. if (!that.IsNumber()) return false;
        11. return StrictNumberEquals(*this, that);
        12. } else if (this->IsString()) {
        13. if (!that.IsString()) return false;
        14. return String::cast(*this).Equals(String::cast(that));
        15. } else if (this->IsBigInt()) {
        16. if (!that.IsBigInt()) return false;
        17. return BigInt::EqualToBigInt(BigInt::cast(*this), BigInt::cast(that));
        18. }
        19. return *this == that;
        20. }

        大家难道没发现:
        image.png即便是我们所谓的 “原始值类型”(Primitive)
        其实本质上也是对应的基类经过一系列操作返回给我们的值而已. (我这好像说的有问题,有待考察源码。。)

        1. v8/src/objects/fixed-array.h

        kMaxLength是FixedArray的最大长度

        1. v8/include/v8-context.h

        v8对于js上下文的宏观操作定义

        1. v8/src/objects/contexts.h

        这里面有个注释就是说 Context必须由image.png

        1. JSFunctions are pairs (context, function code), sometimes also called
        2. closures. A Context object is used to represent function contexts and
        3. dynamically pushed 'with' contexts (or 'scopes' in ECMA-262 speak).
        4. At runtime, the contexts build a stack in parallel to the execution
        5. stack, with the top-most context being the current context. All contexts
        6. have the following slots:
        7. [ scope_info ] This is the scope info describing the current context. It
        8. contains the names of statically allocated context slots,
        9. and stack-allocated locals. The names are needed for
        10. dynamic lookups in the presence of 'with' or 'eval', and
        11. for the debugger.
        12. [ previous ] A pointer to the previous context.
        13. [ extension ] Additional data. This slot is only available when
        14. ScopeInfo::HasContextExtensionSlot returns true.
        15. For native contexts, it contains the global object.
        16. For module contexts, it contains the module object.
        17. For await contexts, it contains the generator object.
        18. For var block contexts, it may contain an "extension
        19. object".
        20. For with contexts, it contains an "extension object".
        21. An "extension object" is used to dynamically extend a
        22. context with additional variables, namely in the
        23. implementation of the 'with' construct and the 'eval'
        24. construct. For instance, Context::Lookup also searches
        25. the extension object for properties. (Storing the
        26. extension object is the original purpose of this context
        27. slot, hence the name.)
        28. In addition, function contexts with sloppy eval may have statically
        29. allocated context slots to store local variables/functions that are accessed
        30. from inner functions (via static context addresses) or through 'eval'
        31. (dynamic context lookups).
        32. The native context contains additional slots for fast access to native
        33. properties.
        34. Finally, with Harmony scoping, the JSFunction representing a top level
        35. script will have the ScriptContext rather than a FunctionContext.
        36. Script contexts from all top-level scripts are gathered in
        37. ScriptContextTable.

        给我仔细读20遍!!最好是看英文的!!

        1. JSFunction 是成对的(上下文、函数代码),有时也称为闭包(注意这里不要狭隘了,
        2. JSFunction和这个closures不是单纯指大家理解的js函数和闭包,这个JSFunction还包含了很多字节码、
        3. 优化代码的相关操作等)。
        4. Context 对象常常用于表示函数上下文和动态推送“with”上下文(或 ECMA-262 中的“scope”)。
        5. 在运行时,上下文构建一个与执行栈并行的堆栈,最顶层的上下文是当前上下文。
        6. 所有上下文有以下插槽:
        7. [ scope_info ] 这是描述当前上下文的范围信息。
        8. 它包含静态分配的上下文槽的名称,(看到没有,context slotscopeinfo中!)
        9. 和栈分配的locals(加上s我认为就是因为有多个)。名称需要用于
        10. 存在“with”或“eval”时的动态查找,以及为了调试器。
        11. [ previous ] 指向前一个上下文的指针。
        12. [ extension ] 附加数据。此插槽仅在以下情况下可用
        13. ScopeInfo::HasContextExtensionSlot 返回 true
        14. 对于 native 上下文,它包含全局对象。
        15. 对于 module 上下文,它包含模块对象。
        16. 对于 await 上下文,它包含生成器对象。
        17. 对于 var 块上下文,它可能包含一个“extension object”。
        18. 对于 with 上下文,它包含一个“extension object”。
        19. extension object”用于动态扩展一个
        20. 带有附加变量的上下文,也就是在'with'结构和'eval'结构的实现中。
        21. 例如, Context::Lookup 也搜索扩展对象的属性。
        22. (存储扩展对象是这个上下文槽的原始目的,因此才有了这个名称。)
        23. 此外,带有草率的 eval 的函数上下文可能会静态地分配上下文槽,来存储要从内部函数(通过静态上下文地址)
        24. 或通过 'eval'(动态上下文查找)访问的局部变量/函数。
        25. native上下文包含用于快速访问native属性的附加插槽。
        26. 最后,在和睦的scope下,JSFunction 表示顶级脚本将具有 ScriptContext而不是 FunctionContext
        27. 来自所有顶级脚本的脚本上下文被收集在脚本上下文表 ScriptContextTable 中。

        上下文类型的定义:所以总共有10种上下文,isNativeContext是最初就定义好了的
        比起作用域类型多了 await, debugevaluate,的处理,但是少了 class 的处理。所以他俩并不是严格同时对应?
        但是一定有上下文一定会有对应的scope_info, 但是产生一种scope的时候,可以没有对应的context

        1. // Predicates for context types. IsNativeContext is already defined on Object.
        2. inline bool IsFunctionContext() const;
        3. inline bool IsCatchContext() const;
        4. inline bool IsWithContext() const;
        5. inline bool IsDebugEvaluateContext() const;
        6. inline bool IsAwaitContext() const;
        7. inline bool IsBlockContext() const;
        8. inline bool IsModuleContext() const;
        9. inline bool IsEvalContext() const;
        10. inline bool IsScriptContext() const;
        1. v8/src/heap/factory.cc

        包含各种上下文的外部实现(只有逻辑上的),具体的还是得看contexts.xxx, scope-infor.xxx

        1. Handle<Context> Factory::NewFunctionContext(Handle<Context> outer,
        2. Handle<ScopeInfo> scope_info) {
        3. Handle<Map> map;
        4. switch (scope_info->scope_type()) {
        5. case EVAL_SCOPE:
        6. map = isolate()->eval_context_map();
        7. break;
        8. case FUNCTION_SCOPE:
        9. map = isolate()->function_context_map();
        10. break;
        11. default:
        12. UNREACHABLE();
        13. }
        14. int variadic_part_length = scope_info->ContextLength();
        15. Context context =
        16. NewContextInternal(map, Context::SizeFor(variadic_part_length),
        17. variadic_part_length, AllocationType::kYoung);
        18. DisallowGarbageCollection no_gc;
        19. context.set_scope_info(*scope_info);
        20. context.set_previous(*outer);
        21. return handle(context, isolate());
        22. }

        NewFunctionContext 就是函数执行上下文

        1. v8/src/objects/scope-info.tq/.h/.cc/.xxxx

        这个 slot 有时要理解为:为对应 变量 所开辟的一个槽
        好好看看 ScopeInfo 类

        1. // scope-info.h
        2. // ScopeInfo represents information about different scopes of a source
        3. // program and the allocation of the scope's variables. Scope information
        4. // is stored in a compressed form in ScopeInfo objects and is used
        5. // at runtime (stack dumps, deoptimization, etc.).
        6. // This object provides quick access to scope info details for runtime
        7. // routines.
        8. class ScopeInfo : public TorqueGeneratedScopeInfo<ScopeInfo, HeapObject> {
        9. public:
        10. DEFINE_TORQUE_GENERATED_SCOPE_FLAGS()
        11. DECL_PRINTER(ScopeInfo)
        12. class BodyDescriptor;
        13. // Return the type of this scope.
        14. ScopeType scope_type() const;
        15. // Return the language mode of this scope.
        16. LanguageMode language_mode() const;
        17. // True if this scope is a (var) declaration scope.
        18. bool is_declaration_scope() const;
        19. // Does this scope make a sloppy eval call?
        20. bool SloppyEvalCanExtendVars() const;
        21. // Return the number of context slots for code if a context is allocated. This
        22. // number consists of three parts:
        23. // 1. Size of header for every context.
        24. // 2. One context slot per context allocated local.
        25. // 3. One context slot for the function name if it is context allocated.
        26. // Parameters allocated in the context count as context allocated locals. If
        27. // no contexts are allocated for this scope ContextLength returns 0.
        28. int ContextLength() const;
        29. int ContextHeaderLength() const;
        30. bool HasContextExtensionSlot() const;
        31. // Does this scope declare a "this" binding?
        32. bool HasReceiver() const;
        33. // Does this scope declare a "this" binding, and the "this" binding is stack-
        34. // or context-allocated?
        35. bool HasAllocatedReceiver() const;
        36. // Does this scope has class brand (for private methods)?
        37. bool HasClassBrand() const;
        38. // Does this scope contain a saved class variable context local slot index
        39. // for checking receivers of static private methods?
        40. bool HasSavedClassVariableIndex() const;
        41. // Does this scope declare a "new.target" binding?
        42. bool HasNewTarget() const;
        43. // Is this scope the scope of a named function expression?
        44. V8_EXPORT_PRIVATE bool HasFunctionName() const;
        45. bool HasContextAllocatedFunctionName() const;
        46. // See SharedFunctionInfo::HasSharedName.
        47. V8_EXPORT_PRIVATE bool HasSharedFunctionName() const;
        48. V8_EXPORT_PRIVATE bool HasInferredFunctionName() const;
        49. void SetFunctionName(Object name);
        50. void SetInferredFunctionName(String name);
        51. // Does this scope belong to a function?
        52. bool HasPositionInfo() const;
        53. // Return if contexts are allocated for this scope.
        54. bool HasContext() const;
        55. // Return if this is a function scope with "use asm".
        56. inline bool IsAsmModule() const;
        57. inline bool HasSimpleParameters() const;
        58. // Return the function_name if present.
        59. V8_EXPORT_PRIVATE Object FunctionName() const;
        60. // The function's name if it is non-empty, otherwise the inferred name or an
        61. // empty string.
        62. String FunctionDebugName() const;
        63. // Return the function's inferred name if present.
        64. // See SharedFunctionInfo::function_identifier.
        65. V8_EXPORT_PRIVATE Object InferredFunctionName() const;
        66. // Position information accessors.
        67. int StartPosition() const;
        68. int EndPosition() const;
        69. void SetPositionInfo(int start, int end);
        70. SourceTextModuleInfo ModuleDescriptorInfo() const;
        71. // Return the name of the given context local.
        72. String ContextLocalName(int var) const;
        73. // Return the mode of the given context local.
        74. VariableMode ContextLocalMode(int var) const;
        75. // Return whether the given context local variable is static.
        76. IsStaticFlag ContextLocalIsStaticFlag(int var) const;
        77. // Return the initialization flag of the given context local.
        78. InitializationFlag ContextLocalInitFlag(int var) const;
        79. bool ContextLocalIsParameter(int var) const;
        80. uint32_t ContextLocalParameterNumber(int var) const;
        81. // Return the initialization flag of the given context local.
        82. MaybeAssignedFlag ContextLocalMaybeAssignedFlag(int var) const;
        83. // Return true if this local was introduced by the compiler, and should not be
        84. // exposed to the user in a debugger.
        85. static bool VariableIsSynthetic(String name);
        86. // Lookup support for serialized scope info. Returns the local context slot
        87. // index for a given slot name if the slot is present; otherwise
        88. // returns a value < 0. The name must be an internalized string.
        89. // If the slot is present and mode != nullptr, sets *mode to the corresponding
        90. // mode for that variable.
        91. // 你看这都说了最后是为变量服务的,所以前面那个 the local context slot index指的应该是
        92. // 这个变量在该局部上下文中对应的槽的索引
        93. static int ContextSlotIndex(ScopeInfo scope_info, String name,
        94. VariableLookupResult* lookup_result);
        95. // Lookup metadata of a MODULE-allocated variable. Return 0 if there is no
        96. // module variable with the given name (the index value of a MODULE variable
        97. // is never 0).
        98. int ModuleIndex(String name, VariableMode* mode,
        99. InitializationFlag* init_flag,
        100. MaybeAssignedFlag* maybe_assigned_flag);
        101. int ModuleVariableCount() const;
        102. // Lookup support for serialized scope info. Returns the function context
        103. // slot index if the function name is present and context-allocated (named
        104. // function expressions, only), otherwise returns a value < 0. The name
        105. // must be an internalized string.
        106. int FunctionContextSlotIndex(String name) const;
        107. // Lookup support for serialized scope info. Returns the receiver context
        108. // slot index if scope has a "this" binding, and the binding is
        109. // context-allocated. Otherwise returns a value < 0.
        110. int ReceiverContextSlotIndex() const;
        111. // Returns the first parameter context slot index.
        112. int ParametersStartIndex() const;
        113. // Lookup support for serialized scope info. Returns the index of the
        114. // saved class variable in context local slots if scope is a class scope
        115. // and it contains static private methods that may be accessed.
        116. // Otherwise returns a value < 0.
        117. int SavedClassVariableContextLocalIndex() const;
        118. FunctionKind function_kind() const;
        119. // Returns true if this ScopeInfo is linked to a outer ScopeInfo.
        120. bool HasOuterScopeInfo() const;
        121. // Returns true if this ScopeInfo was created for a debug-evaluate scope.
        122. bool IsDebugEvaluateScope() const;
        123. // Can be used to mark a ScopeInfo that looks like a with-scope as actually
        124. // being a debug-evaluate scope.
        125. void SetIsDebugEvaluateScope();
        126. // Return the outer ScopeInfo if present.
        127. ScopeInfo OuterScopeInfo() const;
        128. bool is_script_scope() const;
        129. // Returns true if this ScopeInfo has a blocklist attached containing stack
        130. // allocated local variables.
        131. V8_EXPORT_PRIVATE bool HasLocalsBlockList() const;
        132. // Returns a list of stack-allocated locals of parent scopes.
        133. // Used during local debug-evalute to decide whether a context lookup
        134. // can continue upwards after checking this scope.
        135. V8_EXPORT_PRIVATE StringSet LocalsBlockList() const;
        136. // Returns true if this ScopeInfo was created for a scope that skips the
        137. // closest outer class when resolving private names.
        138. bool PrivateNameLookupSkipsOuterClass() const;
        139. // REPL mode scopes allow re-declaraction of let and const variables. They
        140. // come from debug evaluate but are different to IsDebugEvaluateScope().
        141. bool IsReplModeScope() const;
        1. //v8/src/objects/scope-info.h 中,对于搜索变量所需要的字段
        2. struct VariableLookupResult {
        3. int context_index;//哪个上下文
        4. int slot_index;//哪个字段槽
        5. // repl_mode flag is needed to disable inlining of 'const' variables in REPL mode.
        6. bool is_repl_mode;//调试的时候涉及
        7. IsStaticFlag is_static_flag;//是否是static模式
        8. VariableMode mode;//变量模式:var/let/const/......
        9. InitializationFlag init_flag;//分为:创建时立即初始化、是否需要初始化
        10. MaybeAssignedFlag maybe_assigned_flag;//这个变量是否被分配了空间?如果初始化后,这就是true了
        11. };
        1. //True if this scope is a (var) declaration scope.
        2. bool is_declaration_scope() const;
        1. // Get the context where var declarations will be hoisted to, which
        2. // may be the context itself.
        3. Contextdeclaration_context() const;
        4. bool is_declaration_context() const;

        image.png

        https://tc39.es/ecma262/#sec-environment-records

        1. Environment Records can be thought of as existing in a simple object-oriented hierarchy where Environment Record is an abstract class with three concrete subclasses: declarative Environment Record, object Environment Record, and global Environment Record. Function Environment Records and module Environment Records are subclasses of declarative Environment Record.
        2. Environment Record (abstract)
        3. A declarative Environment Record is used to define the effect of ECMAScript language syntactic elements such as FunctionDeclarations, VariableDeclarations, and Catch clauses that directly associate identifier bindings with ECMAScript language values.
        4. A function Environment Record corresponds to the invocation of an ECMAScript function object, and contains bindings for the top-level declarations within that function. It may establish a new this binding. It also captures the state necessary to support super method invocations.
        5. A module Environment Record contains the bindings for the top-level declarations of a Module. It also contains the bindings that are explicitly imported by the Module. Its [[OuterEnv]] is a global Environment Record.
        6. An object Environment Record is used to define the effect of ECMAScript elements such as WithStatement that associate identifier bindings with the properties of some object.
        7. A global Environment Record is used for Script global declarations. It does not have an outer environment; its [[OuterEnv]] is null. It may be prepopulated with identifier bindings and it includes an associated global object whose properties provide some of the global environment's identifier bindings. As ECMAScript code is executed, additional properties may be added to the global object and the initial properties may be modified.
        8. The Environment Record abstract class includes the abstract specification methods defined in Table 20. These abstract methods have distinct concrete algorithms for each of the concrete subclasses.
        1. 可以认为环境记录存在于一个简单的面向对象层次结构中,其中环境记录是一个抽象类,具有三个具体的子类:声明性环境记录、对象环境记录和全局环境记录。函数环境记录和模块环境记录是声明性环境记录的子类。
        2. 环境记录(摘要)
        3. 声明性环境记录用于定义 ECMAScript 语言句法元素(例如 FunctionDeclarationsVariableDeclarations Catch 子句)的效果,这些元素将标识符绑定与 ECMAScript 语言值直接关联。
        4. 函数环境记录对应于 ECMAScript 函数对象的调用,并包含该函数内顶级声明的绑定。它可能会建立一个新的 this 绑定。它还捕获支持超级方法调用所需的状态。
        5. 模块环境记录包含模块顶级声明的绑定。它还包含由模块显式导入的绑定。它的 [[OuterEnv]] 是一个全局环境记录。
        6. 对象环境记录用于定义 ECMAScript 元素(例如 WithStatement)的效果,这些元素将标识符绑定与某些对象的属性相关联。
        7. 全局环境记录用于脚本全局声明。它没有外部环境;它的 [[OuterEnv]] 为空。它可以预先填充标识符绑定,并且它包括一个关联的全局对象,其属性提供一些全局环境的标识符绑定。在执行 ECMAScript 代码时,可能会向全局对象添加其他属性,并且可能会修改初始属性。
        8. Environment Record 抽象类包括表 20 中定义的抽象规范方法。这些抽象方法对于每个具体子类具有不同的具体算法。
        1. v8/src/objects/contexts.cc

        image.png

        1. v8/src/ast/scopes.h

        淦!!原来chromium根本就没有什么环境记录的说法,就是scope-info, previous, extension;
        找到最后才发现scope真正实现是在ast文件夹下,先看scopes.h再看.cc
        只有DeclarativeScope&ModuleScope&ClassScope三种, 他们的基类都是Scope!!
        好好读读:

        1. // Global invariants after AST construction: Each reference (i.e. identifier)
        2. // to a JavaScript variable (including global properties) is represented by a
        3. // VariableProxy node. Immediately after AST construction and before variable
        4. // allocation, most VariableProxy nodes are "unresolved", i.e. not bound to a
        5. // corresponding variable (though some are bound during parse time). Variable
        6. // allocation binds each unresolved VariableProxy to one Variable and assigns
        7. // a location. Note that many VariableProxy nodes may refer to the same Java-
        8. // Script variable.
        9. // JS environments are represented in the parser using Scope, DeclarationScope
        10. // and ModuleScope. DeclarationScope is used for any scope that hosts 'var'
        11. // declarations. This includes script, module, eval, varblock, and function
        12. // scope. ModuleScope further specializes DeclarationScope.
        1. AST 构造后有个全局不变的策略:
        2. JavaScript 变量(包括全局属性)的每个引用(即标识符)由 VariableProxy 节点表示。
        3. AST构造之后和变量分配到内存之前,大多数 VariableProxy 节点都“未解析”,
        4. 即未绑定到相应的变量(尽管有些在解析时绑定)。变量分配将每个未解析的 VariableProxy
        5. 绑定到一个变量并赋予一个位置。 请注意,许多 VariableProxy 节点可能引用相同的JavaScript变量
        6. (因为我们js代码里总在反复用同一个变量嘛,相应产生多个ast节点,所以肯定需要多个VariableProxy
        7. 因为ast是静态编译产物)。
        8. JS 环境在解析器中使用 ScopeDeclarationScope ModuleScope 表示。DeclarationScope用于
        9. 能够 真正承载/真正持有(品味下我对这个词的翻译,这是我结合源码总结出来的。比如:
        10. 虽然表面上你写了{var a;},但block并不能承载var)“var”声明的任何scope
        11. 这包括脚本、模块、evalvarblock(这个小东西。。) 和函数作用域(其实还有个类作用域,
        12. 但它是语法糖所以要单独的处理)。 ModuleScope 进一步特殊化了DeclarationScope
        1. Position in the source where this scope begins and ends.
        2. * For the scope of a with statement
        3. with (obj) stmt
        4. start position: start position of first token of 'stmt'
        5. end position: end position of last token of 'stmt'
        6. * For the scope of a block
        7. { stmts }
        8. start position: start position of '{'
        9. end position: end position of '}'
        10. * For the scope of a function literal or decalaration
        11. function fun(a,b) { stmts }
        12. start position: start position of '('
        13. end position: end position of '}'
        14. * For the scope of a catch block
        15. try { stms } catch(e) { stmts }
        16. start position: start position of '('
        17. end position: end position of ')'
        18. * For the scope of a for-statement
        19. for (let x ...) stmt
        20. start position: start position of '('
        21. end position: end position of last token of 'stmt'
        22. * For the scope of a switch statement
        23. switch (tag) { cases }
        24. start position: start position of '{'
        25. end position: end position of '}'
        1. // Resolve and fill in the allocation information for all variables
        2. // in this scopes. Must be called *after* all scopes have been
        3. // processed (parsed) to ensure that unresolved variables can be
        4. // resolved properly.
        5. //
        6. // In the case of code compiled and run using 'eval', the context
        7. // parameter is the context in which eval was called. In all other
        8. // cases the context parameter is an empty handle.
        9. //
        10. // Returns false if private names can not be resolved.
        11. bool AllocateVariables(ParseInfo* info);
        12. 解析并填写所有变量的分配信息
        13. 在这个范围内。 必须在*所有作用域都被调用之后*
        14. 处理(解析)以确保未解析的变量可以
        15. 妥善解决。
        16. 在使用“eval”编译和运行代码的情况下,上下文
        17. 参数是调用 eval 的上下文。 在所有其他
        18. 如果上下文参数是空句柄。
        19. 如果无法解析私有名称,则返回 false

        image.png

        1. v8/src/zone/zone.h ```javascript The Zone supports very fast allocation of small chunks of memory. The chunks cannot be deallocated individually, but instead the Zone supports deallocating all chunks in one fast operation. The Zone is used to hold temporary data structures like the abstract syntax tree, which is deallocated after compilation.

        Note: There is no need to initialize the Zone; the first time an allocation is attempted, a segment of memory will be requested through the allocator.

        Note: The implementation is inherently not thread safe. Do not use from multi-threaded code.

        1. ```javascript
        2. Zone 支持非常快速地分配小块
        3. 记忆。 块不能单独释放,而是
        4. Zone 支持一次性快速释放所有块
        5. 手术。 Zone 用于保存临时数据结构,例如
        6. 抽象语法树,编译后释放。
        7. 注意:不需要初始化Zone; 第一次
        8. 尝试分配,将通过分配器请求一段内存。
        9. 注意:该实现本质上不是线程安全的。 不要使用来自多线程代码。
        1. V8/src/common/globals.h 多读读,好多关键的定义/枚举都在这里 ```javascript //枚举默认自动从0递增 // The order of this enum has to be kept in sync with the predicates below. enum class VariableMode : uint8_t { // User declared variables: kLet, // declared via ‘let’ declarations (first lexical)

          kConst, // declared via ‘const’ declarations (last lexical)

          kVar, // declared via ‘var’, and ‘function’ declarations

          // Variables introduced by the compiler: kTemporary, // temporary variables (not user-visible), stack-allocated

          1. // unless the scope as a whole has forced context allocation

          kDynamic, // always require dynamic lookup (we don’t know

          1. // the declaration)

          kDynamicGlobal, // requires dynamic lookup, but we know that the

          1. // variable is global unless it has been shadowed
          2. // by an eval-introduced variable

          kDynamicLocal, // requires dynamic lookup, but we know that the

          1. // variable is local and where it is unless it
          2. // has been shadowed by an eval-introduced
          3. // variable

          // Variables for private methods or accessors whose access require // brand check. Declared only in class scopes by the compiler // and allocated only in class contexts: kPrivateMethod, // Does not coexist with any other variable with the same

          1. // name in the same scope.

          kPrivateSetterOnly, // Incompatible with variables with the same name but

          1. // any mode other than kPrivateGetterOnly. Transition to
          2. // kPrivateGetterAndSetter if a later declaration for the
          3. // same name with kPrivateGetterOnly is made.

          kPrivateGetterOnly, // Incompatible with variables with the same name but

          1. // any mode other than kPrivateSetterOnly. Transition to
          2. // kPrivateGetterAndSetter if a later declaration for the
          3. // same name with kPrivateSetterOnly is made.

          kPrivateGetterAndSetter, // Does not coexist with any other variable with the

          1. // same name in the same scope.

          kLastLexicalVariableMode = kConst, };

        inline bool IsDeclaredVariableMode(VariableMode mode) { STATIC_ASSERT(static_cast(VariableMode::kLet) == 0); // Implies that mode >= VariableMode::kLet. return mode <= VariableMode::kVar; } //所以说let/const/var都属于声明的变量模式, 只是后续会细分词法变量模式 inline bool IsConstVariableMode(VariableMode mode) { return mode == VariableMode::kConst || IsPrivateMethodOrAccessorVariableMode(mode); }

        inline bool IsLexicalVariableMode(VariableMode mode) { STATIC_ASSERT(static_cast(VariableMode::kLet) == 0); // Implies that mode >= VariableMode::kLet. return mode <= VariableMode::kLastLexicalVariableMode; }

        enum VariableLocation : uint8_t { // Before and during variable allocation, a variable whose location is // not yet determined. After allocation, a variable looked up as a // property on the global object (and possibly absent). name() is the // variable name, index() is invalid. UNALLOCATED,

        // A slot in the parameter section on the stack. index() is the // parameter index, counting left-to-right. The receiver is index -1; // the first parameter is index 0. PARAMETER,

        // A slot in the local section on the stack. index() is the variable // index in the stack frame, starting at 0. LOCAL,

        // An indexed slot in a heap context. index() is the variable index in // the context object on the heap, starting at 0. scope() is the // corresponding scope. CONTEXT,

        // A named slot in a heap context. name() is the variable name in the // context object on the heap, with lookup starting at the current // context. index() is invalid. LOOKUP,

        // A named slot in a module’s export table. MODULE,

        // An indexed slot in a script context. index() is the variable // index in the context object on the heap, starting at 0. // Important: REPL_GLOBAL variables from different scripts with the // same name share a single script context slot. Every // script context will reserve a slot, but only one will be used. // REPL_GLOBAL variables are stored in script contexts, but accessed like // globals, i.e. they always require a lookup at runtime to find the right // script context.

        REPL_GLOBAL,

        kLastVariableLocation = REPL_GLOBAL };

        // ES6 specifies declarative environment records with mutable and immutable // bindings that can be in two states: initialized and uninitialized. // When accessing a binding, it needs to be checked for initialization. // However in the following cases the binding is initialized immediately // after creation so the initialization check can always be skipped: // // 1. Var declared local variables. // var foo; // 2. A local variable introduced by a function declaration. // function foo() {} // 3. Parameters // function x(foo) {} // 4. Catch bound variables. // try {} catch (foo) {} // 6. Function name variables of named function expressions. // var x = function foo() {} // 7. Implicit binding of ‘this’. // 8. Implicit binding of ‘arguments’ in functions. // // The following enum specifies a flag that indicates if the binding needs a // distinct initialization step (kNeedsInitialization) or if the binding is // immediately initialized upon creation (kCreatedInitialized). enum InitializationFlag : uint8_t { kNeedsInitialization, kCreatedInitialized };

        1. ```javascript
        2. 脚本上下文中的索引槽。 index() 是堆上上下文对象中的变量索引,从 0 开始。
        3. 重要提示:来自不同脚本的 REPL_GLOBAL 变量
        4. 同名共享一个脚本上下文槽。每一个
        5. 脚本上下文将保留一个插槽,但只会使用一个。
        6. REPL_GLOBAL 变量存储在脚本上下文中,但像全局变量一样访问,即它们总是需要在运行时查找才能找到正确的脚本上下文。
        7. 主要看这段:
        8. ES6 指定了具有可变和不可变绑定的声明性环境记录,它们可以处于两种状态:已初始化和未初始化。
        9. 访问绑定时,需要检查它的初始化。
        10. 但是在以下情况下,绑定会立即初始化
        11. 创建后,始终可以跳过初始化检查:
        12. 1. var 声明局部变量。
        13. var foo;
        14. 2. 由函数声明引入的局部变量。
        15. function foo() {}
        16. 3. 函数参数
        17. function x(foo) {}
        18. 4. 捕获绑定变量。
        19. try {} catch (foo) {}
        20. 6. 命名函数表达式的函数名变量。
        21. var x = function foo() {}
        22. 7. 'this' 的隐式绑定。
        23. 8. 函数中“参数”的隐式绑定。
        24. 以下枚举指定了一个标志,指示绑定是否需要不同的初始化步骤 (kNeedsInitialization) 或绑定是否为
        25. 创建时立即初始化 (kCreatedInitialized)。

        这里定义了 作用域类型, 变量模式/类型,并有相关说明

        1. enum ScopeType : uint8_t {
        2. CLASS_SCOPE, // The scope introduced by a class.
        3. EVAL_SCOPE, // The top-level scope for an eval source.
        4. FUNCTION_SCOPE, // The top-level scope for a function.
        5. MODULE_SCOPE, // The scope introduced by a module literal
        6. SCRIPT_SCOPE, // The top-level scope for a script or a top-level eval.
        7. CATCH_SCOPE, // The scope introduced by catch.
        8. BLOCK_SCOPE, // The scope introduced by a new block.
        9. WITH_SCOPE // The scope introduced by with.
        10. //应该还有个闭包类型。或者说闭包不是单独的一种scope类型,因为闭包可以通过scope的outer往上找到
        11. };
        12. ......
        13. // The order of this enum has to be kept in sync with the predicates below.
        14. enum class VariableMode : uint8_t {
        15. // User declared variables:
        16. kLet, // declared via 'let' declarations (first lexical)
        17. kConst, // declared via 'const' declarations (last lexical)
        18. kVar, // declared via 'var', and 'function' declarations
        19. // Variables introduced by the compiler:
        20. kTemporary, // temporary variables (not user-visible), stack-allocated
        21. // unless the scope as a whole has forced context allocation
        22. kDynamic, // always require dynamic lookup (we don't know
        23. // the declaration)
        24. kDynamicGlobal, // requires dynamic lookup, but we know that the
        25. // variable is global unless it has been shadowed
        26. // by an eval-introduced variable
        27. kDynamicLocal, // requires dynamic lookup, but we know that the
        28. // variable is local and where it is unless it
        29. // has been shadowed by an eval-introduced
        30. // variable
        31. // Variables for private methods or accessors whose access require
        32. // brand check. Declared only in class scopes by the compiler
        33. // and allocated only in class contexts:
        34. kPrivateMethod, // Does not coexist with any other variable with the same
        35. // name in the same scope.
        36. kPrivateSetterOnly, // Incompatible with variables with the same name but
        37. // any mode other than kPrivateGetterOnly. Transition to
        38. // kPrivateGetterAndSetter if a later declaration for the
        39. // same name with kPrivateGetterOnly is made.
        40. kPrivateGetterOnly, // Incompatible with variables with the same name but
        41. // any mode other than kPrivateSetterOnly. Transition to
        42. // kPrivateGetterAndSetter if a later declaration for the
        43. // same name with kPrivateSetterOnly is made.
        44. kPrivateGetterAndSetter, // Does not coexist with any other variable with the
        45. // same name in the same scope.
        46. kLastLexicalVariableMode = kConst,
        47. };
        1. v8/src/runtime/runtime-scopes.cc
        1. v8/src/parsing/parser-base.h

        parser就是js解析器,

        1. template <typename Impl>
        2. void ParserBase<Impl>::ParseVariableDeclarations(
        3. VariableDeclarationContext var_context,
        4. DeclarationParsingResult* parsing_result,
        5. ZonePtrList<const AstRawString>* names) {
        6. // VariableDeclarations ::
        7. // ('var' | 'const' | 'let') (Identifier ('=' AssignmentExpression)?)+[',']
        8. //
        9. // ES6:
        10. // FIXME(marja, nikolaos): Add an up-to-date comment about ES6 variable
        11. // declaration syntax.
        12. DCHECK_NOT_NULL(parsing_result);
        13. parsing_result->descriptor.kind = NORMAL_VARIABLE;
        14. parsing_result->descriptor.declaration_pos = peek_position();
        15. parsing_result->descriptor.initialization_pos = peek_position();
        16. switch (peek()) {
        17. case Token::VAR:
        18. parsing_result->descriptor.mode = VariableMode::kVar;
        19. Consume(Token::VAR);
        20. break;
        21. case Token::CONST:
        22. Consume(Token::CONST);
        23. DCHECK_NE(var_context, kStatement);
        24. parsing_result->descriptor.mode = VariableMode::kConst;
        25. break;
        26. case Token::LET:
        27. Consume(Token::LET);
        28. DCHECK_NE(var_context, kStatement);
        29. parsing_result->descriptor.mode = VariableMode::kLet;
        30. break;
        31. default:
        32. UNREACHABLE(); // by current callers
        33. break;
        34. }
        35. ......
        36. }

        ps: 此处证明了,var所过之处(提升至函数作用域途径的作用域)也有了相同的声明
        毕竟我记得提升也是需要一层层找outer的啊,而var所过之处,都受折磨。所以:

        1. // Check if the scope has conflicting var/let declarations from different
        2. // scopes. This covers for example
        3. //
        4. // function f() { { { var x; } let x; } }
        5. // function g() { { var x; let x; } }
        6. //
        7. // The var declarations are hoisted to the function scope, but originate from
        8. // a scope where the name has also been let bound or the var declaration is
        9. // hoisted over such a scope.
        10. // var 声明会被提升到函数作用域,但他的根源作用域里也有let绑定的同名变量
        11. // 或者说 var 声明被提升会经过这样一个作用域。
        12. void CheckConflictingVarDeclarations(DeclarationScope* scope) {
        13. if (has_error()) return;
        14. bool allowed_catch_binding_var_redeclaration = false;
        15. Declaration* decl = scope->CheckConflictingVarDeclarations(
        16. &allowed_catch_binding_var_redeclaration);
        17. if (allowed_catch_binding_var_redeclaration) {
        18. impl()->CountUsage(v8::Isolate::kVarRedeclaredCatchBinding);
        19. }
        20. if (decl != nullptr) {
        21. // In ES6, conflicting variable bindings are early errors.
        22. const AstRawString* name = decl->var()->raw_name();
        23. int position = decl->position();
        24. Scanner::Location location =
        25. position == kNoSourcePosition
        26. ? Scanner::Location::invalid()
        27. : Scanner::Location(position, position + 1);
        28. impl()->ReportMessageAt(location, MessageTemplate::kVarRedeclaration,
        29. name);
        30. }
        31. }
        1. // The parser base object implements pure parsing, according to the
        2. // language grammar. Different parser implementations may exhibit
        3. // different parser-driven behavior that is not considered as pure
        4. // parsing, e.g., early error detection and reporting, AST generation, etc.
        5. // The ParserTypes structure encapsulates the differences in the
        6. // types used in parsing methods. E.g., Parser methods use Expression*
        7. // and PreParser methods use PreParserExpression. For any given parser
        8. // implementation class Impl, it is expected to contain the following typedefs:
        9. //
        10. // template <>
        11. // struct ParserTypes<Impl> {
        12. // // Synonyms for ParserBase<Impl> and Impl, respectively.
        13. // typedef Base;
        14. // typedef Impl;
        15. // // Return types for traversing functions.
        16. // typedef Identifier;
        17. // typedef Expression;
        18. // typedef FunctionLiteral;
        19. // typedef ObjectLiteralProperty;
        20. // typedef ClassLiteralProperty;
        21. // typedef ExpressionList;
        22. // typedef ObjectPropertyList;
        23. // typedef ClassPropertyList;
        24. // typedef FormalParameters;
        25. // typedef Statement;
        26. // typedef StatementList;
        27. // typedef Block;
        28. // typedef BreakableStatement;
        29. // typedef ForStatement;
        30. // typedef IterationStatement;
        31. // // For constructing objects returned by the traversing functions.
        32. // typedef Factory;
        33. // // For other implementation-specific tasks.
        34. // typedef Target;
        35. // typedef TargetScope;
        36. // };
        1. v8/src/parsing/parser.cc

        读下这个函数: DesugarLexicalBindingsInForStatement 解开For语句中绑定的词发环境的语法糖?

        1. ES6 13.7.4.8 specifies that on each loop iteration the let variables are
        2. copied into a new environment. Moreover, the "next" statement must be
        3. evaluated not in the environment of the just completed iteration but in
        4. that of the upcoming one. We achieve this with the following desugaring.
        5. Extra care is needed to preserve the completion value of the original loop.
        6. We are given a for statement of the form
        7. labels: for (let/const x = i; cond; next) body
        8. and rewrite it as follows. Here we write {{ ... }} for init-blocks, ie.,
        9. blocks whose ignore_completion_value_ flag is set.
        10. ES6 13.7.4.8 指定在每次循环迭代中 let 变量是
        11. 复制到新环境中。 此外,“下一个”语句必须不是在刚刚完成的迭代的环境中进行评估,而是在即将进行的迭代的环境中进行评估。 我们通过以下脱糖来实现这一点。
        12. 需要特别注意保留原始循环的完成值。
        13. 我们得到了一份表格的声明
        14. 标签: for (let/const x = i; cond; next) body
        15. 并改写如下。 这里我们为初始化块写 {{ ... }},即
        16. 设置了 ignore_completion_value_ 标志的块。
        17. {
        18. let/const x = i;
        19. temp_x = x;
        20. first = 1;
        21. undefined;
        22. outer: for (;;) {
        23. let/const x = temp_x;
        24. {{ if (first == 1) {
        25. first = 0;
        26. } else {
        27. next;
        28. }
        29. flag = 1;
        30. if (!cond) break;
        31. }}
        32. labels: for (; flag == 1; flag = 0, temp_x = x) {
        33. body
        34. }
        35. {{ if (flag == 1) // Body used break.
        36. break;
        37. }}
        38. }
        39. }
        40. //在这个源码上面一点点位置
        41. // Rewrite a for-in/of statement of the form
        42. //
        43. // for (let/const/var x in/of e) b
        44. //
        45. // into
        46. //
        47. // {
        48. // var temp;
        49. // for (temp in/of e) {
        50. // let/const/var x = temp;
        51. // b;
        52. // }
        53. // let x; // for TDZ
        54. // }
        1. ast ```cpp 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’.

        查找由名称给出的变量引用,从此范围开始,并在到达 external_scope_end 范围时停止。 如果由于调用了“eval”而执行了代码,则上下文参数应设置为“eval”的调用上下文。


        1. 14. 忘了是哪两个文件下了
        2. ```javascript
        3. Contains the names of local variables and parameters that are allocated in the context. They are stored in increasing order of the context slot index starting with Context::MIN_CONTEXT_SLOTS.
        4. List of stack allocated local variables. Used by debug evaluate to properly abort variable lookup when a name clashes with a stack allocated local that can't be materialized.
        5. Mark contexts slots with the parameter number they represent. We walk the list of parameters. That can include duplicate entries if a
        6. parameter name is repeated. By walking upwards, we'll automatically
        7. mark the context slot with the highest parameter number that uses this variable. That will be the parameter number that is represented by the context slot. All lower parameters will only be available on the stack through the arguments object.
        8. 包含在上下文中分配的局部变量和参数的名称。 它们以上下文槽索引的递增顺序存储,从 Context::MIN_CONTEXT_SLOTS 开始。
        9. 堆栈分配的局部变量列表。 当名称与无法实现的本地分配堆栈发生冲突时,由调试评估使用以正确中止变量查找。
        10. 用它们代表的参数编号标记上下文插槽。 我们遍历参数列表。 这可能包括重复的条目,如果
        11. 参数名称重复。 通过向上走,我们会自动
        12. 使用使用此变量的最高参数编号标记上下文槽。 这将是由上下文槽表示的参数编号。 所有较低的参数只能通过参数对象在堆栈上使用。
        1. scopes.cc

        这个就是检查var变量声明是否有冲突(会经过上层作用域,直到到达第一个非eval的声明式作用域)
        其实let/const都算作var变量声明,只是有另外一个属性mode控制他们是var还是let还是const

        1. Declaration* DeclarationScope::CheckConflictingVarDeclarations(
        2. bool* allowed_catch_binding_var_redeclaration) {
        3. if (has_checked_syntax_) return nullptr;
        4. for (Declaration* decl : decls_) {
        5. // Lexical vs lexical conflicts within the same scope have already been
        6. // captured in Parser::Declare. The only conflicts we still need to check
        7. // are lexical vs nested var.
        8. if (decl->IsVariableDeclaration() &&
        9. decl->AsVariableDeclaration()->AsNested() != nullptr) {
        10. Scope* current = decl->AsVariableDeclaration()->AsNested()->scope();
        11. DCHECK(decl->var()->mode() == VariableMode::kVar ||
        12. decl->var()->mode() == VariableMode::kDynamic);
        13. // Iterate through all scopes until the declaration scope.
        14. do {
        15. // There is a conflict if there exists a non-VAR binding.
        16. Variable* other_var = current->LookupLocal(decl->var()->raw_name());
        17. if (current->is_catch_scope()) {
        18. *allowed_catch_binding_var_redeclaration |= other_var != nullptr;
        19. current = current->outer_scope();
        20. continue;
        21. }
        22. if (other_var != nullptr) {
        23. DCHECK(IsLexicalVariableMode(other_var->mode()));
        24. return decl;
        25. }
        26. current = current->outer_scope();
        27. } while (current != this);
        28. }
        29. }
        30. if (V8_LIKELY(!is_eval_scope())) return nullptr;
        31. if (!is_sloppy(language_mode())) return nullptr;
        32. // Var declarations in sloppy eval are hoisted to the first non-eval
        33. // declaration scope. Check for conflicts between the eval scope that
        34. // declaration scope.
        35. Scope* end = outer_scope()->GetNonEvalDeclarationScope()->outer_scope();
        36. for (Declaration* decl : decls_) {
        37. if (IsLexicalVariableMode(decl->var()->mode())) continue;
        38. Scope* current = outer_scope_;
        39. // Iterate through all scopes until and including the declaration scope.
        40. do {
        41. // There is a conflict if there exists a non-VAR binding up to the
        42. // declaration scope in which this sloppy-eval runs.
        43. //
        44. // Use the current scope as the cache, since the general cache would be
        45. // the end scope.
        46. Variable* other_var =
        47. current->LookupInScopeOrScopeInfo(decl->var()->raw_name(), current);
        48. if (other_var != nullptr && !current->is_catch_scope()) {
        49. // If this is a VAR, then we know that it doesn't conflict with
        50. // anything, so we can't conflict with anything either. The one
        51. // exception is the binding variable in catch scopes, which is handled
        52. // by the if above.
        53. if (!IsLexicalVariableMode(other_var->mode())) break;
        54. return decl;
        55. }
        56. current = current->outer_scope();
        57. } while (current != end);
        58. }
        59. return nullptr;
        60. }
        1. v8/src/common/variables.h

          1. static InitializationFlag DefaultInitializationFlag(VariableMode mode) {
          2. DCHECK(IsDeclaredVariableMode(mode));
          3. return mode == VariableMode::kVar ? kCreatedInitialized
          4. : kNeedsInitialization;
          5. }
        2. v8/src/objects/scope-info.cc

        这就是查找变量,顺便把结果都通过指针修改到lookup_result了,这样就不需要返回这些变量的信息了。
        只需要在外部定义一个VariableLookupResult lookup_result; 然后 &lookup_result作为最后一个参数即可!

        1. // static
        2. int ScopeInfo::ContextSlotIndex(ScopeInfo scope_info, String name,
        3. VariableLookupResult* lookup_result) {
        4. DisallowGarbageCollection no_gc;
        5. DCHECK(name.IsInternalizedString());
        6. DCHECK_NOT_NULL(lookup_result);
        7. if (scope_info.IsEmpty()) return -1;
        8. int context_local_count = scope_info.context_local_count();
        9. for (int var = 0; var < context_local_count; ++var) {
        10. if (name != scope_info.context_local_names(var)) {
        11. continue;
        12. }
        13. lookup_result->mode = scope_info.ContextLocalMode(var);
        14. lookup_result->is_static_flag = scope_info.ContextLocalIsStaticFlag(var);
        15. lookup_result->init_flag = scope_info.ContextLocalInitFlag(var);
        16. lookup_result->maybe_assigned_flag =
        17. scope_info.ContextLocalMaybeAssignedFlag(var);
        18. lookup_result->is_repl_mode = scope_info.IsReplModeScope();
        19. int result = scope_info.ContextHeaderLength() + var;
        20. DCHECK_LT(result, scope_info.ContextLength());
        21. return result;
        22. }
        23. return -1;
        24. }