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