@ball-lang/engine 0.2.0 → 1.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2027 @@
1
+ /**
2
+ * Shared engine setup for the Ball TS self-hosted engine.
3
+ *
4
+ * This module factors the proto3-JSON normalization, method-dispatch handler,
5
+ * extra std-function registrations, and compiled-engine patches OUT of
6
+ * index.ts so they can be applied to ANY compiled-engine module instance —
7
+ * both the committed `compiled_engine.ts` (used by index.ts / ts/engine) and
8
+ * a freshly compiled engine (used by the Phase 2.7b conformance harness in
9
+ * ts/compiler).
10
+ *
11
+ * The single source of truth for 'what a working engine needs' therefore
12
+ * lives here, eliminating drift between the two harnesses.
13
+ *
14
+ * `createEngineSetup(mod)` takes the compiled-engine module namespace
15
+ * (its exports: BallEngine, StdModuleHandler, BallGenerator, _FlowSignal, and
16
+ * optionally BallFuture) and returns the bound setup helpers.
17
+ */
18
+ export function createEngineSetup(mod) {
19
+ const StdModuleHandler = mod.StdModuleHandler;
20
+ const BallGenerator = mod.BallGenerator;
21
+ const _FlowSignal = mod._FlowSignal;
22
+ const _EngineBallDouble = globalThis.BallDouble;
23
+ if (_EngineBallDouble?.prototype) {
24
+ const _origBallDoubleToString = _EngineBallDouble.prototype.toString;
25
+ _EngineBallDouble.prototype.toString = function () {
26
+ const v = this.value;
27
+ if (Object.is(v, -0))
28
+ return '-0.0';
29
+ return _origBallDoubleToString.call(this);
30
+ };
31
+ }
32
+ const _EngineBallFuture = mod.BallFuture;
33
+ class _ShimBallFuture {
34
+ value;
35
+ completed;
36
+ error;
37
+ constructor(value, completed = true) {
38
+ this.value = value;
39
+ this.completed = completed;
40
+ this.__ball_future__ = true;
41
+ }
42
+ }
43
+ const BallFuture = _EngineBallFuture ?? _ShimBallFuture;
44
+ function _isFutureLike(v) {
45
+ if (v == null || typeof v !== 'object')
46
+ return false;
47
+ if (_EngineBallFuture && v instanceof _EngineBallFuture)
48
+ return true;
49
+ return v.__ball_future__ === true;
50
+ }
51
+ // ── BallDouble helper ──────────────────────────────────────────────────────
52
+ //
53
+ // Creates a BallDouble-like value that behaves like a number but prints
54
+ // with a decimal point (e.g. 42 -> "42.0"). Matches the BallDouble class
55
+ // in the compiled engine's preamble.
56
+ function _makeBallDouble(v) {
57
+ // Use the compiled engine's BallDouble class (exposed on globalThis by preamble)
58
+ const BD = globalThis.BallDouble;
59
+ if (BD)
60
+ return new BD(v);
61
+ return v;
62
+ }
63
+ const _INT64_MAX_B = 9223372036854775807n;
64
+ const _INT64_MIN_B = -9223372036854775808n;
65
+ function _extractNumericArg(v) {
66
+ if (typeof v === 'bigint' || typeof v === 'number')
67
+ return v;
68
+ const BD = globalThis.BallDouble;
69
+ if (BD && v instanceof BD)
70
+ return v.value;
71
+ if (typeof v === 'object' && v !== null) {
72
+ const raw = v['value'] ?? v['arg0'] ?? v;
73
+ if (typeof raw === 'bigint' || typeof raw === 'number')
74
+ return raw;
75
+ if (BD && raw instanceof BD)
76
+ return raw.value;
77
+ return Number(raw);
78
+ }
79
+ return Number(v);
80
+ }
81
+ function _toIntValue(v) {
82
+ const n = _extractNumericArg(v);
83
+ if (typeof n === 'bigint') {
84
+ if (n > _INT64_MAX_B)
85
+ return _INT64_MAX_B;
86
+ if (n < _INT64_MIN_B)
87
+ return _INT64_MIN_B;
88
+ const asNum = Number(n);
89
+ return Number.isSafeInteger(asNum) ? asNum : n;
90
+ }
91
+ const tb = BigInt(Math.trunc(n));
92
+ if (tb > _INT64_MAX_B)
93
+ return _INT64_MAX_B;
94
+ if (tb < _INT64_MIN_B)
95
+ return _INT64_MIN_B;
96
+ return Number(tb);
97
+ }
98
+ function _toDoubleValue(v) {
99
+ const n = _extractNumericArg(v);
100
+ if (typeof n === 'bigint')
101
+ return _makeBallDouble(Number(n));
102
+ return _makeBallDouble(typeof n === 'number' && !Number.isNaN(n) ? n : 0);
103
+ }
104
+ function _coerceInt(v) {
105
+ if (v == null)
106
+ return 0;
107
+ if (typeof v === 'bigint')
108
+ return v;
109
+ const BD = globalThis.BallDouble;
110
+ if (BD && v instanceof BD)
111
+ return _coerceInt(v.value);
112
+ if (typeof v === 'number')
113
+ return Number.isInteger(v) ? v : Math.trunc(v);
114
+ if (typeof v === 'string') {
115
+ const s = v.trim();
116
+ if (/^-?\d+$/.test(s)) {
117
+ const b = BigInt(s);
118
+ if (b > 9007199254740991n || b < -9007199254740991n)
119
+ return b;
120
+ return Number(b);
121
+ }
122
+ const n = parseInt(s, 10);
123
+ return Number.isNaN(n) ? 0 : n;
124
+ }
125
+ if (typeof v === 'boolean')
126
+ return v ? 1 : 0;
127
+ return 0;
128
+ }
129
+ function _coerceNum(v) {
130
+ if (v == null)
131
+ return 0;
132
+ if (typeof v === 'bigint')
133
+ return _toIntValue(v);
134
+ const BD = globalThis.BallDouble;
135
+ if (BD && v instanceof BD)
136
+ return v.value;
137
+ if (typeof v === 'number')
138
+ return v;
139
+ if (typeof v === 'string') {
140
+ const s = v.trim();
141
+ if (/^-?\d+$/.test(s)) {
142
+ const b = BigInt(s);
143
+ if (b > 9007199254740991n || b < -9007199254740991n) {
144
+ return _toIntValue(b);
145
+ }
146
+ return Number(b);
147
+ }
148
+ const n = Number(s);
149
+ return Number.isNaN(n) ? 0 : n;
150
+ }
151
+ if (typeof v === 'boolean')
152
+ return v ? 1 : 0;
153
+ return 0;
154
+ }
155
+ function _coerceNumPair(a, b) {
156
+ const left = _coerceNum(a);
157
+ const right = _coerceNum(b);
158
+ if (typeof left === 'bigint' || typeof right === 'bigint') {
159
+ const lb = typeof left === 'bigint' ? left : BigInt(Math.trunc(Number(left)));
160
+ const rb = typeof right === 'bigint' ? right : BigInt(Math.trunc(Number(right)));
161
+ return [lb, rb];
162
+ }
163
+ return [left, right];
164
+ }
165
+ function _coerceIntPair(a, b) {
166
+ const left = _coerceInt(a);
167
+ const right = _coerceInt(b);
168
+ if (typeof left === 'bigint' || typeof right === 'bigint') {
169
+ const lb = typeof left === 'bigint' ? left : BigInt(Math.trunc(Number(left)));
170
+ const rb = typeof right === 'bigint' ? right : BigInt(Math.trunc(Number(right)));
171
+ return [lb, rb];
172
+ }
173
+ return [left, right];
174
+ }
175
+ function _asInt64(v) {
176
+ if (typeof v === 'bigint')
177
+ return BigInt.asIntN(64, v);
178
+ return BigInt.asIntN(64, BigInt(Math.trunc(Number(v))));
179
+ }
180
+ function _int64Result(v) {
181
+ const masked = BigInt.asIntN(64, v);
182
+ const asNum = Number(masked);
183
+ return Number.isSafeInteger(asNum) ? asNum : masked;
184
+ }
185
+ function _int64Binary(op, a, b) {
186
+ return _int64Result(op(_asInt64(a), _asInt64(b)));
187
+ }
188
+ function _int64Unary(op, v) {
189
+ return _int64Result(op(_asInt64(v)));
190
+ }
191
+ function _int64ShiftLeft(a, b) {
192
+ const shift = Number(_asInt64(b) & 63n);
193
+ return _int64Result(_asInt64(a) << BigInt(shift));
194
+ }
195
+ function _int64ShiftRight(a, b) {
196
+ const shift = Number(_asInt64(b) & 63n);
197
+ return _int64Result(_asInt64(a) >> BigInt(shift));
198
+ }
199
+ function _int64UnsignedShiftRight(a, b) {
200
+ const shift = Number(_asInt64(b) & 63n);
201
+ return _int64Result(BigInt.asUintN(64, _asInt64(a)) >> BigInt(shift));
202
+ }
203
+ function _int64Divide(a, b) {
204
+ const [l, r] = _coerceIntPair(a, b);
205
+ if (typeof l === 'bigint' || typeof r === 'bigint') {
206
+ const lb = typeof l === 'bigint' ? l : BigInt(l);
207
+ const rb = typeof r === 'bigint' ? r : BigInt(r);
208
+ return _int64Result(lb / rb);
209
+ }
210
+ return Math.trunc(l / r);
211
+ }
212
+ function _int64Modulo(a, b) {
213
+ const [l, r] = _coerceIntPair(a, b);
214
+ if (typeof l === 'bigint' || typeof r === 'bigint') {
215
+ const lb = typeof l === 'bigint' ? l : BigInt(l);
216
+ const rb = typeof r === 'bigint' ? r : BigInt(r);
217
+ let rem = lb % rb;
218
+ if (rem < 0n)
219
+ rem += (rb < 0n ? -rb : rb);
220
+ return _int64Result(rem);
221
+ }
222
+ const rem = l % r;
223
+ return rem < 0 ? rem + Math.abs(r) : rem;
224
+ }
225
+ function _mathAbs(v) {
226
+ const coerced = _coerceInt(v);
227
+ if (typeof coerced === 'bigint') {
228
+ return _int64Result(coerced < 0n ? -coerced : coerced);
229
+ }
230
+ return Math.abs(coerced);
231
+ }
232
+ function _int64Add(a, b) {
233
+ const [l, r] = _coerceIntPair(a, b);
234
+ if (typeof l === 'bigint' || typeof r === 'bigint') {
235
+ const lb = typeof l === 'bigint' ? l : BigInt(l);
236
+ const rb = typeof r === 'bigint' ? r : BigInt(r);
237
+ return _int64Result(lb + rb);
238
+ }
239
+ return l + r;
240
+ }
241
+ function _int64Subtract(a, b) {
242
+ const [l, r] = _coerceIntPair(a, b);
243
+ if (typeof l === 'bigint' || typeof r === 'bigint') {
244
+ const lb = typeof l === 'bigint' ? l : BigInt(l);
245
+ const rb = typeof r === 'bigint' ? r : BigInt(r);
246
+ return _int64Result(lb - rb);
247
+ }
248
+ return l - r;
249
+ }
250
+ function _int64Multiply(a, b) {
251
+ const [l, r] = _coerceIntPair(a, b);
252
+ if (typeof l === 'bigint' || typeof r === 'bigint') {
253
+ const lb = typeof l === 'bigint' ? l : BigInt(l);
254
+ const rb = typeof r === 'bigint' ? r : BigInt(r);
255
+ return _int64Result(lb * rb);
256
+ }
257
+ return l * r;
258
+ }
259
+ function _int64Negate(v) {
260
+ const coerced = _coerceInt(v);
261
+ if (typeof coerced === 'bigint')
262
+ return _int64Result(-coerced);
263
+ return -coerced;
264
+ }
265
+ function _collectionFieldAccess(object, fieldName) {
266
+ if (object == null || typeof object !== 'object' || Array.isArray(object))
267
+ return undefined;
268
+ if (object instanceof Set || object instanceof Map)
269
+ return undefined;
270
+ const keys = Object.keys(object).filter((k) => !k.startsWith('__'));
271
+ switch (fieldName) {
272
+ case 'isEmpty': return keys.length === 0;
273
+ case 'isNotEmpty': return keys.length > 0;
274
+ case 'length': return keys.length;
275
+ default: return undefined;
276
+ }
277
+ }
278
+ function _numFieldAccess(object, fieldName) {
279
+ let n;
280
+ if (typeof object === 'number')
281
+ n = object;
282
+ else {
283
+ const BD = globalThis.BallDouble;
284
+ if (BD && object instanceof BD)
285
+ n = object.value;
286
+ }
287
+ if (n === undefined)
288
+ return undefined;
289
+ switch (fieldName) {
290
+ case 'isNaN': return Number.isNaN(n);
291
+ case 'isFinite': return Number.isFinite(n);
292
+ case 'isInfinite': return !Number.isFinite(n) && !Number.isNaN(n);
293
+ case 'isNegative': return n < 0;
294
+ case 'sign': return n > 0 ? 1 : n < 0 ? -1 : 0;
295
+ default: return undefined;
296
+ }
297
+ }
298
+ // Dart List.indexWhere — used by compiled-engine list-pattern matching.
299
+ if (!Array.prototype.indexWhere) {
300
+ Object.defineProperty(Array.prototype, 'indexWhere', {
301
+ value(pred) {
302
+ for (let i = 0; i < this.length; i++) {
303
+ if (pred(this[i], i))
304
+ return i;
305
+ }
306
+ return -1;
307
+ },
308
+ writable: true,
309
+ configurable: true,
310
+ });
311
+ }
312
+ // ── Proto3 JSON normalization ──────────────────────────────────────────────
313
+ //
314
+ // The compiled engine is a transpilation of the Dart reference engine and
315
+ // expects objects that behave like Dart's protobuf runtime:
316
+ // - Every repeated field defaults to [].
317
+ // - Every string field defaults to ''.
318
+ // - metadata objects expose .fields['key'] returning a Value wrapper
319
+ // with .stringValue, .boolValue, .listValue, .whichKind(), etc.
320
+ // - undefined is replaced with null (Dart has no undefined).
321
+ /** String fields that Dart's protobuf defaults to ''. */
322
+ const STRING_DEFAULTS = [
323
+ 'name', 'module', 'function', 'outputType', 'inputType',
324
+ 'typeName', 'field', 'version', 'entryModule', 'entryFunction',
325
+ 'description', 'integrity', 'url', 'path', 'ref', 'package',
326
+ 'type_name', 'type', 'label', 'variable',
327
+ ];
328
+ /** Repeated fields that Dart's protobuf defaults to []. */
329
+ const REPEATED_DEFAULTS = [
330
+ 'modules', 'functions', 'typeDefs', 'types', 'typeAliases',
331
+ 'enums', 'moduleImports', 'fields', 'statements', 'elements',
332
+ 'values', 'parameters', 'field',
333
+ ];
334
+ function protoWrap(obj, isMetadata = false) {
335
+ if (obj == null || typeof obj !== 'object')
336
+ return obj;
337
+ if (Array.isArray(obj))
338
+ return obj.map((v) => protoWrap(v));
339
+ // Normalize `return` statements into expression calls to `std.return`.
340
+ // The Ball encoder emits `{ "return": { "value": <expr> } }` as a statement,
341
+ // but the compiled engine only recognizes `let` and `expression` statement types.
342
+ // std.return is a control-flow call that expects a messageCreation input with a
343
+ // `value` field, matching the Dart engine's _evalReturn(call, scope) dispatch.
344
+ if (obj.return !== undefined && obj.expression === undefined && obj.let === undefined) {
345
+ const retVal = obj.return;
346
+ const valueExpr = retVal?.value ?? retVal;
347
+ const inputMsg = valueExpr != null
348
+ ? { messageCreation: { typeName: '', fields: [{ name: 'value', value: valueExpr }] } }
349
+ : { messageCreation: { typeName: '', fields: [] } };
350
+ return protoWrap({
351
+ expression: {
352
+ call: {
353
+ module: 'std',
354
+ function: 'return',
355
+ input: inputMsg,
356
+ },
357
+ },
358
+ });
359
+ }
360
+ const base = {};
361
+ for (const [k, v] of Object.entries(obj)) {
362
+ base[k] = protoWrap(v, k === 'metadata');
363
+ }
364
+ for (const f of STRING_DEFAULTS) {
365
+ if (base[f] === undefined)
366
+ base[f] = '';
367
+ }
368
+ for (const f of REPEATED_DEFAULTS) {
369
+ if (base[f] === undefined)
370
+ base[f] = [];
371
+ }
372
+ // Ensure metadata is present on definition-like objects.
373
+ if (base['name'] !== undefined && base['name'] !== '') {
374
+ if (base['metadata'] === undefined || base['metadata'] === null) {
375
+ base['metadata'] = protoWrap({}, true);
376
+ }
377
+ }
378
+ // Wrap metadata objects with a Struct-compatible .fields accessor.
379
+ if (isMetadata) {
380
+ const rawMap = {};
381
+ for (const [k, v] of Object.entries(base)) {
382
+ if (k === 'fields' && Array.isArray(v) && v.length === 0)
383
+ continue;
384
+ rawMap[k] = v;
385
+ }
386
+ const dartMethods = new Set(['containsKey', 'forEach', 'entries', 'keys', 'values', 'length', 'isEmpty', 'isNotEmpty', 'toString', 'toList']);
387
+ const fieldsProxy = new Proxy(rawMap, {
388
+ get(target, prop) {
389
+ if (typeof prop !== 'string')
390
+ return undefined;
391
+ if (prop in target)
392
+ return wrapValue(target[prop]);
393
+ if (dartMethods.has(prop))
394
+ return Object.prototype[prop];
395
+ return null;
396
+ },
397
+ });
398
+ Object.defineProperty(base, 'fields', {
399
+ value: fieldsProxy,
400
+ writable: true,
401
+ configurable: true,
402
+ enumerable: false,
403
+ });
404
+ }
405
+ // Dart has no undefined — replace with null.
406
+ for (const k of Object.keys(base)) {
407
+ if (base[k] === undefined)
408
+ base[k] = null;
409
+ }
410
+ return base;
411
+ }
412
+ /** Wrap a plain JS value to provide proto Struct Value API. */
413
+ function wrapValue(raw) {
414
+ return {
415
+ _raw: raw,
416
+ whichKind() {
417
+ if (raw === null || raw === undefined)
418
+ return 'nullValue';
419
+ if (typeof raw === 'string')
420
+ return 'stringValue';
421
+ if (typeof raw === 'boolean')
422
+ return 'boolValue';
423
+ if (typeof raw === 'number')
424
+ return 'numberValue';
425
+ if (Array.isArray(raw))
426
+ return 'listValue';
427
+ if (typeof raw === 'object')
428
+ return 'structValue';
429
+ return 'nullValue';
430
+ },
431
+ get stringValue() { return typeof raw === 'string' ? raw : String(raw ?? ''); },
432
+ get boolValue() { return !!raw; },
433
+ get numberValue() { return Number(raw); },
434
+ get listValue() {
435
+ const arr = Array.isArray(raw) ? raw : [];
436
+ return { values: arr.map(wrapValue) };
437
+ },
438
+ get structValue() {
439
+ const obj = (typeof raw === 'object' && raw !== null) ? raw : {};
440
+ const f = {};
441
+ for (const [k, v] of Object.entries(obj))
442
+ f[k] = wrapValue(v);
443
+ return { fields: f };
444
+ },
445
+ hasStringValue() { return typeof raw === 'string'; },
446
+ hasBoolValue() { return typeof raw === 'boolean'; },
447
+ hasNumberValue() { return typeof raw === 'number'; },
448
+ hasListValue() { return Array.isArray(raw); },
449
+ hasStructValue() { return typeof raw === 'object' && raw !== null && !Array.isArray(raw); },
450
+ hasNullValue() { return raw == null; },
451
+ toString() { return String(raw); },
452
+ valueOf() { return raw; },
453
+ };
454
+ }
455
+ /** Dart-style toString for Ball values. */
456
+ function __bts(v) {
457
+ if (v === null || v === undefined)
458
+ return 'null';
459
+ if (typeof v === 'boolean')
460
+ return v ? 'true' : 'false';
461
+ const BD = globalThis.BallDouble;
462
+ if (BD && v instanceof BD)
463
+ return v.toString();
464
+ if (typeof v === 'bigint')
465
+ return v.toString();
466
+ if (typeof v === 'number') {
467
+ if (Number.isNaN(v))
468
+ return 'NaN';
469
+ if (!Number.isFinite(v))
470
+ return v.toString();
471
+ if (Object.is(v, -0))
472
+ return '-0.0';
473
+ if (Number.isInteger(v))
474
+ return v.toString();
475
+ const s = v.toString();
476
+ return s.includes('.') || s.includes('e') ? s : s + '.0';
477
+ }
478
+ if (typeof v === 'string')
479
+ return v;
480
+ // Unwrap BallFuture / BallGenerator before formatting
481
+ if (_isFutureLike(v))
482
+ return __bts(v.value);
483
+ if (BallGenerator && v instanceof BallGenerator)
484
+ return __bts(v.values);
485
+ if (v && typeof v === 'object' && v.__ball_future__ === true)
486
+ return __bts(v.value);
487
+ if (v && typeof v === 'object' && v.__ball_generator__ === true)
488
+ return __bts(v.values);
489
+ if (Array.isArray(v))
490
+ return '[' + v.map(__bts).join(', ') + ']';
491
+ if (v instanceof Map) {
492
+ const parts = [];
493
+ for (const [k, val] of v.entries())
494
+ parts.push(__bts(k) + ': ' + __bts(val));
495
+ return '{' + parts.join(', ') + '}';
496
+ }
497
+ if (v instanceof Set)
498
+ return '{' + [...v].map(__bts).join(', ') + '}';
499
+ if (typeof v === 'object') {
500
+ if (typeof v['__buffer__'] === 'string')
501
+ return v['__buffer__'];
502
+ if (v['__buffer__'] && Array.isArray(v['__buffer__']))
503
+ return v['__buffer__'].join('');
504
+ const tn = v['__type__'];
505
+ if (typeof tn === 'string' && (tn.endsWith(':StringBuffer') || tn === 'StringBuffer'))
506
+ return v['__buffer__'] ?? '';
507
+ if (v.toString !== Object.prototype.toString && typeof v.toString === 'function')
508
+ return v.toString();
509
+ const keys = Object.keys(v).filter((k) => !k.startsWith('__'));
510
+ if (keys.length > 0)
511
+ return '{' + keys.map((k) => __bts(k) + ': ' + __bts(v[k])).join(', ') + '}';
512
+ return '{}';
513
+ }
514
+ return String(v);
515
+ }
516
+ // ── Method dispatch handler ────────────────────────────────────────────────
517
+ //
518
+ // Intercepts method-style calls (no module, self field in input) and
519
+ // dispatches to JS built-in collection/string/number methods.
520
+ class MethodDispatchHandler {
521
+ handles(module) { return module === '' || module == null; }
522
+ init(_engine) { }
523
+ call(fn, input, _engine) {
524
+ if (input == null || typeof input !== 'object')
525
+ return undefined;
526
+ const self = input.self ?? input['self'];
527
+ if (self === undefined)
528
+ return undefined;
529
+ const arg0 = input.arg0 ?? input['arg0'];
530
+ const arg1 = input.arg1 ?? input['arg1'];
531
+ if (Array.isArray(self)) {
532
+ switch (fn) {
533
+ case 'add':
534
+ self.push(arg0);
535
+ return null;
536
+ case 'removeLast': return self.pop();
537
+ case 'removeAt': return self.splice(typeof arg0 === 'number' ? arg0 : 0, 1)[0];
538
+ case 'insert':
539
+ self.splice(typeof arg0 === 'number' ? arg0 : 0, 0, arg1);
540
+ return null;
541
+ case 'clear':
542
+ if (Array.isArray(self)) {
543
+ self.length = 0;
544
+ }
545
+ else {
546
+ for (const k of Object.keys(self))
547
+ if (!k.startsWith('__'))
548
+ delete self[k];
549
+ }
550
+ return null;
551
+ case 'contains': return self.includes(arg0);
552
+ case 'indexOf': return self.indexOf(arg0);
553
+ case 'join': return self.join(arg0 ?? ',');
554
+ case 'sublist': return self.slice(arg0, arg1);
555
+ case 'sort':
556
+ self.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
557
+ return null;
558
+ case 'reversed': return [...self].reverse();
559
+ case 'length': return self.length;
560
+ case 'isEmpty': return self.length === 0;
561
+ case 'isNotEmpty': return self.length > 0;
562
+ case 'first': return self[0];
563
+ case 'last': return self[self.length - 1];
564
+ case 'filled': return Array(typeof arg0 === 'number' ? arg0 : 0).fill(arg1);
565
+ case 'toList': return [...self];
566
+ case 'toString': return '[' + self.join(', ') + ']';
567
+ }
568
+ }
569
+ if (typeof self === 'string') {
570
+ switch (fn) {
571
+ case 'contains': return self.includes(String(arg0));
572
+ case 'substring': return self.substring(arg0, arg1);
573
+ case 'indexOf': return self.indexOf(String(arg0));
574
+ case 'split': return self.split(String(arg0));
575
+ case 'trim': return self.trim();
576
+ case 'toUpperCase': return self.toUpperCase();
577
+ case 'toLowerCase': return self.toLowerCase();
578
+ case 'replaceAll': return self.split(String(arg0)).join(String(arg1));
579
+ case 'startsWith': return self.startsWith(String(arg0));
580
+ case 'endsWith': return self.endsWith(String(arg0));
581
+ case 'padLeft': return self.padStart(arg0, arg1 ?? ' ');
582
+ case 'padRight': return self.padEnd(arg0, arg1 ?? ' ');
583
+ case 'length': return self.length;
584
+ case 'isEmpty': return self.length === 0;
585
+ case 'isNotEmpty': return self.length > 0;
586
+ case 'toString': return self;
587
+ case 'codeUnitAt': return self.charCodeAt(Number(arg0 ?? 0));
588
+ case 'compareTo': return self < String(arg0) ? -1 : self > String(arg0) ? 1 : 0;
589
+ case 'replaceFirst': return self.replace(arg0 instanceof RegExp ? arg0 : String(arg0), String(arg1 ?? ''));
590
+ }
591
+ }
592
+ if (typeof self === 'number') {
593
+ switch (fn) {
594
+ case 'toDouble': return self;
595
+ case 'toInt': return Math.trunc(self);
596
+ case 'toString': return String(self);
597
+ case 'toStringAsFixed': return self.toFixed(arg0);
598
+ case 'abs': return Math.abs(self);
599
+ case 'round': return Math.round(self);
600
+ case 'floor': return Math.floor(self);
601
+ case 'ceil': return Math.ceil(self);
602
+ case 'compareTo': return self < arg0 ? -1 : self > arg0 ? 1 : 0;
603
+ case 'clamp': return Math.min(Math.max(self, arg0), arg1);
604
+ }
605
+ }
606
+ if (typeof self === 'object' && self !== null && '__type__' in self) {
607
+ switch (fn) {
608
+ case 'write':
609
+ if (!self['__buffer__'])
610
+ self['__buffer__'] = [];
611
+ self['__buffer__'].push(String(arg0 ?? ''));
612
+ return null;
613
+ case 'writeCharCode':
614
+ if (!self['__buffer__'])
615
+ self['__buffer__'] = [];
616
+ self['__buffer__'].push(String.fromCharCode(Number(arg0 ?? 0)));
617
+ return null;
618
+ case 'toString':
619
+ if (self['__buffer__'])
620
+ return self['__buffer__'].join('');
621
+ break;
622
+ }
623
+ }
624
+ if (self instanceof Set) {
625
+ switch (fn) {
626
+ case 'union': {
627
+ const o = arg0 instanceof Set ? arg0 : new Set(Array.isArray(arg0) ? arg0 : []);
628
+ return new Set([...self, ...o]);
629
+ }
630
+ case 'intersection': {
631
+ const o = arg0 instanceof Set ? arg0 : new Set(Array.isArray(arg0) ? arg0 : []);
632
+ return new Set([...self].filter(x => o.has(x)));
633
+ }
634
+ case 'difference': {
635
+ const o = arg0 instanceof Set ? arg0 : new Set(Array.isArray(arg0) ? arg0 : []);
636
+ return new Set([...self].filter(x => !o.has(x)));
637
+ }
638
+ case 'contains': return self.has(arg0);
639
+ case 'add':
640
+ self.add(arg0);
641
+ return null;
642
+ case 'remove': return self.delete(arg0);
643
+ case 'length': return self.size;
644
+ case 'isEmpty': return self.size === 0;
645
+ case 'isNotEmpty': return self.size > 0;
646
+ case 'toList': return [...self];
647
+ case 'toString': return '{' + [...self].join(', ') + '}';
648
+ }
649
+ }
650
+ if (typeof self === 'object' && self !== null && !Array.isArray(self) && !(self instanceof Set)) {
651
+ switch (fn) {
652
+ case 'containsKey': return String(arg0) in self;
653
+ case 'containsValue': return Object.values(self).includes(arg0);
654
+ case 'remove': {
655
+ const v = self[String(arg0)];
656
+ delete self[String(arg0)];
657
+ return v;
658
+ }
659
+ case 'length': return Object.keys(self).length;
660
+ case 'isEmpty': return Object.keys(self).length === 0;
661
+ case 'isNotEmpty': return Object.keys(self).length > 0;
662
+ case 'keys': return Object.keys(self);
663
+ case 'values': return Object.values(self);
664
+ case 'entries': return Object.entries(self).map(([k, v]) => ({ key: k, value: v }));
665
+ case 'putIfAbsent':
666
+ if (!(String(arg0) in self))
667
+ self[String(arg0)] = typeof arg1 === 'function' ? arg1() : arg1;
668
+ return self[String(arg0)];
669
+ case 'toString': return '{' + Object.entries(self).map(([k, v]) => k + ': ' + v).join(', ') + '}';
670
+ }
671
+ }
672
+ return undefined;
673
+ }
674
+ }
675
+ // ── Register extra std functions ───────────────────────────────────────────
676
+ //
677
+ // The compiled Dart engine's StdModuleHandler builds its dispatch table from
678
+ // the Dart std library. In TypeScript we need to provide JS implementations
679
+ // for functions that the compiled engine's _buildStdDispatch doesn't cover
680
+ // (collection higher-order functions, string helpers, etc.).
681
+ function registerExtraStdFunctions(stdHandler) {
682
+ const _r = stdHandler.register.bind(stdHandler);
683
+ const _m = (i) => (typeof i === 'object' && i !== null) ? i : {};
684
+ // ── Collection: list_* ─────────────────────────────────────────────
685
+ _r('list_foreach', async (i) => {
686
+ const m = _m(i);
687
+ const coll = m['list'] ?? m['collection'];
688
+ const fn = m['function'] ?? m['value'] ?? m['callback'];
689
+ if (typeof fn !== 'function')
690
+ return null;
691
+ if (Array.isArray(coll)) {
692
+ for (const item of coll) {
693
+ let r = fn(item);
694
+ if (r?.then)
695
+ r = await r;
696
+ }
697
+ }
698
+ else if (coll instanceof Set) {
699
+ for (const item of coll) {
700
+ let r = fn(item);
701
+ if (r?.then)
702
+ r = await r;
703
+ }
704
+ }
705
+ else if (typeof coll === 'object' && coll !== null) {
706
+ for (const [k, v] of Object.entries(coll).filter(([k]) => !k.startsWith('__'))) {
707
+ let r = fn({ 'key': k, 'value': v, 'arg0': k, 'arg1': v });
708
+ if (r?.then)
709
+ r = await r;
710
+ }
711
+ }
712
+ return null;
713
+ });
714
+ _r('list_map', async (i) => {
715
+ const m = _m(i);
716
+ const list = m['list'] ?? m['collection'] ?? [];
717
+ const fn = m['function'] ?? m['value'] ?? m['callback'];
718
+ if (!Array.isArray(list) || typeof fn !== 'function')
719
+ return [];
720
+ const result = [];
721
+ for (const item of list) {
722
+ let r = fn(item);
723
+ if (r?.then)
724
+ r = await r;
725
+ result.push(r);
726
+ }
727
+ return result;
728
+ });
729
+ _r('list_filter', async (i) => {
730
+ const m = _m(i);
731
+ const list = m['list'] ?? m['collection'] ?? [];
732
+ const fn = m['function'] ?? m['value'] ?? m['callback'];
733
+ if (!Array.isArray(list) || typeof fn !== 'function')
734
+ return [];
735
+ const result = [];
736
+ for (const item of list) {
737
+ let r = fn(item);
738
+ if (r?.then)
739
+ r = await r;
740
+ if (r)
741
+ result.push(item);
742
+ }
743
+ return result;
744
+ });
745
+ _r('list_where', async (i) => {
746
+ const m = _m(i);
747
+ const list = m['list'] ?? m['collection'] ?? [];
748
+ const fn = m['function'] ?? m['value'] ?? m['callback'];
749
+ if (!Array.isArray(list) || typeof fn !== 'function')
750
+ return [];
751
+ const result = [];
752
+ for (const item of list) {
753
+ let r = fn(item);
754
+ if (r?.then)
755
+ r = await r;
756
+ if (r)
757
+ result.push(item);
758
+ }
759
+ return result;
760
+ });
761
+ _r('list_reduce', async (i) => {
762
+ const m = _m(i);
763
+ const list = m['list'] ?? m['collection'] ?? [];
764
+ const fn = m['function'] ?? m['value'] ?? m['callback'];
765
+ const init = m['initial'] ?? m['initialValue'];
766
+ if (!Array.isArray(list) || typeof fn !== 'function')
767
+ return init ?? null;
768
+ let acc = init;
769
+ for (const item of list) {
770
+ if (acc === undefined) {
771
+ acc = item;
772
+ continue;
773
+ }
774
+ let r = fn({ 'arg0': acc, 'arg1': item, 'left': acc, 'right': item });
775
+ if (r?.then)
776
+ r = await r;
777
+ acc = r;
778
+ }
779
+ return acc;
780
+ });
781
+ _r('list_sort', async (i) => {
782
+ const m = _m(i);
783
+ const list = m['list'] ?? m['collection'] ?? [];
784
+ const fn = m['compare'] ?? m['comparator'] ?? m['function'] ?? m['value'];
785
+ if (!Array.isArray(list))
786
+ return [];
787
+ const sorted = [...list];
788
+ if (typeof fn === 'function') {
789
+ async function ms(arr) {
790
+ if (arr.length <= 1)
791
+ return arr;
792
+ const mid = Math.floor(arr.length / 2);
793
+ const l = await ms(arr.slice(0, mid)), r = await ms(arr.slice(mid));
794
+ const res = [];
795
+ let li = 0, ri = 0;
796
+ while (li < l.length && ri < r.length) {
797
+ let c = fn({ 'arg0': l[li], 'arg1': r[ri], 'left': l[li], 'right': r[ri] });
798
+ if (c?.then)
799
+ c = await c;
800
+ if ((typeof c === 'number' ? c : 0) <= 0)
801
+ res.push(l[li++]);
802
+ else
803
+ res.push(r[ri++]);
804
+ }
805
+ while (li < l.length)
806
+ res.push(l[li++]);
807
+ while (ri < r.length)
808
+ res.push(r[ri++]);
809
+ return res;
810
+ }
811
+ return await ms(sorted);
812
+ }
813
+ sorted.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
814
+ return sorted;
815
+ });
816
+ _r('list_any', async (i) => {
817
+ const m = _m(i);
818
+ const list = m['list'] ?? m['collection'] ?? [];
819
+ const fn = m['function'] ?? m['value'] ?? m['callback'];
820
+ if (!Array.isArray(list) || typeof fn !== 'function')
821
+ return false;
822
+ for (const item of list) {
823
+ let r = fn(item);
824
+ if (r?.then)
825
+ r = await r;
826
+ if (r)
827
+ return true;
828
+ }
829
+ return false;
830
+ });
831
+ _r('list_every', async (i) => {
832
+ const m = _m(i);
833
+ const list = m['list'] ?? m['collection'] ?? [];
834
+ const fn = m['function'] ?? m['value'] ?? m['callback'];
835
+ if (!Array.isArray(list) || typeof fn !== 'function')
836
+ return false;
837
+ for (const item of list) {
838
+ let r = fn(item);
839
+ if (r?.then)
840
+ r = await r;
841
+ if (!r)
842
+ return false;
843
+ }
844
+ return true;
845
+ });
846
+ _r('list_find', async (i) => {
847
+ const m = _m(i);
848
+ const list = m['list'] ?? m['collection'] ?? [];
849
+ const fn = m['function'] ?? m['value'] ?? m['callback'];
850
+ if (!Array.isArray(list) || typeof fn !== 'function')
851
+ return null;
852
+ for (const item of list) {
853
+ let r = fn(item);
854
+ if (r?.then)
855
+ r = await r;
856
+ if (r)
857
+ return item;
858
+ }
859
+ return null;
860
+ });
861
+ _r('list_expand', async (i) => {
862
+ const m = _m(i);
863
+ const list = m['list'] ?? m['collection'] ?? [];
864
+ const fn = m['function'] ?? m['value'] ?? m['callback'];
865
+ if (!Array.isArray(list) || typeof fn !== 'function')
866
+ return [];
867
+ const result = [];
868
+ for (const item of list) {
869
+ let r = fn(item);
870
+ if (r?.then)
871
+ r = await r;
872
+ if (Array.isArray(r))
873
+ result.push(...r);
874
+ else
875
+ result.push(r);
876
+ }
877
+ return result;
878
+ });
879
+ _r('list_length', (i) => { const m = _m(i); const l = m['list'] ?? m['collection'] ?? i; return Array.isArray(l) ? l.length : (typeof l === 'string' ? l.length : 0); });
880
+ _r('list_reversed', (i) => { const m = _m(i); const l = m['list'] ?? m['collection'] ?? []; return Array.isArray(l) ? [...l].reverse() : []; });
881
+ _r('list_sublist', (i) => { const m = _m(i); const l = m['list'] ?? m['collection'] ?? []; const s = Number(m['start'] ?? m['arg0'] ?? 0); const e = m['end'] ?? m['arg1']; return Array.isArray(l) ? l.slice(s, e != null ? Number(e) : undefined) : []; });
882
+ _r('list_index_of', (i) => { const m = _m(i); const l = m['list'] ?? m['collection'] ?? []; const v = m['value'] ?? m['element']; if (typeof l === 'string')
883
+ return l.indexOf(String(v)); return Array.isArray(l) ? l.indexOf(v) : -1; });
884
+ _r('list_add', (i) => { const m = _m(i); const l = m['list'] ?? m['collection']; const v = m['value'] ?? m['element']; if (Array.isArray(l))
885
+ l.push(v); return null; });
886
+ _r('list_add_all', (i) => { const m = _m(i); const l = m['list'] ?? m['collection']; const o = m['other'] ?? m['elements'] ?? []; if (Array.isArray(l) && Array.isArray(o))
887
+ l.push(...o); return null; });
888
+ _r('list_remove_at', (i) => { const m = _m(i); const l = m['list'] ?? m['collection']; const idx = Number(m['index'] ?? 0); return Array.isArray(l) ? l.splice(idx, 1)[0] : null; });
889
+ _r('list_insert', (i) => { const m = _m(i); const l = m['list'] ?? m['collection']; const idx = Number(m['index'] ?? 0); const v = m['value'] ?? m['element']; if (Array.isArray(l))
890
+ l.splice(idx, 0, v); return null; });
891
+ _r('list_clear', (i) => { const m = _m(i); const l = m['list'] ?? m['collection']; if (Array.isArray(l))
892
+ l.length = 0; return null; });
893
+ _r('list_contains', (i) => {
894
+ const m = _m(i);
895
+ const l = m['list'] ?? m['collection'] ?? [];
896
+ const v = m['value'] ?? m['element'];
897
+ if (l instanceof Set) {
898
+ if (l.has(v))
899
+ return true;
900
+ if (typeof v === 'number')
901
+ return l.has(String(v));
902
+ if (typeof v === 'string') {
903
+ const n = Number(v);
904
+ if (!isNaN(n))
905
+ return l.has(n);
906
+ }
907
+ return false;
908
+ }
909
+ if (Array.isArray(l))
910
+ return l.includes(v);
911
+ if (typeof l === 'string')
912
+ return l.includes(String(v));
913
+ return false;
914
+ });
915
+ _r('list_remove', (i) => { const m = _m(i); const l = m['list'] ?? m['collection']; const v = m['value'] ?? m['element']; if (Array.isArray(l)) {
916
+ const idx = l.indexOf(v);
917
+ if (idx >= 0) {
918
+ l.splice(idx, 1);
919
+ return true;
920
+ }
921
+ } return false; });
922
+ _r('list_remove_last', (i) => { const m = _m(i); const l = m['list'] ?? m['collection']; return Array.isArray(l) ? l.pop() : null; });
923
+ _r('list_to_list', (i) => { const m = _m(i); const r = m['list'] ?? m['value']; if (Array.isArray(r))
924
+ return [...r]; if (r instanceof Set)
925
+ return [...r]; return []; });
926
+ _r('list_join', (i) => {
927
+ const m = _m(i);
928
+ const l = m['list'] ?? m['collection'] ?? [];
929
+ const sep = m['separator'] ?? m['delimiter'] ?? ', ';
930
+ if (!Array.isArray(l))
931
+ return '';
932
+ return l.map((x) => { if (x === null || x === undefined)
933
+ return 'null'; if (typeof x === 'boolean')
934
+ return x ? 'true' : 'false'; return String(x); }).join(String(sep));
935
+ });
936
+ _r('list_push', (i) => { const m = _m(i); const l = m['list'] ?? m['collection']; const v = m['value'] ?? m['element']; if (Array.isArray(l)) {
937
+ l.push(v);
938
+ return l;
939
+ } return [...(l ?? []), v]; });
940
+ _r('list_pop', (i) => { const m = _m(i); const l = m['list'] ?? m['collection']; return (Array.isArray(l) && l.length > 0) ? l.pop() : null; });
941
+ _r('list_peek', (i) => { const m = _m(i); const l = m['list'] ?? m['collection']; return (Array.isArray(l) && l.length > 0) ? l[l.length - 1] : null; });
942
+ _r('list_take', (i) => { const m = _m(i); const l = m['list'] ?? m['collection'] ?? []; const n = Number(m['count'] ?? m['value'] ?? m['n'] ?? 0); return Array.isArray(l) ? l.slice(0, n) : []; });
943
+ _r('list_skip', (i) => { const m = _m(i); const l = m['list'] ?? m['collection'] ?? []; const n = Number(m['count'] ?? m['value'] ?? m['n'] ?? 0); return Array.isArray(l) ? l.slice(n) : []; });
944
+ _r('list_first', (i) => { const m = _m(i); const l = m['list'] ?? m['collection'] ?? []; return (Array.isArray(l) && l.length > 0) ? l[0] : null; });
945
+ _r('list_last', (i) => { const m = _m(i); const l = m['list'] ?? m['collection'] ?? []; return (Array.isArray(l) && l.length > 0) ? l[l.length - 1] : null; });
946
+ _r('list_set', (i) => { const m = _m(i); const l = m['list'] ?? m['collection']; const idx = Number(m['index'] ?? 0); if (Array.isArray(l))
947
+ l[idx] = m['value']; return null; });
948
+ _r('list_slice', (i) => {
949
+ const m = _m(i);
950
+ const l = m['list'] ?? m['collection'] ?? [];
951
+ if (!Array.isArray(l))
952
+ return [];
953
+ if ('start' in m || 'end' in m)
954
+ return l.slice(Number(m['start'] ?? 0), m['end'] != null ? Number(m['end']) : undefined);
955
+ const val = m['value'];
956
+ if (Array.isArray(val) && val.length >= 2)
957
+ return l.slice(Number(val[0]), Number(val[1]));
958
+ if ('arg0' in m)
959
+ return l.slice(Number(m['arg0'] ?? 0), m['arg1'] != null ? Number(m['arg1']) : undefined);
960
+ if (val != null && !Array.isArray(val))
961
+ return l.slice(Number(val));
962
+ return [...l];
963
+ });
964
+ _r('list_of', (i) => { const m = _m(i); const s = m['list'] ?? m['iterable'] ?? m['arg0'] ?? m['value'] ?? i; return Array.isArray(s) ? [...s] : (s instanceof Set ? [...s] : []); });
965
+ _r('dart_list_of', (i) => { const m = _m(i); const s = m['list'] ?? m['iterable'] ?? m['arg0'] ?? m['value'] ?? i; return Array.isArray(s) ? [...s] : (s instanceof Set ? [...s] : []); });
966
+ _r('list_from', (i) => { const m = _m(i); const s = m['list'] ?? m['iterable'] ?? m['arg0'] ?? m['value'] ?? i; return Array.isArray(s) ? [...s] : (s instanceof Set ? [...s] : []); });
967
+ _r('dart_list_from', (i) => { const m = _m(i); const s = m['list'] ?? m['iterable'] ?? m['arg0'] ?? m['value'] ?? i; return Array.isArray(s) ? [...s] : (s instanceof Set ? [...s] : []); });
968
+ // ── Collection: map_* ──────────────────────────────────────────────
969
+ const _mapFromEntries = (i) => {
970
+ const m = _m(i);
971
+ const _own = (o, k) => Object.prototype.hasOwnProperty.call(o, k) ? o[k] : undefined;
972
+ const entries = _own(m, 'entries') ?? _own(m, 'list') ?? _own(m, 'arg0') ?? [];
973
+ const result = {};
974
+ if (Array.isArray(entries)) {
975
+ for (const e of entries) {
976
+ if (typeof e === 'object' && e !== null) {
977
+ result[_own(e, 'key') ?? _own(e, 'arg0') ?? _own(e, 'name') ?? ''] = Object.prototype.hasOwnProperty.call(e, 'value') ? e['value'] : (_own(e, 'arg1') ?? undefined);
978
+ }
979
+ }
980
+ }
981
+ return result;
982
+ };
983
+ _r('map_from_entries', _mapFromEntries);
984
+ _r('map_fromEntries', _mapFromEntries);
985
+ _r('fromEntries', _mapFromEntries);
986
+ _r('map_containsKey', (i) => { const m = _m(i); const map = m['map'] ?? m['collection'] ?? {}; const key = m['key'] ?? m['value'] ?? ''; return typeof map === 'object' && map !== null ? String(key) in map : false; });
987
+ _r('map_contains_key', (i) => { const m = _m(i); const map = m['map'] ?? m['collection'] ?? {}; const key = m['key'] ?? m['value'] ?? ''; return typeof map === 'object' && map !== null ? String(key) in map : false; });
988
+ _r('map_length', (i) => { const m = _m(i); const map = m['map'] ?? m['collection'] ?? {}; return typeof map === 'object' && map !== null ? Object.keys(map).filter(k => !k.startsWith('__')).length : 0; });
989
+ _r('map_keys', (i) => { const m = _m(i); const map = m['map'] ?? m['collection'] ?? {}; return typeof map === 'object' && map !== null ? Object.keys(map).filter(k => !k.startsWith('__')) : []; });
990
+ _r('map_values', (i) => { const m = _m(i); const map = m['map'] ?? m['collection'] ?? {}; return typeof map === 'object' && map !== null ? Object.keys(map).filter(k => !k.startsWith('__')).map(k => map[k]) : []; });
991
+ _r('map_entries', (i) => { const m = _m(i); const map = m['map'] ?? m['collection'] ?? {}; return typeof map === 'object' && map !== null ? Object.entries(map).filter(([k]) => !k.startsWith('__')).map(([k, v]) => ({ key: k, value: v })) : []; });
992
+ _r('map_remove', (i) => { const m = _m(i); const map = m['map'] ?? m['collection']; const key = m['key'] ?? ''; if (typeof map === 'object' && map !== null) {
993
+ const v = map[String(key)];
994
+ delete map[String(key)];
995
+ return v;
996
+ } return null; });
997
+ _r('map_put_if_absent', (i) => { const m = _m(i); const map = m['map'] ?? m['collection']; const key = String(m['key'] ?? ''); const value = m['value']; const ia = m['ifAbsent'] ?? m['if_absent']; if (typeof map === 'object' && map !== null) {
998
+ if (!(key in map))
999
+ map[key] = typeof ia === 'function' ? ia() : (value ?? null);
1000
+ return map[key];
1001
+ } return null; });
1002
+ _r('map_for_each', async (i) => {
1003
+ const m = _m(i);
1004
+ const map = m['map'] ?? m['collection'] ?? {};
1005
+ const fn = m['function'] ?? m['callback'];
1006
+ if (typeof fn === 'function' && typeof map === 'object' && map !== null) {
1007
+ for (const [k, v] of Object.entries(map).filter(([k]) => !k.startsWith('__'))) {
1008
+ let r = fn({ key: k, value: v, arg0: k, arg1: v });
1009
+ if (r?.then)
1010
+ await r;
1011
+ }
1012
+ }
1013
+ return null;
1014
+ });
1015
+ _r('map_map', async (i) => {
1016
+ const m = _m(i);
1017
+ const map = m['map'] ?? m['collection'] ?? {};
1018
+ const fn = m['function'] ?? m['callback'];
1019
+ const result = {};
1020
+ if (typeof fn === 'function' && typeof map === 'object' && map !== null) {
1021
+ for (const [k, v] of Object.entries(map).filter(([mk]) => !mk.startsWith('__'))) {
1022
+ let r = fn({ key: k, value: v, arg0: k, arg1: v });
1023
+ if (r?.then)
1024
+ r = await r;
1025
+ if (typeof r === 'object' && r !== null && 'key' in r)
1026
+ result[r.key] = r.value;
1027
+ else
1028
+ result[k] = r;
1029
+ }
1030
+ }
1031
+ return result;
1032
+ });
1033
+ _r('map_create', (i) => {
1034
+ const m = _m(i);
1035
+ const result = {};
1036
+ // Read `entry`/`entries` as OWN properties only. Plain bracket access hits
1037
+ // the Object.prototype `.entries` getter installed by the preamble, which
1038
+ // would (wrongly) treat the input map's own keys (e.g. `type_args`) as
1039
+ // entry records and re-inject them into the result.
1040
+ const hop = Object.prototype.hasOwnProperty;
1041
+ const entries = hop.call(m, 'entry') ? m['entry'] : (hop.call(m, 'entries') ? m['entries'] : undefined);
1042
+ if (Array.isArray(entries)) {
1043
+ for (const e of entries) {
1044
+ if (typeof e === 'object' && e !== null)
1045
+ result[e['key'] ?? e['name'] ?? ''] = e['value'];
1046
+ }
1047
+ }
1048
+ else if (typeof entries === 'object' && entries !== null)
1049
+ result[entries['key'] ?? entries['name'] ?? ''] = entries['value'];
1050
+ return result;
1051
+ });
1052
+ _r('map_update', (i) => {
1053
+ const m = _m(i);
1054
+ const map = m['map'] ?? m['collection'];
1055
+ const key = String(m['key'] ?? '');
1056
+ const fn = m['update'] ?? m['function'] ?? m['value'];
1057
+ const ia = m['ifAbsent'] ?? m['if_absent'];
1058
+ if (typeof map === 'object' && map !== null) {
1059
+ if (key in map && typeof fn === 'function')
1060
+ map[key] = fn(map[key]);
1061
+ else if (typeof ia === 'function')
1062
+ map[key] = ia();
1063
+ return map[key];
1064
+ }
1065
+ return null;
1066
+ });
1067
+ _r('map_clear', (i) => { const m = _m(i); const map = m['map'] ?? m['collection']; if (typeof map === 'object' && map !== null) {
1068
+ for (const k of Object.keys(map)) {
1069
+ if (!k.startsWith('__'))
1070
+ delete map[k];
1071
+ }
1072
+ } return null; });
1073
+ _r('map_add_all', (i) => { const m = _m(i); const map = m['map'] ?? m['collection']; const other = m['other'] ?? m['entries'] ?? {}; if (typeof map === 'object' && map !== null && typeof other === 'object' && other !== null) {
1074
+ for (const [k, v] of Object.entries(other)) {
1075
+ if (!k.startsWith('__'))
1076
+ map[k] = v;
1077
+ }
1078
+ } return null; });
1079
+ // ── Collection: set_* ──────────────────────────────────────────────
1080
+ _r('set_union', (i) => { const m = _m(i); const a = m['set'] ?? m['set1'] ?? m['collection'] ?? []; const b = m['other'] ?? m['set2'] ?? []; return new Set([...(Array.isArray(a) ? a : (a instanceof Set ? [...a] : [])), ...(Array.isArray(b) ? b : (b instanceof Set ? [...b] : []))]); });
1081
+ _r('set_intersection', (i) => { const m = _m(i); const a = m['set'] ?? m['set1'] ?? m['collection'] ?? []; const b = m['other'] ?? m['set2'] ?? []; const sA = a instanceof Set ? a : new Set(Array.isArray(a) ? a : []); const sB = b instanceof Set ? b : new Set(Array.isArray(b) ? b : []); return new Set([...sA].filter(x => sB.has(x))); });
1082
+ _r('set_difference', (i) => { const m = _m(i); const a = m['set'] ?? m['set1'] ?? m['collection'] ?? []; const b = m['other'] ?? m['set2'] ?? []; const sA = a instanceof Set ? a : new Set(Array.isArray(a) ? a : []); const sB = b instanceof Set ? b : new Set(Array.isArray(b) ? b : []); return new Set([...sA].filter(x => !sB.has(x))); });
1083
+ _r('set_contains', (i) => { const m = _m(i); const s = m['set'] ?? m['collection'] ?? new Set(); const v = m['value'] ?? m['element']; if (s instanceof Set)
1084
+ return s.has(v); if (Array.isArray(s))
1085
+ return s.includes(v); return false; });
1086
+ _r('set_to_list', (i) => { const m = _m(i); const s = m['set'] ?? m['collection'] ?? []; return s instanceof Set ? [...s] : (Array.isArray(s) ? [...new Set(s)] : []); });
1087
+ _r('set_length', (i) => { const m = _m(i); const s = m['set'] ?? m['collection'] ?? new Set(); return s instanceof Set ? s.size : (Array.isArray(s) ? new Set(s).size : 0); });
1088
+ _r('set_from', (i) => { const m = _m(i); const l = m['list'] ?? m['collection'] ?? m['iterable'] ?? []; return new Set(Array.isArray(l) ? l : []); });
1089
+ _r('set_create', (i) => { const m = _m(i); return new Set(Array.isArray(m['elements'] ?? m['values'] ?? []) ? (m['elements'] ?? m['values'] ?? []) : []); });
1090
+ _r('set_add', (i) => { const m = _m(i); const s = m['set'] ?? m['collection']; const v = m['value'] ?? m['element']; if (s instanceof Set) {
1091
+ s.add(v);
1092
+ return true;
1093
+ } return false; });
1094
+ _r('set_remove', (i) => { const m = _m(i); const s = m['set'] ?? m['collection']; const v = m['value'] ?? m['element']; return s instanceof Set ? s.delete(v) : false; });
1095
+ _r('set_add_all', (i) => { const m = _m(i); const s = m['set'] ?? m['collection']; const other = m['other'] ?? m['elements'] ?? []; if (s instanceof Set) {
1096
+ const items = Array.isArray(other) ? other : (other instanceof Set ? [...other] : []);
1097
+ for (const item of items)
1098
+ s.add(item);
1099
+ } return null; });
1100
+ _r('union', (i) => { const m = _m(i); const self = m['self'] ?? m['set'] ?? new Set(); const other = m['arg0'] ?? m['other'] ?? new Set(); const sA = self instanceof Set ? self : new Set(Array.isArray(self) ? self : []); const sB = other instanceof Set ? other : new Set(Array.isArray(other) ? other : []); return new Set([...sA, ...sB]); });
1101
+ _r('intersection', (i) => { const m = _m(i); const self = m['self'] ?? m['set'] ?? new Set(); const other = m['arg0'] ?? m['other'] ?? new Set(); const sA = self instanceof Set ? self : new Set(Array.isArray(self) ? self : []); const sB = other instanceof Set ? other : new Set(Array.isArray(other) ? other : []); return new Set([...sA].filter(x => sB.has(x))); });
1102
+ _r('difference', (i) => { const m = _m(i); const self = m['self'] ?? m['set'] ?? new Set(); const other = m['arg0'] ?? m['other'] ?? new Set(); const sA = self instanceof Set ? self : new Set(Array.isArray(self) ? self : []); const sB = other instanceof Set ? other : new Set(Array.isArray(other) ? other : []); return new Set([...sA].filter(x => !sB.has(x))); });
1103
+ // ── String ─────────────────────────────────────────────────────────
1104
+ _r('string_code_unit_at', (i) => { const m = _m(i); return String(m['value'] ?? m['string'] ?? '').charCodeAt(Number(m['index'] ?? 0)); });
1105
+ _r('string_char_code_at', (i) => { const m = _m(i); return String(m['value'] ?? m['string'] ?? '').charCodeAt(Number(m['index'] ?? m['arg0'] ?? 0)); });
1106
+ _r('string_from_char_code', (i) => { const m = _m(i); return String.fromCharCode(Number(m['value'] ?? m['code'] ?? m['arg0'] ?? 0)); });
1107
+ _r('string_from_char_codes', (i) => { const m = _m(i); const codes = m['codes'] ?? m['list'] ?? []; return Array.isArray(codes) ? String.fromCharCode(...codes.map(Number)) : ''; });
1108
+ _r('string_replace', (i) => { const m = _m(i); return String(m['value'] ?? m['string'] ?? '').replace(String(m['from'] ?? m['pattern'] ?? ''), String(m['to'] ?? m['replacement'] ?? '')); });
1109
+ _r('string_replace_all', (i) => { const m = _m(i); return String(m['value'] ?? m['string'] ?? '').split(String(m['from'] ?? m['pattern'] ?? '')).join(String(m['to'] ?? m['replacement'] ?? '')); });
1110
+ _r('string_repeat', (i) => { const m = _m(i); return String(m['value'] ?? '').repeat(Number(m['count'] ?? m['times'] ?? 0)); });
1111
+ _r('string_split', (i) => { const m = _m(i); return String(m['value'] ?? m['string'] ?? '').split(String(m['separator'] ?? m['pattern'] ?? m['delimiter'] ?? '')); });
1112
+ _r('string_substring', (i) => { const m = _m(i); return String(m['value'] ?? m['string'] ?? '').substring(Number(m['start'] ?? 0), m['end'] != null ? Number(m['end']) : undefined); });
1113
+ _r('string_contains', (i) => { const m = _m(i); return String(m['value'] ?? m['string'] ?? '').includes(String(m['substring'] ?? m['pattern'] ?? m['other'] ?? '')); });
1114
+ _r('string_length', (i) => { const m = _m(i); return String(m['value'] ?? m['string'] ?? '').length; });
1115
+ _r('string_index_of', (i) => { const m = _m(i); return String(m['value'] ?? m['string'] ?? m['left'] ?? m['arg0'] ?? '').indexOf(String(m['substring'] ?? m['pattern'] ?? m['right'] ?? m['arg1'] ?? '')); });
1116
+ _r('string_to_upper_case', (i) => { const m = _m(i); return String(m['value'] ?? m['string'] ?? '').toUpperCase(); });
1117
+ _r('string_to_lower_case', (i) => { const m = _m(i); return String(m['value'] ?? m['string'] ?? '').toLowerCase(); });
1118
+ _r('string_trim', (i) => { const m = _m(i); return String(m['value'] ?? m['string'] ?? '').trim(); });
1119
+ _r('string_starts_with', (i) => { const m = _m(i); return String(m['value'] ?? m['string'] ?? '').startsWith(String(m['prefix'] ?? m['pattern'] ?? '')); });
1120
+ _r('string_ends_with', (i) => { const m = _m(i); return String(m['value'] ?? m['string'] ?? '').endsWith(String(m['suffix'] ?? m['pattern'] ?? '')); });
1121
+ _r('string_pad_left', (i) => { const m = _m(i); return String(m['value'] ?? m['string'] ?? '').padStart(Number(m['width'] ?? m['length'] ?? 0), String(m['padding'] ?? m['pad'] ?? ' ')); });
1122
+ _r('string_pad_right', (i) => { const m = _m(i); return String(m['value'] ?? m['string'] ?? '').padEnd(Number(m['width'] ?? m['length'] ?? 0), String(m['padding'] ?? m['pad'] ?? ' ')); });
1123
+ _r('string_char_at', (i) => { const m = _m(i); return String(m['value'] ?? m['string'] ?? '').charAt(Number(m['index'] ?? m['arg0'] ?? 0)); });
1124
+ _r('string_to_int', (i) => {
1125
+ const m = _m(i);
1126
+ const s = String(m['value'] ?? m['string'] ?? i ?? '').trim();
1127
+ if (!/^-?\d+$/.test(s))
1128
+ throw Object.assign(new Error('FormatException: ' + s), { __type__: 'FormatException', message: s });
1129
+ return parseInt(s, 10);
1130
+ });
1131
+ _r('writeCharCode', (i) => { const m = _m(i); const self = m['self']; if (typeof self === 'object' && self !== null) {
1132
+ self['__buffer__'] = (self['__buffer__'] ?? '') + String.fromCharCode(Number(m['arg0'] ?? m['value'] ?? 0));
1133
+ } return null; });
1134
+ _r('write', (i) => { const m = _m(i); const self = m['self']; if (typeof self === 'object' && self !== null) {
1135
+ self['__buffer__'] = (self['__buffer__'] ?? '') + String(m['arg0'] ?? m['value'] ?? '');
1136
+ } return null; });
1137
+ // ── Conversion ─────────────────────────────────────────────────────
1138
+ _r('to_double', (i) => _toDoubleValue(_m(i)['value'] ?? _m(i)['arg0'] ?? i));
1139
+ _r('int_to_double', (i) => _toDoubleValue(_m(i)['value'] ?? _m(i)['arg0'] ?? i));
1140
+ _r('to_int', (i) => _toIntValue(_m(i)['value'] ?? _m(i)['arg0'] ?? i));
1141
+ _r('double_to_int', (i) => _toIntValue(_m(i)['value'] ?? _m(i)['arg0'] ?? i));
1142
+ _r('to_string', (i) => {
1143
+ const m = _m(i);
1144
+ let v = Object.prototype.hasOwnProperty.call(m, 'value') ? m['value'] : i;
1145
+ const BD = globalThis.BallDouble;
1146
+ while (v != null && typeof v === 'object' && !Array.isArray(v) && !(v instanceof Set) && !(v instanceof Map)) {
1147
+ if (BD && v instanceof BD)
1148
+ break;
1149
+ const keys = Object.keys(v).filter((k) => !k.startsWith('__'));
1150
+ if (keys.length === 1 && keys[0] === 'value' && Object.prototype.hasOwnProperty.call(v, 'value')) {
1151
+ v = v['value'];
1152
+ continue;
1153
+ }
1154
+ break;
1155
+ }
1156
+ return __bts(v);
1157
+ });
1158
+ _r('equals', (i) => {
1159
+ const m = _m(i);
1160
+ const a = m['left'] ?? m['value'] ?? m['arg0'];
1161
+ const b = m['right'] ?? m['other'] ?? m['arg1'];
1162
+ const BD = globalThis.BallDouble;
1163
+ const unwrap = (v) => (BD && v instanceof BD) ? v.value : v;
1164
+ const av = unwrap(a);
1165
+ const bv = unwrap(b);
1166
+ if (typeof av === 'number' && typeof bv === 'number') {
1167
+ if (Number.isNaN(av) || Number.isNaN(bv))
1168
+ return false;
1169
+ return av === bv;
1170
+ }
1171
+ if (av === bv)
1172
+ return true;
1173
+ if (av != null && bv != null)
1174
+ return __bts(av) === __bts(bv);
1175
+ return false;
1176
+ });
1177
+ _r('not_equals', (i) => {
1178
+ const m = _m(i);
1179
+ const a = m['left'] ?? m['value'] ?? m['arg0'];
1180
+ const b = m['right'] ?? m['other'] ?? m['arg1'];
1181
+ const BD = globalThis.BallDouble;
1182
+ const unwrap = (v) => (BD && v instanceof BD) ? v.value : v;
1183
+ const av = unwrap(a);
1184
+ const bv = unwrap(b);
1185
+ if (typeof av === 'number' && typeof bv === 'number') {
1186
+ if (Number.isNaN(av) || Number.isNaN(bv))
1187
+ return true;
1188
+ return av !== bv;
1189
+ }
1190
+ if (av === bv)
1191
+ return false;
1192
+ if (av != null && bv != null)
1193
+ return __bts(av) !== __bts(bv);
1194
+ return true;
1195
+ });
1196
+ _r('concat', (i) => { const m = _m(i); return String(m['left'] ?? '') + String(m['right'] ?? ''); });
1197
+ _r('null_check', (i) => { const m = _m(i); const v = m['value'] ?? i; if (v == null)
1198
+ throw new Error('Null check operator used on a null value'); return v; });
1199
+ _r('compare_to', (i) => {
1200
+ const m = _m(i);
1201
+ const l = m['left'] ?? m['value'] ?? m['self'] ?? m['a'] ?? 0;
1202
+ const r = m['right'] ?? m['other'] ?? m['arg0'] ?? m['b'] ?? 0;
1203
+ if (typeof l === 'string' && typeof r === 'string')
1204
+ return l < r ? -1 : l > r ? 1 : 0;
1205
+ const [lv, rv] = _coerceNumPair(l, r);
1206
+ return lv < rv ? -1 : lv > rv ? 1 : 0;
1207
+ });
1208
+ // Dart / always returns double — wrap result in BallDouble
1209
+ _r('divide_double', (i) => {
1210
+ const m = _m(i);
1211
+ const l = Number(m['left'] ?? m['value'] ?? m['arg0'] ?? 0);
1212
+ const r = Number(m['right'] ?? m['other'] ?? m['arg1'] ?? 1);
1213
+ return _makeBallDouble(l / r);
1214
+ });
1215
+ // ── Math ───────────────────────────────────────────────────────────
1216
+ _r('math_abs', (i) => _mathAbs(_m(i)['value'] ?? 0));
1217
+ _r('math_max', (i) => { const m = _m(i); return Math.max(Number(m['left'] ?? m['a'] ?? 0), Number(m['right'] ?? m['b'] ?? 0)); });
1218
+ _r('math_min', (i) => { const m = _m(i); return Math.min(Number(m['left'] ?? m['a'] ?? 0), Number(m['right'] ?? m['b'] ?? 0)); });
1219
+ _r('math_sqrt', (i) => Math.sqrt(Number(_m(i)['value'] ?? 0)));
1220
+ _r('math_pow', (i) => { const m = _m(i); return Math.pow(Number(m['base'] ?? m['left'] ?? 0), Number(m['exponent'] ?? m['right'] ?? 0)); });
1221
+ // math_sign / math_is_infinite are registered here so a freshly regenerated
1222
+ // compiled_engine.ts (which emits them as base calls, not embedded impls)
1223
+ // stays self-consistent. Mirrors the Dart engine (engine_std.dart). See #47.
1224
+ _r('math_sign', (i) => Math.sign(Number(_m(i)['value'] ?? 0)));
1225
+ _r('math_is_infinite', (i) => { const v = Number(_m(i)['value'] ?? 0); return v === Infinity || v === -Infinity; });
1226
+ // ── Sort (method dispatch on list) ─────────────────────────────────
1227
+ _r('sort', async (i) => {
1228
+ const m = _m(i);
1229
+ const self = m['self'] ?? m['list'] ?? m['collection'];
1230
+ const fn = m['compare'] ?? m['comparator'] ?? m['function'] ?? m['value'] ?? m['arg0'];
1231
+ if (!Array.isArray(self))
1232
+ return null;
1233
+ if (typeof fn === 'function') {
1234
+ async function ms(arr) {
1235
+ if (arr.length <= 1)
1236
+ return arr;
1237
+ const mid = Math.floor(arr.length / 2);
1238
+ const l = await ms(arr.slice(0, mid)), r = await ms(arr.slice(mid));
1239
+ const res = [];
1240
+ let li = 0, ri = 0;
1241
+ while (li < l.length && ri < r.length) {
1242
+ let c = fn({ 'arg0': l[li], 'arg1': r[ri], 'left': l[li], 'right': r[ri] });
1243
+ if (c?.then)
1244
+ c = await c;
1245
+ if ((typeof c === 'number' ? c : 0) <= 0)
1246
+ res.push(l[li++]);
1247
+ else
1248
+ res.push(r[ri++]);
1249
+ }
1250
+ while (li < l.length)
1251
+ res.push(l[li++]);
1252
+ while (ri < r.length)
1253
+ res.push(r[ri++]);
1254
+ return res;
1255
+ }
1256
+ const sorted = await ms([...self]);
1257
+ for (let si = 0; si < sorted.length; si++)
1258
+ self[si] = sorted[si];
1259
+ }
1260
+ else {
1261
+ self.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
1262
+ }
1263
+ return null;
1264
+ });
1265
+ // ── Static dispatch aliases ────────────────────────────────────────
1266
+ _r('generate', async (i) => {
1267
+ const m = _m(i);
1268
+ const count = Number(m['count'] ?? m['length'] ?? m['arg0'] ?? 0);
1269
+ const gen = m['generator'] ?? m['function'] ?? m['arg1'] ?? m['value'];
1270
+ const result = [];
1271
+ if (typeof gen === 'function') {
1272
+ for (let j = 0; j < count; j++) {
1273
+ let r = gen(j);
1274
+ if (r?.then)
1275
+ r = await r;
1276
+ result.push(r);
1277
+ }
1278
+ }
1279
+ else {
1280
+ for (let j = 0; j < count; j++)
1281
+ result.push(null);
1282
+ }
1283
+ return result;
1284
+ });
1285
+ _r('filled', (i) => { const m = _m(i); return Array(Number(m['count'] ?? m['length'] ?? m['arg0'] ?? 0)).fill(m['value'] ?? m['fill'] ?? m['arg1'] ?? null); });
1286
+ // Override length — compiled engine's _stdLength misses map length
1287
+ _r('length', (i) => {
1288
+ const m = _m(i);
1289
+ const v = m['value'] ?? i;
1290
+ if (typeof v === 'string')
1291
+ return v.length;
1292
+ if (Array.isArray(v))
1293
+ return v.length;
1294
+ if (v instanceof Set)
1295
+ return v.size;
1296
+ if (typeof v === 'object' && v !== null)
1297
+ return Object.keys(v).filter((k) => !k.startsWith('__')).length;
1298
+ return 0;
1299
+ });
1300
+ // Override map_keys/map_values/map_entries — the compiled version reads via a
1301
+ // `.entries` getter, which does not handle the proto3-JSON map shapes
1302
+ // (`{map}`/`{value}`/bare object) these overrides normalize here.
1303
+ _r('map_keys', (i) => { const m = _m(i); const map = m['map'] ?? m['value'] ?? i; if (typeof map !== 'object' || map === null)
1304
+ return []; return Object.keys(map).filter((k) => !k.startsWith('__')); });
1305
+ _r('map_values', (i) => { const m = _m(i); const map = m['map'] ?? m['value'] ?? i; if (typeof map !== 'object' || map === null)
1306
+ return []; return Object.entries(map).filter(([k]) => !k.startsWith('__')).map(([, v]) => v); });
1307
+ _r('map_entries', (i) => { const m = _m(i); const map = m['map'] ?? m['value'] ?? i; if (typeof map !== 'object' || map === null)
1308
+ return []; return Object.entries(map).filter(([k]) => !k.startsWith('__')).map(([k, v]) => ({ key: k, value: v })); });
1309
+ _r('map_length', (i) => { const m = _m(i); const map = m['map'] ?? m['value'] ?? i; if (typeof map !== 'object' || map === null)
1310
+ return 0; return Object.keys(map).filter((k) => !k.startsWith('__')).length; });
1311
+ _r('map_from_entries', (i) => { const m = _m(i); const list = m['list'] ?? m['entries'] ?? m['value'] ?? []; if (!Array.isArray(list))
1312
+ return {}; const r = {}; for (const e of list) {
1313
+ if (typeof e === 'object' && e !== null) {
1314
+ r[e.key ?? e.name ?? e.arg0 ?? e[0]] = e.value ?? e.arg1 ?? e[1];
1315
+ }
1316
+ } return r; });
1317
+ // Override set operations
1318
+ _r('set_create', (i) => { const m = _m(i); const elements = m['elements']; if (Array.isArray(elements))
1319
+ return new Set(elements); return new Set(); });
1320
+ // std.typed_list: a typed list literal `<T>[...]`. The type argument is
1321
+ // erased at runtime — the value is just the element array.
1322
+ _r('typed_list', (i) => { const m = _m(i); const elements = m['elements']; return Array.isArray(elements) ? elements : []; });
1323
+ // ── Async / Generator ──────────────────────────────────────────────
1324
+ //
1325
+ // std.await: unwrap BallFuture (simulated async result).
1326
+ // In a synchronous engine, BallFuture.value is always available.
1327
+ _r('await', async (i) => {
1328
+ const m = _m(i);
1329
+ let val = m['value'] ?? m['arg0'] ?? i;
1330
+ // Unwrap real JS Promises (from async lambda bodies)
1331
+ if (val && typeof val === 'object' && typeof val.then === 'function')
1332
+ val = await val;
1333
+ // Unwrap BallFuture (compiled engine's simulation)
1334
+ if (_isFutureLike(val))
1335
+ return val.value;
1336
+ // Unwrap plain-object BallFuture markers
1337
+ if (val && typeof val === 'object' && val.__ball_future__ === true)
1338
+ return val.value;
1339
+ return val;
1340
+ });
1341
+ // std.yield: in generator context, the caller collects yields via _FlowSignal.
1342
+ // Outside generator context, just return the value.
1343
+ _r('yield', (i) => {
1344
+ const m = _m(i);
1345
+ return m['value'] ?? m['arg0'] ?? i;
1346
+ });
1347
+ // std.yield_each: flatten iterable yields.
1348
+ _r('yield_each', (i) => {
1349
+ const m = _m(i);
1350
+ const val = m['value'] ?? m['arg0'] ?? i;
1351
+ if (Array.isArray(val))
1352
+ return val;
1353
+ return val;
1354
+ });
1355
+ // Override dart_list_generate (compiled version's lambda calling may fail)
1356
+ _r('dart_list_generate', async (i) => {
1357
+ const m = _m(i);
1358
+ const count = Number(m['count'] ?? m['arg0'] ?? 0);
1359
+ const gen = m['generator'] ?? m['arg1'];
1360
+ if (typeof gen !== 'function')
1361
+ return [];
1362
+ const result = [];
1363
+ for (let idx = 0; idx < count; idx++) {
1364
+ let v = gen(idx);
1365
+ if (v?.then)
1366
+ v = await v;
1367
+ result.push(v);
1368
+ }
1369
+ return result;
1370
+ });
1371
+ _r('dart_list_filled', (i) => {
1372
+ const m = _m(i);
1373
+ const count = Number(m['count'] ?? m['length'] ?? m['arg0'] ?? 0);
1374
+ return Array(Math.max(0, count | 0)).fill(m['value'] ?? m['arg1'] ?? null);
1375
+ });
1376
+ _r('list_filled', (i) => {
1377
+ const m = _m(i);
1378
+ const count = Number(m['count'] ?? m['length'] ?? m['arg0'] ?? 0);
1379
+ return Array(Math.max(0, count | 0)).fill(m['value'] ?? m['arg1'] ?? null);
1380
+ });
1381
+ _r('list_generate', (i) => {
1382
+ const m = _m(i);
1383
+ const count = Number(m['count'] ?? m['length'] ?? m['arg0'] ?? 0);
1384
+ const fn = m['function'] ?? m['generator'] ?? m['callback'] ?? m['value'];
1385
+ if (typeof fn !== 'function')
1386
+ return [];
1387
+ const out = [];
1388
+ for (let i2 = 0; i2 < count; i2++) {
1389
+ let r = fn(i2);
1390
+ out.push(r);
1391
+ }
1392
+ return out;
1393
+ });
1394
+ // std_time
1395
+ // NOTE: these override the compiled engine's native std_time handlers,
1396
+ // which reference a `DateTime` class that the (stale) committed engine
1397
+ // does not define. Implementing them here keeps std_time working without
1398
+ // depending on a preamble DateTime polyfill.
1399
+ _r('now', () => Date.now());
1400
+ _r('now_micros', () => Date.now() * 1000);
1401
+ _r('timestamp_ms', () => Date.now());
1402
+ _r('timestamp_micros', () => Date.now() * 1000);
1403
+ _r('format_timestamp', (i) => {
1404
+ const m = _m(i);
1405
+ const ms = Number(m['timestamp_ms'] ?? m['arg0'] ?? 0);
1406
+ return new Date(ms).toISOString();
1407
+ });
1408
+ _r('parse_timestamp', (i) => {
1409
+ const m = _m(i);
1410
+ const s = String(m['value'] ?? m['arg0'] ?? '');
1411
+ return Date.parse(s);
1412
+ });
1413
+ _r('duration_add', (i) => { const m = _m(i); return Number(m['left'] ?? m['arg0'] ?? 0) + Number(m['right'] ?? m['arg1'] ?? 0); });
1414
+ _r('duration_subtract', (i) => { const m = _m(i); return Number(m['left'] ?? m['arg0'] ?? 0) - Number(m['right'] ?? m['arg1'] ?? 0); });
1415
+ _r('year', () => new Date().getUTCFullYear());
1416
+ _r('month', () => new Date().getUTCMonth() + 1);
1417
+ _r('day', () => new Date().getUTCDate());
1418
+ _r('hour', () => new Date().getUTCHours());
1419
+ _r('minute', () => new Date().getUTCMinutes());
1420
+ _r('second', () => new Date().getUTCSeconds());
1421
+ // std_convert
1422
+ _r('json_encode', (i) => {
1423
+ const m = _m(i);
1424
+ const v = m['value'] ?? (m['arg0'] !== undefined ? m['arg0'] : i);
1425
+ return JSON.stringify(v);
1426
+ });
1427
+ _r('json_decode', (i) => {
1428
+ const m = _m(i);
1429
+ const s = String(m['value'] ?? m['arg0'] ?? '');
1430
+ return JSON.parse(s);
1431
+ });
1432
+ _r('utf8_encode', (i) => {
1433
+ const m = _m(i);
1434
+ const s = String(m['value'] ?? m['arg0'] ?? '');
1435
+ return Array.from(new TextEncoder().encode(s));
1436
+ });
1437
+ _r('utf8_decode', (i) => {
1438
+ const m = _m(i);
1439
+ const bytes = m['value'] ?? m['arg0'] ?? [];
1440
+ return new TextDecoder().decode(new Uint8Array(bytes));
1441
+ });
1442
+ _r('base64_encode', (i) => {
1443
+ const m = _m(i);
1444
+ const bytes = m['value'] ?? m['arg0'] ?? [];
1445
+ if (typeof Buffer !== 'undefined')
1446
+ return Buffer.from(bytes).toString('base64');
1447
+ return btoa(String.fromCharCode(...bytes));
1448
+ });
1449
+ _r('base64_decode', (i) => {
1450
+ const m = _m(i);
1451
+ const s = String(m['value'] ?? m['arg0'] ?? '');
1452
+ if (typeof Buffer !== 'undefined')
1453
+ return Array.from(Buffer.from(s, 'base64'));
1454
+ return Array.from(atob(s), (c) => c.charCodeAt(0));
1455
+ });
1456
+ }
1457
+ // ── BallFuture / BallGenerator helpers ─────────────────────────────────────
1458
+ //
1459
+ // BallFuture and BallGenerator are imported directly from the compiled engine
1460
+ // module. We use them for instanceof checks, constructor calls, and patching.
1461
+ /** Unwrap BallFuture/BallGenerator values for display and consumption. */
1462
+ function _unwrapBallValue(v) {
1463
+ if (_isFutureLike(v))
1464
+ return v.value;
1465
+ if (v instanceof BallGenerator)
1466
+ return v.values;
1467
+ if (v && typeof v === 'object' && v.__ball_future__ === true)
1468
+ return v.value;
1469
+ if (v && typeof v === 'object' && v.__ball_generator__ === true)
1470
+ return v.values;
1471
+ return v;
1472
+ }
1473
+ // ── Patch compiled engine for async/generator support ──────────────────────
1474
+ function patchCompiledEngine(engine) {
1475
+ const e = engine;
1476
+ const origEvalFieldAccess = e._evalFieldAccess.bind(e);
1477
+ e._evalFieldAccess = async function (access, scope) {
1478
+ const object = await e._evalExpression(access.object, scope);
1479
+ const fieldName = access.field_2;
1480
+ const numResult = _numFieldAccess(object, fieldName);
1481
+ if (numResult !== undefined)
1482
+ return numResult;
1483
+ const collectionResult = _collectionFieldAccess(object, fieldName);
1484
+ if (collectionResult !== undefined)
1485
+ return collectionResult;
1486
+ return origEvalFieldAccess(access, scope);
1487
+ };
1488
+ e._toInt = function (v) { return _coerceInt(v); };
1489
+ e._toNum = function (v) { return _coerceNum(v); };
1490
+ const origStdBinary = e._stdBinary.bind(e);
1491
+ e._stdBinary = function (input, op) {
1492
+ const rec = e._extractBinaryArgs(input);
1493
+ const left = rec[0];
1494
+ const right = rec[1];
1495
+ const BD = globalThis.BallDouble;
1496
+ const lBD = BD && left instanceof BD;
1497
+ const rBD = BD && right instanceof BD;
1498
+ const [l, r] = _coerceNumPair(left, right);
1499
+ const result = op(l, r);
1500
+ if ((lBD || rBD) && typeof result === 'number')
1501
+ return new BD(result);
1502
+ return result;
1503
+ };
1504
+ const origStdBinaryInt = e._stdBinaryInt.bind(e);
1505
+ e._stdBinaryInt = function (input, op) {
1506
+ const rec = e._extractBinaryArgs(input);
1507
+ const [l, r] = _coerceIntPair(rec[0], rec[1]);
1508
+ return op(l, r);
1509
+ };
1510
+ e._stdUnaryNum = function (input, op) {
1511
+ return op(_coerceNum(e._extractUnaryArg(input)));
1512
+ };
1513
+ e._stdBinaryComp = function (input, op) {
1514
+ const rec = e._extractBinaryArgs(input);
1515
+ const [l, r] = _coerceNumPair(rec[0], rec[1]);
1516
+ return op(l, r);
1517
+ };
1518
+ e._stdAdd = function (input) {
1519
+ const rec = e._extractBinaryArgs(input);
1520
+ const left = rec[0];
1521
+ const right = rec[1];
1522
+ if ((typeof left === 'string') || (typeof right === 'string')) {
1523
+ return (__bts(left ?? '') + __bts(right ?? ''));
1524
+ }
1525
+ const BD = globalThis.BallDouble;
1526
+ const lBD = BD && left instanceof BD;
1527
+ const rBD = BD && right instanceof BD;
1528
+ if (typeof left === 'bigint' || typeof right === 'bigint' ||
1529
+ (typeof _coerceInt(left) === 'bigint') || (typeof _coerceInt(right) === 'bigint')) {
1530
+ const result = _int64Add(left, right);
1531
+ return (lBD || rBD) ? new BD(Number(result)) : result;
1532
+ }
1533
+ const [l, r] = _coerceNumPair(left, right);
1534
+ const result = l + r;
1535
+ if (lBD || rBD) {
1536
+ if (typeof result === 'bigint')
1537
+ return new BD(Number(result));
1538
+ return new BD(result);
1539
+ }
1540
+ return result;
1541
+ };
1542
+ const origPatternKind = e._patternKind?.bind(e);
1543
+ e._patternKind = function (pattern) {
1544
+ const explicit = pattern?.['__pattern_kind__'];
1545
+ if (explicit != null)
1546
+ return explicit;
1547
+ const type = pattern?.['__type__'] ?? pattern?.typeName;
1548
+ switch (type) {
1549
+ case 'VarPattern': return 'var';
1550
+ case 'WildcardPattern': return 'wildcard';
1551
+ case 'ConstPattern': return 'const';
1552
+ case 'ListPattern': return 'list';
1553
+ case 'MapPattern': return 'map';
1554
+ case 'RecordPattern': return 'record';
1555
+ case 'ObjectPattern': return 'object';
1556
+ case 'LogicalAndPattern': return 'logical_and';
1557
+ case 'LogicalOrPattern': return 'logical_or';
1558
+ case 'CastPattern': return 'cast';
1559
+ case 'NullCheckPattern': return 'null_check';
1560
+ case 'NullAssertPattern': return 'null_assert';
1561
+ case 'RelationalPattern': return 'relational';
1562
+ case 'RestPattern': return 'rest';
1563
+ default: return origPatternKind ? origPatternKind(pattern) : null;
1564
+ }
1565
+ };
1566
+ const origMatchPattern = e._matchPattern.bind(e);
1567
+ e._matchPattern = function (value, pattern, bindings) {
1568
+ if (pattern != null && typeof pattern === 'object') {
1569
+ const kind = e._patternKind(pattern);
1570
+ if (kind === 'map') {
1571
+ const entries = pattern.entries;
1572
+ if (Array.isArray(entries) && entries.some((entry) => Array.isArray(entry))) {
1573
+ pattern = { ...pattern, entries: entries.flat() };
1574
+ }
1575
+ }
1576
+ // Fix LogicalAndPattern binding propagation: the compiled engine's
1577
+ // logical_and handler does `bindings = __ball_concat(tempBindings, tempBindings)`
1578
+ // which reassigns the local variable instead of mutating the caller's
1579
+ // object. Override here to correctly propagate bindings.
1580
+ if (kind === 'logical_and') {
1581
+ const tempBindings = {};
1582
+ const leftMatch = e._matchPattern(value, pattern.left, tempBindings);
1583
+ if (!leftMatch)
1584
+ return false;
1585
+ const rightMatch = e._matchPattern(value, pattern.right, tempBindings);
1586
+ if (!rightMatch)
1587
+ return false;
1588
+ Object.assign(bindings, tempBindings);
1589
+ return true;
1590
+ }
1591
+ // Fix LogicalOrPattern binding propagation (same bug pattern)
1592
+ if (kind === 'logical_or') {
1593
+ const leftBindings = {};
1594
+ if (e._matchPattern(value, pattern.left, leftBindings)) {
1595
+ Object.assign(bindings, leftBindings);
1596
+ return true;
1597
+ }
1598
+ return e._matchPattern(value, pattern.right, bindings);
1599
+ }
1600
+ }
1601
+ return origMatchPattern(value, pattern, bindings);
1602
+ };
1603
+ e._matchesTypePattern = function (value, pattern) {
1604
+ const BD = globalThis.BallDouble;
1605
+ const p = typeof pattern === 'string'
1606
+ ? pattern
1607
+ : (e._ballToStringSimple ? e._ballToStringSimple(pattern) : String(pattern));
1608
+ // Nullable type `T?` matches null OR the base type. The no-space guard
1609
+ // stops raw fragments like "var v?" being read as a nullable type (which
1610
+ // would wrongly match null). Mirrors the Dart engine _matchesTypePattern.
1611
+ if (p.length > 1 && p.endsWith('?') && !p.includes(' ')) {
1612
+ if (value == null)
1613
+ return true;
1614
+ return e._matchesTypePattern(value, p.substring(0, p.length - 1));
1615
+ }
1616
+ switch (p) {
1617
+ case 'int':
1618
+ return typeof value === 'bigint' || (typeof value === 'number' && Number.isInteger(value));
1619
+ case 'double':
1620
+ return BD != null && value instanceof BD;
1621
+ case 'num':
1622
+ return typeof value === 'bigint' || typeof value === 'number' || (BD != null && value instanceof BD);
1623
+ case 'String':
1624
+ return typeof value === 'string';
1625
+ case 'bool':
1626
+ return typeof value === 'boolean';
1627
+ case 'List':
1628
+ return Array.isArray(value);
1629
+ case 'Map':
1630
+ return typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Set);
1631
+ case 'Set':
1632
+ return value instanceof Set;
1633
+ case 'Object':
1634
+ return value != null;
1635
+ case 'dynamic':
1636
+ return true;
1637
+ case 'Null':
1638
+ case 'null':
1639
+ return value == null;
1640
+ default:
1641
+ return false;
1642
+ }
1643
+ };
1644
+ const origScopeWithPatternBindings = e._scopeWithPatternBindings.bind(e);
1645
+ e._scopeWithPatternBindings = function (parent, bindings) {
1646
+ if (bindings == null || typeof bindings !== 'object')
1647
+ return parent;
1648
+ const entries = Object.entries(bindings).filter(([k]) => !k.startsWith('__'));
1649
+ if (entries.length === 0)
1650
+ return parent;
1651
+ const child = parent.child();
1652
+ for (const [k, v] of entries)
1653
+ child.bind(k, v);
1654
+ return child;
1655
+ };
1656
+ e._stdMapCreate = function (input) {
1657
+ const m = e._stdAsMap(input);
1658
+ if (m == null)
1659
+ return {};
1660
+ const hop = Object.prototype.hasOwnProperty;
1661
+ const entries = hop.call(m, 'entries') ? m['entries'] : (hop.call(m, 'entry') ? m['entry'] : undefined);
1662
+ const result = {};
1663
+ const ingest = (entry) => {
1664
+ const entryMap = e._stdAsMap(entry);
1665
+ if (entryMap == null)
1666
+ return;
1667
+ const key = entryMap['key'] ?? entryMap['name'] ?? entryMap['arg0'] ?? '';
1668
+ result[String(key)] = entryMap['value'] ?? entryMap['arg1'];
1669
+ };
1670
+ if (Array.isArray(entries)) {
1671
+ for (const entry of entries)
1672
+ ingest(entry);
1673
+ }
1674
+ else if (entries != null && typeof entries === 'object') {
1675
+ ingest(entries);
1676
+ }
1677
+ return result;
1678
+ };
1679
+ const origBallEquals = e._ballEquals.bind(e);
1680
+ e._ballEquals = function (a, b) {
1681
+ const unwrap = (v) => {
1682
+ const BD = globalThis.BallDouble;
1683
+ if (BD && v instanceof BD)
1684
+ return v.value;
1685
+ return v;
1686
+ };
1687
+ const av = unwrap(a);
1688
+ const bv = unwrap(b);
1689
+ if (typeof av === 'number' && typeof bv === 'number') {
1690
+ if (Number.isNaN(av) || Number.isNaN(bv))
1691
+ return false;
1692
+ return av === bv;
1693
+ }
1694
+ return origBallEquals(a, b);
1695
+ };
1696
+ // Store the current generator on the engine instance so yield/yield_each
1697
+ // base functions can push values into it. This avoids scope-chain walking
1698
+ // since _callBaseFunction doesn't receive a scope parameter.
1699
+ e._currentGenerator = null;
1700
+ // Patch _callFunction to:
1701
+ // 1. Fix input binding bug: inside _callFunction, the compiled engine first
1702
+ // binds 'input' to the correct extracted arg0 value, then re-binds 'input'
1703
+ // to the raw input object, overwriting it. We fix this by extracting arg0
1704
+ // before calling the original.
1705
+ // 2. Handle is_sync_star / is_async_star metadata (compiled engine only checks
1706
+ // is_generator and is_async).
1707
+ // 3. Create BallGenerator scope for yield to push values into.
1708
+ // 4. Avoid double-wrapping BallFuture for async functions.
1709
+ const origCallFunction = e._callFunction.bind(e);
1710
+ e._callFunction = async function (moduleName, func, input, parentScope) {
1711
+ if (!func || func.isBase || !func.body) {
1712
+ return origCallFunction(moduleName, func, input, parentScope);
1713
+ }
1714
+ // Fix input binding: for single-param functions with inputType, the compiled
1715
+ // engine binds 'input' twice — first to the extracted arg0 value (correct),
1716
+ // then to the raw input object (overwrites). We extract arg0 here and pass
1717
+ // it directly so both bindings get the same correct value.
1718
+ //
1719
+ // CRITICAL: never unwrap when `self` is present — that signals an instance
1720
+ // method call and the native _callFunction needs the full map to bind
1721
+ // `self` plus the instance fields onto the method scope. Unwrapping to the
1722
+ // bare arg0 dropped `self`, so methods saw `Undefined variable: <field>`.
1723
+ let fixedInput = input;
1724
+ if (func.inputType && func.inputType.isNotEmpty && typeof input === 'object' && input !== null && !Array.isArray(input)) {
1725
+ const params = (e._paramCache[((__bts(moduleName) + '.') + __bts(func.name))]) ?? (func.metadata ? e._extractParams(func.metadata) : []);
1726
+ if (params.length === 1) {
1727
+ const inputMap = e._asMap(input);
1728
+ if (inputMap && 'arg0' in inputMap && !(params[0] in inputMap) && !('self' in inputMap)) {
1729
+ fixedInput = inputMap['arg0'];
1730
+ }
1731
+ }
1732
+ }
1733
+ // Check for async/generator metadata
1734
+ let isAsync = false;
1735
+ let isSyncStar = false;
1736
+ let isAsyncStar = false;
1737
+ let isGenerator = false;
1738
+ if (func.metadata) {
1739
+ const fields = func.metadata?.fields;
1740
+ if (fields) {
1741
+ const getBool = (key) => {
1742
+ const v = fields[key];
1743
+ if (v == null)
1744
+ return false;
1745
+ if (typeof v === 'object' && v !== null)
1746
+ return !!v.boolValue;
1747
+ return !!v;
1748
+ };
1749
+ isAsync = getBool('is_async');
1750
+ isSyncStar = getBool('is_sync_star');
1751
+ isAsyncStar = getBool('is_async_star');
1752
+ isGenerator = getBool('is_generator');
1753
+ }
1754
+ }
1755
+ const isGenFunc = isSyncStar || isAsyncStar || isGenerator;
1756
+ // Generator functions are handled natively by the regenerated engine:
1757
+ // it creates a BallGenerator, binds it to scope as `__generator__`, and
1758
+ // `_evalYield`/`_evalYieldEach` walk the scope chain to push values into
1759
+ // it. We must NOT shadow that with our own engine-level `_currentGenerator`
1760
+ // (the old engine's mechanism) — doing so collected zero yields and
1761
+ // returned an empty list. Delegate straight to the native implementation.
1762
+ if (isGenFunc) {
1763
+ return origCallFunction(moduleName, func, fixedInput, parentScope);
1764
+ }
1765
+ let result = await origCallFunction(moduleName, func, fixedInput, parentScope);
1766
+ // Handle async function results.
1767
+ // The compiled engine's _callFunction already wraps async results in BallFuture.
1768
+ // We just need to ensure the result is properly unwrapped if it's a FlowSignal.
1769
+ if (isAsync) {
1770
+ // Unwrap FlowSignal if present
1771
+ if (result instanceof _FlowSignal && result.kind === 'return') {
1772
+ result = result.value;
1773
+ }
1774
+ // If already a BallFuture, return as-is (compiled engine already wrapped it)
1775
+ if (_isFutureLike(result)) {
1776
+ return result;
1777
+ }
1778
+ // Shouldn't happen, but wrap in BallFuture as fallback
1779
+ return new BallFuture(result, true);
1780
+ }
1781
+ return result;
1782
+ };
1783
+ // Patch _evalCall to auto-unwrap BallFuture values, matching the Dart engine's
1784
+ // _unwrapFuture behavior. Async functions return BallFuture(value), but callers
1785
+ // should receive the unwrapped value so async is transparent.
1786
+ const origEvalCall = e._evalCall.bind(e);
1787
+ e._evalCall = async function (call, scope) {
1788
+ const result = await origEvalCall(call, scope);
1789
+ // Auto-unwrap BallFuture so async functions are transparent to callers.
1790
+ if (_isFutureLike(result)) {
1791
+ return result.value;
1792
+ }
1793
+ return result;
1794
+ };
1795
+ // Patch _callBaseFunction to handle yield/yield_each/await.
1796
+ // yield: push value into current generator (stored on engine instance).
1797
+ // yield_each: push all values from iterable into current generator.
1798
+ // await: unwrap BallFuture (the compiled engine already does this, but we
1799
+ // add a safety net for cases where it doesn't fire).
1800
+ const origCallBaseFunction = e._callBaseFunction.bind(e);
1801
+ e._callBaseFunction = function (moduleName, fn, input) {
1802
+ // Handle yield — add value to current generator
1803
+ if (fn === 'yield') {
1804
+ const val = e._extractUnaryArg(input);
1805
+ const gen = e._currentGenerator;
1806
+ if (gen && gen instanceof BallGenerator) {
1807
+ gen.values.push(val);
1808
+ }
1809
+ return val;
1810
+ }
1811
+ // Handle yield_each — add all values from iterable to current generator
1812
+ if (fn === 'yield_each') {
1813
+ const iterable = e._extractUnaryArg(input);
1814
+ const gen = e._currentGenerator;
1815
+ if (gen && gen instanceof BallGenerator) {
1816
+ if (iterable instanceof BallGenerator) {
1817
+ gen.values.push(...iterable.values);
1818
+ }
1819
+ else if (Array.isArray(iterable)) {
1820
+ gen.values.push(...iterable);
1821
+ }
1822
+ }
1823
+ return iterable;
1824
+ }
1825
+ // Handle await — unwrap BallFuture
1826
+ if (fn === 'await') {
1827
+ const val = e._extractUnaryArg(input);
1828
+ if (_isFutureLike(val))
1829
+ return val.value;
1830
+ return val;
1831
+ }
1832
+ return origCallBaseFunction(moduleName, fn, input);
1833
+ };
1834
+ // Patch _evalMessageCreation to avoid Object.prototype getter pollution.
1835
+ //
1836
+ // The preamble installs Dart-flavoured getters (`length`, `keys`, `values`,
1837
+ // `entries`) on Object.prototype so the compiled engine can call
1838
+ // `map.length` etc. The downside is that `('length' in {})` is now `true`,
1839
+ // and the compiled engine's `_evalMessageCreation` uses
1840
+ // `(pair.name in fields)` to detect duplicate field names. A single field
1841
+ // literally named `length` / `keys` / `values` / `entries` therefore trips
1842
+ // the "merge duplicates into a list" path and becomes `[<getterValue>, v]`
1843
+ // (e.g. `List.filled(length: 3, ...)` arrives as `length: [0, 3]`).
1844
+ //
1845
+ // We override the field-collection step to use a null-prototype object plus
1846
+ // hasOwnProperty, then hand the cleaned field map to the original method's
1847
+ // typeName/constructor dispatch by re-binding `_evalExpression` to a no-op
1848
+ // lookup over the already-evaluated values. To keep the heavy dispatch logic
1849
+ // in one place we instead pre-evaluate here and delegate via a synthetic
1850
+ // message whose field values are pre-computed literals.
1851
+ const _POLLUTED_KEYS = new Set(['length', 'keys', 'values', 'entries']);
1852
+ const origEvalMessageCreation = e._evalMessageCreation.bind(e);
1853
+ e._evalMessageCreation = async function (msg, scope) {
1854
+ const rawFields = msg?.fields ?? [];
1855
+ const typeName = msg?.typeName ?? '';
1856
+ // The bug only bites a typeName-less message (a plain std/base-function
1857
+ // input map) whose field is named with a polluted key. Those never go
1858
+ // through the heavy constructor/dispatch tail — they return the field map
1859
+ // directly — so we can safely build a clean (null-proto) map here.
1860
+ const hasPollutedField = rawFields.some((p) => _POLLUTED_KEYS.has(p?.name));
1861
+ if (typeName !== '' || !hasPollutedField) {
1862
+ return origEvalMessageCreation(msg, scope);
1863
+ }
1864
+ // Mirror the engine's duplicate-merge rule but with hasOwnProperty so the
1865
+ // inherited Dart getters never produce a false positive.
1866
+ const fields = Object.create(null);
1867
+ const hop = Object.prototype.hasOwnProperty;
1868
+ for (const pair of rawFields) {
1869
+ const val = await e._evalExpression(pair.value, scope);
1870
+ if (hop.call(fields, pair.name)) {
1871
+ const existing = fields[pair.name];
1872
+ fields[pair.name] = Array.isArray(existing) ? [...existing, val] : [existing, val];
1873
+ }
1874
+ else {
1875
+ fields[pair.name] = val;
1876
+ }
1877
+ }
1878
+ return fields;
1879
+ };
1880
+ // Fix BallGenerator.yieldAll — the compiled engine's version replaces
1881
+ // values with an empty array instead of appending items.
1882
+ const proto = BallGenerator.prototype;
1883
+ if (proto && typeof proto.yieldAll === 'function') {
1884
+ proto.yieldAll = function (items) {
1885
+ if (Array.isArray(items)) {
1886
+ this.values.push(...items);
1887
+ }
1888
+ else if (items && typeof items[Symbol.iterator] === 'function') {
1889
+ for (const item of items)
1890
+ this.values.push(item);
1891
+ }
1892
+ return this.values;
1893
+ };
1894
+ }
1895
+ // Patch __bts (ball-to-string) to unwrap BallFuture/BallGenerator.
1896
+ // The compiled engine sets globalThis.__bts during preamble execution.
1897
+ // We wrap it after the engine runs its preamble.
1898
+ const origBts = globalThis.__bts;
1899
+ if (typeof origBts === 'function') {
1900
+ globalThis.__bts = function (v) {
1901
+ const unwrapped = _unwrapBallValue(v);
1902
+ if (unwrapped !== v)
1903
+ return origBts(unwrapped);
1904
+ return origBts(v);
1905
+ };
1906
+ }
1907
+ for (const handler of (e.moduleHandlers ?? [])) {
1908
+ if (handler == null || typeof handler.register !== 'function')
1909
+ continue;
1910
+ handler.register('add', (i) => e._stdAdd(i));
1911
+ handler.register('subtract', (i) => {
1912
+ const rec = e._extractBinaryArgs(i);
1913
+ const left = rec[0];
1914
+ const right = rec[1];
1915
+ const BD = globalThis.BallDouble;
1916
+ const lBD = BD && left instanceof BD;
1917
+ const rBD = BD && right instanceof BD;
1918
+ if (!lBD && !rBD && (typeof _coerceInt(left) === 'bigint' || typeof _coerceInt(right) === 'bigint')) {
1919
+ return _int64Subtract(left, right);
1920
+ }
1921
+ return e._stdBinary(i, (a, b) => a - b);
1922
+ });
1923
+ handler.register('multiply', (i) => {
1924
+ const rec = e._extractBinaryArgs(i);
1925
+ const left = rec[0];
1926
+ const right = rec[1];
1927
+ // Polymorphic over strings: `'ab' * 3` repeats (Dart String * int).
1928
+ if (typeof left === 'string')
1929
+ return left.repeat(Number(right));
1930
+ const BD = globalThis.BallDouble;
1931
+ const lBD = BD && left instanceof BD;
1932
+ const rBD = BD && right instanceof BD;
1933
+ if (!lBD && !rBD && (typeof _coerceInt(left) === 'bigint' || typeof _coerceInt(right) === 'bigint')) {
1934
+ return _int64Multiply(left, right);
1935
+ }
1936
+ return e._stdBinary(i, (a, b) => a * b);
1937
+ });
1938
+ handler.register('divide', (i) => {
1939
+ const rec = e._extractBinaryArgs(i);
1940
+ return _int64Divide(rec[0], rec[1]);
1941
+ });
1942
+ handler.register('modulo', (i) => {
1943
+ const rec = e._extractBinaryArgs(i);
1944
+ return _int64Modulo(rec[0], rec[1]);
1945
+ });
1946
+ handler.register('negate', (i) => _int64Negate(e._extractUnaryArg(i)));
1947
+ handler.register('bitwise_and', (i) => e._stdBinaryInt(i, (a, b) => _int64Binary((l, r) => l & r, a, b)));
1948
+ handler.register('bitwise_or', (i) => e._stdBinaryInt(i, (a, b) => _int64Binary((l, r) => l | r, a, b)));
1949
+ handler.register('bitwise_xor', (i) => e._stdBinaryInt(i, (a, b) => _int64Binary((l, r) => l ^ r, a, b)));
1950
+ handler.register('bitwise_not', (i) => e._stdUnaryNum(i, (v) => _int64Unary((x) => ~x, v)));
1951
+ handler.register('left_shift', (i) => e._stdBinaryInt(i, (a, b) => _int64ShiftLeft(a, b)));
1952
+ handler.register('right_shift', (i) => e._stdBinaryInt(i, (a, b) => _int64ShiftRight(a, b)));
1953
+ handler.register('unsigned_right_shift', (i) => e._stdBinaryInt(i, (a, b) => _int64UnsignedShiftRight(a, b)));
1954
+ handler.register('math_abs', (i) => _mathAbs(e._extractUnaryArg(i)));
1955
+ handler.register('less_than', (i) => e._stdBinaryComp(i, (a, b) => a < b));
1956
+ handler.register('greater_than', (i) => e._stdBinaryComp(i, (a, b) => a > b));
1957
+ handler.register('lte', (i) => e._stdBinaryComp(i, (a, b) => a <= b));
1958
+ handler.register('gte', (i) => e._stdBinaryComp(i, (a, b) => a >= b));
1959
+ handler.register('map_create', (i) => e._stdMapCreate(i));
1960
+ }
1961
+ }
1962
+ // ── Seed global scope ──────────────────────────────────────────────────────
1963
+ function seedGlobalScope(engine) {
1964
+ const gs = engine._globalScope;
1965
+ if (!gs || !gs.bind)
1966
+ return;
1967
+ gs.bind('List', { '__class_ref__': 'List', '__type__': '__builtin_class__' });
1968
+ gs.bind('Map', { '__class_ref__': 'Map', '__type__': '__builtin_class__' });
1969
+ gs.bind('Set', { '__class_ref__': 'Set', '__type__': '__builtin_class__' });
1970
+ gs.bind('RegExp', { '__class_ref__': 'RegExp', '__type__': '__builtin_class__' });
1971
+ gs.bind('DateTime', { '__class_ref__': 'DateTime', '__type__': '__builtin_class__' });
1972
+ gs.bind('Duration', { '__class_ref__': 'Duration', '__type__': '__builtin_class__' });
1973
+ gs.bind('identical', (input) => {
1974
+ if (input && typeof input === 'object' && !Array.isArray(input)) {
1975
+ const a = input['arg0'] ?? input['left'] ?? input['a'];
1976
+ const b = input['arg1'] ?? input['right'] ?? input['b'];
1977
+ return a === b;
1978
+ }
1979
+ return false;
1980
+ });
1981
+ }
1982
+ // ── Scope-binding patch ────────────────────────────────────────────────
1983
+ //
1984
+ // Replace each scope's _bindings with a null-prototype object so the
1985
+ // Dart-flavoured getters the preamble installs on Object.prototype
1986
+ // (length/keys/values/entries) do not pollute `in` checks.
1987
+ function patchScopeBindings(globalScope) {
1988
+ const sp = Object.getPrototypeOf(globalScope);
1989
+ if (!sp || sp.__bindings_patched)
1990
+ return;
1991
+ const origBind = sp.bind;
1992
+ sp.bind = function (name, value) {
1993
+ if (Object.getPrototypeOf(this._bindings) !== null) {
1994
+ const e = Object.entries(this._bindings);
1995
+ this._bindings = Object.create(null);
1996
+ for (const [k, v] of e)
1997
+ this._bindings[k] = v;
1998
+ }
1999
+ return (this._bindings[name] = value);
2000
+ };
2001
+ const origChild = sp.child;
2002
+ if (origChild) {
2003
+ sp.child = function () {
2004
+ const c = origChild.call(this);
2005
+ if (c._bindings && Object.getPrototypeOf(c._bindings) !== null) {
2006
+ c._bindings = Object.create(null);
2007
+ }
2008
+ return c;
2009
+ };
2010
+ }
2011
+ sp.__bindings_patched = true;
2012
+ }
2013
+ return {
2014
+ protoWrap,
2015
+ wrapValue,
2016
+ __bts,
2017
+ BallFuture,
2018
+ MethodDispatchHandler,
2019
+ registerExtraStdFunctions,
2020
+ patchCompiledEngine,
2021
+ seedGlobalScope,
2022
+ patchScopeBindings,
2023
+ _isFutureLike,
2024
+ _unwrapBallValue,
2025
+ };
2026
+ }
2027
+ //# sourceMappingURL=engine_setup.js.map