sclass LASClassDef { settable S userGivenName; settable S classDefPrefix = "userCode."; settable bool fullCompilation; settable bool verbose; 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 Type type; } S structForHash() { new structure_Data d; d.shouldIncludeField = field -> { S c = shortClassName(field.getDeclaringClass()); S f = field.getName(); ret !(eq(c, "HasTokenRange") && eq(f, "src")); }; ret struct(d, litorderedmap(+userGivenName, +fields, +methods, +fullCompilation)); } simplyCached S classHash() { S struct = structForHash(); if (verbose) print(structForHash := struct); ret md5(struct); } S finalClassName() { ret classDefPrefix() + finalClassNameWithoutPrefix(); } S finalClassNameWithoutPrefix() { ret or2(userGivenName, "C") + "_" + classHash(); } simplyCached byte[] toBytes() { ClassMaker classMaker = new(finalClassName()); var cp = classMaker.getConstantPool(); for (field : fields) { var type = field.type; var fg = new FieldGen( Const.ACC_PUBLIC, typeToBCELType(type), field.name, cp); if (type cast ParameterizedType) fg.addAttribute(new org.apache.bcel.classfile.Signature( cp.addUtf8("Signature"), 2, cp.addUtf8(typeToVMSignature(type)), cp.getConstantPool())); classMaker.addField(fg); } for (method : methods) if (fullCompilation) fullyCompileMethod(classMaker, method); else semiCompileMethod(classMaker, method); classMaker.addDefaultConstructor(); ret classMaker.toBytes(); } // generate interpreted method (slower but always works) // like this: // // new FlexibleVarContext 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); S bodyFieldName = "_body" + iMethod; classMaker.addField(new FieldGen( Const.ACC_PUBLIC | Const.ACC_STATIC, classToBCELType(Evaluable.class), bodyFieldName, classMaker.getConstantPool())); 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 FlexibleVarContext ctx; mm.newObject(FlexibleVarContext); 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); mm.getStaticField(classMaker.className(), bodyFieldName, Evaluable.class); mm.aload(iCtx); mm.invokeInterface(Evaluable.class, 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).body); } } }