1 Method Declarations
這回不從comipler開始,從runtime開始。
GC也需要follow
接下來難點在于如何填充這些表
2 Compiling method declarations
難點:
一個類可以聲明任意數量的方法。運行時需要查找并綁定所有這些方法。如果將這些方法都打包到一條 OP_CLASS 指令中,工作量會非常大。
解決方案
編譯器會發出一條 OP_CLASS 指令,創建一個新的空 ObjClass 對象。然后,編譯器會發出指令,將該類存儲到一個帶有其名稱的變量中。
這里有點像Closure。 emit OP_CLOSURE后,把upvalue 的內容放到這個指令之后。
Method也是。和OP_METHOD是分開的指令。
3 定義Method
現在,對于每個方法聲明,我們都會發出一條新的 OP_METHOD 指令,為該類添加一個方法。當所有 OP_METHOD 指令都執行完畢后,我們就得到了一個完整的類。在用戶看來,類的聲明是一個單一的atomic操作,而虛擬機則將其作為一系列mutation來實現。
3.1 具體實現
identifier 放到 constant里。把constant的index當做OP_METHOD的operand
function函數會 compile 函數parameters 還有body
然后emit ObjClosure
3.2 去哪找class?然后bind method?
Class的定義可能在stack上,可能在global。
因此
編譯時記錄下class的名字
當編譯method以前,先將這個class 調出來放到棧上,namedVariable可以來做這件事情
----
defineVariable也會做這件事(global),但是下一章這兩個call之間有東西,所以不這么干
----
所以編譯完method后要pop出來那個class
3.3 給一個例子
回頭看下為什么OP_CLOSURE之后會pop出來
-----
這OP_CLOSURE 會把function 放到stack。
OP_METHOD會把Closure pop出來?
右側是執行完左側后 stack的樣子。
-----
3.4 運行時,OP_METHOD
之后會把closure pop出來。
4 Method References
兩種情況都要支持
4.1 第一步
需要用“.”來得到method
但要記住,這里需要bind instance的state
舉例,這里應該打出來 jane
jlox中,用的是heap allcoated Environment class。
但是clox,有臨時變量在stack,還有在global的table中的,還有在closure的upvalue。
所以需要一個新的class
5 Bound Method(來自 CPython)
receiver 其實只能是ObjInstance。但是這里為了兼容更多函數,所以用了這個
配套代碼
這樣可以確保method句柄在內存中保留receiver,以便以后調用句柄時仍能找到對象。我們還會跟蹤method closure。
隱藏實現,所以類似打印function
這是最后一個runtime Obj tyep 牛逼
6 Accessing Methods
改下OP_GET_PROPERTY
?
---
原來的
----
具體,先找method,如果不在就報錯。
如果有,就bind method
然后把instance pop 出去,把method放到棧上。
7 bind 的例子
把instance 和 ObjClosure 結合
8 Calling Method?
---
---
目前可以run這樣的函數了
但是目前沒有用instance 的狀態
8 使用This
把這個this 當作局部變量,這樣可以復用很多處理。closure也可以用
false的意思是 是不是canAssign 。這里永遠是false。
variable函數不在乎this 有自己的token 或者不是identifier
要考慮instance 什么時候要進入到memory
至少要在capture closure 以前,clox把所有local variable 在stack上。
每個參數會給自己在stack上有個占位。empty string
那個位置是為method留著的。那里會存this ,也就是instance
這個例子
應該打印 Nested instance
8.1 為此添加
runtime的時候, receiver不在slot zero。要這樣修一下,那里原來是什么?
如果是call 的話, 那里原來是 fn function。現在換成了instance
8.2 this 的錯誤使用
全局放個currentClass
這個類用于 放完nearest enclosing function 又來看繼承
我們創建一個鏈表,用來串起來所有nest class
編譯class時,會隱式的把當前的放到linked stack上
這個classCompiler或者C的stack上。
結束時,pop他,c 會自動回收他
這里就可以檢查是否在class 里面了。
9 Instance Initializers
對initializer的要求
大概這個邏輯
9.1 invoke initializer
例子
init() 方法的新 CallFrame 共享該堆棧窗口,因此這些參數會隱式地轉發給初始化器。
init()后面時"egg" 還有“coffee”
如果沒有定義init函數,但是傳進了參數,就報錯
因為每次創建instance 都要找init ,所有就提前創建這個string,就可以用intern string了
這里有個潛在的bug就是copyString 可能調用GC,這時訪問他就掛了。
所以要給個初值。
9.2 initializer return values
確保initializer 會返回新的instance,而不是nil或者其他東西。
只要初始化方法返回,用戶調用類創建實例的過程就會結束,并在堆棧中留下初始化方法的任何值。這意味著,除非用戶在初始化方法的末尾加上 return this;,否則不會有實例產生。這并不是很有用。
為了解決這個,單獨為initializer設置一個類型。單獨處理。
9.3 Incorrect returns ini initializers
防止他提前返回
10?Optimized Invocations
method invocation= accessing the method + calling the result
現狀支持
?但是效果不好
但是,總是將這些操作作為單獨的操作執行會帶來很大的代價。每次 Lox 程序訪問和調用一個方法時,運行時堆都會分配一個新的 ObjBoundMethod,初始化其字段,然后再將其拉出。之后,GC 必須花費時間釋放所有這些短暫的綁定方法。ObjBoundMethod 不用每次都new 吧
實際上,Compiler可以看到訪問method 然后立即call it
所以可以有個單獨的指令 來完成這兩個操作
---
如果看下bytecode的運行情況,發現有很多OP_XXX都是成組重復出現。
可以把這些instruction組合起來,叫superinstruction?
影響表現表現最大的overhead之一,就是decoding and dispatching each instruction
還不能加太多instruction,否則可能decoding變慢。
----
10.1 代碼這樣改
OP_INVOKE
? 有兩個operand
-
The index of the property name in the constant table.
-
The number of arguments passed to the method.
假設invoke成功,最后更新frame
invoke()函數,先得到instance。要檢查下。
但是好像并沒有bind?
這里都不用bind。因為receiver and method都在正確的位置,之后舉個例子
快了好多,10s能夠call的次數
10.2 如果是field 有點問題
這里和invoke處理的稍微不同。這里的. 是access field。這個filed 被賦值為一個函數。這個field本身不是method。
---
有時有些錯誤,但是算的很快也可以接受。這是Monte Carlo algorithms.
---
這樣修改
在找method之前,先找field。
如果找到了,就把他放到receiver的位置。因為他不是medthod,所以不要instance。
invoke的總結
11 language design
?I believe this is one of the fundamental balancing acts of language design: similarity to other languages lowers learning cost, while divergence raises the compelling advantages.
novelty budget,?idiosyncrasy credit,
作者牛逼