@ball-lang/engine 1.63.1 → 1.65.0
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/dist/compiled_engine.d.ts +3 -0
- package/dist/compiled_engine.d.ts.map +1 -1
- package/dist/compiled_engine.js +125 -13
- package/dist/compiled_engine.js.map +1 -1
- package/dist/engine_setup.d.ts.map +1 -1
- package/dist/engine_setup.js +9 -2
- package/dist/engine_setup.js.map +1 -1
- package/package.json +1 -1
- package/src/compiled_engine.ts +120 -13
- package/src/engine_setup.ts +9 -2
package/src/compiled_engine.ts
CHANGED
|
@@ -1474,6 +1474,36 @@ function __ball_is_type(value: any, typeStr: string): boolean {
|
|
|
1474
1474
|
}
|
|
1475
1475
|
}
|
|
1476
1476
|
|
|
1477
|
+
// std.type_of (#489) - the string form of the very discrimination
|
|
1478
|
+
// __ball_is_type performs, i.e. the canonical BASE type name with generic
|
|
1479
|
+
// type arguments dropped and any module prefix stripped. Bare JS typeof
|
|
1480
|
+
// cannot be used: typeof [] is 'object', not 'list', and it cannot tell an
|
|
1481
|
+
// int from a double. Must agree with the Dart reference engine's _typeNameOf.
|
|
1482
|
+
function __ball_type_of(value: any): string {
|
|
1483
|
+
if (value == null) return 'Null';
|
|
1484
|
+
if (typeof value === 'boolean') return 'bool';
|
|
1485
|
+
if (value instanceof BallDouble) return 'double';
|
|
1486
|
+
if (typeof value === 'number') return Number.isInteger(value) ? 'int' : 'double';
|
|
1487
|
+
if (typeof value === 'string') return 'String';
|
|
1488
|
+
if (Array.isArray(value)) return 'List';
|
|
1489
|
+
if (value instanceof Set) return 'Set';
|
|
1490
|
+
if (typeof value === 'function') return 'Function';
|
|
1491
|
+
if (typeof value === 'object') {
|
|
1492
|
+
const tag = value.__type__;
|
|
1493
|
+
if (typeof tag === 'string' && tag.length > 0) {
|
|
1494
|
+
const colon = tag.indexOf(':');
|
|
1495
|
+
return colon >= 0 ? tag.slice(colon + 1) : tag;
|
|
1496
|
+
}
|
|
1497
|
+
// A compiled user class is a real TS class; a Dart Map is a plain object.
|
|
1498
|
+
const ctor = value.constructor?.name;
|
|
1499
|
+
if (typeof ctor === 'string' && ctor.length > 0 && ctor !== 'Object') {
|
|
1500
|
+
return ctor;
|
|
1501
|
+
}
|
|
1502
|
+
return 'Map';
|
|
1503
|
+
}
|
|
1504
|
+
return typeof value;
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1477
1507
|
// Minimal DateTime / Duration / Future polyfills used by std_time and
|
|
1478
1508
|
// the round-tripped engine's sleep_ms helper. Wide enough for the
|
|
1479
1509
|
// conformance suite, narrow enough to stay out of users' way.
|
|
@@ -1662,6 +1692,12 @@ export class BallEngine {
|
|
|
1662
1692
|
this.maxProgramSizeBytes = maxProgramSizeBytes;
|
|
1663
1693
|
this.sandbox = sandbox;
|
|
1664
1694
|
this.moduleHandlers = moduleHandlers;
|
|
1695
|
+
this.stdout = stdout ?? print;
|
|
1696
|
+
this._resolver = resolver;
|
|
1697
|
+
this.stderr = stderr ?? ((s) => io.stderr.writeln(s));
|
|
1698
|
+
this._envGet = envGet ?? ((name) => io.Platform.environment[name] ?? '');
|
|
1699
|
+
this._args = args ?? [];
|
|
1700
|
+
this.moduleHandlers = moduleHandlers ?? [StdModuleHandler()];
|
|
1665
1701
|
this._validateProgramLimits();
|
|
1666
1702
|
if (enableProfiling) {
|
|
1667
1703
|
this._callCounts = {};
|
|
@@ -2327,6 +2363,7 @@ export class BallEngine {
|
|
|
2327
2363
|
}
|
|
2328
2364
|
}
|
|
2329
2365
|
this._initFieldDefaults(typeName, instanceFields);
|
|
2366
|
+
this._applyConstructorInitializers(func, instanceFields, resolvedParams, true);
|
|
2330
2367
|
let superclass = this._getMetaString(typeDef, 'superclass');
|
|
2331
2368
|
let superObject;
|
|
2332
2369
|
if ((!__ball_eq(superclass, null) && !(superclass.length === 0))) {
|
|
@@ -2391,17 +2428,21 @@ export class BallEngine {
|
|
|
2391
2428
|
targetFields[name] = null;
|
|
2392
2429
|
}
|
|
2393
2430
|
} else {
|
|
2394
|
-
if (
|
|
2395
|
-
targetFields[name] =
|
|
2431
|
+
if ((__ball_ge(valStr.length, 2) && ((valStr.startsWith('\'') && valStr.endsWith('\'')) || (valStr.startsWith('"') && valStr.endsWith('"'))))) {
|
|
2432
|
+
targetFields[name] = valStr.substring(1, __ball_sub(valStr.length, 1));
|
|
2396
2433
|
} else {
|
|
2397
|
-
if (__ball_eq(valStr, '
|
|
2398
|
-
targetFields[name] =
|
|
2434
|
+
if (__ball_eq(valStr, 'true')) {
|
|
2435
|
+
targetFields[name] = true;
|
|
2399
2436
|
} else {
|
|
2400
|
-
if (
|
|
2401
|
-
|
|
2402
|
-
targetFields[name] = (valStr.includes('.') ? new BallDouble(new BallDouble(Number(numVal))) : __ball_to_int(numVal));
|
|
2437
|
+
if (__ball_eq(valStr, 'false')) {
|
|
2438
|
+
targetFields[name] = false;
|
|
2403
2439
|
} else {
|
|
2404
|
-
|
|
2440
|
+
if (!__ball_eq(num.tryParse(valStr), null)) {
|
|
2441
|
+
let numVal = num.parse(valStr);
|
|
2442
|
+
targetFields[name] = (valStr.includes('.') ? new BallDouble(new BallDouble(Number(numVal))) : __ball_to_int(numVal));
|
|
2443
|
+
} else {
|
|
2444
|
+
targetFields[name] = (__ball_index(resolvedParams, valStr) ?? valStr);
|
|
2445
|
+
}
|
|
2405
2446
|
}
|
|
2406
2447
|
}
|
|
2407
2448
|
}
|
|
@@ -4264,6 +4305,25 @@ export class BallEngine {
|
|
|
4264
4305
|
}
|
|
4265
4306
|
}
|
|
4266
4307
|
|
|
4308
|
+
_isBareSelfConstruction(msg: any): any {
|
|
4309
|
+
const input = msg;
|
|
4310
|
+
let ctorEntry = this._lookupConstructor(msg.typeName);
|
|
4311
|
+
let params = ((!__ball_eq(ctorEntry, null) && hasMetadata(ctorEntry.func)) ? this._extractParams(ctorEntry.func.metadata) : []);
|
|
4312
|
+
for (const pair of msg.fields) {
|
|
4313
|
+
let name = pair.name;
|
|
4314
|
+
if (((__ball_eq(name, '__type_args__') || __ball_eq(name, 'type_args')) || __ball_eq(name, '__const__'))) {
|
|
4315
|
+
continue;
|
|
4316
|
+
}
|
|
4317
|
+
if (new RegExp('^arg\\d+$').hasMatch(name)) {
|
|
4318
|
+
return false;
|
|
4319
|
+
}
|
|
4320
|
+
if (params.includes(name)) {
|
|
4321
|
+
return false;
|
|
4322
|
+
}
|
|
4323
|
+
}
|
|
4324
|
+
return true;
|
|
4325
|
+
}
|
|
4326
|
+
|
|
4267
4327
|
async _evalMessageCreation(msg: any, scope: any): Promise<any> {
|
|
4268
4328
|
let fields = {};
|
|
4269
4329
|
for (const pair of msg.fields) {
|
|
@@ -4288,7 +4348,7 @@ export class BallEngine {
|
|
|
4288
4348
|
let self = scope.lookup('self');
|
|
4289
4349
|
let constructorType = scope.lookup('__constructor_type__');
|
|
4290
4350
|
let selfMap = this._asMap(self);
|
|
4291
|
-
if ((!__ball_eq(selfMap, null) && __ball_eq(constructorType, msg.typeName))) {
|
|
4351
|
+
if (((!__ball_eq(selfMap, null) && __ball_eq(constructorType, msg.typeName)) && this._isBareSelfConstruction(msg))) {
|
|
4292
4352
|
return self;
|
|
4293
4353
|
}
|
|
4294
4354
|
}
|
|
@@ -4334,7 +4394,7 @@ export class BallEngine {
|
|
|
4334
4394
|
}
|
|
4335
4395
|
}
|
|
4336
4396
|
}
|
|
4337
|
-
if ((
|
|
4397
|
+
if ((!__ball_eq(ctorEntry, null) && hasMetadata(ctorEntry.func))) {
|
|
4338
4398
|
this._applyConstructorInitializers(ctorEntry.func, instanceFields, resolvedParams, true);
|
|
4339
4399
|
}
|
|
4340
4400
|
let superclass = this._getMetaString(typeDef, 'superclass');
|
|
@@ -4388,7 +4448,7 @@ export class BallEngine {
|
|
|
4388
4448
|
}
|
|
4389
4449
|
return instance;
|
|
4390
4450
|
} else {
|
|
4391
|
-
if (((scope.has('self') && scope.has('__constructor_type__')) && __ball_eq(scope.lookup('__constructor_type__'), msg.typeName))) {
|
|
4451
|
+
if ((((scope.has('self') && scope.has('__constructor_type__')) && __ball_eq(scope.lookup('__constructor_type__'), msg.typeName)) && this._isBareSelfConstruction(msg))) {
|
|
4392
4452
|
let self = scope.lookup('self');
|
|
4393
4453
|
if (!__ball_eq(this._asMap(self), null)) {
|
|
4394
4454
|
return self;
|
|
@@ -5460,7 +5520,7 @@ export class BallEngine {
|
|
|
5460
5520
|
let eBare = (__ball_ge(eColonIdx, 0) ? eType.substring(__ball_add(eColonIdx, 1)) : eType);
|
|
5461
5521
|
matches = (__ball_eq(eType, catchType) || __ball_eq(eBare, catchType));
|
|
5462
5522
|
} else {
|
|
5463
|
-
matches = __ball_eq(
|
|
5523
|
+
matches = __ball_eq(__ball_type_of(e), catchType);
|
|
5464
5524
|
}
|
|
5465
5525
|
}
|
|
5466
5526
|
if (!matches) {
|
|
@@ -7276,7 +7336,7 @@ export class BallEngine {
|
|
|
7276
7336
|
}), ['null_aware_access']: this._stdNullAwareAccess.bind(this), ['null_aware_call']: this._stdNullAwareCall.bind(this), ['if']: this._stdIf.bind(this), ['is']: this._stdTypeCheck.bind(this), ['is_not']: ((i) => {
|
|
7277
7337
|
const input = i;
|
|
7278
7338
|
return !this._stdTypeCheck(i);
|
|
7279
|
-
}), ['as']: this._extractUnaryArg.bind(this), ['index']: this._stdIndex.bind(this), ['cascade']: this._stdCascade.bind(this), ['null_aware_cascade']: this._stdNullAwareCascade.bind(this), ['spread']: this._extractUnaryArg.bind(this), ['null_spread']: this._extractUnaryArg.bind(this), ['invoke']: this._stdInvoke.bind(this), ['tear_off']: ((i) => {
|
|
7339
|
+
}), ['as']: this._extractUnaryArg.bind(this), ['type_of']: this._stdTypeOf.bind(this), ['index']: this._stdIndex.bind(this), ['cascade']: this._stdCascade.bind(this), ['null_aware_cascade']: this._stdNullAwareCascade.bind(this), ['spread']: this._extractUnaryArg.bind(this), ['null_spread']: this._extractUnaryArg.bind(this), ['invoke']: this._stdInvoke.bind(this), ['tear_off']: ((i) => {
|
|
7280
7340
|
const input = i;
|
|
7281
7341
|
let m = this._stdAsMap(i);
|
|
7282
7342
|
if (!__ball_eq(m, null)) {
|
|
@@ -8855,6 +8915,52 @@ export class BallEngine {
|
|
|
8855
8915
|
return this._typeMatches(value, type);
|
|
8856
8916
|
}
|
|
8857
8917
|
|
|
8918
|
+
_stdTypeOf(input: any): any {
|
|
8919
|
+
let m = this._stdAsMap(input);
|
|
8920
|
+
if (__ball_eq(m, null)) {
|
|
8921
|
+
throw new BallRuntimeError('std.type_of: expected an input message');
|
|
8922
|
+
}
|
|
8923
|
+
return this._typeNameOf(__ball_index(m, 'value'));
|
|
8924
|
+
}
|
|
8925
|
+
|
|
8926
|
+
_typeNameOf(value: any): any {
|
|
8927
|
+
const input = value;
|
|
8928
|
+
if ((__ball_eq(value, null) || (value == null))) {
|
|
8929
|
+
return 'Null';
|
|
8930
|
+
}
|
|
8931
|
+
if (_ballIsBool(value)) {
|
|
8932
|
+
return 'bool';
|
|
8933
|
+
}
|
|
8934
|
+
if (_ballIsInt(value)) {
|
|
8935
|
+
return 'int';
|
|
8936
|
+
}
|
|
8937
|
+
if (_ballIsDouble(value)) {
|
|
8938
|
+
return 'double';
|
|
8939
|
+
}
|
|
8940
|
+
if (_ballIsString(value)) {
|
|
8941
|
+
return 'String';
|
|
8942
|
+
}
|
|
8943
|
+
if (_ballIsList(value)) {
|
|
8944
|
+
return 'List';
|
|
8945
|
+
}
|
|
8946
|
+
if (this._isBallSet(value)) {
|
|
8947
|
+
return 'Set';
|
|
8948
|
+
}
|
|
8949
|
+
if (((typeof value === 'function') || (typeof value === 'function'))) {
|
|
8950
|
+
return 'Function';
|
|
8951
|
+
}
|
|
8952
|
+
let objMap = this._stdAsMap(value);
|
|
8953
|
+
if (!__ball_eq(objMap, null)) {
|
|
8954
|
+
let tag = __ball_index(objMap, '__type__');
|
|
8955
|
+
if (((typeof tag === 'string') && !(tag.length === 0))) {
|
|
8956
|
+
let colonIdx = tag.indexOf(':');
|
|
8957
|
+
return (__ball_ge(colonIdx, 0) ? tag.substring(__ball_add(colonIdx, 1)) : tag);
|
|
8958
|
+
}
|
|
8959
|
+
return 'Map';
|
|
8960
|
+
}
|
|
8961
|
+
return __ball_to_string(value.runtimeType);
|
|
8962
|
+
}
|
|
8963
|
+
|
|
8858
8964
|
_typeMatches(value: any, type: any): any {
|
|
8859
8965
|
let genericMatch = new RegExp('^(\\w+)<(.+)>$').firstMatch(type);
|
|
8860
8966
|
if (!__ball_eq(genericMatch, null)) {
|
|
@@ -10229,6 +10335,7 @@ export class StdModuleHandler extends BallModuleHandler {
|
|
|
10229
10335
|
|
|
10230
10336
|
constructor() {
|
|
10231
10337
|
super();
|
|
10338
|
+
this._allowlist = null;
|
|
10232
10339
|
}
|
|
10233
10340
|
|
|
10234
10341
|
get registeredFunctions(): any {
|
package/src/engine_setup.ts
CHANGED
|
@@ -976,8 +976,15 @@ export function createEngineSetup(mod: EngineModule) {
|
|
|
976
976
|
_r('string_to_upper_case', (i: any) => { const m = _m(i); return String(m['value'] ?? m['string'] ?? '').toUpperCase(); });
|
|
977
977
|
_r('string_to_lower_case', (i: any) => { const m = _m(i); return String(m['value'] ?? m['string'] ?? '').toLowerCase(); });
|
|
978
978
|
_r('string_trim', (i: any) => { const m = _m(i); return String(m['value'] ?? m['string'] ?? '').trim(); });
|
|
979
|
-
|
|
980
|
-
|
|
979
|
+
// The encoder emits both of these as a canonical `BinaryInput`
|
|
980
|
+
// (`left`/`right`), like every other two-operand string op. Reading only
|
|
981
|
+
// `value`/`prefix` made BOTH lookups miss and collapse to
|
|
982
|
+
// `''.startsWith('')` / `''.endsWith('')` -- ALWAYS TRUE, silently. The
|
|
983
|
+
// corpus never caught it because every fixture using them asserted a TRUE
|
|
984
|
+
// answer (260/382/416); 260_string_functions now pins the false cases too.
|
|
985
|
+
// Legacy `value`/`prefix`/`suffix`/`pattern` spellings stay as fallbacks.
|
|
986
|
+
_r('string_starts_with', (i: any) => { const m = _m(i); return String(m['left'] ?? m['value'] ?? m['string'] ?? '').startsWith(String(m['right'] ?? m['prefix'] ?? m['pattern'] ?? '')); });
|
|
987
|
+
_r('string_ends_with', (i: any) => { const m = _m(i); return String(m['left'] ?? m['value'] ?? m['string'] ?? '').endsWith(String(m['right'] ?? m['suffix'] ?? m['pattern'] ?? '')); });
|
|
981
988
|
// `m['width'] ?? m['length'] ?? 0` has the same Object.prototype '.length'
|
|
982
989
|
// getter hazard as `_countOf` above: plain bracket access on 'length'
|
|
983
990
|
// never falls through to the `0` default (it returns the input map's own
|