porffor 0.19.5 → 0.19.7

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.
@@ -13,10 +13,13 @@ export const __Map_prototype_get = (_this: Map, key: any) => {
13
13
  const keys: Set = Porffor.wasm.i32.load(_this, 0, 0);
14
14
  const vals: any[] = Porffor.wasm.i32.load(_this, 0, 4);
15
15
 
16
- const keyIdx: i32 = Porffor.set.indexOf(keys, key);
17
- if (keyIdx == -1) return undefined;
16
+ const size: i32 = Porffor.wasm.i32.load(keys, 0, 0);
17
+
18
+ for (let i: i32 = 0; i < size; i++) {
19
+ if (Porffor.set.read(keys, i) === key) return vals[i];
20
+ }
18
21
 
19
- return vals[keyIdx];
22
+ return undefined;
20
23
  };
21
24
 
22
25
  export const __Map_prototype_set = (_this: Map, key: any, value: any) => {
@@ -25,30 +28,42 @@ export const __Map_prototype_set = (_this: Map, key: any, value: any) => {
25
28
 
26
29
  const size: i32 = Porffor.wasm.i32.load(keys, 0, 0);
27
30
 
28
- let keyIdx: i32 = Porffor.set.indexOf(keys, key);
29
- if (keyIdx == -1) {
30
- // add key if non-existent
31
- keyIdx = size;
32
- __Set_prototype_add(keys, key);
31
+ for (let i: i32 = 0; i < size; i++) {
32
+ if (Porffor.set.read(keys, i) === key) {
33
+ vals[i] = value;
34
+ return _this;
35
+ }
33
36
  }
34
37
 
35
- vals[keyIdx] = value;
38
+ // add key if non-existent
39
+ // increment size by 1
40
+ Porffor.wasm.i32.store(keys, size + 1, 0, 0);
41
+
42
+ // write new key at end
43
+ __Porffor_set_write(keys, size, key);
44
+
45
+ // write new value at end
46
+ vals[size] = value;
36
47
 
37
48
  return _this;
38
49
  };
39
50
 
40
51
  export const __Map_prototype_delete = (_this: Map, key: any) => {
41
52
  const keys: Set = Porffor.wasm.i32.load(_this, 0, 0);
53
+ const vals: any[] = Porffor.wasm.i32.load(_this, 0, 4);
42
54
 
43
- const keyIdx = Porffor.set.indexOf(keys, key);
44
- if (keyIdx == -1) return false;
55
+ const size: i32 = Porffor.wasm.i32.load(keys, 0, 0);
45
56
 
46
- __Set_prototype_delete(keys, key);
57
+ for (let i: i32 = 0; i < size; i++) {
58
+ if (Porffor.set.read(keys, i) === key) {
59
+ __Set_prototype_delete(keys, key);
60
+ __Array_prototype_splice(vals, i, 1);
47
61
 
48
- const vals: any[] = Porffor.wasm.i32.load(_this, 0, 4);
49
- __Array_prototype_splice(vals, keyIdx, 1);
62
+ return true;
63
+ }
64
+ }
50
65
 
51
- return true;
66
+ return false;
52
67
  };
53
68
 
54
69
  export const __Map_prototype_clear = (_this: Map) => {
@@ -1231,12 +1231,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1231
1231
  return func;
1232
1232
  };
1233
1233
 
1234
- const includeBuiltin = (scope, builtin) => {
1235
- const code = builtinFuncs[builtin];
1236
- if (code.wasm) return asmFunc(builtin, code);
1237
-
1238
- return code.body.map(x => generate(scope, x));
1239
- };
1234
+ const includeBuiltin = (scope, builtin) => asmFunc(builtin, builtinFuncs[builtin]);
1240
1235
 
1241
1236
  const generateLogicExp = (scope, decl) => {
1242
1237
  return performLogicOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right));
@@ -1341,6 +1336,10 @@ const getNodeType = (scope, node) => {
1341
1336
  return getType(scope, node.name);
1342
1337
  }
1343
1338
 
1339
+ if (node.type === 'ObjectExpression') {
1340
+ return TYPES.object;
1341
+ }
1342
+
1344
1343
  if (node.type === 'CallExpression' || node.type === 'NewExpression') {
1345
1344
  const name = node.callee.name;
1346
1345
  if (!name) {
@@ -1926,7 +1925,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1926
1925
  }
1927
1926
 
1928
1927
  // TODO: only allows callee as identifier
1929
- if (!name) return todo(scope, `only identifier callees (got ${decl.callee.type})`);
1928
+ // if (!name) return todo(scope, `only identifier callees (got ${decl.callee.type})`);
1930
1929
 
1931
1930
  let idx = funcIndex[name] ?? importedFuncs[name];
1932
1931
  if (idx === undefined && builtinFuncs[name]) {
@@ -1942,7 +1941,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1942
1941
  return internalConstrs[name].generate(scope, decl, _global, _name);
1943
1942
  }
1944
1943
 
1945
- if (idx === undefined && !decl._new && name.startsWith('__Porffor_wasm_')) {
1944
+ if (idx === undefined && !decl._new && name && name.startsWith('__Porffor_wasm_')) {
1946
1945
  const wasmOps = {
1947
1946
  // pointer, align, offset
1948
1947
  i32_load: { imms: 2, args: [ true ], returns: 1 },
@@ -1989,162 +1988,171 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1989
1988
  }
1990
1989
 
1991
1990
  if (idx === undefined) {
1992
- if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) {
1993
- const [ local, global ] = lookupName(scope, name);
1994
- if (!Prefs.indirectCalls || local == null) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
1991
+ if (!Prefs.indirectCalls) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
1995
1992
 
1996
- // todo: only works when function uses typedParams and typedReturns
1993
+ // todo: only works when function uses typedParams and typedReturns
1997
1994
 
1998
- const indirectMode = Prefs.indirectCallMode ?? 'vararg';
1999
- // options: vararg, strict
2000
- // - strict: simpler, smaller size usage, no func lut needed.
2001
- // ONLY works when arg count of call == arg count of function being called
2002
- // - vararg: large size usage, cursed.
2003
- // works when arg count of call != arg count of function being called*
2004
- // * most of the time, some edgecases
1995
+ const indirectMode = Prefs.indirectCallMode ?? 'vararg';
1996
+ // options: vararg, strict
1997
+ // - strict: simpler, smaller size usage, no func lut needed.
1998
+ // ONLY works when arg count of call == arg count of function being called
1999
+ // - vararg: large size usage, cursed.
2000
+ // works when arg count of call != arg count of function being called*
2001
+ // * most of the time, some edgecases
2005
2002
 
2006
- funcs.table = true;
2007
- scope.table = true;
2003
+ funcs.table = true;
2004
+ scope.table = true;
2008
2005
 
2009
- let args = decl.arguments;
2010
- let locals = [];
2006
+ let args = decl.arguments;
2007
+ let locals = [];
2011
2008
 
2012
- if (indirectMode === 'vararg') {
2013
- const minArgc = Prefs.indirectCallMinArgc ?? 3;
2009
+ if (indirectMode === 'vararg') {
2010
+ const minArgc = Prefs.indirectCallMinArgc ?? 3;
2014
2011
 
2015
- if (args.length < minArgc) {
2016
- args = args.concat(new Array(minArgc - args.length).fill(DEFAULT_VALUE));
2017
- }
2012
+ if (args.length < minArgc) {
2013
+ args = args.concat(new Array(minArgc - args.length).fill(DEFAULT_VALUE));
2018
2014
  }
2015
+ }
2019
2016
 
2020
- for (let i = 0; i < args.length; i++) {
2021
- const arg = args[i];
2022
- out = out.concat(generate(scope, arg));
2017
+ for (let i = 0; i < args.length; i++) {
2018
+ const arg = args[i];
2019
+ out = out.concat(generate(scope, arg));
2023
2020
 
2024
- if (valtypeBinary !== Valtype.i32 && (
2025
- (builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
2026
- (importedFuncs[name] && name.startsWith('profile'))
2027
- )) {
2028
- out.push(Opcodes.i32_to);
2029
- }
2021
+ if (valtypeBinary !== Valtype.i32 && (
2022
+ (builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
2023
+ (importedFuncs[name] && name.startsWith('profile'))
2024
+ )) {
2025
+ out.push(Opcodes.i32_to);
2026
+ }
2030
2027
 
2031
- out = out.concat(getNodeType(scope, arg));
2028
+ out = out.concat(getNodeType(scope, arg));
2032
2029
 
2033
- if (indirectMode === 'vararg') {
2034
- const typeLocal = localTmp(scope, `#indirect_arg${i}_type`, Valtype.i32);
2035
- const valLocal = localTmp(scope, `#indirect_arg${i}_val`);
2030
+ if (indirectMode === 'vararg') {
2031
+ const typeLocal = localTmp(scope, `#indirect_arg${i}_type`, Valtype.i32);
2032
+ const valLocal = localTmp(scope, `#indirect_arg${i}_val`);
2036
2033
 
2037
- locals.push([valLocal, typeLocal]);
2034
+ locals.push([valLocal, typeLocal]);
2038
2035
 
2039
- out.push(
2040
- [ Opcodes.local_set, typeLocal ],
2041
- [ Opcodes.local_set, valLocal ]
2042
- );
2043
- }
2036
+ out.push(
2037
+ [ Opcodes.local_set, typeLocal ],
2038
+ [ Opcodes.local_set, valLocal ]
2039
+ );
2044
2040
  }
2041
+ }
2042
+
2043
+ if (indirectMode === 'strict') {
2044
+ return [
2045
+ ...generate(scope, decl.callee),
2046
+ [ Opcodes.local_set, localTmp(scope, '#indirect_callee') ],
2045
2047
 
2046
- if (indirectMode === 'strict') {
2047
- return typeSwitch(scope, getNodeType(scope, decl.callee), {
2048
+ ...typeSwitch(scope, getNodeType(scope, decl.callee), {
2048
2049
  [TYPES.function]: [
2049
2050
  ...out,
2050
- [ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
2051
+
2052
+ [ Opcodes.local_get, localTmp(scope, '#indirect_callee') ],
2053
+ ...generate(scope, decl.callee),
2051
2054
  Opcodes.i32_to_u,
2052
2055
  [ Opcodes.call_indirect, args.length, 0 ],
2053
2056
  ...setLastType(scope)
2054
2057
  ],
2055
2058
  default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
2056
- });
2059
+ })
2060
+ ];
2061
+ }
2062
+
2063
+ // hi, I will now explain how vararg mode works:
2064
+ // wasm's indirect_call instruction requires you know the func type at compile-time
2065
+ // since we have varargs (variable argument count), we do not know it.
2066
+ // we could just store args in memory and not use wasm func args,
2067
+ // but that is slow (probably) and breaks js exports.
2068
+ // instead, we generate every* possibility of argc and use different indirect_call
2069
+ // ops for each one, with type depending on argc for that branch.
2070
+ // then we load the argc for the wanted function from a memory lut,
2071
+ // and call the branch with the matching argc we require.
2072
+ // sorry, yes it is very cursed (and size inefficient), but indirect calls
2073
+ // are kind of rare anyway (mostly callbacks) so I am not concerned atm.
2074
+ // *for argc 0-3, in future (todo:) the max number should be
2075
+ // dynamically changed to the max argc of any func in the js file.
2076
+
2077
+ const funcLocal = localTmp(scope, '#indirect_func', Valtype.i32);
2078
+ const flags = localTmp(scope, '#indirect_flags', Valtype.i32);
2079
+
2080
+ const gen = argc => {
2081
+ const argsOut = [];
2082
+ for (let i = 0; i < argc; i++) {
2083
+ argsOut.push(
2084
+ [ Opcodes.local_get, locals[i][0] ],
2085
+ [ Opcodes.local_get, locals[i][1] ]
2086
+ );
2057
2087
  }
2058
2088
 
2059
- // hi, I will now explain how vararg mode works:
2060
- // wasm's indirect_call instruction requires you know the func type at compile-time
2061
- // since we have varargs (variable argument count), we do not know it.
2062
- // we could just store args in memory and not use wasm func args,
2063
- // but that is slow (probably) and breaks js exports.
2064
- // instead, we generate every* possibility of argc and use different indirect_call
2065
- // ops for each one, with type depending on argc for that branch.
2066
- // then we load the argc for the wanted function from a memory lut,
2067
- // and call the branch with the matching argc we require.
2068
- // sorry, yes it is very cursed (and size inefficient), but indirect calls
2069
- // are kind of rare anyway (mostly callbacks) so I am not concerned atm.
2070
- // *for argc 0-3, in future (todo:) the max number should be
2071
- // dynamically changed to the max argc of any func in the js file.
2072
-
2073
- const funcLocal = localTmp(scope, '#indirect_func', Valtype.i32);
2074
- const flags = localTmp(scope, '#indirect_flags', Valtype.i32);
2075
-
2076
- const gen = argc => {
2077
- const argsOut = [];
2078
- for (let i = 0; i < argc; i++) {
2079
- argsOut.push(
2080
- [ Opcodes.local_get, locals[i][0] ],
2081
- [ Opcodes.local_get, locals[i][1] ]
2082
- );
2083
- }
2089
+ const checkFlag = (flag, pass, fail) => [
2090
+ [ Opcodes.local_get, flags ],
2091
+ ...number(flag, Valtype.i32),
2092
+ [ Opcodes.i32_and ],
2093
+ [ Opcodes.if, valtypeBinary ],
2094
+ ...pass,
2095
+ [ Opcodes.else ],
2096
+ ...fail,
2097
+ [ Opcodes.end ]
2098
+ ];
2084
2099
 
2085
- const checkFlag = (flag, pass, fail) => [
2086
- [ Opcodes.local_get, flags ],
2087
- ...number(flag, Valtype.i32),
2088
- [ Opcodes.i32_and ],
2089
- [ Opcodes.if, valtypeBinary ],
2090
- ...pass,
2091
- [ Opcodes.else ],
2092
- ...fail,
2093
- [ Opcodes.end ]
2094
- ];
2100
+ // pain.
2101
+ // return checkFlag(0b10, [ // constr
2102
+ // [ Opcodes.i32_const, decl._new ? 1 : 0 ],
2103
+ // ...argsOut,
2104
+ // [ Opcodes.local_get, funcLocal ],
2105
+ // [ Opcodes.call_indirect, argc, 0, 'constr' ],
2106
+ // ...setLastType(scope),
2107
+ // ], [
2108
+ // ...argsOut,
2109
+ // [ Opcodes.local_get, funcLocal ],
2110
+ // [ Opcodes.call_indirect, argc, 0 ],
2111
+ // ...setLastType(scope),
2112
+ // ]);
2113
+
2114
+ return checkFlag(0b1, // no type return
2115
+ checkFlag(0b10, [ // no type return & constr
2116
+ [ Opcodes.i32_const, decl._new ? 1 : 0 ],
2117
+ ...argsOut,
2118
+ [ Opcodes.local_get, funcLocal ],
2119
+ [ Opcodes.call_indirect, argc, 0, 'no_type_return', 'constr' ],
2120
+ ], [
2121
+ ...argsOut,
2122
+ [ Opcodes.local_get, funcLocal ],
2123
+ [ Opcodes.call_indirect, argc, 0, 'no_type_return' ]
2124
+ ]),
2125
+ checkFlag(0b10, [ // type return & constr
2126
+ [ Opcodes.i32_const, decl._new ? 1 : 0 ],
2127
+ ...argsOut,
2128
+ [ Opcodes.local_get, funcLocal ],
2129
+ [ Opcodes.call_indirect, argc, 0, 'constr' ],
2130
+ ...setLastType(scope),
2131
+ ], [
2132
+ ...argsOut,
2133
+ [ Opcodes.local_get, funcLocal ],
2134
+ [ Opcodes.call_indirect, argc, 0 ],
2135
+ ...setLastType(scope),
2136
+ ]),
2137
+ );
2138
+ };
2095
2139
 
2096
- // pain.
2097
- // return checkFlag(0b10, [ // constr
2098
- // [ Opcodes.i32_const, decl._new ? 1 : 0 ],
2099
- // ...argsOut,
2100
- // [ Opcodes.local_get, funcLocal ],
2101
- // [ Opcodes.call_indirect, argc, 0, 'constr' ],
2102
- // ...setLastType(scope),
2103
- // ], [
2104
- // ...argsOut,
2105
- // [ Opcodes.local_get, funcLocal ],
2106
- // [ Opcodes.call_indirect, argc, 0 ],
2107
- // ...setLastType(scope),
2108
- // ]);
2109
-
2110
- return checkFlag(0b1, // no type return
2111
- checkFlag(0b10, [ // no type return & constr
2112
- [ Opcodes.i32_const, decl._new ? 1 : 0 ],
2113
- ...argsOut,
2114
- [ Opcodes.local_get, funcLocal ],
2115
- [ Opcodes.call_indirect, argc, 0, 'no_type_return', 'constr' ],
2116
- ], [
2117
- ...argsOut,
2118
- [ Opcodes.local_get, funcLocal ],
2119
- [ Opcodes.call_indirect, argc, 0, 'no_type_return' ]
2120
- ]),
2121
- checkFlag(0b10, [ // type return & constr
2122
- [ Opcodes.i32_const, decl._new ? 1 : 0 ],
2123
- ...argsOut,
2124
- [ Opcodes.local_get, funcLocal ],
2125
- [ Opcodes.call_indirect, argc, 0, 'constr' ],
2126
- ...setLastType(scope),
2127
- ], [
2128
- ...argsOut,
2129
- [ Opcodes.local_get, funcLocal ],
2130
- [ Opcodes.call_indirect, argc, 0 ],
2131
- ...setLastType(scope),
2132
- ]),
2133
- );
2134
- };
2140
+ const tableBc = {};
2141
+ for (let i = 0; i <= args.length; i++) {
2142
+ tableBc[i] = gen(i);
2143
+ }
2135
2144
 
2136
- const tableBc = {};
2137
- for (let i = 0; i <= args.length; i++) {
2138
- tableBc[i] = gen(i);
2139
- }
2145
+ // todo/perf: check if we should use br_table here or just generate our own big if..elses
2140
2146
 
2141
- // todo/perf: check if we should use br_table here or just generate our own big if..elses
2147
+ return [
2148
+ ...generate(scope, decl.callee),
2149
+ [ Opcodes.local_set, localTmp(scope, '#indirect_callee') ],
2142
2150
 
2143
- return typeSwitch(scope, getNodeType(scope, decl.callee), {
2151
+ ...typeSwitch(scope, getNodeType(scope, decl.callee), {
2144
2152
  [TYPES.function]: [
2145
2153
  ...out,
2146
2154
 
2147
- [ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
2155
+ [ Opcodes.local_get, localTmp(scope, '#indirect_callee') ],
2148
2156
  Opcodes.i32_to_u,
2149
2157
  [ Opcodes.local_set, funcLocal ],
2150
2158
 
@@ -2177,10 +2185,8 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2177
2185
  ], tableBc, valtypeBinary)
2178
2186
  ],
2179
2187
  default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
2180
- });
2181
- }
2182
-
2183
- return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
2188
+ })
2189
+ ];
2184
2190
  }
2185
2191
 
2186
2192
  const func = funcs[idx - importedFuncs.length]; // idx === scope.index ? scope : funcs.find(x => x.index === idx);
@@ -2296,6 +2302,8 @@ const codeToSanitizedStr = code => {
2296
2302
  const sanitize = str => str.replace(/[^0-9a-zA-Z_]/g, _ => codeToSanitizedStr(_.charCodeAt(0)));
2297
2303
 
2298
2304
  const unhackName = name => {
2305
+ if (!name) return name;
2306
+
2299
2307
  if (name.startsWith('__')) return name.slice(2).replaceAll('_', '.');
2300
2308
  return name;
2301
2309
  };
@@ -2702,22 +2710,22 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2702
2710
  const { type, name } = decl.left;
2703
2711
  const [ local, isGlobal ] = lookupName(scope, name);
2704
2712
 
2705
- if (isFuncType(decl.right.type)) {
2706
- // hack for a = function () { ... }
2707
- decl.right.id = { name };
2713
+ // if (isFuncType(decl.right.type)) {
2714
+ // // hack for a = function () { ... }
2715
+ // decl.right.id = { name };
2708
2716
 
2709
- const func = generateFunc(scope, decl.right);
2717
+ // const func = generateFunc(scope, decl.right);
2710
2718
 
2711
- return [
2712
- ...number(func.index - importedFuncs.length),
2713
- ...(local != null ? [
2714
- [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
2715
- [ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
2719
+ // return [
2720
+ // ...number(func.index - importedFuncs.length),
2721
+ // ...(local != null ? [
2722
+ // [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
2723
+ // [ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
2716
2724
 
2717
- ...setType(scope, name, TYPES.function)
2718
- ] : [])
2719
- ];
2720
- }
2725
+ // ...setType(scope, name, TYPES.function)
2726
+ // ] : [])
2727
+ // ];
2728
+ // }
2721
2729
 
2722
2730
  const op = decl.operator.slice(0, -1) || '=';
2723
2731
 
@@ -2746,18 +2754,27 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2746
2754
  }
2747
2755
 
2748
2756
  // arr[i]
2749
- if (decl.left.type === 'MemberExpression' && decl.left.computed) {
2757
+ if (decl.left.type === 'MemberExpression') {
2750
2758
  const newValueTmp = localTmp(scope, '#member_setter_val_tmp');
2751
2759
  const pointerTmp = localTmp(scope, '#member_setter_ptr_tmp', Valtype.i32);
2752
2760
 
2761
+ const object = decl.left.object;
2762
+ const property = decl.left.computed ? decl.left.property : {
2763
+ type: 'Literal',
2764
+ value: decl.left.property.name
2765
+ };
2766
+
2767
+ includeBuiltin(scope, '__Map_prototype_get');
2768
+ includeBuiltin(scope, '__Map_prototype_set');
2769
+
2753
2770
  return [
2754
2771
  ...typeSwitch(scope, getNodeType(scope, decl.left.object), {
2755
2772
  [TYPES.array]: [
2756
- ...generate(scope, decl.left.object),
2773
+ ...generate(scope, object),
2757
2774
  Opcodes.i32_to_u,
2758
2775
 
2759
2776
  // get index as valtype
2760
- ...generate(scope, decl.left.property),
2777
+ ...generate(scope, property),
2761
2778
  Opcodes.i32_to_u,
2762
2779
 
2763
2780
  // turn into byte offset by * valtypeSize + 1
@@ -2781,6 +2798,34 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2781
2798
  [ Opcodes.i32_store8, 0, ValtypeSize.i32 + ValtypeSize[valtype] ],
2782
2799
  ],
2783
2800
 
2801
+ [TYPES.object]: [
2802
+ ...generate(scope, object),
2803
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, localTmp(scope, '#objset_object') ] ]),
2804
+ ...getNodeType(scope, object),
2805
+
2806
+ ...generate(scope, property),
2807
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, localTmp(scope, '#objset_property') ] ]),
2808
+ ...getNodeType(scope, property),
2809
+
2810
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2811
+ [ Opcodes.local_get, localTmp(scope, '#objset_object') ],
2812
+ ...getNodeType(scope, object),
2813
+
2814
+ [ Opcodes.local_get, localTmp(scope, '#objset_property') ],
2815
+ ...getNodeType(scope, property),
2816
+
2817
+ [ Opcodes.call, funcIndex.__Map_prototype_get ],
2818
+ ...setLastType(scope)
2819
+ ], generate(scope, decl.right), getLastType(scope), getNodeType(scope, decl.right), false, name, true)),
2820
+ [ Opcodes.local_tee, newValueTmp ],
2821
+ ...getNodeType(scope, decl),
2822
+
2823
+ [ Opcodes.call, funcIndex.__Map_prototype_set ],
2824
+ [ Opcodes.drop ],
2825
+
2826
+ ...setLastType(scope, getNodeType(scope, decl)),
2827
+ ],
2828
+
2784
2829
  ...wrapBC({
2785
2830
  [TYPES.uint8array]: [
2786
2831
  [ Opcodes.i32_add ],
@@ -2924,11 +2969,11 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2924
2969
  ],
2925
2970
  }, {
2926
2971
  prelude: [
2927
- ...generate(scope, decl.left.object),
2972
+ ...generate(scope, object),
2928
2973
  Opcodes.i32_to_u,
2929
2974
  [ Opcodes.i32_load, 0, 4 ],
2930
2975
 
2931
- ...generate(scope, decl.left.property),
2976
+ ...generate(scope, property),
2932
2977
  Opcodes.i32_to_u,
2933
2978
  ],
2934
2979
  postlude: setLastType(scope, TYPES.number)
@@ -2957,7 +3002,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2957
3002
  // set global and return (eg a = 2)
2958
3003
  return [
2959
3004
  ...generateVar(scope, { kind: 'var', _bare: true, declarations: [ { id: { name }, init: decl.right } ] }),
2960
- [ Opcodes.global_get, globals[name].idx ]
3005
+ ...generate(scope, decl.left)
2961
3006
  ];
2962
3007
  }
2963
3008
 
@@ -4193,12 +4238,50 @@ const generateArray = (scope, decl, global = false, name = '$undeclared', initEm
4193
4238
  };
4194
4239
 
4195
4240
  const generateObject = (scope, decl, global = false, name = '$undeclared') => {
4196
- if (decl.properties.length > 0) return todo(scope, 'objects are not supported yet', true);
4241
+ includeBuiltin(scope, 'Map');
4242
+ includeBuiltin(scope, '__Map_prototype_set');
4197
4243
 
4198
- return [
4199
- ...number(1),
4200
- ...setLastType(scope, TYPES.object)
4244
+ // todo: optimize const objects
4245
+ const tmp = localTmp(scope, `#objectexpr${randId()}`);
4246
+ const out = [
4247
+ ...generateCall(scope, {
4248
+ callee: {
4249
+ type: 'Identifier',
4250
+ name: 'Map'
4251
+ },
4252
+ arguments: [],
4253
+ _new: true
4254
+ }),
4255
+ [ Opcodes.local_tee, tmp ]
4201
4256
  ];
4257
+
4258
+ for (const x of decl.properties) {
4259
+ const { method, shorthand, computed, kind, key, value } = x;
4260
+ if (kind !== 'init') return todo(scope, 'complex objects are not supported yet', true);
4261
+
4262
+ const k = computed ? key : {
4263
+ type: 'Literal',
4264
+ value: key.name
4265
+ };
4266
+
4267
+ out.push(
4268
+ [ Opcodes.local_get, tmp ],
4269
+ ...number(TYPES.map, Valtype.i32),
4270
+
4271
+ ...generate(scope, k),
4272
+ ...getNodeType(scope, k),
4273
+
4274
+ ...generate(scope, value),
4275
+ ...getNodeType(scope, value),
4276
+
4277
+ [ Opcodes.call, funcIndex.__Map_prototype_set ],
4278
+
4279
+ [ Opcodes.drop ],
4280
+ [ Opcodes.drop ]
4281
+ );
4282
+ }
4283
+
4284
+ return out;
4202
4285
  };
4203
4286
 
4204
4287
  const withType = (scope, wasm, type) => [
@@ -4323,14 +4406,19 @@ const generateMember = (scope, decl, _global, _name) => {
4323
4406
  }, valtypeBinary);
4324
4407
  }
4325
4408
 
4326
- const object = generate(scope, decl.object);
4327
- const property = generate(scope, decl.property);
4409
+ const object = decl.object;
4410
+ const objectWasm = generate(scope, object);
4411
+ const property = decl.computed ? decl.property : {
4412
+ type: 'Literal',
4413
+ value: decl.property.name
4414
+ };
4415
+ const propertyWasm = generate(scope, property);
4328
4416
 
4329
4417
  // // todo: we should only do this for strings but we don't know at compile-time :(
4330
4418
  // hack: this is naughty and will break things!
4331
4419
  let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
4332
4420
 
4333
- const known = knownType(scope, getNodeType(scope, decl.object));
4421
+ const known = knownType(scope, getNodeType(scope, object));
4334
4422
  if ((known === TYPES.string || known === TYPES.bytestring) || (pages.hasAnyString && known == null)) {
4335
4423
  // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
4336
4424
  0, [ newOut, newPointer ] = makeArray(scope, {
@@ -4338,11 +4426,14 @@ const generateMember = (scope, decl, _global, _name) => {
4338
4426
  }, _global, _name, true, 'i16', true);
4339
4427
  }
4340
4428
 
4341
- return typeSwitch(scope, getNodeType(scope, decl.object), {
4429
+ includeBuiltin(scope, '__Map_prototype_get');
4430
+
4431
+ return typeSwitch(scope, getNodeType(scope, object), {
4342
4432
  [TYPES.array]: [
4343
- ...loadArray(scope, object, property),
4433
+ ...loadArray(scope, objectWasm, propertyWasm),
4344
4434
  ...setLastType(scope)
4345
4435
  ],
4436
+
4346
4437
  [TYPES.string]: [
4347
4438
  // setup new/out array
4348
4439
  ...newOut,
@@ -4354,13 +4445,13 @@ const generateMember = (scope, decl, _global, _name) => {
4354
4445
  // use as pointer for store later
4355
4446
  ...newPointer,
4356
4447
 
4357
- ...property,
4448
+ ...propertyWasm,
4358
4449
  Opcodes.i32_to_u,
4359
4450
 
4360
4451
  ...number(ValtypeSize.i16, Valtype.i32),
4361
4452
  [ Opcodes.i32_mul ],
4362
4453
 
4363
- ...object,
4454
+ ...objectWasm,
4364
4455
  Opcodes.i32_to_u,
4365
4456
  [ Opcodes.i32_add ],
4366
4457
 
@@ -4375,6 +4466,7 @@ const generateMember = (scope, decl, _global, _name) => {
4375
4466
  Opcodes.i32_from_u,
4376
4467
  ...setLastType(scope, TYPES.string)
4377
4468
  ],
4469
+
4378
4470
  [TYPES.bytestring]: [
4379
4471
  // setup new/out array
4380
4472
  ...newOut,
@@ -4386,10 +4478,10 @@ const generateMember = (scope, decl, _global, _name) => {
4386
4478
  // use as pointer for store later
4387
4479
  ...newPointer,
4388
4480
 
4389
- ...property,
4481
+ ...propertyWasm,
4390
4482
  Opcodes.i32_to_u,
4391
4483
 
4392
- ...object,
4484
+ ...objectWasm,
4393
4485
  Opcodes.i32_to_u,
4394
4486
  [ Opcodes.i32_add ],
4395
4487
 
@@ -4405,6 +4497,17 @@ const generateMember = (scope, decl, _global, _name) => {
4405
4497
  ...setLastType(scope, TYPES.bytestring)
4406
4498
  ],
4407
4499
 
4500
+ [TYPES.object]: [
4501
+ ...objectWasm,
4502
+ ...getNodeType(scope, object),
4503
+
4504
+ ...propertyWasm,
4505
+ ...getNodeType(scope, property),
4506
+
4507
+ [ Opcodes.call, funcIndex.__Map_prototype_get ],
4508
+ ...setLastType(scope)
4509
+ ],
4510
+
4408
4511
  ...wrapBC({
4409
4512
  [TYPES.uint8array]: [
4410
4513
  [ Opcodes.i32_add ],
@@ -4473,22 +4576,23 @@ const generateMember = (scope, decl, _global, _name) => {
4473
4576
  ],
4474
4577
  }, {
4475
4578
  prelude: [
4476
- ...object,
4579
+ ...objectWasm,
4477
4580
  Opcodes.i32_to_u,
4478
4581
  [ Opcodes.i32_load, 0, 4 ],
4479
4582
 
4480
- ...property,
4583
+ ...propertyWasm,
4481
4584
  Opcodes.i32_to_u
4482
4585
  ],
4483
4586
  postlude: setLastType(scope, TYPES.number)
4484
4587
  }),
4485
4588
 
4486
- default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
4589
+ default: internalThrow(scope, 'TypeError', 'Unsupported member expression object', true)
4487
4590
  });
4488
4591
  };
4489
4592
 
4490
4593
  const randId = () => Math.random().toString(16).slice(1, -2).padEnd(12, '0');
4491
4594
 
4595
+ let objectHackers = [];
4492
4596
  const objectHack = node => {
4493
4597
  if (!node) return node;
4494
4598
 
@@ -4503,16 +4607,10 @@ const objectHack = node => {
4503
4607
  if (objectName && ['undefined', 'null', 'NaN', 'Infinity'].includes(objectName)) return;
4504
4608
 
4505
4609
  if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
4506
-
4507
- // if .name or .length, give up (hack within a hack!)
4508
- if (['name', 'length', 'size', 'description', 'byteLength', 'byteOffset', 'buffer', 'detached', 'resizable', 'growable', 'maxByteLength'].includes(node.property.name)) {
4509
- node.object = objectHack(node.object);
4610
+ if (!objectName || (!objectHackers.includes(objectName) && !objectHackers.some(x => objectName.startsWith(`${x}_`)))) {
4510
4611
  return;
4511
4612
  }
4512
4613
 
4513
- // no object name, give up
4514
- if (!objectName) return;
4515
-
4516
4614
  const name = '__' + objectName + '_' + node.property.name;
4517
4615
  if (Prefs.codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
4518
4616
 
@@ -4861,6 +4959,9 @@ export default program => {
4861
4959
  prototypeFuncs = new PrototypeFuncs();
4862
4960
  allocator = makeAllocator(Prefs.allocator ?? 'static');
4863
4961
 
4962
+ const getObjectName = x => x.startsWith('__') && x.slice(2, x.indexOf('_', 2));
4963
+ objectHackers = ['assert', 'compareArray', 'Test262Error', ...new Set(Object.keys(builtinFuncs).map(getObjectName).concat(Object.keys(builtinVars).map(getObjectName)).filter(x => x))];
4964
+
4864
4965
  program.id = { name: 'main' };
4865
4966
 
4866
4967
  const scope = {
@@ -4004,22 +4004,22 @@ export const BuiltinFuncs = function() {
4004
4004
  locals: [124,127], localNames: ["_this","_this#type","key","key#type","keys","#last_type"],
4005
4005
  };
4006
4006
  this.__Map_prototype_get = {
4007
- wasm: (scope, {builtin}) => [[32,0],[252,2],[40,0,0],[183],[33,4],[32,0],[252,2],[40,0,4],[183],[33,5],[32,4],[65,19],[32,2],[32,3],[16, builtin('__Porffor_set_indexOf')],[33,7],[34,6],[68,0,0,0,0,0,0,240,191],[97],[4,64],[68,0,0,0,0,0,0,0,0],[65,128,1],[15],[11],[32,6],[252,3],[65,9],[108],[32,5],[252,3],[106],[34,8],[43,0,4],[32,8],[45,0,12],[34,7],[15]],
4007
+ wasm: (scope, {builtin}) => [[32,0],[252,2],[40,0,0],[183],[33,4],[32,0],[252,2],[40,0,4],[183],[33,5],[32,4],[252,2],[40,0,0],[183],[33,6],[68,0,0,0,0,0,0,0,0],[33,7],[3,64],[32,7],[32,6],[99],[4,64],[2,64],[2,127,"string_only"],[32,4],[65,19],[32,7],[65,1],[16, builtin('__Porffor_set_read')],[33,8],[34,9,"string_only"],[32,2],[34,10,"string_only"],[32,8,"string_only|start"],[65,195,1],[70],[32,3],[65,195,1],[70],[113],[4,64],[32,9],[252,3],[34,11],[32,10],[252,3],[34,13],[71],[4,127],[32,11],[40,0,0],[34,12],[32,13],[40,0,0],[71],[4,64],[65,0],[12,1],[11],[65,0],[33,14],[32,12],[33,15],[3,64],[32,14],[32,11],[106],[45,0,4],[32,14],[32,13],[106],[45,0,4],[71],[4,64],[65,0],[12,2],[11],[32,14],[65,1],[106],[34,14],[32,15],[72],[13,0],[11],[65,1],[5],[65,1],[11],[12,1],[11],[32,8],[65,195,0],[70],[32,8],[65,195,1],[70],[114],[32,3],[65,195,0],[70],[32,3],[65,195,1],[70],[114],[114],[4,64],[32,9],[252,3],[34,11],[32,10],[252,3],[34,13],[71],[4,127],[32,11],[40,0,0],[34,12],[32,13],[40,0,0],[32,8],[65,195,1],[70],[4,64],[32,11],[32,12],[16, builtin('__Porffor_bytestringToString')],[33,11],[11],[32,3],[65,195,1],[70],[4,64],[32,13],[32,13],[40,0,0],[16, builtin('__Porffor_bytestringToString')],[33,13],[11],[71],[4,64],[65,0],[12,1],[11],[65,0],[33,14],[32,12],[65,2],[108],[33,15],[3,64],[32,14],[32,11],[106],[47,0,4],[32,14],[32,13],[106],[47,0,4],[71],[4,64],[65,0],[12,2],[11],[32,14],[65,2],[106],[34,14],[32,15],[72],[13,0],[11],[65,1],[5],[65,1],[11],[12,1],[11,"string_only|end"],[97],[11,"string_only"],[32,8],[65,128,1],[114],[32,3],[65,128,1],[114],[70],[113],[4,64],[32,7],[252,3],[65,9],[108],[32,5],[252,3],[106],[34,16],[43,0,4],[32,16],[45,0,12],[34,8],[15],[11],[11],[32,7],[68,0,0,0,0,0,0,240,63],[160],[33,7],[12,1],[11],[11],[68,0,0,0,0,0,0,0,0],[65,128,1],[15]],
4008
4008
  params: [124,127,124,127], typedParams: 1,
4009
4009
  returns: [124,127], typedReturns: 1,
4010
- locals: [124,124,124,127,127], localNames: ["_this","_this#type","key","key#type","keys","vals","keyIdx","#last_type","#loadArray_offset"],
4010
+ locals: [124,124,124,124,127,124,124,127,127,127,127,127,127], localNames: ["_this","_this#type","key","key#type","keys","vals","size","i","#last_type","__tmpop_left","__tmpop_right","compare_left_pointer","compare_left_length","compare_right_pointer","compare_index","compare_index_end","#loadArray_offset"],
4011
4011
  };
4012
4012
  this.__Map_prototype_set = {
4013
- wasm: (scope, {builtin}) => [[32,0],[252,2],[40,0,0],[183],[33,6],[32,0],[252,2],[40,0,4],[183],[33,7],[32,6],[252,2],[40,0,0],[183],[33,8],[32,6],[65,19],[32,2],[32,3],[16, builtin('__Porffor_set_indexOf')],[33,10],[34,9],[68,0,0,0,0,0,0,240,191],[97],[4,64],[32,8],[33,9],[32,6],[65,19],[32,2],[32,3],[16, builtin('__Set_prototype_add')],[33,10],[26],[11],[32,7],[252,3],[32,9],[252,3],[65,9],[108],[106],[34,12],[32,4],[34,11],[57,0,4],[32,12],[32,5],[58,0,12],[32,0],[65,20],[15]],
4013
+ wasm: (scope, {builtin}) => [[32,0],[252,2],[40,0,0],[183],[33,6],[32,0],[252,2],[40,0,4],[183],[33,7],[32,6],[252,2],[40,0,0],[183],[33,8],[68,0,0,0,0,0,0,0,0],[33,9],[3,64],[32,9],[32,8],[99],[4,64],[2,64],[2,127,"string_only"],[32,6],[65,19],[32,9],[65,1],[16, builtin('__Porffor_set_read')],[33,10],[34,11,"string_only"],[32,2],[34,12,"string_only"],[32,10,"string_only|start"],[65,195,1],[70],[32,3],[65,195,1],[70],[113],[4,64],[32,11],[252,3],[34,13],[32,12],[252,3],[34,15],[71],[4,127],[32,13],[40,0,0],[34,14],[32,15],[40,0,0],[71],[4,64],[65,0],[12,1],[11],[65,0],[33,16],[32,14],[33,17],[3,64],[32,16],[32,13],[106],[45,0,4],[32,16],[32,15],[106],[45,0,4],[71],[4,64],[65,0],[12,2],[11],[32,16],[65,1],[106],[34,16],[32,17],[72],[13,0],[11],[65,1],[5],[65,1],[11],[12,1],[11],[32,10],[65,195,0],[70],[32,10],[65,195,1],[70],[114],[32,3],[65,195,0],[70],[32,3],[65,195,1],[70],[114],[114],[4,64],[32,11],[252,3],[34,13],[32,12],[252,3],[34,15],[71],[4,127],[32,13],[40,0,0],[34,14],[32,15],[40,0,0],[32,10],[65,195,1],[70],[4,64],[32,13],[32,14],[16, builtin('__Porffor_bytestringToString')],[33,13],[11],[32,3],[65,195,1],[70],[4,64],[32,15],[32,15],[40,0,0],[16, builtin('__Porffor_bytestringToString')],[33,15],[11],[71],[4,64],[65,0],[12,1],[11],[65,0],[33,16],[32,14],[65,2],[108],[33,17],[3,64],[32,16],[32,13],[106],[47,0,4],[32,16],[32,15],[106],[47,0,4],[71],[4,64],[65,0],[12,2],[11],[32,16],[65,2],[106],[34,16],[32,17],[72],[13,0],[11],[65,1],[5],[65,1],[11],[12,1],[11,"string_only|end"],[97],[11,"string_only"],[32,10],[65,128,1],[114],[32,3],[65,128,1],[114],[70],[113],[4,64],[32,7],[252,3],[32,9],[252,3],[65,9],[108],[106],[34,19],[32,4],[34,18],[57,0,4],[32,19],[32,5],[58,0,12],[32,0],[65,20],[15],[11],[11],[32,9],[68,0,0,0,0,0,0,240,63],[160],[33,9],[12,1],[11],[11],[32,6],[252,2],[32,8],[68,0,0,0,0,0,0,240,63],[160],[252,2],[54,0,0],[32,6],[65,19],[32,8],[65,1],[32,2],[32,3],[16, builtin('__Porffor_set_write')],[33,10],[26],[32,7],[252,3],[32,8],[252,3],[65,9],[108],[106],[34,19],[32,4],[34,18],[57,0,4],[32,19],[32,5],[58,0,12],[32,0],[65,20],[15]],
4014
4014
  params: [124,127,124,127,124,127], typedParams: 1,
4015
4015
  returns: [124,127], typedReturns: 1,
4016
- locals: [124,124,124,124,127,124,127], localNames: ["_this","_this#type","key","key#type","value","value#type","keys","vals","size","keyIdx","#last_type","#member_setter_val_tmp","#member_setter_ptr_tmp"],
4016
+ locals: [124,124,124,124,127,124,124,127,127,127,127,127,124,127], localNames: ["_this","_this#type","key","key#type","value","value#type","keys","vals","size","i","#last_type","__tmpop_left","__tmpop_right","compare_left_pointer","compare_left_length","compare_right_pointer","compare_index","compare_index_end","#member_setter_val_tmp","#member_setter_ptr_tmp"],
4017
4017
  };
4018
4018
  this.__Map_prototype_delete = {
4019
- wasm: (scope, {builtin}) => [[32,0],[252,2],[40,0,0],[183],[34,4],[65,19],[32,2],[32,3],[16, builtin('__Porffor_set_indexOf')],[33,7],[33,5],[32,7],[33,6],[32,5],[68,0,0,0,0,0,0,240,191],[97],[4,64],[68,0,0,0,0,0,0,0,0],[65,2],[15],[11],[32,4],[65,19],[32,2],[32,3],[16, builtin('__Set_prototype_delete')],[33,7],[26],[32,0],[252,2],[40,0,4],[183],[34,8],[65,208,0],[32,5],[32,6],[68,0,0,0,0,0,0,240,63],[65,1],[65,4],[65,0],[54,1,0],[68,0,0,0,0,0,0,16,64],[65,208,0],[16, builtin('__Array_prototype_splice')],[33,7],[26],[68,0,0,0,0,0,0,240,63],[65,2],[15]],
4019
+ wasm: (scope, {builtin}) => [[32,0],[252,2],[40,0,0],[183],[33,4],[32,0],[252,2],[40,0,4],[183],[33,5],[32,4],[252,2],[40,0,0],[183],[33,6],[68,0,0,0,0,0,0,0,0],[33,7],[3,64],[32,7],[32,6],[99],[4,64],[2,64],[2,127,"string_only"],[32,4],[65,19],[32,7],[65,1],[16, builtin('__Porffor_set_read')],[33,8],[34,9,"string_only"],[32,2],[34,10,"string_only"],[32,8,"string_only|start"],[65,195,1],[70],[32,3],[65,195,1],[70],[113],[4,64],[32,9],[252,3],[34,11],[32,10],[252,3],[34,13],[71],[4,127],[32,11],[40,0,0],[34,12],[32,13],[40,0,0],[71],[4,64],[65,0],[12,1],[11],[65,0],[33,14],[32,12],[33,15],[3,64],[32,14],[32,11],[106],[45,0,4],[32,14],[32,13],[106],[45,0,4],[71],[4,64],[65,0],[12,2],[11],[32,14],[65,1],[106],[34,14],[32,15],[72],[13,0],[11],[65,1],[5],[65,1],[11],[12,1],[11],[32,8],[65,195,0],[70],[32,8],[65,195,1],[70],[114],[32,3],[65,195,0],[70],[32,3],[65,195,1],[70],[114],[114],[4,64],[32,9],[252,3],[34,11],[32,10],[252,3],[34,13],[71],[4,127],[32,11],[40,0,0],[34,12],[32,13],[40,0,0],[32,8],[65,195,1],[70],[4,64],[32,11],[32,12],[16, builtin('__Porffor_bytestringToString')],[33,11],[11],[32,3],[65,195,1],[70],[4,64],[32,13],[32,13],[40,0,0],[16, builtin('__Porffor_bytestringToString')],[33,13],[11],[71],[4,64],[65,0],[12,1],[11],[65,0],[33,14],[32,12],[65,2],[108],[33,15],[3,64],[32,14],[32,11],[106],[47,0,4],[32,14],[32,13],[106],[47,0,4],[71],[4,64],[65,0],[12,2],[11],[32,14],[65,2],[106],[34,14],[32,15],[72],[13,0],[11],[65,1],[5],[65,1],[11],[12,1],[11,"string_only|end"],[97],[11,"string_only"],[32,8],[65,128,1],[114],[32,3],[65,128,1],[114],[70],[113],[4,64],[32,4],[65,19],[32,2],[32,3],[16, builtin('__Set_prototype_delete')],[33,8],[26],[32,5],[65,208,0],[32,7],[65,1],[68,0,0,0,0,0,0,240,63],[65,1],[65,4],[65,0],[54,1,0],[68,0,0,0,0,0,0,16,64],[65,208,0],[16, builtin('__Array_prototype_splice')],[33,8],[26],[68,0,0,0,0,0,0,240,63],[65,2],[15],[11],[11],[32,7],[68,0,0,0,0,0,0,240,63],[160],[33,7],[12,1],[11],[11],[68,0,0,0,0,0,0,0,0],[65,2],[15]],
4020
4020
  params: [124,127,124,127], typedParams: 1,
4021
4021
  returns: [124,127], typedReturns: 1,
4022
- locals: [124,124,127,127,124], localNames: ["_this","_this#type","key","key#type","keys","keyIdx","keyIdx#type","#last_type","vals"],
4022
+ locals: [124,124,124,124,127,124,124,127,127,127,127,127], localNames: ["_this","_this#type","key","key#type","keys","vals","size","i","#last_type","__tmpop_left","__tmpop_right","compare_left_pointer","compare_left_length","compare_right_pointer","compare_index","compare_index_end"],
4023
4023
  };
4024
4024
  this.__Map_prototype_clear = {
4025
4025
  wasm: (scope, {builtin}) => [[32,0],[252,2],[40,0,0],[183],[34,2],[65,19],[16, builtin('__Set_prototype_clear')],[26],[26],[32,0],[252,2],[40,0,4],[183],[34,4],[252,3],[68,0,0,0,0,0,0,0,0],[34,5],[252,3],[54,1,0],[68,0,0,0,0,0,0,0,0],[65,128,1],[15]],
package/compiler/wrap.js CHANGED
@@ -10,16 +10,26 @@ const fs = (typeof process?.version !== 'undefined' ? (await import('node:fs'))
10
10
 
11
11
  const bold = x => `\u001b[1m${x}\u001b[0m`;
12
12
 
13
+ const checkOOB = (memory, ptr) => ptr >= memory.buffer.byteLength;
14
+
15
+ let dv;
16
+ const read = (ta, memory, ptr, length) => {
17
+ if (ta === Uint8Array) return new Uint8Array(memory.buffer, ptr, length);
18
+ return new ta(memory.buffer.slice(ptr, ptr + length * ta.BYTES_PER_ELEMENT), 0, length);
19
+ };
20
+
13
21
  export const readByteStr = (memory, ptr) => {
14
- const length = (new Uint32Array(memory.buffer, ptr, 1))[0];
15
- return Array.from(new Uint8Array(memory.buffer, ptr + 4, length)).map(x => String.fromCharCode(x)).join('');
22
+ const length = read(Uint32Array, memory, ptr, 1)[0];
23
+ return Array.from(read(Uint8Array, memory, ptr + 4, length)).map(x => String.fromCharCode(x)).join('');
16
24
  };
17
25
 
18
26
  export const writeByteStr = (memory, ptr, str) => {
19
27
  const length = str.length;
20
- (new Uint32Array(memory.buffer, ptr, 1))[0] = length;
21
28
 
22
- const arr = new Uint8Array(memory.buffer, ptr + 4, length);
29
+ if (dv.memory !== memory) dv = new DataView(memory.buffer);
30
+ dv.setUint32(ptr, length, true);
31
+
32
+ const arr = read(Uint8Array, memory, ptr + 4, length);
23
33
  for (let i = 0; i < length; i++) {
24
34
  arr[i] = str.charCodeAt(i);
25
35
  }
@@ -32,7 +42,31 @@ const porfToJSValue = ({ memory, funcs, pages }, value, type) => {
32
42
  return undefined;
33
43
 
34
44
  case TYPES.boolean: return Boolean(value);
35
- case TYPES.object: return value === 0 ? null : {};
45
+ case TYPES.object: {
46
+ if (value === 0 || checkOOB(memory, value)) return null;
47
+
48
+ const [ keysPtr, valsPtr ] = read(Uint32Array, memory, value, 2);
49
+ if (checkOOB(memory, keysPtr) || checkOOB(memory, valsPtr)) return null;
50
+
51
+ const size = read(Uint32Array, memory, keysPtr, 1);
52
+
53
+ const out = {};
54
+ for (let i = 0; i < size; i++) {
55
+ const offset = 4 + (i * 9);
56
+
57
+ const kValue = read(Float64Array, memory, keysPtr + offset, 1)[0];
58
+ const kType = read(Uint8Array, memory, keysPtr + offset + 8, 1)[0];
59
+ const k = porfToJSValue({ memory, funcs, pages }, kValue, kType);
60
+
61
+ const vValue = read(Float64Array, memory, valsPtr + offset, 1)[0];
62
+ const vType = read(Uint8Array, memory, valsPtr + offset + 8, 1)[0];
63
+ const v = porfToJSValue({ memory, funcs, pages }, vValue, vType);
64
+
65
+ out[k] = v;
66
+ }
67
+
68
+ return out;
69
+ }
36
70
 
37
71
  case TYPES.function: {
38
72
  let func;
@@ -49,29 +83,24 @@ const porfToJSValue = ({ memory, funcs, pages }, value, type) => {
49
83
  }
50
84
 
51
85
  case TYPES.string: {
52
- const length = (new Uint32Array(memory.buffer, value, 1))[0];
53
- return Array.from(new Uint16Array(memory.buffer, value + 4, length)).map(x => String.fromCharCode(x)).join('');
86
+ const length = read(Uint32Array, memory, value, 1)[0];
87
+ return Array.from(read(Uint16Array, memory, value + 4, length)).map(x => String.fromCharCode(x)).join('');
54
88
  }
55
89
 
56
90
  case TYPES.bytestring: {
57
- const length = (new Uint32Array(memory.buffer, value, 1))[0];
58
- return Array.from(new Uint8Array(memory.buffer, value + 4, length)).map(x => String.fromCharCode(x)).join('');
91
+ const length = read(Uint32Array, memory, value, 1)[0];
92
+ return Array.from(read(Uint8Array, memory, value + 4, length)).map(x => String.fromCharCode(x)).join('');
59
93
  }
60
94
 
61
95
  case TYPES.array: {
62
- const length = (new Uint32Array(memory.buffer, value, 1))[0];
96
+ const length = read(Uint32Array, memory, value, 1)[0];
63
97
 
64
98
  const out = [];
65
99
  for (let i = 0; i < length; i++) {
66
100
  const offset = value + 4 + (i * 9);
67
101
 
68
- // have to slice because of memory alignment (?)
69
- const v = (new Float64Array(memory.buffer.slice(offset, offset + 8), 0, 1))[0];
70
- const t = (new Uint8Array(memory.buffer, offset + 8, 1))[0];
71
-
72
- // console.log(`reading value at index ${i}...`)
73
- // console.log(' memory:', Array.from(new Uint8Array(memory.buffer, offset, 9)).map(x => x.toString(16).padStart(2, '0')).join(' '));
74
- // console.log(' read:', { value: v, type: t }, '\n');
102
+ const v = read(Float64Array, memory, offset, 1)[0];
103
+ const t = read(Uint8Array, memory, offset + 8, 1)[0];
75
104
 
76
105
  out.push(porfToJSValue({ memory, funcs, pages }, v, t));
77
106
  }
@@ -80,7 +109,7 @@ const porfToJSValue = ({ memory, funcs, pages }, value, type) => {
80
109
  }
81
110
 
82
111
  case TYPES.date: {
83
- const t = (new Float64Array(memory.buffer, value, 1))[0];
112
+ const t = read(Float64Array, memory, value, 1)[0];
84
113
  return new Date(t);
85
114
  }
86
115
 
@@ -90,15 +119,15 @@ const porfToJSValue = ({ memory, funcs, pages }, value, type) => {
90
119
 
91
120
  const offset = descStore + 4 + ((value - 1) * 9);
92
121
 
93
- const v = (new Float64Array(memory.buffer.slice(offset, offset + 8), 0, 1))[0];
94
- const t = (new Uint8Array(memory.buffer, offset + 8, 1))[0];
122
+ const v = read(Float64Array, memory, offset, 1)[0];
123
+ const t = read(Uint8Array, memory, offset + 8, 1)[0];
95
124
 
96
125
  const desc = porfToJSValue({ memory, funcs, pages }, v, t);
97
126
  return Symbol(desc);
98
127
  }
99
128
 
100
129
  case TYPES.arraybuffer: {
101
- const length = (new Uint32Array(memory.buffer.slice(value, value + 4), 0, 1))[0];
130
+ const length = read(Uint32Array, memory, value, 1)[0];
102
131
  if (length === 4294967295) {
103
132
  // mock detached
104
133
  const buf = new ArrayBuffer(0);
@@ -110,16 +139,16 @@ const porfToJSValue = ({ memory, funcs, pages }, value, type) => {
110
139
  }
111
140
 
112
141
  case TYPES.sharedarraybuffer: {
113
- const length = (new Uint32Array(memory.buffer.slice(value, value + 4), 0, 1))[0];
142
+ const length = read(Uint32Array, memory, value, 1)[0];
114
143
  const buf = memory.buffer.slice(value + 4, value + 4 + length);
115
144
  buf.shared = true;
116
145
  return buf;
117
146
  }
118
147
 
119
148
  case TYPES.dataview: {
120
- const [ length, ptr, byteOffset ] = (new Uint32Array(memory.buffer.slice(value, value + 12), 0, 3));
149
+ const [ length, ptr, byteOffset ] = read(Uint32Array, memory, value, 3);
121
150
  const bufferPtr = ptr - byteOffset;
122
- const bufferLen = (new Uint32Array(memory.buffer.slice(bufferPtr, bufferPtr + 4), 0, 1))[0];
151
+ const bufferLen = read(Uint32Array, memory, bufferPtr, 1)[0];
123
152
  const buffer = memory.buffer.slice(bufferPtr + 4, bufferPtr + 4 + bufferLen);
124
153
  return new DataView(buffer, byteOffset, length);
125
154
  }
@@ -133,26 +162,26 @@ const porfToJSValue = ({ memory, funcs, pages }, value, type) => {
133
162
  case TYPES.int32array:
134
163
  case TYPES.float32array:
135
164
  case TYPES.float64array: {
136
- const [ length, ptr ] = (new Uint32Array(memory.buffer.slice(value, value + 8), 0, 2));
137
- return new globalThis[TYPE_NAMES[type]](memory.buffer, ptr + 4, length);
165
+ const [ length, ptr ] = read(Uint32Array, memory, value, 2);
166
+ return read(globalThis[TYPE_NAMES[type]], memory, ptr + 4, length);
138
167
  }
139
168
 
140
169
  case TYPES.weakref: {
141
- const v = (new Float64Array(memory.buffer.slice(value, value + 8), 0, 1))[0];
142
- const t = (new Uint8Array(memory.buffer, value + 8, 1))[0];
170
+ const v = read(Float64Array, memory, value, 1)[0];
171
+ const t = read(Uint8Array, memory, value + 8, 1)[0];
143
172
 
144
173
  return new WeakRef(porfToJSValue({ memory, funcs, pages }, v, t));
145
174
  }
146
175
 
147
176
  case TYPES.weakset:
148
177
  case TYPES.set: {
149
- const size = (new Uint32Array(memory.buffer, value, 1))[0];
178
+ const size = read(Uint32Array, memory, value, 1)[0];
150
179
 
151
180
  const out = type === TYPES.weakset ? new WeakSet() : new Set();
152
181
  for (let i = 0; i < size; i++) {
153
182
  const offset = value + 4 + (i * 9);
154
- const v = (new Float64Array(memory.buffer.slice(offset, offset + 8), 0, 1))[0];
155
- const t = (new Uint8Array(memory.buffer, offset + 8, 1))[0];
183
+ const v = read(Float64Array, memory, offset, 1)[0];
184
+ const t = read(Uint8Array, memory, offset + 8, 1)[0];
156
185
 
157
186
  out.add(porfToJSValue({ memory, funcs, pages }, v, t));
158
187
  }
@@ -162,19 +191,19 @@ const porfToJSValue = ({ memory, funcs, pages }, value, type) => {
162
191
 
163
192
  case TYPES.weakmap:
164
193
  case TYPES.map: {
165
- const [ keysPtr, valsPtr ] = (new Uint32Array(memory.buffer, value, 2));
166
- const size = (new Uint32Array(memory.buffer, keysPtr, 1))[0];
194
+ const [ keysPtr, valsPtr ] = read(Uint32Array, memory, value, 2);
195
+ const size = read(Uint32Array, memory, keysPtr, 1)[0];
167
196
 
168
197
  const out = type === TYPES.weakmap ? new WeakMap() : new Map();
169
198
  for (let i = 0; i < size; i++) {
170
199
  const offset = 4 + (i * 9);
171
200
 
172
- const kValue = (new Float64Array(memory.buffer.slice(keysPtr + offset, keysPtr + offset + 8), 0, 1))[0];
173
- const kType = (new Uint8Array(memory.buffer, keysPtr + offset + 8, 1))[0];
201
+ const kValue = read(Float64Array, memory, keysPtr + offset, 1)[0];
202
+ const kType = read(Uint8Array, memory, keysPtr + offset + 8, 1)[0];
174
203
  const k = porfToJSValue({ memory, funcs, pages }, kValue, kType);
175
204
 
176
- const vValue = (new Float64Array(memory.buffer.slice(valsPtr + offset, valsPtr + offset + 8), 0, 1))[0];
177
- const vType = (new Uint8Array(memory.buffer, valsPtr + offset + 8, 1))[0];
205
+ const vValue = read(Float64Array, memory, valsPtr + offset, 1)[0];
206
+ const vType = read(Uint8Array, memory, valsPtr + offset + 8, 1)[0];
178
207
  const v = porfToJSValue({ memory, funcs, pages }, vValue, vType);
179
208
 
180
209
  out.set(k, v);
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.19.5+2501992d8",
4
+ "version": "0.19.7+120636c7c",
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.19.5+2501992d8';
3
+ globalThis.version = '0.19.7+120636c7c';
4
4
 
5
5
  // deno compat
6
6
  if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
package/runner/repl.js CHANGED
@@ -82,7 +82,10 @@ let prev = '';
82
82
  const run = (source, _context, _filename, callback, run = true) => {
83
83
  // hack: print "secret" before latest code ran to only enable printing for new code
84
84
 
85
- let toRun = (prev ? (prev + `;\nprint(-0x1337);\n`) : '') + source.trim();
85
+ source = source.trim();
86
+ if (source.startsWith('{') && source.endsWith('}')) source = '(' + source + ')';
87
+
88
+ let toRun = (prev ? (prev + `;\nprint(-0x1337);\n`) : '') + source;
86
89
 
87
90
  let shouldPrint = !prev;
88
91
  const { exports, pages } = compile(toRun, [], {}, str => {