porffor 0.25.1 → 0.25.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CONTRIBUTING.md CHANGED
@@ -200,13 +200,12 @@ Store the character code into the `out` pointer variable, and increment it.
200
200
 
201
201
  - For declaring variables, you must use explicit type annotations currently (eg `let a: number = 1`, not `let a = 1`).
202
202
  - You might spot `Porffor.fastOr`/`Porffor.fastAnd`, these are non-short circuiting versions of `||`/`&&`, taking any number of conditions as arguments. You shouldn't don't need to use or worry about these.
203
- - **There are ~no objects, you cannot use them.**
204
- - Attempt to avoid string/array-heavy code and use more variables instead if possible, easier on memory and CPU/perf.
203
+ - Attempt to avoid object/string/array-heavy code and use more variables instead if possible, easier on memory and CPU/perf.
205
204
  - Do not set a return type for prototype methods, it can cause errors/unexpected results.
206
205
  - You cannot use other functions in the file not exported, or variables not inside the current function.
207
206
  - `if (...)` uses a fast truthy implementation which is not spec-compliant as most conditions should be strictly checked. To use spec-compliant behavior, use `if (Boolean(...))`.
208
207
  - For object (string/array/etc) literals, you must use a variable eg `const out: bytestring = 'foobar'; console.log(out);` instead of `console.log('foobar')` due to precompile's allocator constraints.
209
- - Generally prefer/use non-strict equality ops (`==`/`!=`).
208
+ - You should generally use non-strict equality ops (`==`/`!=`).
210
209
 
211
210
  <br>
212
211
 
@@ -190,7 +190,7 @@ export const BuiltinFuncs = function() {
190
190
  ...number(TYPES.number, Valtype.i32),
191
191
  [ Opcodes.local_get, 1 ],
192
192
  ...number(TYPES.number, Valtype.i32),
193
- [ Opcodes.call, builtin('__Math_pow') ],
193
+ [ Opcodes.call, ...builtin('__Math_pow') ],
194
194
  [ Opcodes.drop ],
195
195
  ]
196
196
  };
@@ -1,4 +1,4 @@
1
- import { Opcodes, PageSize, Valtype } from './wasmSpec.js';
1
+ import { Blocktype, Opcodes, PageSize, Valtype } from './wasmSpec.js';
2
2
  import { TYPES } from './types.js';
3
3
  import { number } from './embedding.js';
4
4
 
@@ -6,64 +6,92 @@ export default function({ builtinFuncs }, Prefs) {
6
6
  const done = new Set();
7
7
  const object = (name, props) => {
8
8
  done.add(name);
9
+ const prefix = name === 'globalThis' ? '' : `__${name}_`;
10
+
11
+ builtinFuncs['#get_' + name] = {
12
+ params: [],
13
+ locals: [],
14
+ globals: [ Valtype.i32 ],
15
+ globalNames: [ '#getptr_' + name ],
16
+ returns: [ Valtype.i32 ],
17
+ returnType: TYPES.object,
18
+ wasm: (scope, { allocPage, makeString, generate, getNodeType, builtin }) => {
19
+ if (globalThis.precompile) return [ [ 'get object', name ] ];
20
+
21
+ // todo/perf: precompute bytes here instead of calling real funcs if we really care about perf later
22
+
23
+ const page = allocPage(scope, `builtin object: ${name}`);
24
+ const ptr = page === 0 ? 4 : page * PageSize;
25
+
26
+ const out = [
27
+ // check if already made/cached
28
+ [ Opcodes.global_get, 0 ],
29
+ [ Opcodes.if, Blocktype.void ],
30
+ [ Opcodes.global_get, 0 ],
31
+ [ Opcodes.return ],
32
+ [ Opcodes.end ],
33
+
34
+ // set cache & ptr for use
35
+ ...number(ptr, Valtype.i32),
36
+ [ Opcodes.global_set, 0 ],
37
+ ];
9
38
 
10
- let cached;
11
- this[name] = (scope, { allocPage, makeString, generateIdent, getNodeType, builtin }) => {
12
- if (cached) {
13
- return number(cached);
14
- }
15
-
16
- // todo: precompute bytes here instead of calling real funcs if we really care about perf later
39
+ for (const x in props) {
40
+ let value = {
41
+ type: 'Identifier',
42
+ name: prefix + x
43
+ };
17
44
 
18
- const page = allocPage(scope, `builtin object: ${name}`);
19
- const ptr = page === 0 ? 4 : page * PageSize;
20
- cached = ptr;
45
+ let flags = 0b0000;
21
46
 
22
- const out = [];
47
+ const d = props[x];
48
+ if (d.configurable) flags |= 0b0010;
49
+ if (d.enumerable) flags |= 0b0100;
50
+ if (d.writable) flags |= 0b1000;
23
51
 
24
- for (const x in props) {
25
- const value = {
26
- type: 'Identifier',
27
- name: '__' + name + '_' + x
28
- };
52
+ // hack: do not generate objects inside of objects as it causes issues atm
53
+ if (this[prefix + x]?.type === TYPES.object) value = { type: 'ObjectExpression', properties: [] };
29
54
 
30
- let flags = 0b0000;
55
+ out.push(
56
+ [ Opcodes.global_get, 0 ],
57
+ ...number(TYPES.object, Valtype.i32),
31
58
 
32
- const d = props[x];
33
- if (d.configurable) flags |= 0b0010;
34
- if (d.enumerable) flags |= 0b0100;
35
- if (d.writable) flags |= 0b1000;
36
-
37
- out.push(
38
- ...number(ptr, Valtype.i32),
39
- ...number(TYPES.object, Valtype.i32),
59
+ ...makeString(scope, x, false, `#builtin_object_${name}_${x}`),
60
+ Opcodes.i32_to_u,
61
+ ...number(TYPES.bytestring, Valtype.i32),
40
62
 
41
- ...makeString(scope, x, false, `#builtin_object_${name}_${x}`),
42
- Opcodes.i32_to_u,
43
- ...number(TYPES.bytestring, Valtype.i32),
63
+ ...generate(scope, value),
64
+ ...getNodeType(scope, value),
44
65
 
45
- ...generateIdent(scope, value),
46
- ...getNodeType(scope, value),
66
+ ...number(flags, Valtype.i32),
67
+ ...number(TYPES.number, Valtype.i32),
47
68
 
48
- ...number(flags, Valtype.i32),
49
- ...number(TYPES.number, Valtype.i32),
69
+ [ Opcodes.call, ...builtin('__Porffor_object_define') ],
70
+ [ Opcodes.drop ],
71
+ [ Opcodes.drop ]
72
+ );
73
+ }
50
74
 
51
- [ Opcodes.call, ...builtin('__Porffor_object_define') ],
52
- [ Opcodes.drop ],
53
- [ Opcodes.drop ]
75
+ out.push(
76
+ // return ptr
77
+ [ Opcodes.global_get, 0 ]
54
78
  );
79
+ return out;
55
80
  }
56
-
57
- out.push(...number(ptr));
58
- return out;
59
81
  };
82
+
83
+
84
+ this[name] = (scope, { builtin }) => [
85
+ [ Opcodes.call, ...builtin('#get_' + name) ],
86
+ Opcodes.i32_from_u
87
+ ];
60
88
  this[name].type = TYPES.object;
61
89
 
62
90
  for (const x in props) {
63
91
  const d = props[x];
64
92
 
65
93
  if (d.value) {
66
- const k = '__' + name + '_' + x;
94
+ const k = prefix + x;
67
95
 
68
96
  if (typeof d.value === 'number') {
69
97
  this[k] = number(d.value);
@@ -189,7 +217,42 @@ export default function({ builtinFuncs }, Prefs) {
189
217
  });
190
218
  }
191
219
 
192
- object('globalThis', {})
220
+ const enumerableGlobals = [ 'atob', 'btoa', 'performance', 'crypto', 'navigator' ];
221
+ object('globalThis', {
222
+ // 19.1 Value Properties of the Global Object
223
+ // https://tc39.es/ecma262/#sec-value-properties-of-the-global-object
224
+ // 19.1.1 globalThis
225
+ globalThis: {
226
+ writable: true,
227
+ enumerable: false,
228
+ configurable: true
229
+ },
230
+
231
+ // 19.1.2 Infinity
232
+ // 19.1.3 NaN
233
+ // 19.1.4 undefined
234
+ ...props({
235
+ writable: false,
236
+ enumerable: false,
237
+ configurable: false
238
+ }, [ 'Infinity', 'NaN', 'undefined' ]),
239
+
240
+ // 19.2 Function Properties of the Global Object
241
+ // https://tc39.es/ecma262/#sec-function-properties-of-the-global-object
242
+ // 19.3 Constructor Properties of the Global Object
243
+ // https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object
244
+ ...props({
245
+ writable: true,
246
+ enumerable: false,
247
+ configurable: true
248
+ }, builtinFuncKeys.filter(x => !x.startsWith('__') && !enumerableGlobals.includes(x) && !x.startsWith('f64') && !x.startsWith('i32'))),
249
+
250
+ ...props({
251
+ writable: true,
252
+ enumerable: true,
253
+ configurable: true
254
+ }, enumerableGlobals)
255
+ });
193
256
 
194
257
  if (Prefs.logMissingObjects) for (const x of Object.keys(builtinFuncs).concat(Object.keys(this))) {
195
258
  if (!x.startsWith('__')) continue;