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