@ball-lang/compiler 0.1.0 → 1.3.5

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.
package/src/preamble.ts CHANGED
@@ -14,10 +14,178 @@
14
14
  */
15
15
  export const TS_RUNTIME_PREAMBLE = String.raw`// ── Ball runtime preamble (generated by @ball-lang/compiler) ────────
16
16
 
17
+ // Base class for all Ball runtime values.
18
+ class BallValue {}
19
+
20
+ // A cast pattern (value as T) ASSERTS the runtime type — it throws on a type
21
+ // mismatch (Dart semantics), it does NOT refute / fall through. Conjoined into a
22
+ // switch-case condition by the compiler; returns true when the type check passed,
23
+ // else throws a catchable error. (conformance 302_cast_patterns)
24
+ function ball_cast_assert(ok: boolean, t: string): boolean {
25
+ if (!ok) throw new Error('TypeError: type cast failed: not a ' + t);
26
+ return true;
27
+ }
28
+
29
+ // ── Ball container runtime types ────────────────────────────────────
30
+ //
31
+ // The self-hosted engine IR models class instances as 'BallObject extends
32
+ // BallMap'. The compiler treats maps/lists transparently (a Ball map is a
33
+ // plain JS object, a Ball list a plain JS array) and _asMap() returns an
34
+ // instance verbatim, reading its data through bracket access (obj of f)
35
+ // and the Object.prototype .entries / .keys / .length getters. To stay
36
+ // compatible we make BallObject a plain-object-like instance: the field data
37
+ // lives as OWN ENUMERABLE properties (so bracket access and .entries see it),
38
+ // while the class bookkeeping (typeName/fields/methods/superObject) is stored
39
+ // non-enumerably so it never leaks into .entries / .keys / .length.
40
+ //
41
+ // BallMap / BallList exist only so 'extends BallMap' resolves and any stray
42
+ // new BallMap(...) / new BallList(...) behaves like the transparent value.
43
+ class BallMap extends BallValue {
44
+ constructor(entries?: any) {
45
+ super();
46
+ if (entries && typeof entries === 'object') {
47
+ if (entries instanceof Map) {
48
+ for (const [k, v] of entries) (this as any)[k] = v;
49
+ } else {
50
+ for (const k of Object.keys(entries)) (this as any)[k] = entries[k];
51
+ }
52
+ }
53
+ }
54
+ }
55
+
56
+ class BallList extends Array {
57
+ constructor(items?: any) {
58
+ super();
59
+ if (Array.isArray(items)) for (const it of items) this.push(it);
60
+ }
61
+ }
62
+
63
+ class BallObject extends BallMap {
64
+ constructor(arg0?: any, superObject?: any, fields?: any, methods?: any) {
65
+ super();
66
+ // Accept either the named-args object the encoder emits
67
+ // (new BallObject({typeName, superObject, fields, methods})) or the
68
+ // positional form, so the class works regardless of how it is invoked.
69
+ let typeName: any = arg0;
70
+ if (arg0 && typeof arg0 === 'object' && !Array.isArray(arg0) &&
71
+ ('typeName' in arg0 || 'fields' in arg0 || 'methods' in arg0 ||
72
+ 'superObject' in arg0)) {
73
+ typeName = arg0.typeName;
74
+ superObject = arg0.superObject;
75
+ fields = arg0.fields;
76
+ methods = arg0.methods;
77
+ }
78
+ const fieldMap = (fields && typeof fields === 'object') ? fields : {};
79
+ const methodMap = (methods && typeof methods === 'object') ? methods : {};
80
+ // Field data → own enumerable properties (visible to bracket access and
81
+ // the Object.prototype map getters).
82
+ for (const k of Object.keys(fieldMap)) (this as any)[k] = fieldMap[k];
83
+ // Class bookkeeping → own props the engine reads/writes by bracket name.
84
+ (this as any)['__type__'] = typeName ?? '';
85
+ (this as any)['__super__'] = superObject ?? null;
86
+ (this as any)['__fields__'] = fieldMap;
87
+ (this as any)['__methods__'] = methodMap;
88
+ // Mirror the Dart class fields too, but non-enumerably so they never show
89
+ // up as Ball fields in .entries / .keys / .length.
90
+ for (const [name, value] of [
91
+ ['typeName', typeName ?? ''], ['superObject', superObject ?? null],
92
+ ['fields', fieldMap], ['methods', methodMap],
93
+ ] as Array<[string, any]>) {
94
+ Object.defineProperty(this, name, {
95
+ value, writable: true, configurable: true, enumerable: false,
96
+ });
97
+ }
98
+ }
99
+
100
+ setField(name: any, value: any): void {
101
+ (this as any).fields[name] = value;
102
+ (this as any)[name] = value;
103
+ }
104
+ }
105
+ (globalThis as any).BallMap = BallMap;
106
+ (globalThis as any).BallList = BallList;
107
+ (globalThis as any).BallObject = BallObject;
108
+
109
+ // BallDouble wrapper — tracks that a number should print as a double
110
+ // (e.g. 42.0 not 42). Used by the compiled Dart engine's _toDouble.
111
+ class BallDouble {
112
+ readonly value: number;
113
+ constructor(v: number) { this.value = v; }
114
+ valueOf(): number { return this.value; }
115
+ get isNaN(): boolean { return Number.isNaN(this.value); }
116
+ get isFinite(): boolean { return Number.isFinite(this.value); }
117
+ get isInfinite(): boolean { return !Number.isFinite(this.value) && !Number.isNaN(this.value); }
118
+ get isNegative(): boolean { return this.value < 0 || (this.value === 0 && 1/this.value === -Infinity); }
119
+ toString(): string {
120
+ const v = this.value;
121
+ if (!isFinite(v)) return v.toString();
122
+ if (v === 0 && 1/v === -Infinity) return '-0.0';
123
+ if (Number.isInteger(v)) return v.toFixed(1);
124
+ return v.toString();
125
+ }
126
+ // Arithmetic: unwrap for operations
127
+ [Symbol.toPrimitive](hint: string): any {
128
+ if (hint === 'string') return this.toString();
129
+ return this.value;
130
+ }
131
+ }
132
+ (globalThis as any).BallDouble = BallDouble;
133
+
134
+ // Arithmetic helpers that propagate BallDouble through operations.
135
+ // If either operand is BallDouble, the result is BallDouble (preserving .0).
136
+ function __ball_mul(a: any, b: any): any {
137
+ if (a != null && typeof a === 'object' && typeof a.__op_mul === 'function') return a.__op_mul(b);
138
+ if (typeof a === 'bigint' || typeof b === 'bigint') return __i64_wrap(__to_bigint(a) * __to_bigint(b));
139
+ const av = a instanceof BallDouble ? a.value : a;
140
+ const bv = b instanceof BallDouble ? b.value : b;
141
+ const r = av * bv;
142
+ return (a instanceof BallDouble || b instanceof BallDouble) ? new BallDouble(r) : r;
143
+ }
144
+ function __ball_add(a: any, b: any): any {
145
+ if (a != null && typeof a === 'object' && typeof a.__op_add === 'function') return a.__op_add(b);
146
+ if (typeof a === 'bigint' || typeof b === 'bigint') return __i64_wrap(__to_bigint(a) + __to_bigint(b));
147
+ const av = a instanceof BallDouble ? a.value : a;
148
+ const bv = b instanceof BallDouble ? b.value : b;
149
+ const r = av + bv;
150
+ return (a instanceof BallDouble || b instanceof BallDouble) ? new BallDouble(r) : r;
151
+ }
152
+ function __ball_sub(a: any, b: any): any {
153
+ if (a != null && typeof a === 'object' && typeof a.__op_sub === 'function') return a.__op_sub(b);
154
+ if (typeof a === 'bigint' || typeof b === 'bigint') return __i64_wrap(__to_bigint(a) - __to_bigint(b));
155
+ const av = a instanceof BallDouble ? a.value : a;
156
+ const bv = b instanceof BallDouble ? b.value : b;
157
+ const r = av - bv;
158
+ return (a instanceof BallDouble || b instanceof BallDouble) ? new BallDouble(r) : r;
159
+ }
160
+
161
+ // Dart equality: NaN != NaN, BallDouble value comparison, null == undefined.
162
+ function __ball_eq(a: any, b: any): boolean {
163
+ if (a != null && typeof a === 'object' && typeof a.__op_eq === 'function') return a.__op_eq(b);
164
+ if (typeof a === 'bigint' || typeof b === 'bigint') {
165
+ if (typeof a === 'bigint' && typeof b === 'bigint') return a === b;
166
+ if (typeof a === 'bigint' && typeof b === 'number') return a === BigInt(b);
167
+ if (typeof a === 'number' && typeof b === 'bigint') return BigInt(a) === b;
168
+ return false;
169
+ }
170
+ if (a instanceof BallDouble || b instanceof BallDouble) {
171
+ const av = a instanceof BallDouble ? a.value : a;
172
+ const bv = b instanceof BallDouble ? b.value : b;
173
+ if (Number.isNaN(av) || Number.isNaN(bv)) return false;
174
+ return av === bv;
175
+ }
176
+ if (a == null && b == null) return true;
177
+ if (a == null || b == null) return a == b;
178
+ return a === b;
179
+ }
180
+
17
181
  function __ball_to_string(v: any): string {
18
182
  if (v === null || v === undefined) return 'null';
183
+ if (typeof v === 'bigint') return v.toString();
19
184
  if (typeof v === 'boolean') return v ? 'true' : 'false';
185
+ if (v instanceof BallDouble) return v.toString();
20
186
  if (typeof v === 'number') {
187
+ if (!isFinite(v) || Number.isNaN(v)) return v.toString();
188
+ if (v === 0 && 1/v === -Infinity) return '-0.0';
21
189
  if (Number.isInteger(v)) return v.toString();
22
190
  const s = v.toString();
23
191
  return s.includes('.') || s.includes('e') ? s : s + '.0';
@@ -33,6 +201,22 @@ function __ball_to_string(v: any): string {
33
201
  }
34
202
  return '{' + parts.join(', ') + '}';
35
203
  }
204
+ if (typeof v === 'object' && !Array.isArray(v)) {
205
+ // StringBuffer-like objects
206
+ if (v['__buffer__'] && Array.isArray(v['__buffer__'])) {
207
+ return v['__buffer__'].join('');
208
+ }
209
+ // Check for custom toString method on the instance (not Object.prototype).
210
+ if (v.toString !== Object.prototype.toString && typeof v.toString === 'function') {
211
+ return v.toString();
212
+ }
213
+ // Dart Map-like object: format as {key: value, ...}
214
+ const keys = Object.keys(v).filter((k: string) => !k.startsWith('__'));
215
+ if (keys.length > 0) {
216
+ return '{' + keys.map((k: string) => __ball_to_string(k) + ': ' + __ball_to_string(v[k])).join(', ') + '}';
217
+ }
218
+ return '{}';
219
+ }
36
220
  return String(v);
37
221
  }
38
222
 
@@ -44,6 +228,31 @@ function __ball_parse_int(s: string): number {
44
228
  return parseInt(trimmed, 10);
45
229
  }
46
230
 
231
+ // Dart-style int conversion that preserves int64 precision. JS numbers lose
232
+ // precision above 2^53, so integer literals encoded as decimal strings (the
233
+ // JSON proto3 representation of int64) would round. When the string magnitude
234
+ // exceeds Number.MAX_SAFE_INTEGER we return a BigInt to keep the exact value;
235
+ // otherwise we keep a plain number so the common arithmetic path is unchanged.
236
+ function __ball_to_int(v: any): any {
237
+ if (typeof v === 'bigint') return v;
238
+ if (typeof v === 'string') {
239
+ if (/^-?\d+$/.test(v)) {
240
+ const b = BigInt(v);
241
+ if (b > 9007199254740991n || b < -9007199254740991n) return b;
242
+ return Number(b);
243
+ }
244
+ return Math.trunc(Number(v)) || 0;
245
+ }
246
+ const n = (v instanceof BallDouble) ? v.value : v;
247
+ const t = Math.trunc(n) || 0;
248
+ if (t >= 9223372036854775808) return __I64_MAX;
249
+ if (t <= -9223372036854775808) return __I64_MIN;
250
+ if (t > 9007199254740991 || t < -9007199254740991) {
251
+ try { return __i64_wrap(BigInt(Math.round(t))); } catch {}
252
+ }
253
+ return t;
254
+ }
255
+
47
256
  function __ball_parse_double(s: string): number {
48
257
  const n = parseFloat(s);
49
258
  if (Number.isNaN(n)) throw new Error('FormatException: ' + s);
@@ -55,9 +264,161 @@ function __ball_double_to_string(n: number): string {
55
264
  return n.toString();
56
265
  }
57
266
 
267
+ // Polymorphic concat / merge used for std.list_concat. The encoder emits
268
+ // list_concat both for Dart list concat AND for Map.addAll(...) (encoded as
269
+ // m = list_concat(m, other)). Arrays concat positionally; plain objects
270
+ // (Ball maps) merge by key with the right side winning (so child class
271
+ // methods override parent methods).
272
+ function __ball_concat(a: any, b: any): any {
273
+ const aIsArr = Array.isArray(a);
274
+ const bIsArr = Array.isArray(b);
275
+ if (aIsArr || bIsArr) {
276
+ const al = aIsArr ? a : (a == null ? [] : [a]);
277
+ const bl = bIsArr ? b : (b == null ? [] : [b]);
278
+ return [...al, ...bl];
279
+ }
280
+ if ((a && typeof a === 'object') || (b && typeof b === 'object')) {
281
+ return Object.assign({}, a ?? {}, b ?? {});
282
+ }
283
+ return [a, b];
284
+ }
285
+
286
+ // In-place collection append: mutates target by appending all elements.
287
+ // For arrays: pushes elements (Dart List.addAll semantics).
288
+ // For objects: merges keys (Dart Map.addAll semantics).
289
+ // Preserves reference identity so callers sharing the same collection see changes.
290
+ function __ball_push_all(target: any, items: any): void {
291
+ if (Array.isArray(target)) {
292
+ if (Array.isArray(items)) {
293
+ for (let i = 0; i < items.length; i++) target.push(items[i]);
294
+ } else if (items != null) {
295
+ target.push(items);
296
+ }
297
+ } else if (target && typeof target === 'object') {
298
+ if (items && typeof items === 'object' && !Array.isArray(items)) {
299
+ for (const k of Object.keys(items)) target[k] = items[k];
300
+ }
301
+ }
302
+ }
303
+
304
+ // ── BigInt / signed-64-bit integer support ──────────────────────
305
+ // Dart int is signed 64-bit; JS Number loses precision above 2^53.
306
+ // Arithmetic on BigInt values wraps to the signed 64-bit range and
307
+ // demotes back to Number when the result fits in MAX_SAFE_INTEGER.
308
+ const __I64_MAX = 9223372036854775807n;
309
+ const __I64_MIN = -9223372036854775808n;
310
+ const __I64_MOD = 18446744073709551616n;
311
+ function __i64_wrap(v: bigint): any {
312
+ v = ((v % __I64_MOD) + __I64_MOD) % __I64_MOD;
313
+ if (v > __I64_MAX) v = v - __I64_MOD;
314
+ if (v >= -9007199254740991n && v <= 9007199254740991n) return Number(v);
315
+ return v;
316
+ }
317
+ function __to_bigint(v: any): bigint {
318
+ if (typeof v === 'bigint') return v;
319
+ if (v instanceof BallDouble) return BigInt(Math.trunc(v.value));
320
+ return BigInt(v);
321
+ }
322
+ function __ball_bitand(a: any, b: any): any { return __i64_wrap(__to_bigint(a) & __to_bigint(b)); }
323
+ function __ball_bitor(a: any, b: any): any { return __i64_wrap(__to_bigint(a) | __to_bigint(b)); }
324
+ function __ball_bitxor(a: any, b: any): any { return __i64_wrap(__to_bigint(a) ^ __to_bigint(b)); }
325
+ function __ball_bitnot(a: any): any { return __i64_wrap(~__to_bigint(a)); }
326
+ function __ball_shl(a: any, b: any): any { return __i64_wrap(__to_bigint(a) << __to_bigint(b)); }
327
+ function __ball_shr(a: any, b: any): any { return __i64_wrap(__to_bigint(a) >> __to_bigint(b)); }
328
+ function __ball_negate(a: any): any {
329
+ if (typeof a === 'bigint') return __i64_wrap(-a);
330
+ if (a instanceof BallDouble) return new BallDouble(-a.value);
331
+ return -a;
332
+ }
333
+ function __ball_divide(a: any, b: any): any {
334
+ if (typeof a === 'bigint' || typeof b === 'bigint') {
335
+ const ba = __to_bigint(a), bb = __to_bigint(b);
336
+ const r = ba / bb;
337
+ return __i64_wrap(r);
338
+ }
339
+ return Math.trunc(a / b);
340
+ }
341
+ function __ball_math_abs(a: any): any {
342
+ if (typeof a === 'bigint') {
343
+ const neg = -a;
344
+ return __i64_wrap(neg < 0n ? a : neg);
345
+ }
346
+ return Math.abs(a);
347
+ }
348
+ // Greatest common divisor (Dart's int.gcd). Preserves BigInt (i64) inputs so
349
+ // integer identities round-trip; falls back to Number arithmetic otherwise.
350
+ function __ball_math_gcd(a: any, b: any): any {
351
+ if (typeof a === 'bigint' || typeof b === 'bigint') {
352
+ let x = __to_bigint(a); x = x < 0n ? -x : x;
353
+ let y = __to_bigint(b); y = y < 0n ? -y : y;
354
+ while (y) { const t = y; y = x % y; x = t; }
355
+ return __i64_wrap(x);
356
+ }
357
+ let x = Math.abs(Number(a)), y = Math.abs(Number(b));
358
+ while (y) { const t = y; y = x % y; x = t; }
359
+ return x;
360
+ }
361
+ // Least common multiple, derived from gcd. lcm(0, n) == lcm(n, 0) == 0.
362
+ function __ball_math_lcm(a: any, b: any): any {
363
+ if (typeof a === 'bigint' || typeof b === 'bigint') {
364
+ const x = __to_bigint(a), y = __to_bigint(b);
365
+ if (x === 0n || y === 0n) return __i64_wrap(0n);
366
+ const g = __to_bigint(__ball_math_gcd(x, y));
367
+ const r = (x / g) * y;
368
+ return __i64_wrap(r < 0n ? -r : r);
369
+ }
370
+ const x = Number(a), y = Number(b);
371
+ if (x === 0 || y === 0) return 0;
372
+ const g = Number(__ball_math_gcd(x, y));
373
+ return Math.abs((x / g) * y);
374
+ }
375
+
376
+ // Dart-style Euclidean modulo: result is always non-negative.
377
+ // JS % is remainder (can be negative), Dart % is Euclidean modulo.
378
+ function __dart_mod(a: any, b: any): any {
379
+ if (typeof a === 'bigint' || typeof b === 'bigint') {
380
+ const ba = __to_bigint(a), bb = __to_bigint(b);
381
+ const r = ba % bb;
382
+ return __i64_wrap(r < 0n ? r + (bb < 0n ? -bb : bb) : r);
383
+ }
384
+ const r = a % b;
385
+ return r < 0 ? r + (b < 0 ? -b : b) : r;
386
+ }
387
+
58
388
  // Active exception for rethrow. Catch bodies shadow with a local.
59
389
  let __ball_active_error: any = undefined;
60
390
 
391
+ // Safe own-property lookup. Returns undefined if key is not an own property.
392
+ // Avoids triggering Object.prototype getters (entries, keys, values) on
393
+ // plain objects that aren't meant to be Dart Maps.
394
+ function __ball_own(obj: any, key: any): any {
395
+ if (obj == null || typeof obj !== 'object') return undefined;
396
+ return Object.prototype.hasOwnProperty.call(obj, key) ? obj[key] : undefined;
397
+ }
398
+
399
+ // Dart-style index access. Dart's List '[]' operator throws RangeError on
400
+ // out-of-bounds access, whereas JS array indexing silently returns undefined.
401
+ // To make 'on RangeError' catch clauses behave like Dart we bounds-check list
402
+ // (array) access here and throw a RangeError-shaped exception. Maps, strings
403
+ // and objects keep their JS semantics (no throw) — Dart Map '[]' returns null
404
+ // for absent keys, and String '[]' is handled by callers.
405
+ function __ball_index(target: any, idx: any): any {
406
+ if (Array.isArray(target) && typeof idx === 'number' && Number.isInteger(idx)) {
407
+ if (idx < 0 || idx >= target.length) {
408
+ throw {
409
+ __type__: 'RangeError',
410
+ message: 'RangeError (index): Invalid value: ' +
411
+ (target.length === 0
412
+ ? 'Valid value range is empty: ' + idx
413
+ : 'Not in inclusive range 0..' + (target.length - 1) + ': ' + idx),
414
+ index: idx,
415
+ };
416
+ }
417
+ return target[idx];
418
+ }
419
+ return target[idx];
420
+ }
421
+
61
422
  // Dart type shims — provide static methods for Dart built-in types
62
423
  // that don't exist in JS (int, double, num, bool).
63
424
  const int = {
@@ -84,6 +445,161 @@ const bool = {
84
445
  // late-initialized variables and block-scoped flow tracking.
85
446
  const __no_init__: unique symbol = Symbol('__no_init__');
86
447
 
448
+ // Null-aware spread source normalizer (the ...? operator). Returns an
449
+ // iterable for the spread loop, mapping null / undefined / the __no_init__
450
+ // sentinel (an uninitialized nullable, e.g. List<int>? n;) to an empty list
451
+ // — matching Dart's ...?n which contributes nothing when the operand is null.
452
+ function __ball_spread_iter(v: any): any {
453
+ if (v == null || v === __no_init__) return [];
454
+ return v;
455
+ }
456
+
457
+ // Dart type constructor shims — List, Map, etc.
458
+ const List = {
459
+ filled: (count: any, value: any) => Array(count).fill(value),
460
+ generate: (count: any, generator: any) => {
461
+ const r: any[] = [];
462
+ for (let i = 0; i < count; i++) r.push(generator(i));
463
+ return r;
464
+ },
465
+ from: (iter: any) => Array.isArray(iter) ? [...iter] : [...iter],
466
+ of: (iter: any) => Array.isArray(iter) ? [...iter] : [...iter],
467
+ unmodifiable: (iter: any) => Object.freeze(Array.isArray(iter) ? [...iter] : [...iter]),
468
+ empty: (opts?: any) => [],
469
+ castFrom: (source: any) => Array.isArray(source) ? [...source] : [],
470
+ };
471
+
472
+ // Set.unmodifiable
473
+ const _nativeSet = Set;
474
+ (Set as any).unmodifiable = (iter: any) => {
475
+ const s = new _nativeSet(iter);
476
+ return Object.freeze(s);
477
+ };
478
+ (Set as any).from = (iter: any) => new _nativeSet(iter);
479
+ (Set as any).of = (iter: any) => new _nativeSet(iter);
480
+ // Ball encoder sometimes uses set_create for lists, then list_push on them.
481
+ // Bridge the gap with push/indexOf/length on Set.
482
+ if (!(Set.prototype as any).push) (Set.prototype as any).push = function(v: any) { this.add(v); return this.size; };
483
+ Object.defineProperty(Set.prototype, 'length', { configurable: true, get() { return this.size; } });
484
+ Object.defineProperty(Set.prototype, 'isEmpty', { configurable: true, get() { return this.size === 0; } });
485
+ Object.defineProperty(Set.prototype, 'isNotEmpty', { configurable: true, get() { return this.size !== 0; } });
486
+
487
+ // Patch: scope _bindings must use null-prototype objects to avoid
488
+ // Object.prototype getters (entries, keys, values, length) polluting
489
+ // the "in" operator used by scope.lookup/has/set.
490
+ const _origScopeInit = { patched: false };
491
+ function _patchScopeBindings(scope: any) {
492
+ if (!scope || _origScopeInit.patched) return;
493
+ const ScopeClass = scope.constructor;
494
+ if (!ScopeClass) return;
495
+ const origCtor = ScopeClass;
496
+ const origBind = ScopeClass.prototype.bind;
497
+ // Override bind to lazily convert _bindings to null-proto object
498
+ ScopeClass.prototype.bind = function(name: any, value: any) {
499
+ if (Object.getPrototypeOf(this._bindings) !== null) {
500
+ const entries = Object.entries(this._bindings);
501
+ this._bindings = Object.create(null);
502
+ for (const [k, v] of entries) this._bindings[k] = v;
503
+ }
504
+ return (this._bindings[name] = value);
505
+ };
506
+ // Also patch child() to create null-proto bindings
507
+ const origChild = ScopeClass.prototype.child;
508
+ if (origChild) {
509
+ ScopeClass.prototype.child = function() {
510
+ const c = origChild.call(this);
511
+ if (Object.getPrototypeOf(c._bindings) !== null) {
512
+ c._bindings = Object.create(null);
513
+ }
514
+ return c;
515
+ };
516
+ }
517
+ _origScopeInit.patched = true;
518
+ }
519
+
520
+ // Proto has* functions as global helpers (encoder routes method calls through ball_proto)
521
+ // Generic has* helper — returns true if obj[field] is present and non-null
522
+ function _has(obj: any, field: string): boolean { return obj?.[field] !== undefined && obj?.[field] !== null; }
523
+ function hasMetadata(obj: any): boolean { return _has(obj, 'metadata'); }
524
+ function hasBody(obj: any): boolean { return _has(obj, 'body'); }
525
+ function hasInput(obj: any): boolean { return _has(obj, 'input'); }
526
+ function hasDescriptor(obj: any): boolean { return _has(obj, 'descriptor'); }
527
+ function hasStringValue(obj: any): boolean { return _has(obj, 'stringValue'); }
528
+ function hasBoolValue(obj: any): boolean { return _has(obj, 'boolValue'); }
529
+ function hasNumberValue(obj: any): boolean { return _has(obj, 'numberValue'); }
530
+ function hasResult(obj: any): boolean { return _has(obj, 'result'); }
531
+ function hasCall(obj: any): boolean { return _has(obj, 'call'); }
532
+ function hasListValue(obj: any): boolean { return _has(obj, 'listValue'); }
533
+ function hasNullValue(obj: any): boolean { return _has(obj, 'nullValue'); }
534
+ function hasStructValue(obj: any): boolean { return _has(obj, 'structValue'); }
535
+ function hasMatch(obj: any): boolean { return _has(obj, 'match'); }
536
+ function hasXxx(obj: any): boolean { return false; }
537
+ function whichXxx(obj: any): string { return 'notSet'; }
538
+ // whichExpr/whichValue/whichStmt/whichKind sit on the hottest path of the
539
+ // compiled engine (whichExpr alone runs up to 8x per _evalExpression). The
540
+ // previous 'typeof obj.whichXxx === "function"' probe always walked the
541
+ // prototype chain to the Object.prototype shim installed by installProtoShims
542
+ // (true for EVERY plain object) and then *invoked* it — a prototype walk + a
543
+ // megamorphic keyed-load loop per call, even though the compiled engine's AST
544
+ // nodes never carry an own whichXxx. We gate the method probe behind an
545
+ // own-property check (so plain nodes skip the prototype walk entirely and use
546
+ // the inline discriminator) while still honoring hand-rolled wrapper objects —
547
+ // notably the metadata wrapValue Value wrappers, whose own getters
548
+ // (.stringValue etc.) are always-defined, so their own whichXxx() must win
549
+ // over the inline field probes. Hence the own-method check stays FIRST.
550
+ function whichExpr(obj: any): string {
551
+ if (!obj) return 'notSet';
552
+ if (Object.prototype.hasOwnProperty.call(obj, 'whichExpr') && typeof obj.whichExpr === 'function') return obj.whichExpr();
553
+ if (obj.call) return 'call'; if (obj.literal) return 'literal';
554
+ if (obj.reference) return 'reference'; if (obj.fieldAccess) return 'fieldAccess';
555
+ if (obj.messageCreation) return 'messageCreation'; if (obj.block) return 'block';
556
+ if (obj.lambda) return 'lambda'; return 'notSet';
557
+ }
558
+ function whichValue(obj: any): string {
559
+ if (!obj) return 'notSet';
560
+ if (Object.prototype.hasOwnProperty.call(obj, 'whichValue') && typeof obj.whichValue === 'function') return obj.whichValue();
561
+ if (obj.intValue !== undefined) return 'intValue'; if (obj.doubleValue !== undefined) return 'doubleValue';
562
+ if (obj.stringValue !== undefined) return 'stringValue'; if (obj.boolValue !== undefined) return 'boolValue';
563
+ if (obj.listValue) return 'listValue'; if (obj.bytesValue !== undefined) return 'bytesValue';
564
+ return 'notSet';
565
+ }
566
+ function whichStmt(obj: any): string {
567
+ if (!obj) return 'notSet';
568
+ if (Object.prototype.hasOwnProperty.call(obj, 'whichStmt') && typeof obj.whichStmt === 'function') return obj.whichStmt();
569
+ if (obj.let) return 'let'; if (obj.expression) return 'expression'; return 'notSet';
570
+ }
571
+ function whichKind(obj: any): string {
572
+ if (!obj) return 'notSet';
573
+ if (Object.prototype.hasOwnProperty.call(obj, 'whichKind') && typeof obj.whichKind === 'function') return obj.whichKind();
574
+ if (obj.nullValue !== undefined) return 'nullValue'; if (obj.numberValue !== undefined) return 'numberValue';
575
+ if (obj.stringValue !== undefined) return 'stringValue'; if (obj.boolValue !== undefined) return 'boolValue';
576
+ if (obj.structValue) return 'structValue'; if (obj.listValue) return 'listValue';
577
+ return 'notSet';
578
+ }
579
+ function whichSource(obj: any): string {
580
+ if (!obj) return 'notSet';
581
+ if (obj.path) return 'path'; if (obj.url) return 'url'; if (obj.inline) return 'inline';
582
+ return 'notSet';
583
+ }
584
+
585
+ // Identical function (Dart identical())
586
+ function identical(a: any, b: any): boolean { return a === b; }
587
+
588
+ // Function.apply shim (Dart Function.apply)
589
+ (Function as any).apply = function(fn: any, positionalArgs: any, namedArgs?: any) {
590
+ if (typeof fn !== 'function') return undefined;
591
+ const args = positionalArgs == null ? [] : (Array.isArray(positionalArgs) ? positionalArgs : [positionalArgs]);
592
+ return fn(...args);
593
+ };
594
+
595
+ // Dart cascade helper — evaluates target, applies ops, returns target.
596
+ function __ball_cascade(target: any, ops: any[]): any {
597
+ for (const op of ops) {
598
+ if (typeof op === 'function') op(target);
599
+ }
600
+ return target;
601
+ }
602
+
87
603
  // ── Dart \u2192 JS method-name polyfills ────────────────────────────────
88
604
  //
89
605
  // Idempotent: guarded so multiple preamble inclusions don't double-install.
@@ -118,14 +634,39 @@ const __no_init__: unique symbol = Symbol('__no_init__');
118
634
  for (const v of iter) this.push(v);
119
635
  };
120
636
  if (!ap.removeLast) ap.removeLast = function () { return this.pop(); };
637
+ if (!ap.removeAt) ap.removeAt = function (i: any) { return this.splice(i, 1)[0]; };
638
+ if (!ap.insert) ap.insert = function (i: any, v: any) { this.splice(i, 0, v); };
639
+ if (!ap.setAll) ap.setAll = function (idx: number, values: any[]) { for (let i = 0; i < values.length; i++) this[idx + i] = values[i]; };
121
640
  if (!ap.where) ap.where = Array.prototype.filter;
122
641
  if (!ap.toList) ap.toList = function () { return this.slice(); };
123
642
  if (!ap.toSet) ap.toSet = function () { return new Set(this); };
124
643
  if (!ap.contains) ap.contains = function (v: any) { return this.indexOf(v) >= 0; };
644
+ if (!ap.sublist) ap.sublist = function (start: any, end?: any) { return this.slice(start, end); };
645
+ if (!ap.asMap) ap.asMap = function () {
646
+ const m: any = {};
647
+ for (let i = 0; i < this.length; i++) m[i] = this[i];
648
+ return m;
649
+ };
650
+ if (!ap.expand) ap.expand = function (fn: any) { return this.flatMap(fn); };
651
+ if (!ap.take) ap.take = function (n: any) { return this.slice(0, n); };
652
+ if (!ap.skip) ap.skip = function (n: any) { return this.slice(n); };
653
+ if (!ap.any) ap.any = function (fn: any) { return this.some(fn); };
654
+ if (!ap.fold) ap.fold = function (init: any, fn: any) { return this.reduce(fn, init); };
655
+ if (!ap.followedBy) ap.followedBy = function (other: any) { return [...this, ...other]; };
656
+ if (!ap.getRange) ap.getRange = function (start: any, end: any) { return this.slice(start, end); };
657
+ if (!ap.fillRange) ap.fillRange = function (start: any, end: any, fill: any) {
658
+ for (let i = start; i < end; i++) this[i] = fill;
659
+ };
660
+ if (!ap.setRange) ap.setRange = function (start: any, end: any, iterable: any, skipCount?: any) {
661
+ const src = Array.isArray(iterable) ? iterable : [...iterable];
662
+ const skip = skipCount ?? 0;
663
+ for (let i = start; i < end; i++) this[i] = src[i - start + skip];
664
+ };
125
665
 
126
666
  // Dart Set polyfills — Set.contains → Set.has, etc.
127
667
  const setp: any = Set.prototype;
128
668
  if (!setp.contains) setp.contains = function (v: any) { return this.has(v); };
669
+ if (!setp.includes) setp.includes = function (v: any) { return this.has(v); };
129
670
  if (!setp.toList) setp.toList = function () { return [...this]; };
130
671
  if (!setp.add) { /* Set already has .add */ }
131
672
  if (!setp.remove) setp.remove = function (v: any) { return this.delete(v); };
@@ -149,6 +690,9 @@ const __no_init__: unique symbol = Symbol('__no_init__');
149
690
  Object.defineProperty(sp, 'isNotEmpty', {
150
691
  configurable: true, get() { return this.length !== 0; },
151
692
  });
693
+ // Note: undefined/null safety for .isEmpty/.isNotEmpty is handled in the
694
+ // generated code via optional-chaining (?.isEmpty) patterns and protoWrap;
695
+ // properties cannot be installed on undefined/null directly.
152
696
  // Dart String methods not on JS String.
153
697
  if (!sp.contains) sp.contains = function (s: any) { return this.includes(s); };
154
698
  if (!sp.replaceFirst) sp.replaceFirst = function (from: any, to: any) {
@@ -180,6 +724,37 @@ const __no_init__: unique symbol = Symbol('__no_init__');
180
724
  };
181
725
  if (!rp.hasMatch) rp.hasMatch = function (s: any) { return this.test(s); };
182
726
 
727
+ // Dart Number polyfills — Dart num/int methods not on JS Number.prototype.
728
+ const _ballNp: any = Number.prototype;
729
+ if (!_ballNp.gcd) _ballNp.gcd = function (other: any) {
730
+ let a = Math.abs(this as number), b = Math.abs(Number(other));
731
+ while (b) { const t = b; b = a % b; a = t; }
732
+ return a;
733
+ };
734
+ Object.defineProperty(_ballNp, 'sign', {
735
+ configurable: true, get() { const n = Number(this); return n > 0 ? 1 : n < 0 ? -1 : 0; },
736
+ });
737
+ Object.defineProperty(_ballNp, 'isNaN', {
738
+ configurable: true, get() { return Number.isNaN(Number(this)); },
739
+ });
740
+ Object.defineProperty(_ballNp, 'isFinite', {
741
+ configurable: true, get() { return Number.isFinite(Number(this)); },
742
+ });
743
+ Object.defineProperty(_ballNp, 'isInfinite', {
744
+ configurable: true, get() { const n = Number(this); return n === Infinity || n === -Infinity; },
745
+ });
746
+ if (!_ballNp.abs) _ballNp.abs = function () { return Math.abs(Number(this)); };
747
+ if (!_ballNp.ceil) _ballNp.ceil = function () { return Math.ceil(Number(this)); };
748
+ if (!_ballNp.floor) _ballNp.floor = function () { return Math.floor(Number(this)); };
749
+ if (!_ballNp.round) _ballNp.round = function () { return Math.round(Number(this)); };
750
+ if (!_ballNp.truncate) _ballNp.truncate = function () { return Math.trunc(Number(this)); };
751
+ if (!_ballNp.toInt) _ballNp.toInt = function () { return Math.trunc(Number(this)); };
752
+ if (!_ballNp.toDouble) _ballNp.toDouble = function () { return Number(this); };
753
+ if (!_ballNp.clamp) _ballNp.clamp = function (lo: any, hi: any) { const n = Number(this); return n < lo ? lo : n > hi ? hi : n; };
754
+ if (!_ballNp.compareTo) _ballNp.compareTo = function (other: any) { const a = Number(this), b = Number(other); return a < b ? -1 : a > b ? 1 : 0; };
755
+ if (!_ballNp.toStringAsFixed) _ballNp.toStringAsFixed = function (digits: any) { return Number(this).toFixed(digits); };
756
+ if (!_ballNp.remainder) _ballNp.remainder = function (other: any) { return Number(this) % Number(other); };
757
+
183
758
  // Object.prototype polyfills — used by the compiled engine when
184
759
  // checking Ball program inputs (plain objects, not Maps).
185
760
  const op2: any = Object.prototype;
@@ -189,7 +764,7 @@ const __no_init__: unique symbol = Symbol('__no_init__');
189
764
  value: function (k: any) {
190
765
  if (this instanceof Map) return this.has(k);
191
766
  if (this == null || typeof this !== 'object') return false;
192
- return k in this;
767
+ return Object.prototype.hasOwnProperty.call(this, k);
193
768
  },
194
769
  });
195
770
  }
@@ -207,6 +782,44 @@ const __no_init__: unique symbol = Symbol('__no_init__');
207
782
  },
208
783
  });
209
784
  }
785
+ // addAll — Dart Map.addAll. Works on plain objects too.
786
+ if (!op2.addAll) {
787
+ Object.defineProperty(op2, 'addAll', {
788
+ configurable: true, writable: true, enumerable: false,
789
+ value: function (other: any) {
790
+ if (this instanceof Map) {
791
+ if (other instanceof Map) {
792
+ for (const [k, v] of other.entries()) this.set(k, v);
793
+ } else if (other && typeof other === 'object') {
794
+ for (const k of Object.keys(other)) this.set(k, other[k]);
795
+ }
796
+ } else {
797
+ if (other instanceof Map) {
798
+ for (const [k, v] of other) this[k] = v;
799
+ } else if (other && typeof other === 'object') {
800
+ Object.assign(this, other);
801
+ }
802
+ }
803
+ },
804
+ });
805
+ }
806
+ // forEach — Dart Map.forEach. Works on plain objects.
807
+ // Don't overwrite native Map.prototype.forEach.
808
+ Object.defineProperty(op2, 'forEach', {
809
+ configurable: true, writable: true, enumerable: false,
810
+ value: function (fn: any) {
811
+ if (this instanceof Map) {
812
+ return Map.prototype.forEach.call(this, fn);
813
+ }
814
+ if (Array.isArray(this)) {
815
+ return Array.prototype.forEach.call(this, fn);
816
+ }
817
+ // Plain object: Dart Map.forEach(void f(K key, V value))
818
+ if (typeof fn === 'function') {
819
+ for (const k of Object.keys(this)) fn(k, this[k]);
820
+ }
821
+ },
822
+ });
210
823
  // remove — Dart Map.remove.
211
824
  if (!op2.remove) {
212
825
  Object.defineProperty(op2, 'remove', {
@@ -217,6 +830,14 @@ const __no_init__: unique symbol = Symbol('__no_init__');
217
830
  },
218
831
  });
219
832
  }
833
+ // cast — Dart Map.cast<K2,V2>() / List.cast<E2>(). The cast is a static
834
+ // re-typing only; at runtime it returns the same collection unchanged.
835
+ if (!op2.cast) {
836
+ Object.defineProperty(op2, 'cast', {
837
+ configurable: true, writable: true, enumerable: false,
838
+ value: function () { return this; },
839
+ });
840
+ }
220
841
  // Dart Map has .entries / .keys / .values as GETTERS (no parens).
221
842
  // JS Map has them as METHODS (need parens). The compiled engine
222
843
  // accesses map.entries as a getter. Shadow BOTH Map.prototype AND
@@ -240,41 +861,81 @@ const __no_init__: unique symbol = Symbol('__no_init__');
240
861
  get() { return [..._nativeMapValues.call(this)]; },
241
862
  });
242
863
  // For plain objects — same getters on Object.prototype.
243
- Object.defineProperty(op2, 'entries', {
864
+ // Helper: define a getter on Object.prototype that also allows
865
+ // own-property assignment (setter stores as a data property on the
866
+ // instance, shadowing the prototype getter for future accesses).
867
+ function defDartGetter(name: string, getter: () => any) {
868
+ Object.defineProperty(op2, name, {
869
+ configurable: true, enumerable: false,
870
+ get: getter,
871
+ set(v: any) {
872
+ Object.defineProperty(this, name, {
873
+ value: v, writable: true, configurable: true, enumerable: true,
874
+ });
875
+ },
876
+ });
877
+ }
878
+ defDartGetter('entries', function (this: any) {
879
+ if (this instanceof Map) return [..._nativeMapEntries.call(this)].map(([k, v]: any) => ({ key: k, value: v }));
880
+ if (this == null || typeof this !== 'object') return [];
881
+ return Object.entries(this).map(([k, v]: any) => ({ key: k, value: v }));
882
+ });
883
+ defDartGetter('keys', function (this: any) {
884
+ if (this instanceof Map) return [..._nativeMapKeys.call(this)];
885
+ if (this == null || typeof this !== 'object') return [];
886
+ return Object.keys(this);
887
+ });
888
+ defDartGetter('values', function (this: any) {
889
+ if (this instanceof Map) return [..._nativeMapValues.call(this)];
890
+ if (this == null || typeof this !== 'object') return [];
891
+ return Object.values(this);
892
+ });
893
+ defDartGetter('length', function (this: any) {
894
+ if (this instanceof Map) return this.size;
895
+ if (this instanceof Set) return this.size;
896
+ if (typeof this === 'string' || Array.isArray(this)) return this.length;
897
+ if (this == null || typeof this !== 'object') return 0;
898
+ return Object.keys(this).filter((k: string) => !k.startsWith('__')).length;
899
+ });
900
+
901
+ // runtimeType — Dart's Object.runtimeType. Returns the Dart-style
902
+ // type name for any JS value. Used by the compiled engine for type
903
+ // checking and error messages.
904
+ Object.defineProperty(op2, 'runtimeType', {
244
905
  configurable: true, enumerable: false,
245
906
  get() {
246
- if (this instanceof Map) return [..._nativeMapEntries.call(this)].map(([k, v]: any) => ({ key: k, value: v }));
247
- if (this == null || typeof this !== 'object') return [];
248
- return Object.entries(this).map(([k, v]: any) => ({ key: k, value: v }));
907
+ if (this === null || this === undefined) return 'Null';
908
+ if (this instanceof BallDouble) return 'double';
909
+ if (typeof this === 'number' || this instanceof Number) return Number.isInteger(+this) ? 'int' : 'double';
910
+ if (typeof this === 'string' || this instanceof String) return 'String';
911
+ if (typeof this === 'boolean' || this instanceof Boolean) return 'bool';
912
+ if (typeof this === 'function') return 'Function';
913
+ if (Array.isArray(this)) return 'List';
914
+ if (this instanceof Set) return 'Set';
915
+ if (this instanceof Map) return 'Map';
916
+ if (this instanceof RegExp) return 'RegExp';
917
+ const t = this['__type__'];
918
+ if (typeof t === 'string' && t.length > 0) {
919
+ const ci = t.indexOf(':');
920
+ return ci >= 0 ? t.substring(ci + 1) : t;
921
+ }
922
+ return 'Map';
249
923
  },
250
924
  });
251
- Object.defineProperty(op2, 'keys', {
925
+ // Also add to Number.prototype, String.prototype, Boolean.prototype
926
+ // (they don't inherit from Object.prototype getters reliably for primitives).
927
+ Object.defineProperty(Number.prototype, 'runtimeType', {
252
928
  configurable: true, enumerable: false,
253
- get() {
254
- if (this instanceof Map) return [..._nativeMapKeys.call(this)];
255
- if (this == null || typeof this !== 'object') return [];
256
- return Object.keys(this);
257
- },
929
+ get() { return Number.isInteger(+this) ? 'int' : 'double'; },
258
930
  });
259
- Object.defineProperty(op2, 'values', {
931
+ Object.defineProperty(String.prototype, 'runtimeType', {
260
932
  configurable: true, enumerable: false,
261
- get() {
262
- if (this instanceof Map) return [..._nativeMapValues.call(this)];
263
- if (this == null || typeof this !== 'object') return [];
264
- return Object.values(this);
265
- },
933
+ get() { return 'String'; },
934
+ });
935
+ Object.defineProperty(Boolean.prototype, 'runtimeType', {
936
+ configurable: true, enumerable: false,
937
+ get() { return 'bool'; },
266
938
  });
267
-
268
- // Number polyfills for Dart-style methods.
269
- const np: any = Number.prototype;
270
- if (!np.toInt) np.toInt = function () { return Math.trunc(this); };
271
- if (!np.toDouble) np.toDouble = function () { return this + 0.0; };
272
- if (!np.compareTo) np.compareTo = function (other: any) {
273
- return this < other ? -1 : this > other ? 1 : 0;
274
- };
275
- if (!np.clamp) np.clamp = function (lo: any, hi: any) {
276
- return Math.min(Math.max(this, lo), hi);
277
- };
278
939
  })();
279
940
 
280
941
  // ── Protobuf Struct/Value compatibility ─────────────────────────
@@ -470,4 +1131,208 @@ const ModuleImport_Source = {
470
1131
  return [];
471
1132
  });
472
1133
  })();
1134
+
1135
+ // ── Reified generics helpers ────────────────────────────────────────
1136
+ function __ball_with_type_args<T>(obj: T, args: string[]): T {
1137
+ (obj as any).__type_args__ = args;
1138
+ return obj;
1139
+ }
1140
+
1141
+ // ── Generic type checking helper ────────────────────────────────────
1142
+ function __ball_split_type_args(s: string): string[] {
1143
+ const result: string[] = [];
1144
+ let depth = 0, start = 0;
1145
+ for (let i = 0; i < s.length; i++) {
1146
+ if (s[i] === '<') depth++;
1147
+ else if (s[i] === '>') depth--;
1148
+ else if (s[i] === ',' && depth === 0) {
1149
+ result.push(s.slice(start, i).trim());
1150
+ start = i + 1;
1151
+ }
1152
+ }
1153
+ const last = s.slice(start).trim();
1154
+ if (last) result.push(last);
1155
+ return result;
1156
+ }
1157
+ function __ball_is_type(value: any, typeStr: string): boolean {
1158
+ const t = typeStr.trim();
1159
+ if (t.endsWith('?')) {
1160
+ if (value == null) return true;
1161
+ return __ball_is_type(value, t.slice(0, -1));
1162
+ }
1163
+ const ltIdx = t.indexOf('<');
1164
+ if (ltIdx === -1) {
1165
+ switch (t) {
1166
+ case 'int': return typeof value === 'number' && Number.isInteger(value);
1167
+ case 'double': return value instanceof BallDouble || (typeof value === 'number' && !Number.isInteger(value));
1168
+ case 'num': case 'number': return typeof value === 'number' || value instanceof BallDouble;
1169
+ case 'String': case 'string': return typeof value === 'string';
1170
+ case 'bool': case 'boolean': return typeof value === 'boolean';
1171
+ case 'List': case 'Iterable': return Array.isArray(value);
1172
+ case 'Map': return typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof BallDouble) && !(value instanceof Set);
1173
+ case 'Set': return value instanceof Set;
1174
+ case 'Null': return value == null;
1175
+ case 'Function': return typeof value === 'function';
1176
+ case 'Object': case 'dynamic': return value != null;
1177
+ default: {
1178
+ const objType = value?.__type__ ?? value?.constructor?.name;
1179
+ if (objType === t) return true;
1180
+ return value != null;
1181
+ }
1182
+ }
1183
+ }
1184
+ const baseType = t.slice(0, ltIdx).trim();
1185
+ const typeArgs = __ball_split_type_args(t.slice(ltIdx + 1, t.lastIndexOf('>')));
1186
+ switch (baseType) {
1187
+ case 'List': case 'Iterable':
1188
+ if (!Array.isArray(value)) return false;
1189
+ if (typeArgs.length === 0) return true;
1190
+ return value.every((e: any) => __ball_is_type(e, typeArgs[0]));
1191
+ case 'Map':
1192
+ if (typeof value !== 'object' || value === null || Array.isArray(value) || value instanceof BallDouble || value instanceof Set) return false;
1193
+ if (typeArgs.length < 2) return true;
1194
+ return Object.keys(value).filter((k: string) => !k.startsWith('__')).every((k: string) => __ball_is_type(k, typeArgs[0]) && __ball_is_type(value[k], typeArgs[1]));
1195
+ case 'Set':
1196
+ if (!(value instanceof Set)) return false;
1197
+ if (typeArgs.length === 0) return true;
1198
+ for (const e of value) { if (!__ball_is_type(e, typeArgs[0])) return false; }
1199
+ return true;
1200
+ default: {
1201
+ const objType = value?.__type__ ?? value?.constructor?.name;
1202
+ if (objType === baseType) {
1203
+ const objArgs = value.__type_args__;
1204
+ if (Array.isArray(objArgs)) {
1205
+ if (objArgs.length !== typeArgs.length) return false;
1206
+ return objArgs.every((a: any, i: number) => String(a).trim() === typeArgs[i].trim());
1207
+ }
1208
+ return false;
1209
+ }
1210
+ return __ball_is_type(value, baseType);
1211
+ }
1212
+ }
1213
+ }
1214
+
1215
+ // Minimal DateTime / Duration / Future polyfills used by std_time and
1216
+ // the round-tripped engine's sleep_ms helper. Wide enough for the
1217
+ // conformance suite, narrow enough to stay out of users' way.
1218
+ class DateTime {
1219
+ readonly _epochMs: number;
1220
+ readonly isUtc: boolean;
1221
+ constructor(epochMs?: number, isUtc: boolean = false) {
1222
+ this._epochMs = typeof epochMs === 'number' ? epochMs : Date.now();
1223
+ this.isUtc = isUtc;
1224
+ }
1225
+ static now(): DateTime { return new DateTime(Date.now(), false); }
1226
+ static fromMillisecondsSinceEpoch(ms: number, isUtc: any = false): DateTime {
1227
+ return new DateTime(ms, isUtc === true || (isUtc && (isUtc as any).isUtc === true));
1228
+ }
1229
+ static parse(s: string): DateTime { return new DateTime(Date.parse(s), true); }
1230
+ get millisecondsSinceEpoch(): number { return this._epochMs; }
1231
+ get microsecondsSinceEpoch(): number { return this._epochMs * 1000; }
1232
+ toUtc(): DateTime { return new DateTime(this._epochMs, true); }
1233
+ toIso8601String(): string { return new Date(this._epochMs).toISOString(); }
1234
+ get year(): number { return new Date(this._epochMs).getUTCFullYear(); }
1235
+ get month(): number { return new Date(this._epochMs).getUTCMonth() + 1; }
1236
+ get day(): number { return new Date(this._epochMs).getUTCDate(); }
1237
+ get hour(): number { return new Date(this._epochMs).getUTCHours(); }
1238
+ get minute(): number { return new Date(this._epochMs).getUTCMinutes(); }
1239
+ get second(): number { return new Date(this._epochMs).getUTCSeconds(); }
1240
+ }
1241
+ class Duration {
1242
+ readonly _us: number;
1243
+ constructor(opts?: any) {
1244
+ const o = opts ?? {};
1245
+ const ms = o.milliseconds ?? 0;
1246
+ const s = o.seconds ?? 0;
1247
+ const m = o.minutes ?? 0;
1248
+ const us = o.microseconds ?? 0;
1249
+ this._us = us + ms * 1000 + s * 1_000_000 + m * 60_000_000;
1250
+ }
1251
+ get inMilliseconds(): number { return Math.floor(this._us / 1000); }
1252
+ get inMicroseconds(): number { return this._us; }
1253
+ }
1254
+ const Future = {
1255
+ delayed(d: any): Promise<void> {
1256
+ const ms = (d && typeof d.inMilliseconds === 'number') ? d.inMilliseconds : Number(d ?? 0);
1257
+ return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
1258
+ },
1259
+ value(v: any): Promise<any> { return Promise.resolve(v); },
1260
+ };
1261
+
1262
+ // ── dart:typed_data shims (ByteData, Endian) ───────────────────────
1263
+ // ball_protobuf uses ByteData for IEEE 754 float/double bit conversion.
1264
+ // Dart's ByteData wraps a fixed-size byte buffer with typed get/set methods.
1265
+ const Endian = { little: true, big: false, host: true };
1266
+
1267
+ class ByteData {
1268
+ _view: DataView;
1269
+ _length: number;
1270
+ constructor(size: number) {
1271
+ this._view = new DataView(new ArrayBuffer(size));
1272
+ this._length = size;
1273
+ }
1274
+ get lengthInBytes(): number { return this._length; }
1275
+ getUint8(i: number): number { return this._view.getUint8(i); }
1276
+ setUint8(i: number, v: number): void { this._view.setUint8(i, v); }
1277
+ getInt8(i: number): number { return this._view.getInt8(i); }
1278
+ setInt8(i: number, v: number): void { this._view.setInt8(i, v); }
1279
+ getUint16(i: number, endian?: any): number { return this._view.getUint16(i, endian === Endian.little); }
1280
+ setUint16(i: number, v: number, endian?: any): void { this._view.setUint16(i, v, endian === Endian.little); }
1281
+ getInt16(i: number, endian?: any): number { return this._view.getInt16(i, endian === Endian.little); }
1282
+ setInt16(i: number, v: number, endian?: any): void { this._view.setInt16(i, v, endian === Endian.little); }
1283
+ getUint32(i: number, endian?: any): number { return this._view.getUint32(i, endian === Endian.little); }
1284
+ setUint32(i: number, v: number, endian?: any): void { this._view.setUint32(i, v, endian === Endian.little); }
1285
+ getInt32(i: number, endian?: any): number { return this._view.getInt32(i, endian === Endian.little); }
1286
+ setInt32(i: number, v: number, endian?: any): void { this._view.setInt32(i, v, endian === Endian.little); }
1287
+ getFloat32(i: number, endian?: any): number { return this._view.getFloat32(i, endian === Endian.little); }
1288
+ setFloat32(i: number, v: number, endian?: any): void { this._view.setFloat32(i, v, endian === Endian.little); }
1289
+ getFloat64(i: number, endian?: any): number { return this._view.getFloat64(i, endian === Endian.little); }
1290
+ setFloat64(i: number, v: number, endian?: any): void { this._view.setFloat64(i, v, endian === Endian.little); }
1291
+ getUint64(i: number, endian?: any): bigint {
1292
+ const lo = this._view.getUint32(i, endian === Endian.little);
1293
+ const hi = this._view.getUint32(i + 4, endian === Endian.little);
1294
+ return endian === Endian.little
1295
+ ? BigInt(lo) | (BigInt(hi) << 32n)
1296
+ : (BigInt(lo) << 32n) | BigInt(hi);
1297
+ }
1298
+ setUint64(i: number, v: bigint, endian?: any): void {
1299
+ const lo = Number(v & 0xFFFFFFFFn);
1300
+ const hi = Number((v >> 32n) & 0xFFFFFFFFn);
1301
+ if (endian === Endian.little) {
1302
+ this._view.setUint32(i, lo, true);
1303
+ this._view.setUint32(i + 4, hi, true);
1304
+ } else {
1305
+ this._view.setUint32(i, hi, false);
1306
+ this._view.setUint32(i + 4, lo, false);
1307
+ }
1308
+ }
1309
+ getInt64(i: number, endian?: any): bigint {
1310
+ const unsigned = this.getUint64(i, endian);
1311
+ return unsigned >= 0x8000000000000000n ? unsigned - 0x10000000000000000n : unsigned;
1312
+ }
1313
+ setInt64(i: number, v: bigint, endian?: any): void {
1314
+ this.setUint64(i, v < 0n ? v + 0x10000000000000000n : v, endian);
1315
+ }
1316
+ get buffer(): { asUint8List: (start?: number, length?: number) => number[] } {
1317
+ const view = this._view;
1318
+ return {
1319
+ asUint8List: (start?: number, length?: number) => {
1320
+ const s = start ?? 0;
1321
+ const l = length ?? view.byteLength - s;
1322
+ return [...new Uint8Array(view.buffer, s, l)];
1323
+ },
1324
+ };
1325
+ }
1326
+ }
1327
+
1328
+ // ── dart:convert shims (utf8, jsonEncode, jsonDecode) ───────────────
1329
+ // ball_protobuf uses utf8.encode/decode for string↔bytes and
1330
+ // jsonEncode/jsonDecode for JSON serialization.
1331
+ const utf8 = {
1332
+ encode(s: string): number[] { return [...new TextEncoder().encode(s)]; },
1333
+ decode(bytes: any): string { return new TextDecoder().decode(new Uint8Array(bytes)); },
1334
+ };
1335
+
1336
+ function jsonEncode(obj: any): string { return JSON.stringify(obj); }
1337
+ function jsonDecode(s: string): any { return JSON.parse(s); }
473
1338
  `;