set flag AllPublic. // so we can access main call() // functions used in the generated code please include functions call preciseGetOrCallMethod set. srecord noeq LASToByteCode(MethodMaker m) { bool callPing = true; replace Tb with GazelleV_LeftArrowScript. // Short for "Toolbox" delegate Evaluable to Tb. // parameter index of VarContext settable int iVarContext = -1; delegate none, objValue, intValue, doubleValue to JVMStackCellType. delegate convertToObject, discardStackTop, here, loadNull to m. Tb.Script returnableScript; settable IF1 postConversion; JVMStackCellType compileScript(Tb.Script script) { returnableScript = script; var stackTop = compile(script); if (postConversion != null) stackTop = postConversion.get(stackTop); ret stackTop; } void compileToObject(Tb.Evaluable code) { convertToObject(compile(code)); } JVMStackCellType compile(Tb.Evaluable code) { if (code cast Tb.Const) { O o = code.value; if (o == null) { m.add(new ACONST_NULL); ret objValue; } else if (o cast S) { m.stringConstant(o); ret objValue; } else if (o cast Int) { m.intConstant(o); ret intValue; } else if (o cast Double) { m.doubleConstant(o); ret doubleValue; } else if (o cast Class) { m.classConstant(o); ret objValue; } else if (o cast Bool) { m.boolConstant(o); ret intValue; } else fail("Can't compile const value: " + toStringWithClass(o)); } else if (code cast Tb.Script) { var stackTop = none; for (step : code.steps) { // pop result of last instruction if any if (stackTop != none) m.add(new POP); stackTop = compile(step); } ret stackTop; } else if (code cast Tb.CallMethod) { // push target compileToObject(code.target); // push method name m.stringConstant(code.methodName); // push argument array argumentsAsArray(code.args); // call call() m.invokeStatic(mainFunctionHolder call, O, "call", O, S, O[].class); ret objValue; } else if (code cast Tb.CallMethodOrGetField) { // push target compileToObject(code.target); // push method/field name m.stringConstant(code.name); // call call() m.invokeStatic(mainFunctionHolder call, O, "preciseGetOrCallMethod", O, S); ret objValue; } else if (code cast Tb.SetField) { // push target compileToObject(code.target); // push field name m.stringConstant(code.name); // push value compileToObject(code.expr); // call set() m.invokeStatic(mainFunctionHolder set, O, "set", O, S, O); ret none; } else if (code cast Tb.NewObject) { // push class m.classConstant(code.c); argumentsAsArray(code.args); // call nuObject() m.invokeStatic(mainFunctionHolder nuObject, O, "nuObject", Class.class, O[].class); ret objValue; } else if (code cast Tb.Assignment) { // evaluate expression, store in temporary variable compileToObject(code.expression); m.astore(iTemp()); // Call VarContext.set() loadVarContext(); m.stringConstant(code.var); m.aload(iTemp()); m.invokeVirtual(VarContext, void.class, "set", S, O); m.aload(iTemp()); ret objValue; } else if (code cast Tb.While) { /* Simulating this: while (ping() && (Bool) condition.get(ctx)) { body.get(ctx); } null; */ // Mark loop start as jump target var loopStart = m.il.append(new NOP); // Call ping() unless disabled BranchInstruction branch1 = null; if (callPing) { m.invokeStatic(mainFunctionHolder ping, bool.class, "ping"); branch1 = new IFEQ(null); m.add(branch1); } // Evaluate condition compileToBool(code.condition); var branch2 = new IFEQ(null); m.add(branch2); // Evaluate body discardStackTop(compile(code.body)); // Jump to loop start m.add(new GOTO(loopStart)); // Update forward branches var loopEnd = m.il.append(new NOP); branch1?.setTarget(loopEnd); branch2.setTarget(loopEnd); ret none; } else if (code cast Tb.IfThen) { compileToBool(code.condition); var branch1 = new IFEQ(null); m.add(branch1); // Evaluate body var stackTop = compile(code.body); // body returns nothing? then we don't need an else branch if (stackTop == none) { branch1.setTarget(here()); ret stackTop; } else { // otherwise, convert to object and return null in else branch convertToObject(stackTop); var jumpToEnd = m.forwardGoto(); branch1.setTarget(here()); loadNull(); jumpToEnd.setTarget(m.here()); ret objValue; } } else if (code cast Tb.GetVar) { ret compileGetVar(code); } else if (code cast Tb.ReturnFromScript) { if (code.script != returnableScript) fail("Can only return from current script"); var stackTop = compile(code.value); if (postConversion != null) stackTop = postConversion.get(stackTop); m.returnWithType(stackTop); ret none; } fail("Can't compile yet: " + className(code)); } void argumentsAsArray(Evaluable[] args) { int n = l(args); m.intConst(n); m.add(new ANEWARRAY(m.classRef(O))); for iArg to n: { m.dup(); // get array again m.intConst(iArg); compileToObject(args[iArg]); m.add(new AASTORE); } } void loadVarContext { assertTrue("Need VarContext", iVarContext >= 0); m.aload(iVarContext); } // index of a temporary variable, created on demand simplyCached int iTemp() { ret m.newLocalVar(); } void compileToBool(Evaluable condition) { var stackTop = compile(condition); if (stackTop == objValue) { // cast to Boolean m.checkCast(Bool.class); // call Boolean.booleanValue() m.invokeVirtual(Bool.class, bool.class, "booleanValue"); } else if (stackTop == intValue) {} // ok else fail("Can't convert to bool: " + stackTop); } JVMStackCellType compileGetVar(Tb.GetVar code) { // Prepare VarContext.get() loadVarContext(); m.stringConstant(code.var); // call VarContext.get() m.invokeVirtual(VarContext, O, "get", S); ret objValue; } }