sclass LASClassDef extends HasTokenRangeWithSrc { settable S userGivenName; settable S classDefPrefix = "userCode."; settable bool fullCompilation; settable bool verbose = true; replace Tb with GazelleV_LeftArrowScript. delegate FunctionDef, Evaluable, Script to Tb. settable Type superClass = O.class; new L fields; new L methods; new L methodBodies; new L interfaces; FunctionDef initializerMethod; new L initializers; new BitSet isFieldInitializer; sclass FieldDef { settable S name; settable Type type; settable Evaluable initializer; } S structForHash() { ret Tb.scriptStruct(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(), className(typeToClass(superClass)), mapToStringArray(interfaces, i -> className(typeToClass(i)))); 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); } if (nempty(initializers)) { methods.add(initializerMethod = new FunctionDef("$initFields", new S[0], new Script(initializers)).returnType(void.class)); if (!hasUserDefinedDefaultConstructor()) methods.add(new FunctionDef("", new S[0], new Script(emptyList())).returnType(void.class)); } for (method : methods) if (fullCompilation) fullyCompileMethod(classMaker, method); else semiCompileMethod(classMaker, method); if (!hasUserDefinedDefaultConstructor()) classMaker.addDefaultConstructor(); if (srcRef() != null) classMaker.addField(new FieldGen( Const.ACC_PUBLIC | Const.ACC_STATIC, classToBCELType(TokenRangeWithSrc), srcRefField(), classMaker.getConstantPool())); ret classMaker.toBytes(); } TokenRangeWithSrc srcRef() { ret tokenRangeWithSrc(); } void addToInitializerMethod(FieldDef field) { isFieldInitializer.set(l(initializers)); initializers.add( new Tb.SetField(new Tb.GetVar("this"), field.name, field.initializer)); } bool hasUserDefinedDefaultConstructor() { ret any(methods, m -> m.isConstructor() && empty(m.args)); } // 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.cg(), Const.ACC_PUBLIC, typeToClassName(method.returnType), method.name, /*repArray(Class.class, O.class, nArgs)*/ repArray(S.class, className(O), nArgs)); // TODO: proper argument types! int iThis = 0, iFirstArg = 1, iCtx = iFirstArg+nArgs; // If we are compiling a constructor, call the superclass constructor and $initFields if (method.isConstructor()) { mm.aload(iThis); mm.invokeConstructor(typeToClass(superClass)); if (initializerMethod != null) { mm.aload(iThis); mm.il.append(mm.factory.createInvoke(classMaker.className(), initializerMethod.name, mm.wrapType(void.class), mm.wrapTypes(new Class[0]), Const.INVOKESPECIAL)); } } // 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); if (method.returnType() == void.class) mm.return(); else { if (method.returnType() != O) mm.checkCast(typeToClass(method.returnType())); mm.areturn(); } mm.done(); //print("Made method " + mm.mg.getMethod()); } // fully compile method to byte code (this works only for very simple methods) void fullyCompileMethod(ClassMaker classMaker, FunctionDef method) { MethodMaker mm = new(classMaker, typeToClass(method.returnType()), 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); } setOpt(c, srcRefField(), srcRef()); } ResolvableLASClass resolvable(ILASClassLoader lasClassLoader) { ret new ResolvableLASClass(lasClassLoader, this); } void addField(FieldDef field) { fields.add(field); if (field.initializer != null) addToInitializerMethod(field); } S srcRefField() { ret "__srcRef"; } }