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