@ball-lang/engine 1.4.2 → 1.4.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.
@@ -3385,9 +3385,6 @@ export class BallEngine {
3385
3385
  }
3386
3386
  }
3387
3387
  }
3388
- if (((__ball_eq(name, 'List') || __ball_eq(name, 'Map')) || __ball_eq(name, 'Set'))) {
3389
- return { ['__class_ref__']: name, ['__type__']: '__builtin_class__' };
3390
- }
3391
3388
  if (scope.has(name)) {
3392
3389
  let bound = scope.lookup(name);
3393
3390
  if (!__ball_eq(bound, null)) {
@@ -3499,6 +3496,9 @@ export class BallEngine {
3499
3496
  this._globalScope.bind(staticField.fullName, value);
3500
3497
  return value;
3501
3498
  }
3499
+ if (((__ball_eq(name, 'List') || __ball_eq(name, 'Map')) || __ball_eq(name, 'Set'))) {
3500
+ return { ['__class_ref__']: name, ['__type__']: '__builtin_class__' };
3501
+ }
3502
3502
  return scope.lookup(name);
3503
3503
  }
3504
3504
 
@@ -3992,15 +3992,17 @@ export class BallEngine {
3992
3992
  }
3993
3993
  let instanceFields = {};
3994
3994
  let allFieldNames = this._collectAllFieldNames(msg.typeName);
3995
- for (const fieldName of typeDef.fieldNames) {
3996
- instanceFields[fieldName] = null;
3997
- }
3998
3995
  for (const entry of fields.entries) {
3999
3996
  if (!entry.key.startsWith('arg')) {
4000
3997
  instanceFields[entry.key] = entry.value;
4001
3998
  }
4002
3999
  }
4003
4000
  this._initFieldDefaults(msg.typeName, instanceFields);
4001
+ for (const fieldName of typeDef.fieldNames) {
4002
+ if (!(fieldName in instanceFields)) {
4003
+ instanceFields[fieldName] = null;
4004
+ }
4005
+ }
4004
4006
  let ctorEntry = this._lookupConstructor(msg.typeName);
4005
4007
  let resolvedParams = {};
4006
4008
  if ((!__ball_eq(ctorEntry, null) && hasMetadata(ctorEntry.func))) {
@@ -4282,6 +4284,24 @@ export class BallEngine {
4282
4284
  if ((__ball_eq(trimmed, '""') || __ball_eq(trimmed, '\'\''))) {
4283
4285
  return '';
4284
4286
  }
4287
+ if ((((trimmed.length >= 2) && trimmed.startsWith('[')) && trimmed.endsWith(']'))) {
4288
+ let inner = trimmed.substring(1, __ball_sub(trimmed.length, 1)).trim();
4289
+ if ((inner.length === 0)) {
4290
+ return [];
4291
+ }
4292
+ if ((!inner.includes('[') && !inner.includes('{'))) {
4293
+ return [...inner.split(',').map(((s) => {
4294
+ const input = s;
4295
+ return s.trim();
4296
+ })).filter(((s) => {
4297
+ const input = s;
4298
+ return !(s.length === 0);
4299
+ })).map(((s) => {
4300
+ const input = s;
4301
+ return this._parseInitializer(s);
4302
+ }))];
4303
+ }
4304
+ }
4285
4305
  let intVal = int.tryParse(trimmed);
4286
4306
  if (!__ball_eq(intVal, null)) {
4287
4307
  return intVal;
@@ -4290,7 +4310,7 @@ export class BallEngine {
4290
4310
  if (!__ball_eq(doubleVal, null)) {
4291
4311
  return doubleVal;
4292
4312
  }
4293
- if (((trimmed.startsWith('\'') && trimmed.endsWith('\'')) || (trimmed.startsWith('"') && trimmed.endsWith('"')))) {
4313
+ if (((trimmed.length >= 2) && ((trimmed.startsWith('\'') && trimmed.endsWith('\'')) || (trimmed.startsWith('"') && trimmed.endsWith('"'))))) {
4294
4314
  return trimmed.substring(1, __ball_sub(trimmed.length, 1));
4295
4315
  }
4296
4316
  return trimmed;
@@ -4334,6 +4354,12 @@ export class BallEngine {
4334
4354
  superFields[fname] = __ball_index(childFields, fname);
4335
4355
  }
4336
4356
  }
4357
+ this._initFieldDefaults(superclass, superFields);
4358
+ for (const fname of parentTypeDef.fieldNames) {
4359
+ if (!(fname in superFields)) {
4360
+ superFields[fname] = null;
4361
+ }
4362
+ }
4337
4363
  let parentMethods = this._resolveTypeMethods(qualifiedSuperclass);
4338
4364
  let parentMethodsMap = parentMethods.cast();
4339
4365
  let grandparent = parentTypeDef.superclass;
@@ -485,8 +485,16 @@ export function createEngineSetup(mod: EngineModule) {
485
485
  if (v && typeof v === 'object' && v.__ball_generator__ === true) return __bts(v.values);
486
486
  if (Array.isArray(v)) return '[' + v.map(__bts).join(', ') + ']';
487
487
  if (v instanceof Map) {
488
+ // NOT `v.entries()` — the compiled engine's own preamble shadows
489
+ // `Map.prototype.entries` with a Dart-style GETTER (returning an array
490
+ // of {key,value} objects, matching Dart's `Map.entries` property) so
491
+ // Ball's `.entries` field access works. That shadow makes `v.entries`
492
+ // non-callable, so `v.entries()` throws "not a function" for any real
493
+ // Map value. Iterate the Map directly instead — a Map's default
494
+ // iterator already yields [key, value] pairs and is unaffected by the
495
+ // entries/keys/values property shadowing.
488
496
  const parts: string[] = [];
489
- for (const [k, val] of v.entries()) parts.push(__bts(k) + ': ' + __bts(val));
497
+ for (const [k, val] of v) parts.push(__bts(k) + ': ' + __bts(val));
490
498
  return '{' + parts.join(', ') + '}';
491
499
  }
492
500
  if (v instanceof Set) return '{' + [...v].map(__bts).join(', ') + '}';
@@ -906,7 +914,16 @@ export function createEngineSetup(mod: EngineModule) {
906
914
  _r('string_char_at', (i: any) => { const m = _m(i); return String(m['value'] ?? m['string'] ?? '').charAt(Number(m['index'] ?? m['arg0'] ?? 0)); });
907
915
  _r('string_to_int', (i: any) => {
908
916
  const m = _m(i); const s = String(m['value'] ?? m['string'] ?? i ?? '').trim();
909
- if (!/^-?\d+$/.test(s)) throw Object.assign(new Error('FormatException: ' + s), { __type__: 'FormatException', message: s });
917
+ // `.message` must be the bare invalid text (Dart's `FormatException.message`
918
+ // is never prefixed with the type name — only `.toString()` adds that).
919
+ // Setting `name: 'FormatException'` (not just a `message` override) lets
920
+ // the default `Error.prototype.toString()` ("name: message") produce the
921
+ // Dart-correct "FormatException: <text>" for anything that prints the
922
+ // caught exception itself rather than `.message`. Previously the message
923
+ // override clobbered the "FormatException: " prefix right back out,
924
+ // leaving `.name` as the native "Error" — undetected because no
925
+ // conformance fixture prints a caught FormatException directly.
926
+ if (!/^-?\d+$/.test(s)) throw Object.assign(new Error(s), { name: 'FormatException', __type__: 'FormatException' });
910
927
  return parseInt(s, 10);
911
928
  });
912
929
  _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; });
@@ -965,7 +982,21 @@ export function createEngineSetup(mod: EngineModule) {
965
982
  return true;
966
983
  });
967
984
  _r('concat', (i: any) => { const m = _m(i); return String(m['left'] ?? '') + String(m['right'] ?? ''); });
968
- _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; });
985
+ // `?? i` (used by most unary helpers below) can't distinguish "no `value`
986
+ // field" from "`value` is explicitly null" — for every OTHER helper that's
987
+ // harmless (a null numeric/string arg just coerces away), but for
988
+ // null_check it inverts the function's entire purpose: `x!` where `x` is
989
+ // null must throw (matches the Dart engine's `_extractUnaryArg` + null
990
+ // check — see engine_std.dart's 'null_check' and its
991
+ // "throws BallRuntimeError on null" test), yet `m['value'] ?? i` silently
992
+ // fell back to the (truthy) input map and returned it instead of
993
+ // throwing. hasOwnProperty-gate so an explicit null is honored.
994
+ _r('null_check', (i: any) => {
995
+ const m = _m(i);
996
+ const v = Object.prototype.hasOwnProperty.call(m, 'value') ? m['value'] : i;
997
+ if (v == null) throw new Error('Null check operator used on a null value');
998
+ return v;
999
+ });
969
1000
  _r('compare_to', (i: any) => {
970
1001
  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;
971
1002
  if (typeof l === 'string' && typeof r === 'string') return l < r ? -1 : l > r ? 1 : 0;