Transpiled version (31611L) is out of date.
1 | // See GazelleV_LeftArrowScriptParser |
2 | |
3 | // TODO: decide whether we allow calling methods/getting fields on |
4 | // a null reference (just returning null), or whether we throw a |
5 | // NullPointerException. Currently we just return null. Probably |
6 | // that's pretty cool. Null propagation as a default, just like in |
7 | // JavaX. Just more automatic! |
8 | |
9 | sclass GazelleV_LeftArrowScript { |
10 | replace EXITCHECK with if (ctx.exiting()) null; . // Keep the dot in, it belongs there |
11 | |
12 | replace const with _const. |
13 | |
14 | macro HANDLENULLREFERENCE { |
15 | settable bool allowNullReference; |
16 | |
17 | O handleNullReference() { |
18 | if (allowNullReference) |
19 | null; |
20 | else |
21 | throw new NullPointerException(); |
22 | } |
23 | } |
24 | |
25 | // Base = any script element with a reference to its source code |
26 | asclass Base > HasTokenRangeWithSrc { |
27 | RuntimeException rethrowWithSrc(S msg default "", Throwable e) { |
28 | if (!e instanceof QuickException && tokenRangeWithSrc() != null) |
29 | /*throw rethrowAndAppendToMessage(e, squareBracketed( |
30 | joinNemptiesWithComma(msg, src)));*/ |
31 | throw new ScriptError(tokenRangeWithSrc(), msg, e); |
32 | else |
33 | throw rethrow(e); |
34 | } |
35 | |
36 | S indented() { |
37 | ret indentedScriptStruct(this); |
38 | } |
39 | |
40 | S shortenedSrc() { |
41 | ret shortenSrc(tokenRangeWithSrc()); |
42 | } |
43 | } |
44 | |
45 | sclass ScriptError extends RuntimeException is IHasTokenRangeWithSrc { |
46 | *(TokenRangeWithSrc src, Throwable reason) { |
47 | super(reason); |
48 | tokenRangeWithSrc(src); |
49 | } |
50 | |
51 | *(TokenRangeWithSrc src, S msg, Throwable reason) { |
52 | super(msg, reason); |
53 | tokenRangeWithSrc(src); |
54 | } |
55 | |
56 | !include #1036424 // (I)HasTokenRangeWithSrc Include |
57 | |
58 | TokenRangeWithSrc src() { ret tokenRangeWithSrc(); } |
59 | |
60 | toString { |
61 | ret super.toString() + "\n " + src() + " {{ " + shortenSrc(src()) + " }}"; |
62 | } |
63 | } |
64 | |
65 | sS shortenSrc(TokenRangeWithSrc src) { |
66 | ret src == null ?: shorten(nlToSpace(src.text())); |
67 | } |
68 | |
69 | // Evaluable = a script element that can be evaluated |
70 | |
71 | interface Evaluable extends IF0, IHasTokenRangeWithSrc { |
72 | public O get(VarContext ctx default new FlexibleVarContext); |
73 | public default LASValueDescriptor returnType() { null; } |
74 | public default Evaluable optimize() { this; } |
75 | |
76 | // informs this object that its return value will not be needed |
77 | // so it can perform appropriate optimizations |
78 | public default Evaluable optimizeForReturnValueNotNeeded() { this; } |
79 | |
80 | public default O getWithParams(O... params) { |
81 | ret get(flexibleVarContextFromParams(params)); |
82 | } |
83 | } |
84 | |
85 | // Base + Evaluable + explicitly stored return type |
86 | |
87 | asclass EvaluableBase > Base is Evaluable { |
88 | settable LASValueDescriptor returnType; |
89 | bool returnValueNeeded = true; |
90 | |
91 | public Evaluable optimizeForReturnValueNotNeeded() { |
92 | returnValueNeeded = false; |
93 | ret optimize(); |
94 | } |
95 | } |
96 | |
97 | /*interface Cmd { |
98 | // returns true if a return was issued |
99 | public bool run(VarContext ctx default new FlexibleVarContext); |
100 | }*/ |
101 | |
102 | static new AtomicLong scriptIDCounter; |
103 | static long scriptID() { ret incAtomicLong(scriptIDCounter); } |
104 | |
105 | srecord noeq ListFromScript(Script script) > EvaluableBase { |
106 | public O get(VarContext ctx) { |
107 | ret script.getAsList(ctx); |
108 | } |
109 | } |
110 | |
111 | sclass Script > EvaluableBase { |
112 | transient long id = scriptID(); // just for printing |
113 | Map<S, FunctionDef> functionDefs; |
114 | Evaluable[] steps; |
115 | //settable LASScope scope; |
116 | Map<S, LASValueDescriptor> params; |
117 | |
118 | *() {} |
119 | *(L<Evaluable> steps) { this.steps = toTypedArray(Evaluable.class, steps); } |
120 | |
121 | public O get(VarContext ctx) { |
122 | O result = null; |
123 | var pingSource = pingSource(); |
124 | for (step : steps) { |
125 | ping(pingSource); |
126 | result = step.get(ctx); |
127 | |
128 | // exiting from anything? |
129 | |
130 | var exiting = ctx.exitFromScript; |
131 | if (exiting != null) { |
132 | printVars ifdef ReturnFromScript_debug("Checking exitFromScript", |
133 | +ctx, +exiting, script := this); |
134 | |
135 | // we're the exit point |
136 | if (exiting == this) { |
137 | ctx.exitFromScript = null; |
138 | result = ctx.returnValue; |
139 | ctx.returnValue(null); |
140 | ret result; |
141 | } |
142 | |
143 | // otherwise exit further |
144 | null; |
145 | } |
146 | } |
147 | ret result; |
148 | } |
149 | |
150 | O getAsList(VarContext ctx) { |
151 | O result = null; |
152 | var pingSource = pingSource(); |
153 | new L list; |
154 | for (step : steps) { |
155 | ping(pingSource); |
156 | list.add(step.get(ctx)); |
157 | |
158 | // exiting from anything? |
159 | |
160 | var exiting = ctx.exitFromScript; |
161 | if (exiting != null) { |
162 | printVars ifdef ReturnFromScript_debug("Checking exitFromScript", |
163 | +ctx, +exiting, script := this); |
164 | |
165 | // we're the exit point |
166 | if (exiting == this) { |
167 | ctx.exitFromScript = null; |
168 | result = ctx.returnValue; |
169 | ctx.returnValue(null); |
170 | list.add(result); |
171 | ret list; |
172 | } |
173 | |
174 | // otherwise exit further |
175 | null; |
176 | } |
177 | } |
178 | |
179 | ret list; |
180 | } |
181 | |
182 | S toStringLong() { ret pnlToLines(steps); } |
183 | toString { ret "Script " + n2(id); } |
184 | |
185 | FunctionDef getFunction(S name) { ret mapGet(functionDefs, name); } |
186 | |
187 | public Script optimizeScript aka optimize() { |
188 | int n = returnValueNeeded ? steps.length-1 : steps.length; |
189 | for (int i = 0; i < n; i++) |
190 | steps[i] = steps[i].optimizeForReturnValueNotNeeded(); |
191 | |
192 | for (f : values(functionDefs)) |
193 | f.optimize(); |
194 | |
195 | this; |
196 | } |
197 | } // end of Script |
198 | |
199 | srecord noeq FunctionDef(S name, S[] args, Script body) > Base { |
200 | //settable LASScope scope; |
201 | settable Type returnType = O.class; |
202 | settable LASValueDescriptor[] argTypes; |
203 | settable bool synthetic; |
204 | settable bool varargs; // Is last argument varargs ("...") |
205 | |
206 | *(S *name, LS args, Script *body) { |
207 | this.args = toStringArray(args); |
208 | } |
209 | |
210 | public O callFromOutside(O... args) { |
211 | ret call(new FlexibleVarContext, args); |
212 | } |
213 | |
214 | public O call(VarContext ctx, O... args) { |
215 | VarContext ctx2 = /*scope != null && scope.useFixedVars |
216 | ? new FixedVarContext(ctx, scope.names) :*/ new FlexibleVarContext(ctx); |
217 | |
218 | int n = min(l(args), l(this.args)); |
219 | |
220 | if (varargs && n == l(this.args)) { |
221 | --n; |
222 | ctx2.put(this.args[n], subArray(args, n)); |
223 | } |
224 | for i to n: |
225 | ctx2.put(this.args[i], args[i]); |
226 | print ifdef GazelleV_LeftArrowScript_debug(ctx2 := ctx2.vars); |
227 | ret body.get(ctx2); |
228 | } |
229 | |
230 | void optimize { |
231 | body = body.optimize(); |
232 | } |
233 | |
234 | bool isConstructor() { ret eq(name, "<init>"); } |
235 | } |
236 | |
237 | srecord noeq Assignment(S var, Evaluable expression) > EvaluableBase { |
238 | public O get(VarContext ctx) { |
239 | O o = expression.get(ctx); |
240 | ctx.set(var, o); |
241 | ret o; |
242 | } |
243 | |
244 | toString { ret var + " <- " + expression; } |
245 | } |
246 | |
247 | /*asclass FixedVarBase > EvaluableBase { |
248 | settable LASScope scope; |
249 | S var; |
250 | int varIdx; |
251 | |
252 | S varToStr() { ret var + " [" + varIdx + "]"; } |
253 | |
254 | void assertResolved { |
255 | if (varIdx < 0) |
256 | fail("Unresolved variable access: " + var); |
257 | } |
258 | |
259 | void resolve { |
260 | varIdx = scope.resolveVar(var); |
261 | } |
262 | } |
263 | |
264 | persistable sclass FixedVarAssignment > FixedVarBase { |
265 | Evaluable expression; |
266 | *(LASScope *scope, S *var, Evaluable *expression) {} |
267 | |
268 | public O get(VarContext ctx) { |
269 | O o = expression.get(ctx); |
270 | ctx/FixedVarContext.set(varIdx, o); |
271 | ret o; |
272 | } |
273 | |
274 | toString { ret varToStr() + " <- " + expression; } |
275 | }*/ |
276 | |
277 | // type isn't used yet |
278 | srecord noeq VarDeclaration(S var, Class type, Evaluable expression) > EvaluableBase { |
279 | public O get(VarContext ctx) { |
280 | try { |
281 | O o = expression?.get(ctx); |
282 | ctx.set(var, o); |
283 | ret o; |
284 | } catch e { |
285 | throw rethrowWithSrc(e); |
286 | } |
287 | } |
288 | |
289 | toString { ret "var " + var + " <- " + expression; } |
290 | } |
291 | |
292 | srecord noeq AssignmentToOuterVar(S var, Evaluable expression) > EvaluableBase { |
293 | public O get(VarContext ctx) { |
294 | var parent = ctx.parent(); |
295 | assertNotNull("No outer variable context", parent); |
296 | O o = expression.get(ctx); |
297 | parent.set(var, o); |
298 | ret o; |
299 | } |
300 | |
301 | toString { ret "outer " + var + " <- " + expression; } |
302 | } |
303 | |
304 | persistable sclass NewObject > EvaluableBase { |
305 | Class c; |
306 | Evaluable[] args; |
307 | |
308 | *(Class c) { this(c, null); } |
309 | *(Class *c, Evaluable[] *args) { |
310 | returnType(new LASValueDescriptor.Exact(c, false)); |
311 | } |
312 | |
313 | public O get(VarContext ctx) { |
314 | try { |
315 | ret preciseNuObject(c, evalArgs(args, ctx)); |
316 | } catch e { |
317 | throw rethrowWithSrc(e); |
318 | } |
319 | } |
320 | |
321 | toString { ret "new " + formatFunctionCall(className(c), args); } |
322 | } |
323 | |
324 | // new object of a script-defined class |
325 | // (we don't generate/resolve these classes until after the complete |
326 | // parse is done so parsing stays lightweight) |
327 | persistable sclass NewObject_LASClass > NewObject { |
328 | ResolvableLASClass lasClass; |
329 | |
330 | *(ResolvableLASClass *lasClass) {} |
331 | *(ResolvableLASClass *lasClass, Evaluable[] *args) {} |
332 | |
333 | void resolve { |
334 | if (c == null) |
335 | c = lasClass!; |
336 | } |
337 | |
338 | public O get(VarContext ctx) { |
339 | resolve(); |
340 | ret super.get(ctx); |
341 | } |
342 | |
343 | toString { ret "new " + formatFunctionCall(str(lasClass), args); } |
344 | } |
345 | |
346 | // new object of a class defined in expression |
347 | srecord noeq NewObject_UnknownClass(Evaluable classExpr, Evaluable[] args) > NewObject { |
348 | |
349 | public O get(VarContext ctx) { |
350 | try { |
351 | Class c = cast classExpr.get(ctx); |
352 | ret preciseNuObject(c, evalArgs(args, ctx)); |
353 | } catch e { |
354 | throw rethrowWithSrc(e); |
355 | } |
356 | } |
357 | |
358 | toString { ret "new " + formatFunctionCall(classExpr, args); } |
359 | } |
360 | |
361 | srecord noeq CallFunction(FunctionDef f, Evaluable[] args) > EvaluableBase { |
362 | public O get(VarContext ctx) { |
363 | var evaledArgs = evalArgs(args, ctx); |
364 | EXITCHECK |
365 | ret f.call(ctx, evaledArgs); |
366 | } |
367 | |
368 | toString { ret formatFunctionCall(f.name, args); } |
369 | |
370 | public LASValueDescriptor returnType() { |
371 | ret LASValueDescriptor.fromType(f.getReturnType()); |
372 | } |
373 | } |
374 | |
375 | srecord noeq GetVar(S var) > EvaluableBase { |
376 | public O get(VarContext ctx) { |
377 | ret ctx.get(var); |
378 | } |
379 | |
380 | toString { ret var; } |
381 | } |
382 | |
383 | /*persistable sclass GetFixedVar > FixedVarBase { |
384 | *(LASScope *scope, S *var) {} |
385 | |
386 | public O get(VarContext ctx) { |
387 | assertResolved(); |
388 | try { |
389 | ret ctx/FixedVarContext.get(varIdx); |
390 | } catch ArrayIndexOutOfBoundsException e { |
391 | assertResolved(); |
392 | throw e; |
393 | } |
394 | } |
395 | |
396 | toString { ret var + " [" + varIdx + "]"; } |
397 | }*/ |
398 | |
399 | srecord noeq Const(O value) > EvaluableBase { |
400 | public O get(VarContext ctx) { |
401 | ret value; |
402 | } |
403 | |
404 | toString { ret strOrClassName(value); } |
405 | |
406 | public LASValueDescriptor returnType() { |
407 | ret new LASValueDescriptor.KnownValue(value); |
408 | } |
409 | |
410 | // Structuring this object is only done to make a class def hash, |
411 | // or to show a parsed script to a user. |
412 | // So the structure just has to be unique, not serializable. |
413 | // As the contents of "value" could be an unpersistable object, |
414 | // we just stringify it. |
415 | O _serialize() { |
416 | bool ok = isUnproblematicValue(value); |
417 | printVars ifdef g22const_debug("Const._serialize", |
418 | className := value?.getClass(), +ok); |
419 | ret ok |
420 | ? this |
421 | : toStringWithClass(value); |
422 | } |
423 | } |
424 | |
425 | srecord noeq GetStaticField(Field field) > EvaluableBase { |
426 | public O get(VarContext ctx) ctex { |
427 | ret field.get(null); |
428 | } |
429 | |
430 | // See Const._serialize |
431 | S _serialize() { ret str(field); } |
432 | } |
433 | |
434 | abstract srecord noeq CallOnTarget(Evaluable target) > EvaluableBase { |
435 | HANDLENULLREFERENCE |
436 | |
437 | abstract O evalOnTarget(VarContext ctx, O object); |
438 | |
439 | public O get(VarContext ctx) { |
440 | try { |
441 | O object = target.get(ctx); |
442 | if (object == null) |
443 | ret handleNullReference(); |
444 | |
445 | ret evalOnTarget(ctx, object); |
446 | } catch e { |
447 | throw rethrowWithSrc(e); |
448 | } |
449 | } |
450 | } |
451 | |
452 | persistable sclass CallMethodOrGetField > CallOnTarget { |
453 | S name; |
454 | |
455 | *(Evaluable *target, S *name) {} |
456 | |
457 | O evalOnTarget(VarContext ctx, O object) { |
458 | try { |
459 | ret preciseGetOrCallMethod(object, name); |
460 | } catch e { |
461 | throw rethrowWithSrc("Was getting " + name, e); |
462 | } |
463 | } |
464 | } |
465 | |
466 | persistable sclass GetField > CallOnTarget { |
467 | S name; |
468 | |
469 | *(Evaluable *target, S *name) {} |
470 | |
471 | O evalOnTarget(VarContext ctx, O object) { |
472 | try { |
473 | ret _get(object, name); |
474 | } catch e { |
475 | throw rethrowWithSrc("Was getting " + name, e); |
476 | } |
477 | } |
478 | } |
479 | |
480 | sclass GetVarContext > EvaluableBase { |
481 | public O get(VarContext ctx) { ret ctx; } |
482 | } |
483 | |
484 | srecord noeq ThrowMethodNotFoundException(CallMethod instruction) > EvaluableBase { |
485 | public O get(VarContext ctx) { |
486 | fail("Method not found: " + instruction); |
487 | } |
488 | } |
489 | |
490 | srecord noeq ThrowNullPointerException(CallMethod instruction) > EvaluableBase { |
491 | public O get(VarContext ctx) { |
492 | fail("Null pointer exception: " + instruction); |
493 | } |
494 | } |
495 | |
496 | persistable sclass CallMethod > CallOnTarget { |
497 | S methodName; |
498 | Evaluable[] args; |
499 | |
500 | *(Evaluable *target, S *methodName, Evaluable[] *args) {} |
501 | |
502 | O evalOnTarget(VarContext ctx, O object) { |
503 | ret /*call*/newPreciseCall(object, methodName, evalArgs(args, ctx)); |
504 | } |
505 | |
506 | toString { ret target + "." + formatFunctionCall(methodName, args); } |
507 | |
508 | // optimize to DirectMethodCallOnKnownTarget if target type |
509 | // and all argument types are known exactly |
510 | public Evaluable optimize() { |
511 | var targetType = target.returnType(); |
512 | if (targetType != null && targetType.knownValue()) { |
513 | O o = targetType.value(); |
514 | if (o == null) |
515 | ret allowNullReference |
516 | ? const(null) |
517 | : new ThrowNullPointerException(this); |
518 | |
519 | Class[] argTypes = new[l(args)]; |
520 | for i over args: { |
521 | var type = args[i].returnType(); |
522 | if (type == null || !type.javaClassIsExact()) |
523 | this; |
524 | argTypes[i] = type.javaClass(); |
525 | } |
526 | |
527 | // can't optimize varargs |
528 | L<Method> methods = findMethodsNamed_cached(o, methodName); |
529 | if (any(methods, m -> m.isVarArgs())) this; |
530 | |
531 | var method, widening = unpair findMethod_withPrimitiveWidening_onTypes(o, methodName, argTypes); |
532 | if (method == null) ret new ThrowMethodNotFoundException(this); |
533 | |
534 | ret new DirectMethodCallOnKnownTarget(widening, o instanceof Class ? null : o, method, args); |
535 | } |
536 | |
537 | this; |
538 | } |
539 | } |
540 | |
541 | static O[] evalArgs(Evaluable[] args, VarContext ctx) { |
542 | ret mapToArrayOrNull(args, arg -> arg.get(ctx)); |
543 | } |
544 | |
545 | // method invocation on unknown object with "magic switch" |
546 | persistable sclass CallMethodOrGlobalFunction > CallMethod { |
547 | MethodOnObject globalFunction; |
548 | |
549 | static final new O methodNotFoundSentinel; |
550 | |
551 | *(Evaluable *target, S *methodName, MethodOnObject *globalFunction, Evaluable[] *args) {} |
552 | |
553 | // mark "magic switch" with + |
554 | toString { ret target + "." + formatFunctionCall(methodName + "+", args); } |
555 | |
556 | O evalOnTarget(VarContext ctx, O o) { |
557 | O[] realArgs = evalArgs(args, ctx); |
558 | O result = newPreciseCall_sentinel(o, methodName, methodNotFoundSentinel, realArgs); |
559 | if (result != methodNotFoundSentinel) |
560 | ret result; |
561 | print ifdef sentinelDebug("Sentinel triggered: " + globalFunction); |
562 | ret newPreciseCall( |
563 | globalFunction.object, |
564 | globalFunction.method, |
565 | itemPlusArray(o, realArgs)); |
566 | } |
567 | } |
568 | |
569 | // method invocation/field get on unknown object with "magic switch" |
570 | persistable sclass CallMethodOrGetFieldOrGlobalFunction > CallMethodOrGetField { |
571 | MethodOnObject globalFunction; |
572 | |
573 | static final new O notFoundSentinel; |
574 | |
575 | *(Evaluable *target, S *name, MethodOnObject *globalFunction) {} |
576 | |
577 | // mark "magic switch" with + |
578 | toString { ret target + "." + name + "+"; } |
579 | |
580 | O evalOnTarget(VarContext ctx, O o) { |
581 | try { |
582 | O result = preciseGetOrCallMethod_sentinel(o, name, notFoundSentinel); |
583 | if (result != notFoundSentinel) |
584 | ret result; |
585 | print ifdef sentinelDebug("Sentinel triggered: " + globalFunction); |
586 | ret newPreciseCall( |
587 | globalFunction.object, |
588 | globalFunction.method, |
589 | o); |
590 | } catch e { |
591 | throw rethrowWithSrc("Was getting " + name, e); |
592 | } |
593 | } |
594 | } |
595 | |
596 | abstract srecord noeq LambdaBase(Class intrface) > EvaluableBase { |
597 | transient Method implementedMethod; |
598 | |
599 | // !customConstructor |
600 | *(Class *intrface) { |
601 | implementedMethod = findSingleInterfaceMethodOrFail(intrface); |
602 | } |
603 | } |
604 | |
605 | // expects the interface method to have exactly one argument |
606 | sclass LambdaMethodOnArgument > LambdaBase { |
607 | S methodName; |
608 | Evaluable[] args; |
609 | |
610 | *(Class intrface, S *methodName, Evaluable[] *args) { |
611 | super(intrface); |
612 | } |
613 | |
614 | public O get(VarContext ctx) { |
615 | ret proxyFromInvocationHandler(intrface, (proxy, method, actualArgs) -> { |
616 | if (method.getDeclaringClass() == intrface) |
617 | ret forwardCall(actualArgs[0], ctx); |
618 | else |
619 | ret handleObjectMethodsInProxyInvocationHandler( |
620 | this, implementedMethod, method, proxy, actualArgs); |
621 | }); |
622 | } |
623 | |
624 | O forwardCall(O target, VarContext ctx) { |
625 | ret call(target, methodName, evalArgs(args, ctx)); |
626 | } |
627 | } |
628 | |
629 | srecord noeq LambdaDef(Class intrface, S[] args, Evaluable body) > EvaluableBase { |
630 | new Map<S, LASValueDescriptor> argTypes; |
631 | transient Method implementedMethod; |
632 | |
633 | // !customConstructor |
634 | *(Class *intrface, S[] *args, Evaluable *body) { |
635 | implementedMethod = findSingleInterfaceMethodOrFail(intrface); |
636 | if (implementedMethod.getParameterCount() != l(args)) |
637 | fail("Bad parameter count for lambda: " + implementedMethod + " vs: " + joinWithComma(args)); |
638 | } |
639 | |
640 | // We have to create the proxy when the lambda definition is |
641 | // evaluated since we have to put the context in there |
642 | public O get(VarContext ctx) { |
643 | ret proxyFromInvocationHandler(intrface, (proxy, method, actualArgs) -> { |
644 | ping(); |
645 | |
646 | if (method.getDeclaringClass() == intrface) { |
647 | var ctx2 = new FlexibleVarContext(ctx); |
648 | var argNames = args; |
649 | // We already know that the count matches. |
650 | for i over args: |
651 | ctx2.put(argNames[i], actualArgs[i]); |
652 | |
653 | ret body.get(ctx2); |
654 | } else |
655 | ret handleObjectMethodsInProxyInvocationHandler( |
656 | this, implementedMethod, method, proxy, actualArgs); |
657 | }); |
658 | } |
659 | |
660 | toString { ret "Lambda(" + shortenedSrc() + ")"; } |
661 | } |
662 | |
663 | abstract srecord noeq CurriedLambdaBase(Class intrface, Evaluable[] curriedArgs) > EvaluableBase { |
664 | transient Method implementedMethod; |
665 | |
666 | // !customConstructor |
667 | *(Class *intrface, Evaluable[] *curriedArgs) { |
668 | implementedMethod = findSingleInterfaceMethodOrFail(intrface); |
669 | } |
670 | |
671 | public O get(VarContext ctx) { |
672 | try { |
673 | O[] curriedArguments = evalArgs(curriedArgs, ctx); |
674 | |
675 | ret proxyFromInvocationHandler(intrface, (proxy, method, actualArgs) -> { |
676 | // Comparing a Method is more expensive than just |
677 | // comparing the class (should be enough to distinguish |
678 | // methods since we are dealing with a single-method class). |
679 | // tl;dr So far we are getting away with this so all is good ^^ |
680 | try { |
681 | if (method.getDeclaringClass() == intrface) |
682 | ret forwardCall(ctx, concatMethodArgs(curriedArguments, actualArgs)); |
683 | else |
684 | ret handleObjectMethodsInProxyInvocationHandler( |
685 | this, implementedMethod, method, proxy, actualArgs); } catch e { |
686 | throw rethrowWithSrc(e); |
687 | } |
688 | }); |
689 | } catch e { |
690 | throw rethrowWithSrc(e); |
691 | } |
692 | } |
693 | |
694 | abstract O forwardCall(VarContext ctx, O[] args); |
695 | } |
696 | |
697 | persistable sclass CurriedMethodLambda > CurriedLambdaBase { |
698 | O target; |
699 | S targetMethod; |
700 | |
701 | *(Class intrface, O *target, S *targetMethod, Evaluable[] curriedArgs) { |
702 | super(intrface, curriedArgs); |
703 | } |
704 | |
705 | O forwardCall(VarContext ctx, O[] args) { |
706 | ret newPreciseCall(target, targetMethod, args); |
707 | } |
708 | } |
709 | |
710 | sclass CurriedScriptFunctionLambda > CurriedLambdaBase { |
711 | FunctionDef f; |
712 | |
713 | *(Class intrface, FunctionDef *f, Evaluable[] curriedArgs) { |
714 | super(intrface, curriedArgs); |
715 | } |
716 | |
717 | O forwardCall(VarContext ctx, O[] args) { |
718 | ret f.call(ctx, args); |
719 | } |
720 | } |
721 | |
722 | sclass CurriedConstructorLambda > CurriedLambdaBase { |
723 | Constructor[] ctors; |
724 | |
725 | *(Class intrface, Constructor[] *ctors, Evaluable[] curriedArgs) { |
726 | super(intrface, curriedArgs); |
727 | } |
728 | |
729 | O forwardCall(VarContext ctx, O[] args) { |
730 | ret preciseNuObject(ctors, args); |
731 | } |
732 | } |
733 | |
734 | srecord noeq DirectMethodCallOnKnownTarget(bool widening, O target, Method method, Evaluable[] args) > EvaluableBase { |
735 | public O get(VarContext ctx) { |
736 | var evaluatedArgs = evalArgs(args, ctx); |
737 | ret widening |
738 | ? invokeMethodWithWidening(method, target, evaluatedArgs) |
739 | : invokeMethod(method, target, evaluatedArgs); |
740 | } |
741 | |
742 | toString { ret (target == null ? "" : target + ".") + formatFunctionCall(str(method), args); } |
743 | |
744 | public LASValueDescriptor returnType() { |
745 | ret LASValueDescriptor.fromClass(method.getReturnType()); |
746 | } |
747 | } |
748 | |
749 | srecord noeq While(Evaluable condition, Evaluable body) > EvaluableBase { |
750 | public O get(VarContext ctx) { |
751 | while (!ctx.exiting() && (Bool) condition.get(ctx)) { |
752 | body.get(ctx); |
753 | } |
754 | |
755 | // while loops don't return anything |
756 | null; |
757 | } |
758 | } |
759 | |
760 | srecord noeq DoWhile(Evaluable condition, Evaluable body) > EvaluableBase { |
761 | public O get(VarContext ctx) { |
762 | do { |
763 | body.get(ctx); |
764 | } while (!ctx.exiting() && (Bool) condition.get(ctx)); |
765 | |
766 | // while loops don't return anything |
767 | null; |
768 | } |
769 | } |
770 | |
771 | // collection is an Iterable, an array etc. |
772 | abstract srecord noeq ForEachBase(Evaluable collection, Evaluable body) > EvaluableBase { |
773 | macro ITERATE_ARRAY { |
774 | out = emptyList(array.length); |
775 | for (element : array) { |
776 | EXITCHECK |
777 | processElement(ctx, out, element); |
778 | } |
779 | } |
780 | |
781 | public O get(VarContext ctx) { |
782 | var coll = collection.get(ctx); |
783 | Iterator iterator; |
784 | L out; |
785 | try { |
786 | if (coll == null) |
787 | out = new L; |
788 | else if (coll.getClass().isArray()) { |
789 | if (coll cast O[]) { var array = coll; ITERATE_ARRAY } |
790 | else if (coll cast byte[]) { var array = coll; ITERATE_ARRAY } |
791 | else if (coll cast short[]) { var array = coll; ITERATE_ARRAY } |
792 | else if (coll cast double[]) { var array = coll; ITERATE_ARRAY } |
793 | else if (coll cast float[]) { var array = coll; ITERATE_ARRAY } |
794 | else if (coll cast int[]) { var array = coll; ITERATE_ARRAY } |
795 | else if (coll cast long[]) { var array = coll; ITERATE_ARRAY } |
796 | else if (coll cast char[]) { var array = coll; ITERATE_ARRAY } |
797 | else if (coll cast bool[]) { var array = coll; ITERATE_ARRAY } |
798 | else fail("todo for each with: " + coll); |
799 | } else if (coll cast Iterable) { |
800 | out = emptyList(coll); |
801 | iterator = coll.iterator(); |
802 | try { |
803 | while (iterator.hasNext()) { |
804 | EXITCHECK |
805 | var element = iterator.next(); |
806 | processElement(ctx, out, element); |
807 | } |
808 | } finally { |
809 | if (iterator cast AutoCloseable) ctex { |
810 | iterator.close(); |
811 | } |
812 | } |
813 | } else |
814 | fail("Not iterable: " + className(coll)); |
815 | } finally { |
816 | loopDone(ctx); |
817 | } |
818 | |
819 | ret out; |
820 | } |
821 | |
822 | abstract void processElement(VarContext ctx, L out, O o); |
823 | abstract void loopDone(VarContext ctx); |
824 | } |
825 | |
826 | persistable sclass ForEach > ForEachBase { |
827 | S var; |
828 | settable LASValueDescriptor varType; |
829 | |
830 | *(Evaluable *collection, S *var, Evaluable *body) {} |
831 | |
832 | void processElement(VarContext ctx, L out, O o) { |
833 | ctx.set(var, o); |
834 | out.add(body.get(ctx)); |
835 | } |
836 | |
837 | void loopDone(VarContext ctx) { |
838 | ctx.unset(var); |
839 | } |
840 | } |
841 | |
842 | srecord ForIterator(Evaluable iterable, S var, Evaluable body) > EvaluableBase { |
843 | public O get(VarContext ctx) { |
844 | VarContext subContext = new FlexibleVarContext(ctx); |
845 | |
846 | var iterable = this.iterable.get(ctx); |
847 | Iterator iterator = iterator_gen(iterable); |
848 | |
849 | ret new MapI(value -> { |
850 | subContext.set(var, value); |
851 | ret body.get(subContext); |
852 | }, iterator); |
853 | } |
854 | } |
855 | |
856 | srecord ForNested(Evaluable iterable, S var, Evaluable body) > EvaluableBase { |
857 | public O get(VarContext ctx) { |
858 | VarContext subContext = new FlexibleVarContext(ctx); |
859 | |
860 | var iterable = this.iterable.get(ctx); |
861 | Iterator iterator = iterator_gen(iterable); |
862 | |
863 | ret nestedIterator(iterator, value -> { |
864 | subContext.set(var, value); |
865 | ret iterator_gen(body.get(subContext)); |
866 | }); |
867 | } |
868 | } |
869 | |
870 | persistable sclass ForPairs > ForEachBase { |
871 | S varA, varB; |
872 | |
873 | *(Evaluable *collection, Evaluable *body, S *varA, S *varB) {} |
874 | |
875 | void processElement(VarContext ctx, L out, O o) { |
876 | Pair p = cast o; |
877 | ctx.set(varA, p.a); |
878 | ctx.set(varB, p.b); |
879 | out.add(body.get(ctx)); |
880 | } |
881 | |
882 | void loopDone(VarContext ctx) { |
883 | ctx.unset(varA); |
884 | ctx.unset(varB); |
885 | } |
886 | } |
887 | |
888 | srecord noeq ForKeyValue(Evaluable map, Evaluable body, S varA, S varB) > EvaluableBase { |
889 | public O get(VarContext ctx) { |
890 | Map<?, ?> theMap = (Map) map.get(ctx); |
891 | L out; |
892 | try { |
893 | if (theMap != null) { |
894 | out = emptyList(theMap.size()); |
895 | for (entry : theMap.entrySet()) { |
896 | EXITCHECK |
897 | ctx.set(varA, entry.getKey()); |
898 | ctx.set(varB, entry.getValue()); |
899 | out.add(body.get(ctx)); |
900 | } |
901 | } else |
902 | out = new L; |
903 | } finally { |
904 | ctx.unset(varA); |
905 | ctx.unset(varB); |
906 | } |
907 | |
908 | ret out; |
909 | } |
910 | } |
911 | |
912 | srecord noeq ForIntTo(Evaluable endValue, S var, Evaluable body) > EvaluableBase { |
913 | public Evaluable optimize() { |
914 | if (!returnValueNeeded) |
915 | body = body.optimizeForReturnValueNotNeeded(); |
916 | this; |
917 | } |
918 | |
919 | public O get(VarContext ctx) { |
920 | int n = (Int) endValue.get(ctx), i = 0; |
921 | L out = returnValueNeeded ? new L : null; |
922 | |
923 | try { |
924 | ctx.put(var, i); |
925 | |
926 | while (i < n) { |
927 | EXITCHECK |
928 | O o = body.get(ctx); |
929 | out?.add(o); |
930 | ctx.set(var, i = (Int) ctx.get(var)+1); |
931 | } |
932 | } finally { |
933 | ctx.unset(var); |
934 | } |
935 | |
936 | ret out; |
937 | } |
938 | } |
939 | |
940 | persistable sclass ForIndex > EvaluableBase { |
941 | Evaluable collection, body; |
942 | S varIndex, varElement; |
943 | |
944 | *(Evaluable *collection, Evaluable *body, S *varIndex, S *varElement) {} |
945 | |
946 | public O get(VarContext ctx) { |
947 | ret new ForIndex_instance(collection, body, varIndex, varElement).get(ctx); |
948 | } |
949 | } |
950 | |
951 | sclass ForIndex_instance > ForEachBase { |
952 | S varIndex, varElement; |
953 | int index; |
954 | |
955 | *(Evaluable *collection, Evaluable *body, S *varIndex, S *varElement) {} |
956 | |
957 | void processElement(VarContext ctx, L out, O o) { |
958 | ctx.set(varIndex, index++); |
959 | ctx.set(varElement, o); |
960 | out.add(body.get(ctx)); |
961 | } |
962 | |
963 | void loopDone(VarContext ctx) { |
964 | ctx.unset(varIndex); |
965 | ctx.unset(varElement); |
966 | } |
967 | } |
968 | |
969 | srecord noeq IfThen(Evaluable condition, Evaluable body, |
970 | Evaluable elseBranch) > EvaluableBase { |
971 | |
972 | IfThen(Evaluable condition, Evaluable body) { |
973 | this.condition = condition; |
974 | this.body = body; |
975 | } |
976 | |
977 | public O get(VarContext ctx) { |
978 | if ((Bool) condition.get(ctx)) |
979 | ret body.get(ctx); |
980 | else if (elseBranch != null) |
981 | ret elseBranch.get(ctx); |
982 | else |
983 | null; |
984 | } |
985 | } |
986 | |
987 | srecord noeq ReturnFromScript(Script script, Evaluable value) > EvaluableBase { |
988 | public O get(VarContext ctx) { |
989 | O result = value.get(ctx); |
990 | printVars ifdef ReturnFromScript_debug("ReturnFromScript", |
991 | +result, +ctx, +script); |
992 | ctx.exitFromScript(script); |
993 | ctx.returnValue(result); |
994 | null; |
995 | } |
996 | |
997 | toString { |
998 | ret formatFunctionCall ReturnFromScript(script, value); |
999 | } |
1000 | } |
1001 | |
1002 | srecord noeq Continue(Script loopBody) > EvaluableBase { |
1003 | public O get(VarContext ctx) { |
1004 | ctx.exitFromScript(loopBody); |
1005 | ctx.returnValue(null); |
1006 | null; |
1007 | } |
1008 | |
1009 | toString { |
1010 | ret formatFunctionCall Continue(loopBody); |
1011 | } |
1012 | } |
1013 | |
1014 | srecord noeq RepeatN(Evaluable n, Evaluable body) > EvaluableBase { |
1015 | public O get(VarContext ctx) { |
1016 | long count = ((Number) n.get(ctx)).longValue(); |
1017 | |
1018 | repeat count { |
1019 | EXITCHECK |
1020 | body.get(ctx); |
1021 | } |
1022 | |
1023 | null; |
1024 | } |
1025 | } |
1026 | |
1027 | persistable srecord BoolAnd(Evaluable a, Evaluable b) > EvaluableBase { |
1028 | public O get(VarContext ctx) { |
1029 | if (!((Bool) a.get(ctx))) false; |
1030 | ret b.get(ctx); |
1031 | } |
1032 | } |
1033 | |
1034 | persistable srecord BoolOr(Evaluable a, Evaluable b) > EvaluableBase { |
1035 | public O get(VarContext ctx) { |
1036 | if (((Bool) a.get(ctx))) true; |
1037 | ret b.get(ctx); |
1038 | } |
1039 | } |
1040 | |
1041 | srecord noeq TempBlock(Evaluable tempExpr, |
1042 | Evaluable body) > EvaluableBase { |
1043 | |
1044 | public O get(VarContext ctx) { |
1045 | temp (AutoCloseable) tempExpr.get(ctx); |
1046 | ret body.get(ctx); |
1047 | } |
1048 | } |
1049 | |
1050 | srecord noeq WillReturn(Evaluable exp, Evaluable body) > EvaluableBase { |
1051 | public O get(VarContext ctx) { |
1052 | body.get(ctx); |
1053 | ret exp.get(ctx); |
1054 | } |
1055 | } |
1056 | |
1057 | srecord ClassDef(ResolvableLASClass lasClass) > EvaluableBase { |
1058 | public O get(VarContext ctx) { |
1059 | ret lasClass!; |
1060 | } |
1061 | } |
1062 | |
1063 | srecord noeq SetField(Evaluable target, S name, Evaluable expr) > EvaluableBase { |
1064 | HANDLENULLREFERENCE |
1065 | |
1066 | public O get(VarContext ctx) { |
1067 | try { |
1068 | O value = expr.get(ctx); |
1069 | |
1070 | O object = target.get(ctx); |
1071 | if (object == null) |
1072 | handleNullReference(); |
1073 | else |
1074 | set(object, name, value); |
1075 | |
1076 | ret value; |
1077 | } catch e { |
1078 | throw rethrowWithSrc(e); |
1079 | } |
1080 | } |
1081 | } |
1082 | |
1083 | srecord noeq SetStaticField(Field field, Evaluable expr) > EvaluableBase { |
1084 | public O get(VarContext ctx) { |
1085 | try { |
1086 | O value = expr.get(ctx); |
1087 | field.set(null, value); |
1088 | ret value; |
1089 | } catch e { |
1090 | throw rethrowWithSrc(e); |
1091 | } |
1092 | } |
1093 | } |
1094 | |
1095 | srecord noeq Throw(Evaluable expr) > EvaluableBase { |
1096 | public O get(VarContext ctx) { |
1097 | throw asRuntimeException((Throwable) expr.get(ctx)); |
1098 | } |
1099 | } |
1100 | |
1101 | srecord noeq TryCatch(Evaluable body, S var, Evaluable catchBlock) > EvaluableBase { |
1102 | public O get(VarContext ctx) { |
1103 | try { |
1104 | ret body.get(ctx); |
1105 | } catch e { |
1106 | var addVar = var == null ?: ctx.tempPut(var, e); |
1107 | temp addVar; |
1108 | ret catchBlock.get(ctx); |
1109 | } |
1110 | } |
1111 | } |
1112 | |
1113 | srecord noeq TryFinally(Evaluable body, Evaluable finallyBlock) > EvaluableBase { |
1114 | public O get(VarContext ctx) { |
1115 | try { |
1116 | ret body.get(ctx); |
1117 | } finally { |
1118 | finallyBlock.get(ctx); |
1119 | } |
1120 | } |
1121 | } |
1122 | |
1123 | static structure_Data structureDataForLAS() { |
1124 | new structure_Data d; |
1125 | d.skipDefaultValues(true); |
1126 | |
1127 | d.shouldIncludeField = field -> { |
1128 | S c = shortClassName(field.getDeclaringClass()); |
1129 | S f = field.getName(); |
1130 | bool shouldInclude = !(eq(c, "HasTokenRangeWithSrc") && eq(f, "src")); |
1131 | //printVars("shouldIncludeField", +c, +f, +shouldInclude); |
1132 | ret shouldInclude; |
1133 | }; |
1134 | |
1135 | ret d; |
1136 | } |
1137 | |
1138 | // with respect to serialization |
1139 | sbool isUnproblematicValue(O o) { |
1140 | ret o == null || o instanceof Number || o instanceof S |
1141 | || o instanceof Bool || o instanceof Class; |
1142 | } |
1143 | |
1144 | // These structs are a. for the user to see the parsed script |
1145 | // and b. for checking if two class defs are equal. |
1146 | // |
1147 | // (The resulting struct string doesn't have to unstructure()-able, |
1148 | // which is why we can e.g. shorten class names.) |
1149 | sS scriptStruct(O o) { |
1150 | S s = struct(o, structureDataForLAS()); |
1151 | LS tok = structTok(s); |
1152 | S prefix = shortName(GazelleV_LeftArrowScript) + "$"; |
1153 | for (int i = 1; i < l(tok); i += 2) |
1154 | tok.set(i, replacePrefix(prefix, "$", tok.get(i))); |
1155 | ret join(tok); |
1156 | } |
1157 | |
1158 | sS indentedScriptStruct(O o) { |
1159 | ret indentStructureString(scriptStruct(o)); |
1160 | } |
1161 | |
1162 | static Evaluable const(O o) { |
1163 | ret new Const(o); |
1164 | } |
1165 | |
1166 | sclass Then > CallOnTarget { |
1167 | CallOnTarget call1, call2; |
1168 | |
1169 | *(CallOnTarget *call1, CallOnTarget *call2) { |
1170 | target = call1.target; |
1171 | call1.target = null; |
1172 | } |
1173 | |
1174 | O evalOnTarget(VarContext ctx, O object) { |
1175 | call1.evalOnTarget(ctx, object); |
1176 | ret call2.evalOnTarget(ctx, object); |
1177 | } |
1178 | } |
1179 | |
1180 | srecord noeq Synchronized(Evaluable target, Evaluable body) > EvaluableBase { |
1181 | public Evaluable optimize() { |
1182 | if (!returnValueNeeded) |
1183 | body = body.optimizeForReturnValueNotNeeded(); |
1184 | this; |
1185 | } |
1186 | |
1187 | public O get(VarContext ctx) { |
1188 | synchronized(target.get(ctx)) { |
1189 | ret body.get(ctx); |
1190 | } |
1191 | } |
1192 | } |
1193 | |
1194 | } // end of GazelleV_LeftArrowScript |
Began life as a copy of #1033976
download show line numbers debug dex old transpilations
Travelled to 4 computer(s): bhatertpkbcr, ekrmjmnbrukm, mowyntqkapby, mqqgnosmbjvj
No comments. add comment
Snippet ID: | #1033981 |
Snippet name: | GazelleV_LeftArrowScript [LIVE] |
Eternal ID of this version: | #1033981/307 |
Text MD5: | 320cfff0069602d4e5e829dbc8a3c21b |
Author: | stefan |
Category: | javax |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2025-01-03 20:40:01 |
Source code size: | 34857 bytes / 1194 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 1085 / 3404 |
Version history: | 306 change(s) |
Referenced in: | [show references] |