porffor 0.2.0-1afe9b8 → 0.2.0-22b3092
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/.vscode/launch.json +18 -0
- package/LICENSE +20 -20
- package/README.md +121 -83
- package/asur/README.md +2 -0
- package/asur/index.js +1262 -0
- package/byg/index.js +237 -0
- package/compiler/2c.js +317 -72
- package/compiler/{sections.js → assemble.js} +62 -14
- package/compiler/builtins/annexb_string.js +72 -0
- package/compiler/builtins/annexb_string.ts +19 -0
- package/compiler/builtins/array.ts +145 -0
- package/compiler/builtins/base64.ts +151 -0
- package/compiler/builtins/crypto.ts +120 -0
- package/compiler/builtins/escape.ts +141 -0
- package/compiler/builtins/int.ts +147 -0
- package/compiler/builtins/number.ts +527 -0
- package/compiler/builtins/porffor.d.ts +42 -0
- package/compiler/builtins/string.ts +1055 -0
- package/compiler/builtins/tostring.ts +45 -0
- package/compiler/builtins.js +445 -249
- package/compiler/{codeGen.js → codegen.js} +853 -334
- package/compiler/embedding.js +22 -22
- package/compiler/encoding.js +108 -10
- package/compiler/generated_builtins.js +695 -0
- package/compiler/index.js +36 -34
- package/compiler/log.js +6 -3
- package/compiler/opt.js +50 -36
- package/compiler/parse.js +35 -27
- package/compiler/precompile.js +123 -0
- package/compiler/prefs.js +26 -0
- package/compiler/prototype.js +13 -28
- package/compiler/types.js +37 -0
- package/compiler/wasmSpec.js +27 -8
- package/compiler/wrap.js +46 -44
- package/package.json +9 -5
- package/porf +4 -0
- package/rhemyn/compile.js +5 -3
- package/rhemyn/parse.js +323 -320
- package/rhemyn/test/parse.js +58 -58
- package/runner/compare.js +34 -34
- package/runner/debug.js +122 -0
- package/runner/index.js +49 -10
- package/runner/profiler.js +102 -0
- package/runner/repl.js +40 -7
- package/runner/sizes.js +37 -37
- package/test262_changes_from_1afe9b87d2_to_04-09.md +270 -0
- package/compiler/builtins/base64.js +0 -92
- package/r.js +0 -4
- package/runner/info.js +0 -89
- package/runner/profile.js +0 -46
- package/runner/results.json +0 -1
- package/runner/transform.js +0 -15
- package/util/enum.js +0 -20
@@ -7,6 +7,8 @@ import { number, i32x4, enforceOneByte, enforceTwoBytes, enforceFourBytes, enfor
|
|
7
7
|
import { log } from "./log.js";
|
8
8
|
import parse from "./parse.js";
|
9
9
|
import * as Rhemyn from "../rhemyn/compile.js";
|
10
|
+
import Prefs from './prefs.js';
|
11
|
+
import { TYPES, TYPE_NAMES } from './types.js';
|
10
12
|
|
11
13
|
let globals = {};
|
12
14
|
let globalInd = 0;
|
@@ -23,35 +25,37 @@ const debug = str => {
|
|
23
25
|
const logChar = n => {
|
24
26
|
code.push(...number(n));
|
25
27
|
|
26
|
-
code.push(Opcodes.call);
|
27
|
-
code.push(...unsignedLEB128(0));
|
28
|
+
code.push([ Opcodes.call, 0 ]);
|
28
29
|
};
|
29
30
|
|
30
31
|
for (let i = 0; i < str.length; i++) {
|
31
32
|
logChar(str.charCodeAt(i));
|
32
33
|
}
|
33
34
|
|
34
|
-
logChar(
|
35
|
+
logChar(10); // new line
|
35
36
|
|
36
37
|
return code;
|
37
38
|
};
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
this.name = 'TodoError';
|
44
|
-
}
|
40
|
+
class TodoError extends Error {
|
41
|
+
constructor(message) {
|
42
|
+
super(message);
|
43
|
+
this.name = 'TodoError';
|
45
44
|
}
|
45
|
+
}
|
46
|
+
const todo = (scope, msg, expectsValue = undefined) => {
|
47
|
+
switch (Prefs.todoTime ?? 'runtime') {
|
48
|
+
case 'compile':
|
49
|
+
throw new TodoError(msg);
|
46
50
|
|
47
|
-
|
48
|
-
|
49
|
-
const code = [];
|
50
|
-
|
51
|
-
code.push(...debug(`todo! ` + msg));
|
52
|
-
code.push(Opcodes.unreachable);
|
51
|
+
case 'runtime':
|
52
|
+
return internalThrow(scope, 'TodoError', msg, expectsValue);
|
53
53
|
|
54
|
-
|
54
|
+
// return [
|
55
|
+
// ...debug(`todo! ${msg}`),
|
56
|
+
// [ Opcodes.unreachable ]
|
57
|
+
// ];
|
58
|
+
}
|
55
59
|
};
|
56
60
|
|
57
61
|
const isFuncType = type => type === 'FunctionDeclaration' || type === 'FunctionExpression' || type === 'ArrowFunctionExpression';
|
@@ -104,7 +108,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
104
108
|
return generateUnary(scope, decl);
|
105
109
|
|
106
110
|
case 'UpdateExpression':
|
107
|
-
return generateUpdate(scope, decl);
|
111
|
+
return generateUpdate(scope, decl, global, name, valueUnused);
|
108
112
|
|
109
113
|
case 'IfStatement':
|
110
114
|
return generateIf(scope, decl);
|
@@ -115,6 +119,9 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
115
119
|
case 'WhileStatement':
|
116
120
|
return generateWhile(scope, decl);
|
117
121
|
|
122
|
+
case 'DoWhileStatement':
|
123
|
+
return generateDoWhile(scope, decl);
|
124
|
+
|
118
125
|
case 'ForOfStatement':
|
119
126
|
return generateForOf(scope, decl);
|
120
127
|
|
@@ -124,6 +131,9 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
124
131
|
case 'ContinueStatement':
|
125
132
|
return generateContinue(scope, decl);
|
126
133
|
|
134
|
+
case 'LabeledStatement':
|
135
|
+
return generateLabel(scope, decl);
|
136
|
+
|
127
137
|
case 'EmptyStatement':
|
128
138
|
return generateEmpty(scope, decl);
|
129
139
|
|
@@ -137,7 +147,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
137
147
|
return generateTry(scope, decl);
|
138
148
|
|
139
149
|
case 'DebuggerStatement':
|
140
|
-
// todo:
|
150
|
+
// todo: hook into terminal debugger
|
141
151
|
return [];
|
142
152
|
|
143
153
|
case 'ArrayExpression':
|
@@ -151,16 +161,22 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
151
161
|
const funcsBefore = funcs.length;
|
152
162
|
generate(scope, decl.declaration);
|
153
163
|
|
154
|
-
if (funcsBefore
|
164
|
+
if (funcsBefore !== funcs.length) {
|
165
|
+
// new func added
|
166
|
+
const newFunc = funcs[funcs.length - 1];
|
167
|
+
newFunc.export = true;
|
168
|
+
}
|
169
|
+
|
170
|
+
// if (funcsBefore === funcs.length) throw new Error('no new func added in export');
|
155
171
|
|
156
|
-
const newFunc = funcs[funcs.length - 1];
|
157
|
-
newFunc.export = true;
|
172
|
+
// const newFunc = funcs[funcs.length - 1];
|
173
|
+
// newFunc.export = true;
|
158
174
|
|
159
175
|
return [];
|
160
176
|
|
161
177
|
case 'TaggedTemplateExpression': {
|
162
178
|
const funcs = {
|
163
|
-
|
179
|
+
__Porffor_wasm: str => {
|
164
180
|
let out = [];
|
165
181
|
|
166
182
|
for (const line of str.split('\n')) {
|
@@ -168,8 +184,8 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
168
184
|
if (asm[0] === '') continue; // blank
|
169
185
|
|
170
186
|
if (asm[0] === 'local') {
|
171
|
-
const [ name,
|
172
|
-
scope.locals[name] = { idx:
|
187
|
+
const [ name, type ] = asm.slice(1);
|
188
|
+
scope.locals[name] = { idx: scope.localInd++, type: Valtype[type] };
|
173
189
|
continue;
|
174
190
|
}
|
175
191
|
|
@@ -179,7 +195,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
179
195
|
}
|
180
196
|
|
181
197
|
if (asm[0] === 'memory') {
|
182
|
-
allocPage('asm instrinsic');
|
198
|
+
allocPage(scope, 'asm instrinsic');
|
183
199
|
// todo: add to store/load offset insts
|
184
200
|
continue;
|
185
201
|
}
|
@@ -188,30 +204,65 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
188
204
|
if (!inst) throw new Error(`inline asm: inst ${asm[0]} not found`);
|
189
205
|
|
190
206
|
if (!Array.isArray(inst)) inst = [ inst ];
|
191
|
-
const immediates = asm.slice(1).map(x =>
|
207
|
+
const immediates = asm.slice(1).map(x => {
|
208
|
+
const int = parseInt(x);
|
209
|
+
if (Number.isNaN(int)) return scope.locals[x]?.idx;
|
210
|
+
return int;
|
211
|
+
});
|
192
212
|
|
193
213
|
out.push([ ...inst, ...immediates ]);
|
194
214
|
}
|
195
215
|
|
196
216
|
return out;
|
197
|
-
}
|
198
|
-
|
217
|
+
},
|
218
|
+
|
219
|
+
__Porffor_bs: str => [
|
220
|
+
...makeString(scope, str, global, name, true),
|
221
|
+
|
222
|
+
...(name ? setType(scope, name, TYPES._bytestring) : [
|
223
|
+
...number(TYPES._bytestring, Valtype.i32),
|
224
|
+
...setLastType(scope)
|
225
|
+
])
|
226
|
+
],
|
227
|
+
__Porffor_s: str => [
|
228
|
+
...makeString(scope, str, global, name, false),
|
199
229
|
|
200
|
-
|
230
|
+
...(name ? setType(scope, name, TYPES.string) : [
|
231
|
+
...number(TYPES.string, Valtype.i32),
|
232
|
+
...setLastType(scope)
|
233
|
+
])
|
234
|
+
],
|
235
|
+
};
|
236
|
+
|
237
|
+
const func = decl.tag.name;
|
201
238
|
// hack for inline asm
|
202
|
-
if (!funcs[
|
239
|
+
if (!funcs[func]) return todo(scope, 'tagged template expressions not implemented', true);
|
240
|
+
|
241
|
+
const { quasis, expressions } = decl.quasi;
|
242
|
+
let str = quasis[0].value.raw;
|
203
243
|
|
204
|
-
|
205
|
-
|
244
|
+
for (let i = 0; i < expressions.length; i++) {
|
245
|
+
const e = expressions[i];
|
246
|
+
if (!e.name) {
|
247
|
+
if (e.type === 'BinaryExpression' && e.operator === '+' && e.left.type === 'Identifier' && e.right.type === 'Literal') {
|
248
|
+
str += lookupName(scope, e.left.name)[0].idx + e.right.value;
|
249
|
+
} else todo(scope, 'unsupported expression in intrinsic');
|
250
|
+
} else str += lookupName(scope, e.name)[0].idx;
|
251
|
+
|
252
|
+
str += quasis[i + 1].value.raw;
|
253
|
+
}
|
254
|
+
|
255
|
+
return funcs[func](str);
|
206
256
|
}
|
207
257
|
|
208
258
|
default:
|
209
|
-
|
210
|
-
|
259
|
+
// ignore typescript nodes
|
260
|
+
if (decl.type.startsWith('TS') ||
|
261
|
+
decl.type === 'ImportDeclaration' && decl.importKind === 'type') {
|
211
262
|
return [];
|
212
263
|
}
|
213
264
|
|
214
|
-
return todo(`no generation for ${decl.type}!`);
|
265
|
+
return todo(scope, `no generation for ${decl.type}!`);
|
215
266
|
}
|
216
267
|
};
|
217
268
|
|
@@ -239,7 +290,7 @@ const lookupName = (scope, _name) => {
|
|
239
290
|
return [ undefined, undefined ];
|
240
291
|
};
|
241
292
|
|
242
|
-
const internalThrow = (scope, constructor, message, expectsValue =
|
293
|
+
const internalThrow = (scope, constructor, message, expectsValue = Prefs.alwaysValueInternalThrows) => [
|
243
294
|
...generateThrow(scope, {
|
244
295
|
argument: {
|
245
296
|
type: 'NewExpression',
|
@@ -263,7 +314,10 @@ const generateIdent = (scope, decl) => {
|
|
263
314
|
|
264
315
|
if (Object.hasOwn(builtinVars, name)) {
|
265
316
|
if (builtinVars[name].floatOnly && valtype[0] === 'i') throw new Error(`Cannot use ${unhackName(name)} with integer valtype`);
|
266
|
-
|
317
|
+
|
318
|
+
let wasm = builtinVars[name];
|
319
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, { name });
|
320
|
+
return wasm.slice();
|
267
321
|
}
|
268
322
|
|
269
323
|
if (Object.hasOwn(builtinFuncs, name) || Object.hasOwn(internalConstrs, name)) {
|
@@ -271,6 +325,11 @@ const generateIdent = (scope, decl) => {
|
|
271
325
|
return number(1);
|
272
326
|
}
|
273
327
|
|
328
|
+
if (isExistingProtoFunc(name)) {
|
329
|
+
// todo: return an actual something
|
330
|
+
return number(1);
|
331
|
+
}
|
332
|
+
|
274
333
|
if (local?.idx === undefined) {
|
275
334
|
// no local var with name
|
276
335
|
if (Object.hasOwn(importedFuncs, name)) return number(importedFuncs[name]);
|
@@ -301,14 +360,18 @@ const generateReturn = (scope, decl) => {
|
|
301
360
|
// just bare "return"
|
302
361
|
return [
|
303
362
|
...number(UNDEFINED), // "undefined" if func returns
|
304
|
-
...
|
363
|
+
...(scope.returnType != null ? [] : [
|
364
|
+
...number(TYPES.undefined, Valtype.i32) // type undefined
|
365
|
+
]),
|
305
366
|
[ Opcodes.return ]
|
306
367
|
];
|
307
368
|
}
|
308
369
|
|
309
370
|
return [
|
310
371
|
...generate(scope, decl.argument),
|
311
|
-
...
|
372
|
+
...(scope.returnType != null ? [] : [
|
373
|
+
...getNodeType(scope, decl.argument)
|
374
|
+
]),
|
312
375
|
[ Opcodes.return ]
|
313
376
|
];
|
314
377
|
};
|
@@ -322,7 +385,8 @@ const localTmp = (scope, name, type = valtypeBinary) => {
|
|
322
385
|
return idx;
|
323
386
|
};
|
324
387
|
|
325
|
-
const isIntOp = op => op && (op[0] >=
|
388
|
+
const isIntOp = op => op && ((op[0] >= 0x45 && op[0] <= 0x4f) || (op[0] >= 0x67 && op[0] <= 0x78) || op[0] === 0x41);
|
389
|
+
const isFloatToIntOp = op => op && (op[0] >= 0xb7 && op[0] <= 0xba);
|
326
390
|
|
327
391
|
const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
328
392
|
const checks = {
|
@@ -331,7 +395,7 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
331
395
|
'??': nullish
|
332
396
|
};
|
333
397
|
|
334
|
-
if (!checks[op]) return todo(`logic operator ${op} not implemented yet
|
398
|
+
if (!checks[op]) return todo(scope, `logic operator ${op} not implemented yet`, true);
|
335
399
|
|
336
400
|
// generic structure for {a} OP {b}
|
337
401
|
// -->
|
@@ -339,8 +403,8 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
339
403
|
|
340
404
|
// if we can, use int tmp and convert at the end to help prevent unneeded conversions
|
341
405
|
// (like if we are in an if condition - very common)
|
342
|
-
const leftIsInt =
|
343
|
-
const rightIsInt =
|
406
|
+
const leftIsInt = isFloatToIntOp(left[left.length - 1]);
|
407
|
+
const rightIsInt = isFloatToIntOp(right[right.length - 1]);
|
344
408
|
|
345
409
|
const canInt = leftIsInt && rightIsInt;
|
346
410
|
|
@@ -357,12 +421,12 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
357
421
|
...right,
|
358
422
|
// note type
|
359
423
|
...rightType,
|
360
|
-
setLastType(scope),
|
424
|
+
...setLastType(scope),
|
361
425
|
[ Opcodes.else ],
|
362
426
|
[ Opcodes.local_get, localTmp(scope, 'logictmpi', Valtype.i32) ],
|
363
427
|
// note type
|
364
428
|
...leftType,
|
365
|
-
setLastType(scope),
|
429
|
+
...setLastType(scope),
|
366
430
|
[ Opcodes.end ],
|
367
431
|
Opcodes.i32_from
|
368
432
|
];
|
@@ -376,17 +440,17 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
376
440
|
...right,
|
377
441
|
// note type
|
378
442
|
...rightType,
|
379
|
-
setLastType(scope),
|
443
|
+
...setLastType(scope),
|
380
444
|
[ Opcodes.else ],
|
381
445
|
[ Opcodes.local_get, localTmp(scope, 'logictmp') ],
|
382
446
|
// note type
|
383
447
|
...leftType,
|
384
|
-
setLastType(scope),
|
448
|
+
...setLastType(scope),
|
385
449
|
[ Opcodes.end ]
|
386
450
|
];
|
387
451
|
};
|
388
452
|
|
389
|
-
const concatStrings = (scope, left, right, global, name, assign) => {
|
453
|
+
const concatStrings = (scope, left, right, global, name, assign = false, bytestrings = false) => {
|
390
454
|
// todo: this should be rewritten into a built-in/func: String.prototype.concat
|
391
455
|
// todo: convert left and right to strings if not
|
392
456
|
// todo: optimize by looking up names in arrays and using that if exists?
|
@@ -396,11 +460,8 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
396
460
|
const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
|
397
461
|
const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
|
398
462
|
|
399
|
-
const aotWFA = process.argv.includes('-aot-well-formed-string-approximation');
|
400
|
-
if (aotWFA) addVarMeta(name, { wellFormed: undefined });
|
401
|
-
|
402
463
|
if (assign) {
|
403
|
-
const pointer = arrays
|
464
|
+
const pointer = scope.arrays?.get(name ?? '$undeclared');
|
404
465
|
|
405
466
|
return [
|
406
467
|
// setup right
|
@@ -425,11 +486,11 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
425
486
|
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ],
|
426
487
|
|
427
488
|
// copy right
|
428
|
-
// dst = out pointer + length size + current length *
|
489
|
+
// dst = out pointer + length size + current length * sizeof valtype
|
429
490
|
...number(pointer + ValtypeSize.i32, Valtype.i32),
|
430
491
|
|
431
492
|
[ Opcodes.local_get, leftLength ],
|
432
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
493
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
433
494
|
[ Opcodes.i32_mul ],
|
434
495
|
[ Opcodes.i32_add ],
|
435
496
|
|
@@ -438,9 +499,9 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
438
499
|
...number(ValtypeSize.i32, Valtype.i32),
|
439
500
|
[ Opcodes.i32_add ],
|
440
501
|
|
441
|
-
// size = right length *
|
502
|
+
// size = right length * sizeof valtype
|
442
503
|
[ Opcodes.local_get, rightLength ],
|
443
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
504
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
444
505
|
[ Opcodes.i32_mul ],
|
445
506
|
|
446
507
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
@@ -498,11 +559,11 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
498
559
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
499
560
|
|
500
561
|
// copy right
|
501
|
-
// dst = out pointer + length size + left length *
|
562
|
+
// dst = out pointer + length size + left length * sizeof valtype
|
502
563
|
...number(pointer + ValtypeSize.i32, Valtype.i32),
|
503
564
|
|
504
565
|
[ Opcodes.local_get, leftLength ],
|
505
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
566
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
506
567
|
[ Opcodes.i32_mul ],
|
507
568
|
[ Opcodes.i32_add ],
|
508
569
|
|
@@ -511,9 +572,9 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
511
572
|
...number(ValtypeSize.i32, Valtype.i32),
|
512
573
|
[ Opcodes.i32_add ],
|
513
574
|
|
514
|
-
// size = right length *
|
575
|
+
// size = right length * sizeof valtype
|
515
576
|
[ Opcodes.local_get, rightLength ],
|
516
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
577
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
517
578
|
[ Opcodes.i32_mul ],
|
518
579
|
|
519
580
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
@@ -523,7 +584,7 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
523
584
|
];
|
524
585
|
};
|
525
586
|
|
526
|
-
const compareStrings = (scope, left, right) => {
|
587
|
+
const compareStrings = (scope, left, right, bytestrings = false) => {
|
527
588
|
// todo: this should be rewritten into a func
|
528
589
|
// todo: convert left and right to strings if not
|
529
590
|
// todo: optimize by looking up names in arrays and using that if exists?
|
@@ -532,7 +593,6 @@ const compareStrings = (scope, left, right) => {
|
|
532
593
|
const leftPointer = localTmp(scope, 'compare_left_pointer', Valtype.i32);
|
533
594
|
const leftLength = localTmp(scope, 'compare_left_length', Valtype.i32);
|
534
595
|
const rightPointer = localTmp(scope, 'compare_right_pointer', Valtype.i32);
|
535
|
-
const rightLength = localTmp(scope, 'compare_right_length', Valtype.i32);
|
536
596
|
|
537
597
|
const index = localTmp(scope, 'compare_index', Valtype.i32);
|
538
598
|
const indexEnd = localTmp(scope, 'compare_index_end', Valtype.i32);
|
@@ -560,7 +620,6 @@ const compareStrings = (scope, left, right) => {
|
|
560
620
|
|
561
621
|
[ Opcodes.local_get, rightPointer ],
|
562
622
|
[ Opcodes.i32_load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(0) ],
|
563
|
-
[ Opcodes.local_tee, rightLength ],
|
564
623
|
|
565
624
|
// fast path: check leftLength != rightLength
|
566
625
|
[ Opcodes.i32_ne ],
|
@@ -575,11 +634,13 @@ const compareStrings = (scope, left, right) => {
|
|
575
634
|
...number(0, Valtype.i32),
|
576
635
|
[ Opcodes.local_set, index ],
|
577
636
|
|
578
|
-
// setup index end as length * sizeof
|
637
|
+
// setup index end as length * sizeof valtype (1 for bytestring, 2 for string)
|
579
638
|
// we do this instead of having to do mul/div each iter for perf™
|
580
639
|
[ Opcodes.local_get, leftLength ],
|
581
|
-
...
|
582
|
-
|
640
|
+
...(bytestrings ? [] : [
|
641
|
+
...number(ValtypeSize.i16, Valtype.i32),
|
642
|
+
[ Opcodes.i32_mul ],
|
643
|
+
]),
|
583
644
|
[ Opcodes.local_set, indexEnd ],
|
584
645
|
|
585
646
|
// iterate over each char and check if eq
|
@@ -589,13 +650,17 @@ const compareStrings = (scope, left, right) => {
|
|
589
650
|
[ Opcodes.local_get, index ],
|
590
651
|
[ Opcodes.local_get, leftPointer ],
|
591
652
|
[ Opcodes.i32_add ],
|
592
|
-
|
653
|
+
bytestrings ?
|
654
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ] :
|
655
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
593
656
|
|
594
657
|
// fetch right
|
595
658
|
[ Opcodes.local_get, index ],
|
596
659
|
[ Opcodes.local_get, rightPointer ],
|
597
660
|
[ Opcodes.i32_add ],
|
598
|
-
|
661
|
+
bytestrings ?
|
662
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ] :
|
663
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
599
664
|
|
600
665
|
// not equal, "return" false
|
601
666
|
[ Opcodes.i32_ne ],
|
@@ -604,13 +669,13 @@ const compareStrings = (scope, left, right) => {
|
|
604
669
|
[ Opcodes.br, 2 ],
|
605
670
|
[ Opcodes.end ],
|
606
671
|
|
607
|
-
// index += sizeof
|
672
|
+
// index += sizeof valtype (1 for bytestring, 2 for string)
|
608
673
|
[ Opcodes.local_get, index ],
|
609
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
674
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
610
675
|
[ Opcodes.i32_add ],
|
611
676
|
[ Opcodes.local_tee, index ],
|
612
677
|
|
613
|
-
// if index != index end (length * sizeof
|
678
|
+
// if index != index end (length * sizeof valtype), loop
|
614
679
|
[ Opcodes.local_get, indexEnd ],
|
615
680
|
[ Opcodes.i32_ne ],
|
616
681
|
[ Opcodes.br_if, 0 ],
|
@@ -631,16 +696,18 @@ const compareStrings = (scope, left, right) => {
|
|
631
696
|
};
|
632
697
|
|
633
698
|
const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
634
|
-
if (
|
699
|
+
if (isFloatToIntOp(wasm[wasm.length - 1])) return [
|
635
700
|
...wasm,
|
636
701
|
...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
|
637
702
|
];
|
703
|
+
// if (isIntOp(wasm[wasm.length - 1])) return [ ...wasm ];
|
638
704
|
|
639
|
-
const
|
705
|
+
const useTmp = knownType(scope, type) == null;
|
706
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
640
707
|
|
641
708
|
const def = [
|
642
709
|
// if value != 0
|
643
|
-
[ Opcodes.local_get, tmp ],
|
710
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
644
711
|
|
645
712
|
// ...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
646
713
|
...(!intOut || (intIn && intOut) ? [] : [ Opcodes.i32_to_u ]),
|
@@ -652,7 +719,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
652
719
|
|
653
720
|
return [
|
654
721
|
...wasm,
|
655
|
-
[ Opcodes.local_set, tmp ],
|
722
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
656
723
|
|
657
724
|
...typeSwitch(scope, type, {
|
658
725
|
// [TYPES.number]: def,
|
@@ -661,7 +728,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
661
728
|
...number(1, intOut ? Valtype.i32 : valtypeBinary)
|
662
729
|
],
|
663
730
|
[TYPES.string]: [
|
664
|
-
[ Opcodes.local_get, tmp ],
|
731
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
665
732
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
666
733
|
|
667
734
|
// get length
|
@@ -673,7 +740,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
673
740
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
674
741
|
],
|
675
742
|
[TYPES._bytestring]: [ // duplicate of string
|
676
|
-
|
743
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
677
744
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
678
745
|
|
679
746
|
// get length
|
@@ -687,10 +754,12 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
687
754
|
};
|
688
755
|
|
689
756
|
const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
690
|
-
const
|
757
|
+
const useTmp = knownType(scope, type) == null;
|
758
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
759
|
+
|
691
760
|
return [
|
692
761
|
...wasm,
|
693
|
-
[ Opcodes.local_set, tmp ],
|
762
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
694
763
|
|
695
764
|
...typeSwitch(scope, type, {
|
696
765
|
[TYPES._array]: [
|
@@ -698,7 +767,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
698
767
|
...number(0, intOut ? Valtype.i32 : valtypeBinary)
|
699
768
|
],
|
700
769
|
[TYPES.string]: [
|
701
|
-
[ Opcodes.local_get, tmp ],
|
770
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
702
771
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
703
772
|
|
704
773
|
// get length
|
@@ -709,7 +778,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
709
778
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
710
779
|
],
|
711
780
|
[TYPES._bytestring]: [ // duplicate of string
|
712
|
-
[ Opcodes.local_get, tmp ],
|
781
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
713
782
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
714
783
|
|
715
784
|
// get length
|
@@ -721,7 +790,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
721
790
|
],
|
722
791
|
default: [
|
723
792
|
// if value == 0
|
724
|
-
[ Opcodes.local_get, tmp ],
|
793
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
725
794
|
|
726
795
|
...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
727
796
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
@@ -731,10 +800,12 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
731
800
|
};
|
732
801
|
|
733
802
|
const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
|
734
|
-
const
|
803
|
+
const useTmp = knownType(scope, type) == null;
|
804
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
805
|
+
|
735
806
|
return [
|
736
807
|
...wasm,
|
737
|
-
[ Opcodes.local_set, tmp ],
|
808
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
738
809
|
|
739
810
|
...typeSwitch(scope, type, {
|
740
811
|
[TYPES.undefined]: [
|
@@ -743,7 +814,7 @@ const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
743
814
|
],
|
744
815
|
[TYPES.object]: [
|
745
816
|
// object, null if == 0
|
746
|
-
[ Opcodes.local_get, tmp ],
|
817
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
747
818
|
|
748
819
|
...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
749
820
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
@@ -772,11 +843,14 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
772
843
|
return performLogicOp(scope, op, left, right, leftType, rightType);
|
773
844
|
}
|
774
845
|
|
846
|
+
const knownLeft = knownType(scope, leftType);
|
847
|
+
const knownRight = knownType(scope, rightType);
|
848
|
+
|
775
849
|
const eqOp = ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(op);
|
776
850
|
const strictOp = op === '===' || op === '!==';
|
777
851
|
|
778
852
|
const startOut = [], endOut = [];
|
779
|
-
const
|
853
|
+
const finalize = out => startOut.concat(out, endOut);
|
780
854
|
|
781
855
|
// if strict (in)equal check types match
|
782
856
|
if (strictOp) {
|
@@ -821,31 +895,59 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
821
895
|
// todo: if equality op and an operand is undefined, return false
|
822
896
|
// todo: niche null hell with 0
|
823
897
|
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
898
|
+
if (knownLeft === TYPES.string || knownRight === TYPES.string) {
|
899
|
+
if (op === '+') {
|
900
|
+
// todo: this should be dynamic too but for now only static
|
901
|
+
// string concat (a + b)
|
902
|
+
return concatStrings(scope, left, right, _global, _name, assign, false);
|
903
|
+
}
|
904
|
+
|
905
|
+
// not an equality op, NaN
|
906
|
+
if (!eqOp) return number(NaN);
|
907
|
+
|
908
|
+
// else leave bool ops
|
909
|
+
// todo: convert string to number if string and number/bool
|
910
|
+
// todo: string (>|>=|<|<=) string
|
911
|
+
|
912
|
+
// string comparison
|
913
|
+
if (op === '===' || op === '==') {
|
914
|
+
return compareStrings(scope, left, right);
|
915
|
+
}
|
916
|
+
|
917
|
+
if (op === '!==' || op === '!=') {
|
918
|
+
return [
|
919
|
+
...compareStrings(scope, left, right),
|
920
|
+
[ Opcodes.i32_eqz ]
|
921
|
+
];
|
922
|
+
}
|
923
|
+
}
|
924
|
+
|
925
|
+
if (knownLeft === TYPES._bytestring || knownRight === TYPES._bytestring) {
|
926
|
+
if (op === '+') {
|
927
|
+
// todo: this should be dynamic too but for now only static
|
928
|
+
// string concat (a + b)
|
929
|
+
return concatStrings(scope, left, right, _global, _name, assign, true);
|
930
|
+
}
|
931
|
+
|
932
|
+
// not an equality op, NaN
|
933
|
+
if (!eqOp) return number(NaN);
|
934
|
+
|
935
|
+
// else leave bool ops
|
936
|
+
// todo: convert string to number if string and number/bool
|
937
|
+
// todo: string (>|>=|<|<=) string
|
938
|
+
|
939
|
+
// string comparison
|
940
|
+
if (op === '===' || op === '==') {
|
941
|
+
return compareStrings(scope, left, right, true);
|
942
|
+
}
|
943
|
+
|
944
|
+
if (op === '!==' || op === '!=') {
|
945
|
+
return [
|
946
|
+
...compareStrings(scope, left, right, true),
|
947
|
+
[ Opcodes.i32_eqz ]
|
948
|
+
];
|
949
|
+
}
|
950
|
+
}
|
849
951
|
|
850
952
|
let ops = operatorOpcode[valtype][op];
|
851
953
|
|
@@ -855,33 +957,69 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
855
957
|
includeBuiltin(scope, builtinName);
|
856
958
|
const idx = funcIndex[builtinName];
|
857
959
|
|
858
|
-
return
|
960
|
+
return finalize([
|
859
961
|
...left,
|
860
962
|
...right,
|
861
963
|
[ Opcodes.call, idx ]
|
862
964
|
]);
|
863
965
|
}
|
864
966
|
|
865
|
-
if (!ops) return todo(`operator ${op} not implemented yet
|
967
|
+
if (!ops) return todo(scope, `operator ${op} not implemented yet`, true);
|
866
968
|
|
867
969
|
if (!Array.isArray(ops)) ops = [ ops ];
|
868
970
|
ops = [ ops ];
|
869
971
|
|
870
972
|
let tmpLeft, tmpRight;
|
871
973
|
// if equal op, check if strings for compareStrings
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
// todo: intelligent partial skip later
|
877
|
-
// if neither known are string, stop this madness
|
878
|
-
if ((knownLeft != null && knownLeft !== TYPES.string) && (knownRight != null && knownRight !== TYPES.string)) {
|
879
|
-
return;
|
880
|
-
}
|
974
|
+
// todo: intelligent partial skip later
|
975
|
+
// if neither known are string, stop this madness
|
976
|
+
// we already do known checks earlier, so don't need to recheck
|
881
977
|
|
978
|
+
if ((op === '===' || op === '==' || op === '!==' || op === '!=') && (knownLeft == null && knownRight == null)) {
|
882
979
|
tmpLeft = localTmp(scope, '__tmpop_left');
|
883
980
|
tmpRight = localTmp(scope, '__tmpop_right');
|
884
981
|
|
982
|
+
// returns false for one string, one not - but more ops/slower
|
983
|
+
// ops.unshift(...stringOnly([
|
984
|
+
// // if left is string
|
985
|
+
// ...leftType,
|
986
|
+
// ...number(TYPES.string, Valtype.i32),
|
987
|
+
// [ Opcodes.i32_eq ],
|
988
|
+
|
989
|
+
// // if right is string
|
990
|
+
// ...rightType,
|
991
|
+
// ...number(TYPES.string, Valtype.i32),
|
992
|
+
// [ Opcodes.i32_eq ],
|
993
|
+
|
994
|
+
// // if either are true
|
995
|
+
// [ Opcodes.i32_or ],
|
996
|
+
// [ Opcodes.if, Blocktype.void ],
|
997
|
+
|
998
|
+
// // todo: convert non-strings to strings, for now fail immediately if one is not
|
999
|
+
// // if left is not string
|
1000
|
+
// ...leftType,
|
1001
|
+
// ...number(TYPES.string, Valtype.i32),
|
1002
|
+
// [ Opcodes.i32_ne ],
|
1003
|
+
|
1004
|
+
// // if right is not string
|
1005
|
+
// ...rightType,
|
1006
|
+
// ...number(TYPES.string, Valtype.i32),
|
1007
|
+
// [ Opcodes.i32_ne ],
|
1008
|
+
|
1009
|
+
// // if either are true
|
1010
|
+
// [ Opcodes.i32_or ],
|
1011
|
+
// [ Opcodes.if, Blocktype.void ],
|
1012
|
+
// ...number(0, Valtype.i32),
|
1013
|
+
// [ Opcodes.br, 2 ],
|
1014
|
+
// [ Opcodes.end ],
|
1015
|
+
|
1016
|
+
// ...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
1017
|
+
// ...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
1018
|
+
// [ Opcodes.br, 1 ],
|
1019
|
+
// [ Opcodes.end ],
|
1020
|
+
// ]));
|
1021
|
+
|
1022
|
+
// does not handle one string, one not (such cases go past)
|
885
1023
|
ops.unshift(...stringOnly([
|
886
1024
|
// if left is string
|
887
1025
|
...leftType,
|
@@ -893,30 +1031,28 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
893
1031
|
...number(TYPES.string, Valtype.i32),
|
894
1032
|
[ Opcodes.i32_eq ],
|
895
1033
|
|
896
|
-
// if
|
897
|
-
[ Opcodes.
|
1034
|
+
// if both are true
|
1035
|
+
[ Opcodes.i32_and ],
|
898
1036
|
[ Opcodes.if, Blocktype.void ],
|
1037
|
+
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
1038
|
+
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
1039
|
+
[ Opcodes.br, 1 ],
|
1040
|
+
[ Opcodes.end ],
|
899
1041
|
|
900
|
-
//
|
901
|
-
// if left is not string
|
1042
|
+
// if left is bytestring
|
902
1043
|
...leftType,
|
903
|
-
...number(TYPES.
|
904
|
-
[ Opcodes.
|
1044
|
+
...number(TYPES._bytestring, Valtype.i32),
|
1045
|
+
[ Opcodes.i32_eq ],
|
905
1046
|
|
906
|
-
// if right is
|
1047
|
+
// if right is bytestring
|
907
1048
|
...rightType,
|
908
|
-
...number(TYPES.
|
909
|
-
[ Opcodes.
|
1049
|
+
...number(TYPES._bytestring, Valtype.i32),
|
1050
|
+
[ Opcodes.i32_eq ],
|
910
1051
|
|
911
|
-
// if
|
912
|
-
[ Opcodes.
|
1052
|
+
// if both are true
|
1053
|
+
[ Opcodes.i32_and ],
|
913
1054
|
[ Opcodes.if, Blocktype.void ],
|
914
|
-
...
|
915
|
-
[ Opcodes.br, 1 ],
|
916
|
-
[ Opcodes.end ],
|
917
|
-
|
918
|
-
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
919
|
-
// ...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
1055
|
+
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], true),
|
920
1056
|
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
921
1057
|
[ Opcodes.br, 1 ],
|
922
1058
|
[ Opcodes.end ],
|
@@ -928,9 +1064,9 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
928
1064
|
// endOut.push(stringOnly([ Opcodes.end ]));
|
929
1065
|
endOut.unshift(stringOnly([ Opcodes.end ]));
|
930
1066
|
// }
|
931
|
-
}
|
1067
|
+
}
|
932
1068
|
|
933
|
-
return
|
1069
|
+
return finalize([
|
934
1070
|
...left,
|
935
1071
|
...(tmpLeft != null ? stringOnly([ [ Opcodes.local_tee, tmpLeft ] ]) : []),
|
936
1072
|
...right,
|
@@ -947,7 +1083,22 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
947
1083
|
return out;
|
948
1084
|
};
|
949
1085
|
|
950
|
-
const
|
1086
|
+
const asmFuncToAsm = (func, { name = '#unknown_asm_func', params = [], locals = [], returns = [], localInd = 0 }) => {
|
1087
|
+
return func({ name, params, locals, returns, localInd }, {
|
1088
|
+
TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage,
|
1089
|
+
builtin: name => {
|
1090
|
+
let idx = funcIndex[name] ?? importedFuncs[name];
|
1091
|
+
if (idx === undefined && builtinFuncs[name]) {
|
1092
|
+
includeBuiltin(null, name);
|
1093
|
+
idx = funcIndex[name];
|
1094
|
+
}
|
1095
|
+
|
1096
|
+
return idx;
|
1097
|
+
}
|
1098
|
+
});
|
1099
|
+
};
|
1100
|
+
|
1101
|
+
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [] }) => {
|
951
1102
|
const existing = funcs.find(x => x.name === name);
|
952
1103
|
if (existing) return existing;
|
953
1104
|
|
@@ -959,18 +1110,14 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
959
1110
|
locals[nameParam(i)] = { idx: i, type: allLocals[i] };
|
960
1111
|
}
|
961
1112
|
|
962
|
-
|
963
|
-
const
|
964
|
-
|
965
|
-
|
966
|
-
locals,
|
967
|
-
returns,
|
968
|
-
localInd: allLocals.length,
|
969
|
-
};
|
970
|
-
|
971
|
-
wasm = wasm(scope, { TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString });
|
1113
|
+
for (const x of _data) {
|
1114
|
+
const copy = { ...x };
|
1115
|
+
copy.offset += pages.size * pageSize;
|
1116
|
+
data.push(copy);
|
972
1117
|
}
|
973
1118
|
|
1119
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, { name, params, locals, returns, localInd: allLocals.length });
|
1120
|
+
|
974
1121
|
let baseGlobalIdx, i = 0;
|
975
1122
|
for (const type of globalTypes) {
|
976
1123
|
if (baseGlobalIdx === undefined) baseGlobalIdx = globalInd;
|
@@ -993,7 +1140,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
993
1140
|
params,
|
994
1141
|
locals,
|
995
1142
|
returns,
|
996
|
-
returnType:
|
1143
|
+
returnType: returnType ?? TYPES.number,
|
997
1144
|
wasm,
|
998
1145
|
internal: true,
|
999
1146
|
index: currentFuncIndex++
|
@@ -1016,6 +1163,7 @@ const generateLogicExp = (scope, decl) => {
|
|
1016
1163
|
return performLogicOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right));
|
1017
1164
|
};
|
1018
1165
|
|
1166
|
+
// potential future ideas for nan boxing (unused):
|
1019
1167
|
// T = JS type, V = value/pointer
|
1020
1168
|
// 0bTTT
|
1021
1169
|
// qNAN: 0 11111111111 1000000000000000000000000000000000000000000000000001
|
@@ -1039,49 +1187,29 @@ const generateLogicExp = (scope, decl) => {
|
|
1039
1187
|
// 4: internal type
|
1040
1188
|
// 5: pointer
|
1041
1189
|
|
1042
|
-
const
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
object: 0x04,
|
1048
|
-
function: 0x05,
|
1049
|
-
symbol: 0x06,
|
1050
|
-
bigint: 0x07,
|
1051
|
-
|
1052
|
-
// these are not "typeof" types but tracked internally
|
1053
|
-
_array: 0x10,
|
1054
|
-
_regexp: 0x11,
|
1055
|
-
_bytestring: 0x12
|
1056
|
-
};
|
1057
|
-
|
1058
|
-
const TYPE_NAMES = {
|
1059
|
-
[TYPES.number]: 'Number',
|
1060
|
-
[TYPES.boolean]: 'Boolean',
|
1061
|
-
[TYPES.string]: 'String',
|
1062
|
-
[TYPES.undefined]: 'undefined',
|
1063
|
-
[TYPES.object]: 'Object',
|
1064
|
-
[TYPES.function]: 'Function',
|
1065
|
-
[TYPES.symbol]: 'Symbol',
|
1066
|
-
[TYPES.bigint]: 'BigInt',
|
1067
|
-
|
1068
|
-
[TYPES._array]: 'Array',
|
1069
|
-
[TYPES._regexp]: 'RegExp',
|
1070
|
-
[TYPES._bytestring]: 'ByteString'
|
1190
|
+
const isExistingProtoFunc = name => {
|
1191
|
+
if (name.startsWith('__Array_prototype')) return !!prototypeFuncs[TYPES._array][name.slice(18)];
|
1192
|
+
if (name.startsWith('__String_prototype_')) return !!prototypeFuncs[TYPES.string][name.slice(19)];
|
1193
|
+
|
1194
|
+
return false;
|
1071
1195
|
};
|
1072
1196
|
|
1073
1197
|
const getType = (scope, _name) => {
|
1074
1198
|
const name = mapName(_name);
|
1075
1199
|
|
1200
|
+
// if (scope.locals[name] && !scope.locals[name + '#type']) console.log(name);
|
1201
|
+
|
1202
|
+
if (typedInput && scope.locals[name]?.metadata?.type != null) return number(scope.locals[name].metadata.type, Valtype.i32);
|
1076
1203
|
if (scope.locals[name]) return [ [ Opcodes.local_get, scope.locals[name + '#type'].idx ] ];
|
1204
|
+
|
1205
|
+
if (typedInput && globals[name]?.metadata?.type != null) return number(globals[name].metadata.type, Valtype.i32);
|
1077
1206
|
if (globals[name]) return [ [ Opcodes.global_get, globals[name + '#type'].idx ] ];
|
1078
1207
|
|
1079
1208
|
let type = TYPES.undefined;
|
1080
|
-
if (builtinVars[name]) type =
|
1209
|
+
if (builtinVars[name]) type = builtinVars[name].type ?? TYPES.number;
|
1081
1210
|
if (builtinFuncs[name] !== undefined || importedFuncs[name] !== undefined || funcIndex[name] !== undefined || internalConstrs[name] !== undefined) type = TYPES.function;
|
1082
1211
|
|
1083
|
-
if (name
|
1084
|
-
name.startsWith('__String_prototype_') && prototypeFuncs[TYPES.string][name.slice(19)]) type = TYPES.function;
|
1212
|
+
if (isExistingProtoFunc(name)) type = TYPES.function;
|
1085
1213
|
|
1086
1214
|
return number(type, Valtype.i32);
|
1087
1215
|
};
|
@@ -1104,15 +1232,16 @@ const setType = (scope, _name, type) => {
|
|
1104
1232
|
];
|
1105
1233
|
|
1106
1234
|
// throw new Error('could not find var');
|
1235
|
+
return [];
|
1107
1236
|
};
|
1108
1237
|
|
1109
1238
|
const getLastType = scope => {
|
1110
1239
|
scope.gotLastType = true;
|
1111
|
-
return [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ];
|
1240
|
+
return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1112
1241
|
};
|
1113
1242
|
|
1114
1243
|
const setLastType = scope => {
|
1115
|
-
return [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ];
|
1244
|
+
return [ [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1116
1245
|
};
|
1117
1246
|
|
1118
1247
|
const getNodeType = (scope, node) => {
|
@@ -1137,7 +1266,7 @@ const getNodeType = (scope, node) => {
|
|
1137
1266
|
const name = node.callee.name;
|
1138
1267
|
if (!name) {
|
1139
1268
|
// iife
|
1140
|
-
if (scope.locals['#last_type']) return
|
1269
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1141
1270
|
|
1142
1271
|
// presume
|
1143
1272
|
// todo: warn here?
|
@@ -1151,7 +1280,7 @@ const getNodeType = (scope, node) => {
|
|
1151
1280
|
if (func.returnType) return func.returnType;
|
1152
1281
|
}
|
1153
1282
|
|
1154
|
-
if (builtinFuncs[name]) return
|
1283
|
+
if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return builtinFuncs[name].returnType ?? TYPES.number;
|
1155
1284
|
if (internalConstrs[name]) return internalConstrs[name].type;
|
1156
1285
|
|
1157
1286
|
// check if this is a prototype function
|
@@ -1166,7 +1295,12 @@ const getNodeType = (scope, node) => {
|
|
1166
1295
|
if (protoFuncs.length === 1) return protoFuncs[0].returnType ?? TYPES.number;
|
1167
1296
|
}
|
1168
1297
|
|
1169
|
-
if (
|
1298
|
+
if (name.startsWith('__Porffor_wasm_')) {
|
1299
|
+
// todo: return undefined for non-returning ops
|
1300
|
+
return TYPES.number;
|
1301
|
+
}
|
1302
|
+
|
1303
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1170
1304
|
|
1171
1305
|
// presume
|
1172
1306
|
// todo: warn here?
|
@@ -1214,6 +1348,15 @@ const getNodeType = (scope, node) => {
|
|
1214
1348
|
|
1215
1349
|
if (node.type === 'BinaryExpression') {
|
1216
1350
|
if (['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(node.operator)) return TYPES.boolean;
|
1351
|
+
if (node.operator !== '+') return TYPES.number;
|
1352
|
+
|
1353
|
+
const knownLeft = knownType(scope, getNodeType(scope, node.left));
|
1354
|
+
const knownRight = knownType(scope, getNodeType(scope, node.right));
|
1355
|
+
|
1356
|
+
// todo: this should be dynamic but for now only static
|
1357
|
+
if (knownLeft === TYPES.string || knownRight === TYPES.string) return TYPES.string;
|
1358
|
+
if (knownLeft === TYPES._bytestring || knownRight === TYPES._bytestring) return TYPES._bytestring;
|
1359
|
+
|
1217
1360
|
return TYPES.number;
|
1218
1361
|
|
1219
1362
|
// todo: string concat types
|
@@ -1238,7 +1381,7 @@ const getNodeType = (scope, node) => {
|
|
1238
1381
|
if (node.operator === '!') return TYPES.boolean;
|
1239
1382
|
if (node.operator === 'void') return TYPES.undefined;
|
1240
1383
|
if (node.operator === 'delete') return TYPES.boolean;
|
1241
|
-
if (node.operator === 'typeof') return
|
1384
|
+
if (node.operator === 'typeof') return Prefs.bytestring ? TYPES._bytestring : TYPES.string;
|
1242
1385
|
|
1243
1386
|
return TYPES.number;
|
1244
1387
|
}
|
@@ -1249,15 +1392,21 @@ const getNodeType = (scope, node) => {
|
|
1249
1392
|
|
1250
1393
|
// ts hack
|
1251
1394
|
if (scope.locals[node.object.name]?.metadata?.type === TYPES.string) return TYPES.string;
|
1395
|
+
if (scope.locals[node.object.name]?.metadata?.type === TYPES._bytestring) return TYPES._bytestring;
|
1252
1396
|
if (scope.locals[node.object.name]?.metadata?.type === TYPES._array) return TYPES.number;
|
1253
1397
|
|
1254
|
-
if (scope.locals['#last_type']) return
|
1398
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1255
1399
|
|
1256
1400
|
// presume
|
1257
1401
|
return TYPES.number;
|
1258
1402
|
}
|
1259
1403
|
|
1260
|
-
if (
|
1404
|
+
if (node.type === 'TaggedTemplateExpression') {
|
1405
|
+
// hack
|
1406
|
+
if (node.tag.name.startsWith('__Porffor_')) return TYPES.number;
|
1407
|
+
}
|
1408
|
+
|
1409
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1261
1410
|
|
1262
1411
|
// presume
|
1263
1412
|
// todo: warn here?
|
@@ -1290,7 +1439,7 @@ const generateLiteral = (scope, decl, global, name) => {
|
|
1290
1439
|
return makeString(scope, decl.value, global, name);
|
1291
1440
|
|
1292
1441
|
default:
|
1293
|
-
return todo(`cannot generate literal of type ${typeof decl.value}
|
1442
|
+
return todo(scope, `cannot generate literal of type ${typeof decl.value}`, true);
|
1294
1443
|
}
|
1295
1444
|
};
|
1296
1445
|
|
@@ -1299,6 +1448,8 @@ const countLeftover = wasm => {
|
|
1299
1448
|
|
1300
1449
|
for (let i = 0; i < wasm.length; i++) {
|
1301
1450
|
const inst = wasm[i];
|
1451
|
+
if (inst[0] == null) continue;
|
1452
|
+
|
1302
1453
|
if (depth === 0 && (inst[0] === Opcodes.if || inst[0] === Opcodes.block || inst[0] === Opcodes.loop)) {
|
1303
1454
|
if (inst[0] === Opcodes.if) count--;
|
1304
1455
|
if (inst[1] !== Blocktype.void) count++;
|
@@ -1307,7 +1458,7 @@ const countLeftover = wasm => {
|
|
1307
1458
|
if (inst[0] === Opcodes.end) depth--;
|
1308
1459
|
|
1309
1460
|
if (depth === 0)
|
1310
|
-
if ([Opcodes.throw,Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
|
1461
|
+
if ([Opcodes.throw, Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
|
1311
1462
|
else if ([null, Opcodes.i32_eqz, Opcodes.i64_eqz, Opcodes.f64_ceil, Opcodes.f64_floor, Opcodes.f64_trunc, Opcodes.f64_nearest, Opcodes.f64_sqrt, Opcodes.local_tee, Opcodes.i32_wrap_i64, Opcodes.i64_extend_i32_s, Opcodes.i64_extend_i32_u, Opcodes.f32_demote_f64, Opcodes.f64_promote_f32, Opcodes.f64_convert_i32_s, Opcodes.f64_convert_i32_u, Opcodes.i32_clz, Opcodes.i32_ctz, Opcodes.i32_popcnt, Opcodes.f64_neg, Opcodes.end, Opcodes.i32_trunc_sat_f64_s[0], Opcodes.i32x4_extract_lane, Opcodes.i16x8_extract_lane, Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load, Opcodes.v128_load, Opcodes.i32_load16_u, Opcodes.i32_load16_s, Opcodes.i32_load8_u, Opcodes.i32_load8_s, Opcodes.memory_grow].includes(inst[0]) && (inst[0] !== 0xfc || inst[1] < 0x0a)) {}
|
1312
1463
|
else if ([Opcodes.local_get, Opcodes.global_get, Opcodes.f64_const, Opcodes.i32_const, Opcodes.i64_const, Opcodes.v128_const].includes(inst[0])) count++;
|
1313
1464
|
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
|
@@ -1410,10 +1561,21 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1410
1561
|
name = func.name;
|
1411
1562
|
}
|
1412
1563
|
|
1413
|
-
if (name === 'eval' && decl.arguments[0]
|
1564
|
+
if (name === 'eval' && decl.arguments[0]?.type === 'Literal') {
|
1414
1565
|
// literal eval hack
|
1415
|
-
const code = decl.arguments[0]
|
1416
|
-
|
1566
|
+
const code = decl.arguments[0]?.value ?? '';
|
1567
|
+
|
1568
|
+
let parsed;
|
1569
|
+
try {
|
1570
|
+
parsed = parse(code, []);
|
1571
|
+
} catch (e) {
|
1572
|
+
if (e.name === 'SyntaxError') {
|
1573
|
+
// throw syntax errors of evals at runtime instead
|
1574
|
+
return internalThrow(scope, 'SyntaxError', e.message, true);
|
1575
|
+
}
|
1576
|
+
|
1577
|
+
throw e;
|
1578
|
+
}
|
1417
1579
|
|
1418
1580
|
const out = generate(scope, {
|
1419
1581
|
type: 'BlockStatement',
|
@@ -1427,13 +1589,13 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1427
1589
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1428
1590
|
out.push(
|
1429
1591
|
...getNodeType(scope, finalStatement),
|
1430
|
-
setLastType(scope)
|
1592
|
+
...setLastType(scope)
|
1431
1593
|
);
|
1432
1594
|
} else if (countLeftover(out) === 0) {
|
1433
1595
|
out.push(...number(UNDEFINED));
|
1434
1596
|
out.push(
|
1435
1597
|
...number(TYPES.undefined, Valtype.i32),
|
1436
|
-
setLastType(scope)
|
1598
|
+
...setLastType(scope)
|
1437
1599
|
);
|
1438
1600
|
}
|
1439
1601
|
|
@@ -1455,6 +1617,9 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1455
1617
|
|
1456
1618
|
target = { ...decl.callee };
|
1457
1619
|
target.name = spl.slice(0, -1).join('_');
|
1620
|
+
|
1621
|
+
// failed to lookup name, abort
|
1622
|
+
if (!lookupName(scope, target.name)[0]) protoName = null;
|
1458
1623
|
}
|
1459
1624
|
|
1460
1625
|
// literal.func()
|
@@ -1477,7 +1642,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1477
1642
|
Opcodes.i32_from_u,
|
1478
1643
|
|
1479
1644
|
...number(TYPES.boolean, Valtype.i32),
|
1480
|
-
setLastType(scope)
|
1645
|
+
...setLastType(scope)
|
1481
1646
|
];
|
1482
1647
|
}
|
1483
1648
|
|
@@ -1502,12 +1667,30 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1502
1667
|
// }
|
1503
1668
|
|
1504
1669
|
if (protoName) {
|
1670
|
+
const protoBC = {};
|
1671
|
+
|
1672
|
+
const builtinProtoCands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + protoName));
|
1673
|
+
|
1674
|
+
if (!decl._protoInternalCall && builtinProtoCands.length > 0) {
|
1675
|
+
for (const x of builtinProtoCands) {
|
1676
|
+
const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
|
1677
|
+
if (type == null) continue;
|
1678
|
+
|
1679
|
+
protoBC[type] = generateCall(scope, {
|
1680
|
+
callee: {
|
1681
|
+
name: x
|
1682
|
+
},
|
1683
|
+
arguments: [ target, ...decl.arguments ],
|
1684
|
+
_protoInternalCall: true
|
1685
|
+
});
|
1686
|
+
}
|
1687
|
+
}
|
1688
|
+
|
1505
1689
|
const protoCands = Object.keys(prototypeFuncs).reduce((acc, x) => {
|
1506
1690
|
if (Object.hasOwn(prototypeFuncs[x], protoName)) acc[x] = prototypeFuncs[x][protoName];
|
1507
1691
|
return acc;
|
1508
1692
|
}, {});
|
1509
1693
|
|
1510
|
-
// no prototype function candidates, ignore
|
1511
1694
|
if (Object.keys(protoCands).length > 0) {
|
1512
1695
|
// use local for cached i32 length as commonly used
|
1513
1696
|
const lengthLocal = localTmp(scope, '__proto_length_cache', Valtype.i32);
|
@@ -1525,7 +1708,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1525
1708
|
|
1526
1709
|
let allOptUnused = true;
|
1527
1710
|
let lengthI32CacheUsed = false;
|
1528
|
-
const protoBC = {};
|
1529
1711
|
for (const x in protoCands) {
|
1530
1712
|
const protoFunc = protoCands[x];
|
1531
1713
|
if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
|
@@ -1533,7 +1715,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1533
1715
|
...RTArrayUtil.getLength(getPointer),
|
1534
1716
|
|
1535
1717
|
...number(TYPES.number, Valtype.i32),
|
1536
|
-
setLastType(scope)
|
1718
|
+
...setLastType(scope)
|
1537
1719
|
];
|
1538
1720
|
continue;
|
1539
1721
|
}
|
@@ -1570,7 +1752,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1570
1752
|
...protoOut,
|
1571
1753
|
|
1572
1754
|
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1573
|
-
setLastType(scope),
|
1755
|
+
...setLastType(scope),
|
1574
1756
|
[ Opcodes.end ]
|
1575
1757
|
];
|
1576
1758
|
}
|
@@ -1596,10 +1778,19 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1596
1778
|
}, allOptUnused && unusedValue ? Blocktype.void : valtypeBinary),
|
1597
1779
|
];
|
1598
1780
|
}
|
1781
|
+
|
1782
|
+
if (Object.keys(protoBC).length > 0) {
|
1783
|
+
return typeSwitch(scope, getNodeType(scope, target), {
|
1784
|
+
...protoBC,
|
1785
|
+
|
1786
|
+
// TODO: error better
|
1787
|
+
default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`)
|
1788
|
+
}, valtypeBinary);
|
1789
|
+
}
|
1599
1790
|
}
|
1600
1791
|
|
1601
1792
|
// TODO: only allows callee as literal
|
1602
|
-
if (!name) return todo(`only literal callees (got ${decl.callee.type})`);
|
1793
|
+
if (!name) return todo(scope, `only literal callees (got ${decl.callee.type})`);
|
1603
1794
|
|
1604
1795
|
let idx = funcIndex[name] ?? importedFuncs[name];
|
1605
1796
|
if (idx === undefined && builtinFuncs[name]) {
|
@@ -1632,16 +1823,65 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1632
1823
|
idx = -1;
|
1633
1824
|
}
|
1634
1825
|
|
1826
|
+
if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
|
1827
|
+
const wasmOps = {
|
1828
|
+
// pointer, align, offset
|
1829
|
+
i32_load: { imms: 2, args: 1, returns: 1 },
|
1830
|
+
// pointer, value, align, offset
|
1831
|
+
i32_store: { imms: 2, args: 2, returns: 0 },
|
1832
|
+
// pointer, align, offset
|
1833
|
+
i32_load8_u: { imms: 2, args: 1, returns: 1 },
|
1834
|
+
// pointer, value, align, offset
|
1835
|
+
i32_store8: { imms: 2, args: 2, returns: 0 },
|
1836
|
+
// pointer, align, offset
|
1837
|
+
i32_load16_u: { imms: 2, args: 1, returns: 1 },
|
1838
|
+
// pointer, value, align, offset
|
1839
|
+
i32_store16: { imms: 2, args: 2, returns: 0 },
|
1840
|
+
|
1841
|
+
// pointer, align, offset
|
1842
|
+
f64_load: { imms: 2, args: 1, returns: 1 },
|
1843
|
+
// pointer, value, align, offset
|
1844
|
+
f64_store: { imms: 2, args: 2, returns: 0 },
|
1845
|
+
|
1846
|
+
// value
|
1847
|
+
i32_const: { imms: 1, args: 0, returns: 1 },
|
1848
|
+
|
1849
|
+
// a, b
|
1850
|
+
i32_or: { imms: 0, args: 2, returns: 1 },
|
1851
|
+
};
|
1852
|
+
|
1853
|
+
const opName = name.slice('__Porffor_wasm_'.length);
|
1854
|
+
|
1855
|
+
if (wasmOps[opName]) {
|
1856
|
+
const op = wasmOps[opName];
|
1857
|
+
|
1858
|
+
const argOut = [];
|
1859
|
+
for (let i = 0; i < op.args; i++) argOut.push(
|
1860
|
+
...generate(scope, decl.arguments[i]),
|
1861
|
+
Opcodes.i32_to
|
1862
|
+
);
|
1863
|
+
|
1864
|
+
// literals only
|
1865
|
+
const imms = decl.arguments.slice(op.args).map(x => x.value);
|
1866
|
+
|
1867
|
+
return [
|
1868
|
+
...argOut,
|
1869
|
+
[ Opcodes[opName], ...imms ],
|
1870
|
+
...(new Array(op.returns).fill(Opcodes.i32_from))
|
1871
|
+
];
|
1872
|
+
}
|
1873
|
+
}
|
1874
|
+
|
1635
1875
|
if (idx === undefined) {
|
1636
|
-
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function
|
1637
|
-
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined
|
1876
|
+
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
|
1877
|
+
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
1638
1878
|
}
|
1639
1879
|
|
1640
1880
|
const func = funcs.find(x => x.index === idx);
|
1641
1881
|
|
1642
1882
|
const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
|
1643
1883
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1644
|
-
const
|
1884
|
+
const typedReturns = (func ? func.returnType == null : userFunc) || builtinFuncs[name]?.typedReturns;
|
1645
1885
|
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1646
1886
|
|
1647
1887
|
let args = decl.arguments;
|
@@ -1658,14 +1898,24 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1658
1898
|
if (func && func.throws) scope.throws = true;
|
1659
1899
|
|
1660
1900
|
let out = [];
|
1661
|
-
for (
|
1901
|
+
for (let i = 0; i < args.length; i++) {
|
1902
|
+
const arg = args[i];
|
1662
1903
|
out = out.concat(generate(scope, arg));
|
1904
|
+
|
1905
|
+
if (builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1906
|
+
out.push(Opcodes.i32_to);
|
1907
|
+
}
|
1908
|
+
|
1909
|
+
if (importedFuncs[name] && name.startsWith('profile')) {
|
1910
|
+
out.push(Opcodes.i32_to);
|
1911
|
+
}
|
1912
|
+
|
1663
1913
|
if (typedParams) out = out.concat(getNodeType(scope, arg));
|
1664
1914
|
}
|
1665
1915
|
|
1666
1916
|
out.push([ Opcodes.call, idx ]);
|
1667
1917
|
|
1668
|
-
if (!
|
1918
|
+
if (!typedReturns) {
|
1669
1919
|
// let type;
|
1670
1920
|
// if (builtinFuncs[name]) type = TYPES[builtinFuncs[name].returnType ?? 'number'];
|
1671
1921
|
// if (internalConstrs[name]) type = internalConstrs[name].type;
|
@@ -1675,7 +1925,11 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1675
1925
|
// ...number(type, Valtype.i32),
|
1676
1926
|
// [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
|
1677
1927
|
// );
|
1678
|
-
} else out.push(setLastType(scope));
|
1928
|
+
} else out.push(...setLastType(scope));
|
1929
|
+
|
1930
|
+
if (builtinFuncs[name] && builtinFuncs[name].returns?.[0] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1931
|
+
out.push(Opcodes.i32_from);
|
1932
|
+
}
|
1679
1933
|
|
1680
1934
|
return out;
|
1681
1935
|
};
|
@@ -1684,7 +1938,7 @@ const generateNew = (scope, decl, _global, _name) => {
|
|
1684
1938
|
// hack: basically treat this as a normal call for builtins for now
|
1685
1939
|
const name = mapName(decl.callee.name);
|
1686
1940
|
if (internalConstrs[name] && !internalConstrs[name].notConstr) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1687
|
-
if (!builtinFuncs[name]) return todo(`new statement is not supported yet`); // return todo(`new statement is not supported yet (new ${unhackName(name)})`);
|
1941
|
+
if (!builtinFuncs[name]) return todo(scope, `new statement is not supported yet`); // return todo(scope, `new statement is not supported yet (new ${unhackName(name)})`);
|
1688
1942
|
|
1689
1943
|
return generateCall(scope, decl, _global, _name);
|
1690
1944
|
};
|
@@ -1801,14 +2055,14 @@ const brTable = (input, bc, returns) => {
|
|
1801
2055
|
};
|
1802
2056
|
|
1803
2057
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
1804
|
-
if (!
|
2058
|
+
if (!Prefs.bytestring) delete bc[TYPES._bytestring];
|
1805
2059
|
|
1806
2060
|
const known = knownType(scope, type);
|
1807
2061
|
if (known != null) {
|
1808
2062
|
return bc[known] ?? bc.default;
|
1809
2063
|
}
|
1810
2064
|
|
1811
|
-
if (
|
2065
|
+
if (Prefs.typeswitchUseBrtable)
|
1812
2066
|
return brTable(type, bc, returns);
|
1813
2067
|
|
1814
2068
|
const tmp = localTmp(scope, '#typeswitch_tmp', Valtype.i32);
|
@@ -1818,8 +2072,6 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
1818
2072
|
[ Opcodes.block, returns ]
|
1819
2073
|
];
|
1820
2074
|
|
1821
|
-
// todo: use br_table?
|
1822
|
-
|
1823
2075
|
for (const x in bc) {
|
1824
2076
|
if (x === 'default') continue;
|
1825
2077
|
|
@@ -1843,7 +2095,7 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
1843
2095
|
return out;
|
1844
2096
|
};
|
1845
2097
|
|
1846
|
-
const allocVar = (scope, name, global = false) => {
|
2098
|
+
const allocVar = (scope, name, global = false, type = true) => {
|
1847
2099
|
const target = global ? globals : scope.locals;
|
1848
2100
|
|
1849
2101
|
// already declared
|
@@ -1857,8 +2109,10 @@ const allocVar = (scope, name, global = false) => {
|
|
1857
2109
|
let idx = global ? globalInd++ : scope.localInd++;
|
1858
2110
|
target[name] = { idx, type: valtypeBinary };
|
1859
2111
|
|
1860
|
-
|
1861
|
-
|
2112
|
+
if (type) {
|
2113
|
+
let typeIdx = global ? globalInd++ : scope.localInd++;
|
2114
|
+
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
|
2115
|
+
}
|
1862
2116
|
|
1863
2117
|
return idx;
|
1864
2118
|
};
|
@@ -1873,11 +2127,14 @@ const addVarMetadata = (scope, name, global = false, metadata = {}) => {
|
|
1873
2127
|
};
|
1874
2128
|
|
1875
2129
|
const typeAnnoToPorfType = x => {
|
1876
|
-
if (
|
1877
|
-
if (TYPES[
|
2130
|
+
if (!x) return null;
|
2131
|
+
if (TYPES[x] != null) return TYPES[x];
|
2132
|
+
if (TYPES['_' + x] != null) return TYPES['_' + x];
|
1878
2133
|
|
1879
2134
|
switch (x) {
|
1880
2135
|
case 'i32':
|
2136
|
+
case 'i64':
|
2137
|
+
case 'f64':
|
1881
2138
|
return TYPES.number;
|
1882
2139
|
}
|
1883
2140
|
|
@@ -1888,7 +2145,7 @@ const extractTypeAnnotation = decl => {
|
|
1888
2145
|
let a = decl;
|
1889
2146
|
while (a.typeAnnotation) a = a.typeAnnotation;
|
1890
2147
|
|
1891
|
-
let type, elementType;
|
2148
|
+
let type = null, elementType = null;
|
1892
2149
|
if (a.typeName) {
|
1893
2150
|
type = a.typeName.name;
|
1894
2151
|
} else if (a.type.endsWith('Keyword')) {
|
@@ -1901,7 +2158,7 @@ const extractTypeAnnotation = decl => {
|
|
1901
2158
|
const typeName = type;
|
1902
2159
|
type = typeAnnoToPorfType(type);
|
1903
2160
|
|
1904
|
-
if (type === TYPES._bytestring && !
|
2161
|
+
if (type === TYPES._bytestring && !Prefs.bytestring) type = TYPES.string;
|
1905
2162
|
|
1906
2163
|
// if (decl.name) console.log(decl.name, { type, elementType });
|
1907
2164
|
|
@@ -1919,7 +2176,7 @@ const generateVar = (scope, decl) => {
|
|
1919
2176
|
for (const x of decl.declarations) {
|
1920
2177
|
const name = mapName(x.id.name);
|
1921
2178
|
|
1922
|
-
if (!name) return todo('destructuring is not supported yet');
|
2179
|
+
if (!name) return todo(scope, 'destructuring is not supported yet');
|
1923
2180
|
|
1924
2181
|
if (x.init && isFuncType(x.init.type)) {
|
1925
2182
|
// hack for let a = function () { ... }
|
@@ -1936,9 +2193,10 @@ const generateVar = (scope, decl) => {
|
|
1936
2193
|
continue; // always ignore
|
1937
2194
|
}
|
1938
2195
|
|
1939
|
-
|
2196
|
+
const typed = typedInput && x.id.typeAnnotation;
|
2197
|
+
let idx = allocVar(scope, name, global, !typed);
|
1940
2198
|
|
1941
|
-
if (
|
2199
|
+
if (typed) {
|
1942
2200
|
addVarMetadata(scope, name, global, extractTypeAnnotation(x.id));
|
1943
2201
|
}
|
1944
2202
|
|
@@ -1956,7 +2214,8 @@ const generateVar = (scope, decl) => {
|
|
1956
2214
|
return out;
|
1957
2215
|
};
|
1958
2216
|
|
1959
|
-
|
2217
|
+
// todo: optimize this func for valueUnused
|
2218
|
+
const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
1960
2219
|
const { type, name } = decl.left;
|
1961
2220
|
|
1962
2221
|
if (type === 'ObjectPattern') {
|
@@ -1974,9 +2233,9 @@ const generateAssign = (scope, decl) => {
|
|
1974
2233
|
// hack: .length setter
|
1975
2234
|
if (decl.left.type === 'MemberExpression' && decl.left.property.name === 'length') {
|
1976
2235
|
const name = decl.left.object.name;
|
1977
|
-
const pointer = arrays
|
2236
|
+
const pointer = scope.arrays?.get(name);
|
1978
2237
|
|
1979
|
-
const aotPointer = pointer != null;
|
2238
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
1980
2239
|
|
1981
2240
|
const newValueTmp = localTmp(scope, '__length_setter_tmp');
|
1982
2241
|
|
@@ -2001,9 +2260,9 @@ const generateAssign = (scope, decl) => {
|
|
2001
2260
|
// arr[i]
|
2002
2261
|
if (decl.left.type === 'MemberExpression' && decl.left.computed) {
|
2003
2262
|
const name = decl.left.object.name;
|
2004
|
-
const pointer = arrays
|
2263
|
+
const pointer = scope.arrays?.get(name);
|
2005
2264
|
|
2006
|
-
const aotPointer = pointer != null;
|
2265
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2007
2266
|
|
2008
2267
|
const newValueTmp = localTmp(scope, '__member_setter_val_tmp');
|
2009
2268
|
const pointerTmp = op === '=' ? -1 : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
|
@@ -2059,7 +2318,7 @@ const generateAssign = (scope, decl) => {
|
|
2059
2318
|
];
|
2060
2319
|
}
|
2061
2320
|
|
2062
|
-
if (!name) return todo('destructuring is not supported yet');
|
2321
|
+
if (!name) return todo(scope, 'destructuring is not supported yet', true);
|
2063
2322
|
|
2064
2323
|
const [ local, isGlobal ] = lookupName(scope, name);
|
2065
2324
|
|
@@ -2107,9 +2366,7 @@ const generateAssign = (scope, decl) => {
|
|
2107
2366
|
], getType(scope, name), getNodeType(scope, decl.right), isGlobal, name, true),
|
2108
2367
|
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
2109
2368
|
|
2110
|
-
getLastType(scope)
|
2111
|
-
// hack: type is idx+1
|
2112
|
-
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx + 1 ],
|
2369
|
+
...setType(scope, name, getLastType(scope))
|
2113
2370
|
];
|
2114
2371
|
}
|
2115
2372
|
|
@@ -2120,9 +2377,7 @@ const generateAssign = (scope, decl) => {
|
|
2120
2377
|
|
2121
2378
|
// todo: string concat types
|
2122
2379
|
|
2123
|
-
|
2124
|
-
...number(TYPES.number, Valtype.i32),
|
2125
|
-
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx + 1 ],
|
2380
|
+
...setType(scope, name, TYPES.number)
|
2126
2381
|
];
|
2127
2382
|
};
|
2128
2383
|
|
@@ -2168,7 +2423,7 @@ const generateUnary = (scope, decl) => {
|
|
2168
2423
|
return out;
|
2169
2424
|
}
|
2170
2425
|
|
2171
|
-
case 'delete':
|
2426
|
+
case 'delete': {
|
2172
2427
|
let toReturn = true, toGenerate = true;
|
2173
2428
|
|
2174
2429
|
if (decl.argument.type === 'Identifier') {
|
@@ -2190,9 +2445,26 @@ const generateUnary = (scope, decl) => {
|
|
2190
2445
|
|
2191
2446
|
out.push(...number(toReturn ? 1 : 0));
|
2192
2447
|
return out;
|
2448
|
+
}
|
2449
|
+
|
2450
|
+
case 'typeof': {
|
2451
|
+
let overrideType, toGenerate = true;
|
2452
|
+
|
2453
|
+
if (decl.argument.type === 'Identifier') {
|
2454
|
+
const out = generateIdent(scope, decl.argument);
|
2455
|
+
|
2456
|
+
// if ReferenceError (undeclared var), ignore and return undefined
|
2457
|
+
if (out[1]) {
|
2458
|
+
// does not exist (2 ops from throw)
|
2459
|
+
overrideType = number(TYPES.undefined, Valtype.i32);
|
2460
|
+
toGenerate = false;
|
2461
|
+
}
|
2462
|
+
}
|
2193
2463
|
|
2194
|
-
|
2195
|
-
|
2464
|
+
const out = toGenerate ? generate(scope, decl.argument) : [];
|
2465
|
+
disposeLeftover(out);
|
2466
|
+
|
2467
|
+
out.push(...typeSwitch(scope, overrideType ?? getNodeType(scope, decl.argument), {
|
2196
2468
|
[TYPES.number]: makeString(scope, 'number', false, '#typeof_result'),
|
2197
2469
|
[TYPES.boolean]: makeString(scope, 'boolean', false, '#typeof_result'),
|
2198
2470
|
[TYPES.string]: makeString(scope, 'string', false, '#typeof_result'),
|
@@ -2203,27 +2475,30 @@ const generateUnary = (scope, decl) => {
|
|
2203
2475
|
|
2204
2476
|
// object and internal types
|
2205
2477
|
default: makeString(scope, 'object', false, '#typeof_result'),
|
2206
|
-
});
|
2478
|
+
}));
|
2479
|
+
|
2480
|
+
return out;
|
2481
|
+
}
|
2207
2482
|
|
2208
2483
|
default:
|
2209
|
-
return todo(`unary operator ${decl.operator} not implemented yet
|
2484
|
+
return todo(scope, `unary operator ${decl.operator} not implemented yet`, true);
|
2210
2485
|
}
|
2211
2486
|
};
|
2212
2487
|
|
2213
|
-
const generateUpdate = (scope, decl) => {
|
2488
|
+
const generateUpdate = (scope, decl, _global, _name, valueUnused = false) => {
|
2214
2489
|
const { name } = decl.argument;
|
2215
2490
|
|
2216
2491
|
const [ local, isGlobal ] = lookupName(scope, name);
|
2217
2492
|
|
2218
2493
|
if (local === undefined) {
|
2219
|
-
return todo(`update expression with undefined variable
|
2494
|
+
return todo(scope, `update expression with undefined variable`, true);
|
2220
2495
|
}
|
2221
2496
|
|
2222
2497
|
const idx = local.idx;
|
2223
2498
|
const out = [];
|
2224
2499
|
|
2225
2500
|
out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2226
|
-
if (!decl.prefix) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2501
|
+
if (!decl.prefix && !valueUnused) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2227
2502
|
|
2228
2503
|
switch (decl.operator) {
|
2229
2504
|
case '++':
|
@@ -2236,7 +2511,7 @@ const generateUpdate = (scope, decl) => {
|
|
2236
2511
|
}
|
2237
2512
|
|
2238
2513
|
out.push([ isGlobal ? Opcodes.global_set : Opcodes.local_set, idx ]);
|
2239
|
-
if (decl.prefix) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2514
|
+
if (decl.prefix && !valueUnused) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2240
2515
|
|
2241
2516
|
return out;
|
2242
2517
|
};
|
@@ -2276,7 +2551,7 @@ const generateConditional = (scope, decl) => {
|
|
2276
2551
|
// note type
|
2277
2552
|
out.push(
|
2278
2553
|
...getNodeType(scope, decl.consequent),
|
2279
|
-
setLastType(scope)
|
2554
|
+
...setLastType(scope)
|
2280
2555
|
);
|
2281
2556
|
|
2282
2557
|
out.push([ Opcodes.else ]);
|
@@ -2285,7 +2560,7 @@ const generateConditional = (scope, decl) => {
|
|
2285
2560
|
// note type
|
2286
2561
|
out.push(
|
2287
2562
|
...getNodeType(scope, decl.alternate),
|
2288
|
-
setLastType(scope)
|
2563
|
+
...setLastType(scope)
|
2289
2564
|
);
|
2290
2565
|
|
2291
2566
|
out.push([ Opcodes.end ]);
|
@@ -2299,7 +2574,7 @@ const generateFor = (scope, decl) => {
|
|
2299
2574
|
const out = [];
|
2300
2575
|
|
2301
2576
|
if (decl.init) {
|
2302
|
-
out.push(...generate(scope, decl.init));
|
2577
|
+
out.push(...generate(scope, decl.init, false, undefined, true));
|
2303
2578
|
disposeLeftover(out);
|
2304
2579
|
}
|
2305
2580
|
|
@@ -2317,7 +2592,7 @@ const generateFor = (scope, decl) => {
|
|
2317
2592
|
out.push(...generate(scope, decl.body));
|
2318
2593
|
out.push([ Opcodes.end ]);
|
2319
2594
|
|
2320
|
-
if (decl.update) out.push(...generate(scope, decl.update));
|
2595
|
+
if (decl.update) out.push(...generate(scope, decl.update, false, undefined, true));
|
2321
2596
|
|
2322
2597
|
out.push([ Opcodes.br, 1 ]);
|
2323
2598
|
out.push([ Opcodes.end ], [ Opcodes.end ]);
|
@@ -2345,6 +2620,36 @@ const generateWhile = (scope, decl) => {
|
|
2345
2620
|
return out;
|
2346
2621
|
};
|
2347
2622
|
|
2623
|
+
const generateDoWhile = (scope, decl) => {
|
2624
|
+
const out = [];
|
2625
|
+
|
2626
|
+
out.push([ Opcodes.loop, Blocktype.void ]);
|
2627
|
+
depth.push('dowhile');
|
2628
|
+
|
2629
|
+
// block for break (includes all)
|
2630
|
+
out.push([ Opcodes.block, Blocktype.void ]);
|
2631
|
+
depth.push('block');
|
2632
|
+
|
2633
|
+
// block for continue
|
2634
|
+
// includes body but not test+loop so we can exit body at anytime
|
2635
|
+
// and still test+loop after
|
2636
|
+
out.push([ Opcodes.block, Blocktype.void ]);
|
2637
|
+
depth.push('block');
|
2638
|
+
|
2639
|
+
out.push(...generate(scope, decl.body));
|
2640
|
+
|
2641
|
+
out.push([ Opcodes.end ]);
|
2642
|
+
depth.pop();
|
2643
|
+
|
2644
|
+
out.push(...generate(scope, decl.test), Opcodes.i32_to);
|
2645
|
+
out.push([ Opcodes.br_if, 1 ]);
|
2646
|
+
|
2647
|
+
out.push([ Opcodes.end ], [ Opcodes.end ]);
|
2648
|
+
depth.pop(); depth.pop();
|
2649
|
+
|
2650
|
+
return out;
|
2651
|
+
};
|
2652
|
+
|
2348
2653
|
const generateForOf = (scope, decl) => {
|
2349
2654
|
const out = [];
|
2350
2655
|
|
@@ -2381,7 +2686,10 @@ const generateForOf = (scope, decl) => {
|
|
2381
2686
|
generateVar(scope, { kind: 'var', _bare: true, declarations: [ { id: { name: leftName } } ] })
|
2382
2687
|
}
|
2383
2688
|
|
2689
|
+
// if (!leftName) console.log(decl.left?.declarations?.[0]?.id ?? decl.left);
|
2690
|
+
|
2384
2691
|
const [ local, isGlobal ] = lookupName(scope, leftName);
|
2692
|
+
if (!local) return todo(scope, 'for of failed to get left local (probably destructure)');
|
2385
2693
|
|
2386
2694
|
depth.push('block');
|
2387
2695
|
depth.push('block');
|
@@ -2390,6 +2698,7 @@ const generateForOf = (scope, decl) => {
|
|
2390
2698
|
// hack: this is naughty and will break things!
|
2391
2699
|
let newOut = number(0, Valtype.f64), newPointer = -1;
|
2392
2700
|
if (pages.hasAnyString) {
|
2701
|
+
// todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
|
2393
2702
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2394
2703
|
rawElements: new Array(1)
|
2395
2704
|
}, isGlobal, leftName, true, 'i16');
|
@@ -2481,6 +2790,56 @@ const generateForOf = (scope, decl) => {
|
|
2481
2790
|
[ Opcodes.end ],
|
2482
2791
|
[ Opcodes.end ]
|
2483
2792
|
],
|
2793
|
+
[TYPES._bytestring]: [
|
2794
|
+
...setType(scope, leftName, TYPES._bytestring),
|
2795
|
+
|
2796
|
+
[ Opcodes.loop, Blocktype.void ],
|
2797
|
+
|
2798
|
+
// setup new/out array
|
2799
|
+
...newOut,
|
2800
|
+
[ Opcodes.drop ],
|
2801
|
+
|
2802
|
+
...number(0, Valtype.i32), // base 0 for store after
|
2803
|
+
|
2804
|
+
// load current string ind {arg}
|
2805
|
+
[ Opcodes.local_get, pointer ],
|
2806
|
+
[ Opcodes.local_get, counter ],
|
2807
|
+
[ Opcodes.i32_add ],
|
2808
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
2809
|
+
|
2810
|
+
// store to new string ind 0
|
2811
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
2812
|
+
|
2813
|
+
// return new string (page)
|
2814
|
+
...number(newPointer),
|
2815
|
+
|
2816
|
+
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2817
|
+
|
2818
|
+
[ Opcodes.block, Blocktype.void ],
|
2819
|
+
[ Opcodes.block, Blocktype.void ],
|
2820
|
+
...generate(scope, decl.body),
|
2821
|
+
[ Opcodes.end ],
|
2822
|
+
|
2823
|
+
// increment iter pointer
|
2824
|
+
// [ Opcodes.local_get, pointer ],
|
2825
|
+
// ...number(1, Valtype.i32),
|
2826
|
+
// [ Opcodes.i32_add ],
|
2827
|
+
// [ Opcodes.local_set, pointer ],
|
2828
|
+
|
2829
|
+
// increment counter by 1
|
2830
|
+
[ Opcodes.local_get, counter ],
|
2831
|
+
...number(1, Valtype.i32),
|
2832
|
+
[ Opcodes.i32_add ],
|
2833
|
+
[ Opcodes.local_tee, counter ],
|
2834
|
+
|
2835
|
+
// loop if counter != length
|
2836
|
+
[ Opcodes.local_get, length ],
|
2837
|
+
[ Opcodes.i32_ne ],
|
2838
|
+
[ Opcodes.br_if, 1 ],
|
2839
|
+
|
2840
|
+
[ Opcodes.end ],
|
2841
|
+
[ Opcodes.end ]
|
2842
|
+
],
|
2484
2843
|
default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
|
2485
2844
|
}, Blocktype.void));
|
2486
2845
|
|
@@ -2491,28 +2850,65 @@ const generateForOf = (scope, decl) => {
|
|
2491
2850
|
return out;
|
2492
2851
|
};
|
2493
2852
|
|
2853
|
+
// find the nearest loop in depth map by type
|
2494
2854
|
const getNearestLoop = () => {
|
2495
2855
|
for (let i = depth.length - 1; i >= 0; i--) {
|
2496
|
-
if (
|
2856
|
+
if (['while', 'dowhile', 'for', 'forof'].includes(depth[i])) return i;
|
2497
2857
|
}
|
2498
2858
|
|
2499
2859
|
return -1;
|
2500
2860
|
};
|
2501
2861
|
|
2502
2862
|
const generateBreak = (scope, decl) => {
|
2503
|
-
const
|
2863
|
+
const target = decl.label ? scope.labels.get(decl.label.name) : getNearestLoop();
|
2864
|
+
const type = depth[target];
|
2865
|
+
|
2866
|
+
// different loop types have different branch offsets
|
2867
|
+
// as they have different wasm block/loop/if structures
|
2868
|
+
// we need to use the right offset by type to branch to the one we want
|
2869
|
+
// for a break: exit the loop without executing anything else inside it
|
2870
|
+
const offset = ({
|
2871
|
+
for: 2, // loop > if (wanted branch) > block (we are here)
|
2872
|
+
while: 2, // loop > if (wanted branch) (we are here)
|
2873
|
+
dowhile: 2, // loop > block (wanted branch) > block (we are here)
|
2874
|
+
forof: 2, // loop > block (wanted branch) > block (we are here)
|
2875
|
+
if: 1 // break inside if, branch 0 to skip the rest of the if
|
2876
|
+
})[type];
|
2877
|
+
|
2504
2878
|
return [
|
2505
|
-
[ Opcodes.br, ...signedLEB128(
|
2879
|
+
[ Opcodes.br, ...signedLEB128(depth.length - target - offset) ]
|
2506
2880
|
];
|
2507
2881
|
};
|
2508
2882
|
|
2509
2883
|
const generateContinue = (scope, decl) => {
|
2510
|
-
const
|
2884
|
+
const target = decl.label ? scope.labels.get(decl.label.name) : getNearestLoop();
|
2885
|
+
const type = depth[target];
|
2886
|
+
|
2887
|
+
// different loop types have different branch offsets
|
2888
|
+
// as they have different wasm block/loop/if structures
|
2889
|
+
// we need to use the right offset by type to branch to the one we want
|
2890
|
+
// for a continue: do test for the loop, and then loop depending on that success
|
2891
|
+
const offset = ({
|
2892
|
+
for: 3, // loop (wanted branch) > if > block (we are here)
|
2893
|
+
while: 1, // loop (wanted branch) > if (we are here)
|
2894
|
+
dowhile: 3, // loop > block > block (wanted branch) (we are here)
|
2895
|
+
forof: 3 // loop > block > block (wanted branch) (we are here)
|
2896
|
+
})[type];
|
2897
|
+
|
2511
2898
|
return [
|
2512
|
-
[ Opcodes.br, ...signedLEB128(
|
2899
|
+
[ Opcodes.br, ...signedLEB128(depth.length - target - offset) ]
|
2513
2900
|
];
|
2514
2901
|
};
|
2515
2902
|
|
2903
|
+
const generateLabel = (scope, decl) => {
|
2904
|
+
scope.labels ??= new Map();
|
2905
|
+
|
2906
|
+
const name = decl.label.name;
|
2907
|
+
scope.labels.set(name, depth.length);
|
2908
|
+
|
2909
|
+
return generate(scope, decl.body);
|
2910
|
+
};
|
2911
|
+
|
2516
2912
|
const generateThrow = (scope, decl) => {
|
2517
2913
|
scope.throws = true;
|
2518
2914
|
|
@@ -2533,6 +2929,9 @@ const generateThrow = (scope, decl) => {
|
|
2533
2929
|
let exceptId = exceptions.push({ constructor, message }) - 1;
|
2534
2930
|
let tagIdx = tags[0].idx;
|
2535
2931
|
|
2932
|
+
scope.exceptions ??= [];
|
2933
|
+
scope.exceptions.push(exceptId);
|
2934
|
+
|
2536
2935
|
// todo: write a description of how this works lol
|
2537
2936
|
|
2538
2937
|
return [
|
@@ -2542,7 +2941,7 @@ const generateThrow = (scope, decl) => {
|
|
2542
2941
|
};
|
2543
2942
|
|
2544
2943
|
const generateTry = (scope, decl) => {
|
2545
|
-
if (decl.finalizer) return todo('try finally not implemented yet');
|
2944
|
+
if (decl.finalizer) return todo(scope, 'try finally not implemented yet');
|
2546
2945
|
|
2547
2946
|
const out = [];
|
2548
2947
|
|
@@ -2573,11 +2972,11 @@ const generateAssignPat = (scope, decl) => {
|
|
2573
2972
|
// TODO
|
2574
2973
|
// if identifier declared, use that
|
2575
2974
|
// else, use default (right)
|
2576
|
-
return todo('assignment pattern (optional arg)');
|
2975
|
+
return todo(scope, 'assignment pattern (optional arg)');
|
2577
2976
|
};
|
2578
2977
|
|
2579
2978
|
let pages = new Map();
|
2580
|
-
const allocPage = (reason, type) => {
|
2979
|
+
const allocPage = (scope, reason, type) => {
|
2581
2980
|
if (pages.has(reason)) return pages.get(reason).ind;
|
2582
2981
|
|
2583
2982
|
if (reason.startsWith('array:')) pages.hasArray = true;
|
@@ -2588,16 +2987,20 @@ const allocPage = (reason, type) => {
|
|
2588
2987
|
const ind = pages.size;
|
2589
2988
|
pages.set(reason, { ind, type });
|
2590
2989
|
|
2591
|
-
|
2990
|
+
scope.pages ??= new Map();
|
2991
|
+
scope.pages.set(reason, { ind, type });
|
2992
|
+
|
2993
|
+
if (Prefs.allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
|
2592
2994
|
|
2593
2995
|
return ind;
|
2594
2996
|
};
|
2595
2997
|
|
2998
|
+
// todo: add scope.pages
|
2596
2999
|
const freePage = reason => {
|
2597
3000
|
const { ind } = pages.get(reason);
|
2598
3001
|
pages.delete(reason);
|
2599
3002
|
|
2600
|
-
if (allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
3003
|
+
if (Prefs.allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
2601
3004
|
|
2602
3005
|
return ind;
|
2603
3006
|
};
|
@@ -2623,15 +3026,14 @@ const StoreOps = {
|
|
2623
3026
|
|
2624
3027
|
let data = [];
|
2625
3028
|
|
2626
|
-
const compileBytes = (val, itemType
|
3029
|
+
const compileBytes = (val, itemType) => {
|
2627
3030
|
// todo: this is a mess and needs confirming / ????
|
2628
3031
|
switch (itemType) {
|
2629
3032
|
case 'i8': return [ val % 256 ];
|
2630
|
-
case 'i16': return [ val % 256,
|
2631
|
-
|
2632
|
-
case 'i32':
|
2633
|
-
|
2634
|
-
return enforceFourBytes(signedLEB128(val));
|
3033
|
+
case 'i16': return [ val % 256, (val / 256 | 0) % 256 ];
|
3034
|
+
case 'i16': return [ val % 256, (val / 256 | 0) % 256 ];
|
3035
|
+
case 'i32': return [...new Uint8Array(new Int32Array([ val ]).buffer)];
|
3036
|
+
// todo: i64
|
2635
3037
|
|
2636
3038
|
case 'f64': return ieee754_binary64(val);
|
2637
3039
|
}
|
@@ -2649,16 +3051,20 @@ const getAllocType = itemType => {
|
|
2649
3051
|
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
2650
3052
|
const out = [];
|
2651
3053
|
|
3054
|
+
scope.arrays ??= new Map();
|
3055
|
+
|
2652
3056
|
let firstAssign = false;
|
2653
|
-
if (!arrays.has(name) || name === '$undeclared') {
|
3057
|
+
if (!scope.arrays.has(name) || name === '$undeclared') {
|
2654
3058
|
firstAssign = true;
|
2655
3059
|
|
2656
3060
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
2657
3061
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
2658
|
-
|
3062
|
+
|
3063
|
+
if (Prefs.scopedPageNames) scope.arrays.set(name, allocPage(scope, `${scope.name} | ${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
|
3064
|
+
else scope.arrays.set(name, allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
|
2659
3065
|
}
|
2660
3066
|
|
2661
|
-
const pointer = arrays.get(name);
|
3067
|
+
const pointer = scope.arrays.get(name);
|
2662
3068
|
|
2663
3069
|
const useRawElements = !!decl.rawElements;
|
2664
3070
|
const elements = useRawElements ? decl.rawElements : decl.elements;
|
@@ -2666,19 +3072,25 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2666
3072
|
const valtype = itemTypeToValtype[itemType];
|
2667
3073
|
const length = elements.length;
|
2668
3074
|
|
2669
|
-
if (firstAssign && useRawElements) {
|
2670
|
-
|
3075
|
+
if (firstAssign && useRawElements && !Prefs.noData) {
|
3076
|
+
// if length is 0 memory/data will just be 0000... anyway
|
3077
|
+
if (length !== 0) {
|
3078
|
+
let bytes = compileBytes(length, 'i32');
|
2671
3079
|
|
2672
|
-
|
2673
|
-
|
3080
|
+
if (!initEmpty) for (let i = 0; i < length; i++) {
|
3081
|
+
if (elements[i] == null) continue;
|
2674
3082
|
|
2675
|
-
|
2676
|
-
|
3083
|
+
bytes.push(...compileBytes(elements[i], itemType));
|
3084
|
+
}
|
2677
3085
|
|
2678
|
-
|
2679
|
-
|
2680
|
-
|
2681
|
-
|
3086
|
+
const ind = data.push({
|
3087
|
+
offset: pointer,
|
3088
|
+
bytes
|
3089
|
+
}) - 1;
|
3090
|
+
|
3091
|
+
scope.data ??= [];
|
3092
|
+
scope.data.push(ind);
|
3093
|
+
}
|
2682
3094
|
|
2683
3095
|
// local value as pointer
|
2684
3096
|
out.push(...number(pointer));
|
@@ -2712,7 +3124,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2712
3124
|
};
|
2713
3125
|
|
2714
3126
|
const byteStringable = str => {
|
2715
|
-
if (!
|
3127
|
+
if (!Prefs.bytestring) return false;
|
2716
3128
|
|
2717
3129
|
for (let i = 0; i < str.length; i++) {
|
2718
3130
|
if (str.charCodeAt(i) > 0xFF) return false;
|
@@ -2721,9 +3133,9 @@ const byteStringable = str => {
|
|
2721
3133
|
return true;
|
2722
3134
|
};
|
2723
3135
|
|
2724
|
-
const makeString = (scope, str, global = false, name = '$undeclared') => {
|
3136
|
+
const makeString = (scope, str, global = false, name = '$undeclared', forceBytestring = undefined) => {
|
2725
3137
|
const rawElements = new Array(str.length);
|
2726
|
-
let byteStringable =
|
3138
|
+
let byteStringable = Prefs.bytestring;
|
2727
3139
|
for (let i = 0; i < str.length; i++) {
|
2728
3140
|
const c = str.charCodeAt(i);
|
2729
3141
|
rawElements[i] = c;
|
@@ -2731,25 +3143,36 @@ const makeString = (scope, str, global = false, name = '$undeclared') => {
|
|
2731
3143
|
if (byteStringable && c > 0xFF) byteStringable = false;
|
2732
3144
|
}
|
2733
3145
|
|
3146
|
+
if (byteStringable && forceBytestring === false) byteStringable = false;
|
3147
|
+
|
2734
3148
|
return makeArray(scope, {
|
2735
3149
|
rawElements
|
2736
3150
|
}, global, name, false, byteStringable ? 'i8' : 'i16')[0];
|
2737
3151
|
};
|
2738
3152
|
|
2739
|
-
let arrays = new Map();
|
2740
3153
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
2741
3154
|
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
2742
3155
|
};
|
2743
3156
|
|
2744
3157
|
export const generateMember = (scope, decl, _global, _name) => {
|
2745
3158
|
const name = decl.object.name;
|
2746
|
-
const pointer = arrays
|
3159
|
+
const pointer = scope.arrays?.get(name);
|
2747
3160
|
|
2748
|
-
const aotPointer = pointer != null;
|
3161
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2749
3162
|
|
2750
3163
|
// hack: .length
|
2751
3164
|
if (decl.property.name === 'length') {
|
2752
|
-
|
3165
|
+
const func = funcs.find(x => x.name === name);
|
3166
|
+
if (func) {
|
3167
|
+
const userFunc = funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name];
|
3168
|
+
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
3169
|
+
return number(typedParams ? func.params.length / 2 : func.params.length);
|
3170
|
+
}
|
3171
|
+
|
3172
|
+
if (builtinFuncs[name]) return number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length);
|
3173
|
+
if (importedFuncs[name]) return number(importedFuncs[name].params);
|
3174
|
+
if (internalConstrs[name]) return number(internalConstrs[name].length ?? 0);
|
3175
|
+
|
2753
3176
|
return [
|
2754
3177
|
...(aotPointer ? number(0, Valtype.i32) : [
|
2755
3178
|
...generate(scope, decl.object),
|
@@ -2793,7 +3216,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2793
3216
|
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2794
3217
|
|
2795
3218
|
...number(TYPES.number, Valtype.i32),
|
2796
|
-
setLastType(scope)
|
3219
|
+
...setLastType(scope)
|
2797
3220
|
],
|
2798
3221
|
|
2799
3222
|
[TYPES.string]: [
|
@@ -2825,7 +3248,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2825
3248
|
...number(newPointer),
|
2826
3249
|
|
2827
3250
|
...number(TYPES.string, Valtype.i32),
|
2828
|
-
setLastType(scope)
|
3251
|
+
...setLastType(scope)
|
2829
3252
|
],
|
2830
3253
|
[TYPES._bytestring]: [
|
2831
3254
|
// setup new/out array
|
@@ -2844,19 +3267,19 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2844
3267
|
]),
|
2845
3268
|
|
2846
3269
|
// load current string ind {arg}
|
2847
|
-
[ Opcodes.i32_load8_u,
|
3270
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2848
3271
|
|
2849
3272
|
// store to new string ind 0
|
2850
|
-
[ Opcodes.i32_store8,
|
3273
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
2851
3274
|
|
2852
3275
|
// return new string (page)
|
2853
3276
|
...number(newPointer),
|
2854
3277
|
|
2855
3278
|
...number(TYPES._bytestring, Valtype.i32),
|
2856
|
-
setLastType(scope)
|
3279
|
+
...setLastType(scope)
|
2857
3280
|
],
|
2858
3281
|
|
2859
|
-
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet')
|
3282
|
+
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
|
2860
3283
|
});
|
2861
3284
|
};
|
2862
3285
|
|
@@ -2866,28 +3289,36 @@ const objectHack = node => {
|
|
2866
3289
|
if (!node) return node;
|
2867
3290
|
|
2868
3291
|
if (node.type === 'MemberExpression') {
|
2869
|
-
|
3292
|
+
const out = (() => {
|
3293
|
+
if (node.computed || node.optional) return;
|
2870
3294
|
|
2871
|
-
|
3295
|
+
let objectName = node.object.name;
|
2872
3296
|
|
2873
|
-
|
2874
|
-
|
3297
|
+
// if object is not identifier or another member exp, give up
|
3298
|
+
if (node.object.type !== 'Identifier' && node.object.type !== 'MemberExpression') return;
|
3299
|
+
if (objectName && ['undefined', 'null', 'NaN', 'Infinity'].includes(objectName)) return;
|
2875
3300
|
|
2876
|
-
|
3301
|
+
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
2877
3302
|
|
2878
|
-
|
2879
|
-
|
3303
|
+
// if .length, give up (hack within a hack!)
|
3304
|
+
if (node.property.name === 'length') {
|
3305
|
+
node.object = objectHack(node.object);
|
3306
|
+
return;
|
3307
|
+
}
|
2880
3308
|
|
2881
|
-
|
2882
|
-
|
3309
|
+
// no object name, give up
|
3310
|
+
if (!objectName) return;
|
2883
3311
|
|
2884
|
-
|
2885
|
-
|
3312
|
+
const name = '__' + objectName + '_' + node.property.name;
|
3313
|
+
if (Prefs.codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
2886
3314
|
|
2887
|
-
|
2888
|
-
|
2889
|
-
|
2890
|
-
|
3315
|
+
return {
|
3316
|
+
type: 'Identifier',
|
3317
|
+
name
|
3318
|
+
};
|
3319
|
+
})();
|
3320
|
+
|
3321
|
+
if (out) return out;
|
2891
3322
|
}
|
2892
3323
|
|
2893
3324
|
for (const x in node) {
|
@@ -2901,8 +3332,8 @@ const objectHack = node => {
|
|
2901
3332
|
};
|
2902
3333
|
|
2903
3334
|
const generateFunc = (scope, decl) => {
|
2904
|
-
if (decl.async) return todo('async functions are not supported');
|
2905
|
-
if (decl.generator) return todo('generator functions are not supported');
|
3335
|
+
if (decl.async) return todo(scope, 'async functions are not supported');
|
3336
|
+
if (decl.generator) return todo(scope, 'generator functions are not supported');
|
2906
3337
|
|
2907
3338
|
const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
|
2908
3339
|
const params = decl.params ?? [];
|
@@ -2918,6 +3349,11 @@ const generateFunc = (scope, decl) => {
|
|
2918
3349
|
name
|
2919
3350
|
};
|
2920
3351
|
|
3352
|
+
if (typedInput && decl.returnType) {
|
3353
|
+
innerScope.returnType = extractTypeAnnotation(decl.returnType).type;
|
3354
|
+
innerScope.returns = [ valtypeBinary ];
|
3355
|
+
}
|
3356
|
+
|
2921
3357
|
for (let i = 0; i < params.length; i++) {
|
2922
3358
|
allocVar(innerScope, params[i].name, false);
|
2923
3359
|
|
@@ -2944,6 +3380,8 @@ const generateFunc = (scope, decl) => {
|
|
2944
3380
|
};
|
2945
3381
|
funcIndex[name] = func.index;
|
2946
3382
|
|
3383
|
+
if (name === 'main') func.gotLastType = true;
|
3384
|
+
|
2947
3385
|
// quick hack fixes
|
2948
3386
|
for (const inst of wasm) {
|
2949
3387
|
if (inst[0] === Opcodes.call && inst[1] === -1) {
|
@@ -2978,16 +3416,6 @@ const generateCode = (scope, decl) => {
|
|
2978
3416
|
};
|
2979
3417
|
|
2980
3418
|
const internalConstrs = {
|
2981
|
-
Boolean: {
|
2982
|
-
generate: (scope, decl) => {
|
2983
|
-
if (decl.arguments.length === 0) return number(0);
|
2984
|
-
|
2985
|
-
// should generate/run all args
|
2986
|
-
return truthy(scope, generate(scope, decl.arguments[0]), getNodeType(scope, decl.arguments[0]), false, false);
|
2987
|
-
},
|
2988
|
-
type: TYPES.boolean
|
2989
|
-
},
|
2990
|
-
|
2991
3419
|
Array: {
|
2992
3420
|
generate: (scope, decl, global, name) => {
|
2993
3421
|
// new Array(i0, i1, ...)
|
@@ -3005,7 +3433,7 @@ const internalConstrs = {
|
|
3005
3433
|
|
3006
3434
|
// todo: check in wasm instead of here
|
3007
3435
|
const literalValue = arg.value ?? 0;
|
3008
|
-
if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length');
|
3436
|
+
if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length', true);
|
3009
3437
|
|
3010
3438
|
return [
|
3011
3439
|
...number(0, Valtype.i32),
|
@@ -3016,7 +3444,8 @@ const internalConstrs = {
|
|
3016
3444
|
...number(pointer)
|
3017
3445
|
];
|
3018
3446
|
},
|
3019
|
-
type: TYPES._array
|
3447
|
+
type: TYPES._array,
|
3448
|
+
length: 1
|
3020
3449
|
},
|
3021
3450
|
|
3022
3451
|
__Array_of: {
|
@@ -3028,7 +3457,94 @@ const internalConstrs = {
|
|
3028
3457
|
}, global, name);
|
3029
3458
|
},
|
3030
3459
|
type: TYPES._array,
|
3460
|
+
notConstr: true,
|
3461
|
+
length: 0
|
3462
|
+
},
|
3463
|
+
|
3464
|
+
__Porffor_fastOr: {
|
3465
|
+
generate: (scope, decl) => {
|
3466
|
+
const out = [];
|
3467
|
+
|
3468
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3469
|
+
out.push(
|
3470
|
+
...generate(scope, decl.arguments[i]),
|
3471
|
+
Opcodes.i32_to_u,
|
3472
|
+
...(i > 0 ? [ [ Opcodes.i32_or ] ] : [])
|
3473
|
+
);
|
3474
|
+
}
|
3475
|
+
|
3476
|
+
return out;
|
3477
|
+
},
|
3478
|
+
type: TYPES.boolean,
|
3031
3479
|
notConstr: true
|
3480
|
+
},
|
3481
|
+
|
3482
|
+
__Porffor_fastAnd: {
|
3483
|
+
generate: (scope, decl) => {
|
3484
|
+
const out = [];
|
3485
|
+
|
3486
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3487
|
+
out.push(
|
3488
|
+
...generate(scope, decl.arguments[i]),
|
3489
|
+
Opcodes.i32_to_u,
|
3490
|
+
...(i > 0 ? [ [ Opcodes.i32_and ] ] : [])
|
3491
|
+
);
|
3492
|
+
}
|
3493
|
+
|
3494
|
+
return out;
|
3495
|
+
},
|
3496
|
+
type: TYPES.boolean,
|
3497
|
+
notConstr: true
|
3498
|
+
},
|
3499
|
+
|
3500
|
+
Boolean: {
|
3501
|
+
generate: (scope, decl) => {
|
3502
|
+
// todo: boolean object when used as constructor
|
3503
|
+
const arg = decl.arguments[0] ?? DEFAULT_VALUE;
|
3504
|
+
return truthy(scope, generate(scope, arg), getNodeType(scope, arg));
|
3505
|
+
},
|
3506
|
+
type: TYPES.boolean,
|
3507
|
+
length: 1
|
3508
|
+
},
|
3509
|
+
|
3510
|
+
__Math_max: {
|
3511
|
+
generate: (scope, decl) => {
|
3512
|
+
const out = [
|
3513
|
+
...number(-Infinity)
|
3514
|
+
];
|
3515
|
+
|
3516
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3517
|
+
out.push(
|
3518
|
+
...generate(scope, decl.arguments[i]),
|
3519
|
+
[ Opcodes.f64_max ]
|
3520
|
+
);
|
3521
|
+
}
|
3522
|
+
|
3523
|
+
return out;
|
3524
|
+
},
|
3525
|
+
type: TYPES.number,
|
3526
|
+
notConstr: true,
|
3527
|
+
length: 2
|
3528
|
+
},
|
3529
|
+
|
3530
|
+
__Math_min: {
|
3531
|
+
generate: (scope, decl) => {
|
3532
|
+
const out = [
|
3533
|
+
...number(Infinity)
|
3534
|
+
];
|
3535
|
+
|
3536
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3537
|
+
out.push(
|
3538
|
+
...generate(scope, decl.arguments[i]),
|
3539
|
+
[ Opcodes.f64_min ]
|
3540
|
+
);
|
3541
|
+
}
|
3542
|
+
|
3543
|
+
return out;
|
3544
|
+
},
|
3545
|
+
type: TYPES.number,
|
3546
|
+
notConstr: true,
|
3547
|
+
length: 2
|
3032
3548
|
}
|
3033
3549
|
};
|
3034
3550
|
|
@@ -3057,7 +3573,6 @@ export default program => {
|
|
3057
3573
|
funcs = [];
|
3058
3574
|
funcIndex = {};
|
3059
3575
|
depth = [];
|
3060
|
-
arrays = new Map();
|
3061
3576
|
pages = new Map();
|
3062
3577
|
data = [];
|
3063
3578
|
currentFuncIndex = importedFuncs.length;
|
@@ -3071,6 +3586,10 @@ export default program => {
|
|
3071
3586
|
|
3072
3587
|
const valtypeInd = ['i32', 'i64', 'f64'].indexOf(valtype);
|
3073
3588
|
|
3589
|
+
globalThis.pageSize = PageSize;
|
3590
|
+
const pageSizeOpt = process.argv.find(x => x.startsWith('-page-size='));
|
3591
|
+
if (pageSizeOpt) pageSize = parseInt(pageSizeOpt.split('=')[1]) * 1024;
|
3592
|
+
|
3074
3593
|
// set generic opcodes for current valtype
|
3075
3594
|
Opcodes.const = [ Opcodes.i32_const, Opcodes.i64_const, Opcodes.f64_const ][valtypeInd];
|
3076
3595
|
Opcodes.eq = [ Opcodes.i32_eq, Opcodes.i64_eq, Opcodes.f64_eq ][valtypeInd];
|
@@ -3079,10 +3598,10 @@ export default program => {
|
|
3079
3598
|
Opcodes.add = [ Opcodes.i32_add, Opcodes.i64_add, Opcodes.f64_add ][valtypeInd];
|
3080
3599
|
Opcodes.sub = [ Opcodes.i32_sub, Opcodes.i64_sub, Opcodes.f64_sub ][valtypeInd];
|
3081
3600
|
|
3082
|
-
Opcodes.i32_to = [ [
|
3083
|
-
Opcodes.i32_to_u = [ [
|
3084
|
-
Opcodes.i32_from = [ [
|
3085
|
-
Opcodes.i32_from_u = [ [
|
3601
|
+
Opcodes.i32_to = [ [], [ Opcodes.i32_wrap_i64 ], Opcodes.i32_trunc_sat_f64_s ][valtypeInd];
|
3602
|
+
Opcodes.i32_to_u = [ [], [ Opcodes.i32_wrap_i64 ], Opcodes.i32_trunc_sat_f64_u ][valtypeInd];
|
3603
|
+
Opcodes.i32_from = [ [], [ Opcodes.i64_extend_i32_s ], [ Opcodes.f64_convert_i32_s ] ][valtypeInd];
|
3604
|
+
Opcodes.i32_from_u = [ [], [ Opcodes.i64_extend_i32_u ], [ Opcodes.f64_convert_i32_u ] ][valtypeInd];
|
3086
3605
|
|
3087
3606
|
Opcodes.load = [ Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load ][valtypeInd];
|
3088
3607
|
Opcodes.store = [ Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store ][valtypeInd];
|
@@ -3095,10 +3614,6 @@ export default program => {
|
|
3095
3614
|
|
3096
3615
|
program.id = { name: 'main' };
|
3097
3616
|
|
3098
|
-
globalThis.pageSize = PageSize;
|
3099
|
-
const pageSizeOpt = process.argv.find(x => x.startsWith('-page-size='));
|
3100
|
-
if (pageSizeOpt) pageSize = parseInt(pageSizeOpt.split('=')[1]) * 1024;
|
3101
|
-
|
3102
3617
|
const scope = {
|
3103
3618
|
locals: {},
|
3104
3619
|
localInd: 0
|
@@ -3109,7 +3624,7 @@ export default program => {
|
|
3109
3624
|
body: program.body
|
3110
3625
|
};
|
3111
3626
|
|
3112
|
-
if (
|
3627
|
+
if (Prefs.astLog) console.log(JSON.stringify(program.body.body, null, 2));
|
3113
3628
|
|
3114
3629
|
generateFunc(scope, program);
|
3115
3630
|
|
@@ -3126,7 +3641,11 @@ export default program => {
|
|
3126
3641
|
}
|
3127
3642
|
|
3128
3643
|
if (lastInst[0] === Opcodes.end || lastInst[0] === Opcodes.local_set || lastInst[0] === Opcodes.global_set) {
|
3129
|
-
|
3644
|
+
if (lastInst[0] === Opcodes.local_set && lastInst[1] === main.locals['#last_type'].idx) {
|
3645
|
+
main.wasm.splice(main.wasm.length - 1, 1);
|
3646
|
+
} else {
|
3647
|
+
main.returns = [];
|
3648
|
+
}
|
3130
3649
|
}
|
3131
3650
|
|
3132
3651
|
if (lastInst[0] === Opcodes.call) {
|