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.
