iThink
iThink copied to clipboard
神器jsvu
背景
组内的小朋友写了一篇雄文[探究js-v8引擎下的-数组-底层实现],通过分析v8的底层实现来感知js在v8中的内存分配,通过文章可以看出v8底层的优化和针对不同的场景的优化策略。小朋友主动来找我探讨里面技术点,我冒出一个想发,即使这些技术点都对,我们怎么能够验证出来呢,看代码当然不是最合适的方式,效率不高。于是我就踏上了探索之路。
字节码
在学习nodejs的过程中,我知道怎么可以通过观察js最终执行生成的字节码来看最终代码的转化。那怎么查看js最终生成的字节码呢?
测试代码如下:
function a() {
var a = [1,2,3];
a[4] = 5;
console.log(a[2]);
}
a();
通过下列指令就可以查看a函数最终生成的字节码,就可以大体上看出底层的一些状态:
❯ node --print-bytecode --print-bytecode-filter=a test.js
[generated bytecode for function: a]
Parameter count 1
Frame size 32
10 E> 0x15262f2d4952 @ 0 : a0 StackCheck
26 S> 0x15262f2d4953 @ 1 : 77 00 00 25 CreateArrayLiteral [0], [0], #37
0x15262f2d4957 @ 5 : 26 fb Star r0
38 S> 0x15262f2d4959 @ 7 : 0c 04 LdaSmi [4]
0x15262f2d495b @ 9 : 26 f9 Star r2
0x15262f2d495d @ 11 : 0c 05 LdaSmi [5]
43 E> 0x15262f2d495f @ 13 : 2e fb f9 01 StaKeyedProperty r0, r2, [1]
51 S> 0x15262f2d4963 @ 17 : 13 01 03 LdaGlobal [1], [3]
0x15262f2d4966 @ 20 : 26 f9 Star r2
59 E> 0x15262f2d4968 @ 22 : 28 f9 02 05 LdaNamedProperty r2, [2], [5]
0x15262f2d496c @ 26 : 26 fa Star r1
0x15262f2d496e @ 28 : 0c 02 LdaSmi [2]
64 E> 0x15262f2d4970 @ 30 : 29 fb 07 LdaKeyedProperty r0, [7]
0x15262f2d4973 @ 33 : 26 f8 Star r3
59 E> 0x15262f2d4975 @ 35 : 57 fa f9 f8 09 CallProperty1 r1, r2, r3, [9]
0x15262f2d497a @ 40 : 0d LdaUndefined
70 S> 0x15262f2d497b @ 41 : a4 Return
Constant pool (size = 3)
Handler Table (size = 0)
3
通过字节码我们可以感知到一些信息,但是这个信息还不够全面和直观,需要对字节码指令特别熟悉才行。还有没有更好的办法?
走后门 在学习v8的一些基础点的时候,了解到v8其实是有后门可以让js直接调用v8中的c++函数的,时间久了忘记是什么特性了,是时候复习一波了。 通过查询文档发现这个特性是--allow-natives-syntax
测试代码:
const a = [1,2,3];
%DebugPrint(a);
通过下列指令,开启这个特性:
❯ node --allow-natives-syntax hello.js
0x0dcdf5eea059 <JSArray[3]>
看到下列输出的时候眼泪都要流下来了,这不是我想要的啊。随着时间的推移,v8也在变化,在release版本中屏蔽了很多特性。我们只能上硬核方式了,
- 编译node源码,开启debug特性
- 编译v8代码,开启debug特性
1,2成本差不多,都很费神。 知道我发现了一个神器jsvu. 可以帮助我们安装各种js的引擎。So easy.
npm install -g jsvu
jsvu安装v8-debug. 1, 2, 3默念 见证奇迹的时刻。
❯ ~/.jsvu/v8-debug --allow-natives-syntax hello.js
Warning: unknown flag --natives_blob=/Users/hf/.jsvu/engines/v8-debug/natives_blob.bin.
Try --help for options
DebugPrint: 0x2cc50154b351: [JSArray]
- map: 0x2cc54d0029e9 <Map(PACKED_SMI_ELEMENTS)> [FastProperties]
- prototype: 0x2cc59d4d19a9 <JSArray[0]>
- elements: 0x2cc50154b2c9 <FixedArray[3]> [PACKED_SMI_ELEMENTS (COW)]
- length: 3
- properties: 0x2cc576bc0b29 <FixedArray[0]> {
#length: 0x2cc55ea40191 <AccessorInfo> (const accessor descriptor)
}
- elements: 0x2cc50154b2c9 <FixedArray[3]> {
0: 1
1: 2
2: 3
}
0x2cc54d0029e9: [Map]
- type: JS_ARRAY_TYPE
- instance size: 32
- inobject properties: 0
- elements kind: PACKED_SMI_ELEMENTS
- unused property fields: 0
- enum length: invalid
- back pointer: 0x2cc576bc0471 <undefined>
- prototype_validity cell: 0x2cc55ea40661 <Cell value= 1>
- instance descriptors (own) #1: 0x2cc59d4d26a1 <DescriptorArray[1]>
- transitions #1: 0x2cc59d4d26d1 <TransitionArray[4]>Transition array #1:
0x2cc576bc46e1 <Symbol: (elements_transition_symbol)>: (transition to HOLEY_SMI_ELEMENTS) -> 0x2cc54d002ac1 <Map(HOLEY_SMI_ELEMENTS)>
- prototype: 0x2cc59d4d19a9 <JSArray[0]>
- constructor: 0x2cc59d4d1759 <JSFunction Array (sfi = 0x2cc55ea54189)>
- dependent code: 0x2cc576bc0289 <Other heap object (WEAK_FIXED_ARRAY_TYPE)>
- construction counter: 0
v8里面支持哪些内置函数呢?请移步v8/v8/blob/master/src/runtime/runtime.h
MacBook-Pro:test allanxu$ ~/.jsvu/v8-debug --allow-natives-syntax test.js
Warning: unknown flag --natives_blob=/Users/allanxu/.jsvu/engines/v8-debug/natives_blob.bin.
Try --help for options
交流下,为啥我打印出来这样?