Ok, there is more details. This is simplified example without loops
dog3.doIt(); //deopt - wrong map
%OptimizeFunctionOnNextCall(dog3.doIt)
dog3.doIt() //v8 gives up and compiles this.done to slow calls c++ runtime functions - LoadICTrampoline and StoreICStrictTrampoline
}
test()
Running with latest node with arguments "node --trace-deopt --print-opt-code --allow-natives-syntax test.js" there will be output of asm instructions for each optimization of doIt() method. After first optimizations (with only Cat class invoking) I got
0x3e9869385180 0 55 push rbp
0x3e9869385181 1 4889e5 REX.W movq rbp,rsp
0x3e9869385184 4 56 push rsi
0x3e9869385185 5 57 push rdi
0x3e9869385186 6 493ba5480c0000 REX.W cmpq rsp,[r13+0xc48]
0x3e986938518d d 0f863f000000 jna 0x3e98693851d2 <+0x52>
0x3e9869385193 13 488b4510 REX.W movq rax,[rbp+0x10]
0x3e9869385197 17 a801 test al,0x1
0x3e9869385199 19 0f844a000000 jz 0x3e98693851e9 <+0x69>
0x3e986938519f 1f 48bb9112642f9b320000 REX.W movq rbx,0x329b2f641291 ;; object: 0x329b2f641291 <Map(FAST_HOLEY_ELEMENTS)>
0x3e98693851a9 29 483958ff REX.W cmpq [rax-0x1],rbx
0x3e98693851ad 2d 0f853b000000 jnz 0x3e98693851ee <+0x6e>
0x3e98693851b3 33 8b581b movl rbx,[rax+0x1b]
0x3e98693851b6 36 83ebff subl rbx,0xff
0x3e98693851b9 39 0f8034000000 jo 0x3e98693851f3 <+0x73>
0x3e98693851bf 3f 48c1e320 REX.W shlq rbx, 32
0x3e98693851c3 43 48895817 REX.W movq [rax+0x17],rbx
0x3e98693851c7 47 498b45a0 REX.W movq rax,[r13-0x60]
0x3e98693851cb 4b 488be5 REX.W movq rsp,rbp
0x3e98693851ce 4e 5d pop rbp
0x3e98693851cf 4f c20800 ret 0x8
than after few deoptimizations for Dog2 class V8 compiles doIt() method to this
0x3e9869385540 0 55 push rbp
0x3e9869385541 1 4889e5 REX.W movq rbp,rsp
0x3e9869385544 4 56 push rsi
0x3e9869385545 5 57 push rdi
0x3e9869385546 6 493ba5480c0000 REX.W cmpq rsp,[r13+0xc48]
0x3e986938554d d 0f867b000000 jna 0x3e98693855ce <+0x8e>
0x3e9869385553 13 488b4510 REX.W movq rax,[rbp+0x10]
0x3e9869385557 17 a801 test al,0x1
0x3e9869385559 19 0f8489000000 jz 0x3e98693855e8 <+0xa8>
0x3e986938555f 1f 488b58ff REX.W movq rbx,[rax-0x1]
0x3e9869385563 23 48ba9112642f9b320000 REX.W movq rdx,0x329b2f641291 ;; object: 0x329b2f641291 <Map(FAST_HOLEY_ELEMENTS)>
0x3e986938556d 2d 483bd3 REX.W cmpq rdx,rbx
0x3e9869385570 30 0f8439000000 jz 0x3e98693855af <+0x6f>
0x3e9869385576 36 48ba4914642f9b320000 REX.W movq rdx,0x329b2f641449 ;; object: 0x329b2f641449 <Map(FAST_HOLEY_ELEMENTS)>
0x3e9869385580 40 483bd3 REX.W cmpq rdx,rbx
0x3e9869385583 43 0f8426000000 jz 0x3e98693855af <+0x6f>
0x3e9869385589 49 48ba5115642f9b320000 REX.W movq rdx,0x329b2f641551 ;; object: 0x329b2f641551 <Map(FAST_HOLEY_ELEMENTS)>
0x3e9869385593 53 483bd3 REX.W cmpq rdx,rbx
0x3e9869385596 56 0f8413000000 jz 0x3e98693855af <+0x6f>
0x3e986938559c 5c 48ba5916642f9b320000 REX.W movq rdx,0x329b2f641659 ;; object: 0x329b2f641659 <Map(FAST_HOLEY_ELEMENTS)>
0x3e98693855a6 66 483bd3 REX.W cmpq rdx,rbx
0x3e98693855a9 69 0f853e000000 jnz 0x3e98693855ed <+0xad>
0x3e98693855af 6f 8b581b movl rbx,[rax+0x1b]
0x3e98693855b2 72 83ebff subl rbx,0xff
0x3e98693855b5 75 0f8037000000 jo 0x3e98693855f2 <+0xb2>
0x3e98693855bb 7b 48c1e320 REX.W shlq rbx, 32
0x3e98693855bf 7f 48895817 REX.W movq [rax+0x17],rbx
0x3e98693855c3 83 498b45a0 REX.W movq rax,[r13-0x60]
0x3e98693855c7 87 488be5 REX.W movq rsp,rbp
0x3e98693855ca 8a 5d pop rbp
0x3e98693855cb 8b c20800 ret 0x8
and on Dog3 class v8 gives up and compiles to this (with slow runtime calls LoadICTrampoline and StoreICStrictTrampoline)
0x3e98693856a0 0 55 push rbp
0x3e98693856a1 1 4889e5 REX.W movq rbp,rsp
0x3e98693856a4 4 56 push rsi
0x3e98693856a5 5 57 push rdi
0x3e98693856a6 6 493ba5480c0000 REX.W cmpq rsp,[r13+0xc48]
0x3e98693856ad d 0f8669000000 jna 0x3e986938571c <+0x7c>
0x3e98693856b3 13 488b75f8 REX.W movq rsi,[rbp-0x8]
0x3e98693856b7 17 48b80000000003000000 REX.W movq rax,0x300000000
0x3e98693856c1 21 488b5510 REX.W movq rdx,[rbp+0x10]
0x3e98693856c5 25 498b8dd0050000 REX.W movq rcx,[r13+0x5d0]
0x3e98693856cc 2c 488bd9 REX.W movq rbx,rcx
0x3e98693856cf 2f e8acd2f4ff call 0x3e98692d2980 (LoadICTrampoline) ;; code: LOAD_IC
0x3e98693856d4 34 a801 test al,0x1
0x3e98693856d6 36 0f8557000000 jnz 0x3e9869385733 <+0x93>
0x3e98693856dc 3c 488bd8 REX.W movq rbx,rax
0x3e98693856df 3f 48c1eb20 REX.W shrq rbx, 32
0x3e98693856e3 43 83ebff subl rbx,0xff
0x3e98693856e6 46 0f804c000000 jo 0x3e9869385738 <+0x98>
0x3e98693856ec 4c 48c1e320 REX.W shlq rbx, 32
0x3e98693856f0 50 48bf0000000005000000 REX.W movq rdi,0x500000000
0x3e98693856fa 5a 488b5510 REX.W movq rdx,[rbp+0x10]
0x3e98693856fe 5e 498b8dd0050000 REX.W movq rcx,[r13+0x5d0]
0x3e9869385705 65 488bc3 REX.W movq rax,rbx
0x3e9869385708 68 488b75f8 REX.W movq rsi,[rbp-0x8]
0x3e986938570c 6c e8af1af5ff call 0x3e98692d71c0 (StoreICStrictTrampoline) ;; code: STORE_IC
0x3e9869385711 71 498b45a0 REX.W movq rax,[r13-0x60]
0x3e9869385715 75 488be5 REX.W movq rsp,rbp
0x3e9869385718 78 5d pop rbp
0x3e9869385719 79 c20800 ret 0x8
So I suspect either there is a bug in v8 or v8 generally can't handle inheritance and subclassing and always will be compile to slow polymorphic access to this object in ancestors methods, thus "extends" keyword turns out as performance antipattern