Chrome V8을 이해해 봅시다 — 12장: JSFunction이란 무엇입니까?
55087 단어 v8cppjavascriptchrome
Let’s Understand Chrome V8의 다른 장에 오신 것을 환영합니다.
바이트 코드를 저장하는 데이터 구조는 SharedFunction이고 컨텍스트에 바인딩된 SharedFunction은 JSFunction입니다. 차이점은 무엇입니까? 내 생각에 SharedFunction은 여러 프로그램에서 공유할 수 있는 공통 라이브러리인 DLL과 같습니다. JSFunction은 특정 실행 프로그램입니다. 이 백서에서는 JSFunction의 코드와 메모리 레이아웃에 대해 설명합니다.
1. JSFunction의 코드
다음은 JSFunction의 정의입니다.
1. class JSFunction : public JSObject {
2. public:
3. DECL_ACCESSORS(prototype_or_initial_map, HeapObject)
4. DECL_ACCESSORS(shared, SharedFunctionInfo)
5. static const int kLengthDescriptorIndex = 0;
6. static const int kNameDescriptorIndex = 1;
7. static const int kMaybeHomeObjectDescriptorIndex = 2;
8. inline Context context();
9. inline bool has_context() const;
10. inline void set_context(HeapObject context);
11. inline JSGlobalProxy global_proxy();
12. inline NativeContext native_context();
13. inline int length();
14. static Handle<Object> GetName(Isolate* isolate, Handle<JSFunction> function);
15. static Handle<NativeContext> GetFunctionRealm(Handle<JSFunction> function);
16. inline Code code() const;
17. inline void set_code(Code code);
18. inline void set_code_no_write_barrier(Code code);
19. inline AbstractCode abstract_code();
20. inline bool IsInterpreted();
21. inline bool ChecksOptimizationMarker();
22. inline bool IsOptimized();
23. inline bool HasOptimizedCode();
24. inline bool HasOptimizationMarker();
25. void MarkForOptimization(ConcurrencyMode mode);
26. inline bool IsMarkedForOptimization();
27. inline bool IsMarkedForConcurrentOptimization();
28. inline bool IsInOptimizationQueue();
29. inline void ClearOptimizedCodeSlot(const char* reason);
30. inline void SetOptimizationMarker(OptimizationMarker marker);
31. inline void ClearOptimizationMarker();
32. int ComputeInstanceSizeWithMinSlack(Isolate* isolate);
33. inline void CompleteInobjectSlackTrackingIfActive();
34. DECL_ACCESSORS(raw_feedback_cell, FeedbackCell)
35. inline FeedbackVector feedback_vector() const;
36. inline bool has_feedback_vector() const;
37. V8_EXPORT_PRIVATE static void EnsureFeedbackVector(
38. Handle<JSFunction> function);
39. inline bool has_closure_feedback_cell_array() const;
40. inline ClosureFeedbackCellArray closure_feedback_cell_array() const;
41. static void EnsureClosureFeedbackCellArray(Handle<JSFunction> function);
42. static void InitializeFeedbackCell(Handle<JSFunction> function);
43. void ClearTypeFeedbackInfo();
44. inline bool NeedsResetDueToFlushedBytecode();
45. inline void ResetIfBytecodeFlushed();
46. DECL_GETTER(has_prototype_slot, bool)
47. DECL_GETTER(initial_map, Map)
48. static void SetInitialMap(Handle<JSFunction> function, Handle<Map> map,
49. Handle<HeapObject> prototype);
50. DECL_GETTER(has_initial_map, bool)
51. V8_EXPORT_PRIVATE static void EnsureHasInitialMap(
52. Handle<JSFunction> function);
53. static V8_WARN_UNUSED_RESULT MaybeHandle<Map> GetDerivedMap(
54. Isolate* isolate, Handle<JSFunction> constructor,
55. Handle<JSReceiver> new_target);
56. DECL_GETTER(has_prototype, bool)
57. DECL_GETTER(has_instance_prototype, bool)
58. DECL_GETTER(prototype, Object)
59. DECL_GETTER(instance_prototype, HeapObject)
60. DECL_GETTER(has_prototype_property, bool)
61. DECL_GETTER(PrototypeRequiresRuntimeLookup, bool)
62. static void SetPrototype(Handle<JSFunction> function, Handle<Object> value);
63. inline bool is_compiled() const;
64. static int GetHeaderSize(bool function_has_prototype_slot) {
65. return function_has_prototype_slot ? JSFunction::kSizeWithPrototype
66. : JSFunction::kSizeWithoutPrototype;
67. }
68. void PrintName(FILE* out = stdout);
69. DECL_CAST(JSFunction)
70. static V8_WARN_UNUSED_RESULT int CalculateExpectedNofProperties(
71. Isolate* isolate, Handle<JSFunction> function);
72. static void CalculateInstanceSizeHelper(InstanceType instance_type,
73. bool has_prototype_slot,
74. int requested_embedder_fields,
75. int requested_in_object_properties,
76. int* instance_size,
77. int* in_object_properties);
78. DECL_PRINTER(JSFunction)
79. DECL_VERIFIER(JSFunction)
80. static Handle<String> GetName(Handle<JSFunction> function);
81. static V8_WARN_UNUSED_RESULT bool SetName(Handle<JSFunction> function,
82. Handle<Name> name,
83. Handle<String> prefix);
84. static Handle<String> GetDebugName(Handle<JSFunction> function);
85. static Handle<String> ToString(Handle<JSFunction> function);
86. //
87. DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize,
88. TORQUE_GENERATED_JSFUNCTION_FIELDS)
89. static constexpr int kSizeWithoutPrototype = kPrototypeOrInitialMapOffset;
90. static constexpr int kSizeWithPrototype = kSize;
91. OBJECT_CONSTRUCTORS(JSFunction, JSObject);
92. };
세 개의 인덱스 멤버는 5행에서 7행까지 정의되며 정적 멤버이며 모든 SharedFunction 인스턴스에 적용됩니다. 10, 11, 12행은 컨텍스트와 전역 수신기를 바인딩합니다. 17행은 바이트코드 실행을 위한 진입점인 InterpreterEntryTrampoline을 바인딩합니다. 18~45행은 TurboFan에서 사용되는 최적화 및 역최적화 기능을 정의합니다. 새로운 JSFunction의 프로세스에 대해 이야기해 봅시다.
(1) SharedFunction 생성 및 바이트코드 설치
1. void InstallBytecodeArray(Handle<BytecodeArray> bytecode_array,
2. Handle<SharedFunctionInfo> shared_info,
3. ParseInfo* parse_info, Isolate* isolate) {
4. if (!FLAG_interpreted_frames_native_stack) {
5. shared_info->set_bytecode_array(*bytecode_array);
6. return;
7. }
8. Handle<Code> code = isolate->factory()->CopyCode(Handle<Code>::cast(
9. isolate->factory()->interpreter_entry_trampoline_for_profiling()));
10. Handle<InterpreterData> interpreter_data =
11. Handle<InterpreterData>::cast(isolate->factory()->NewStruct(
12. INTERPRETER_DATA_TYPE, AllocationType::kOld));
13. interpreter_data->set_bytecode_array(*bytecode_array);
14. interpreter_data->set_interpreter_trampoline(*code);
15. shared_info->set_interpreter_data(*interpreter_data);
16. Handle<Script> script = parse_info->script();
17. Handle<AbstractCode> abstract_code = Handle<AbstractCode>::cast(code);
18. int line_num =
19. Script::GetLineNumber(script, shared_info->StartPosition()) + 1;
20. int column_num =
21. Script::GetColumnNumber(script, shared_info->StartPosition()) + 1;
22. String script_name = script->name().IsString()
23. ? String::cast(script->name())
24. : ReadOnlyRoots(isolate).empty_string();
25. CodeEventListener::LogEventsAndTags log_tag = Logger::ToNativeByScript(
26. CodeEventListener::INTERPRETED_FUNCTION_TAG, *script);
27. PROFILE(isolate, CodeCreateEvent(log_tag, *abstract_code, *shared_info,
28. script_name, line_num, column_num));
29. }
새 JSFunction 인스턴스를 만들 때 먼저 SharedFunction 인스턴스를 만들어야 합니다. SharedFunction 인스턴스를 생성할 때 중요한 단계는 바이트코드를 설치하는 것입니다. 위 코드의 13행을 참조하십시오.
(2) 새로운 JSFunction
아래 코드를 통해 JS를 생성하는 것이 실제로 SharedFunction과 컨텍스트를 바인딩하는 과정임을 알 수 있습니다.
1. Local<Script> UnboundScript::BindToCurrentContext() {
2. auto function_info =
3. i::Handle<i::SharedFunctionInfo>::cast(Utils::OpenHandle(this));
4. i::Isolate* isolate = function_info->GetIsolate();
5. i::Handle<i::JSFunction> function =
6. isolate->factory()->NewFunctionFromSharedFunctionInfo(
7. function_info, isolate->native_context());
8. return ToApiHandle<Script>(function);
9. }
10. //..................分隔线........................................
11. Handle<JSFunction> Factory::NewFunctionFromSharedFunctionInfo(
12. Handle<Map> initial_map, Handle<SharedFunctionInfo> info,
13. Handle<Context> context, AllocationType allocation) {
14. DCHECK_EQ(JS_FUNCTION_TYPE, initial_map->instance_type());
15. Handle<JSFunction> result =
16. NewFunction(initial_map, info, context, allocation);
17. // Give compiler a chance to pre-initialize.
18. Compiler::PostInstantiation(result);
19. return result;
20. }
위 코드의 2행은 이것을 SharedFunctionInfo(원래 SharedFunctionInfo임)로 캐스팅합니다. 5행, NewFunctionFromSharedFunctionInfo를 호출하여 JSFunction을 생성합니다. 참고: 매개변수는 S와 C입니다. 11행을 참조하세요. 마지막으로 아래에 있는 NewFunction을 호출합니다.
1. Handle<JSFunction> Factory::NewFunction(Handle<Map> map,
2. Handle<SharedFunctionInfo> info,
3. Handle<Context> context,
4. AllocationType allocation) {
5. Handle<JSFunction> function(JSFunction::cast(New(map, allocation)),
6. isolate());
7. function->initialize_properties(isolate());
8. function->initialize_elements();
9. function->set_shared(*info);
10. function->set_code(info->GetCode());
11. function->set_context(*context);
12. function->set_raw_feedback_cell(*many_closures_cell());
13. int header_size;
14. if (map->has_prototype_slot()) {
15. header_size = JSFunction::kSizeWithPrototype;
16. function->set_prototype_or_initial_map(*the_hole_value());
17. } else {
18. header_size = JSFunction::kSizeWithoutPrototype;
19. }
20. InitializeJSObjectBody(function, map, header_size);
21. return function;
22. }
위 코드의 9번째 및 11번째 줄은 SharedFunctionInfo 및 컨텍스트를 해당 위치로 설정하며, 이는 위에서 언급한 바인드입니다.
10행의 GetCode는 무엇입니까? 아래를 참조하십시오.
1. Code SharedFunctionInfo::GetCode() const {
2. Isolate* isolate = GetIsolate();
3. Object data = function_data();
4. if (data.IsSmi()) {
5. DCHECK(HasBuiltinId());
6. return isolate->builtins()->builtin(builtin_id());
7. } else if (data.IsBytecodeArray()) {
8. DCHECK(HasBytecodeArray());
9. return isolate->builtins()->builtin(Builtins::kInterpreterEntryTrampoline);
10. } else if (data.IsAsmWasmData()) {
11. DCHECK(HasAsmWasmData());
12. return isolate->builtins()->builtin(Builtins::kInstantiateAsmJs);
13. } else if (data.IsUncompiledData()) {
14. DCHECK(HasUncompiledData());
15. return isolate->builtins()->builtin(Builtins::kCompileLazy);
16. } else if (data.IsFunctionTemplateInfo()) {
17. DCHECK(IsApiFunction());
18. return isolate->builtins()->builtin(Builtins::kHandleApiCall);
19. } else if (data.IsWasmExportedFunctionData()) {
20. DCHECK(HasWasmExportedFunctionData());
21. return wasm_exported_function_data().wrapper_code();
22. } else if (data.IsInterpreterData()) {
23. Code code = InterpreterTrampoline();
24. DCHECK(code.IsCode());
25. DCHECK(code.is_interpreter_trampoline_builtin());
26. return code;
27. } else if (data.IsWasmJSFunctionData()) {
28. return wasm_js_function_data().wrapper_code();
29. } else if (data.IsWasmCapiFunctionData()) {
30. return wasm_capi_function_data().wrapper_code();
31. }
32. UNREACHABLE();
33. }
테스트 사례(Chapter 4 참조)는 바이트코드 배열이므로 GetCode의 반환 값은 kInterpreterEntryTrampoline입니다. GetCode의 역할은 SharedFunctionInfo의 유형에 따라 해당 바이트코드로 점프하여 실행하는 데 사용되는 진입 방법을 반환하는 것입니다(Chapter 8 참조).
요약하면 JS를 만들 때 바이트코드 유형, 컨텍스트 및 입력 방법은 우리가 중요하게 생각하고 나머지는 중요하지 않습니다. 그림 1은 호출 스택을 보여줍니다.
2. JSFunction의 메모리 레이아웃
JSFunction은 V8의 힙 관리 객체이며 메모리 오프셋을 사용하여 내부 멤버를 나타냅니다. 코드는 다음과 같습니다.
#define TORQUE_GENERATED_JSFUNCTION_FIELDS(V) \
V(kStartOfStrongFieldsOffset, 0) \
V(kSharedFunctionInfoOffset, kTaggedSize) \
V(kContextOffset, kTaggedSize) \
V(kFeedbackCellOffset, kTaggedSize) \
V(kEndOfStrongFieldsOffset, 0) \
V(kStartOfWeakFieldsOffset, 0) \
V(kCodeOffset, kTaggedSize) \
V(kPrototypeOrInitialMapOffset, kTaggedSize) \
V(kEndOfWeakFieldsOffset, 0) \
V(kSize, 0) \
#define DEFINE_ONE_FIELD_OFFSET(Name, Size) Name, Name##End = Name + (Size)-1,
#define DEFINE_FIELD_OFFSET_CONSTANTS(StartOffset, LIST_MACRO) \
enum { \
LIST_MACRO##_StartOffset = StartOffset - 1, \
LIST_MACRO(DEFINE_ONE_FIELD_OFFSET) \
};
TORQUE_GENERATED_JSFUNCTION_FIELDS는 모든 멤버의 오프셋을 정의하고 StartOffset은 기본 주소입니다. V8은 StartOffset+offset으로 멤버를 읽고 씁니다. set_code를 예로 들어 설명하면 코드는 아래와 같습니다.
void JSFunction::set_code(Code value) {
DCHECK(!ObjectInYoungGeneration(value));
RELAXED_WRITE_FIELD(*this, kCodeOffset, value);
#ifndef V8_DISABLE_WRITE_BARRIERS
MarkingBarrier(*this, RawField(kCodeOffset), value);
#endif
}
매크로 템플릿 RELAXED_WRITE_FIELD는 다음과 같습니다.
#define RELAXED_WRITE_FIELD(p, offset, value) \
TaggedField<Object>::Relaxed_Store(p, offset, value)
//...........................................
template <typename T, int kFieldOffset>
void TaggedField<T, kFieldOffset>::Relaxed_Store(HeapObject host, int offset,
T value) {
AsAtomicTagged::Relaxed_Store(location(host, offset),
full_to_tagged(value.ptr()));
}
//........................................
template <typename T>
static void Relaxed_Store(T* addr,
typename std::remove_reference<T>::type new_value) {
STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
base::Relaxed_Store(to_storage_addr(addr),
cast_helper<T>::to_storage_type(new_value));
}
위의 두 개의 Relaxed_Store는 set_code의 쓰기 작업을 구현합니다. 그림 2는 호출 스택을 보여줍니다.
읽기 동작은 위에서 언급한 쓰기와 동일합니다. 오프셋에서 바이트를 읽고 예상 유형으로 변환합니다. 코드는 아래에 있습니다. 직접 분석하십시오.
#define RELAXED_READ_FIELD(p, offset) \
TaggedField<Object>::Relaxed_Load(p, offset)
//............................................
template <typename T>
static T Relaxed_Load(T* addr) {
STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
return cast_helper<T>::to_return_type(
base::Relaxed_Load(to_storage_addr(addr)));
}
그건 그렇고, Relaxed_Load는 V8에서 힙 객체를 관리할 때 자주 사용되는 기본 메서드입니다.
자, 이것으로 이번 공유를 마치겠습니다. 다음에 뵙겠습니다 여러분 건강하세요!
문제가 있으면 저에게 연락해 주세요. 위챗: qq9123013 이메일: [email protected]
Reference
이 문제에 관하여(Chrome V8을 이해해 봅시다 — 12장: JSFunction이란 무엇입니까?), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/v8blink/lets-understand-chrome-v8-chapter-12-what-is-jsfunction-2ih4텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)