Day05:從 class 看 bytecode


前言

前面已經看過許多滿基本的操作,這一篇就讓我們從 class 以及 prototype 的觀點來看 bytecode 吧!

prototype

先來個簡單的範例,宣告一個簡單的 class 然後有簡單的 method,因為要讓 filter 找到的關係,所以必須在很多地方都加上 find_me prefix:

function find_me_test() {
  function find_me_Person(name) {
    this.name = name
  }

  find_me_Person.prototype.sayHello = function find_me_sayHello() {
    console.log(this.name)
  }

  var p = new find_me_Person('yo')
  p.sayHello()
}

find_me_test()

產生出來的結果:

[generated bytecode for function: find_me_test]
Parameter count 1
Frame size 32
         0x262e8849deba @    0 : 81 00 00 02       CreateClosure [0], [0], #2
         0x262e8849debe @    4 : 26 fb             Star r0
   21 E> 0x262e8849dec0 @    6 : a5                StackCheck 
  103 S> 0x262e8849dec1 @    7 : 28 fb 01 01       LdaNamedProperty r0, [1], [1]
         0x262e8849dec5 @   11 : 26 f9             Star r2
         0x262e8849dec7 @   13 : 81 02 03 01       CreateClosure [2], [3], #1
  122 E> 0x262e8849decb @   17 : 2d f9 03 04       StaNamedProperty r2, [3], [4]
  196 S> 0x262e8849decf @   21 : 12 04             LdaConstant [4]
         0x262e8849ded1 @   23 : 26 f8             Star r3
         0x262e8849ded3 @   25 : 25 fb             Ldar r0
  196 E> 0x262e8849ded5 @   27 : 65 fb f8 01 06    Construct r0, r3-r3, [6]
         0x262e8849deda @   32 : 26 fa             Star r1
  225 S> 0x262e8849dedc @   34 : 28 fa 03 08       LdaNamedProperty r1, [3], [8]
         0x262e8849dee0 @   38 : 26 f9             Star r2
  225 E> 0x262e8849dee2 @   40 : 58 f9 fa 0a       CallProperty0 r2, r1, [10]
         0x262e8849dee6 @   44 : 0d                LdaUndefined 
  236 S> 0x262e8849dee7 @   45 : a9                Return 
Constant pool (size = 5)
0x262e8849de21: [FixedArray] in OldSpace
 - map: 0x262ee5d807b1 <Map>
 - length: 5
           0: 0x262e8849dd61 <SharedFunctionInfo find_me_Person>
           1: 0x262ee5d84371 <String[#9]: prototype>
           2: 0x262e8849ddb9 <SharedFunctionInfo find_me_sayHello>
           3: 0x262e8849d909 <String[#8]: sayHello>
           4: 0x262e8849dcf9 <String[#2]: yo>
Handler Table (size = 0)
[generated bytecode for function: find_me_Person]
Parameter count 2
Frame size 0
   51 E> 0x262e8849e0c2 @    0 : a5                StackCheck 
   64 S> 0x262e8849e0c3 @    1 : 25 02             Ldar a0
   74 E> 0x262e8849e0c5 @    3 : 2d 03 00 00       StaNamedProperty <this>, [0], [0]
         0x262e8849e0c9 @    7 : 0d                LdaUndefined 
   83 S> 0x262e8849e0ca @    8 : a9                Return 
Constant pool (size = 1)
0x262e8849e051: [FixedArray] in OldSpace
 - map: 0x262ee5d807b1 <Map>
 - length: 1
           0: 0x262ee5d84059 <String[#4]: name>
Handler Table (size = 0)
[generated bytecode for function: find_me_sayHello]
Parameter count 1
Frame size 24
  149 E> 0x262e8849e24a @    0 : a5                StackCheck 
  158 S> 0x262e8849e24b @    1 : 13 00 00          LdaGlobal [0], [0]
         0x262e8849e24e @    4 : 26 fa             Star r1
  166 E> 0x262e8849e250 @    6 : 28 fa 01 02       LdaNamedProperty r1, [1], [2]
         0x262e8849e254 @   10 : 26 fb             Star r0
  175 E> 0x262e8849e256 @   12 : 28 02 02 04       LdaNamedProperty <this>, [2], [4]
         0x262e8849e25a @   16 : 26 f9             Star r2
  166 E> 0x262e8849e25c @   18 : 59 fb fa f9 06    CallProperty1 r0, r1, r2, [6]
         0x262e8849e261 @   23 : 0d                LdaUndefined 
  183 S> 0x262e8849e262 @   24 : a9                Return 
Constant pool (size = 3)
0x262e8849e1c9: [FixedArray] in OldSpace
 - map: 0x262ee5d807b1 <Map>
 - length: 3
           0: 0x262e81e900e9 <String[#7]: console>
           1: 0x262e81e8fbe9 <String[#3]: log>
           2: 0x262ee5d84059 <String[#4]: name>
Handler Table (size = 0)
yo

產生出來的 bytecode 比想像中少滿多的,find_me_Personfind_me_sayHello的部分應該可以不用多提了,程式碼滿少滿簡單的。主要是因為有 <this> 這個關鍵字,所以就只是利用了這個關鍵字而已。

其他部分我簡單翻譯了一下:

CreateClosure [0], [0], #2
Star r0                        // r0 = find_me_Person
StackCheck 
LdaNamedProperty r0, [1], [1]
Star r2                        // r2 = find_me_Person.prototype
CreateClosure [2], [3], #1
StaNamedProperty r2, [3], [4]  // r2.sayHello = find_me_sayHello
LdaConstant [4]
Star r3                        // r3 = yo
Ldar r0
Construct r0, r3-r3, [6]       
Star r1                        // r1 = new find_me_Person(r3)
LdaNamedProperty r1, [3], [8]
Star r2                        // r2 = r1.sayHello
CallProperty0 r2, r1, [10]     // r1.sayHello()
LdaUndefined 
Return

突然發現這樣拆成一步一步來,就瞬間清楚了很多。就是一連串的讀取 property 然後呼叫 function,唯一比較特別的地方就是 new 的時候會呼叫 Construct,其他的部分沒有什麼太特別的地方。

class

接著我們來看看 class 的版本,功能都一樣,只差在從 class 的語法變成 prototype 而已:

function find_me_test() {
  class find_me_Person {
    constructor(name){
      this.name = name
    }

    sayHello() {
      console.log(this.name)
    }
  }
  var p = new find_me_Person('yo')
  p.sayHello()
}

find_me_test()

結果:

[generated bytecode for function: find_me_test]
Parameter count 1
Frame size 72
   21 E> 0x1f61be31deea @    0 : a5                StackCheck 
         0x1f61be31deeb @    1 : 82 00             CreateBlockContext [0]
         0x1f61be31deed @    3 : 16 f8             PushContext r3
         0x1f61be31deef @    5 : 0f                LdaTheHole 
         0x1f61be31def0 @    6 : 26 f4             Star r7
         0x1f61be31def2 @    8 : 81 02 00 02       CreateClosure [2], [0], #2
         0x1f61be31def6 @   12 : 26 f7             Star r4
         0x1f61be31def8 @   14 : 12 01             LdaConstant [1]
         0x1f61be31defa @   16 : 26 f6             Star r5
         0x1f61be31defc @   18 : 81 03 01 02       CreateClosure [3], [1], #2
         0x1f61be31df00 @   22 : 26 f3             Star r8
         0x1f61be31df02 @   24 : 27 f7 f5          Mov r4, r6
         0x1f61be31df05 @   27 : 61 27 00 f6 04    CallRuntime [DefineClass], r5-r8
         0x1f61be31df0a @   32 : 26 f6             Star r5
         0x1f61be31df0c @   34 : 27 f5 fb          Mov r6, r0
         0x1f61be31df0f @   37 : 17 f8             PopContext r3
         0x1f61be31df11 @   39 : 27 fb fa          Mov r0, r1
  156 S> 0x1f61be31df14 @   42 : 12 04             LdaConstant [4]
         0x1f61be31df16 @   44 : 26 f7             Star r4
         0x1f61be31df18 @   46 : 25 fa             Ldar r1
  156 E> 0x1f61be31df1a @   48 : 65 fa f7 01 02    Construct r1, r4-r4, [2]
         0x1f61be31df1f @   53 : 26 f9             Star r2
  185 S> 0x1f61be31df21 @   55 : 28 f9 05 04       LdaNamedProperty r2, [5], [4]
         0x1f61be31df25 @   59 : 26 f8             Star r3
  185 E> 0x1f61be31df27 @   61 : 58 f8 f9 06       CallProperty0 r3, r2, [6]
         0x1f61be31df2b @   65 : 0d                LdaUndefined 
  196 S> 0x1f61be31df2c @   66 : a9                Return 
Constant pool (size = 6)
0x1f61be31de51: [FixedArray] in OldSpace
 - map: 0x1f61ea3807b1 <Map>
 - length: 6
           0: 0x1f61be31dd69 <ScopeInfo BLOCK_SCOPE [3]>
           1: 0x1f618190b089 <FixedArray[7]>
           2: 0x1f61be31dd91 <SharedFunctionInfo find_me_Person>
           3: 0x1f61be31dde9 <SharedFunctionInfo sayHello>
           4: 0x1f61be31dd01 <String[#2]: yo>
           5: 0x1f61be31d909 <String[#8]: sayHello>
Handler Table (size = 0)
[generated bytecode for function: find_me_Person]
Parameter count 2
Frame size 0
   64 E> 0x1f61be31e0b2 @    0 : a5                StackCheck 
   76 S> 0x1f61be31e0b3 @    1 : 25 02             Ldar a0
   86 E> 0x1f61be31e0b5 @    3 : 2d 03 00 00       StaNamedProperty <this>, [0], [0]
         0x1f61be31e0b9 @    7 : 0d                LdaUndefined 
   95 S> 0x1f61be31e0ba @    8 : a9                Return 
Constant pool (size = 1)
0x1f61be31e041: [FixedArray] in OldSpace
 - map: 0x1f61ea3807b1 <Map>
 - length: 1
           0: 0x1f61ea384059 <String[#4]: name>
Handler Table (size = 0)
yo

長得滿複雜的 bytecode,而重點大概是這一行:CallRuntime [DefineClass], r5-r8,總之就是把 function 載入以後呼叫這一行去定義 class。那如果把 class 的定義移到外面呢?

class find_me_Person {
  constructor(name){
    this.name = name
  }

  sayHello() {
    console.log(this.name)
  }
}

function find_me_test() {
  var p = new find_me_Person('yo')
  p.sayHello()
}

find_me_test()

結果:

[generated bytecode for function: find_me_test]
Parameter count 1
Frame size 24
  140 E> 0x28bfd949dec2 @    0 : a5                StackCheck 
  155 S> 0x28bfd949dec3 @    1 : 1a 04             LdaCurrentContextSlot [4]
         0x28bfd949dec5 @    3 : aa 00             ThrowReferenceErrorIfHole [0]
         0x28bfd949dec7 @    5 : 26 fa             Star r1
         0x28bfd949dec9 @    7 : 12 01             LdaConstant [1]
         0x28bfd949decb @    9 : 26 f9             Star r2
         0x28bfd949decd @   11 : 25 fa             Ldar r1
  155 E> 0x28bfd949decf @   13 : 65 fa f9 01 00    Construct r1, r2-r2, [0]
         0x28bfd949ded4 @   18 : 26 fb             Star r0
  184 S> 0x28bfd949ded6 @   20 : 28 fb 02 02       LdaNamedProperty r0, [2], [2]
         0x28bfd949deda @   24 : 26 fa             Star r1
  184 E> 0x28bfd949dedc @   26 : 58 fa fb 04       CallProperty0 r1, r0, [4]
         0x28bfd949dee0 @   30 : 0d                LdaUndefined 
  195 S> 0x28bfd949dee1 @   31 : a9                Return 
Constant pool (size = 3)
0x28bfd949de41: [FixedArray] in OldSpace
 - map: 0x28bfd59807b1 <Map>
 - length: 3
           0: 0x28bfd949d8c9 <String[#14]: find_me_Person>
           1: 0x28bfd949ddc1 <String[#2]: yo>
           2: 0x28bfd949d8e9 <String[#8]: sayHello>
Handler Table (size = 0)
[generated bytecode for function: find_me_Person]
Parameter count 2
Frame size 0
   36 E> 0x28bfd949e032 @    0 : a5                StackCheck 
   48 S> 0x28bfd949e033 @    1 : 25 02             Ldar a0
   58 E> 0x28bfd949e035 @    3 : 2d 03 00 00       StaNamedProperty <this>, [0], [0]
         0x28bfd949e039 @    7 : 0d                LdaUndefined 
   67 S> 0x28bfd949e03a @    8 : a9                Return 
Constant pool (size = 1)
0x28bfd949dfc1: [FixedArray] in OldSpace
 - map: 0x28bfd59807b1 <Map>
 - length: 1
           0: 0x28bfd5984059 <String[#4]: name>
Handler Table (size = 0)
yo

程式碼看起來乾淨許多,而且因為 find_me_Person 是宣告在 function 外面的緣故,所以要用 LdaCurrentContextSlot 從 context 裡面把這個東西給讀進來。

當我們把 class 宣告在外面的時候,雖然我們是寫 class find_me_Person,不過若是你有注意到,它依然被當作一個 function 來看待。

不過我還是很好奇到底是如何處理 class 的宣告,因此我們可以把 filter 拿掉,把所有 bytecode 印出來:

[generated bytecode for function: ]
Parameter count 1
Frame size 72
         0x2cf5f961dbe2 @    0 : 12 00             LdaConstant [0]
         0x2cf5f961dbe4 @    2 : 26 f9             Star r2
         0x2cf5f961dbe6 @    4 : 61 36 01 f9 01    CallRuntime [NewScriptContext], r2-r2
         0x2cf5f961dbeb @    9 : 16 f9             PushContext r2
         0x2cf5f961dbed @   11 : 0f                LdaTheHole 
         0x2cf5f961dbee @   12 : 1d 04             StaCurrentContextSlot [4]
         0x2cf5f961dbf0 @   14 : 12 01             LdaConstant [1]
         0x2cf5f961dbf2 @   16 : 26 f8             Star r3
         0x2cf5f961dbf4 @   18 : 0b                LdaZero 
         0x2cf5f961dbf5 @   19 : 26 f7             Star r4
         0x2cf5f961dbf7 @   21 : 27 fe f6          Mov <closure>, r5
         0x2cf5f961dbfa @   24 : 61 2d 01 f8 03    CallRuntime [DeclareGlobals], r3-r5
    0 E> 0x2cf5f961dbff @   29 : a5                StackCheck 
         0x2cf5f961dc00 @   30 : 82 02             CreateBlockContext [2]
         0x2cf5f961dc02 @   32 : 16 f8             PushContext r3
         0x2cf5f961dc04 @   34 : 0f                LdaTheHole 
         0x2cf5f961dc05 @   35 : 26 f4             Star r7
         0x2cf5f961dc07 @   37 : 81 04 03 00       CreateClosure [4], [3], #0
         0x2cf5f961dc0b @   41 : 26 f7             Star r4
         0x2cf5f961dc0d @   43 : 12 03             LdaConstant [3]
         0x2cf5f961dc0f @   45 : 26 f6             Star r5
         0x2cf5f961dc11 @   47 : 81 05 04 00       CreateClosure [5], [4], #0
         0x2cf5f961dc15 @   51 : 26 f3             Star r8
         0x2cf5f961dc17 @   53 : 27 f7 f5          Mov r4, r6
         0x2cf5f961dc1a @   56 : 61 27 00 f6 04    CallRuntime [DefineClass], r5-r8
         0x2cf5f961dc1f @   61 : 26 f6             Star r5
         0x2cf5f961dc21 @   63 : 27 f5 fb          Mov r6, r0
         0x2cf5f961dc24 @   66 : 17 f8             PopContext r3
         0x2cf5f961dc26 @   68 : 25 fb             Ldar r0
    0 E> 0x2cf5f961dc28 @   70 : 1d 04             StaCurrentContextSlot [4]
  198 S> 0x2cf5f961dc2a @   72 : 0d                LdaUndefined 
         0x2cf5f961dc2b @   73 : 26 f7             Star r4
         0x2cf5f961dc2d @   75 : 13 06 00          LdaGlobal [6], [0]
         0x2cf5f961dc30 @   78 : 26 f8             Star r3
  198 E> 0x2cf5f961dc32 @   80 : 5f f8 f7 01       CallNoFeedback r3, r4-r4
         0x2cf5f961dc36 @   84 : 26 fa             Star r1
  212 S> 0x2cf5f961dc38 @   86 : a9                Return 
Constant pool (size = 7)
0x2cf5f961db41: [FixedArray] in OldSpace
 - map: 0x2cf5190807b1 <Map>
 - length: 7
           0: 0x2cf5f961d939 <ScopeInfo SCRIPT_SCOPE [9]>
           1: 0x2cf5f961d9f9 <FixedArray[4]>
           2: 0x2cf5f961d991 <ScopeInfo BLOCK_SCOPE [4]>
           3: 0x2cf5a8b8b059 <FixedArray[7]>
           4: 0x2cf5f961da81 <SharedFunctionInfo find_me_Person>
           5: 0x2cf5f961dad9 <SharedFunctionInfo sayHello>
           6: 0x2cf5f961d901 <String[#12]: find_me_test>
Handler Table (size = 0)
[generated bytecode for function: find_me_test]
Parameter count 1
Frame size 24
  140 E> 0x2cf5f961dec2 @    0 : a5                StackCheck 
  155 S> 0x2cf5f961dec3 @    1 : 1a 04             LdaCurrentContextSlot [4]
         0x2cf5f961dec5 @    3 : aa 00             ThrowReferenceErrorIfHole [0]
         0x2cf5f961dec7 @    5 : 26 fa             Star r1
         0x2cf5f961dec9 @    7 : 12 01             LdaConstant [1]
         0x2cf5f961decb @    9 : 26 f9             Star r2
         0x2cf5f961decd @   11 : 25 fa             Ldar r1
  155 E> 0x2cf5f961decf @   13 : 65 fa f9 01 00    Construct r1, r2-r2, [0]
         0x2cf5f961ded4 @   18 : 26 fb             Star r0
  184 S> 0x2cf5f961ded6 @   20 : 28 fb 02 02       LdaNamedProperty r0, [2], [2]
         0x2cf5f961deda @   24 : 26 fa             Star r1
  184 E> 0x2cf5f961dedc @   26 : 58 fa fb 04       CallProperty0 r1, r0, [4]
         0x2cf5f961dee0 @   30 : 0d                LdaUndefined 
  195 S> 0x2cf5f961dee1 @   31 : a9                Return 
Constant pool (size = 3)
0x2cf5f961de41: [FixedArray] in OldSpace
 - map: 0x2cf5190807b1 <Map>
 - length: 3
           0: 0x2cf5f961d8c9 <String[#14]: find_me_Person>
           1: 0x2cf5f961ddc1 <String[#2]: yo>
           2: 0x2cf5f961d8e9 <String[#8]: sayHello>
Handler Table (size = 0)
[generated bytecode for function: find_me_Person]
Parameter count 2
Frame size 0
   36 E> 0x2cf5f961e032 @    0 : a5                StackCheck 
   48 S> 0x2cf5f961e033 @    1 : 25 02             Ldar a0
   58 E> 0x2cf5f961e035 @    3 : 2d 03 00 00       StaNamedProperty <this>, [0], [0]
         0x2cf5f961e039 @    7 : 0d                LdaUndefined 
   67 S> 0x2cf5f961e03a @    8 : a9                Return 
Constant pool (size = 1)
0x2cf5f961dfc1: [FixedArray] in OldSpace
 - map: 0x2cf5190807b1 <Map>
 - length: 1
           0: 0x2cf519084059 <String[#4]: name>
Handler Table (size = 0)
[generated bytecode for function: sayHello]
Parameter count 1
Frame size 24
   80 E> 0x2cf5f961e1c2 @    0 : a5                StackCheck 
   89 S> 0x2cf5f961e1c3 @    1 : 13 00 00          LdaGlobal [0], [0]
         0x2cf5f961e1c6 @    4 : 26 fa             Star r1
   97 E> 0x2cf5f961e1c8 @    6 : 28 fa 01 02       LdaNamedProperty r1, [1], [2]
         0x2cf5f961e1cc @   10 : 26 fb             Star r0
  106 E> 0x2cf5f961e1ce @   12 : 28 02 02 04       LdaNamedProperty <this>, [2], [4]
         0x2cf5f961e1d2 @   16 : 26 f9             Star r2
   97 E> 0x2cf5f961e1d4 @   18 : 59 fb fa f9 06    CallProperty1 r0, r1, r2, [6]
         0x2cf5f961e1d9 @   23 : 0d                LdaUndefined 
  114 S> 0x2cf5f961e1da @   24 : a9                Return 
Constant pool (size = 3)
0x2cf5f961e141: [FixedArray] in OldSpace
 - map: 0x2cf5190807b1 <Map>
 - length: 3
           0: 0x2cf5d1e100e9 <String[#7]: console>
           1: 0x2cf5d1e0fbe9 <String[#3]: log>
           2: 0x2cf519084059 <String[#4]: name>
Handler Table (size = 0)
yo

第一個沒有名字的 function bytecode 就是我們整個 script 了,可以看到裡面依然呼叫了 CallRuntime [DefineClass]。雖然常說 class 只是 prototype 的語法糖,但是在 bytecode 的層面上,兩個依然是不同的東西(或許在 TurboFan 裡面會再做處理)。

不過只研究到這邊我還不甘心,明明可以再繼續往下深入研究看看,因此我們可以先獨立看 bytecode 建立 class 的那一段:

LdaTheHole 
Star r7
CreateClosure [4], [3], #0
Star r4
LdaConstant [3]
Star r5
CreateClosure [5], [4], #0
Star r8
Mov r4, r6
CallRuntime [DefineClass], r5-r8

// r5: <FixedArray[7]>
// r6: <SharedFunctionInfo find_me_Person>
// r7: TheHole
// r8: <SharedFunctionInfo sayHello>

底下我把 r5-r8 的內容都整理出來了,會用這幾個參數去呼叫 DefineClass。那這個 DefineClass 到底是什麼呢?可以直接去 V8 的 source code 裡面搜尋一下,發現被定義在 src/runtime/runtime-classes.cc 裡面:

RUNTIME_FUNCTION(Runtime_DefineClass) {
  HandleScope scope(isolate);
  DCHECK_LE(ClassBoilerplate::kFirstDynamicArgumentIndex, args.length());
  CONVERT_ARG_HANDLE_CHECKED(ClassBoilerplate, class_boilerplate, 0);
  CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, 1);
  CONVERT_ARG_HANDLE_CHECKED(Object, super_class, 2);
  DCHECK_EQ(class_boilerplate->arguments_count(), args.length());

  RETURN_RESULT_OR_FAILURE(
      isolate,
      DefineClass(isolate, class_boilerplate, super_class, constructor, args));
}

從這邊就可以很明顯看出來我們傳進去的四個參數是如何被處理,第一個 <FixedArray[7]> 是 class_boilerplate,應該是建立 class 時需要的一些資訊,可以先不管它。第二個參數 <SharedFunctionInfo find_me_Person> 就是 constructor,第三個 TheHolesuper_class,最後一個 <SharedFunctionInfo sayHello> 則是額外的參數 args。

而 DefineClass 的詳細內容,也可以在上面一點的地方找到:

MaybeHandle<Object> DefineClass(Isolate* isolate,
                                Handle<ClassBoilerplate> class_boilerplate,
                                Handle<Object> super_class,
                                Handle<JSFunction> constructor,
                                Arguments& args) {
  Handle<Object> prototype_parent;
  Handle<Object> constructor_parent;

  if (super_class->IsTheHole(isolate)) {
    prototype_parent = isolate->initial_object_prototype();
  } else {
    if (super_class->IsNull(isolate)) {
      prototype_parent = isolate->factory()->null_value();
    } else if (super_class->IsConstructor()) {
      DCHECK(!super_class->IsJSFunction() ||
             !IsResumableFunction(
                 Handle<JSFunction>::cast(super_class)->shared()->kind()));
      ASSIGN_RETURN_ON_EXCEPTION(
          isolate, prototype_parent,
          Runtime::GetObjectProperty(isolate, super_class,
                                     isolate->factory()->prototype_string()),
          Object);
      if (!prototype_parent->IsNull(isolate) &&
          !prototype_parent->IsJSReceiver()) {
        THROW_NEW_ERROR(
            isolate, NewTypeError(MessageTemplate::kPrototypeParentNotAnObject,
                                  prototype_parent),
            Object);
      }
      // Create new handle to avoid |constructor_parent| corruption because of
      // |super_class| handle value overwriting via storing to
      // args[ClassBoilerplate::kPrototypeArgumentIndex] below.
      constructor_parent = handle(*super_class, isolate);
    } else {
      THROW_NEW_ERROR(isolate,
                      NewTypeError(MessageTemplate::kExtendsValueNotConstructor,
                                   super_class),
                      Object);
    }
  }

  Handle<JSObject> prototype = CreateClassPrototype(isolate);
  DCHECK_EQ(*constructor, args[ClassBoilerplate::kConstructorArgumentIndex]);
  args.set_at(ClassBoilerplate::kPrototypeArgumentIndex, *prototype);

  if (!InitClassConstructor(isolate, class_boilerplate, constructor_parent,
                            constructor, args) ||
      !InitClassPrototype(isolate, class_boilerplate, prototype,
                          prototype_parent, constructor, args)) {
    DCHECK(isolate->has_pending_exception());
    return MaybeHandle<Object>();
  }
  if (FLAG_trace_maps) {
    LOG(isolate,
        MapEvent("InitialMap", Map(), constructor->map(),
                 "init class constructor", constructor->shared()->DebugName()));
    LOG(isolate, MapEvent("InitialMap", Map(), prototype->map(),
                          "init class prototype"));
  }

  return prototype;
}

若是把一些不會進入的分支去掉,就會變成這樣:

MaybeHandle<Object> DefineClass(Isolate* isolate,
                                Handle<ClassBoilerplate> class_boilerplate,
                                Handle<Object> super_class,
                                Handle<JSFunction> constructor,
                                Arguments& args) {
  Handle<Object> prototype_parent;
  Handle<Object> constructor_parent;

  // 沒有 super class 的話,就把 prototype parent 設成 Object.prototype
  if (super_class->IsTheHole(isolate)) {
    prototype_parent = isolate->initial_object_prototype();
  }

  Handle<JSObject> prototype = CreateClassPrototype(isolate);
  DCHECK_EQ(*constructor, args[ClassBoilerplate::kConstructorArgumentIndex]);
  args.set_at(ClassBoilerplate::kPrototypeArgumentIndex, *prototype);

  // 初始化 ClassConstructor 與 ClassPrototype
  if (!InitClassConstructor(isolate, class_boilerplate, constructor_parent,
                            constructor, args) ||
      !InitClassPrototype(isolate, class_boilerplate, prototype,
                          prototype_parent, constructor, args)) {
    DCHECK(isolate->has_pending_exception());
    return MaybeHandle<Object>();
  }

  return prototype;
}

總而言之呢,有關 class 的所有東西都會在這邊被處理好,若是想要深入細節的話可以再往下研究,但是我功力有限,追到這邊就差不多了...於是只好就此打住,只要知道是在這邊被處理的就行了。

#javascript







Related Posts

JavaScript除錯

JavaScript除錯

給自己看的 JS 進階-Hoisting

給自己看的 JS 進階-Hoisting

Day00 系列介紹

Day00 系列介紹




Sponsored



Comments