JS is a strange language
Many implementations
Multiple platforms
JIT (Just In Time) Compiler
function addA(a,b) {
const weirdConstant = 10
return a + b + weirdConstant
}
const c = addA(4,5)
forEach polyfill in JS
forEach polyfill in JS
Array.prototype.forEach = function (callback, thisArg) {
var T, k
if (this == null) {
throw new TypeError(' this is null or not defined')
}
var O = Object(this)
var len = O.length >>> 0
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function')
}
if (arguments.length > 1) {
T = thisArg
}
k = 0
while (k < len) {
var kValue
if (k in O) {
kValue = O[k]
callback.call(T, kValue, k, O)
}
k++
}
}
Fast and generate bytecode
Used only for "HOT" functions
Before dinosaurs => 2008
2010 - First optimizing compiler
2014 - Lets add more of them?
Crankshaft and full codegen
Other engines follow using even more than 2 compilers per engine
2016 - Interpreter idea
2017 - Back to the future
Property access
const getXProp = obj => obj.x function getXProp (obj) { return obj.x }
9.1.8.1 OrdinaryGet ( O, P, Receiver ) When the abstract operation OrdinaryGet is called with Object O, property key P, and
ECMAScript language value Receiver, the following steps are taken:
Assert :IsPropertyKey (P) istrue .- Let desc be ? O.[[GetOwnProperty]](P).
- If desc is
undefined , then
- Let parent be ? O.[[GetPrototypeOf]]().
- If parent is
null , returnundefined .- Return ? parent.[[Get]](P, Receiver).
- If
IsDataDescriptor (desc) istrue , return desc.[[Value]].Assert :IsAccessorDescriptor (desc) istrue .- Let getter be desc.[[Get]].
- If getter is
undefined , returnundefined .- Return ?
Call (getter, Receiver).
const getXProp = obj => obj.x getXProp({ x: 1 })
SET { x:int } => get from offset 1 in IC (Inline Cache)
getXProp({ x: 2 }) { x: 2 } type of { x:int } GET from offset 1
const firstX = { x: 2 } const secondX = { x: 12 } console.log(%HaveSameMap(firstX, secondX)) --allow-native-syntax
Let TurboFan do the work
const getPropX = obj => obj.x
for(var i=0; i<10000; i++) {
getPropX({ x: i })
}
0xc0abb749632 18 488bf0 REX.W movq rsi,rax ;;; <@18,#13> stack-check 0xc0abb749635 21 493ba5880a0000 REX.W cmpq rsp,[r13+0xa88] 0xc0abb74963c 28 7305 jnc 35 (0xc0abb749643) 0xc0abb74963e 30 e89d35efff call StackCheck (0xc0abb63cbe0) ;; code: BUILTIN ;;; <@20,#13> lazy-bailout ;;; <@21,#13> gap 0xc0abb749643 35 488b4510 REX.W movq rax,[rbp+0x10] ;;; <@22,#15> check-non-smi 0xc0abb749647 39 a801 test al,0x1 0xc0abb749649 41 0f8427000000 jz 86 (0xc0abb749676) ;;; <@24,#16> check-maps 0xc0abb74964f 47 49bad9bde1bd02370000 REX.W movq r10,0x3702bde1bdd9 ;; object: 0x3702bde1bdd9 Map(FAST_HOLEY_ELEMENTS) 0xc0abb749659 57 4c3950ff REX.W cmpq [rax-0x1],r10 0xc0abb74965d 61 0f8518000000 jnz 91 (0xc0abb74967b) ;;; <@26,#17> load-named-field 0xc0abb749663 67 8b401b movl rax,[rax+0x1b] ;;; <@28,#21> smi-tag 0xc0abb749666 70 8bd8 movl rbx,rax 0xc0abb749668 72 48c1e320 REX.W shlq rbx, 32 ;;; <@29,#21> gap 0xc0abb74966c 76 488bc3 REX.W movq rax,rbx ;;; <@30,#19> return 0xc0abb74966f 79 488be5 REX.W movq rsp,rbp 0xc0abb749672 82 5d pop rbp 0xc0abb749673 83 c21000 ret 0x10 ;;; -------------------- Jump table -------------------- 0xc0abb749676 86 e88fc9bbff call 0xc0abb30600a ;; deoptimization bailout 1 0xc0abb74967b 91 e894c9bbff call 0xc0abb306014 ;; deoptimization bailout 2
Let TurboFan do the work
const getPropX = obj => obj.x
for(var i=0; i<10000; i++) {
getPropX({ x: i })
}
0xc0abb749632 18 488bf0 REX.W movq rsi,rax ;;; <@18,#13> stack-check 0xc0abb749635 21 493ba5880a0000 REX.W cmpq rsp,[r13+0xa88] 0xc0abb74963c 28 7305 jnc 35 (0xc0abb749643) 0xc0abb74963e 30 e89d35efff call StackCheck (0xc0abb63cbe0) ;; code: BUILTIN ;;; <@20,#13> lazy-bailout ;;; <@21,#13> gap 0xc0abb749643 35 488b4510 REX.W movq rax,[rbp+0x10] ;;; <@22,#15> check-non-smi 0xc0abb749647 39 a801 test al,0x1 0xc0abb749649 41 0f8427000000 jz 86 (0xc0abb749676) ;;; <@24,#16> check-maps 0xc0abb74964f 47 49bad9bde1bd02370000 REX.W movq r10,0x3702bde1bdd9 ;; object: 0x3702bde1bdd9 Map(FAST_HOLEY_ELEMENTS) 0xc0abb749659 57 4c3950ff REX.W cmpq [rax-0x1],r10 0xc0abb74965d 61 0f8518000000 jnz 91 (0xc0abb74967b) ;;; <@26,#17> load-named-field 0xc0abb749663 67 8b401b movl rax,[rax+0x1b] ;;; <@28,#21> smi-tag 0xc0abb749666 70 8bd8 movl rbx,rax 0xc0abb749668 72 48c1e320 REX.W shlq rbx, 32 ;;; <@29,#21> gap 0xc0abb74966c 76 488bc3 REX.W movq rax,rbx ;;; <@30,#19> return 0xc0abb74966f 79 488be5 REX.W movq rsp,rbp 0xc0abb749672 82 5d pop rbp 0xc0abb749673 83 c21000 ret 0x10 ;;; -------------------- Jump table -------------------- 0xc0abb749676 86 e88fc9bbff call 0xc0abb30600a ;; deoptimization bailout 1 0xc0abb74967b 91 e894c9bbff call 0xc0abb306014 ;; deoptimization bailout 2
Let TurboFan do the work
const getPropX = obj => obj.x
for(var i=0; i<10000; i++) {
getPropX({ x: i })
}
0xc0abb749632 18 488bf0 REX.W movq rsi,rax ;;; <@18,#13> stack-check 0xc0abb749635 21 493ba5880a0000 REX.W cmpq rsp,[r13+0xa88] 0xc0abb74963c 28 7305 jnc 35 (0xc0abb749643) 0xc0abb74963e 30 e89d35efff call StackCheck (0xc0abb63cbe0) ;; code: BUILTIN ;;; <@20,#13> lazy-bailout ;;; <@21,#13> gap 0xc0abb749643 35 488b4510 REX.W movq rax,[rbp+0x10] ;;; <@22,#15> check-non-smi 0xc0abb749647 39 a801 test al,0x1 0xc0abb749649 41 0f8427000000 jz 86 (0xc0abb749676) ;;; <@24,#16> check-maps< 0xc0abb74964f 47 49bad9bde1bd02370000 REX.W movq r10,0x3702bde1bdd9 ;; object: 0x3702bde1bdd9 Map(FAST_HOLEY_ELEMENTS) 0xc0abb749659 57 4c3950ff REX.W cmpq [rax-0x1],r10 0xc0abb74965d 61 0f8518000000 jnz 91 (0xc0abb74967b) ;;; <@26,#17> load-named-field 0xc0abb749663 67 8b401b movl rax,[rax+0x1b] ;;; <@28,#21> smi-tag 0xc0abb749666 70 8bd8 movl rbx,rax 0xc0abb749668 72 48c1e320 REX.W shlq rbx, 32 ;;; <@29,#21> gap 0xc0abb74966c 76 488bc3 REX.W movq rax,rbx ;;; <@30,#19> return 0xc0abb74966f 79 488be5 REX.W movq rsp,rbp 0xc0abb749672 82 5d pop rbp 0xc0abb749673 83 c21000 ret 0x10 ;;; -------------------- Jump table -------------------- 0xc0abb749676 86 e88fc9bbff call 0xc0abb30600a ;; deoptimization bailout 1 0xc0abb74967b 91 e894c9bbff call 0xc0abb306014 ;; deoptimization bailout 2
Let TurboFan do the work
const getPropX = obj => obj.x
for(var i=0; i<10000; i++) {
getPropX({ x: i })
}
0xc0abb749632 18 488bf0 REX.W movq rsi,rax ;;; <@18,#13> stack-check 0xc0abb749635 21 493ba5880a0000 REX.W cmpq rsp,[r13+0xa88] 0xc0abb74963c 28 7305 jnc 35 (0xc0abb749643) 0xc0abb74963e 30 e89d35efff call StackCheck (0xc0abb63cbe0) ;; code: BUILTIN ;;; <@20,#13> lazy-bailout ;;; <@21,#13> gap 0xc0abb749643 35 488b4510 REX.W movq rax,[rbp+0x10] ;;; <@22,#15> check-non-smi 0xc0abb749647 39 a801 test al,0x1 0xc0abb749649 41 0f8427000000 jz 86 (0xc0abb749676) ;;; <@24,#16> check-maps 0xc0abb74964f 47 49bad9bde1bd02370000 REX.W movq r10,0x3702bde1bdd9 ;; object: 0x3702bde1bdd9 Map(FAST_HOLEY_ELEMENTS) 0xc0abb749659 57 4c3950ff REX.W cmpq [rax-0x1],r10 0xc0abb74965d 61 0f8518000000 jnz 91 (0xc0abb74967b) ;;; <@26,#17> load-named-field 0xc0abb749663 67 8b401b movl rax,[rax+0x1b] ;;; <@28,#21> smi-tag 0xc0abb749666 70 8bd8 movl rbx,rax 0xc0abb749668 72 48c1e320 REX.W shlq rbx, 32 ;;; <@29,#21> gap 0xc0abb74966c 76 488bc3 REX.W movq rax,rbx ;;; <@30,#19> return 0xc0abb74966f 79 488be5 REX.W movq rsp,rbp 0xc0abb749672 82 5d pop rbp 0xc0abb749673 83 c21000 ret 0x10 ;;; -------------------- Jump table -------------------- 0xc0abb749676 86 e88fc9bbff call 0xc0abb30600a ;; deoptimization bailout 1 0xc0abb74967b 91 e894c9bbff call 0xc0abb306014 ;; deoptimization bailout 2
Let TurboFan do the work
const getPropX = obj => obj.x
for(var i=0; i<10000; i++) {
getPropX({ x: i })
}
0xc0abb749632 18 488bf0 REX.W movq rsi,rax ;;; <@18,#13> stack-check 0xc0abb749635 21 493ba5880a0000 REX.W cmpq rsp,[r13+0xa88] 0xc0abb74963c 28 7305 jnc 35 (0xc0abb749643) 0xc0abb74963e 30 e89d35efff call StackCheck (0xc0abb63cbe0) ;; code: BUILTIN ;;; <@20,#13> lazy-bailout ;;; <@21,#13> gap 0xc0abb749643 35 488b4510 REX.W movq rax,[rbp+0x10] ;;; <@22,#15> check-non-smi 0xc0abb749647 39 a801 test al,0x1 0xc0abb749649 41 0f8427000000 jz 86 (0xc0abb749676) ;;; <@24,#16> check-maps 0xc0abb74964f 47 49bad9bde1bd02370000 REX.W movq r10,0x3702bde1bdd9 ;; object: 0x3702bde1bdd9 Map(FAST_HOLEY_ELEMENTS) 0xc0abb749659 57 4c3950ff REX.W cmpq [rax-0x1],r10 0xc0abb74965d 61 0f8518000000 jnz 91 (0xc0abb74967b) ;;; <@26,#17> load-named-field 0xc0abb749663 67 8b401b movl rax,[rax+0x1b] ;;; <@28,#21> smi-tag 0xc0abb749666 70 8bd8 movl rbx,rax 0xc0abb749668 72 48c1e320 REX.W shlq rbx, 32 ;;; <@29,#21> gap 0xc0abb74966c 76 488bc3 REX.W movq rax,rbx ;;; <@30,#19> return 0xc0abb74966f 79 488be5 REX.W movq rsp,rbp 0xc0abb749672 82 5d pop rbp 0xc0abb749673 83 c21000 ret 0x10 ;;; -------------------- Jump table -------------------- 0xc0abb749676 86 e88fc9bbff call 0xc0abb30600a ;; deoptimization bailout 1 0xc0abb74967b 91 e894c9bbff call 0xc0abb306014 ;; deoptimization bailout 2
Let TurboFan do the work
const getPropX = obj => obj.x
for(var i=0; i<10000; i++) {
getPropX({ x: i })
}
0xc0abb749632 18 488bf0 REX.W movq rsi,rax ;;; <@18,#13> stack-check 0xc0abb749635 21 493ba5880a0000 REX.W cmpq rsp,[r13+0xa88] 0xc0abb74963c 28 7305 jnc 35 (0xc0abb749643) 0xc0abb74963e 30 e89d35efff call StackCheck (0xc0abb63cbe0) ;; code: BUILTIN ;;; <@20,#13> lazy-bailout ;;; <@21,#13> gap 0xc0abb749643 35 488b4510 REX.W movq rax,[rbp+0x10] ;;; <@22,#15> check-non-smi 0xc0abb749647 39 a801 test al,0x1 0xc0abb749649 41 0f8427000000 jz 86 (0xc0abb749676) ;;; <@24,#16> check-maps 0xc0abb74964f 47 49bad9bde1bd02370000 REX.W movq r10,0x3702bde1bdd9 ;; object: 0x3702bde1bdd9 Map(FAST_HOLEY_ELEMENTS) 0xc0abb749659 57 4c3950ff REX.W cmpq [rax-0x1],r10 0xc0abb74965d 61 0f8518000000 jnz 91 (0xc0abb74967b) ;;; <@26,#17> load-named-field 0xc0abb749663 67 8b401b movl rax,[rax+0x1b] ;;; <@28,#21> smi-tag 0xc0abb749666 70 8bd8 movl rbx,rax 0xc0abb749668 72 48c1e320 REX.W shlq rbx, 32 ;;; <@29,#21> gap 0xc0abb74966c 76 488bc3 REX.W movq rax,rbx ;;; <@30,#19> return 0xc0abb74966f 79 488be5 REX.W movq rsp,rbp 0xc0abb749672 82 5d pop rbp 0xc0abb749673 83 c21000 ret 0x10 ;;; -------------------- Jump table -------------------- 0xc0abb749676 86 e88fc9bbff call 0xc0abb30600a ;; deoptimization bailout 1 0xc0abb74967b 91 e894c9bbff call 0xc0abb306014 ;; deoptimization bailout 2
getPropX({ x: 13, y: 20 })
Try it again...
const getPropX = obj => obj.x
for(var i=0; i<10000; i++) {
getPropX({ x: i })
getPropX({ x: i, y: i })
getPropX({ x: i, z: i })
}
0x18eb24149772 18 488bf0 REX.W movq rsi,rax ;;; <@18,#13> stack-check 0x18eb24149775 21 493ba5880a0000 REX.W cmpq rsp,[r13+0xa88] 0x18eb2414977c 28 7305 jnc 35 (0x18eb24149783) 0x18eb2414977e 30 e85d34efff call StackCheck (0x18eb2403cbe0) ;; code: BUILTIN ;;; <@20,#13> lazy-bailout ;;; <@21,#13> gap 0x18eb24149783 35 488b4510 REX.W movq rax,[rbp+0x10] ;;; <@22,#15> check-non-smi 0x18eb24149787 39 a801 test al,0x1 0x18eb24149789 41 0f8447000000 jz 118 (0x18eb241497d6) ;;; <@24,#16> check-maps 0x18eb2414978f 47 49bad9bd9144b9130000 REX.W movq r10,0x13b94491bdd9 ;; object: 0x13b94491bdd9 Map(FAST_HOLEY_ELEMENTS)> 0x18eb24149799 57 4c3950ff REX.W cmpq [rax-0x1],r10 0x18eb2414979d 61 7424 jz 99 (0x18eb241497c3) 0x18eb2414979f 63 49ba89be9144b9130000 REX.W movq r10,0x13b94491be89 ;; object: 0x13b94491be89 Map(FAST_HOLEY_ELEMENTS)> 0x18eb241497a9 73 4c3950ff REX.W cmpq [rax-0x1],r10 0x18eb241497ad 77 7414 jz 99 (0x18eb241497c3) 0x18eb241497af 79 49bae1be9144b9130000 REX.W movq r10,0x13b94491bee1 ;; object: 0x13b94491bee1 Map(FAST_HOLEY_ELEMENTS)> 0x18eb241497b9 89 4c3950ff REX.W cmpq [rax-0x1],r10 0x18eb241497bd 93 0f8518000000 jnz 123 (0x18eb241497db) ;;; <@26,#17> load-named-field 0x18eb241497c3 99 8b401b movl rax,[rax+0x1b] ;;; <@28,#21> smi-tag 0x18eb241497c6 102 8bd8 movl rbx,rax 0x18eb241497c8 104 48c1e320 REX.W shlq rbx, 32 ;;; <@29,#21> gap 0x18eb241497cc 108 488bc3 REX.W movq rax,rbx ;;; <@30,#19> return 0x18eb241497cf 111 488be5 REX.W movq rsp,rbp 0x18eb241497d2 114 5d pop rbp 0x18eb241497d3 115 c21000 ret 0x10 ;;; -------------------- Jump table -------------------- 0x18eb241497d6 118 e82fc8bbff call 0x18eb23d0600a ;; deoptimization bailout 1 0x18eb241497db 123 e834c8bbff call 0x18eb23d06014 ;; deoptimization bailout 2
9.1.9.1 OrdinarySet ( O, P, V, Receiver ) When the abstract operation OrdinarySet is called with Object O, property key P, value V, and
ECMAScript language value Receiver, the following steps are taken:
Assert :IsPropertyKey (P) istrue .- Let ownDesc be ? O.[[GetOwnProperty]](P).
- If ownDesc is
undefined , then
- Let parent be ? O.[[GetPrototypeOf]]().
- If parent is not
null , then
- Return ? parent.[[Set]](P, V, Receiver).
- Else,
- Set ownDesc to the PropertyDescriptor{[[Value]]:
undefined , [[Writable]]:true , [[Enumerable]]:true , [[Configurable]]:true }.- If
IsDataDescriptor (ownDesc) istrue , then
- If ownDesc.[[Writable]] is
false , returnfalse .- If
Type (Receiver) is not Object, returnfalse .- Let existingDescriptor be ? Receiver.[[GetOwnProperty]](P).
- If existingDescriptor is not
undefined , then
- If
IsAccessorDescriptor (existingDescriptor) istrue , returnfalse .- If existingDescriptor.[[Writable]] is
false , returnfalse .- Let valueDesc be the PropertyDescriptor{[[Value]]: V}.
- Return ? Receiver.[[DefineOwnProperty]](P, valueDesc).
- Else Receiver does not currently have a property P,
- Return ?
CreateDataProperty (Receiver, P, V).Assert :IsAccessorDescriptor (ownDesc) istrue .- Let setter be ownDesc.[[Set]].
- If setter is
undefined , returnfalse .- Perform ?
Call (setter, Receiver, « V »).- Return
true .
Property access
if (obj) { return obj.x } ... ........... 27 cmpq [r13-0x40],rax 31 jz 128 37 test al,0x1 39 setzl bl 42 movzxbl rbx,rbx 45 cmpl rbx,0x0 48 jnz 185 54 cmpq [r13-0x38],rax 58 jz 128 64 movq rdx,[rax-0x1] 68 testb [rdx+0xc],0x10 72 jnz 128 78 cmpq [r13+0x50],rdx 82 jz 160 160 vmovsd xmm0,[rax+0x7] 165 movq [rbp-0x18],rbx 169 vxorpd xmm1,xmm1,xmm1 173 vucomisd xmm1,xmm0 177 jz 128 179 movq rbx,[rbp-0x18] 183 jmp 88 185 movq [rbp-0x18],rbx 189 cmpq rax,0x0 193 jz 128 195 movq rbx,[rbp-0x18] 199 jmp 88 ... ...........
Property access
if (obj !== undefined) { return obj.x } ... ........... 23 cmpq [r13-0x60],rax 27 jz 72 ... ...........
Try it
node --v8-options node --print_opt_code test.js > nodejsOut.txt node --print_opt_code --turbo test.js > nodejsOut.txt d8 --print-opt-code text.js > d8Out.txt
Code readability gets worse
Each engine is different
V8 is changing on daily basis :D
Users use different engines either way