New direction
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
declass: class form -> prototype form
|
||||
|
||||
Plain text. Read it next to class_form.txt (the input) and proto_form.txt (the target).
|
||||
Every instruction below is copied in the same dump format those files use.
|
||||
|
||||
The class in function "test" has this cpool:
|
||||
|
||||
cpool: [ FnById(2), FnById(3), FnById(4) ]
|
||||
|
||||
Const(0) = FnById(2) = the m1 method
|
||||
Const(1) = FnById(3) = the constructor
|
||||
Const(2) = FnById(4) = the fields initializer
|
||||
|
||||
|
||||
====================================================================
|
||||
STEP 1 in function "test": rewrite how the class is built
|
||||
====================================================================
|
||||
|
||||
DETECT, abstractly:
|
||||
|
||||
push_const8 #ctor ; define_class @Name
|
||||
( fclosure8 #method ; define_method @meth )*
|
||||
( fclosure8 #fields ; set_home_object ; put_loc ; drop )?
|
||||
|
||||
which in the example is:
|
||||
|
||||
[5] = PushConst8 Const(1) [],
|
||||
[6, 7] = DefineClass AtomU8 { atom: file_atom[2], arg: 0 } [5, 4],
|
||||
[8] = Fclosure8 Const(0) [],
|
||||
[9] = DefineMethod AtomU8 { atom: file_atom[5], arg: 0 } [8, 7],
|
||||
[10] = Fclosure8 Const(2) [],
|
||||
[11, 12] = SetHomeObject None [10, 9],
|
||||
[] = PutLoc1 None [12],
|
||||
[] = Drop None [11],
|
||||
|
||||
REPLACE it with:
|
||||
|
||||
[6] = Fclosure8 Const(1) [],
|
||||
[8] = Fclosure8 Const(0) [],
|
||||
[18] = GetField Atom(atom("prototype")) [6],
|
||||
[] = PutField Atom(file_atom[5]) [8, 18],
|
||||
|
||||
INSTRUCTION BY INSTRUCTION:
|
||||
|
||||
PushConst8 Const(1) + DefineClass [5,4] -> Fclosure8 Const(1), keep dst [6]
|
||||
Fclosure8 Const(0) -> unchanged (the method closure)
|
||||
DefineMethod atom=A [8,7] -> GetField "prototype" [6]
|
||||
then PutField atom=A [8,18]
|
||||
Fclosure8 Const(2) -> deleted
|
||||
SetHomeObject -> deleted
|
||||
PutLoc1 [12] -> deleted
|
||||
Drop [11] -> deleted
|
||||
|
||||
[18] is any fresh value id.
|
||||
Repeat the DefineMethod line for each method in the class.
|
||||
|
||||
REMEMBER for step 2 and 3:
|
||||
|
||||
constructor function = Const(1) = FnById(3)
|
||||
fields function = Const(2) = FnById(4)
|
||||
class name = file_atom[2]
|
||||
|
||||
|
||||
====================================================================
|
||||
STEP 2 splice the field initialiser into the constructor FnById(3)
|
||||
====================================================================
|
||||
|
||||
The constructor does not set its own fields. quickjs hoisted them into a
|
||||
separate initialiser function (FnById(4)) that the constructor reaches through
|
||||
the captured <class_fields_init> and runs with a call. Splice that function's
|
||||
field writes straight into the constructor, in place of the call, so the
|
||||
constructor initialises its fields inline and the separate initialiser is left
|
||||
unused.
|
||||
|
||||
DETECT the call block, abstractly (from the get_var_ref_check of
|
||||
<class_fields_init> through the next call_method):
|
||||
|
||||
get_var_ref_check <class_fields_init> ; dup ; if_false
|
||||
; get_loc ; swap ; call_method
|
||||
|
||||
which in the example is:
|
||||
|
||||
[2] = GetVarRefCheck VarRef(0) [],
|
||||
[3, 4] = Dup None [2],
|
||||
[] = IfFalse8 Label { target: 14 } [4],
|
||||
[5] = GetLoc0 None [],
|
||||
[6, 7] = Swap None [5, 3],
|
||||
[8] = CallMethod Npop(0) [7, 6],
|
||||
|
||||
REPLACE the call block by splicing in the field writes from the initialiser
|
||||
FnById(4):
|
||||
|
||||
[9] = GetLoc0 None [],
|
||||
[10] = Push1 None [],
|
||||
[11] = DefineField Atom(file_atom[4]) [10, 9],
|
||||
|
||||
The initialiser FnById(4) in full looks like this:
|
||||
|
||||
[2] = SpecialObject U8(4) [],
|
||||
[] = PutLoc1 None [2],
|
||||
[3] = PushThis None [],
|
||||
[] = PutLoc0 None [3],
|
||||
[] = Goto8 Label { target: 7 } [],
|
||||
[4] = GetLoc0 None [],
|
||||
[5] = Push1 None [],
|
||||
[6] = DefineField Atom(file_atom[4]) [5, 4],
|
||||
[] = ReturnUndef None [],
|
||||
|
||||
HOW THE SPLICE IS BUILT:
|
||||
|
||||
Take the initialiser FnById(4). Drop these opcodes:
|
||||
SpecialObject, PushThis, PutLoc0, PutLoc1, PutLoc2, PutLoc3,
|
||||
Goto8, Goto, ReturnUndef
|
||||
What is left is the field writes; these are what gets spliced in.
|
||||
Renumber their value ids to fresh ids in the constructor: 4,5,6 -> 9,10,11.
|
||||
The constructor's own PushThis / PutLoc0 / CheckCtor prologue and its
|
||||
final ReturnUndef stay as they were, wrapping the spliced-in writes.
|
||||
|
||||
|
||||
====================================================================
|
||||
STEP 3 in the constructor FnById(3): fix its header
|
||||
====================================================================
|
||||
|
||||
DETECT, abstractly:
|
||||
|
||||
a constructor (name "<null>") that captures <class_fields_init>
|
||||
|
||||
which in the example is:
|
||||
|
||||
name: "<null>",
|
||||
captures: [ Capture { name: "<class_fields_init>", ... } ],
|
||||
|
||||
REPLACE with:
|
||||
|
||||
name: "Counter",
|
||||
captures: [],
|
||||
|
||||
name becomes the class name from step 1.
|
||||
Remove the <class_fields_init> capture.
|
||||
If other captures remain after it, subtract 1 from every VarRef(n)
|
||||
whose n is greater than the removed capture index.
|
||||
|
||||
|
||||
====================================================================
|
||||
ELIMINATED (opcodes this pass removes from live code)
|
||||
====================================================================
|
||||
|
||||
Step 1 removes these from the class build:
|
||||
|
||||
DefineClass replaced by Fclosure8
|
||||
DefineMethod replaced by GetField + PutField
|
||||
SetHomeObject deleted
|
||||
PushConst8 the class-template one, folded into Fclosure8
|
||||
Fclosure8 the fields-init one, deleted
|
||||
PutLoc1 the SetHomeObject leftover store, deleted
|
||||
Drop the SetHomeObject leftover drop, deleted
|
||||
|
||||
Step 2 removes these from the constructor (the field-init call block):
|
||||
|
||||
GetVarRefCheck of the <class_fields_init> capture
|
||||
Dup
|
||||
IfFalse8
|
||||
GetLoc0 the one feeding the call
|
||||
Swap
|
||||
CallMethod
|
||||
|
||||
After the pass, these three class-only opcodes never appear again anywhere:
|
||||
|
||||
DefineClass
|
||||
DefineMethod
|
||||
SetHomeObject
|
||||
|
||||
The rest (PushConst8, Fclosure8, GetVarRefCheck, Dup, IfFalse8, GetLoc0,
|
||||
Swap, CallMethod, PutLoc1, Drop) still occur elsewhere for ordinary reasons.
|
||||
Only the specific class-pattern instances listed above are removed.
|
||||
|
||||
|
||||
====================================================================
|
||||
NOT TOUCHED (passed through unchanged)
|
||||
====================================================================
|
||||
|
||||
DefineClass AtomU8 { arg: 1 } derived class
|
||||
DefineMethod AtomU8 { arg: 1 } getter
|
||||
DefineMethod AtomU8 { arg: 2 } setter
|
||||
DefineMethodComputed ... computed method name
|
||||
DefineClassComputed ... computed class name
|
||||
|
||||
|
||||
====================================================================
|
||||
LEFT BEHIND (removed by a later pass)
|
||||
====================================================================
|
||||
|
||||
FnById(4), the fields function. Nothing references it anymore.
|
||||
Reference in New Issue
Block a user