Fantom

 

class compiler::ClosureVars

sys::Obj
  compiler::CompilerSupport
    compiler::CompilerStep
      compiler::ClosureVars

Source

ClosureVars is used to process closure variables which have been enclosed from their parent scope:

ResolveExpr ----------- ResolveExpr we detected variables used from parent scope and created shadow variables in the closure's scope with a reference via MethodVar.shadows. Also during this step we note any variables which are reassigned making them non-final (according to Java final variable semantics).

Process Method -------------- First we walk all types looking for methods which use closure variables:

  1. For each one walk thru its variables to see if any variables enclosed are non-final (reassigned at some point). These variables as hoisted onto the heap with wrappers:
    class Wrapper$T { new make(T v) { val=v }  T val }
  2. If no wrapped variables, then we can leave a cvars method alone - everything stays the same. If however we do have wrapped variables, then we need to walk the expr tree of the method replacing all access of the variable with its wrapper access:
    x := 3     =>   x := Wrapper$Int(3)
    x = x + 1  =>   x.val = x.val + 1
  3. If any params were wrapped, we generated a new local variable in wrapNonFinalVars. During the expr tree walk we replaced all references to the param to its new wrapped local. To finish processing the method we insert a bit of code in the beginning of the method to initialize the local.

Process Closure --------------- After we have walked all methods using closure variables (which might include closure doCall methods themselves), then we walk all the closures.

  1. For each shadowed variables we need:
    1. Define field on the closure to store variable
    2. Pass variable to closure constructor at substitution site
    3. Add variable to as closure constructor param
    4. Assign param to field in constructor If the variable has been wrapped we are doing this for the wrapped variable (we don't unwrap it).
  2. If any of the closures shadowed variables are wrapped, then we do a expr tree walk of doCall - the exact same thing as step 2 of the processMethod stage.
genWrapper

const static CField genWrapper(CompilerSupport cs, CType ctype)

Source

Given a variable type, generate a wrapper class of the format:

class Wrap$ctype[$n] { CType val }

Wrappers are used to manage variables on the heap so that they can be shared between methods and closures. We generate one wrapper class per variable type per pod with potentially a non-nullable and nullable variant ($n suffix).

Eventually we'd probably like to share wrappers for common types like Int, Str, Obj, etc.

Return the val field of the wrapper.

make

new make(Compiler compiler)

Source

makeOuterThisField

const static CField makeOuterThisField(ClosureExpr closure)

Source

This method is called by ClosureExpr to auto-generate the implicit outer "this" field in the Closure's implementation class:

  1. Add $this field to closure's anonymous class
  2. Add $this param to closure's make constructor
  3. Pass this to closure constructor at substitute site
  4. Set field from param in constructor
run

virtual override Void run()

Source

visitExpr

virtual override Expr visitExpr(Expr expr)

Source

visitStmt

virtual override Stmt[]? visitStmt(Stmt stmt)

Source