porffor 0.30.0 → 0.30.2

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.
@@ -665,4 +665,21 @@ export const __Object_prototype_toLocaleString = (_this: any) => __Object_protot
665
665
  export const __Object_prototype_valueOf = (_this: any) => {
666
666
  // todo: ToObject
667
667
  return _this;
668
+ };
669
+
670
+
671
+ export const __Porffor_object_spread = (dst: object, src: any): object => {
672
+ if (src == null) return;
673
+
674
+ // todo/perf: optimize this (and assign) for object instead of reading over object 2x
675
+ const keys: any[] = __Object_keys(src);
676
+ const vals: any[] = __Object_values(src);
677
+
678
+ const len: i32 = keys.length;
679
+ for (let i: i32 = 0; i < len; i++) {
680
+ // target[keys[i]] = vals[i];
681
+ Porffor.object.expr.init(dst, keys[i], vals[i]);
682
+ }
683
+
684
+ return dst;
668
685
  };
@@ -1914,6 +1914,12 @@ export const BuiltinFuncs = function() {
1914
1914
  returns: [124,127], typedReturns: 1,
1915
1915
  locals: [], localNames: ["_this","_this#type"],
1916
1916
  };
1917
+ this.__Porffor_object_spread = {
1918
+ wasm: (scope, {builtin}) => [[32,2],[33,4],[32,3],[33,5],[2,127],[32,5],[65,0],[70],[4,64,"TYPESWITCH|empty"],[65,1],[12,1],[11],[32,5],[65,7],[70],[4,64,"TYPESWITCH|Object"],[32,4],[68,0,0,0,0,0,0,0,0],[97],[12,1],[11],[32,5],[65,128,1],[70],[4,64,"TYPESWITCH|undefined"],[65,1],[12,1],[11],[65,0],[11,"TYPESWITCH_end"],[4,64],[68,0,0,0,0,0,0,0,0],[65,128,1],[15],[11],[32,2],[32,3],[16, builtin('__Object_keys')],[26],[33,6],[32,2],[32,3],[16, builtin('__Object_values')],[26],[33,8],[32,6],[252,3],[40,1,0],[184],[33,9],[68,0,0,0,0,0,0,0,0],[33,10],[3,64],[32,10],[32,9],[99],[4,64],[32,0],[252,2],[65,7],[32,6],[33,11],[32,10],[34,12],[252,3],[65,9],[108],[32,11],[252,3],[106],[34,13],[43,0,4],[32,13],[45,0,12],[33,7],[252,2],[32,7],[32,8],[33,11],[32,10],[34,12],[252,3],[65,9],[108],[32,11],[252,3],[106],[34,13],[43,0,4],[32,13],[45,0,12],[34,7],[16, builtin('__Porffor_object_expr_init')],[33,7],[183],[26],[32,10],[68,0,0,0,0,0,0,240,63],[160],[33,10],[12,1],[11],[11],[32,0],[65,7],[15]],
1919
+ params: [124,127,124,127], typedParams: 1,
1920
+ returns: [124,127], typedReturns: 1,
1921
+ locals: [124,127,124,127,124,124,124,124,124,127,127,127], localNames: ["dst","dst#type","src","src#type","#logicinner_tmp","#typeswitch_tmp1","keys","#last_type","vals","len","i","#member_obj","#member_prop","#loadArray_offset","#member_allocd","#swap"],
1922
+ };
1917
1923
  this.__ecma262_NewPromiseReactionJob = {
1918
1924
  wasm: (scope, {builtin}) => [[65,22],[16, builtin('__Porffor_allocateBytes')],[183],[34,4],[33,7],[68,0,0,0,0,0,0,0,0],[33,8],[32,7],[252,3],[32,8],[252,3],[65,9],[108],[106],[34,6],[32,0],[34,5],[57,0,4],[32,6],[65,208,0],[58,0,12],[32,4],[33,7],[68,0,0,0,0,0,0,240,63],[33,8],[32,7],[252,3],[32,8],[252,3],[65,9],[108],[106],[34,6],[32,2],[34,5],[57,0,4],[32,6],[32,3],[58,0,12],[32,4],[65,208,0],[15]],
1919
1925
  params: [124,127,124,127], typedParams: 1,
@@ -1438,11 +1438,25 @@ const getType = (scope, _name) => {
1438
1438
 
1439
1439
  if (Object.hasOwn(builtinVars, name)) return number(builtinVars[name].type ?? TYPES.number, Valtype.i32);
1440
1440
 
1441
- if (typedInput && scope.locals[name]?.metadata?.type != null) return number(scope.locals[name].metadata.type, Valtype.i32);
1442
- if (Object.hasOwn(scope.locals, name)) return [ [ Opcodes.local_get, scope.locals[name + '#type'].idx ] ];
1441
+ if (Object.hasOwn(scope.locals, name)) {
1442
+ if (scope.locals[name]?.metadata?.type != null) return number(scope.locals[name].metadata.type, Valtype.i32);
1443
1443
 
1444
- if (typedInput && globals[name]?.metadata?.type != null) return number(globals[name].metadata.type, Valtype.i32);
1445
- if (Object.hasOwn(globals, name)) return [ [ Opcodes.global_get, globals[name + '#type'].idx ] ];
1444
+ const typeLocal = scope.locals[name + '#type'];
1445
+ if (typeLocal) return [ [ Opcodes.local_get, typeLocal.idx ] ];
1446
+
1447
+ // todo: warn here?
1448
+ return number(TYPES.undefined, Valtype.i32);
1449
+ }
1450
+
1451
+ if (Object.hasOwn(globals, name)) {
1452
+ if (globals[name]?.metadata?.type != null) return number(globals[name].metadata.type, Valtype.i32);
1453
+
1454
+ const typeLocal = globals[name + '#type'];
1455
+ if (typeLocal) return [ [ Opcodes.global_get, typeLocal.idx ] ];
1456
+
1457
+ // todo: warn here?
1458
+ return number(TYPES.undefined, Valtype.i32);
1459
+ }
1446
1460
 
1447
1461
  if (Object.hasOwn(builtinFuncs, name) || Object.hasOwn(importedFuncs, name) ||
1448
1462
  Object.hasOwn(funcIndex, name) || Object.hasOwn(internalConstrs, name))
@@ -1458,17 +1472,31 @@ const setType = (scope, _name, type) => {
1458
1472
 
1459
1473
  const out = typeof type === 'number' ? number(type, Valtype.i32) : type;
1460
1474
 
1461
- if (typedInput && scope.locals[name]?.metadata?.type != null) return [];
1462
- if (Object.hasOwn(scope.locals, name)) return [
1463
- ...out,
1464
- [ Opcodes.local_set, scope.locals[name + '#type'].idx ]
1465
- ];
1475
+ if (Object.hasOwn(scope.locals, name)) {
1476
+ if (scope.locals[name]?.metadata?.type != null) return [];
1466
1477
 
1467
- if (typedInput && globals[name]?.metadata?.type != null) return [];
1468
- if (Object.hasOwn(globals, name)) return [
1469
- ...out,
1470
- [ Opcodes.global_set, globals[name + '#type'].idx ]
1471
- ];
1478
+ const typeLocal = scope.locals[name + '#type'];
1479
+ if (typeLocal) return [
1480
+ ...out,
1481
+ [ Opcodes.local_set, typeLocal.idx ]
1482
+ ];
1483
+
1484
+ // todo: warn here?
1485
+ return [];
1486
+ }
1487
+
1488
+ if (Object.hasOwn(globals, name)) {
1489
+ if (globals[name]?.metadata?.type != null) return [];
1490
+
1491
+ const typeLocal = globals[name + '#type'];
1492
+ if (typeLocal) return [
1493
+ ...out,
1494
+ [ Opcodes.global_set, typeLocal.idx ]
1495
+ ];
1496
+
1497
+ // todo: warn here?
1498
+ return [];
1499
+ }
1472
1500
 
1473
1501
  // throw new Error('could not find var');
1474
1502
  return [];
@@ -1772,6 +1800,13 @@ const disposeLeftover = wasm => {
1772
1800
  const generateExp = (scope, decl) => {
1773
1801
  const expression = decl.expression;
1774
1802
 
1803
+ if (expression.type === 'Literal' && typeof expression.value === 'string') {
1804
+ if (expression.value === 'use strict') {
1805
+ scope.strict = true;
1806
+ }
1807
+ return [];
1808
+ }
1809
+
1775
1810
  const out = generate(scope, expression, undefined, undefined, Prefs.optUnused);
1776
1811
  disposeLeftover(out);
1777
1812
 
@@ -3096,7 +3131,7 @@ const generateVarDstr = (scope, kind, pattern, init, defaultValue, global) => {
3096
3131
  elements.push(...e.argument.elements);
3097
3132
  } else {
3098
3133
  decls.push(
3099
- ...generateVarDstr(scope, kind, e.argument.name, {
3134
+ ...generateVarDstr(scope, kind, e.argument, {
3100
3135
  type: 'CallExpression',
3101
3136
  callee: {
3102
3137
  type: 'Identifier',
@@ -3202,8 +3237,22 @@ const generateVarDstr = (scope, kind, pattern, init, defaultValue, global) => {
3202
3237
  }, undefined, global)
3203
3238
  );
3204
3239
  }
3205
- } else { // let { ...foo } = {}
3206
- return todo(scope, 'object rest destructuring is not supported yet')
3240
+ } else if (prop.type === 'RestElement') { // let { ...foo } = {}
3241
+ decls.push(
3242
+ ...generateVarDstr(scope, kind, prop.argument, {
3243
+ type: 'CallExpression',
3244
+ callee: {
3245
+ type: 'Identifier',
3246
+ name: '__Porffor_object_spread'
3247
+ },
3248
+ arguments: [
3249
+ { type: 'ObjectExpression', properties: [] },
3250
+ { type: 'Identifier', name: tmpName }
3251
+ ]
3252
+ }, undefined, global)
3253
+ );
3254
+ } else {
3255
+ return todo(scope, `${prop.type} is not supported in object patterns`);
3207
3256
  }
3208
3257
  }
3209
3258
 
@@ -3565,10 +3614,8 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3565
3614
  }
3566
3615
 
3567
3616
  if (local === undefined) {
3568
- // todo: this should be a sloppy mode only thing
3569
-
3570
- // only allow = for this
3571
- if (op !== '=') return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`);
3617
+ // only allow = for this, or if in strict mode always throw
3618
+ if (op !== '=' || scope.strict) return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
3572
3619
 
3573
3620
  if (type != 'Identifier') {
3574
3621
  const tmpName = '#rhs' + uniqId();
@@ -3581,6 +3628,8 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3581
3628
  }
3582
3629
 
3583
3630
  if (Object.hasOwn(builtinVars, name)) {
3631
+ if (scope.strict) return internalThrow(scope, 'TypeError', `Cannot assign to non-writable global ${name}`, true);
3632
+
3584
3633
  // just return rhs (eg `NaN = 2`)
3585
3634
  return generate(scope, decl.right);
3586
3635
  }
@@ -3707,7 +3756,6 @@ const generateUnary = (scope, decl) => {
3707
3756
 
3708
3757
  // if ReferenceError (undeclared var), ignore and return true. otherwise false
3709
3758
  if (!out[1]) {
3710
- // todo: throw in strict mode
3711
3759
  // exists
3712
3760
  toReturn = false;
3713
3761
  } else {
@@ -3979,10 +4027,9 @@ const generateForOf = (scope, decl) => {
3979
4027
 
3980
4028
  // setup local for left
3981
4029
  let setVar;
3982
-
3983
4030
  if (decl.left.type === 'Identifier') {
3984
- // todo: should be sloppy mode only
3985
- setVar = generateVarDstr(scope, 'var', decl.left.name, { type: 'Identifier', name: tmpName }, undefined, true);
4031
+ if (scope.strict) return internalThrow(scope, 'ReferenceError', `${decl.left.name} is not defined`);
4032
+ setVar = generateVarDstr(scope, 'var', decl.left, { type: 'Identifier', name: tmpName }, undefined, true);
3986
4033
  } else {
3987
4034
  // todo: verify this is correct
3988
4035
  const global = scope.name === 'main' && decl.left.kind === 'var';
@@ -4347,14 +4394,13 @@ const generateForIn = (scope, decl) => {
4347
4394
  localTmp(scope, tmpName + '#type', Valtype.i32);
4348
4395
 
4349
4396
  let setVar;
4350
-
4351
4397
  if (decl.left.type === 'Identifier') {
4352
- // todo: should be sloppy mode only
4353
- setVar = generateVarDstr(scope, 'var', decl.left.name, { type: 'Identifier', name: tmpName }, undefined, true);
4398
+ if (scope.strict) return internalThrow(scope, 'ReferenceError', `${decl.left.name} is not defined`);
4399
+ setVar = generateVarDstr(scope, 'var', decl.left, { type: 'Identifier', name: tmpName }, undefined, true);
4354
4400
  } else {
4355
4401
  // todo: verify this is correct
4356
4402
  const global = scope.name === 'main' && decl.left.kind === 'var';
4357
- setVar = generateVarDstr(scope, 'var', decl.left.declarations[0].id, { type: 'Identifier', name: tmpName }, undefined, global);
4403
+ setVar = generateVarDstr(scope, 'var', decl.left?.declarations?.[0]?.id ?? decl.left, { type: 'Identifier', name: tmpName }, undefined, global);
4358
4404
  }
4359
4405
 
4360
4406
  // set type for local
@@ -5090,12 +5136,33 @@ const generateObject = (scope, decl, global = false, name = '$undeclared') => {
5090
5136
  ];
5091
5137
 
5092
5138
  if (decl.properties.length > 0) {
5093
- const tmp = localTmp(scope, `#objectexpr${uniqId()}`, Valtype.i32);
5139
+ const tmpName = `#objectexpr${uniqId()}`;
5140
+ const tmp = localTmp(scope, tmpName, Valtype.i32);
5141
+ addVarMetadata(scope, tmpName, false, { type: TYPES.object });
5142
+
5094
5143
  out.push([ Opcodes.local_tee, tmp ]);
5095
5144
 
5096
5145
  for (const x of decl.properties) {
5097
5146
  // method, shorthand are made into useful values by parser for us :)
5098
- const { computed, kind, key, value } = x;
5147
+ const { type, argument, computed, kind, key, value } = x;
5148
+
5149
+ if (type === 'SpreadElement') {
5150
+ out.push(
5151
+ ...generateCall(scope, {
5152
+ type: 'CallExpression',
5153
+ callee: {
5154
+ type: 'Identifier',
5155
+ name: '__Porffor_object_spread'
5156
+ },
5157
+ arguments: [
5158
+ { type: 'Identifier', name: tmpName },
5159
+ argument
5160
+ ]
5161
+ }),
5162
+ [ Opcodes.drop ]
5163
+ );
5164
+ continue;
5165
+ }
5099
5166
 
5100
5167
  let k = key;
5101
5168
  if (!computed && key.type !== 'Literal') k = {
@@ -5577,7 +5644,8 @@ const generateFunc = (scope, decl) => {
5577
5644
  // not arrow function or main
5578
5645
  (decl.type && decl.type !== 'ArrowFunctionExpression' && decl.type !== 'Program') &&
5579
5646
  // not async or generator
5580
- !decl.async && !decl.generator
5647
+ !decl.async && !decl.generator,
5648
+ strict: scope.strict
5581
5649
  };
5582
5650
 
5583
5651
  funcIndex[name] = func.index;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "porffor",
3
3
  "description": "a basic experimental wip aot optimizing js -> wasm engine/compiler/runtime in js",
4
- "version": "0.30.0+b1785d63e",
4
+ "version": "0.30.2+2e9f2148f",
5
5
  "author": "CanadaHonk",
6
6
  "license": "MIT",
7
7
  "scripts": {},
package/runner/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import fs from 'node:fs';
3
- globalThis.version = '0.30.0+b1785d63e';
3
+ globalThis.version = '0.30.2+2e9f2148f';
4
4
 
5
5
  // deno compat
6
6
  if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
@@ -115,7 +115,10 @@ if (process.argv.length >= 4) {
115
115
  if (evalIndex !== -1) {
116
116
  source = process.argv[evalIndex + 1];
117
117
  if (source) {
118
- if (source.startsWith('"') || source.startsWith("'")) source = source.slice(1, -1);
118
+ // todo: this isn't entirely right, because shells should do the quoting for us but works well enough, see below as well
119
+ if ((source.startsWith('"') || source.startsWith("'")) && (source.endsWith('"') || source.endsWith("'"))) {
120
+ source = source.slice(1, -1);
121
+ }
119
122
  process.argv.splice(evalIndex, 2); // remove flag and value
120
123
  }
121
124
  }
@@ -126,7 +129,9 @@ if (process.argv.length >= 4) {
126
129
  process.argv.push('--no-opt-unused');
127
130
  source = process.argv[printIndex + 1];
128
131
  if (source) {
129
- if (source.startsWith('"') || source.startsWith("'")) source = source.slice(1, -1);
132
+ if ((source.startsWith('"') || source.startsWith("'")) && (source.endsWith('"') || source.endsWith("'"))) {
133
+ source = source.slice(1, -1);
134
+ }
130
135
  process.argv.splice(printIndex, 2); // remove flag and value
131
136
  }
132
137