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