Transpiled version (31510L) is out of date.
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 | var fg = new FieldGen( |
74 | Const.ACC_PUBLIC, typeToBCELType(type), |
75 | field.name, cp); |
76 | fg.isTransient(field.hasModifier("transient")); |
77 | fg.isStatic(field.isStatic()); |
78 | if (type cast ParameterizedType) |
79 | fg.addAttribute(new org.apache.bcel.classfile.Signature( |
80 | cp.addUtf8("Signature"), 2, |
81 | cp.addUtf8(typeToVMSignature(type)), |
82 | cp.getConstantPool())); |
83 | classMaker.addField(fg); |
84 | field.bcel = fg; |
85 | } |
86 | |
87 | if (nempty(initializers)) { |
88 | addMethod(initializerMethod = new FunctionDef("$initFields", new S[0], new Script(initializers)).returnType(void.class)); |
89 | |
90 | if (!hasUserDefinedDefaultConstructor()) |
91 | addMethod(new FunctionDef("<init>", new S[0], new Script(emptyList())).returnType(void.class)); |
92 | } |
93 | |
94 | for (method : methods) |
95 | if (fullCompilation) |
96 | fullyCompileMethod(classMaker, method); |
97 | else |
98 | semiCompileMethod(classMaker, method); |
99 | |
100 | if (!hasUserDefinedDefaultConstructor()) |
101 | classMaker.addDefaultConstructor(); |
102 | |
103 | if (srcRef() != null) |
104 | classMaker.addField(new FieldGen( |
105 | Const.ACC_PUBLIC | Const.ACC_STATIC, classToBCELType(TokenRangeWithSrc), |
106 | srcRefField(), classMaker.getConstantPool())); |
107 | |
108 | ret classMaker.toBytes(); |
109 | } |
110 | |
111 | TokenRangeWithSrc srcRef() { ret tokenRangeWithSrc(); } |
112 | |
113 | void addToInitializerMethod(FieldDef field) { |
114 | if (field.isStatic()) ret; |
115 | isFieldInitializer.set(l(initializers)); |
116 | initializers.add( |
117 | new Tb.SetField(new Tb.GetVar("this"), field.name, field.initializer)); |
118 | } |
119 | |
120 | bool hasUserDefinedDefaultConstructor() { |
121 | ret any(methods, m -> m.isConstructor() && empty(m.args)); |
122 | } |
123 | |
124 | // generate interpreted method (slower but always works) |
125 | // like this: |
126 | // |
127 | // new FlexibleVarContext ctx; |
128 | // ctx.put("this", this); |
129 | // ctx.put("arg1", arg1); |
130 | // ctx.put("arg2", arg2); |
131 | // ret methodBody.get(ctx); |
132 | |
133 | void semiCompileMethod(ClassMaker classMaker, FunctionDef method) { |
134 | // make static field for method body later |
135 | int iMethod = l(methodBodies); |
136 | methodBodies.add(method); |
137 | S bodyFieldName = "_body" + iMethod; |
138 | classMaker.addField(new FieldGen( |
139 | Const.ACC_PUBLIC | Const.ACC_STATIC, classToBCELType(Evaluable.class), |
140 | bodyFieldName, classMaker.getConstantPool())); |
141 | |
142 | int nArgs = l(method.args); |
143 | MethodMaker mm = new(classMaker.cg(), Const.ACC_PUBLIC, typeToBCEL(method.returnType), method.name, |
144 | mapToArray typeToBCEL(method.argTypes)); |
145 | |
146 | int iThis = 0, iCtx = mm.frameSize; |
147 | |
148 | // If we are compiling a constructor, call the superclass constructor and $initFields |
149 | if (method.isConstructor()) { |
150 | mm.aload(iThis); |
151 | mm.invokeConstructor(typeToClass(superClass)); |
152 | |
153 | if (initializerMethod != null) { |
154 | mm.aload(iThis); |
155 | mm.il.append(mm.factory.createInvoke(classMaker.className(), initializerMethod.name, |
156 | mm.wrapType(void.class), |
157 | mm.wrapTypes(new Class[0]), Const.INVOKESPECIAL)); |
158 | } |
159 | } |
160 | |
161 | // new FlexibleVarContext ctx; |
162 | mm.newObject(FlexibleVarContext); |
163 | mm.astore(iCtx); |
164 | |
165 | // ctx.put("this", this); |
166 | mm.aload(iCtx); |
167 | mm.stringConstant("this"); |
168 | mm.aload(iThis); |
169 | mm.invokeVirtual(VarContext, void.class, "put", S, O); |
170 | |
171 | // ctx.put("arg<n>", arg<n>); |
172 | for iArg to nArgs: { |
173 | mm.aload(iCtx); |
174 | mm.stringConstant(method.args[iArg]); |
175 | mm.aloadArgWithAutoboxing(iArg); |
176 | mm.invokeVirtual(VarContext, void.class, "put", S, O); |
177 | } |
178 | |
179 | // ret methodBody.get(ctx); |
180 | mm.getStaticField(classMaker.className(), bodyFieldName, Evaluable.class); |
181 | mm.aload(iCtx); |
182 | mm.invokeInterface(Evaluable.class, O, "get", VarContext); |
183 | |
184 | // cast and return |
185 | |
186 | Type type = method.returnType(); |
187 | if (type == void.class) |
188 | mm.return(); |
189 | else { |
190 | if (type cast Class && isPrimitiveType(type)) { |
191 | // unbox primitive value |
192 | Class c = type; |
193 | Class boxed = primitiveToBoxedType(c); |
194 | mm.checkCast(typeToBCEL(boxed)); |
195 | mm.invokeVirtual(boxed, c, c + "Value"); |
196 | mm.returnPrimitive(c); |
197 | } else { |
198 | if (type != O) |
199 | mm.checkCast(typeToBCEL(type)); |
200 | mm.areturn(); |
201 | } |
202 | } |
203 | |
204 | mm.done(); |
205 | |
206 | //print("Made method " + mm.mg.getMethod()); |
207 | } |
208 | |
209 | // fully compile method to byte code (this works only for very simple methods) |
210 | void fullyCompileMethod(ClassMaker classMaker, FunctionDef method) { |
211 | MethodMaker mm = new(classMaker, typeToClass(method.returnType()), method.name, |
212 | repArray(Class.class, O.class, l(method.args))); |
213 | |
214 | var tbc = LASToByteCode(mm) { |
215 | JVMStackCellType compileGetVar(Tb.GetVar code) { |
216 | // load "this" |
217 | |
218 | if (eq(code.var, "this")) { |
219 | mm.aload(0); |
220 | ret JVMStackCellType.objValue; |
221 | } |
222 | |
223 | // load method argument |
224 | |
225 | int iArg = indexOf(method.args, code.var); |
226 | if (iArg >= 0) { |
227 | mm.aload(iArg+1); |
228 | ret JVMStackCellType.objValue; |
229 | } |
230 | |
231 | ret super.compileGetVar(code); |
232 | } |
233 | }; |
234 | tbc.postConversion = stackTop -> mm.convertToObject(stackTop); |
235 | tbc.compileScript(method.body); |
236 | mm.areturn(); |
237 | mm.done(); |
238 | } |
239 | |
240 | void init(Class c) { |
241 | if (resolvedClass != null) ret; |
242 | resolvedClass = c; |
243 | |
244 | for iMethod over methodBodies: { |
245 | S bodyFieldName = "_body" + iMethod; |
246 | set(c, bodyFieldName, methodBodies.get(iMethod).body); |
247 | } |
248 | |
249 | for (field : fields) |
250 | if (field.isStatic() && field.initializer != null) |
251 | set(c, field.name, field.initializer.get()); |
252 | |
253 | setOpt(c, srcRefField(), srcRef()); |
254 | } |
255 | |
256 | ResolvableLASClass resolvable(ILASClassLoader lasClassLoader) { |
257 | ret new ResolvableLASClass(lasClassLoader, this); |
258 | } |
259 | |
260 | O typeToBCEL(LASValueDescriptor descriptor) { |
261 | ret or(descriptor?.javaClass(), O); |
262 | } |
263 | |
264 | O typeToBCEL(Type type) { |
265 | try object resolvableClassToName(type); |
266 | ret typeToClass(type); |
267 | } |
268 | |
269 | void addField(FieldDef field) { |
270 | fields.add(field); |
271 | fieldsByName.put(field.name, field); |
272 | if (field.initializer != null) |
273 | addToInitializerMethod(field); |
274 | } |
275 | |
276 | void addMethod(FunctionDef method) { |
277 | methods.add(method); |
278 | methodsByName.put(method.name, method); |
279 | } |
280 | |
281 | S srcRefField() { ret "__srcRef"; } |
282 | } |
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: | 385 / 781 |
Version history: | 98 change(s) |
Referenced in: | [show references] |