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. transient Class resolvedClass; settable Type superClass = O.class; new L fields; new Map fieldsByName; new L methods; new MultiMap methodsByName; new L methodBodies; new L interfaces; FunctionDef initializerMethod; new L initializers; new BitSet isFieldInitializer; //new L staticInitializers; new LPair staticFieldInitializers; sclass FieldDef { settable S name; settable Type type; settable Evaluable initializer; Set modifiers; transient FieldGen bcel; bool hasModifier(S modifier) { ret contains(modifiers, modifier); } void addModifier(S modifier) { modifiers = createOrAddToSet(modifiers, modifier); } bool isStatic() { ret hasModifier("static"); } } 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); fg.isTransient(field.hasModifier("transient")); fg.isStatic(field.isStatic()); 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); field.bcel = fg; } if (nempty(initializers)) { addMethod(initializerMethod = new FunctionDef("$initFields", new S[0], new Script(initializers)).returnType(void.class)); if (!hasUserDefinedDefaultConstructor()) addMethod(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) { if (field.isStatic()) ret; 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, typeToBCEL(method.returnType), method.name, mapToArray typeToBCEL(method.argTypes)); int iThis = 0, iCtx = mm.frameSize; // 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("arg", arg); for iArg to nArgs: { mm.aload(iCtx); mm.stringConstant(method.args[iArg]); mm.aloadArgWithAutoboxing(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); // cast and return Type type = method.returnType(); if (type == void.class) mm.return(); else { if (type cast Class && isPrimitiveType(type)) { // unbox primitive value Class c = type; Class boxed = primitiveToBoxedType(c); mm.checkCast(typeToBCEL(boxed)); mm.invokeVirtual(boxed, c, c + "Value"); mm.returnPrimitive(c); } else { if (type != O) mm.checkCast(typeToBCEL(type)); 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) { if (resolvedClass != null) ret; resolvedClass = c; for iMethod over methodBodies: { S bodyFieldName = "_body" + iMethod; set(c, bodyFieldName, methodBodies.get(iMethod).body); } for (field : fields) if (field.isStatic() && field.initializer != null) set(c, field.name, field.initializer.get()); setOpt(c, srcRefField(), srcRef()); } ResolvableLASClass resolvable(ILASClassLoader lasClassLoader) { ret new ResolvableLASClass(lasClassLoader, this); } O typeToBCEL(LASValueDescriptor descriptor) { ret or(descriptor?.javaClass(), O); } O typeToBCEL(Type type) { try object resolvableClassToName(type); ret typeToClass(type); } void addField(FieldDef field) { fields.add(field); fieldsByName.put(field.name, field); if (field.initializer != null) addToInitializerMethod(field); } void addMethod(FunctionDef method) { methods.add(method); methodsByName.put(method.name, method); } S srcRefField() { ret "__srcRef"; } }