porffor 0.14.0-f67c123a1 → 0.16.0-a2e115b05

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/compiler/index.js CHANGED
@@ -12,14 +12,7 @@ globalThis.decompile = decompile;
12
12
  const logFuncs = (funcs, globals, exceptions) => {
13
13
  console.log('\n' + underline(bold('funcs')));
14
14
 
15
- const startIndex = funcs.sort((a, b) => a.index - b.index)[0].index;
16
15
  for (const f of funcs) {
17
- console.log(`${underline(f.name)} (${f.index - startIndex})`);
18
-
19
- console.log(`params: ${f.params.map((_, i) => Object.keys(f.locals)[Object.values(f.locals).indexOf(Object.values(f.locals).find(x => x.idx === i))]).join(', ')}`);
20
- console.log(`returns: ${f.returns.length > 0 ? true : false}`);
21
- console.log(`locals: ${Object.keys(f.locals).sort((a, b) => f.locals[a].idx - f.locals[b].idx).map(x => `${x} (${f.locals[x].idx})`).join(', ')}`);
22
- console.log();
23
16
  console.log(decompile(f.wasm, f.name, f.index, f.locals, f.params, f.returns, funcs, globals, exceptions));
24
17
  }
25
18
 
@@ -44,12 +37,14 @@ export default (code, flags) => {
44
37
  opt(funcs, globals, pages, tags, exceptions);
45
38
  if (Prefs.profileCompiler) console.log(`3. optimized in ${(performance.now() - t2).toFixed(2)}ms`);
46
39
 
47
- if (Prefs.optFuncs) logFuncs(funcs, globals, exceptions);
40
+ // if (Prefs.optFuncs) logFuncs(funcs, globals, exceptions);
48
41
 
49
42
  const t3 = performance.now();
50
43
  const wasm = assemble(funcs, globals, tags, pages, data, flags);
51
44
  if (Prefs.profileCompiler) console.log(`4. assembled in ${(performance.now() - t3).toFixed(2)}ms`);
52
45
 
46
+ if (Prefs.optFuncs) logFuncs(funcs, globals, exceptions);
47
+
53
48
  if (Prefs.allocLog) {
54
49
  const wasmPages = Math.ceil((pages.size * pageSize) / 65536);
55
50
  const bytes = wasmPages * 65536;
@@ -89,7 +84,8 @@ export default (code, flags) => {
89
84
  else compiler = [ compiler ];
90
85
 
91
86
  const tmpfile = 'porffor_tmp.c';
92
- const args = [ ...compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native', '-s', '-ffast-math', '-fno-exceptions' ];
87
+ const args = [ ...compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO ];
88
+ if (!Prefs.compiler) args.push('-flto=thin', '-march=native', '-s', '-ffast-math', '-fno-exceptions', '-fno-ident', '-fno-asynchronous-unwind-tables', '-ffunction-sections', '-fdata-sections', '-Wl,--gc-sections');
93
89
 
94
90
  const c = toc(out);
95
91
  fs.writeFileSync(tmpfile, c);
package/compiler/opt.js CHANGED
@@ -108,7 +108,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
108
108
  for (const f of funcs) {
109
109
  const wasm = f.wasm;
110
110
 
111
- const lastType = f.locals['#last_type'];
111
+ const lastType = f.locals['#last_type']?.idx;
112
112
 
113
113
  let runs = (+Prefs.optWasmRuns) || 2; // todo: how many by default?
114
114
  while (runs > 0) {
@@ -248,7 +248,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
248
248
  }
249
249
 
250
250
  // remove setting last type if it is never gotten
251
- if (!f.gotLastType && inst[0] === Opcodes.local_set && inst[1] === lastType?.idx) {
251
+ if (!f.gotLastType && inst[0] === Opcodes.local_set && inst[1] === lastType) {
252
252
  // replace this inst with drop
253
253
  wasm.splice(i, 1, [ Opcodes.drop ]); // remove this and last inst
254
254
  if (i > 0) i--;
package/compiler/parse.js CHANGED
@@ -7,10 +7,10 @@ if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
7
7
  globalThis.process = { argv: ['', '', ...Deno.args], stdout: { write: str => Deno.writeAllSync(Deno.stdout, textEncoder.encode(str)) } };
8
8
  }
9
9
 
10
- const file = process.argv.slice(2).find(x => x[0] !== '-');
10
+ const file = process.argv.slice(2).find(x => x[0] !== '-' && !['run', 'wasm', 'native', 'c', 'profile', 'debug', 'debug-wasm'].includes(x));
11
11
 
12
12
  // should we try to support types (while parsing)
13
- const types = Prefs.parseTypes || file?.endsWith('.ts');
13
+ const types = Prefs.parseTypes || Prefs.t || file?.endsWith('.ts');
14
14
  globalThis.typedInput = types && Prefs.optTypes;
15
15
 
16
16
  // todo: review which to use by default
@@ -18,9 +18,9 @@ const compile = async (file, [ _funcs, _globals ]) => {
18
18
  first = source.slice(0, source.indexOf('\n'));
19
19
  }
20
20
 
21
- let args = ['--bytestring', '--todo-time=compile', '--no-aot-pointer-opt', '--no-treeshake-wasm-imports', '--no-rm-unused-types', '--scoped-page-names', '--funsafe-no-unlikely-proto-checks', '--parse-types', '--opt-types'];
21
+ let args = ['--bytestring', '--todo-time=compile', '--truthy=no_nan_negative', '--no-treeshake-wasm-imports', '--no-rm-unused-types', '--scoped-page-names', '--funsafe-no-unlikely-proto-checks', '--fast-length', '--parse-types', '--opt-types'];
22
22
  if (first.startsWith('// @porf')) {
23
- args = args.concat(first.slice('// @porf '.length).split(' '));
23
+ args = first.slice('// @porf '.length).split(' ').concat(args);
24
24
  }
25
25
  process.argv = argv.concat(args);
26
26
 
@@ -111,8 +111,9 @@ ${funcs.map(x => {
111
111
  ${x.returnType != null ? `returnType: ${JSON.stringify(x.returnType)}` : 'typedReturns: true'},
112
112
  locals: ${JSON.stringify(Object.values(x.locals).slice(x.params.length).map(x => x.type))},
113
113
  localNames: ${JSON.stringify(Object.keys(x.locals))},
114
- ${x.data && x.data.length > 0 ? ` data: ${JSON.stringify(x.data)}` : ''}
115
- };`.replaceAll('\n\n', '\n').replaceAll('\n\n', '\n')
114
+ ${x.data && x.data.length > 0 ? ` data: ${JSON.stringify(x.data)},` : ''}
115
+ ${x.table ? ` table: true` : ''}
116
+ };`.replaceAll('\n\n', '\n').replaceAll('\n\n', '\n').replaceAll('\n\n', '\n');
116
117
  }).join('\n')}
117
118
  };`;
118
119
  };
package/compiler/prefs.js CHANGED
@@ -1,4 +1,4 @@
1
- const onByDefault = [ 'bytestring', 'aotPointerOpt', 'treeshakeWasmImports', 'alwaysMemory' ];
1
+ const onByDefault = [ 'bytestring', 'treeshakeWasmImports', 'alwaysMemory', 'indirectCalls', 'optUnused' ];
2
2
 
3
3
  let cache = {};
4
4
  const obj = new Proxy({}, {
@@ -6,7 +6,7 @@ const obj = new Proxy({}, {
6
6
  // intentionally misses with undefined values cached
7
7
  if (cache[p]) return cache[p];
8
8
 
9
- return cache[p] = (() => {
9
+ const ret = (() => {
10
10
  // fooBar -> foo-bar
11
11
  const name = p[0] === '_' ? p : p.replace(/[A-Z]/g, c => `-${c.toLowerCase()}`);
12
12
  const prefix = name.length === 1 ? '-' : '--';
@@ -19,6 +19,10 @@ const obj = new Proxy({}, {
19
19
  if (onByDefault.includes(p)) return true;
20
20
  return undefined;
21
21
  })();
22
+
23
+ // do not cache in web demo as args are changed live
24
+ if (!globalThis.document) cache[p] = ret;
25
+ return ret;
22
26
  }
23
27
  });
24
28
 
@@ -16,7 +16,7 @@ export const PrototypeFuncs = function() {
16
16
 
17
17
  this[TYPES.array] = {
18
18
  // lX = local accessor of X ({ get, set }), iX = local index of X, wX = wasm ops of X
19
- at: (pointer, length, wIndex, iTmp) => [
19
+ at: (pointer, length, wIndex, wType, iTmp) => [
20
20
  ...wIndex,
21
21
  Opcodes.i32_to,
22
22
  [ Opcodes.local_tee, iTmp ],
@@ -47,31 +47,39 @@ export const PrototypeFuncs = function() {
47
47
  [ Opcodes.end ],
48
48
 
49
49
  [ Opcodes.local_get, iTmp ],
50
- ...number(ValtypeSize[valtype], Valtype.i32),
50
+ ...number(ValtypeSize[valtype] + 1, Valtype.i32),
51
51
  [ Opcodes.i32_mul ],
52
-
53
52
  ...pointer,
54
53
  [ Opcodes.i32_add ],
54
+ [ Opcodes.local_set, iTmp ],
55
55
 
56
56
  // read from memory
57
- [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(ValtypeSize.i32) ]
57
+ [ Opcodes.local_get, iTmp ],
58
+ [ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
59
+
60
+ [ Opcodes.local_get, iTmp ],
61
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ]
58
62
  ],
59
63
 
60
64
  // todo: only for 1 argument
61
- push: (pointer, length, wNewMember, _1, _2, _3, unusedValue) => [
65
+ push: (pointer, length, wNewMember, wType, iTmp, _2, _3, unusedValue) => [
62
66
  // get memory offset of array at last index (length)
63
67
  ...length.getCachedI32(),
64
- ...number(ValtypeSize[valtype], Valtype.i32),
68
+ ...number(ValtypeSize[valtype] + 1, Valtype.i32),
65
69
  [ Opcodes.i32_mul ],
66
-
67
70
  ...pointer,
68
71
  [ Opcodes.i32_add ],
72
+ [ Opcodes.local_set, iTmp ],
69
73
 
70
- // generated wasm for new member
74
+ // store value
75
+ [ Opcodes.local_get, iTmp ],
71
76
  ...wNewMember,
77
+ [ Opcodes.store, 0, ...unsignedLEB128(ValtypeSize.i32) ],
72
78
 
73
- // store in memory
74
- [ Opcodes.store, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
79
+ // store type
80
+ [ Opcodes.local_get, iTmp ],
81
+ ...wType,
82
+ [ Opcodes.i32_store8, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
75
83
 
76
84
  // bump array length by 1 and return it
77
85
  ...length.setI32([
@@ -93,7 +101,7 @@ export const PrototypeFuncs = function() {
93
101
  // ...length.get()
94
102
  ],
95
103
 
96
- pop: (pointer, length, _1, _2, _3, _4, unusedValue) => [
104
+ pop: (pointer, length, _1, _2, iTmp, _3, _4, unusedValue) => [
97
105
  // if length == 0, noop
98
106
  ...length.getCachedI32(),
99
107
  [ Opcodes.i32_eqz ],
@@ -121,13 +129,18 @@ export const PrototypeFuncs = function() {
121
129
  // load last element
122
130
  ...(unusedValue() ? [] : [
123
131
  ...length.getCachedI32(),
124
- ...number(ValtypeSize[valtype], Valtype.i32),
132
+ ...number(ValtypeSize[valtype] + 1, Valtype.i32),
125
133
  [ Opcodes.i32_mul ],
126
134
 
127
135
  ...pointer,
128
136
  [ Opcodes.i32_add ],
137
+ [ Opcodes.local_set, iTmp ],
129
138
 
130
- [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(ValtypeSize.i32) ]
139
+ [ Opcodes.local_get, iTmp ],
140
+ [ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
141
+
142
+ [ Opcodes.local_get, iTmp ],
143
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ]
131
144
  ])
132
145
  ],
133
146
 
@@ -153,8 +166,12 @@ export const PrototypeFuncs = function() {
153
166
  ]),
154
167
 
155
168
  // load first element
169
+ // todo/perf: unusedValue opt
156
170
  ...pointer,
157
- [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
171
+ [ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
172
+
173
+ ...pointer,
174
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
158
175
 
159
176
  // offset page by -1 ind
160
177
  // ...number(pointer + ValtypeSize.i32, Valtype.i32), // dst = base array index + length size
@@ -170,13 +187,13 @@ export const PrototypeFuncs = function() {
170
187
  [ Opcodes.i32_add ],
171
188
 
172
189
  // src = base array index + length size + an index
173
- ...number(ValtypeSize.i32 + ValtypeSize[valtype], Valtype.i32),
190
+ ...number(ValtypeSize.i32 + ValtypeSize[valtype] + 1, Valtype.i32),
174
191
  ...pointer,
175
192
  [ Opcodes.i32_add ],
176
193
 
177
194
  // size = new length * sizeof element
178
195
  ...length.getCachedI32(),
179
- ...number(ValtypeSize[valtype], Valtype.i32),
196
+ ...number(ValtypeSize[valtype] + 1, Valtype.i32),
180
197
  [ Opcodes.i32_mul ],
181
198
  [ ...Opcodes.memory_copy, 0x00, 0x00 ]
182
199
 
@@ -194,7 +211,7 @@ export const PrototypeFuncs = function() {
194
211
  // ]),
195
212
  ],
196
213
 
197
- fill: (pointer, length, wElement, iTmp) => [
214
+ fill: (pointer, length, wElement, wType, iTmp, iTmp2) => [
198
215
  ...wElement,
199
216
  [ Opcodes.local_set, iTmp ],
200
217
 
@@ -206,7 +223,7 @@ export const PrototypeFuncs = function() {
206
223
  [ Opcodes.i32_sub ],
207
224
 
208
225
  // * sizeof value
209
- ...number(ValtypeSize[valtype], Valtype.i32),
226
+ ...number(ValtypeSize[valtype] + 1, Valtype.i32),
210
227
  [ Opcodes.i32_mul ],
211
228
 
212
229
  ...length.setCachedI32(),
@@ -224,17 +241,25 @@ export const PrototypeFuncs = function() {
224
241
 
225
242
  [ Opcodes.loop, Blocktype.void ],
226
243
 
227
- // set element using pointer
244
+ // calculate offset
228
245
  ...length.getCachedI32(),
229
246
  ...pointer,
230
247
  [ Opcodes.i32_add ],
248
+ [ Opcodes.local_set, iTmp2 ],
231
249
 
250
+ // store value
251
+ [ Opcodes.local_get, iTmp2 ],
232
252
  [ Opcodes.local_get, iTmp ],
233
- [ Opcodes.store, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128( ValtypeSize.i32) ],
253
+ [ Opcodes.store, 0, ...unsignedLEB128(ValtypeSize.i32) ],
254
+
255
+ // store type
256
+ [ Opcodes.local_get, iTmp2 ],
257
+ ...wType,
258
+ [ Opcodes.i32_store8, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
234
259
 
235
260
  // pointer - sizeof value
236
261
  ...length.getCachedI32(),
237
- ...number(ValtypeSize[valtype], Valtype.i32),
262
+ ...number(ValtypeSize[valtype] + 1, Valtype.i32),
238
263
  [ Opcodes.i32_sub ],
239
264
 
240
265
  ...length.setCachedI32(),
@@ -254,12 +279,16 @@ export const PrototypeFuncs = function() {
254
279
  };
255
280
 
256
281
  this[TYPES.array].at.local = Valtype.i32;
282
+ this[TYPES.array].push.returnType = TYPES.number;
257
283
  this[TYPES.array].push.noArgRetLength = true;
284
+ this[TYPES.array].push.local = Valtype.i32;
285
+ this[TYPES.array].pop.local = Valtype.i32;
258
286
  this[TYPES.array].fill.local = valtypeBinary;
287
+ this[TYPES.array].fill.local2 = Valtype.i32;
259
288
  this[TYPES.array].fill.returnType = TYPES.array;
260
289
 
261
290
  this[TYPES.string] = {
262
- at: (pointer, length, wIndex, iTmp, _, arrayShell) => {
291
+ at: (pointer, length, wIndex, wType, iTmp, _, arrayShell) => {
263
292
  const [ newOut, newPointer ] = arrayShell(1, 'i16');
264
293
 
265
294
  return [
@@ -317,7 +346,7 @@ export const PrototypeFuncs = function() {
317
346
  },
318
347
 
319
348
  // todo: out of bounds properly
320
- charAt: (pointer, length, wIndex, _1, _2, arrayShell) => {
349
+ charAt: (pointer, length, wIndex, wType, _1, _2, arrayShell) => {
321
350
  const [ newOut, newPointer ] = arrayShell(1, 'i16');
322
351
 
323
352
  return [
@@ -347,127 +376,124 @@ export const PrototypeFuncs = function() {
347
376
  ];
348
377
  },
349
378
 
350
- charCodeAt: (pointer, length, wIndex, iTmp) => {
351
- return [
352
- ...wIndex,
353
- Opcodes.i32_to,
354
-
355
- ...(zeroChecks.charcodeat ? [] : [
356
- [ Opcodes.local_set, iTmp ],
379
+ charCodeAt: (pointer, length, wIndex, wType, iTmp) => [
380
+ ...wIndex,
381
+ Opcodes.i32_to,
357
382
 
358
- // index < 0
359
- ...(noUnlikelyChecks ? [] : [
360
- [ Opcodes.local_get, iTmp ],
361
- ...number(0, Valtype.i32),
362
- [ Opcodes.i32_lt_s ],
363
- ]),
383
+ ...(zeroChecks.charcodeat ? [] : [
384
+ [ Opcodes.local_set, iTmp ],
364
385
 
365
- // index >= length
386
+ // index < 0
387
+ ...(noUnlikelyChecks ? [] : [
366
388
  [ Opcodes.local_get, iTmp ],
367
- ...length.getCachedI32(),
368
- [ Opcodes.i32_ge_s ],
389
+ ...number(0, Valtype.i32),
390
+ [ Opcodes.i32_lt_s ],
391
+ ]),
369
392
 
370
- ...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
371
- [ Opcodes.if, Blocktype.void ],
372
- ...number(valtype === 'i32' ? -1 : NaN),
373
- [ Opcodes.br, 1 ],
374
- [ Opcodes.end ],
393
+ // index >= length
394
+ [ Opcodes.local_get, iTmp ],
395
+ ...length.getCachedI32(),
396
+ [ Opcodes.i32_ge_s ],
375
397
 
376
- [ Opcodes.local_get, iTmp ],
377
- ]),
398
+ ...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
399
+ [ Opcodes.if, Blocktype.void ],
400
+ ...number(valtype === 'i32' ? -1 : NaN),
401
+ [ Opcodes.br, 1 ],
402
+ [ Opcodes.end ],
378
403
 
379
- ...number(ValtypeSize.i16, Valtype.i32),
380
- [ Opcodes.i32_mul ],
404
+ [ Opcodes.local_get, iTmp ],
405
+ ]),
381
406
 
382
- ...pointer,
383
- [ Opcodes.i32_add ],
407
+ ...number(ValtypeSize.i16, Valtype.i32),
408
+ [ Opcodes.i32_mul ],
384
409
 
385
- // load current string ind {arg}
386
- [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
387
- Opcodes.i32_from_u
388
- ];
389
- },
410
+ ...pointer,
411
+ [ Opcodes.i32_add ],
390
412
 
391
- isWellFormed: (pointer, length, wIndex, iTmp, iTmp2) => {
392
- return [
393
- // note: we cannot presume it begins as 0 in case it was used previously
394
- ...pointer,
395
- [ Opcodes.local_set, iTmp ],
413
+ // load current string ind {arg}
414
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
415
+ Opcodes.i32_from_u
416
+ ],
396
417
 
397
- // use cached length as end pointer
398
- ...length.getCachedI32(),
399
- ...number(ValtypeSize.i16, Valtype.i32),
400
- [ Opcodes.i32_mul ],
401
- ...pointer,
402
- [ Opcodes.i32_add ],
403
- ...length.setCachedI32(),
418
+ isWellFormed: (pointer, length, _1, _2, iTmp, iTmp2) => [
419
+ // note: we cannot presume it begins as 0 in case it was used previously
420
+ ...pointer,
421
+ [ Opcodes.local_set, iTmp ],
422
+
423
+ // use cached length as end pointer
424
+ ...length.getCachedI32(),
425
+ ...number(ValtypeSize.i16, Valtype.i32),
426
+ [ Opcodes.i32_mul ],
427
+ ...pointer,
428
+ [ Opcodes.i32_add ],
429
+ ...length.setCachedI32(),
404
430
 
405
- [ Opcodes.loop, Blocktype.void ],
431
+ [ Opcodes.loop, Blocktype.void ],
406
432
 
407
- [ Opcodes.block, Blocktype.void ],
433
+ [ Opcodes.block, Blocktype.void ],
408
434
 
409
- [ Opcodes.local_get, iTmp ],
410
- [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
411
- [ Opcodes.local_set, iTmp2 ],
412
-
413
- // if not surrogate, continue
414
- [ Opcodes.local_get, iTmp2 ],
415
- ...number(0xF800, Valtype.i32),
416
- [ Opcodes.i32_and ],
417
- ...number(0xD800, Valtype.i32),
418
- [ Opcodes.i32_ne ],
419
- [ Opcodes.br_if, 0 ],
420
-
421
- // if not leading surrogate, return false
422
- [ Opcodes.local_get, iTmp2 ],
423
- ...number(0xDC00, Valtype.i32),
424
- [ Opcodes.i32_ge_s ],
425
- [ Opcodes.if, Blocktype.void ],
426
- ...number(0),
427
- [ Opcodes.br, 3 ],
428
- [ Opcodes.end ],
435
+ [ Opcodes.local_get, iTmp ],
436
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
437
+ [ Opcodes.local_set, iTmp2 ],
438
+
439
+ // if not surrogate, continue
440
+ [ Opcodes.local_get, iTmp2 ],
441
+ ...number(0xF800, Valtype.i32),
442
+ [ Opcodes.i32_and ],
443
+ ...number(0xD800, Valtype.i32),
444
+ [ Opcodes.i32_ne ],
445
+ [ Opcodes.br_if, 0 ],
429
446
 
430
- // if not followed by trailing surrogate, return false
431
- [ Opcodes.local_get, iTmp ],
432
- [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize.i16) ],
433
- ...number(0xFC00, Valtype.i32),
434
- [ Opcodes.i32_and ],
435
- ...number(0xDC00, Valtype.i32),
436
- [ Opcodes.i32_ne ],
437
- [ Opcodes.if, Blocktype.void ],
438
- ...number(0),
439
- [ Opcodes.br, 3 ],
440
- [ Opcodes.end ],
447
+ // if not leading surrogate, return false
448
+ [ Opcodes.local_get, iTmp2 ],
449
+ ...number(0xDC00, Valtype.i32),
450
+ [ Opcodes.i32_ge_s ],
451
+ [ Opcodes.if, Blocktype.void ],
452
+ ...number(0),
453
+ [ Opcodes.br, 3 ],
454
+ [ Opcodes.end ],
441
455
 
442
- // bump index again since gone through two valid chars
443
- [ Opcodes.local_get, iTmp ],
444
- ...number(ValtypeSize.i16, Valtype.i32),
445
- [ Opcodes.i32_add ],
446
- [ Opcodes.local_set, iTmp ],
456
+ // if not followed by trailing surrogate, return false
457
+ [ Opcodes.local_get, iTmp ],
458
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize.i16) ],
459
+ ...number(0xFC00, Valtype.i32),
460
+ [ Opcodes.i32_and ],
461
+ ...number(0xDC00, Valtype.i32),
462
+ [ Opcodes.i32_ne ],
463
+ [ Opcodes.if, Blocktype.void ],
464
+ ...number(0),
465
+ [ Opcodes.br, 3 ],
466
+ [ Opcodes.end ],
447
467
 
448
- [ Opcodes.end ],
468
+ // bump index again since gone through two valid chars
469
+ [ Opcodes.local_get, iTmp ],
470
+ ...number(ValtypeSize.i16, Valtype.i32),
471
+ [ Opcodes.i32_add ],
472
+ [ Opcodes.local_set, iTmp ],
449
473
 
450
- // bump pointer and loop if not at the end
451
- [ Opcodes.local_get, iTmp ],
452
- ...number(ValtypeSize.i16, Valtype.i32),
453
- [ Opcodes.i32_add ],
454
- [ Opcodes.local_tee, iTmp ],
474
+ [ Opcodes.end ],
455
475
 
456
- ...length.getCachedI32(), // end pointer
457
- [ Opcodes.i32_ne ],
458
- [ Opcodes.br_if, 0 ],
476
+ // bump pointer and loop if not at the end
477
+ [ Opcodes.local_get, iTmp ],
478
+ ...number(ValtypeSize.i16, Valtype.i32),
479
+ [ Opcodes.i32_add ],
480
+ [ Opcodes.local_tee, iTmp ],
459
481
 
460
- [ Opcodes.end ],
482
+ ...length.getCachedI32(), // end pointer
483
+ [ Opcodes.i32_ne ],
484
+ [ Opcodes.br_if, 0 ],
461
485
 
462
- // return true
463
- ...number(1)
464
- ]
465
- }
486
+ [ Opcodes.end ],
487
+
488
+ // return true
489
+ ...number(1)
490
+ ]
466
491
  };
467
492
 
468
493
  this[TYPES.string].at.local = Valtype.i32;
469
494
  this[TYPES.string].at.returnType = TYPES.string;
470
495
  this[TYPES.string].charAt.returnType = TYPES.string;
496
+ this[TYPES.string].charCodeAt.returnType = TYPES.number;
471
497
  this[TYPES.string].charCodeAt.local = Valtype.i32;
472
498
  this[TYPES.string].charCodeAt.noPointerCache = zeroChecks.charcodeat;
473
499
 
@@ -477,7 +503,7 @@ export const PrototypeFuncs = function() {
477
503
 
478
504
  if (Prefs.bytestring) {
479
505
  this[TYPES.bytestring] = {
480
- at: (pointer, length, wIndex, iTmp, _, arrayShell) => {
506
+ at: (pointer, length, wIndex, wType, iTmp, _, arrayShell) => {
481
507
  const [ newOut, newPointer ] = arrayShell(1, 'i8');
482
508
 
483
509
  return [
@@ -533,7 +559,7 @@ export const PrototypeFuncs = function() {
533
559
  },
534
560
 
535
561
  // todo: out of bounds properly
536
- charAt: (pointer, length, wIndex, _1, _2, arrayShell) => {
562
+ charAt: (pointer, length, wIndex, wType, _1, _2, arrayShell) => {
537
563
  const [ newOut, newPointer ] = arrayShell(1, 'i8');
538
564
 
539
565
  return [
@@ -560,55 +586,52 @@ export const PrototypeFuncs = function() {
560
586
  ];
561
587
  },
562
588
 
563
- charCodeAt: (pointer, length, wIndex, iTmp) => {
564
- return [
565
- ...wIndex,
566
- Opcodes.i32_to,
567
-
568
- ...(zeroChecks.charcodeat ? [] : [
569
- [ Opcodes.local_set, iTmp ],
589
+ charCodeAt: (pointer, length, wIndex, wType, iTmp) => [
590
+ ...wIndex,
591
+ Opcodes.i32_to,
570
592
 
571
- // index < 0
572
- ...(noUnlikelyChecks ? [] : [
573
- [ Opcodes.local_get, iTmp ],
574
- ...number(0, Valtype.i32),
575
- [ Opcodes.i32_lt_s ],
576
- ]),
593
+ ...(zeroChecks.charcodeat ? [] : [
594
+ [ Opcodes.local_set, iTmp ],
577
595
 
578
- // index >= length
596
+ // index < 0
597
+ ...(noUnlikelyChecks ? [] : [
579
598
  [ Opcodes.local_get, iTmp ],
580
- ...length.getCachedI32(),
581
- [ Opcodes.i32_ge_s ],
599
+ ...number(0, Valtype.i32),
600
+ [ Opcodes.i32_lt_s ],
601
+ ]),
582
602
 
583
- ...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
584
- [ Opcodes.if, Blocktype.void ],
585
- ...number(valtype === 'i32' ? -1 : NaN),
586
- [ Opcodes.br, 1 ],
587
- [ Opcodes.end ],
603
+ // index >= length
604
+ [ Opcodes.local_get, iTmp ],
605
+ ...length.getCachedI32(),
606
+ [ Opcodes.i32_ge_s ],
588
607
 
589
- [ Opcodes.local_get, iTmp ],
590
- ]),
608
+ ...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
609
+ [ Opcodes.if, Blocktype.void ],
610
+ ...number(valtype === 'i32' ? -1 : NaN),
611
+ [ Opcodes.br, 1 ],
612
+ [ Opcodes.end ],
591
613
 
592
- ...pointer,
593
- [ Opcodes.i32_add ],
614
+ [ Opcodes.local_get, iTmp ],
615
+ ]),
594
616
 
595
- // load current string ind {arg}
596
- [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
597
- Opcodes.i32_from_u
598
- ];
599
- },
617
+ ...pointer,
618
+ [ Opcodes.i32_add ],
600
619
 
601
- isWellFormed: () => {
602
- return [
603
- // we know it must be true as it is a bytestring lol
604
- ...number(1)
605
- ]
606
- }
620
+ // load current string ind {arg}
621
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
622
+ Opcodes.i32_from_u
623
+ ],
624
+
625
+ isWellFormed: () => [
626
+ // we know it must be true as it is a bytestring lol
627
+ ...number(1)
628
+ ]
607
629
  };
608
630
 
609
631
  this[TYPES.bytestring].at.local = Valtype.i32;
610
632
  this[TYPES.bytestring].at.returnType = TYPES.bytestring;
611
633
  this[TYPES.bytestring].charAt.returnType = TYPES.bytestring;
634
+ this[TYPES.bytestring].charCodeAt.returnType = TYPES.number;
612
635
  this[TYPES.bytestring].charCodeAt.local = Valtype.i32;
613
636
  this[TYPES.bytestring].charCodeAt.noPointerCache = zeroChecks.charcodeat;
614
637