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

285
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]

Uses 679K of libraries. Click here for Pure Java version (31971L/155K).

1  
sclass LASClassDef extends HasTokenRangeWithSrc {
2  
  settable S userGivenName;
3  
  settable S classDefPrefix = "userCode.";
4  
  settable bool fullCompilation;
5  
  settable bool verbose = true;
6  
  
7  
  replace Tb with GazelleV_LeftArrowScript.
8  
  delegate FunctionDef, Evaluable, Script to Tb.
9  
  
10  
  transient Class resolvedClass;
11  
  
12  
  settable Type superClass = O.class;
13  
  
14  
  new L<FieldDef> fields;
15  
  new Map<S, FieldDef> fieldsByName;
16  
17  
  new L<FunctionDef> methods;
18  
  new MultiMap<S, FunctionDef> methodsByName;
19  
  
20  
  new L<FunctionDef> methodBodies;
21  
  
22  
  new L<Type> interfaces;
23  
  
24  
  FunctionDef initializerMethod;
25  
  
26  
  new L<Evaluable> initializers;
27  
  new BitSet isFieldInitializer;
28  
  
29  
  //new L<Evaluable> staticInitializers;
30  
  new LPair<S, Evaluable> staticFieldInitializers;
31  
  
32  
  sclass FieldDef {
33  
    settable S name;
34  
    settable Type type;
35  
    settable Evaluable initializer;
36  
    Set<S> modifiers;
37  
    
38  
    transient FieldGen bcel;
39  
    
40  
    bool hasModifier(S modifier) { ret contains(modifiers, modifier); }
41  
    void addModifier(S modifier) { modifiers = createOrAddToSet(modifiers, modifier); }
42  
    
43  
    bool isStatic() { ret hasModifier("static"); }
44  
  }
45  
  
46  
  S structForHash() {
47  
    ret Tb.scriptStruct(litorderedmap(
48  
      +userGivenName, +fields, +methods, +fullCompilation
49  
    ));
50  
  }
51  
  
52  
  simplyCached S classHash() {
53  
    S struct = structForHash();
54  
    if (verbose) print(structForHash := struct);
55  
    ret md5(struct);
56  
  }
57  
  
58  
  S finalClassName() {
59  
    ret classDefPrefix() + finalClassNameWithoutPrefix();
60  
  }
61  
  
62  
  S finalClassNameWithoutPrefix() {
63  
    ret or2(userGivenName, "C") + "_" + classHash();
64  
  }
65  
  
66  
  simplyCached byte[] toBytes() {
67  
    ClassMaker classMaker = new(finalClassName(), className(typeToClass(superClass)),
68  
      mapToStringArray(interfaces, i -> className(typeToClass(i))));
69  
    var cp = classMaker.getConstantPool();
70  
    
71  
    for (field : fields) {
72  
      var type = field.type;
73  
      assertNotNull(+type);
74  
      var bcelType = typeToBCELType(type);
75  
      assertNotNull(+bcelType);
76  
      var fg = new FieldGen(
77  
        Const.ACC_PUBLIC, bcelType,
78  
        field.name, cp);
79  
      fg.isTransient(field.hasModifier("transient"));
80  
      fg.isStatic(field.isStatic());
81  
      if (type cast ParameterizedType)
82  
        fg.addAttribute(new org.apache.bcel.classfile.Signature(
83  
          cp.addUtf8("Signature"), 2,
84  
          cp.addUtf8(typeToVMSignature(type)),
85  
          cp.getConstantPool()));
86  
      classMaker.addField(fg);
87  
      field.bcel = fg;
88  
    }
89  
    
90  
    if (nempty(initializers)) {
91  
      addMethod(initializerMethod = new FunctionDef("$initFields", new S[0], new Script(initializers)).returnType(void.class));
92  
      
93  
      if (!hasUserDefinedDefaultConstructor())
94  
        addMethod(new FunctionDef("<init>", new S[0], new Script(emptyList())).returnType(void.class));
95  
    }
96  
    
97  
    for (method : methods)
98  
      if (fullCompilation)
99  
        fullyCompileMethod(classMaker, method);
100  
      else
101  
        semiCompileMethod(classMaker, method);
102  
103  
    if (!hasUserDefinedDefaultConstructor())
104  
      classMaker.addDefaultConstructor();
105  
    
106  
    if (srcRef() != null)
107  
      classMaker.addField(new FieldGen(
108  
        Const.ACC_PUBLIC | Const.ACC_STATIC, classToBCELType(TokenRangeWithSrc),
109  
        srcRefField(), classMaker.getConstantPool()));
110  
111  
    ret classMaker.toBytes();
112  
  }
113  
  
114  
  TokenRangeWithSrc srcRef() { ret tokenRangeWithSrc(); }
115  
  
116  
  void addToInitializerMethod(FieldDef field) {
117  
    if (field.isStatic()) ret;
118  
    isFieldInitializer.set(l(initializers));
119  
    initializers.add(
120  
      new Tb.SetField(new Tb.GetVar("this"), field.name, field.initializer));
121  
  }
122  
  
123  
  bool hasUserDefinedDefaultConstructor() {
124  
    ret any(methods, m -> m.isConstructor() && empty(m.args));
125  
  }
126  
  
127  
  // generate interpreted method (slower but always works)
128  
  // like this:
129  
  //
130  
  // new FlexibleVarContext ctx;
131  
  // ctx.put("this", this);
132  
  // ctx.put("arg1", arg1);
133  
  // ctx.put("arg2", arg2);
134  
  // ret methodBody.get(ctx);
135  
  
136  
  void semiCompileMethod(ClassMaker classMaker, FunctionDef method) {
137  
    // make static field for method body later
138  
    int iMethod = l(methodBodies);
139  
    methodBodies.add(method);
140  
    S bodyFieldName = "_body" + iMethod;
141  
    classMaker.addField(new FieldGen(
142  
      Const.ACC_PUBLIC | Const.ACC_STATIC, classToBCELType(Evaluable.class),
143  
      bodyFieldName, classMaker.getConstantPool()));
144  
145  
    int nArgs = l(method.args);
146  
    MethodMaker mm = new(classMaker.cg(), Const.ACC_PUBLIC, typeToBCEL(method.returnType), method.name,
147  
      mapToArray typeToBCEL(method.argTypes));
148  
    
149  
    int iThis = 0, iCtx = mm.frameSize;
150  
    
151  
    // If we are compiling a constructor, call the superclass constructor and $initFields
152  
    if (method.isConstructor()) {
153  
      mm.aload(iThis);
154  
      mm.invokeConstructor(typeToClass(superClass));
155  
      
156  
      if (initializerMethod != null) {
157  
        mm.aload(iThis);
158  
        mm.il.append(mm.factory.createInvoke(classMaker.className(), initializerMethod.name,
159  
          mm.wrapType(void.class),
160  
          mm.wrapTypes(new Class[0]), Const.INVOKESPECIAL));
161  
      }
162  
    }
163  
    
164  
    // new FlexibleVarContext ctx;
165  
    mm.newObject(FlexibleVarContext);
166  
    mm.astore(iCtx);
167  
168  
    // ctx.put("this", this);
169  
    mm.aload(iCtx);
170  
    mm.stringConstant("this");
171  
    mm.aload(iThis);
172  
    mm.invokeVirtual(VarContext, void.class, "put", S, O);
173  
  
174  
    // ctx.put("arg<n>", arg<n>);
175  
    for iArg to nArgs: {
176  
      mm.aload(iCtx);
177  
      mm.stringConstant(method.args[iArg]);
178  
      mm.aloadArgWithAutoboxing(iArg);
179  
      mm.invokeVirtual(VarContext, void.class, "put", S, O);
180  
    }
181  
  
182  
    // ret methodBody.get(ctx);
183  
    mm.getStaticField(classMaker.className(), bodyFieldName, Evaluable.class);
184  
    mm.aload(iCtx);
185  
    mm.invokeInterface(Evaluable.class, O, "get", VarContext);
186  
    
187  
    // cast and return
188  
    
189  
    Type type = method.returnType();
190  
    if (type == void.class)
191  
      mm.return();
192  
    else {
193  
      if (type cast Class && isPrimitiveType(type)) {
194  
        // unbox primitive value
195  
        Class c = type;
196  
        Class boxed = primitiveToBoxedType(c);
197  
        mm.checkCast(typeToBCEL(boxed));
198  
        mm.invokeVirtual(boxed, c, c + "Value");
199  
        mm.returnPrimitive(c);
200  
      } else {
201  
        if (type != O) 
202  
          mm.checkCast(typeToBCEL(type));
203  
        mm.areturn();
204  
      }
205  
    }
206  
    
207  
    mm.done();
208  
    
209  
    //print("Made method " + mm.mg.getMethod());
210  
  }
211  
212  
  // fully compile method to byte code (this works only for very simple methods)
213  
  void fullyCompileMethod(ClassMaker classMaker, FunctionDef method) {
214  
    MethodMaker mm = new(classMaker, typeToClass(method.returnType()), method.name,
215  
      repArray(Class.class, O.class, l(method.args)));
216  
          
217  
    var tbc = LASToByteCode(mm) {
218  
      JVMStackCellType compileGetVar(Tb.GetVar code) {
219  
        // load "this"
220  
        
221  
        if (eq(code.var, "this")) {
222  
          mm.aload(0);
223  
          ret JVMStackCellType.objValue;
224  
        }
225  
        
226  
        // load method argument
227  
        
228  
        int iArg = indexOf(method.args, code.var);
229  
        if (iArg >= 0) {
230  
          mm.aload(iArg+1);
231  
          ret JVMStackCellType.objValue;
232  
        }
233  
          
234  
        ret super.compileGetVar(code);
235  
      }
236  
    };
237  
    tbc.postConversion = stackTop -> mm.convertToObject(stackTop);
238  
    tbc.compileScript(method.body);
239  
    mm.areturn();
240  
    mm.done();
241  
  }
242  
  
243  
  void init(Class c) {
244  
    if (resolvedClass != null) ret;
245  
    resolvedClass = c;
246  
    
247  
    for iMethod over methodBodies: {
248  
      S bodyFieldName = "_body" + iMethod;
249  
      set(c, bodyFieldName, methodBodies.get(iMethod).body);
250  
    }
251  
    
252  
    for (field : fields)
253  
      if (field.isStatic() && field.initializer != null)
254  
        set(c, field.name, field.initializer.get());
255  
256  
    setOpt(c, srcRefField(), srcRef());
257  
  }
258  
  
259  
  ResolvableLASClass resolvable(ILASClassLoader lasClassLoader) {
260  
    ret new ResolvableLASClass(lasClassLoader, this);
261  
  }
262  
  
263  
  O typeToBCEL(LASValueDescriptor descriptor) {
264  
    ret or(descriptor?.javaClass(), O);
265  
  }
266  
  
267  
  O typeToBCEL(Type type) {
268  
    try object resolvableClassToName(type);
269  
    ret typeToClass(type);
270  
  }
271  
  
272  
  void addField(FieldDef field) {
273  
    fields.add(field);
274  
    fieldsByName.put(field.name, field);
275  
    if (field.initializer != null)
276  
      addToInitializerMethod(field);
277  
  }
278  
  
279  
  void addMethod(FunctionDef method) {
280  
    methods.add(method);
281  
    methodsByName.put(method.name, method);
282  
  }
283  
  
284  
  S srcRefField() { ret "__srcRef"; }
285  
}

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/100
Text MD5: faa397fecd2ac4a1a3ba9848cf58ec5e
Transpilation MD5: 294b22e9d429756fd24e583b6c8f9ef1
Author: stefan
Category: javax / left arrow script
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2025-04-26 23:14:42
Source code size: 8643 bytes / 285 lines
Pitched / IR pitched: No / No
Views / Downloads: 489 / 924
Version history: 99 change(s)
Referenced in: [show references]