//
// Copyright (c) 2006, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
// 16 Jan 06 Brian Frank Creation
// 2 Oct 06 Brian Frank Ported from Java to Fan
//
**
** During the Parse step we created a list of all the closures.
** In InitClosures we map each ClosureExpr into a TypeDef as
** an anonymous class, then we map ClosureExpr.substitute to
** call the constructor anonymous class.
**
class InitClosures : CompilerStep
{
//////////////////////////////////////////////////////////////////////////
// Constructor
//////////////////////////////////////////////////////////////////////////
new make(Compiler compiler)
: super(compiler)
{
}
//////////////////////////////////////////////////////////////////////////
// Run
//////////////////////////////////////////////////////////////////////////
override Void run()
{
log.debug("Closures")
compiler.closures.each |ClosureExpr c| { process(c) }
}
//////////////////////////////////////////////////////////////////////////
// Process
//////////////////////////////////////////////////////////////////////////
private Void process(ClosureExpr c)
{
// class Class$Method$Num : |A,B..->R|
// {
// new $make() {}
// Bool isImmutable() { return true }
// Obj? call(Obj? a, Obj? b, ...) { doCall((A)a, ...) }
// R doCall(A a, B b, ...) { closure.code }
// }
setup(c) // setup our fields to process this closure
genClass // generate anonymous implementation class
genCtor // generate make()
genDoCall // generate doCall(...)
genCall // generate call(...) { doCall(...) }
substitute // substitute closure code with anonymous class ctor
}
//////////////////////////////////////////////////////////////////////////
// Setup
//////////////////////////////////////////////////////////////////////////
private Void setup(ClosureExpr c)
{
this.closure = c
this.loc = c.loc
this.signature = c.signature
this.enclosingType = c.enclosingType
}
//////////////////////////////////////////////////////////////////////////
// Generate Class
//////////////////////////////////////////////////////////////////////////
private Void genClass()
{
cls = TypeDef(ns, loc, closure.enclosingType.unit, closure.name)
cls.flags = FConst.Internal + FConst.Final + FConst.Synthetic
cls.base = closure.signature
cls.closure = closure
closure.cls = cls
addTypeDef(cls)
}
//////////////////////////////////////////////////////////////////////////
// Generate Constructor
//////////////////////////////////////////////////////////////////////////
private Void genCtor()
{
code := Block(loc)
code.stmts.add(ReturnStmt.makeSynthetic(loc))
ctor = MethodDef(loc, cls)
ctor.flags = FConst.Internal + FConst.Ctor + FConst.Synthetic
ctor.name = "make"
ctor.ret = ns.voidType
ctor.code = code
cls.addSlot(ctor)
}
//////////////////////////////////////////////////////////////////////////
// Generate doCall()
//////////////////////////////////////////////////////////////////////////
private Void genDoCall()
{
doCall = MethodDef(loc, cls)
doCall.name = "doCall"
doCall.flags = FConst.Internal + FConst.Synthetic
doCall.code = closure.code
doCall.ret = signature.ret
doCall.paramDefs = signature.toParamDefs(loc)
closure.doCall = doCall
cls.addSlot(doCall)
}
//////////////////////////////////////////////////////////////////////////
// Generate call()
//////////////////////////////////////////////////////////////////////////
private Void genCall()
{
c := CallExpr.makeWithMethod(loc, ThisExpr(loc), doCall)
genMethodCall(compiler, loc, cls, signature, c, false)
}
**
** This method overrides either call(List) or callx(A...) to push the
** args onto the stack, then redirect to the specified CallExpr c.
** We share this code for both closures and curries.
**
static MethodDef genMethodCall(Compiler compiler, Loc loc, TypeDef parent,
FuncType signature, CallExpr c, Bool firstAsTarget)
{
ns := compiler.ns
// method def
m := MethodDef(loc, parent)
m.flags = FConst.Override + FConst.Synthetic
m.ret = ns.objType.toNullable
m.code = Block(loc)
// signature:
// callList(List) // if > MaxIndirectParams
// call(Obj p0, Obj p1, ...) // if <= MaxIndirectParams
paramCount := signature.params.size
useListArgs := paramCount > 8
if (useListArgs)
{
m.name = "callList"
p := ParamDef(loc, ns.objType.toListOf, "list")
m.params.add(p)
}
else
{
m.name = "call"
paramCount.times |Int i|
{
p := ParamDef(loc, ns.objType.toNullable, "p$i")
m.paramDefs.add(p)
}
}
// init the doCall() expr with arguments
paramCount.times |Int i|
{
Expr? arg
// list.get(<i>)
if (useListArgs)
{
listGet := CallExpr.makeWithMethod(loc, UnknownVarExpr(loc, null, "list"), ns.objType.toListOf.method("get"))
listGet.args.add(LiteralExpr(loc, ExprId.intLiteral, ns.intType, i))
arg = listGet
}
// p<i>
else
{
arg = UnknownVarExpr(loc, null, "p$i")
}
// cast to closure param type
arg = TypeCheckExpr.coerce(arg, signature.params[i])
// add to doCall() argument list
if (firstAsTarget && i == 0)
c.target = arg
else
c.args.add(arg)
}
// return:
// doCall; return null; // if doCall() is void
// return doCall // if doCall() non-void
if (signature.ret.isVoid)
{
m.code.add(c.toStmt)
m.code.add(ReturnStmt.makeSynthetic(loc, LiteralExpr.makeNull(loc, ns)))
}
else
{
m.code.add(ReturnStmt.makeSynthetic(loc, c))
}
// add to our synthetic parent class
parent.addSlot(m)
if (parent.isClosure) parent.closure.call = m
return m
}
//////////////////////////////////////////////////////////////////////////
// Substitute
//////////////////////////////////////////////////////////////////////////
**
** Generate in-place subtitution of closure:
** |->| { ... } => Closure$Cls.make()
**
private Void substitute()
{
closure.code = null
closure.substitute = CallExpr.makeWithMethod(loc, null, ctor)
}
//////////////////////////////////////////////////////////////////////////
// Fields
//////////////////////////////////////////////////////////////////////////
ClosureExpr? closure // current closure
Loc? loc // closure.loc
FuncType? signature // closure.sig
TypeDef? enclosingType // closure.enclosingType
TypeDef? cls // current anonymous class implementing closure
MethodDef? ctor // anonymous class make ctor
MethodDef? doCall // R doCall(A a, ...) { closure.code }
}