记录黑客技术中优秀的内容,传播黑客文化,分享黑客技术精华

《Chrome V8原理讲解》第十二篇 JSFunction源码分析

2021-10-20 13:05
robots

 

1 摘要

编译Javascript源码得到的字节码流(Bytecode Array)不能直接执行,绑定了入口(Entry)和上下文(Context)的字节码流才能被解释器(Ignition)执行,绑定了“入口+上下文”的字码流称为JSFunction,JSFunction是Ignition可以执行的实例。读懂JSFunction,可以深入了解Igntion的工作原理,更有助于在脑海中、在演草纸上擘画出字节码的解释(interpreter)过程。本文以SharedFunction(字节码流的表示方法)为输入,讲解JSFuncition源码和重要功能函数,分析JSFunction创建(new)、绑定入口和上下文的过程(章节2);最后,分析JSFunction内存布局,讲解读/写(getter/setter)方法(章节3)。

 

2 JSFunction分析

一段C语言程序要经过编译(Compilation)、汇编(Assembly)和链接(Linking)之后才能执行。不太严谨但很形象的类比:字节码流类似汇编之后的结果(V8称之为SharedFunction),JSFunction类似链接之后的程序,所以说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. };

代码18~45行是关于优化及反优化的功能,这些优化机制用于热点统计,反馈给TurboFan用于优化编译;46~68行定义了GETTER方法。5~7行定义了几个重要的数据索引(index);10,11,12行代码绑定上下文和全局Reciver;17行代码绑定入口的Builtin功能(InterpreterEntryTrampoline),87行是JSFunction的内存布局,下面跟随new 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. }

InstallBytecodeArray()最重要的作用是把编译生成的bytecode_array安装到shared_info中,代码第5行。注: 我的编译选项:FLAG_interpreted_frames_native_stack为false。
(2) 生成JSFunction

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行this转为SharedFunctionInfo,代码5行,function_info和native_context作为参数,返回JSFunction。代码5行的工厂方法factory()进入代码11行的NewFunctionFromSharedFunctionInfo(),初始创建map,最终调用下面的方法:

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行,把SharedFunctionInfo保存到kSharedFunctionInfoOffset位置。代码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. }

上述代码中,我们的测试用例满足代码7行的判断条件,得到Builtins::kInterpreterEntryTrampoline,它负责堆栈,压入参数等等(参见第八篇文章),即前面提到的绑定入口(Entry)。
返回到NewFunction(),11行绑定Context。至此,字节码流,入口、上下文三个最重要的数据都安装到了function中,new JSFunction完成,图1给出函数调用堆栈。注:map机制,JSObject后续文章讲解。

 

3 JSFunction内存布局

下面讲解JSFuncion的内存布局,JSFunction是堆对象,它的成员有固定的存储偏移(offset),class JSFunction中的DEFINE_FIELD_OFFSET_CONSTANTS宏模板定义了JSFuncion成员的读/写方法,源码如下:

#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是kHeaderSize,它是基址,通过基址+偏移的方法实现读/写成员,以JSFunction的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()方法是公共方法,通过图2的堆栈可以看出Relaxed_Load()和Relaxed_Store()都是V8的基类方法,堆上对象的读/写都会用这几个方法。
好了,今天到这里,下次见。

恳请读者批评指正、提出宝贵意见
微信:qq9123013 备注:v8交流 邮箱:v8blink@outlook.com


知识来源: https://www.anquanke.com/post/id/255227

阅读:93023 | 评论:0 | 标签:分析

想收藏或者和大家分享这篇好文章→复制链接地址

“《Chrome V8原理讲解》第十二篇 JSFunction源码分析”共有0条留言

发表评论

姓名:

邮箱:

网址:

验证码:

黑帝公告 📢

永久免费持续更新精选优质黑客技术文章Hackdig,帮你成为掌握黑客技术的英雄

标签云 ☁