porffor 0.16.0-688a50c13 → 0.16.0-7339fdd79

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.
@@ -0,0 +1,62 @@
1
+ // general widely used ecma262/spec functions
2
+
3
+ // 7.1.5 ToIntegerOrInfinity (argument)
4
+ // https://tc39.es/ecma262/#sec-tointegerorinfinity
5
+ export const __ecma262_ToIntegerOrInfinity = (argument: unknown): number => {
6
+ // 1. Let number be ? ToNumber(argument).
7
+ let number: number = Number(argument);
8
+
9
+ // 2. If number is one of NaN, +0𝔽, or -0𝔽, return 0.
10
+ if (Number.isNaN(number)) return 0;
11
+
12
+ // 3. If number is +∞𝔽, return +∞.
13
+ // 4. If number is -∞𝔽, return -∞.
14
+ if (!Number.isFinite(number)) return number;
15
+
16
+ // 5. Return truncate(ℝ(number)).
17
+ number = Math.trunc(number);
18
+
19
+ // return 0 for -0
20
+ if (number == 0) return 0;
21
+ return number;
22
+ };
23
+
24
+ // todo: support non-bytestring properly
25
+ // 7.1.17 ToString (argument)
26
+ // https://tc39.es/ecma262/#sec-tostring
27
+ export const __ecma262_ToString = (argument: unknown): bytestring => {
28
+ let out: bytestring = '';
29
+ const type: i32 = Porffor.rawType(argument);
30
+
31
+ // 1. If argument is a String, return argument.
32
+ if (Porffor.fastOr(
33
+ type == Porffor.TYPES.string,
34
+ type == Porffor.TYPES.bytestring)) return argument;
35
+
36
+ // 2. If argument is a Symbol, throw a TypeError exception.
37
+ if (type == Porffor.TYPES.symbol) throw new TypeError('Cannot convert a Symbol value to a string');
38
+
39
+ // 3. If argument is undefined, return "undefined".
40
+ if (type == Porffor.TYPES.undefined) return out = 'undefined';
41
+
42
+ // 4. If argument is null, return "null".
43
+ if (Porffor.fastAnd(
44
+ type == Porffor.TYPES.object,
45
+ argument == 0)) return out = 'null';
46
+
47
+ if (type == Porffor.TYPES.boolean) {
48
+ // 5. If argument is true, return "true".
49
+ if (argument == true) return out = 'true';
50
+
51
+ // 6. If argument is false, return "false".
52
+ return out = 'false';
53
+ }
54
+
55
+ // 7. If argument is a Number, return Number::toString(argument, 10).
56
+ // 8. If argument is a BigInt, return BigInt::toString(argument, 10).
57
+ // 9. Assert: argument is an Object.
58
+ // 10. Let primValue be ? ToPrimitive(argument, string).
59
+ // 11. Assert: primValue is not an Object.
60
+ // 12. Return ? ToString(primValue).
61
+ return argument.toString();
62
+ };
@@ -9,7 +9,7 @@ import * as Rhemyn from '../rhemyn/compile.js';
9
9
  import parse from './parse.js';
10
10
  import { log } from './log.js';
11
11
  import Prefs from './prefs.js';
12
- import Allocator from './allocators/index.js';
12
+ import makeAllocator from './allocators.js';
13
13
 
14
14
  let globals = {};
15
15
  let tags = [];
@@ -33,11 +33,6 @@ const todo = (scope, msg, expectsValue = undefined) => {
33
33
 
34
34
  case 'runtime':
35
35
  return internalThrow(scope, 'TodoError', msg, expectsValue);
36
-
37
- // return [
38
- // ...debug(`todo! ${msg}`),
39
- // [ Opcodes.unreachable ]
40
- // ];
41
36
  }
42
37
  };
43
38
 
@@ -431,9 +426,9 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
431
426
  const leftPointer = localTmp(scope, 'concat_left_pointer', Valtype.i32);
432
427
 
433
428
  // alloc/assign array
434
- const [ , pointer ] = makeArray(scope, {
429
+ const [ out, pointer ] = makeArray(scope, {
435
430
  rawElements: new Array(0)
436
- }, global, name, true, 'i16', true);
431
+ }, assign ? false : global, assign ? undefined : name, true, 'i16', true);
437
432
 
438
433
  return [
439
434
  // setup left
@@ -447,7 +442,7 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
447
442
  [ Opcodes.local_set, rightPointer ],
448
443
 
449
444
  // calculate length
450
- ...pointer, // base 0 for store later
445
+ ...out,
451
446
 
452
447
  [ Opcodes.local_get, leftPointer ],
453
448
  [ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
@@ -963,12 +958,9 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
963
958
  [ Opcodes.end ],
964
959
  ]));
965
960
 
966
- // if not already in block, add a block
967
- // if (endOut.length === 0) {
968
- startOut.push(stringOnly([ Opcodes.block, Valtype.i32 ]));
969
- // endOut.push(stringOnly([ Opcodes.end ]));
970
- endOut.unshift(stringOnly([ Opcodes.end ]));
971
- // }
961
+ // add a surrounding block
962
+ startOut.push(stringOnly([ Opcodes.block, Valtype.i32 ]));
963
+ endOut.unshift(stringOnly([ Opcodes.end ]));
972
964
  }
973
965
 
974
966
  return finalize([
@@ -1003,7 +995,7 @@ const asmFuncToAsm = (func, scope) => {
1003
995
  });
1004
996
  };
1005
997
 
1006
- const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false }) => {
998
+ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits = [], returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false }) => {
1007
999
  const existing = funcs.find(x => x.name === name);
1008
1000
  if (existing) return existing;
1009
1001
 
@@ -1017,7 +1009,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1017
1009
 
1018
1010
  for (const x of _data) {
1019
1011
  const copy = { ...x };
1020
- copy.offset += pages.size * pageSize;
1012
+ if (copy.offset != null) copy.offset += pages.size * pageSize;
1021
1013
  data.push(copy);
1022
1014
  }
1023
1015
 
@@ -1271,7 +1263,15 @@ const getNodeType = (scope, node) => {
1271
1263
  }
1272
1264
 
1273
1265
  if (node.type === 'AssignmentExpression') {
1274
- return getNodeType(scope, node.right);
1266
+ const op = node.operator.slice(0, -1) || '=';
1267
+ if (op === '=') return getNodeType(scope, node.right);
1268
+
1269
+ return getNodeType(scope, {
1270
+ type: ['||', '&&', '??'].includes(op) ? 'LogicalExpression' : 'BinaryExpression',
1271
+ left: node.left,
1272
+ right: node.right,
1273
+ operator: op
1274
+ });
1275
1275
  }
1276
1276
 
1277
1277
  if (node.type === 'ArrayExpression') {
@@ -1290,23 +1290,6 @@ const getNodeType = (scope, node) => {
1290
1290
  if (knownLeft === TYPES.bytestring || knownRight === TYPES.bytestring) return TYPES.bytestring;
1291
1291
 
1292
1292
  return TYPES.number;
1293
-
1294
- // todo: string concat types
1295
- // if (node.operator !== '+') return TYPES.number;
1296
- // else return [
1297
- // // if left is string
1298
- // ...getNodeType(scope, node.left),
1299
- // ...number(TYPES.string, Valtype.i32),
1300
- // [ Opcodes.i32_eq ],
1301
-
1302
- // // if right is string
1303
- // ...getNodeType(scope, node.right),
1304
- // ...number(TYPES.string, Valtype.i32),
1305
- // [ Opcodes.i32_eq ],
1306
-
1307
- // // if either are true
1308
- // [ Opcodes.i32_or ],
1309
- // ];
1310
1293
  }
1311
1294
 
1312
1295
  if (node.type === 'UnaryExpression') {
@@ -1326,7 +1309,6 @@ const getNodeType = (scope, node) => {
1326
1309
  if (Prefs.fastLength) return TYPES.number;
1327
1310
  }
1328
1311
 
1329
-
1330
1312
  const objectKnownType = knownType(scope, getNodeType(scope, node.object));
1331
1313
  if (objectKnownType != null) {
1332
1314
  if (name === 'length') {
@@ -1337,7 +1319,6 @@ const getNodeType = (scope, node) => {
1337
1319
  if (node.computed) {
1338
1320
  if (objectKnownType === TYPES.string) return TYPES.string;
1339
1321
  if (objectKnownType === TYPES.bytestring) return TYPES.bytestring;
1340
- if (objectKnownType === TYPES.array) return TYPES.number;
1341
1322
  }
1342
1323
  }
1343
1324
 
@@ -1403,10 +1384,10 @@ const countLeftover = wasm => {
1403
1384
 
1404
1385
  if (depth === 0)
1405
1386
  if ([Opcodes.throw, Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
1406
- else if ([null, Opcodes.i32_eqz, Opcodes.i64_eqz, Opcodes.f64_ceil, Opcodes.f64_floor, Opcodes.f64_trunc, Opcodes.f64_nearest, Opcodes.f64_sqrt, Opcodes.local_tee, Opcodes.i32_wrap_i64, Opcodes.i64_extend_i32_s, Opcodes.i64_extend_i32_u, Opcodes.f32_demote_f64, Opcodes.f64_promote_f32, Opcodes.f64_convert_i32_s, Opcodes.f64_convert_i32_u, Opcodes.i32_clz, Opcodes.i32_ctz, Opcodes.i32_popcnt, Opcodes.f64_neg, Opcodes.end, Opcodes.i32_trunc_sat_f64_s[0], Opcodes.i32x4_extract_lane, Opcodes.i16x8_extract_lane, Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load, Opcodes.v128_load, Opcodes.i32_load16_u, Opcodes.i32_load16_s, Opcodes.i32_load8_u, Opcodes.i32_load8_s, Opcodes.memory_grow].includes(inst[0]) && (inst[0] !== 0xfc || inst[1] < 0x0a)) {}
1387
+ else if ([null, Opcodes.i32_eqz, Opcodes.i64_eqz, Opcodes.f64_ceil, Opcodes.f64_floor, Opcodes.f64_trunc, Opcodes.f64_nearest, Opcodes.f64_sqrt, Opcodes.local_tee, Opcodes.i32_wrap_i64, Opcodes.i64_extend_i32_s, Opcodes.i64_extend_i32_u, Opcodes.f32_demote_f64, Opcodes.f64_promote_f32, Opcodes.f64_convert_i32_s, Opcodes.f64_convert_i32_u, Opcodes.i32_clz, Opcodes.i32_ctz, Opcodes.i32_popcnt, Opcodes.f64_neg, Opcodes.end, Opcodes.i32_trunc_sat_f64_s[0], Opcodes.i32x4_extract_lane, Opcodes.i16x8_extract_lane, Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load, Opcodes.v128_load, Opcodes.i32_load16_u, Opcodes.i32_load16_s, Opcodes.i32_load8_u, Opcodes.i32_load8_s, Opcodes.memory_grow].includes(inst[0]) && (inst[0] !== 0xfc || inst[1] < 0x04)) {}
1407
1388
  else if ([Opcodes.local_get, Opcodes.global_get, Opcodes.f64_const, Opcodes.i32_const, Opcodes.i64_const, Opcodes.v128_const, Opcodes.memory_size].includes(inst[0])) count++;
1408
1389
  else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
1409
- else if (Opcodes.memory_copy[0] === inst[0] && Opcodes.memory_copy[1] === inst[1]) count -= 3;
1390
+ else if (inst[0] === Opcodes.memory_copy[0] && (inst[1] === Opcodes.memory_copy[1] || inst[1] === Opcodes.memory_init[1])) count -= 3;
1410
1391
  else if (inst[0] === Opcodes.return) count = 0;
1411
1392
  else if (inst[0] === Opcodes.call) {
1412
1393
  let func = funcs.find(x => x.index === inst[1]);
@@ -1951,13 +1932,18 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1951
1932
  continue;
1952
1933
  }
1953
1934
 
1954
- if (valtypeBinary !== Valtype.i32 && (
1935
+ if (valtypeBinary !== Valtype.i32 &&
1955
1936
  (builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32)
1956
- // (importedFuncs[name] && name.startsWith('profile'))
1957
- )) {
1937
+ ) {
1958
1938
  out.push(Opcodes.i32_to);
1959
1939
  }
1960
1940
 
1941
+ if (valtypeBinary === Valtype.i32 &&
1942
+ (builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.f64)
1943
+ ) {
1944
+ out.push(Opcodes.f64_convert_i32_s);
1945
+ }
1946
+
1961
1947
  if (typedParams) out = out.concat(getNodeType(scope, arg));
1962
1948
  }
1963
1949
 
@@ -1979,6 +1965,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1979
1965
  out.push(Opcodes.i32_from);
1980
1966
  }
1981
1967
 
1968
+ if (builtinFuncs[name] && builtinFuncs[name].returns?.[0] === Valtype.f64 && valtypeBinary === Valtype.i32) {
1969
+ out.push(Opcodes.i32_trunc_sat_f64_s);
1970
+ }
1971
+
1982
1972
  return out;
1983
1973
  };
1984
1974
 
@@ -2002,9 +1992,9 @@ const generateNew = (scope, decl, _global, _name) => {
2002
1992
  if (
2003
1993
  (builtinFuncs[name] && !builtinFuncs[name].constr) ||
2004
1994
  (internalConstrs[name] && builtinFuncs[name].notConstr)
2005
- ) return internalThrow(scope, 'TypeError', `${name} is not a constructor`);
1995
+ ) return internalThrow(scope, 'TypeError', `${name} is not a constructor`, true);
2006
1996
 
2007
- if (!builtinFuncs[name]) return todo(scope, `new statement is not supported yet`); // return todo(scope, `new statement is not supported yet (new ${unhackName(name)})`);
1997
+ if (!builtinFuncs[name]) return todo(scope, `new statement is not supported yet`, true); // return todo(scope, `new statement is not supported yet (new ${unhackName(name)})`);
2008
1998
 
2009
1999
  return generateCall(scope, decl, _global, _name);
2010
2000
  };
@@ -2452,9 +2442,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2452
2442
  [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
2453
2443
  [ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
2454
2444
 
2455
- // todo: string concat types
2456
-
2457
- ...setType(scope, name, TYPES.number)
2445
+ ...setType(scope, name, getNodeType(scope, decl))
2458
2446
  ];
2459
2447
  };
2460
2448
 
@@ -3117,8 +3105,6 @@ const allocPage = (scope, reason, type) => {
3117
3105
  scope.pages ??= new Map();
3118
3106
  scope.pages.set(reason, { ind, type });
3119
3107
 
3120
- if (Prefs.allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
3121
-
3122
3108
  return ind;
3123
3109
  };
3124
3110
 
@@ -3156,6 +3142,46 @@ const compileBytes = (val, itemType) => {
3156
3142
  }
3157
3143
  };
3158
3144
 
3145
+ const makeData = (scope, elements, offset = null, itemType, initEmpty) => {
3146
+ const length = elements.length;
3147
+
3148
+ // if length is 0 memory/data will just be 0000... anyway
3149
+ if (length === 0) return false;
3150
+
3151
+ let bytes = compileBytes(length, 'i32');
3152
+
3153
+ if (!initEmpty) for (let i = 0; i < length; i++) {
3154
+ if (elements[i] == null) continue;
3155
+
3156
+ bytes.push(...compileBytes(elements[i], itemType));
3157
+ }
3158
+
3159
+ const obj = { bytes };
3160
+ if (offset != null) obj.offset = offset;
3161
+
3162
+ const idx = data.push(obj) - 1;
3163
+
3164
+ scope.data ??= [];
3165
+ scope.data.push(idx);
3166
+
3167
+ return { idx, size: bytes.length };
3168
+ };
3169
+
3170
+ const printStaticStr = str => {
3171
+ const out = [];
3172
+
3173
+ for (let i = 0; i < str.length; i++) {
3174
+ out.push(
3175
+ // ...number(str.charCodeAt(i)),
3176
+ ...number(str.charCodeAt(i), Valtype.i32),
3177
+ Opcodes.i32_from_u,
3178
+ [ Opcodes.call, importedFuncs.printChar ]
3179
+ );
3180
+ }
3181
+
3182
+ return out;
3183
+ };
3184
+
3159
3185
  const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, intOut = false, typed = false) => {
3160
3186
  if (itemType !== 'i16' && itemType !== 'i8') {
3161
3187
  pages.hasArray = true;
@@ -3175,17 +3201,50 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3175
3201
  const valtype = itemTypeToValtype[itemType];
3176
3202
  const length = elements.length;
3177
3203
 
3178
- const allocated = allocator.alloc({ scope, pages, globals }, uniqueName, { itemType });
3204
+ const allocated = allocator.alloc({ scope, pages, globals, asmFunc, funcIndex }, uniqueName, { itemType });
3179
3205
 
3180
3206
  let pointer = allocated;
3181
3207
  if (allocator.constructor.name !== 'StaticAllocator') {
3182
- const tmp = localTmp(scope, '#makearray_pointer' + uniqueName, Valtype.i32);
3208
+ // const tmp = localTmp(scope, '#makearray_pointer' + uniqueName, Valtype.i32);
3209
+ const tmp = localTmp(scope, '#makearray_pointer' + name, Valtype.i32);
3183
3210
  out.push(
3184
3211
  ...allocated,
3185
3212
  [ Opcodes.local_set, tmp ]
3186
3213
  );
3187
3214
 
3215
+ if (Prefs.runtimeAllocLog) out.push(
3216
+ ...printStaticStr(`${name}: `),
3217
+
3218
+ [ Opcodes.local_get, tmp ],
3219
+ Opcodes.i32_from_u,
3220
+ [ Opcodes.call, 0 ],
3221
+
3222
+ ...number(10),
3223
+ [ Opcodes.call, 1 ]
3224
+ );
3225
+
3188
3226
  pointer = [ [ Opcodes.local_get, tmp ] ];
3227
+
3228
+ if (Prefs.data && useRawElements) {
3229
+ const data = makeData(scope, elements, null, itemType, initEmpty);
3230
+ if (data) {
3231
+ // init data
3232
+ out.push(
3233
+ ...pointer,
3234
+ ...number(0, Valtype.i32),
3235
+ ...number(data.size, Valtype.i32),
3236
+ [ ...Opcodes.memory_init, ...unsignedLEB128(data.idx), 0 ]
3237
+ );
3238
+ }
3239
+
3240
+ // return pointer in out
3241
+ out.push(
3242
+ ...pointer,
3243
+ ...(!intOut ? [ Opcodes.i32_from_u ] : [])
3244
+ );
3245
+
3246
+ return [ out, pointer ];
3247
+ }
3189
3248
  } else {
3190
3249
  const rawPtr = read_signedLEB128(pointer[0].slice(1));
3191
3250
 
@@ -3194,24 +3253,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3194
3253
  if (firstAssign) scope.arrays.set(uniqueName, rawPtr);
3195
3254
 
3196
3255
  if (Prefs.data && firstAssign && useRawElements) {
3197
- // if length is 0 memory/data will just be 0000... anyway
3198
- if (length !== 0) {
3199
- let bytes = compileBytes(length, 'i32');
3200
-
3201
- if (!initEmpty) for (let i = 0; i < length; i++) {
3202
- if (elements[i] == null) continue;
3203
-
3204
- bytes.push(...compileBytes(elements[i], itemType));
3205
- }
3206
-
3207
- const ind = data.push({
3208
- offset: rawPtr,
3209
- bytes
3210
- }) - 1;
3211
-
3212
- scope.data ??= [];
3213
- scope.data.push(ind);
3214
- }
3256
+ makeData(scope, elements, rawPtr, itemType, initEmpty);
3215
3257
 
3216
3258
  // local value as pointer
3217
3259
  return [ number(rawPtr, intOut ? Valtype.i32 : valtypeBinary), pointer ];
@@ -3479,7 +3521,7 @@ const generateMember = (scope, decl, _global, _name) => {
3479
3521
  // // todo: we should only do this for strings but we don't know at compile-time :(
3480
3522
  // hack: this is naughty and will break things!
3481
3523
  let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
3482
- if (pages.hasAnyString) {
3524
+ if (pages.hasAnyString && knownType(scope, getNodeType(scope, decl.object)) !== TYPES.array) {
3483
3525
  // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
3484
3526
  0, [ newOut, newPointer ] = makeArray(scope, {
3485
3527
  rawElements: new Array(0)
@@ -3694,9 +3736,9 @@ const internalConstrs = {
3694
3736
 
3695
3737
  // new Array(n)
3696
3738
 
3697
- const [ , pointer ] = makeArray(scope, {
3739
+ const [ out, pointer ] = makeArray(scope, {
3698
3740
  rawElements: new Array(0)
3699
- }, global, name, true);
3741
+ }, global, name, true, undefined, true);
3700
3742
 
3701
3743
  const arg = decl.arguments[0] ?? DEFAULT_VALUE;
3702
3744
 
@@ -3705,7 +3747,7 @@ const internalConstrs = {
3705
3747
  if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length', true);
3706
3748
 
3707
3749
  return [
3708
- ...pointer,
3750
+ ...out,
3709
3751
  ...generate(scope, arg, global, name),
3710
3752
  Opcodes.i32_to_u,
3711
3753
  [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
@@ -3895,7 +3937,7 @@ export default program => {
3895
3937
  builtinFuncs = new BuiltinFuncs();
3896
3938
  builtinVars = new BuiltinVars();
3897
3939
  prototypeFuncs = new PrototypeFuncs();
3898
- allocator = Allocator(Prefs.allocator ?? 'static');
3940
+ allocator = makeAllocator(Prefs.allocator ?? 'static');
3899
3941
 
3900
3942
  program.id = { name: 'main' };
3901
3943