Understanding Chrome V8 - Chapter 24: How does V8 Describe Your JavaScript Function

Written by huidou | Published 2022/10/25
Tech Story Tags: javascript-development | understanding-chrome-v8 | nodejs | functional-programming | programming | tutorial | guide | beginners-guide

TLDR"Let's Understand Chrome V8" are serial technology articles that explain the V8 code, it covers many V8 kernel functions and fundamentals.via the TL;DR App

Welcome to other chapters of Let’s Understand Chrome V8

When we learn V8, the SharedFunction and JSFunction are two very common and important terminologies that may confuse beginners. So what are they? What is the difference between them? In general, the main role of the ShardFunction is the place that lays the bytecodes, there is other stuff like the trampoline which I’ll talk about in the future. “Shared”, you can roughly think of the SharedFunction as a DLL library that can be called in anywhere. The compiler compiles a JavaScript function to a SharedFunction, and the SharedFunction can be used anywhere, like a DLL. This is the reason its name is Shared-function.

Roughly, a JSFunction equals a SharedFunction + a context. When V8 is going to call a SharedFunction, V8 needs to bind a context to the ShareFunction, the bind is a term of V8 which means it binds a SharedFuncton and a context together, namely a JSFunction. A JSFunction is an executable instance that can be executed by V8 interpreter ignition.

1. SharedFunction

The following code is SharedFucntion class.

1.  // SharedFunctionInfo describes the JSFunction information that can be
2.  // shared by multiple instances of the function.
3.  class SharedFunctionInfo : public HeapObject {
4.  public:
5.  V8_EXPORT_PRIVATE Code GetCode() const;
6.  V8_EXPORT_PRIVATE static void SetScript(
7.      Handle<SharedFunctionInfo> shared, Handle<Object> script_object,
8.       int function_literal_id, bool reset_preparsed_scope_data = true);
9.   V8_EXPORT_PRIVATE int EndPosition() const;
10.    V8_EXPORT_PRIVATE int StartPosition() const;
11.    V8_EXPORT_PRIVATE void SetPosition(int start_position, int end_position);
12.    inline bool IsApiFunction() const;
13.    inline bool is_class_constructor() const;
14.    inline FunctionTemplateInfo get_api_func_data();
15.    inline void set_api_func_data(FunctionTemplateInfo data);
16.    inline bool HasBytecodeArray() const;
17.    inline BytecodeArray GetBytecodeArray() const;
18.    inline void set_bytecode_array(BytecodeArray bytecode);
19.    inline Code InterpreterTrampoline() const;
20.    inline bool HasInterpreterData() const;
21.    inline InterpreterData interpreter_data() const;
22.    inline void set_interpreter_data(InterpreterData interpreter_data);
23.    // builtin_id corresponds to the auto-generated Builtins::Name id.
24.    inline bool HasBuiltinId() const;
25.    inline int builtin_id() const;
26.    inline void set_builtin_id(int builtin_id);
27.    inline bool HasUncompiledData() const;
28.    inline UncompiledData uncompiled_data() const;
29.    inline void set_uncompiled_data(UncompiledData data);
30.    inline bool HasUncompiledDataWithPreparseData() const;
31.    inline UncompiledDataWithPreparseData uncompiled_data_with_preparse_data()
32.        const;
33.    inline void set_uncompiled_data_with_preparse_data(
34.        UncompiledDataWithPreparseData data);
35.    inline bool HasUncompiledDataWithoutPreparseData() const;
36.    inline LanguageMode language_mode() const;
37.    inline void set_language_mode(LanguageMode language_mode);
38.    DECL_PRIMITIVE_ACCESSORS(syntax_kind, FunctionSyntaxKind)
39.    inline bool is_wrapped() const;
40.    DECL_BOOLEAN_ACCESSORS(has_duplicate_parameters)
41.    DECL_BOOLEAN_ACCESSORS(native)
42.    DECL_BOOLEAN_ACCESSORS(is_asm_wasm_broken)
43.    DECL_BOOLEAN_ACCESSORS(name_should_print_as_anonymous)
44.    DECL_BOOLEAN_ACCESSORS(is_oneshot_iife)
45.    DECL_BOOLEAN_ACCESSORS(are_properties_final)
46.    DECL_BOOLEAN_ACCESSORS(is_safe_to_skip_arguments_adaptor)
47.    DECL_BOOLEAN_ACCESSORS(has_reported_binary_coverage)
48.    DECL_BOOLEAN_ACCESSORS(private_name_lookup_skips_outer_class)
49.    inline FunctionKind kind() const;
50.    DECL_INT_ACCESSORS(function_map_index)
51.    inline void clear_padding();
52.    inline void UpdateFunctionMapIndex();
53.    inline bool optimization_disabled() const;
54.    inline BailoutReason disable_optimization_reason() const;
55.    void DisableOptimization(BailoutReason reason);
56.    DECL_BOOLEAN_ACCESSORS(requires_instance_members_initializer)
57.    bool HasSourceCode() const;
58.    static Handle<Object> GetSourceCode(Handle<SharedFunctionInfo> shared);
59.    static Handle<Object> GetSourceCodeHarmony(Handle<SharedFunctionInfo> shared);
60.    inline bool IsSubjectToDebugging();
61.    inline bool IsUserJavaScript();
62.    inline bool CanDiscardCompiled() const;
63.    void UpdateExpectedNofPropertiesFromEstimate(FunctionLiteral* literal);
64.    void UpdateAndFinalizeExpectedNofPropertiesFromEstimate(
65.        FunctionLiteral* literal);
66.    DECL_CAST(SharedFunctionInfo)
67.    // Constants.
68.    static const uint16_t kDontAdaptArgumentsSentinel = static_cast<uint16_t>(-1);
69.    static const int kMaximumFunctionTokenOffset = kMaxUInt16 - 1;
70.    static const uint16_t kFunctionTokenOutOfRange = static_cast<uint16_t>(-1);
71.    STATIC_ASSERT(kMaximumFunctionTokenOffset + 1 == kFunctionTokenOutOfRange);
72.    DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize,
73.                                  TORQUE_GENERATED_SHARED_FUNCTION_INFO_FIELDS)
74.//............omit..............
75.    };

Lines 1–2 comment that a SharedFunction can be shared by multiple instances.

Here, I give you some important members of a SharedFunction.

  • Line 5, the GetCode() returns code, I would shed some more light on that the code is a Builtin code but not your JavaScript source code, the Builtin code is used to construct the function prologue for interpreter ignition. Before calling a function, V8 needs to construct a call stack and get the first instruction address, for which all of this is responsible by the Builtin code;
  • Line 6, SetScript() sets JavaScript source code into the SharedFunction;
  • Lines 9–11, they set offset that describes the corresponding position between bytecode and JavaScript source code;
  • Lines 12–15, they describe if the SharedFunction is an API function;
  • Lines 16–18, they get and set bytecode array for a SharedFunction;
  • Line 19, it is the InterpreterTrampoline address that will be talked about in the future;
  • Lines 27–35, they are the optimization reason for TurboFan. The following list is the optimization reason.

#define BAILOUT_MESSAGES_LIST(V)                                            \
  V(kNoReason, "no reason")                                                 \
                                                                            \
  V(kBailedOutDueToDependencyChange, "Bailed out due to dependency change") \
  V(kCodeGenerationFailed, "Code generation failed")                        \
  V(kCyclicObjectStateDetectedInEscapeAnalysis,                             \
    "Cyclic object state detected by escape analysis")                      \
  V(kFunctionBeingDebugged, "Function is being debugged")                   \
  V(kGraphBuildingFailed, "Optimized graph construction failed")            \
  V(kFunctionTooBig, "Function is too big to be optimized")                 \
  V(kLiveEdit, "LiveEdit")                                                  \
  V(kNativeFunctionLiteral, "Native function literal")                      \
  V(kNotEnoughVirtualRegistersRegalloc,                                     \
    "Not enough virtual registers (regalloc)")                              \
  V(kOptimizationDisabled, "Optimization disabled")                         \
  V(kNeverOptimize, "Optimization is always disabled")
  • Lines 36–37, set the mode as strict or sloppy;
  • Lines 40–48, the macro DECL_BOOLEAN_ACCESSORS’ definition is below:
#define DECL_PRIMITIVE_ACCESSORS(name, type) \
  inline type name() const;                  \
  inline void set_##name(type value);

#define DECL_BOOLEAN_ACCESSORS(name) DECL_PRIMITIVE_ACCESSORS(name, bool)
  • Line 72, it defines the SharedFunction’s memory layout, the following is the source code and Figure 1 shows the memory layout:
#define DEFINE_FIELD_OFFSET_CONSTANTS(StartOffset, LIST_MACRO) \
  enum {                                                       \
    LIST_MACRO##_StartOffset = StartOffset - 1,                \
    LIST_MACRO(DEFINE_ONE_FIELD_OFFSET)                        \
  };
//==================omit===========================
#define TORQUE_GENERATED_SHARED_FUNCTION_INFO_FIELDS(V) \
V(kStartOfWeakFieldsOffset, 0) \
V(kFunctionDataOffset, kTaggedSize) \
V(kEndOfWeakFieldsOffset, 0) \
V(kStartOfStrongFieldsOffset, 0) \
V(kNameOrScopeInfoOffset, kTaggedSize) \
V(kOuterScopeInfoOrFeedbackMetadataOffset, kTaggedSize) \
V(kScriptOrDebugInfoOffset, kTaggedSize) \
V(kEndOfStrongFieldsOffset, 0) \
V(kLengthOffset, kUInt16Size) \
V(kFormalParameterCountOffset, kUInt16Size) \
V(kExpectedNofPropertiesOffset, kUInt16Size) \
V(kFunctionTokenOffsetOffset, kUInt16Size) \
V(kFlagsOffset, kInt32Size) \
V(kFunctionLiteralIdOffset, kInt32Size) \
V(kUniqueIdOffset, kInt32Size) \
V(kSize, 0) \
//==============omit====================
#define FLAGS_BIT_FIELDS(V, _)//see line 70 above

2. JSFunction

The function NewFunctionFromSharedFunctionInfo reads a SharedFunction and generates the corresponding JSFunction. The following is the source code:

1.  Handle<JSFunction> Factory::NewFunctionFromSharedFunctionInfo(
2.      Handle<SharedFunctionInfo> info, Handle<Context> context,
3.      AllocationType allocation) {
4.    Handle<Map> initial_map(
5.        Map::cast(context->native_context().get(info->function_map_index())),
6.        isolate());
7.    return NewFunctionFromSharedFunctionInfo(initial_map, info, context,
8.                                             allocation);
9.  }
10.  //==============分隔线============
11.  Handle<JSFunction> Factory::NewFunction(Handle<Map> map,
12.                                          Handle<SharedFunctionInfo> info,
13.                                          Handle<Context> context,
14.                                          AllocationType allocation) {
15.    Handle<JSFunction> function(JSFunction::cast(New(map, allocation)),
16.                                isolate());
17.    function->initialize_properties(isolate());
18.    function->initialize_elements();
19.    function->set_shared(*info);
20.    function->set_code(info->GetCode());
21.    function->set_context(*context);
22.    function->set_raw_feedback_cell(*many_closures_cell());
23. //........omit...............
24.  }

In the above code, line 7 calls the NewFunction().

Here, I give you some details about the NewFunction.

  • Lines 17–18, they describe some properties and elements that a SharedFunction doesn’t have.
  • Line 21, it is the bound context that I mentioned above.
  • Lines 19–20, they set the corresponding SharedFunction and Builtins code into the JSFunction.

Okay, a SharedFunction can be shared by multiple instances and a JSFunction is an executable instance.

Okay, that wraps it up for this share. I’ll see you guys next time, take care!

Please reach out to me if you have any issues. WeChat: qq9123013 Email: [email protected]

Also published here.


Written by huidou | a big fan of chrome V8
Published by HackerNoon on 2022/10/25