porffor 0.2.0-623cdf0 → 0.2.0-69d30a8
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 +12 -12
- 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 +443 -273
- package/compiler/{codeGen.js → codegen.js} +841 -337
- package/compiler/embedding.js +22 -22
- package/compiler/encoding.js +108 -10
- package/compiler/generated_builtins.js +695 -0
- package/compiler/index.js +25 -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 +35 -9
- 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/filesize.cmd +0 -2
- 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,7 +204,11 @@ 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
|
}
|
@@ -197,34 +217,52 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
197
217
|
},
|
198
218
|
|
199
219
|
__Porffor_bs: str => [
|
200
|
-
...makeString(scope, str,
|
220
|
+
...makeString(scope, str, global, name, true),
|
201
221
|
|
202
|
-
...
|
203
|
-
|
222
|
+
...(name ? setType(scope, name, TYPES._bytestring) : [
|
223
|
+
...number(TYPES._bytestring, Valtype.i32),
|
224
|
+
...setLastType(scope)
|
225
|
+
])
|
204
226
|
],
|
205
227
|
__Porffor_s: str => [
|
206
|
-
...makeString(scope, str,
|
228
|
+
...makeString(scope, str, global, name, false),
|
207
229
|
|
208
|
-
...
|
209
|
-
|
230
|
+
...(name ? setType(scope, name, TYPES.string) : [
|
231
|
+
...number(TYPES.string, Valtype.i32),
|
232
|
+
...setLastType(scope)
|
233
|
+
])
|
210
234
|
],
|
211
235
|
};
|
212
236
|
|
213
|
-
const
|
237
|
+
const func = decl.tag.name;
|
214
238
|
// hack for inline asm
|
215
|
-
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;
|
243
|
+
|
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
|
+
}
|
216
254
|
|
217
|
-
|
218
|
-
return funcs[name](str);
|
255
|
+
return funcs[func](str);
|
219
256
|
}
|
220
257
|
|
221
258
|
default:
|
222
|
-
|
223
|
-
|
259
|
+
// ignore typescript nodes
|
260
|
+
if (decl.type.startsWith('TS') ||
|
261
|
+
decl.type === 'ImportDeclaration' && decl.importKind === 'type') {
|
224
262
|
return [];
|
225
263
|
}
|
226
264
|
|
227
|
-
return todo(`no generation for ${decl.type}!`);
|
265
|
+
return todo(scope, `no generation for ${decl.type}!`);
|
228
266
|
}
|
229
267
|
};
|
230
268
|
|
@@ -252,7 +290,7 @@ const lookupName = (scope, _name) => {
|
|
252
290
|
return [ undefined, undefined ];
|
253
291
|
};
|
254
292
|
|
255
|
-
const internalThrow = (scope, constructor, message, expectsValue =
|
293
|
+
const internalThrow = (scope, constructor, message, expectsValue = Prefs.alwaysValueInternalThrows) => [
|
256
294
|
...generateThrow(scope, {
|
257
295
|
argument: {
|
258
296
|
type: 'NewExpression',
|
@@ -276,7 +314,10 @@ const generateIdent = (scope, decl) => {
|
|
276
314
|
|
277
315
|
if (Object.hasOwn(builtinVars, name)) {
|
278
316
|
if (builtinVars[name].floatOnly && valtype[0] === 'i') throw new Error(`Cannot use ${unhackName(name)} with integer valtype`);
|
279
|
-
|
317
|
+
|
318
|
+
let wasm = builtinVars[name];
|
319
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, { name });
|
320
|
+
return wasm.slice();
|
280
321
|
}
|
281
322
|
|
282
323
|
if (Object.hasOwn(builtinFuncs, name) || Object.hasOwn(internalConstrs, name)) {
|
@@ -284,6 +325,11 @@ const generateIdent = (scope, decl) => {
|
|
284
325
|
return number(1);
|
285
326
|
}
|
286
327
|
|
328
|
+
if (isExistingProtoFunc(name)) {
|
329
|
+
// todo: return an actual something
|
330
|
+
return number(1);
|
331
|
+
}
|
332
|
+
|
287
333
|
if (local?.idx === undefined) {
|
288
334
|
// no local var with name
|
289
335
|
if (Object.hasOwn(importedFuncs, name)) return number(importedFuncs[name]);
|
@@ -314,14 +360,18 @@ const generateReturn = (scope, decl) => {
|
|
314
360
|
// just bare "return"
|
315
361
|
return [
|
316
362
|
...number(UNDEFINED), // "undefined" if func returns
|
317
|
-
...
|
363
|
+
...(scope.returnType != null ? [] : [
|
364
|
+
...number(TYPES.undefined, Valtype.i32) // type undefined
|
365
|
+
]),
|
318
366
|
[ Opcodes.return ]
|
319
367
|
];
|
320
368
|
}
|
321
369
|
|
322
370
|
return [
|
323
371
|
...generate(scope, decl.argument),
|
324
|
-
...
|
372
|
+
...(scope.returnType != null ? [] : [
|
373
|
+
...getNodeType(scope, decl.argument)
|
374
|
+
]),
|
325
375
|
[ Opcodes.return ]
|
326
376
|
];
|
327
377
|
};
|
@@ -335,7 +385,8 @@ const localTmp = (scope, name, type = valtypeBinary) => {
|
|
335
385
|
return idx;
|
336
386
|
};
|
337
387
|
|
338
|
-
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);
|
339
390
|
|
340
391
|
const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
341
392
|
const checks = {
|
@@ -344,7 +395,7 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
344
395
|
'??': nullish
|
345
396
|
};
|
346
397
|
|
347
|
-
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);
|
348
399
|
|
349
400
|
// generic structure for {a} OP {b}
|
350
401
|
// -->
|
@@ -352,8 +403,8 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
352
403
|
|
353
404
|
// if we can, use int tmp and convert at the end to help prevent unneeded conversions
|
354
405
|
// (like if we are in an if condition - very common)
|
355
|
-
const leftIsInt =
|
356
|
-
const rightIsInt =
|
406
|
+
const leftIsInt = isFloatToIntOp(left[left.length - 1]);
|
407
|
+
const rightIsInt = isFloatToIntOp(right[right.length - 1]);
|
357
408
|
|
358
409
|
const canInt = leftIsInt && rightIsInt;
|
359
410
|
|
@@ -370,12 +421,12 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
370
421
|
...right,
|
371
422
|
// note type
|
372
423
|
...rightType,
|
373
|
-
setLastType(scope),
|
424
|
+
...setLastType(scope),
|
374
425
|
[ Opcodes.else ],
|
375
426
|
[ Opcodes.local_get, localTmp(scope, 'logictmpi', Valtype.i32) ],
|
376
427
|
// note type
|
377
428
|
...leftType,
|
378
|
-
setLastType(scope),
|
429
|
+
...setLastType(scope),
|
379
430
|
[ Opcodes.end ],
|
380
431
|
Opcodes.i32_from
|
381
432
|
];
|
@@ -389,17 +440,17 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
389
440
|
...right,
|
390
441
|
// note type
|
391
442
|
...rightType,
|
392
|
-
setLastType(scope),
|
443
|
+
...setLastType(scope),
|
393
444
|
[ Opcodes.else ],
|
394
445
|
[ Opcodes.local_get, localTmp(scope, 'logictmp') ],
|
395
446
|
// note type
|
396
447
|
...leftType,
|
397
|
-
setLastType(scope),
|
448
|
+
...setLastType(scope),
|
398
449
|
[ Opcodes.end ]
|
399
450
|
];
|
400
451
|
};
|
401
452
|
|
402
|
-
const concatStrings = (scope, left, right, global, name, assign) => {
|
453
|
+
const concatStrings = (scope, left, right, global, name, assign = false, bytestrings = false) => {
|
403
454
|
// todo: this should be rewritten into a built-in/func: String.prototype.concat
|
404
455
|
// todo: convert left and right to strings if not
|
405
456
|
// todo: optimize by looking up names in arrays and using that if exists?
|
@@ -409,11 +460,8 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
409
460
|
const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
|
410
461
|
const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
|
411
462
|
|
412
|
-
const aotWFA = process.argv.includes('-aot-well-formed-string-approximation');
|
413
|
-
if (aotWFA) addVarMeta(name, { wellFormed: undefined });
|
414
|
-
|
415
463
|
if (assign) {
|
416
|
-
const pointer = arrays
|
464
|
+
const pointer = scope.arrays?.get(name ?? '$undeclared');
|
417
465
|
|
418
466
|
return [
|
419
467
|
// setup right
|
@@ -438,11 +486,11 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
438
486
|
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ],
|
439
487
|
|
440
488
|
// copy right
|
441
|
-
// dst = out pointer + length size + current length *
|
489
|
+
// dst = out pointer + length size + current length * sizeof valtype
|
442
490
|
...number(pointer + ValtypeSize.i32, Valtype.i32),
|
443
491
|
|
444
492
|
[ Opcodes.local_get, leftLength ],
|
445
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
493
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
446
494
|
[ Opcodes.i32_mul ],
|
447
495
|
[ Opcodes.i32_add ],
|
448
496
|
|
@@ -451,9 +499,9 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
451
499
|
...number(ValtypeSize.i32, Valtype.i32),
|
452
500
|
[ Opcodes.i32_add ],
|
453
501
|
|
454
|
-
// size = right length *
|
502
|
+
// size = right length * sizeof valtype
|
455
503
|
[ Opcodes.local_get, rightLength ],
|
456
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
504
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
457
505
|
[ Opcodes.i32_mul ],
|
458
506
|
|
459
507
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
@@ -511,11 +559,11 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
511
559
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
512
560
|
|
513
561
|
// copy right
|
514
|
-
// dst = out pointer + length size + left length *
|
562
|
+
// dst = out pointer + length size + left length * sizeof valtype
|
515
563
|
...number(pointer + ValtypeSize.i32, Valtype.i32),
|
516
564
|
|
517
565
|
[ Opcodes.local_get, leftLength ],
|
518
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
566
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
519
567
|
[ Opcodes.i32_mul ],
|
520
568
|
[ Opcodes.i32_add ],
|
521
569
|
|
@@ -524,9 +572,9 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
524
572
|
...number(ValtypeSize.i32, Valtype.i32),
|
525
573
|
[ Opcodes.i32_add ],
|
526
574
|
|
527
|
-
// size = right length *
|
575
|
+
// size = right length * sizeof valtype
|
528
576
|
[ Opcodes.local_get, rightLength ],
|
529
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
577
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
530
578
|
[ Opcodes.i32_mul ],
|
531
579
|
|
532
580
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
@@ -536,7 +584,7 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
536
584
|
];
|
537
585
|
};
|
538
586
|
|
539
|
-
const compareStrings = (scope, left, right) => {
|
587
|
+
const compareStrings = (scope, left, right, bytestrings = false) => {
|
540
588
|
// todo: this should be rewritten into a func
|
541
589
|
// todo: convert left and right to strings if not
|
542
590
|
// todo: optimize by looking up names in arrays and using that if exists?
|
@@ -545,7 +593,6 @@ const compareStrings = (scope, left, right) => {
|
|
545
593
|
const leftPointer = localTmp(scope, 'compare_left_pointer', Valtype.i32);
|
546
594
|
const leftLength = localTmp(scope, 'compare_left_length', Valtype.i32);
|
547
595
|
const rightPointer = localTmp(scope, 'compare_right_pointer', Valtype.i32);
|
548
|
-
const rightLength = localTmp(scope, 'compare_right_length', Valtype.i32);
|
549
596
|
|
550
597
|
const index = localTmp(scope, 'compare_index', Valtype.i32);
|
551
598
|
const indexEnd = localTmp(scope, 'compare_index_end', Valtype.i32);
|
@@ -573,7 +620,6 @@ const compareStrings = (scope, left, right) => {
|
|
573
620
|
|
574
621
|
[ Opcodes.local_get, rightPointer ],
|
575
622
|
[ Opcodes.i32_load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(0) ],
|
576
|
-
[ Opcodes.local_tee, rightLength ],
|
577
623
|
|
578
624
|
// fast path: check leftLength != rightLength
|
579
625
|
[ Opcodes.i32_ne ],
|
@@ -588,11 +634,13 @@ const compareStrings = (scope, left, right) => {
|
|
588
634
|
...number(0, Valtype.i32),
|
589
635
|
[ Opcodes.local_set, index ],
|
590
636
|
|
591
|
-
// setup index end as length * sizeof
|
637
|
+
// setup index end as length * sizeof valtype (1 for bytestring, 2 for string)
|
592
638
|
// we do this instead of having to do mul/div each iter for perf™
|
593
639
|
[ Opcodes.local_get, leftLength ],
|
594
|
-
...
|
595
|
-
|
640
|
+
...(bytestrings ? [] : [
|
641
|
+
...number(ValtypeSize.i16, Valtype.i32),
|
642
|
+
[ Opcodes.i32_mul ],
|
643
|
+
]),
|
596
644
|
[ Opcodes.local_set, indexEnd ],
|
597
645
|
|
598
646
|
// iterate over each char and check if eq
|
@@ -602,13 +650,17 @@ const compareStrings = (scope, left, right) => {
|
|
602
650
|
[ Opcodes.local_get, index ],
|
603
651
|
[ Opcodes.local_get, leftPointer ],
|
604
652
|
[ Opcodes.i32_add ],
|
605
|
-
|
653
|
+
bytestrings ?
|
654
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ] :
|
655
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
606
656
|
|
607
657
|
// fetch right
|
608
658
|
[ Opcodes.local_get, index ],
|
609
659
|
[ Opcodes.local_get, rightPointer ],
|
610
660
|
[ Opcodes.i32_add ],
|
611
|
-
|
661
|
+
bytestrings ?
|
662
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ] :
|
663
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
612
664
|
|
613
665
|
// not equal, "return" false
|
614
666
|
[ Opcodes.i32_ne ],
|
@@ -617,13 +669,13 @@ const compareStrings = (scope, left, right) => {
|
|
617
669
|
[ Opcodes.br, 2 ],
|
618
670
|
[ Opcodes.end ],
|
619
671
|
|
620
|
-
// index += sizeof
|
672
|
+
// index += sizeof valtype (1 for bytestring, 2 for string)
|
621
673
|
[ Opcodes.local_get, index ],
|
622
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
674
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
623
675
|
[ Opcodes.i32_add ],
|
624
676
|
[ Opcodes.local_tee, index ],
|
625
677
|
|
626
|
-
// if index != index end (length * sizeof
|
678
|
+
// if index != index end (length * sizeof valtype), loop
|
627
679
|
[ Opcodes.local_get, indexEnd ],
|
628
680
|
[ Opcodes.i32_ne ],
|
629
681
|
[ Opcodes.br_if, 0 ],
|
@@ -644,16 +696,18 @@ const compareStrings = (scope, left, right) => {
|
|
644
696
|
};
|
645
697
|
|
646
698
|
const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
647
|
-
if (
|
699
|
+
if (isFloatToIntOp(wasm[wasm.length - 1])) return [
|
648
700
|
...wasm,
|
649
701
|
...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
|
650
702
|
];
|
703
|
+
// if (isIntOp(wasm[wasm.length - 1])) return [ ...wasm ];
|
651
704
|
|
652
|
-
const
|
705
|
+
const useTmp = knownType(scope, type) == null;
|
706
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
653
707
|
|
654
708
|
const def = [
|
655
709
|
// if value != 0
|
656
|
-
[ Opcodes.local_get, tmp ],
|
710
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
657
711
|
|
658
712
|
// ...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
659
713
|
...(!intOut || (intIn && intOut) ? [] : [ Opcodes.i32_to_u ]),
|
@@ -665,7 +719,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
665
719
|
|
666
720
|
return [
|
667
721
|
...wasm,
|
668
|
-
[ Opcodes.local_set, tmp ],
|
722
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
669
723
|
|
670
724
|
...typeSwitch(scope, type, {
|
671
725
|
// [TYPES.number]: def,
|
@@ -674,7 +728,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
674
728
|
...number(1, intOut ? Valtype.i32 : valtypeBinary)
|
675
729
|
],
|
676
730
|
[TYPES.string]: [
|
677
|
-
[ Opcodes.local_get, tmp ],
|
731
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
678
732
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
679
733
|
|
680
734
|
// get length
|
@@ -686,7 +740,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
686
740
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
687
741
|
],
|
688
742
|
[TYPES._bytestring]: [ // duplicate of string
|
689
|
-
|
743
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
690
744
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
691
745
|
|
692
746
|
// get length
|
@@ -700,10 +754,12 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
700
754
|
};
|
701
755
|
|
702
756
|
const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
703
|
-
const
|
757
|
+
const useTmp = knownType(scope, type) == null;
|
758
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
759
|
+
|
704
760
|
return [
|
705
761
|
...wasm,
|
706
|
-
[ Opcodes.local_set, tmp ],
|
762
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
707
763
|
|
708
764
|
...typeSwitch(scope, type, {
|
709
765
|
[TYPES._array]: [
|
@@ -711,7 +767,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
711
767
|
...number(0, intOut ? Valtype.i32 : valtypeBinary)
|
712
768
|
],
|
713
769
|
[TYPES.string]: [
|
714
|
-
[ Opcodes.local_get, tmp ],
|
770
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
715
771
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
716
772
|
|
717
773
|
// get length
|
@@ -722,7 +778,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
722
778
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
723
779
|
],
|
724
780
|
[TYPES._bytestring]: [ // duplicate of string
|
725
|
-
[ Opcodes.local_get, tmp ],
|
781
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
726
782
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
727
783
|
|
728
784
|
// get length
|
@@ -734,7 +790,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
734
790
|
],
|
735
791
|
default: [
|
736
792
|
// if value == 0
|
737
|
-
[ Opcodes.local_get, tmp ],
|
793
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
738
794
|
|
739
795
|
...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
740
796
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
@@ -744,10 +800,12 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
744
800
|
};
|
745
801
|
|
746
802
|
const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
|
747
|
-
const
|
803
|
+
const useTmp = knownType(scope, type) == null;
|
804
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
805
|
+
|
748
806
|
return [
|
749
807
|
...wasm,
|
750
|
-
[ Opcodes.local_set, tmp ],
|
808
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
751
809
|
|
752
810
|
...typeSwitch(scope, type, {
|
753
811
|
[TYPES.undefined]: [
|
@@ -756,7 +814,7 @@ const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
756
814
|
],
|
757
815
|
[TYPES.object]: [
|
758
816
|
// object, null if == 0
|
759
|
-
[ Opcodes.local_get, tmp ],
|
817
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
760
818
|
|
761
819
|
...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
762
820
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
@@ -785,11 +843,14 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
785
843
|
return performLogicOp(scope, op, left, right, leftType, rightType);
|
786
844
|
}
|
787
845
|
|
846
|
+
const knownLeft = knownType(scope, leftType);
|
847
|
+
const knownRight = knownType(scope, rightType);
|
848
|
+
|
788
849
|
const eqOp = ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(op);
|
789
850
|
const strictOp = op === '===' || op === '!==';
|
790
851
|
|
791
852
|
const startOut = [], endOut = [];
|
792
|
-
const
|
853
|
+
const finalize = out => startOut.concat(out, endOut);
|
793
854
|
|
794
855
|
// if strict (in)equal check types match
|
795
856
|
if (strictOp) {
|
@@ -834,31 +895,59 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
834
895
|
// todo: if equality op and an operand is undefined, return false
|
835
896
|
// todo: niche null hell with 0
|
836
897
|
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
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
|
+
}
|
862
951
|
|
863
952
|
let ops = operatorOpcode[valtype][op];
|
864
953
|
|
@@ -868,33 +957,69 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
868
957
|
includeBuiltin(scope, builtinName);
|
869
958
|
const idx = funcIndex[builtinName];
|
870
959
|
|
871
|
-
return
|
960
|
+
return finalize([
|
872
961
|
...left,
|
873
962
|
...right,
|
874
963
|
[ Opcodes.call, idx ]
|
875
964
|
]);
|
876
965
|
}
|
877
966
|
|
878
|
-
if (!ops) return todo(`operator ${op} not implemented yet
|
967
|
+
if (!ops) return todo(scope, `operator ${op} not implemented yet`, true);
|
879
968
|
|
880
969
|
if (!Array.isArray(ops)) ops = [ ops ];
|
881
970
|
ops = [ ops ];
|
882
971
|
|
883
972
|
let tmpLeft, tmpRight;
|
884
973
|
// if equal op, check if strings for compareStrings
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
// todo: intelligent partial skip later
|
890
|
-
// if neither known are string, stop this madness
|
891
|
-
if ((knownLeft != null && knownLeft !== TYPES.string) && (knownRight != null && knownRight !== TYPES.string)) {
|
892
|
-
return;
|
893
|
-
}
|
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
|
894
977
|
|
978
|
+
if ((op === '===' || op === '==' || op === '!==' || op === '!=') && (knownLeft == null && knownRight == null)) {
|
895
979
|
tmpLeft = localTmp(scope, '__tmpop_left');
|
896
980
|
tmpRight = localTmp(scope, '__tmpop_right');
|
897
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)
|
898
1023
|
ops.unshift(...stringOnly([
|
899
1024
|
// if left is string
|
900
1025
|
...leftType,
|
@@ -906,30 +1031,28 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
906
1031
|
...number(TYPES.string, Valtype.i32),
|
907
1032
|
[ Opcodes.i32_eq ],
|
908
1033
|
|
909
|
-
// if
|
910
|
-
[ Opcodes.
|
1034
|
+
// if both are true
|
1035
|
+
[ Opcodes.i32_and ],
|
911
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 ],
|
912
1041
|
|
913
|
-
//
|
914
|
-
// if left is not string
|
1042
|
+
// if left is bytestring
|
915
1043
|
...leftType,
|
916
|
-
...number(TYPES.
|
917
|
-
[ Opcodes.
|
1044
|
+
...number(TYPES._bytestring, Valtype.i32),
|
1045
|
+
[ Opcodes.i32_eq ],
|
918
1046
|
|
919
|
-
// if right is
|
1047
|
+
// if right is bytestring
|
920
1048
|
...rightType,
|
921
|
-
...number(TYPES.
|
922
|
-
[ Opcodes.
|
1049
|
+
...number(TYPES._bytestring, Valtype.i32),
|
1050
|
+
[ Opcodes.i32_eq ],
|
923
1051
|
|
924
|
-
// if
|
925
|
-
[ Opcodes.
|
1052
|
+
// if both are true
|
1053
|
+
[ Opcodes.i32_and ],
|
926
1054
|
[ Opcodes.if, Blocktype.void ],
|
927
|
-
...
|
928
|
-
[ Opcodes.br, 2 ],
|
929
|
-
[ Opcodes.end ],
|
930
|
-
|
931
|
-
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
932
|
-
// ...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
1055
|
+
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], true),
|
933
1056
|
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
934
1057
|
[ Opcodes.br, 1 ],
|
935
1058
|
[ Opcodes.end ],
|
@@ -941,9 +1064,9 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
941
1064
|
// endOut.push(stringOnly([ Opcodes.end ]));
|
942
1065
|
endOut.unshift(stringOnly([ Opcodes.end ]));
|
943
1066
|
// }
|
944
|
-
}
|
1067
|
+
}
|
945
1068
|
|
946
|
-
return
|
1069
|
+
return finalize([
|
947
1070
|
...left,
|
948
1071
|
...(tmpLeft != null ? stringOnly([ [ Opcodes.local_tee, tmpLeft ] ]) : []),
|
949
1072
|
...right,
|
@@ -960,7 +1083,22 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
960
1083
|
return out;
|
961
1084
|
};
|
962
1085
|
|
963
|
-
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 = [] }) => {
|
964
1102
|
const existing = funcs.find(x => x.name === name);
|
965
1103
|
if (existing) return existing;
|
966
1104
|
|
@@ -972,18 +1110,14 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
972
1110
|
locals[nameParam(i)] = { idx: i, type: allLocals[i] };
|
973
1111
|
}
|
974
1112
|
|
975
|
-
|
976
|
-
const
|
977
|
-
|
978
|
-
|
979
|
-
locals,
|
980
|
-
returns,
|
981
|
-
localInd: allLocals.length,
|
982
|
-
};
|
983
|
-
|
984
|
-
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);
|
985
1117
|
}
|
986
1118
|
|
1119
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, { name, params, locals, returns, localInd: allLocals.length });
|
1120
|
+
|
987
1121
|
let baseGlobalIdx, i = 0;
|
988
1122
|
for (const type of globalTypes) {
|
989
1123
|
if (baseGlobalIdx === undefined) baseGlobalIdx = globalInd;
|
@@ -1006,7 +1140,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1006
1140
|
params,
|
1007
1141
|
locals,
|
1008
1142
|
returns,
|
1009
|
-
returnType:
|
1143
|
+
returnType: returnType ?? TYPES.number,
|
1010
1144
|
wasm,
|
1011
1145
|
internal: true,
|
1012
1146
|
index: currentFuncIndex++
|
@@ -1029,6 +1163,7 @@ const generateLogicExp = (scope, decl) => {
|
|
1029
1163
|
return performLogicOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right));
|
1030
1164
|
};
|
1031
1165
|
|
1166
|
+
// potential future ideas for nan boxing (unused):
|
1032
1167
|
// T = JS type, V = value/pointer
|
1033
1168
|
// 0bTTT
|
1034
1169
|
// qNAN: 0 11111111111 1000000000000000000000000000000000000000000000000001
|
@@ -1052,49 +1187,29 @@ const generateLogicExp = (scope, decl) => {
|
|
1052
1187
|
// 4: internal type
|
1053
1188
|
// 5: pointer
|
1054
1189
|
|
1055
|
-
const
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
object: 0x04,
|
1061
|
-
function: 0x05,
|
1062
|
-
symbol: 0x06,
|
1063
|
-
bigint: 0x07,
|
1064
|
-
|
1065
|
-
// these are not "typeof" types but tracked internally
|
1066
|
-
_array: 0x10,
|
1067
|
-
_regexp: 0x11,
|
1068
|
-
_bytestring: 0x12
|
1069
|
-
};
|
1070
|
-
|
1071
|
-
const TYPE_NAMES = {
|
1072
|
-
[TYPES.number]: 'Number',
|
1073
|
-
[TYPES.boolean]: 'Boolean',
|
1074
|
-
[TYPES.string]: 'String',
|
1075
|
-
[TYPES.undefined]: 'undefined',
|
1076
|
-
[TYPES.object]: 'Object',
|
1077
|
-
[TYPES.function]: 'Function',
|
1078
|
-
[TYPES.symbol]: 'Symbol',
|
1079
|
-
[TYPES.bigint]: 'BigInt',
|
1080
|
-
|
1081
|
-
[TYPES._array]: 'Array',
|
1082
|
-
[TYPES._regexp]: 'RegExp',
|
1083
|
-
[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;
|
1084
1195
|
};
|
1085
1196
|
|
1086
1197
|
const getType = (scope, _name) => {
|
1087
1198
|
const name = mapName(_name);
|
1088
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);
|
1089
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);
|
1090
1206
|
if (globals[name]) return [ [ Opcodes.global_get, globals[name + '#type'].idx ] ];
|
1091
1207
|
|
1092
1208
|
let type = TYPES.undefined;
|
1093
|
-
if (builtinVars[name]) type =
|
1209
|
+
if (builtinVars[name]) type = builtinVars[name].type ?? TYPES.number;
|
1094
1210
|
if (builtinFuncs[name] !== undefined || importedFuncs[name] !== undefined || funcIndex[name] !== undefined || internalConstrs[name] !== undefined) type = TYPES.function;
|
1095
1211
|
|
1096
|
-
if (name
|
1097
|
-
name.startsWith('__String_prototype_') && prototypeFuncs[TYPES.string][name.slice(19)]) type = TYPES.function;
|
1212
|
+
if (isExistingProtoFunc(name)) type = TYPES.function;
|
1098
1213
|
|
1099
1214
|
return number(type, Valtype.i32);
|
1100
1215
|
};
|
@@ -1117,15 +1232,16 @@ const setType = (scope, _name, type) => {
|
|
1117
1232
|
];
|
1118
1233
|
|
1119
1234
|
// throw new Error('could not find var');
|
1235
|
+
return [];
|
1120
1236
|
};
|
1121
1237
|
|
1122
1238
|
const getLastType = scope => {
|
1123
1239
|
scope.gotLastType = true;
|
1124
|
-
return [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ];
|
1240
|
+
return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1125
1241
|
};
|
1126
1242
|
|
1127
1243
|
const setLastType = scope => {
|
1128
|
-
return [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ];
|
1244
|
+
return [ [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1129
1245
|
};
|
1130
1246
|
|
1131
1247
|
const getNodeType = (scope, node) => {
|
@@ -1150,7 +1266,7 @@ const getNodeType = (scope, node) => {
|
|
1150
1266
|
const name = node.callee.name;
|
1151
1267
|
if (!name) {
|
1152
1268
|
// iife
|
1153
|
-
if (scope.locals['#last_type']) return
|
1269
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1154
1270
|
|
1155
1271
|
// presume
|
1156
1272
|
// todo: warn here?
|
@@ -1164,7 +1280,7 @@ const getNodeType = (scope, node) => {
|
|
1164
1280
|
if (func.returnType) return func.returnType;
|
1165
1281
|
}
|
1166
1282
|
|
1167
|
-
if (builtinFuncs[name]) return
|
1283
|
+
if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return builtinFuncs[name].returnType ?? TYPES.number;
|
1168
1284
|
if (internalConstrs[name]) return internalConstrs[name].type;
|
1169
1285
|
|
1170
1286
|
// check if this is a prototype function
|
@@ -1179,7 +1295,12 @@ const getNodeType = (scope, node) => {
|
|
1179
1295
|
if (protoFuncs.length === 1) return protoFuncs[0].returnType ?? TYPES.number;
|
1180
1296
|
}
|
1181
1297
|
|
1182
|
-
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);
|
1183
1304
|
|
1184
1305
|
// presume
|
1185
1306
|
// todo: warn here?
|
@@ -1227,6 +1348,15 @@ const getNodeType = (scope, node) => {
|
|
1227
1348
|
|
1228
1349
|
if (node.type === 'BinaryExpression') {
|
1229
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
|
+
|
1230
1360
|
return TYPES.number;
|
1231
1361
|
|
1232
1362
|
// todo: string concat types
|
@@ -1251,7 +1381,7 @@ const getNodeType = (scope, node) => {
|
|
1251
1381
|
if (node.operator === '!') return TYPES.boolean;
|
1252
1382
|
if (node.operator === 'void') return TYPES.undefined;
|
1253
1383
|
if (node.operator === 'delete') return TYPES.boolean;
|
1254
|
-
if (node.operator === 'typeof') return
|
1384
|
+
if (node.operator === 'typeof') return Prefs.bytestring ? TYPES._bytestring : TYPES.string;
|
1255
1385
|
|
1256
1386
|
return TYPES.number;
|
1257
1387
|
}
|
@@ -1262,15 +1392,21 @@ const getNodeType = (scope, node) => {
|
|
1262
1392
|
|
1263
1393
|
// ts hack
|
1264
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;
|
1265
1396
|
if (scope.locals[node.object.name]?.metadata?.type === TYPES._array) return TYPES.number;
|
1266
1397
|
|
1267
|
-
if (scope.locals['#last_type']) return
|
1398
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1268
1399
|
|
1269
1400
|
// presume
|
1270
1401
|
return TYPES.number;
|
1271
1402
|
}
|
1272
1403
|
|
1273
|
-
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);
|
1274
1410
|
|
1275
1411
|
// presume
|
1276
1412
|
// todo: warn here?
|
@@ -1303,7 +1439,7 @@ const generateLiteral = (scope, decl, global, name) => {
|
|
1303
1439
|
return makeString(scope, decl.value, global, name);
|
1304
1440
|
|
1305
1441
|
default:
|
1306
|
-
return todo(`cannot generate literal of type ${typeof decl.value}
|
1442
|
+
return todo(scope, `cannot generate literal of type ${typeof decl.value}`, true);
|
1307
1443
|
}
|
1308
1444
|
};
|
1309
1445
|
|
@@ -1312,6 +1448,8 @@ const countLeftover = wasm => {
|
|
1312
1448
|
|
1313
1449
|
for (let i = 0; i < wasm.length; i++) {
|
1314
1450
|
const inst = wasm[i];
|
1451
|
+
if (inst[0] == null) continue;
|
1452
|
+
|
1315
1453
|
if (depth === 0 && (inst[0] === Opcodes.if || inst[0] === Opcodes.block || inst[0] === Opcodes.loop)) {
|
1316
1454
|
if (inst[0] === Opcodes.if) count--;
|
1317
1455
|
if (inst[1] !== Blocktype.void) count++;
|
@@ -1320,7 +1458,7 @@ const countLeftover = wasm => {
|
|
1320
1458
|
if (inst[0] === Opcodes.end) depth--;
|
1321
1459
|
|
1322
1460
|
if (depth === 0)
|
1323
|
-
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--;
|
1324
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)) {}
|
1325
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++;
|
1326
1464
|
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
|
@@ -1423,10 +1561,21 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1423
1561
|
name = func.name;
|
1424
1562
|
}
|
1425
1563
|
|
1426
|
-
if (name === 'eval' && decl.arguments[0]
|
1564
|
+
if (name === 'eval' && decl.arguments[0]?.type === 'Literal') {
|
1427
1565
|
// literal eval hack
|
1428
|
-
const code = decl.arguments[0]
|
1429
|
-
|
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
|
+
}
|
1430
1579
|
|
1431
1580
|
const out = generate(scope, {
|
1432
1581
|
type: 'BlockStatement',
|
@@ -1440,13 +1589,13 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1440
1589
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1441
1590
|
out.push(
|
1442
1591
|
...getNodeType(scope, finalStatement),
|
1443
|
-
setLastType(scope)
|
1592
|
+
...setLastType(scope)
|
1444
1593
|
);
|
1445
1594
|
} else if (countLeftover(out) === 0) {
|
1446
1595
|
out.push(...number(UNDEFINED));
|
1447
1596
|
out.push(
|
1448
1597
|
...number(TYPES.undefined, Valtype.i32),
|
1449
|
-
setLastType(scope)
|
1598
|
+
...setLastType(scope)
|
1450
1599
|
);
|
1451
1600
|
}
|
1452
1601
|
|
@@ -1468,6 +1617,9 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1468
1617
|
|
1469
1618
|
target = { ...decl.callee };
|
1470
1619
|
target.name = spl.slice(0, -1).join('_');
|
1620
|
+
|
1621
|
+
// failed to lookup name, abort
|
1622
|
+
if (!lookupName(scope, target.name)[0]) protoName = null;
|
1471
1623
|
}
|
1472
1624
|
|
1473
1625
|
// literal.func()
|
@@ -1490,7 +1642,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1490
1642
|
Opcodes.i32_from_u,
|
1491
1643
|
|
1492
1644
|
...number(TYPES.boolean, Valtype.i32),
|
1493
|
-
setLastType(scope)
|
1645
|
+
...setLastType(scope)
|
1494
1646
|
];
|
1495
1647
|
}
|
1496
1648
|
|
@@ -1515,12 +1667,30 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1515
1667
|
// }
|
1516
1668
|
|
1517
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
|
+
|
1518
1689
|
const protoCands = Object.keys(prototypeFuncs).reduce((acc, x) => {
|
1519
1690
|
if (Object.hasOwn(prototypeFuncs[x], protoName)) acc[x] = prototypeFuncs[x][protoName];
|
1520
1691
|
return acc;
|
1521
1692
|
}, {});
|
1522
1693
|
|
1523
|
-
// no prototype function candidates, ignore
|
1524
1694
|
if (Object.keys(protoCands).length > 0) {
|
1525
1695
|
// use local for cached i32 length as commonly used
|
1526
1696
|
const lengthLocal = localTmp(scope, '__proto_length_cache', Valtype.i32);
|
@@ -1538,7 +1708,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1538
1708
|
|
1539
1709
|
let allOptUnused = true;
|
1540
1710
|
let lengthI32CacheUsed = false;
|
1541
|
-
const protoBC = {};
|
1542
1711
|
for (const x in protoCands) {
|
1543
1712
|
const protoFunc = protoCands[x];
|
1544
1713
|
if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
|
@@ -1546,7 +1715,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1546
1715
|
...RTArrayUtil.getLength(getPointer),
|
1547
1716
|
|
1548
1717
|
...number(TYPES.number, Valtype.i32),
|
1549
|
-
setLastType(scope)
|
1718
|
+
...setLastType(scope)
|
1550
1719
|
];
|
1551
1720
|
continue;
|
1552
1721
|
}
|
@@ -1583,7 +1752,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1583
1752
|
...protoOut,
|
1584
1753
|
|
1585
1754
|
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1586
|
-
setLastType(scope),
|
1755
|
+
...setLastType(scope),
|
1587
1756
|
[ Opcodes.end ]
|
1588
1757
|
];
|
1589
1758
|
}
|
@@ -1609,10 +1778,19 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1609
1778
|
}, allOptUnused && unusedValue ? Blocktype.void : valtypeBinary),
|
1610
1779
|
];
|
1611
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
|
+
}
|
1612
1790
|
}
|
1613
1791
|
|
1614
1792
|
// TODO: only allows callee as literal
|
1615
|
-
if (!name) return todo(`only literal callees (got ${decl.callee.type})`);
|
1793
|
+
if (!name) return todo(scope, `only literal callees (got ${decl.callee.type})`);
|
1616
1794
|
|
1617
1795
|
let idx = funcIndex[name] ?? importedFuncs[name];
|
1618
1796
|
if (idx === undefined && builtinFuncs[name]) {
|
@@ -1645,16 +1823,65 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1645
1823
|
idx = -1;
|
1646
1824
|
}
|
1647
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
|
+
|
1648
1875
|
if (idx === undefined) {
|
1649
|
-
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function
|
1650
|
-
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);
|
1651
1878
|
}
|
1652
1879
|
|
1653
1880
|
const func = funcs.find(x => x.index === idx);
|
1654
1881
|
|
1655
1882
|
const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
|
1656
1883
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1657
|
-
const
|
1884
|
+
const typedReturns = (func ? func.returnType == null : userFunc) || builtinFuncs[name]?.typedReturns;
|
1658
1885
|
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1659
1886
|
|
1660
1887
|
let args = decl.arguments;
|
@@ -1671,14 +1898,24 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1671
1898
|
if (func && func.throws) scope.throws = true;
|
1672
1899
|
|
1673
1900
|
let out = [];
|
1674
|
-
for (
|
1901
|
+
for (let i = 0; i < args.length; i++) {
|
1902
|
+
const arg = args[i];
|
1675
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
|
+
|
1676
1913
|
if (typedParams) out = out.concat(getNodeType(scope, arg));
|
1677
1914
|
}
|
1678
1915
|
|
1679
1916
|
out.push([ Opcodes.call, idx ]);
|
1680
1917
|
|
1681
|
-
if (!
|
1918
|
+
if (!typedReturns) {
|
1682
1919
|
// let type;
|
1683
1920
|
// if (builtinFuncs[name]) type = TYPES[builtinFuncs[name].returnType ?? 'number'];
|
1684
1921
|
// if (internalConstrs[name]) type = internalConstrs[name].type;
|
@@ -1688,7 +1925,11 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1688
1925
|
// ...number(type, Valtype.i32),
|
1689
1926
|
// [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
|
1690
1927
|
// );
|
1691
|
-
} 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
|
+
}
|
1692
1933
|
|
1693
1934
|
return out;
|
1694
1935
|
};
|
@@ -1697,7 +1938,7 @@ const generateNew = (scope, decl, _global, _name) => {
|
|
1697
1938
|
// hack: basically treat this as a normal call for builtins for now
|
1698
1939
|
const name = mapName(decl.callee.name);
|
1699
1940
|
if (internalConstrs[name] && !internalConstrs[name].notConstr) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1700
|
-
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)})`);
|
1701
1942
|
|
1702
1943
|
return generateCall(scope, decl, _global, _name);
|
1703
1944
|
};
|
@@ -1814,14 +2055,14 @@ const brTable = (input, bc, returns) => {
|
|
1814
2055
|
};
|
1815
2056
|
|
1816
2057
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
1817
|
-
if (!
|
2058
|
+
if (!Prefs.bytestring) delete bc[TYPES._bytestring];
|
1818
2059
|
|
1819
2060
|
const known = knownType(scope, type);
|
1820
2061
|
if (known != null) {
|
1821
2062
|
return bc[known] ?? bc.default;
|
1822
2063
|
}
|
1823
2064
|
|
1824
|
-
if (
|
2065
|
+
if (Prefs.typeswitchUseBrtable)
|
1825
2066
|
return brTable(type, bc, returns);
|
1826
2067
|
|
1827
2068
|
const tmp = localTmp(scope, '#typeswitch_tmp', Valtype.i32);
|
@@ -1831,8 +2072,6 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
1831
2072
|
[ Opcodes.block, returns ]
|
1832
2073
|
];
|
1833
2074
|
|
1834
|
-
// todo: use br_table?
|
1835
|
-
|
1836
2075
|
for (const x in bc) {
|
1837
2076
|
if (x === 'default') continue;
|
1838
2077
|
|
@@ -1856,7 +2095,7 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
1856
2095
|
return out;
|
1857
2096
|
};
|
1858
2097
|
|
1859
|
-
const allocVar = (scope, name, global = false) => {
|
2098
|
+
const allocVar = (scope, name, global = false, type = true) => {
|
1860
2099
|
const target = global ? globals : scope.locals;
|
1861
2100
|
|
1862
2101
|
// already declared
|
@@ -1870,8 +2109,10 @@ const allocVar = (scope, name, global = false) => {
|
|
1870
2109
|
let idx = global ? globalInd++ : scope.localInd++;
|
1871
2110
|
target[name] = { idx, type: valtypeBinary };
|
1872
2111
|
|
1873
|
-
|
1874
|
-
|
2112
|
+
if (type) {
|
2113
|
+
let typeIdx = global ? globalInd++ : scope.localInd++;
|
2114
|
+
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
|
2115
|
+
}
|
1875
2116
|
|
1876
2117
|
return idx;
|
1877
2118
|
};
|
@@ -1886,11 +2127,14 @@ const addVarMetadata = (scope, name, global = false, metadata = {}) => {
|
|
1886
2127
|
};
|
1887
2128
|
|
1888
2129
|
const typeAnnoToPorfType = x => {
|
1889
|
-
if (
|
1890
|
-
if (TYPES[
|
2130
|
+
if (!x) return null;
|
2131
|
+
if (TYPES[x] != null) return TYPES[x];
|
2132
|
+
if (TYPES['_' + x] != null) return TYPES['_' + x];
|
1891
2133
|
|
1892
2134
|
switch (x) {
|
1893
2135
|
case 'i32':
|
2136
|
+
case 'i64':
|
2137
|
+
case 'f64':
|
1894
2138
|
return TYPES.number;
|
1895
2139
|
}
|
1896
2140
|
|
@@ -1901,7 +2145,7 @@ const extractTypeAnnotation = decl => {
|
|
1901
2145
|
let a = decl;
|
1902
2146
|
while (a.typeAnnotation) a = a.typeAnnotation;
|
1903
2147
|
|
1904
|
-
let type, elementType;
|
2148
|
+
let type = null, elementType = null;
|
1905
2149
|
if (a.typeName) {
|
1906
2150
|
type = a.typeName.name;
|
1907
2151
|
} else if (a.type.endsWith('Keyword')) {
|
@@ -1914,7 +2158,7 @@ const extractTypeAnnotation = decl => {
|
|
1914
2158
|
const typeName = type;
|
1915
2159
|
type = typeAnnoToPorfType(type);
|
1916
2160
|
|
1917
|
-
if (type === TYPES._bytestring && !
|
2161
|
+
if (type === TYPES._bytestring && !Prefs.bytestring) type = TYPES.string;
|
1918
2162
|
|
1919
2163
|
// if (decl.name) console.log(decl.name, { type, elementType });
|
1920
2164
|
|
@@ -1932,7 +2176,7 @@ const generateVar = (scope, decl) => {
|
|
1932
2176
|
for (const x of decl.declarations) {
|
1933
2177
|
const name = mapName(x.id.name);
|
1934
2178
|
|
1935
|
-
if (!name) return todo('destructuring is not supported yet');
|
2179
|
+
if (!name) return todo(scope, 'destructuring is not supported yet');
|
1936
2180
|
|
1937
2181
|
if (x.init && isFuncType(x.init.type)) {
|
1938
2182
|
// hack for let a = function () { ... }
|
@@ -1949,9 +2193,10 @@ const generateVar = (scope, decl) => {
|
|
1949
2193
|
continue; // always ignore
|
1950
2194
|
}
|
1951
2195
|
|
1952
|
-
|
2196
|
+
const typed = typedInput && x.id.typeAnnotation;
|
2197
|
+
let idx = allocVar(scope, name, global, !typed);
|
1953
2198
|
|
1954
|
-
if (
|
2199
|
+
if (typed) {
|
1955
2200
|
addVarMetadata(scope, name, global, extractTypeAnnotation(x.id));
|
1956
2201
|
}
|
1957
2202
|
|
@@ -1969,7 +2214,8 @@ const generateVar = (scope, decl) => {
|
|
1969
2214
|
return out;
|
1970
2215
|
};
|
1971
2216
|
|
1972
|
-
|
2217
|
+
// todo: optimize this func for valueUnused
|
2218
|
+
const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
1973
2219
|
const { type, name } = decl.left;
|
1974
2220
|
|
1975
2221
|
if (type === 'ObjectPattern') {
|
@@ -1987,9 +2233,9 @@ const generateAssign = (scope, decl) => {
|
|
1987
2233
|
// hack: .length setter
|
1988
2234
|
if (decl.left.type === 'MemberExpression' && decl.left.property.name === 'length') {
|
1989
2235
|
const name = decl.left.object.name;
|
1990
|
-
const pointer = arrays
|
2236
|
+
const pointer = scope.arrays?.get(name);
|
1991
2237
|
|
1992
|
-
const aotPointer = pointer != null;
|
2238
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
1993
2239
|
|
1994
2240
|
const newValueTmp = localTmp(scope, '__length_setter_tmp');
|
1995
2241
|
|
@@ -2014,9 +2260,9 @@ const generateAssign = (scope, decl) => {
|
|
2014
2260
|
// arr[i]
|
2015
2261
|
if (decl.left.type === 'MemberExpression' && decl.left.computed) {
|
2016
2262
|
const name = decl.left.object.name;
|
2017
|
-
const pointer = arrays
|
2263
|
+
const pointer = scope.arrays?.get(name);
|
2018
2264
|
|
2019
|
-
const aotPointer = pointer != null;
|
2265
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2020
2266
|
|
2021
2267
|
const newValueTmp = localTmp(scope, '__member_setter_val_tmp');
|
2022
2268
|
const pointerTmp = op === '=' ? -1 : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
|
@@ -2072,7 +2318,7 @@ const generateAssign = (scope, decl) => {
|
|
2072
2318
|
];
|
2073
2319
|
}
|
2074
2320
|
|
2075
|
-
if (!name) return todo('destructuring is not supported yet');
|
2321
|
+
if (!name) return todo(scope, 'destructuring is not supported yet', true);
|
2076
2322
|
|
2077
2323
|
const [ local, isGlobal ] = lookupName(scope, name);
|
2078
2324
|
|
@@ -2120,9 +2366,7 @@ const generateAssign = (scope, decl) => {
|
|
2120
2366
|
], getType(scope, name), getNodeType(scope, decl.right), isGlobal, name, true),
|
2121
2367
|
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
2122
2368
|
|
2123
|
-
getLastType(scope)
|
2124
|
-
// hack: type is idx+1
|
2125
|
-
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx + 1 ],
|
2369
|
+
...setType(scope, name, getLastType(scope))
|
2126
2370
|
];
|
2127
2371
|
}
|
2128
2372
|
|
@@ -2133,9 +2377,7 @@ const generateAssign = (scope, decl) => {
|
|
2133
2377
|
|
2134
2378
|
// todo: string concat types
|
2135
2379
|
|
2136
|
-
|
2137
|
-
...number(TYPES.number, Valtype.i32),
|
2138
|
-
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx + 1 ],
|
2380
|
+
...setType(scope, name, TYPES.number)
|
2139
2381
|
];
|
2140
2382
|
};
|
2141
2383
|
|
@@ -2181,7 +2423,7 @@ const generateUnary = (scope, decl) => {
|
|
2181
2423
|
return out;
|
2182
2424
|
}
|
2183
2425
|
|
2184
|
-
case 'delete':
|
2426
|
+
case 'delete': {
|
2185
2427
|
let toReturn = true, toGenerate = true;
|
2186
2428
|
|
2187
2429
|
if (decl.argument.type === 'Identifier') {
|
@@ -2203,9 +2445,26 @@ const generateUnary = (scope, decl) => {
|
|
2203
2445
|
|
2204
2446
|
out.push(...number(toReturn ? 1 : 0));
|
2205
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
|
+
}
|
2206
2463
|
|
2207
|
-
|
2208
|
-
|
2464
|
+
const out = toGenerate ? generate(scope, decl.argument) : [];
|
2465
|
+
disposeLeftover(out);
|
2466
|
+
|
2467
|
+
out.push(...typeSwitch(scope, overrideType ?? getNodeType(scope, decl.argument), {
|
2209
2468
|
[TYPES.number]: makeString(scope, 'number', false, '#typeof_result'),
|
2210
2469
|
[TYPES.boolean]: makeString(scope, 'boolean', false, '#typeof_result'),
|
2211
2470
|
[TYPES.string]: makeString(scope, 'string', false, '#typeof_result'),
|
@@ -2216,27 +2475,30 @@ const generateUnary = (scope, decl) => {
|
|
2216
2475
|
|
2217
2476
|
// object and internal types
|
2218
2477
|
default: makeString(scope, 'object', false, '#typeof_result'),
|
2219
|
-
});
|
2478
|
+
}));
|
2479
|
+
|
2480
|
+
return out;
|
2481
|
+
}
|
2220
2482
|
|
2221
2483
|
default:
|
2222
|
-
return todo(`unary operator ${decl.operator} not implemented yet
|
2484
|
+
return todo(scope, `unary operator ${decl.operator} not implemented yet`, true);
|
2223
2485
|
}
|
2224
2486
|
};
|
2225
2487
|
|
2226
|
-
const generateUpdate = (scope, decl) => {
|
2488
|
+
const generateUpdate = (scope, decl, _global, _name, valueUnused = false) => {
|
2227
2489
|
const { name } = decl.argument;
|
2228
2490
|
|
2229
2491
|
const [ local, isGlobal ] = lookupName(scope, name);
|
2230
2492
|
|
2231
2493
|
if (local === undefined) {
|
2232
|
-
return todo(`update expression with undefined variable
|
2494
|
+
return todo(scope, `update expression with undefined variable`, true);
|
2233
2495
|
}
|
2234
2496
|
|
2235
2497
|
const idx = local.idx;
|
2236
2498
|
const out = [];
|
2237
2499
|
|
2238
2500
|
out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2239
|
-
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 ]);
|
2240
2502
|
|
2241
2503
|
switch (decl.operator) {
|
2242
2504
|
case '++':
|
@@ -2249,7 +2511,7 @@ const generateUpdate = (scope, decl) => {
|
|
2249
2511
|
}
|
2250
2512
|
|
2251
2513
|
out.push([ isGlobal ? Opcodes.global_set : Opcodes.local_set, idx ]);
|
2252
|
-
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 ]);
|
2253
2515
|
|
2254
2516
|
return out;
|
2255
2517
|
};
|
@@ -2289,7 +2551,7 @@ const generateConditional = (scope, decl) => {
|
|
2289
2551
|
// note type
|
2290
2552
|
out.push(
|
2291
2553
|
...getNodeType(scope, decl.consequent),
|
2292
|
-
setLastType(scope)
|
2554
|
+
...setLastType(scope)
|
2293
2555
|
);
|
2294
2556
|
|
2295
2557
|
out.push([ Opcodes.else ]);
|
@@ -2298,7 +2560,7 @@ const generateConditional = (scope, decl) => {
|
|
2298
2560
|
// note type
|
2299
2561
|
out.push(
|
2300
2562
|
...getNodeType(scope, decl.alternate),
|
2301
|
-
setLastType(scope)
|
2563
|
+
...setLastType(scope)
|
2302
2564
|
);
|
2303
2565
|
|
2304
2566
|
out.push([ Opcodes.end ]);
|
@@ -2312,7 +2574,7 @@ const generateFor = (scope, decl) => {
|
|
2312
2574
|
const out = [];
|
2313
2575
|
|
2314
2576
|
if (decl.init) {
|
2315
|
-
out.push(...generate(scope, decl.init));
|
2577
|
+
out.push(...generate(scope, decl.init, false, undefined, true));
|
2316
2578
|
disposeLeftover(out);
|
2317
2579
|
}
|
2318
2580
|
|
@@ -2330,7 +2592,7 @@ const generateFor = (scope, decl) => {
|
|
2330
2592
|
out.push(...generate(scope, decl.body));
|
2331
2593
|
out.push([ Opcodes.end ]);
|
2332
2594
|
|
2333
|
-
if (decl.update) out.push(...generate(scope, decl.update));
|
2595
|
+
if (decl.update) out.push(...generate(scope, decl.update, false, undefined, true));
|
2334
2596
|
|
2335
2597
|
out.push([ Opcodes.br, 1 ]);
|
2336
2598
|
out.push([ Opcodes.end ], [ Opcodes.end ]);
|
@@ -2358,6 +2620,36 @@ const generateWhile = (scope, decl) => {
|
|
2358
2620
|
return out;
|
2359
2621
|
};
|
2360
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
|
+
|
2361
2653
|
const generateForOf = (scope, decl) => {
|
2362
2654
|
const out = [];
|
2363
2655
|
|
@@ -2394,7 +2686,10 @@ const generateForOf = (scope, decl) => {
|
|
2394
2686
|
generateVar(scope, { kind: 'var', _bare: true, declarations: [ { id: { name: leftName } } ] })
|
2395
2687
|
}
|
2396
2688
|
|
2689
|
+
// if (!leftName) console.log(decl.left?.declarations?.[0]?.id ?? decl.left);
|
2690
|
+
|
2397
2691
|
const [ local, isGlobal ] = lookupName(scope, leftName);
|
2692
|
+
if (!local) return todo(scope, 'for of failed to get left local (probably destructure)');
|
2398
2693
|
|
2399
2694
|
depth.push('block');
|
2400
2695
|
depth.push('block');
|
@@ -2403,6 +2698,7 @@ const generateForOf = (scope, decl) => {
|
|
2403
2698
|
// hack: this is naughty and will break things!
|
2404
2699
|
let newOut = number(0, Valtype.f64), newPointer = -1;
|
2405
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?
|
2406
2702
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2407
2703
|
rawElements: new Array(1)
|
2408
2704
|
}, isGlobal, leftName, true, 'i16');
|
@@ -2494,6 +2790,56 @@ const generateForOf = (scope, decl) => {
|
|
2494
2790
|
[ Opcodes.end ],
|
2495
2791
|
[ Opcodes.end ]
|
2496
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
|
+
],
|
2497
2843
|
default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
|
2498
2844
|
}, Blocktype.void));
|
2499
2845
|
|
@@ -2504,28 +2850,65 @@ const generateForOf = (scope, decl) => {
|
|
2504
2850
|
return out;
|
2505
2851
|
};
|
2506
2852
|
|
2853
|
+
// find the nearest loop in depth map by type
|
2507
2854
|
const getNearestLoop = () => {
|
2508
2855
|
for (let i = depth.length - 1; i >= 0; i--) {
|
2509
|
-
if (
|
2856
|
+
if (['while', 'dowhile', 'for', 'forof'].includes(depth[i])) return i;
|
2510
2857
|
}
|
2511
2858
|
|
2512
2859
|
return -1;
|
2513
2860
|
};
|
2514
2861
|
|
2515
2862
|
const generateBreak = (scope, decl) => {
|
2516
|
-
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
|
+
|
2517
2878
|
return [
|
2518
|
-
[ Opcodes.br, ...signedLEB128(
|
2879
|
+
[ Opcodes.br, ...signedLEB128(depth.length - target - offset) ]
|
2519
2880
|
];
|
2520
2881
|
};
|
2521
2882
|
|
2522
2883
|
const generateContinue = (scope, decl) => {
|
2523
|
-
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
|
+
|
2524
2898
|
return [
|
2525
|
-
[ Opcodes.br, ...signedLEB128(
|
2899
|
+
[ Opcodes.br, ...signedLEB128(depth.length - target - offset) ]
|
2526
2900
|
];
|
2527
2901
|
};
|
2528
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
|
+
|
2529
2912
|
const generateThrow = (scope, decl) => {
|
2530
2913
|
scope.throws = true;
|
2531
2914
|
|
@@ -2546,6 +2929,9 @@ const generateThrow = (scope, decl) => {
|
|
2546
2929
|
let exceptId = exceptions.push({ constructor, message }) - 1;
|
2547
2930
|
let tagIdx = tags[0].idx;
|
2548
2931
|
|
2932
|
+
scope.exceptions ??= [];
|
2933
|
+
scope.exceptions.push(exceptId);
|
2934
|
+
|
2549
2935
|
// todo: write a description of how this works lol
|
2550
2936
|
|
2551
2937
|
return [
|
@@ -2555,7 +2941,7 @@ const generateThrow = (scope, decl) => {
|
|
2555
2941
|
};
|
2556
2942
|
|
2557
2943
|
const generateTry = (scope, decl) => {
|
2558
|
-
if (decl.finalizer) return todo('try finally not implemented yet');
|
2944
|
+
if (decl.finalizer) return todo(scope, 'try finally not implemented yet');
|
2559
2945
|
|
2560
2946
|
const out = [];
|
2561
2947
|
|
@@ -2586,11 +2972,11 @@ const generateAssignPat = (scope, decl) => {
|
|
2586
2972
|
// TODO
|
2587
2973
|
// if identifier declared, use that
|
2588
2974
|
// else, use default (right)
|
2589
|
-
return todo('assignment pattern (optional arg)');
|
2975
|
+
return todo(scope, 'assignment pattern (optional arg)');
|
2590
2976
|
};
|
2591
2977
|
|
2592
2978
|
let pages = new Map();
|
2593
|
-
const allocPage = (reason, type) => {
|
2979
|
+
const allocPage = (scope, reason, type) => {
|
2594
2980
|
if (pages.has(reason)) return pages.get(reason).ind;
|
2595
2981
|
|
2596
2982
|
if (reason.startsWith('array:')) pages.hasArray = true;
|
@@ -2601,16 +2987,20 @@ const allocPage = (reason, type) => {
|
|
2601
2987
|
const ind = pages.size;
|
2602
2988
|
pages.set(reason, { ind, type });
|
2603
2989
|
|
2604
|
-
|
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})`);
|
2605
2994
|
|
2606
2995
|
return ind;
|
2607
2996
|
};
|
2608
2997
|
|
2998
|
+
// todo: add scope.pages
|
2609
2999
|
const freePage = reason => {
|
2610
3000
|
const { ind } = pages.get(reason);
|
2611
3001
|
pages.delete(reason);
|
2612
3002
|
|
2613
|
-
if (allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
3003
|
+
if (Prefs.allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
2614
3004
|
|
2615
3005
|
return ind;
|
2616
3006
|
};
|
@@ -2636,15 +3026,14 @@ const StoreOps = {
|
|
2636
3026
|
|
2637
3027
|
let data = [];
|
2638
3028
|
|
2639
|
-
const compileBytes = (val, itemType
|
3029
|
+
const compileBytes = (val, itemType) => {
|
2640
3030
|
// todo: this is a mess and needs confirming / ????
|
2641
3031
|
switch (itemType) {
|
2642
3032
|
case 'i8': return [ val % 256 ];
|
2643
|
-
case 'i16': return [ val % 256,
|
2644
|
-
|
2645
|
-
case 'i32':
|
2646
|
-
|
2647
|
-
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
|
2648
3037
|
|
2649
3038
|
case 'f64': return ieee754_binary64(val);
|
2650
3039
|
}
|
@@ -2662,16 +3051,20 @@ const getAllocType = itemType => {
|
|
2662
3051
|
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
2663
3052
|
const out = [];
|
2664
3053
|
|
3054
|
+
scope.arrays ??= new Map();
|
3055
|
+
|
2665
3056
|
let firstAssign = false;
|
2666
|
-
if (!arrays.has(name) || name === '$undeclared') {
|
3057
|
+
if (!scope.arrays.has(name) || name === '$undeclared') {
|
2667
3058
|
firstAssign = true;
|
2668
3059
|
|
2669
3060
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
2670
3061
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
2671
|
-
|
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);
|
2672
3065
|
}
|
2673
3066
|
|
2674
|
-
const pointer = arrays.get(name);
|
3067
|
+
const pointer = scope.arrays.get(name);
|
2675
3068
|
|
2676
3069
|
const useRawElements = !!decl.rawElements;
|
2677
3070
|
const elements = useRawElements ? decl.rawElements : decl.elements;
|
@@ -2679,19 +3072,25 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2679
3072
|
const valtype = itemTypeToValtype[itemType];
|
2680
3073
|
const length = elements.length;
|
2681
3074
|
|
2682
|
-
if (firstAssign && useRawElements) {
|
2683
|
-
|
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');
|
2684
3079
|
|
2685
|
-
|
2686
|
-
|
3080
|
+
if (!initEmpty) for (let i = 0; i < length; i++) {
|
3081
|
+
if (elements[i] == null) continue;
|
2687
3082
|
|
2688
|
-
|
2689
|
-
|
3083
|
+
bytes.push(...compileBytes(elements[i], itemType));
|
3084
|
+
}
|
2690
3085
|
|
2691
|
-
|
2692
|
-
|
2693
|
-
|
2694
|
-
|
3086
|
+
const ind = data.push({
|
3087
|
+
offset: pointer,
|
3088
|
+
bytes
|
3089
|
+
}) - 1;
|
3090
|
+
|
3091
|
+
scope.data ??= [];
|
3092
|
+
scope.data.push(ind);
|
3093
|
+
}
|
2695
3094
|
|
2696
3095
|
// local value as pointer
|
2697
3096
|
out.push(...number(pointer));
|
@@ -2725,7 +3124,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2725
3124
|
};
|
2726
3125
|
|
2727
3126
|
const byteStringable = str => {
|
2728
|
-
if (!
|
3127
|
+
if (!Prefs.bytestring) return false;
|
2729
3128
|
|
2730
3129
|
for (let i = 0; i < str.length; i++) {
|
2731
3130
|
if (str.charCodeAt(i) > 0xFF) return false;
|
@@ -2736,7 +3135,7 @@ const byteStringable = str => {
|
|
2736
3135
|
|
2737
3136
|
const makeString = (scope, str, global = false, name = '$undeclared', forceBytestring = undefined) => {
|
2738
3137
|
const rawElements = new Array(str.length);
|
2739
|
-
let byteStringable =
|
3138
|
+
let byteStringable = Prefs.bytestring;
|
2740
3139
|
for (let i = 0; i < str.length; i++) {
|
2741
3140
|
const c = str.charCodeAt(i);
|
2742
3141
|
rawElements[i] = c;
|
@@ -2751,20 +3150,29 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
|
|
2751
3150
|
}, global, name, false, byteStringable ? 'i8' : 'i16')[0];
|
2752
3151
|
};
|
2753
3152
|
|
2754
|
-
let arrays = new Map();
|
2755
3153
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
2756
3154
|
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
2757
3155
|
};
|
2758
3156
|
|
2759
3157
|
export const generateMember = (scope, decl, _global, _name) => {
|
2760
3158
|
const name = decl.object.name;
|
2761
|
-
const pointer = arrays
|
3159
|
+
const pointer = scope.arrays?.get(name);
|
2762
3160
|
|
2763
|
-
const aotPointer = pointer != null;
|
3161
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2764
3162
|
|
2765
3163
|
// hack: .length
|
2766
3164
|
if (decl.property.name === 'length') {
|
2767
|
-
|
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
|
+
|
2768
3176
|
return [
|
2769
3177
|
...(aotPointer ? number(0, Valtype.i32) : [
|
2770
3178
|
...generate(scope, decl.object),
|
@@ -2808,7 +3216,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2808
3216
|
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2809
3217
|
|
2810
3218
|
...number(TYPES.number, Valtype.i32),
|
2811
|
-
setLastType(scope)
|
3219
|
+
...setLastType(scope)
|
2812
3220
|
],
|
2813
3221
|
|
2814
3222
|
[TYPES.string]: [
|
@@ -2840,7 +3248,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2840
3248
|
...number(newPointer),
|
2841
3249
|
|
2842
3250
|
...number(TYPES.string, Valtype.i32),
|
2843
|
-
setLastType(scope)
|
3251
|
+
...setLastType(scope)
|
2844
3252
|
],
|
2845
3253
|
[TYPES._bytestring]: [
|
2846
3254
|
// setup new/out array
|
@@ -2859,19 +3267,19 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2859
3267
|
]),
|
2860
3268
|
|
2861
3269
|
// load current string ind {arg}
|
2862
|
-
[ Opcodes.i32_load8_u,
|
3270
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2863
3271
|
|
2864
3272
|
// store to new string ind 0
|
2865
|
-
[ Opcodes.i32_store8,
|
3273
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
2866
3274
|
|
2867
3275
|
// return new string (page)
|
2868
3276
|
...number(newPointer),
|
2869
3277
|
|
2870
3278
|
...number(TYPES._bytestring, Valtype.i32),
|
2871
|
-
setLastType(scope)
|
3279
|
+
...setLastType(scope)
|
2872
3280
|
],
|
2873
3281
|
|
2874
|
-
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)
|
2875
3283
|
});
|
2876
3284
|
};
|
2877
3285
|
|
@@ -2881,28 +3289,36 @@ const objectHack = node => {
|
|
2881
3289
|
if (!node) return node;
|
2882
3290
|
|
2883
3291
|
if (node.type === 'MemberExpression') {
|
2884
|
-
|
3292
|
+
const out = (() => {
|
3293
|
+
if (node.computed || node.optional) return;
|
2885
3294
|
|
2886
|
-
|
3295
|
+
let objectName = node.object.name;
|
2887
3296
|
|
2888
|
-
|
2889
|
-
|
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;
|
2890
3300
|
|
2891
|
-
|
3301
|
+
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
2892
3302
|
|
2893
|
-
|
2894
|
-
|
3303
|
+
// if .length, give up (hack within a hack!)
|
3304
|
+
if (node.property.name === 'length') {
|
3305
|
+
node.object = objectHack(node.object);
|
3306
|
+
return;
|
3307
|
+
}
|
2895
3308
|
|
2896
|
-
|
2897
|
-
|
3309
|
+
// no object name, give up
|
3310
|
+
if (!objectName) return;
|
2898
3311
|
|
2899
|
-
|
2900
|
-
|
3312
|
+
const name = '__' + objectName + '_' + node.property.name;
|
3313
|
+
if (Prefs.codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
2901
3314
|
|
2902
|
-
|
2903
|
-
|
2904
|
-
|
2905
|
-
|
3315
|
+
return {
|
3316
|
+
type: 'Identifier',
|
3317
|
+
name
|
3318
|
+
};
|
3319
|
+
})();
|
3320
|
+
|
3321
|
+
if (out) return out;
|
2906
3322
|
}
|
2907
3323
|
|
2908
3324
|
for (const x in node) {
|
@@ -2916,8 +3332,8 @@ const objectHack = node => {
|
|
2916
3332
|
};
|
2917
3333
|
|
2918
3334
|
const generateFunc = (scope, decl) => {
|
2919
|
-
if (decl.async) return todo('async functions are not supported');
|
2920
|
-
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');
|
2921
3337
|
|
2922
3338
|
const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
|
2923
3339
|
const params = decl.params ?? [];
|
@@ -2933,6 +3349,11 @@ const generateFunc = (scope, decl) => {
|
|
2933
3349
|
name
|
2934
3350
|
};
|
2935
3351
|
|
3352
|
+
if (typedInput && decl.returnType) {
|
3353
|
+
innerScope.returnType = extractTypeAnnotation(decl.returnType).type;
|
3354
|
+
innerScope.returns = [ valtypeBinary ];
|
3355
|
+
}
|
3356
|
+
|
2936
3357
|
for (let i = 0; i < params.length; i++) {
|
2937
3358
|
allocVar(innerScope, params[i].name, false);
|
2938
3359
|
|
@@ -2959,6 +3380,8 @@ const generateFunc = (scope, decl) => {
|
|
2959
3380
|
};
|
2960
3381
|
funcIndex[name] = func.index;
|
2961
3382
|
|
3383
|
+
if (name === 'main') func.gotLastType = true;
|
3384
|
+
|
2962
3385
|
// quick hack fixes
|
2963
3386
|
for (const inst of wasm) {
|
2964
3387
|
if (inst[0] === Opcodes.call && inst[1] === -1) {
|
@@ -2993,16 +3416,6 @@ const generateCode = (scope, decl) => {
|
|
2993
3416
|
};
|
2994
3417
|
|
2995
3418
|
const internalConstrs = {
|
2996
|
-
Boolean: {
|
2997
|
-
generate: (scope, decl) => {
|
2998
|
-
if (decl.arguments.length === 0) return number(0);
|
2999
|
-
|
3000
|
-
// should generate/run all args
|
3001
|
-
return truthy(scope, generate(scope, decl.arguments[0]), getNodeType(scope, decl.arguments[0]), false, false);
|
3002
|
-
},
|
3003
|
-
type: TYPES.boolean
|
3004
|
-
},
|
3005
|
-
|
3006
3419
|
Array: {
|
3007
3420
|
generate: (scope, decl, global, name) => {
|
3008
3421
|
// new Array(i0, i1, ...)
|
@@ -3020,7 +3433,7 @@ const internalConstrs = {
|
|
3020
3433
|
|
3021
3434
|
// todo: check in wasm instead of here
|
3022
3435
|
const literalValue = arg.value ?? 0;
|
3023
|
-
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);
|
3024
3437
|
|
3025
3438
|
return [
|
3026
3439
|
...number(0, Valtype.i32),
|
@@ -3031,7 +3444,8 @@ const internalConstrs = {
|
|
3031
3444
|
...number(pointer)
|
3032
3445
|
];
|
3033
3446
|
},
|
3034
|
-
type: TYPES._array
|
3447
|
+
type: TYPES._array,
|
3448
|
+
length: 1
|
3035
3449
|
},
|
3036
3450
|
|
3037
3451
|
__Array_of: {
|
@@ -3043,7 +3457,94 @@ const internalConstrs = {
|
|
3043
3457
|
}, global, name);
|
3044
3458
|
},
|
3045
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,
|
3046
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
|
3047
3548
|
}
|
3048
3549
|
};
|
3049
3550
|
|
@@ -3072,7 +3573,6 @@ export default program => {
|
|
3072
3573
|
funcs = [];
|
3073
3574
|
funcIndex = {};
|
3074
3575
|
depth = [];
|
3075
|
-
arrays = new Map();
|
3076
3576
|
pages = new Map();
|
3077
3577
|
data = [];
|
3078
3578
|
currentFuncIndex = importedFuncs.length;
|
@@ -3086,6 +3586,10 @@ export default program => {
|
|
3086
3586
|
|
3087
3587
|
const valtypeInd = ['i32', 'i64', 'f64'].indexOf(valtype);
|
3088
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
|
+
|
3089
3593
|
// set generic opcodes for current valtype
|
3090
3594
|
Opcodes.const = [ Opcodes.i32_const, Opcodes.i64_const, Opcodes.f64_const ][valtypeInd];
|
3091
3595
|
Opcodes.eq = [ Opcodes.i32_eq, Opcodes.i64_eq, Opcodes.f64_eq ][valtypeInd];
|
@@ -3094,10 +3598,10 @@ export default program => {
|
|
3094
3598
|
Opcodes.add = [ Opcodes.i32_add, Opcodes.i64_add, Opcodes.f64_add ][valtypeInd];
|
3095
3599
|
Opcodes.sub = [ Opcodes.i32_sub, Opcodes.i64_sub, Opcodes.f64_sub ][valtypeInd];
|
3096
3600
|
|
3097
|
-
Opcodes.i32_to = [ [
|
3098
|
-
Opcodes.i32_to_u = [ [
|
3099
|
-
Opcodes.i32_from = [ [
|
3100
|
-
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];
|
3101
3605
|
|
3102
3606
|
Opcodes.load = [ Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load ][valtypeInd];
|
3103
3607
|
Opcodes.store = [ Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store ][valtypeInd];
|
@@ -3110,10 +3614,6 @@ export default program => {
|
|
3110
3614
|
|
3111
3615
|
program.id = { name: 'main' };
|
3112
3616
|
|
3113
|
-
globalThis.pageSize = PageSize;
|
3114
|
-
const pageSizeOpt = process.argv.find(x => x.startsWith('-page-size='));
|
3115
|
-
if (pageSizeOpt) pageSize = parseInt(pageSizeOpt.split('=')[1]) * 1024;
|
3116
|
-
|
3117
3617
|
const scope = {
|
3118
3618
|
locals: {},
|
3119
3619
|
localInd: 0
|
@@ -3124,7 +3624,7 @@ export default program => {
|
|
3124
3624
|
body: program.body
|
3125
3625
|
};
|
3126
3626
|
|
3127
|
-
if (
|
3627
|
+
if (Prefs.astLog) console.log(JSON.stringify(program.body.body, null, 2));
|
3128
3628
|
|
3129
3629
|
generateFunc(scope, program);
|
3130
3630
|
|
@@ -3141,7 +3641,11 @@ export default program => {
|
|
3141
3641
|
}
|
3142
3642
|
|
3143
3643
|
if (lastInst[0] === Opcodes.end || lastInst[0] === Opcodes.local_set || lastInst[0] === Opcodes.global_set) {
|
3144
|
-
|
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
|
+
}
|
3145
3649
|
}
|
3146
3650
|
|
3147
3651
|
if (lastInst[0] === Opcodes.call) {
|