using compiler class StaticImportDsl : DslPlugin { new make(Compiler c) : super(c) {} override Expr compile(DslExpr expr) { loc := expr.loc qnames := expr.src.splitLines.map { it.trim }.exclude { it->size == 0 } type := findType(loc) qnames.each { createMethod(it, type, loc) } staticImportType := ns.resolveType("staticImport::StaticImport") typeRef := StaticTargetExpr.make(loc, staticImportType) return CallExpr.makeWithMethod(loc, typeRef, staticImportType.method("make")) } private Void createMethod(Str qname, TypeDef ourType, Loc loc) { echo("created method") slot := ns.resolveSlot(qname) newQname := "${ourType.qname}.$slot.name" compiler.errs = compiler.errs.exclude { it.msg == "Unknown method '$newQname'" } type := slot.parent if(!(slot is CMethod)) { throw err("Invalid qname", loc) } CMethod sm := slot m := MethodDef.make(loc, ourType, slot.name, FConst.Private + FConst.Static) m.ret = sm.returnType m.paramDefs = sm.params.map |CParam p -> ParamDef| { ParamDef.make(loc, p.paramType, p.name) } code := Block.make(loc) reg := 0 m.vars = m.paramDefs.map { MethodVar.makeForParam(m, reg++, it, it.paramType) } args := m.vars.map { LocalVarExpr.make(loc, it) } code.add(ReturnStmt.make(loc, CallExpr.makeWithMethod(loc, StaticTargetExpr.make(loc, type), slot, args))) m.code = code ourType.addSlot(m) //update call exprs updateCalls(findUnit(loc), m, loc) } private Void updateCalls(CompilationUnit unit, MethodDef newMethod, Loc loc) { unit.types.each |td| { td.methodDefs.each |md| { md.code.stmts.each |stmt| { if(stmt isnot ExprStmt) return expr := (stmt as ExprStmt).expr if(expr isnot CallExpr) return adjustCall(expr, newMethod, loc) } } } } private Void adjustCall(CallExpr e, MethodDef newMethod, Loc loc) { if(e.name == newMethod.name && (e.target is ThisExpr || e.target == null)) { echo("replacement found") e.method = newMethod e.target = StaticTargetExpr.make(loc, newMethod.parent) e.target.ctype = newMethod.parent } if(e.target is CallExpr) adjustCall(e.target, newMethod, loc) e.args.each { if(it is CallExpr) adjustCall(it, newMethod, loc) } } private TypeDef? findType(Loc loc) { findUnit(loc).types.first } private CompilationUnit findUnit(Loc loc) { units.find |unit| { unit.loc.file == loc.file } } }