Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

282
LINES

< > BotCompany Repo | #1035078 // LASClassDef - a custom class defined in a left-arrow script. also contains the byte code generator

JavaX fragment (include) [tags: use-pretranspiled]

Transpiled version (31510L) is out of date.

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<FieldDef> fields;
  new Map<S, FieldDef> fieldsByName;

  new L<FunctionDef> methods;
  new MultiMap<S, FunctionDef> methodsByName;
  
  new L<FunctionDef> methodBodies;
  
  new L<Type> interfaces;
  
  FunctionDef initializerMethod;
  
  new L<Evaluable> initializers;
  new BitSet isFieldInitializer;
  
  //new L<Evaluable> staticInitializers;
  new LPair<S, Evaluable> staticFieldInitializers;
  
  sclass FieldDef {
    settable S name;
    settable Type type;
    settable Evaluable initializer;
    Set<S> 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("<init>", 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<n>", arg<n>);
    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"; }
}

download  show line numbers  debug dex  old transpilations   

Travelled to 4 computer(s): bhatertpkbcr, ekrmjmnbrukm, mowyntqkapby, mqqgnosmbjvj

No comments. add comment

Snippet ID: #1035078
Snippet name: LASClassDef - a custom class defined in a left-arrow script. also contains the byte code generator
Eternal ID of this version: #1035078/99
Text MD5: c77c85326ed42763140d4105243e3662
Author: stefan
Category: javax / left arrow script
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2023-04-29 23:59:57
Source code size: 8549 bytes / 282 lines
Pitched / IR pitched: No / No
Views / Downloads: 384 / 780
Version history: 98 change(s)
Referenced in: #1003674 - Standard Classes + Interfaces (LIVE continued in #1034167)