porffor 0.19.4 → 0.19.6

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.
@@ -6,17 +6,20 @@ export const __Map_prototype_size$get = (_this: Map) => {
6
6
 
7
7
  export const __Map_prototype_has = (_this: Map, key: any) => {
8
8
  const keys: Set = Porffor.wasm.i32.load(_this, 0, 0);
9
- return __Set_prototype_has(key);
9
+ return __Set_prototype_has(keys, key);
10
10
  };
11
11
 
12
12
  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(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) => {
@@ -1341,6 +1341,10 @@ const getNodeType = (scope, node) => {
1341
1341
  return getType(scope, node.name);
1342
1342
  }
1343
1343
 
1344
+ if (node.type === 'ObjectExpression') {
1345
+ return TYPES.object;
1346
+ }
1347
+
1344
1348
  if (node.type === 'CallExpression' || node.type === 'NewExpression') {
1345
1349
  const name = node.callee.name;
1346
1350
  if (!name) {
@@ -1926,7 +1930,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1926
1930
  }
1927
1931
 
1928
1932
  // TODO: only allows callee as identifier
1929
- if (!name) return todo(scope, `only identifier callees (got ${decl.callee.type})`);
1933
+ // if (!name) return todo(scope, `only identifier callees (got ${decl.callee.type})`);
1930
1934
 
1931
1935
  let idx = funcIndex[name] ?? importedFuncs[name];
1932
1936
  if (idx === undefined && builtinFuncs[name]) {
@@ -1942,7 +1946,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1942
1946
  return internalConstrs[name].generate(scope, decl, _global, _name);
1943
1947
  }
1944
1948
 
1945
- if (idx === undefined && !decl._new && name.startsWith('__Porffor_wasm_')) {
1949
+ if (idx === undefined && !decl._new && name && name.startsWith('__Porffor_wasm_')) {
1946
1950
  const wasmOps = {
1947
1951
  // pointer, align, offset
1948
1952
  i32_load: { imms: 2, args: [ true ], returns: 1 },
@@ -1989,162 +1993,171 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1989
1993
  }
1990
1994
 
1991
1995
  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);
1996
+ if (!Prefs.indirectCalls) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
1995
1997
 
1996
- // todo: only works when function uses typedParams and typedReturns
1998
+ // todo: only works when function uses typedParams and typedReturns
1997
1999
 
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
2000
+ const indirectMode = Prefs.indirectCallMode ?? 'vararg';
2001
+ // options: vararg, strict
2002
+ // - strict: simpler, smaller size usage, no func lut needed.
2003
+ // ONLY works when arg count of call == arg count of function being called
2004
+ // - vararg: large size usage, cursed.
2005
+ // works when arg count of call != arg count of function being called*
2006
+ // * most of the time, some edgecases
2005
2007
 
2006
- funcs.table = true;
2007
- scope.table = true;
2008
+ funcs.table = true;
2009
+ scope.table = true;
2008
2010
 
2009
- let args = decl.arguments;
2010
- let locals = [];
2011
+ let args = decl.arguments;
2012
+ let locals = [];
2011
2013
 
2012
- if (indirectMode === 'vararg') {
2013
- const minArgc = Prefs.indirectCallMinArgc ?? 3;
2014
+ if (indirectMode === 'vararg') {
2015
+ const minArgc = Prefs.indirectCallMinArgc ?? 3;
2014
2016
 
2015
- if (args.length < minArgc) {
2016
- args = args.concat(new Array(minArgc - args.length).fill(DEFAULT_VALUE));
2017
- }
2017
+ if (args.length < minArgc) {
2018
+ args = args.concat(new Array(minArgc - args.length).fill(DEFAULT_VALUE));
2018
2019
  }
2020
+ }
2019
2021
 
2020
- for (let i = 0; i < args.length; i++) {
2021
- const arg = args[i];
2022
- out = out.concat(generate(scope, arg));
2022
+ for (let i = 0; i < args.length; i++) {
2023
+ const arg = args[i];
2024
+ out = out.concat(generate(scope, arg));
2023
2025
 
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
- }
2026
+ if (valtypeBinary !== Valtype.i32 && (
2027
+ (builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
2028
+ (importedFuncs[name] && name.startsWith('profile'))
2029
+ )) {
2030
+ out.push(Opcodes.i32_to);
2031
+ }
2030
2032
 
2031
- out = out.concat(getNodeType(scope, arg));
2033
+ out = out.concat(getNodeType(scope, arg));
2032
2034
 
2033
- if (indirectMode === 'vararg') {
2034
- const typeLocal = localTmp(scope, `#indirect_arg${i}_type`, Valtype.i32);
2035
- const valLocal = localTmp(scope, `#indirect_arg${i}_val`);
2035
+ if (indirectMode === 'vararg') {
2036
+ const typeLocal = localTmp(scope, `#indirect_arg${i}_type`, Valtype.i32);
2037
+ const valLocal = localTmp(scope, `#indirect_arg${i}_val`);
2036
2038
 
2037
- locals.push([valLocal, typeLocal]);
2039
+ locals.push([valLocal, typeLocal]);
2038
2040
 
2039
- out.push(
2040
- [ Opcodes.local_set, typeLocal ],
2041
- [ Opcodes.local_set, valLocal ]
2042
- );
2043
- }
2041
+ out.push(
2042
+ [ Opcodes.local_set, typeLocal ],
2043
+ [ Opcodes.local_set, valLocal ]
2044
+ );
2044
2045
  }
2046
+ }
2045
2047
 
2046
- if (indirectMode === 'strict') {
2047
- return typeSwitch(scope, getNodeType(scope, decl.callee), {
2048
+ if (indirectMode === 'strict') {
2049
+ return [
2050
+ ...generate(scope, decl.callee),
2051
+ [ Opcodes.local_set, localTmp(scope, '#indirect_callee') ],
2052
+
2053
+ ...typeSwitch(scope, getNodeType(scope, decl.callee), {
2048
2054
  [TYPES.function]: [
2049
2055
  ...out,
2050
- [ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
2056
+
2057
+ [ Opcodes.local_get, localTmp(scope, '#indirect_callee') ],
2058
+ ...generate(scope, decl.callee),
2051
2059
  Opcodes.i32_to_u,
2052
2060
  [ Opcodes.call_indirect, args.length, 0 ],
2053
2061
  ...setLastType(scope)
2054
2062
  ],
2055
2063
  default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
2056
- });
2064
+ })
2065
+ ];
2066
+ }
2067
+
2068
+ // hi, I will now explain how vararg mode works:
2069
+ // wasm's indirect_call instruction requires you know the func type at compile-time
2070
+ // since we have varargs (variable argument count), we do not know it.
2071
+ // we could just store args in memory and not use wasm func args,
2072
+ // but that is slow (probably) and breaks js exports.
2073
+ // instead, we generate every* possibility of argc and use different indirect_call
2074
+ // ops for each one, with type depending on argc for that branch.
2075
+ // then we load the argc for the wanted function from a memory lut,
2076
+ // and call the branch with the matching argc we require.
2077
+ // sorry, yes it is very cursed (and size inefficient), but indirect calls
2078
+ // are kind of rare anyway (mostly callbacks) so I am not concerned atm.
2079
+ // *for argc 0-3, in future (todo:) the max number should be
2080
+ // dynamically changed to the max argc of any func in the js file.
2081
+
2082
+ const funcLocal = localTmp(scope, '#indirect_func', Valtype.i32);
2083
+ const flags = localTmp(scope, '#indirect_flags', Valtype.i32);
2084
+
2085
+ const gen = argc => {
2086
+ const argsOut = [];
2087
+ for (let i = 0; i < argc; i++) {
2088
+ argsOut.push(
2089
+ [ Opcodes.local_get, locals[i][0] ],
2090
+ [ Opcodes.local_get, locals[i][1] ]
2091
+ );
2057
2092
  }
2058
2093
 
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
- }
2094
+ const checkFlag = (flag, pass, fail) => [
2095
+ [ Opcodes.local_get, flags ],
2096
+ ...number(flag, Valtype.i32),
2097
+ [ Opcodes.i32_and ],
2098
+ [ Opcodes.if, valtypeBinary ],
2099
+ ...pass,
2100
+ [ Opcodes.else ],
2101
+ ...fail,
2102
+ [ Opcodes.end ]
2103
+ ];
2084
2104
 
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
- ];
2105
+ // pain.
2106
+ // return checkFlag(0b10, [ // constr
2107
+ // [ Opcodes.i32_const, decl._new ? 1 : 0 ],
2108
+ // ...argsOut,
2109
+ // [ Opcodes.local_get, funcLocal ],
2110
+ // [ Opcodes.call_indirect, argc, 0, 'constr' ],
2111
+ // ...setLastType(scope),
2112
+ // ], [
2113
+ // ...argsOut,
2114
+ // [ Opcodes.local_get, funcLocal ],
2115
+ // [ Opcodes.call_indirect, argc, 0 ],
2116
+ // ...setLastType(scope),
2117
+ // ]);
2118
+
2119
+ return checkFlag(0b1, // no type return
2120
+ checkFlag(0b10, [ // no type return & constr
2121
+ [ Opcodes.i32_const, decl._new ? 1 : 0 ],
2122
+ ...argsOut,
2123
+ [ Opcodes.local_get, funcLocal ],
2124
+ [ Opcodes.call_indirect, argc, 0, 'no_type_return', 'constr' ],
2125
+ ], [
2126
+ ...argsOut,
2127
+ [ Opcodes.local_get, funcLocal ],
2128
+ [ Opcodes.call_indirect, argc, 0, 'no_type_return' ]
2129
+ ]),
2130
+ checkFlag(0b10, [ // type return & constr
2131
+ [ Opcodes.i32_const, decl._new ? 1 : 0 ],
2132
+ ...argsOut,
2133
+ [ Opcodes.local_get, funcLocal ],
2134
+ [ Opcodes.call_indirect, argc, 0, 'constr' ],
2135
+ ...setLastType(scope),
2136
+ ], [
2137
+ ...argsOut,
2138
+ [ Opcodes.local_get, funcLocal ],
2139
+ [ Opcodes.call_indirect, argc, 0 ],
2140
+ ...setLastType(scope),
2141
+ ]),
2142
+ );
2143
+ };
2095
2144
 
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
- };
2145
+ const tableBc = {};
2146
+ for (let i = 0; i <= args.length; i++) {
2147
+ tableBc[i] = gen(i);
2148
+ }
2135
2149
 
2136
- const tableBc = {};
2137
- for (let i = 0; i <= args.length; i++) {
2138
- tableBc[i] = gen(i);
2139
- }
2150
+ // todo/perf: check if we should use br_table here or just generate our own big if..elses
2140
2151
 
2141
- // todo/perf: check if we should use br_table here or just generate our own big if..elses
2152
+ return [
2153
+ ...generate(scope, decl.callee),
2154
+ [ Opcodes.local_set, localTmp(scope, '#indirect_callee') ],
2142
2155
 
2143
- return typeSwitch(scope, getNodeType(scope, decl.callee), {
2156
+ ...typeSwitch(scope, getNodeType(scope, decl.callee), {
2144
2157
  [TYPES.function]: [
2145
2158
  ...out,
2146
2159
 
2147
- [ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
2160
+ [ Opcodes.local_get, localTmp(scope, '#indirect_callee') ],
2148
2161
  Opcodes.i32_to_u,
2149
2162
  [ Opcodes.local_set, funcLocal ],
2150
2163
 
@@ -2177,10 +2190,8 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2177
2190
  ], tableBc, valtypeBinary)
2178
2191
  ],
2179
2192
  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);
2193
+ })
2194
+ ];
2184
2195
  }
2185
2196
 
2186
2197
  const func = funcs[idx - importedFuncs.length]; // idx === scope.index ? scope : funcs.find(x => x.index === idx);
@@ -2296,6 +2307,8 @@ const codeToSanitizedStr = code => {
2296
2307
  const sanitize = str => str.replace(/[^0-9a-zA-Z_]/g, _ => codeToSanitizedStr(_.charCodeAt(0)));
2297
2308
 
2298
2309
  const unhackName = name => {
2310
+ if (!name) return name;
2311
+
2299
2312
  if (name.startsWith('__')) return name.slice(2).replaceAll('_', '.');
2300
2313
  return name;
2301
2314
  };
@@ -2702,22 +2715,22 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2702
2715
  const { type, name } = decl.left;
2703
2716
  const [ local, isGlobal ] = lookupName(scope, name);
2704
2717
 
2705
- if (isFuncType(decl.right.type)) {
2706
- // hack for a = function () { ... }
2707
- decl.right.id = { name };
2718
+ // if (isFuncType(decl.right.type)) {
2719
+ // // hack for a = function () { ... }
2720
+ // decl.right.id = { name };
2708
2721
 
2709
- const func = generateFunc(scope, decl.right);
2722
+ // const func = generateFunc(scope, decl.right);
2710
2723
 
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 ],
2724
+ // return [
2725
+ // ...number(func.index - importedFuncs.length),
2726
+ // ...(local != null ? [
2727
+ // [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
2728
+ // [ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
2716
2729
 
2717
- ...setType(scope, name, TYPES.function)
2718
- ] : [])
2719
- ];
2720
- }
2730
+ // ...setType(scope, name, TYPES.function)
2731
+ // ] : [])
2732
+ // ];
2733
+ // }
2721
2734
 
2722
2735
  const op = decl.operator.slice(0, -1) || '=';
2723
2736
 
@@ -2746,18 +2759,27 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2746
2759
  }
2747
2760
 
2748
2761
  // arr[i]
2749
- if (decl.left.type === 'MemberExpression' && decl.left.computed) {
2762
+ if (decl.left.type === 'MemberExpression') {
2750
2763
  const newValueTmp = localTmp(scope, '#member_setter_val_tmp');
2751
2764
  const pointerTmp = localTmp(scope, '#member_setter_ptr_tmp', Valtype.i32);
2752
2765
 
2766
+ const object = decl.left.object;
2767
+ const property = decl.left.computed ? decl.left.property : {
2768
+ type: 'Literal',
2769
+ value: decl.left.property.name
2770
+ };
2771
+
2772
+ includeBuiltin(scope, '__Map_prototype_get');
2773
+ includeBuiltin(scope, '__Map_prototype_set');
2774
+
2753
2775
  return [
2754
2776
  ...typeSwitch(scope, getNodeType(scope, decl.left.object), {
2755
2777
  [TYPES.array]: [
2756
- ...generate(scope, decl.left.object),
2778
+ ...generate(scope, object),
2757
2779
  Opcodes.i32_to_u,
2758
2780
 
2759
2781
  // get index as valtype
2760
- ...generate(scope, decl.left.property),
2782
+ ...generate(scope, property),
2761
2783
  Opcodes.i32_to_u,
2762
2784
 
2763
2785
  // turn into byte offset by * valtypeSize + 1
@@ -2781,6 +2803,34 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2781
2803
  [ Opcodes.i32_store8, 0, ValtypeSize.i32 + ValtypeSize[valtype] ],
2782
2804
  ],
2783
2805
 
2806
+ [TYPES.object]: [
2807
+ ...generate(scope, object),
2808
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, localTmp(scope, '#objset_object') ] ]),
2809
+ ...getNodeType(scope, object),
2810
+
2811
+ ...generate(scope, property),
2812
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, localTmp(scope, '#objset_property') ] ]),
2813
+ ...getNodeType(scope, property),
2814
+
2815
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2816
+ [ Opcodes.local_get, localTmp(scope, '#objset_object') ],
2817
+ ...getNodeType(scope, object),
2818
+
2819
+ [ Opcodes.local_get, localTmp(scope, '#objset_property') ],
2820
+ ...getNodeType(scope, property),
2821
+
2822
+ [ Opcodes.call, funcIndex.__Map_prototype_get ],
2823
+ ...setLastType(scope)
2824
+ ], generate(scope, decl.right), getLastType(scope), getNodeType(scope, decl.right), false, name, true)),
2825
+ [ Opcodes.local_tee, newValueTmp ],
2826
+ ...getNodeType(scope, decl),
2827
+
2828
+ [ Opcodes.call, funcIndex.__Map_prototype_set ],
2829
+ [ Opcodes.drop ],
2830
+
2831
+ ...setLastType(scope, getNodeType(scope, decl)),
2832
+ ],
2833
+
2784
2834
  ...wrapBC({
2785
2835
  [TYPES.uint8array]: [
2786
2836
  [ Opcodes.i32_add ],
@@ -2924,11 +2974,11 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2924
2974
  ],
2925
2975
  }, {
2926
2976
  prelude: [
2927
- ...generate(scope, decl.left.object),
2977
+ ...generate(scope, object),
2928
2978
  Opcodes.i32_to_u,
2929
2979
  [ Opcodes.i32_load, 0, 4 ],
2930
2980
 
2931
- ...generate(scope, decl.left.property),
2981
+ ...generate(scope, property),
2932
2982
  Opcodes.i32_to_u,
2933
2983
  ],
2934
2984
  postlude: setLastType(scope, TYPES.number)
@@ -2957,7 +3007,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2957
3007
  // set global and return (eg a = 2)
2958
3008
  return [
2959
3009
  ...generateVar(scope, { kind: 'var', _bare: true, declarations: [ { id: { name }, init: decl.right } ] }),
2960
- [ Opcodes.global_get, globals[name].idx ]
3010
+ ...generate(scope, decl.left)
2961
3011
  ];
2962
3012
  }
2963
3013
 
@@ -4193,12 +4243,50 @@ const generateArray = (scope, decl, global = false, name = '$undeclared', initEm
4193
4243
  };
4194
4244
 
4195
4245
  const generateObject = (scope, decl, global = false, name = '$undeclared') => {
4196
- if (decl.properties.length > 0) return todo(scope, 'objects are not supported yet', true);
4246
+ if (!funcIndex.Map) includeBuiltin(scope, 'Map');
4247
+ if (!funcIndex.__Map_prototype_set) includeBuiltin(scope, '__Map_prototype_set');
4197
4248
 
4198
- return [
4199
- ...number(1),
4200
- ...setLastType(scope, TYPES.object)
4249
+ // todo: optimize const objects
4250
+ const tmp = localTmp(scope, `#objectexpr${randId()}`);
4251
+ const out = [
4252
+ ...generateCall(scope, {
4253
+ callee: {
4254
+ type: 'Identifier',
4255
+ name: 'Map'
4256
+ },
4257
+ arguments: [],
4258
+ _new: true
4259
+ }),
4260
+ [ Opcodes.local_tee, tmp ]
4201
4261
  ];
4262
+
4263
+ for (const x of decl.properties) {
4264
+ const { method, shorthand, computed, kind, key, value } = x;
4265
+ if (method || shorthand || kind !== 'init' || key.type !== 'Identifier') return todo(scope, 'complex objects are not supported yet', true);
4266
+
4267
+ const k = computed ? key : {
4268
+ type: 'Literal',
4269
+ value: key.name
4270
+ };
4271
+
4272
+ out.push(
4273
+ [ Opcodes.local_get, tmp ],
4274
+ ...number(TYPES.map, Valtype.i32),
4275
+
4276
+ ...generate(scope, k),
4277
+ ...getNodeType(scope, k),
4278
+
4279
+ ...generate(scope, value),
4280
+ ...getNodeType(scope, value),
4281
+
4282
+ [ Opcodes.call, funcIndex.__Map_prototype_set ],
4283
+
4284
+ [ Opcodes.drop ],
4285
+ [ Opcodes.drop ]
4286
+ );
4287
+ }
4288
+
4289
+ return out;
4202
4290
  };
4203
4291
 
4204
4292
  const withType = (scope, wasm, type) => [
@@ -4323,14 +4411,19 @@ const generateMember = (scope, decl, _global, _name) => {
4323
4411
  }, valtypeBinary);
4324
4412
  }
4325
4413
 
4326
- const object = generate(scope, decl.object);
4327
- const property = generate(scope, decl.property);
4414
+ const object = decl.object;
4415
+ const objectWasm = generate(scope, object);
4416
+ const property = decl.computed ? decl.property : {
4417
+ type: 'Literal',
4418
+ value: decl.property.name
4419
+ };
4420
+ const propertyWasm = generate(scope, property);
4328
4421
 
4329
4422
  // // todo: we should only do this for strings but we don't know at compile-time :(
4330
4423
  // hack: this is naughty and will break things!
4331
4424
  let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
4332
4425
 
4333
- const known = knownType(scope, getNodeType(scope, decl.object));
4426
+ const known = knownType(scope, getNodeType(scope, object));
4334
4427
  if ((known === TYPES.string || known === TYPES.bytestring) || (pages.hasAnyString && known == null)) {
4335
4428
  // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
4336
4429
  0, [ newOut, newPointer ] = makeArray(scope, {
@@ -4338,11 +4431,14 @@ const generateMember = (scope, decl, _global, _name) => {
4338
4431
  }, _global, _name, true, 'i16', true);
4339
4432
  }
4340
4433
 
4341
- return typeSwitch(scope, getNodeType(scope, decl.object), {
4434
+ includeBuiltin(scope, '__Map_prototype_get');
4435
+
4436
+ return typeSwitch(scope, getNodeType(scope, object), {
4342
4437
  [TYPES.array]: [
4343
- ...loadArray(scope, object, property),
4438
+ ...loadArray(scope, objectWasm, propertyWasm),
4344
4439
  ...setLastType(scope)
4345
4440
  ],
4441
+
4346
4442
  [TYPES.string]: [
4347
4443
  // setup new/out array
4348
4444
  ...newOut,
@@ -4354,13 +4450,13 @@ const generateMember = (scope, decl, _global, _name) => {
4354
4450
  // use as pointer for store later
4355
4451
  ...newPointer,
4356
4452
 
4357
- ...property,
4453
+ ...propertyWasm,
4358
4454
  Opcodes.i32_to_u,
4359
4455
 
4360
4456
  ...number(ValtypeSize.i16, Valtype.i32),
4361
4457
  [ Opcodes.i32_mul ],
4362
4458
 
4363
- ...object,
4459
+ ...objectWasm,
4364
4460
  Opcodes.i32_to_u,
4365
4461
  [ Opcodes.i32_add ],
4366
4462
 
@@ -4375,6 +4471,7 @@ const generateMember = (scope, decl, _global, _name) => {
4375
4471
  Opcodes.i32_from_u,
4376
4472
  ...setLastType(scope, TYPES.string)
4377
4473
  ],
4474
+
4378
4475
  [TYPES.bytestring]: [
4379
4476
  // setup new/out array
4380
4477
  ...newOut,
@@ -4386,10 +4483,10 @@ const generateMember = (scope, decl, _global, _name) => {
4386
4483
  // use as pointer for store later
4387
4484
  ...newPointer,
4388
4485
 
4389
- ...property,
4486
+ ...propertyWasm,
4390
4487
  Opcodes.i32_to_u,
4391
4488
 
4392
- ...object,
4489
+ ...objectWasm,
4393
4490
  Opcodes.i32_to_u,
4394
4491
  [ Opcodes.i32_add ],
4395
4492
 
@@ -4405,6 +4502,17 @@ const generateMember = (scope, decl, _global, _name) => {
4405
4502
  ...setLastType(scope, TYPES.bytestring)
4406
4503
  ],
4407
4504
 
4505
+ [TYPES.object]: [
4506
+ ...objectWasm,
4507
+ ...getNodeType(scope, object),
4508
+
4509
+ ...propertyWasm,
4510
+ ...getNodeType(scope, property),
4511
+
4512
+ [ Opcodes.call, funcIndex.__Map_prototype_get ],
4513
+ ...setLastType(scope)
4514
+ ],
4515
+
4408
4516
  ...wrapBC({
4409
4517
  [TYPES.uint8array]: [
4410
4518
  [ Opcodes.i32_add ],
@@ -4473,22 +4581,23 @@ const generateMember = (scope, decl, _global, _name) => {
4473
4581
  ],
4474
4582
  }, {
4475
4583
  prelude: [
4476
- ...object,
4584
+ ...objectWasm,
4477
4585
  Opcodes.i32_to_u,
4478
4586
  [ Opcodes.i32_load, 0, 4 ],
4479
4587
 
4480
- ...property,
4588
+ ...propertyWasm,
4481
4589
  Opcodes.i32_to_u
4482
4590
  ],
4483
4591
  postlude: setLastType(scope, TYPES.number)
4484
4592
  }),
4485
4593
 
4486
- default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
4594
+ default: internalThrow(scope, 'TypeError', 'Unsupported member expression object', true)
4487
4595
  });
4488
4596
  };
4489
4597
 
4490
4598
  const randId = () => Math.random().toString(16).slice(1, -2).padEnd(12, '0');
4491
4599
 
4600
+ let objectHackers = [];
4492
4601
  const objectHack = node => {
4493
4602
  if (!node) return node;
4494
4603
 
@@ -4503,16 +4612,10 @@ const objectHack = node => {
4503
4612
  if (objectName && ['undefined', 'null', 'NaN', 'Infinity'].includes(objectName)) return;
4504
4613
 
4505
4614
  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);
4615
+ if (!objectName || (!objectHackers.includes(objectName) && !objectHackers.some(x => objectName.startsWith(`${x}_`)))) {
4510
4616
  return;
4511
4617
  }
4512
4618
 
4513
- // no object name, give up
4514
- if (!objectName) return;
4515
-
4516
4619
  const name = '__' + objectName + '_' + node.property.name;
4517
4620
  if (Prefs.codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
4518
4621
 
@@ -4861,6 +4964,9 @@ export default program => {
4861
4964
  prototypeFuncs = new PrototypeFuncs();
4862
4965
  allocator = makeAllocator(Prefs.allocator ?? 'static');
4863
4966
 
4967
+ const getObjectName = x => x.startsWith('__') && x.slice(2, x.indexOf('_', 2));
4968
+ objectHackers = ['assert', 'compareArray', 'Test262Error', ...new Set(Object.keys(builtinFuncs).map(getObjectName).concat(Object.keys(builtinVars).map(getObjectName)).filter(x => x))];
4969
+
4864
4970
  program.id = { name: 'main' };
4865
4971
 
4866
4972
  const scope = {
@@ -3998,28 +3998,28 @@ export const BuiltinFuncs = function() {
3998
3998
  locals: [], localNames: ["_this","_this#type"],
3999
3999
  };
4000
4000
  this.__Map_prototype_has = {
4001
- wasm: (scope, {builtin}) => [[32,0],[252,2],[40,0,0],[183],[33,4],[32,2],[32,3],[68,0,0,0,0,0,0,0,0],[65,128,1],[16, builtin('__Set_prototype_has')],[34,5],[15]],
4001
+ wasm: (scope, {builtin}) => [[32,0],[252,2],[40,0,0],[183],[34,4],[65,19],[32,2],[32,3],[16, builtin('__Set_prototype_has')],[34,5],[15]],
4002
4002
  params: [124,127,124,127], typedParams: 1,
4003
4003
  returns: [124,127], typedReturns: 1,
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],[33,8],[32,5],[32,6],[68,0,0,0,0,0,0,240,63],[65,1],[68,0,0,0,0,0,0,0,0],[65,128,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.4+1dbbc5d08",
4
+ "version": "0.19.6+432f446a1",
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.4+1dbbc5d08';
3
+ globalThis.version = '0.19.6+432f446a1';
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 => {