sclass LASClassDef { settable S userGivenName; settable S classDefPrefix = "userCode."; replace Tb with GazelleV_LeftArrowScript. delegate FunctionDef, Evaluable to Tb. new L fields; new L methods; new L methodBodies; sclass FieldDef { settable S name; settable Class type; } S structForHash() { ret struct(litorderedmap(+userGivenName, +fields)); } simplyCached S classHash() { ret md5(structForHash()); } S finalClassName() { ret classDefPrefix() + finalClassNameWithoutPrefix(); } S finalClassNameWithoutPrefix() { ret or2(userGivenName, "C") + "_" + classHash(); } simplyCached byte[] toBytes() { ClassMaker classMaker = new(finalClassName()); for (field : fields) { FieldGen fg = new(Const.ACC_PUBLIC, classToBCELType(field.type), field.name, classMaker.getConstantPool()); classMaker.addField(fg); } for (method : methods) fullyCompileMethod(classMaker, method); classMaker.addDefaultConstructor(); ret classMaker.toBytes(); } // generate interpreted method (slower but always works) // like this: // // new VarContext ctx; // ctx.put("this", this); // ctx.put("arg1", arg1); // ctx.put("arg2", arg2); // ret methodBody.get(ctx); void semiCompileMethod(ClassMaker classMaker, FunctionDef method) { // make static field for method body later int iMethod = l(methodBodies); methodBodies.add(method); int nArgs = l(method.args); MethodMaker mm = new(classMaker, O.class, method.name, repArray(Class.class, O.class, nArgs)); int iThis = 0, iFirstArg = 1, iCtx = iFirstArg+nArgs; // new VarContext ctx; mm.newObject(VarContext); mm.astore(iCtx); // ctx.put("this", this); mm.aload(iCtx); mm.stringConstant("this"); mm.aload(iThis); mm.invokeVirtual(VarContext, void.class, "put", S, O); // ctx.put("arg1", arg1); for iArg to nArgs: { mm.aload(iCtx); mm.stringConstant(method.args[iArg]); mm.aload(iFirstArg+iArg); mm.invokeVirtual(VarContext, void.class, "put", S, O); } // ret methodBody.get(ctx); S bodyFieldName = "_body" + iMethod; mm.getStaticField(classMaker.className(), bodyFieldName, Evaluable); mm.aload(iCtx); mm.invokeVirtual(Evaluable, O, "get", VarContext); mm.areturn(); mm.done(); } // fully compile method to byte code (this works only for very simple methods) void fullyCompileMethod(ClassMaker classMaker, FunctionDef method) { MethodMaker mm = new(classMaker, O.class, method.name, repArray(Class.class, O.class, l(method.args))); var tbc = LASToByteCode(mm) { JVMStackCellType compileGetVar(Tb.GetVar code) { // load "this" if (eq(code.var, "this")) { mm.aload(0); ret JVMStackCellType.objValue; } // load method argument int iArg = indexOf(method.args, code.var); if (iArg >= 0) { mm.aload(iArg+1); ret JVMStackCellType.objValue; } ret super.compileGetVar(code); } }; tbc.postConversion = stackTop -> mm.convertToObject(stackTop); tbc.compileScript(method.body); mm.areturn(); mm.done(); } void init(Class c) { for iMethod over methodBodies: { S bodyFieldName = "_body" + iMethod; set(c, bodyFieldName, methodBodies.get(iMethod)); } } }