porffor 0.2.0-aea77ff → 0.2.0-b9abe0d
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 +124 -83
- package/asur/README.md +2 -0
- package/asur/index.js +1262 -0
- package/byg/index.js +237 -0
- package/compiler/2c.js +317 -72
- package/compiler/{sections.js → assemble.js} +63 -15
- 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/date.ts +9 -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 +479 -262
- package/compiler/{codeGen.js → codegen.js} +1049 -394
- package/compiler/embedding.js +22 -22
- package/compiler/encoding.js +108 -10
- package/compiler/generated_builtins.js +722 -0
- package/compiler/index.js +36 -34
- package/compiler/log.js +6 -3
- package/compiler/opt.js +51 -36
- package/compiler/parse.js +35 -27
- package/compiler/precompile.js +123 -0
- package/compiler/prefs.js +26 -0
- package/compiler/prototype.js +177 -37
- package/compiler/types.js +37 -0
- package/compiler/wasmSpec.js +29 -7
- package/compiler/wrap.js +52 -39
- package/package.json +9 -5
- package/porf +4 -0
- package/rhemyn/compile.js +5 -3
- package/rhemyn/parse.js +323 -320
- package/rhemyn/test/parse.js +58 -58
- package/runner/compare.js +34 -34
- package/runner/debug.js +122 -0
- package/runner/index.js +49 -10
- package/runner/profiler.js +102 -0
- package/runner/repl.js +40 -7
- package/runner/sizes.js +37 -37
- package/test262_changes_from_1afe9b87d2_to_04-09.md +270 -0
- package/compiler/builtins/base64.js +0 -92
- package/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,39 +25,41 @@ 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';
|
58
|
-
const generate = (scope, decl, global = false, name = undefined) => {
|
62
|
+
const generate = (scope, decl, global = false, name = undefined, valueUnused = false) => {
|
59
63
|
switch (decl.type) {
|
60
64
|
case 'BinaryExpression':
|
61
65
|
return generateBinaryExp(scope, decl, global, name);
|
@@ -86,7 +90,7 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
86
90
|
return generateExp(scope, decl);
|
87
91
|
|
88
92
|
case 'CallExpression':
|
89
|
-
return generateCall(scope, decl, global, name);
|
93
|
+
return generateCall(scope, decl, global, name, valueUnused);
|
90
94
|
|
91
95
|
case 'NewExpression':
|
92
96
|
return generateNew(scope, decl, global, name);
|
@@ -104,7 +108,7 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
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) => {
|
|
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) => {
|
|
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) => {
|
|
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) => {
|
|
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) => {
|
|
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) => {
|
|
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) => {
|
|
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
|
}
|
@@ -196,35 +216,53 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
196
216
|
return out;
|
197
217
|
},
|
198
218
|
|
199
|
-
|
200
|
-
|
219
|
+
__Porffor_bs: str => [
|
220
|
+
...makeString(scope, str, global, name, true),
|
201
221
|
|
202
|
-
|
203
|
-
...number(
|
204
|
-
|
222
|
+
...(name ? setType(scope, name, TYPES._bytestring) : [
|
223
|
+
...number(TYPES._bytestring, Valtype.i32),
|
224
|
+
...setLastType(scope)
|
225
|
+
])
|
226
|
+
],
|
227
|
+
__Porffor_s: str => [
|
228
|
+
...makeString(scope, str, global, name, false),
|
205
229
|
|
206
|
-
|
207
|
-
...number(
|
208
|
-
|
209
|
-
]
|
210
|
-
|
211
|
-
}
|
230
|
+
...(name ? setType(scope, name, TYPES.string) : [
|
231
|
+
...number(TYPES.string, Valtype.i32),
|
232
|
+
...setLastType(scope)
|
233
|
+
])
|
234
|
+
],
|
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;
|
216
251
|
|
217
|
-
|
218
|
-
|
252
|
+
str += quasis[i + 1].value.raw;
|
253
|
+
}
|
254
|
+
|
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',
|
@@ -274,25 +312,33 @@ const generateIdent = (scope, decl) => {
|
|
274
312
|
const name = mapName(rawName);
|
275
313
|
let local = scope.locals[rawName];
|
276
314
|
|
277
|
-
if (builtinVars
|
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();
|
321
|
+
}
|
322
|
+
|
323
|
+
if (Object.hasOwn(builtinFuncs, name) || Object.hasOwn(internalConstrs, name)) {
|
324
|
+
// todo: return an actual something
|
325
|
+
return number(1);
|
280
326
|
}
|
281
327
|
|
282
|
-
if (
|
328
|
+
if (isExistingProtoFunc(name)) {
|
283
329
|
// todo: return an actual something
|
284
330
|
return number(1);
|
285
331
|
}
|
286
332
|
|
287
|
-
if (local === undefined) {
|
333
|
+
if (local?.idx === undefined) {
|
288
334
|
// no local var with name
|
289
|
-
if (
|
290
|
-
if (funcIndex
|
335
|
+
if (Object.hasOwn(importedFuncs, name)) return number(importedFuncs[name]);
|
336
|
+
if (Object.hasOwn(funcIndex, name)) return number(funcIndex[name]);
|
291
337
|
|
292
|
-
if (globals
|
338
|
+
if (Object.hasOwn(globals, name)) return [ [ Opcodes.global_get, globals[name].idx ] ];
|
293
339
|
}
|
294
340
|
|
295
|
-
if (local === undefined && rawName.startsWith('__')) {
|
341
|
+
if (local?.idx === undefined && rawName.startsWith('__')) {
|
296
342
|
// return undefined if unknown key in already known var
|
297
343
|
let parent = rawName.slice(2).split('_').slice(0, -1).join('_');
|
298
344
|
if (parent.includes('_')) parent = '__' + parent;
|
@@ -301,7 +347,7 @@ const generateIdent = (scope, decl) => {
|
|
301
347
|
if (!parentLookup[1]) return number(UNDEFINED);
|
302
348
|
}
|
303
349
|
|
304
|
-
if (local === undefined) return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
350
|
+
if (local?.idx === undefined) return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
305
351
|
|
306
352
|
return [ [ Opcodes.local_get, local.idx ] ];
|
307
353
|
};
|
@@ -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
|
@@ -685,16 +739,27 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
685
739
|
[ Opcodes.i32_eqz ], */
|
686
740
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
687
741
|
],
|
742
|
+
[TYPES._bytestring]: [ // duplicate of string
|
743
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
744
|
+
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
745
|
+
|
746
|
+
// get length
|
747
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
748
|
+
|
749
|
+
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
750
|
+
],
|
688
751
|
default: def
|
689
752
|
}, intOut ? Valtype.i32 : valtypeBinary)
|
690
753
|
];
|
691
754
|
};
|
692
755
|
|
693
756
|
const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
694
|
-
const
|
757
|
+
const useTmp = knownType(scope, type) == null;
|
758
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
759
|
+
|
695
760
|
return [
|
696
761
|
...wasm,
|
697
|
-
[ Opcodes.local_set, tmp ],
|
762
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
698
763
|
|
699
764
|
...typeSwitch(scope, type, {
|
700
765
|
[TYPES._array]: [
|
@@ -702,7 +767,18 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
702
767
|
...number(0, intOut ? Valtype.i32 : valtypeBinary)
|
703
768
|
],
|
704
769
|
[TYPES.string]: [
|
705
|
-
[ Opcodes.local_get, tmp ],
|
770
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
771
|
+
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
772
|
+
|
773
|
+
// get length
|
774
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
775
|
+
|
776
|
+
// if length == 0
|
777
|
+
[ Opcodes.i32_eqz ],
|
778
|
+
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
779
|
+
],
|
780
|
+
[TYPES._bytestring]: [ // duplicate of string
|
781
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
706
782
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
707
783
|
|
708
784
|
// get length
|
@@ -714,7 +790,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
714
790
|
],
|
715
791
|
default: [
|
716
792
|
// if value == 0
|
717
|
-
[ Opcodes.local_get, tmp ],
|
793
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
718
794
|
|
719
795
|
...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
720
796
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
@@ -724,10 +800,12 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
724
800
|
};
|
725
801
|
|
726
802
|
const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
|
727
|
-
const
|
803
|
+
const useTmp = knownType(scope, type) == null;
|
804
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
805
|
+
|
728
806
|
return [
|
729
807
|
...wasm,
|
730
|
-
[ Opcodes.local_set, tmp ],
|
808
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
731
809
|
|
732
810
|
...typeSwitch(scope, type, {
|
733
811
|
[TYPES.undefined]: [
|
@@ -736,7 +814,7 @@ const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
736
814
|
],
|
737
815
|
[TYPES.object]: [
|
738
816
|
// object, null if == 0
|
739
|
-
[ Opcodes.local_get, tmp ],
|
817
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
740
818
|
|
741
819
|
...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
742
820
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
@@ -765,11 +843,14 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
765
843
|
return performLogicOp(scope, op, left, right, leftType, rightType);
|
766
844
|
}
|
767
845
|
|
846
|
+
const knownLeft = knownType(scope, leftType);
|
847
|
+
const knownRight = knownType(scope, rightType);
|
848
|
+
|
768
849
|
const eqOp = ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(op);
|
769
850
|
const strictOp = op === '===' || op === '!==';
|
770
851
|
|
771
852
|
const startOut = [], endOut = [];
|
772
|
-
const
|
853
|
+
const finalize = out => startOut.concat(out, endOut);
|
773
854
|
|
774
855
|
// if strict (in)equal check types match
|
775
856
|
if (strictOp) {
|
@@ -814,31 +895,59 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
814
895
|
// todo: if equality op and an operand is undefined, return false
|
815
896
|
// todo: niche null hell with 0
|
816
897
|
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
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
|
+
}
|
842
951
|
|
843
952
|
let ops = operatorOpcode[valtype][op];
|
844
953
|
|
@@ -848,33 +957,69 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
848
957
|
includeBuiltin(scope, builtinName);
|
849
958
|
const idx = funcIndex[builtinName];
|
850
959
|
|
851
|
-
return
|
960
|
+
return finalize([
|
852
961
|
...left,
|
853
962
|
...right,
|
854
963
|
[ Opcodes.call, idx ]
|
855
964
|
]);
|
856
965
|
}
|
857
966
|
|
858
|
-
if (!ops) return todo(`operator ${op} not implemented yet
|
967
|
+
if (!ops) return todo(scope, `operator ${op} not implemented yet`, true);
|
859
968
|
|
860
969
|
if (!Array.isArray(ops)) ops = [ ops ];
|
861
970
|
ops = [ ops ];
|
862
971
|
|
863
972
|
let tmpLeft, tmpRight;
|
864
973
|
// if equal op, check if strings for compareStrings
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
// todo: intelligent partial skip later
|
870
|
-
// if neither known are string, stop this madness
|
871
|
-
if ((knownLeft != null && knownLeft !== TYPES.string) && (knownRight != null && knownRight !== TYPES.string)) {
|
872
|
-
return;
|
873
|
-
}
|
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
|
874
977
|
|
978
|
+
if ((op === '===' || op === '==' || op === '!==' || op === '!=') && (knownLeft == null && knownRight == null)) {
|
875
979
|
tmpLeft = localTmp(scope, '__tmpop_left');
|
876
980
|
tmpRight = localTmp(scope, '__tmpop_right');
|
877
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)
|
878
1023
|
ops.unshift(...stringOnly([
|
879
1024
|
// if left is string
|
880
1025
|
...leftType,
|
@@ -886,30 +1031,28 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
886
1031
|
...number(TYPES.string, Valtype.i32),
|
887
1032
|
[ Opcodes.i32_eq ],
|
888
1033
|
|
889
|
-
// if
|
890
|
-
[ Opcodes.
|
1034
|
+
// if both are true
|
1035
|
+
[ Opcodes.i32_and ],
|
891
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 ],
|
892
1041
|
|
893
|
-
//
|
894
|
-
// if left is not string
|
1042
|
+
// if left is bytestring
|
895
1043
|
...leftType,
|
896
|
-
...number(TYPES.
|
897
|
-
[ Opcodes.
|
1044
|
+
...number(TYPES._bytestring, Valtype.i32),
|
1045
|
+
[ Opcodes.i32_eq ],
|
898
1046
|
|
899
|
-
// if right is
|
1047
|
+
// if right is bytestring
|
900
1048
|
...rightType,
|
901
|
-
...number(TYPES.
|
902
|
-
[ Opcodes.
|
1049
|
+
...number(TYPES._bytestring, Valtype.i32),
|
1050
|
+
[ Opcodes.i32_eq ],
|
903
1051
|
|
904
|
-
// if
|
905
|
-
[ Opcodes.
|
1052
|
+
// if both are true
|
1053
|
+
[ Opcodes.i32_and ],
|
906
1054
|
[ Opcodes.if, Blocktype.void ],
|
907
|
-
...
|
908
|
-
[ Opcodes.br, 1 ],
|
909
|
-
[ Opcodes.end ],
|
910
|
-
|
911
|
-
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
912
|
-
// ...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
1055
|
+
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], true),
|
913
1056
|
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
914
1057
|
[ Opcodes.br, 1 ],
|
915
1058
|
[ Opcodes.end ],
|
@@ -921,9 +1064,9 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
921
1064
|
// endOut.push(stringOnly([ Opcodes.end ]));
|
922
1065
|
endOut.unshift(stringOnly([ Opcodes.end ]));
|
923
1066
|
// }
|
924
|
-
}
|
1067
|
+
}
|
925
1068
|
|
926
|
-
return
|
1069
|
+
return finalize([
|
927
1070
|
...left,
|
928
1071
|
...(tmpLeft != null ? stringOnly([ [ Opcodes.local_tee, tmpLeft ] ]) : []),
|
929
1072
|
...right,
|
@@ -940,7 +1083,22 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
940
1083
|
return out;
|
941
1084
|
};
|
942
1085
|
|
943
|
-
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 = [] }) => {
|
944
1102
|
const existing = funcs.find(x => x.name === name);
|
945
1103
|
if (existing) return existing;
|
946
1104
|
|
@@ -952,18 +1110,14 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
952
1110
|
locals[nameParam(i)] = { idx: i, type: allLocals[i] };
|
953
1111
|
}
|
954
1112
|
|
955
|
-
|
956
|
-
const
|
957
|
-
|
958
|
-
|
959
|
-
locals,
|
960
|
-
returns,
|
961
|
-
localInd: allLocals.length,
|
962
|
-
};
|
963
|
-
|
964
|
-
wasm = wasm(scope, { TYPES, typeSwitch, makeArray });
|
1113
|
+
for (const x of _data) {
|
1114
|
+
const copy = { ...x };
|
1115
|
+
copy.offset += pages.size * pageSize;
|
1116
|
+
data.push(copy);
|
965
1117
|
}
|
966
1118
|
|
1119
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, { name, params, locals, returns, localInd: allLocals.length });
|
1120
|
+
|
967
1121
|
let baseGlobalIdx, i = 0;
|
968
1122
|
for (const type of globalTypes) {
|
969
1123
|
if (baseGlobalIdx === undefined) baseGlobalIdx = globalInd;
|
@@ -986,7 +1140,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
986
1140
|
params,
|
987
1141
|
locals,
|
988
1142
|
returns,
|
989
|
-
returnType:
|
1143
|
+
returnType: returnType ?? TYPES.number,
|
990
1144
|
wasm,
|
991
1145
|
internal: true,
|
992
1146
|
index: currentFuncIndex++
|
@@ -1009,6 +1163,7 @@ const generateLogicExp = (scope, decl) => {
|
|
1009
1163
|
return performLogicOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right));
|
1010
1164
|
};
|
1011
1165
|
|
1166
|
+
// potential future ideas for nan boxing (unused):
|
1012
1167
|
// T = JS type, V = value/pointer
|
1013
1168
|
// 0bTTT
|
1014
1169
|
// qNAN: 0 11111111111 1000000000000000000000000000000000000000000000000001
|
@@ -1032,47 +1187,29 @@ const generateLogicExp = (scope, decl) => {
|
|
1032
1187
|
// 4: internal type
|
1033
1188
|
// 5: pointer
|
1034
1189
|
|
1035
|
-
const
|
1036
|
-
|
1037
|
-
|
1038
|
-
string: 0x02,
|
1039
|
-
undefined: 0x03,
|
1040
|
-
object: 0x04,
|
1041
|
-
function: 0x05,
|
1042
|
-
symbol: 0x06,
|
1043
|
-
bigint: 0x07,
|
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)];
|
1044
1193
|
|
1045
|
-
|
1046
|
-
_array: 0x10,
|
1047
|
-
_regexp: 0x11
|
1048
|
-
};
|
1049
|
-
|
1050
|
-
const TYPE_NAMES = {
|
1051
|
-
[TYPES.number]: 'Number',
|
1052
|
-
[TYPES.boolean]: 'Boolean',
|
1053
|
-
[TYPES.string]: 'String',
|
1054
|
-
[TYPES.undefined]: 'undefined',
|
1055
|
-
[TYPES.object]: 'Object',
|
1056
|
-
[TYPES.function]: 'Function',
|
1057
|
-
[TYPES.symbol]: 'Symbol',
|
1058
|
-
[TYPES.bigint]: 'BigInt',
|
1059
|
-
|
1060
|
-
[TYPES._array]: 'Array',
|
1061
|
-
[TYPES._regexp]: 'RegExp'
|
1194
|
+
return false;
|
1062
1195
|
};
|
1063
1196
|
|
1064
1197
|
const getType = (scope, _name) => {
|
1065
1198
|
const name = mapName(_name);
|
1066
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);
|
1067
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);
|
1068
1206
|
if (globals[name]) return [ [ Opcodes.global_get, globals[name + '#type'].idx ] ];
|
1069
1207
|
|
1070
1208
|
let type = TYPES.undefined;
|
1071
|
-
if (builtinVars[name]) type =
|
1209
|
+
if (builtinVars[name]) type = builtinVars[name].type ?? TYPES.number;
|
1072
1210
|
if (builtinFuncs[name] !== undefined || importedFuncs[name] !== undefined || funcIndex[name] !== undefined || internalConstrs[name] !== undefined) type = TYPES.function;
|
1073
1211
|
|
1074
|
-
if (name
|
1075
|
-
name.startsWith('__String_prototype_') && prototypeFuncs[TYPES.string][name.slice(19)]) type = TYPES.function;
|
1212
|
+
if (isExistingProtoFunc(name)) type = TYPES.function;
|
1076
1213
|
|
1077
1214
|
return number(type, Valtype.i32);
|
1078
1215
|
};
|
@@ -1095,15 +1232,16 @@ const setType = (scope, _name, type) => {
|
|
1095
1232
|
];
|
1096
1233
|
|
1097
1234
|
// throw new Error('could not find var');
|
1235
|
+
return [];
|
1098
1236
|
};
|
1099
1237
|
|
1100
1238
|
const getLastType = scope => {
|
1101
1239
|
scope.gotLastType = true;
|
1102
|
-
return [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ];
|
1240
|
+
return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1103
1241
|
};
|
1104
1242
|
|
1105
1243
|
const setLastType = scope => {
|
1106
|
-
return [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ];
|
1244
|
+
return [ [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1107
1245
|
};
|
1108
1246
|
|
1109
1247
|
const getNodeType = (scope, node) => {
|
@@ -1111,6 +1249,8 @@ const getNodeType = (scope, node) => {
|
|
1111
1249
|
if (node.type === 'Literal') {
|
1112
1250
|
if (node.regex) return TYPES._regexp;
|
1113
1251
|
|
1252
|
+
if (typeof node.value === 'string' && byteStringable(node.value)) return TYPES._bytestring;
|
1253
|
+
|
1114
1254
|
return TYPES[typeof node.value];
|
1115
1255
|
}
|
1116
1256
|
|
@@ -1124,6 +1264,27 @@ const getNodeType = (scope, node) => {
|
|
1124
1264
|
|
1125
1265
|
if (node.type === 'CallExpression' || node.type === 'NewExpression') {
|
1126
1266
|
const name = node.callee.name;
|
1267
|
+
if (!name) {
|
1268
|
+
// iife
|
1269
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1270
|
+
|
1271
|
+
// presume
|
1272
|
+
// todo: warn here?
|
1273
|
+
return TYPES.number;
|
1274
|
+
}
|
1275
|
+
|
1276
|
+
if (node.type === 'NewExpression' && builtinFuncs[name + '$constructor']) {
|
1277
|
+
if (builtinFuncs[name + '$constructor'].typedReturns) {
|
1278
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1279
|
+
|
1280
|
+
// presume
|
1281
|
+
// todo: warn here?
|
1282
|
+
return TYPES.number;
|
1283
|
+
}
|
1284
|
+
|
1285
|
+
return builtinFuncs[name + '$constructor'].returnType ?? TYPES.number;
|
1286
|
+
}
|
1287
|
+
|
1127
1288
|
const func = funcs.find(x => x.name === name);
|
1128
1289
|
|
1129
1290
|
if (func) {
|
@@ -1131,7 +1292,7 @@ const getNodeType = (scope, node) => {
|
|
1131
1292
|
if (func.returnType) return func.returnType;
|
1132
1293
|
}
|
1133
1294
|
|
1134
|
-
if (builtinFuncs[name]) return
|
1295
|
+
if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return builtinFuncs[name].returnType ?? TYPES.number;
|
1135
1296
|
if (internalConstrs[name]) return internalConstrs[name].type;
|
1136
1297
|
|
1137
1298
|
// check if this is a prototype function
|
@@ -1142,11 +1303,16 @@ const getNodeType = (scope, node) => {
|
|
1142
1303
|
const spl = name.slice(2).split('_');
|
1143
1304
|
|
1144
1305
|
const func = spl[spl.length - 1];
|
1145
|
-
const protoFuncs = Object.
|
1306
|
+
const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES._bytestring && prototypeFuncs[x][func] != null);
|
1146
1307
|
if (protoFuncs.length === 1) return protoFuncs[0].returnType ?? TYPES.number;
|
1147
1308
|
}
|
1148
1309
|
|
1149
|
-
if (
|
1310
|
+
if (name.startsWith('__Porffor_wasm_')) {
|
1311
|
+
// todo: return undefined for non-returning ops
|
1312
|
+
return TYPES.number;
|
1313
|
+
}
|
1314
|
+
|
1315
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1150
1316
|
|
1151
1317
|
// presume
|
1152
1318
|
// todo: warn here?
|
@@ -1194,6 +1360,15 @@ const getNodeType = (scope, node) => {
|
|
1194
1360
|
|
1195
1361
|
if (node.type === 'BinaryExpression') {
|
1196
1362
|
if (['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(node.operator)) return TYPES.boolean;
|
1363
|
+
if (node.operator !== '+') return TYPES.number;
|
1364
|
+
|
1365
|
+
const knownLeft = knownType(scope, getNodeType(scope, node.left));
|
1366
|
+
const knownRight = knownType(scope, getNodeType(scope, node.right));
|
1367
|
+
|
1368
|
+
// todo: this should be dynamic but for now only static
|
1369
|
+
if (knownLeft === TYPES.string || knownRight === TYPES.string) return TYPES.string;
|
1370
|
+
if (knownLeft === TYPES._bytestring || knownRight === TYPES._bytestring) return TYPES._bytestring;
|
1371
|
+
|
1197
1372
|
return TYPES.number;
|
1198
1373
|
|
1199
1374
|
// todo: string concat types
|
@@ -1218,7 +1393,7 @@ const getNodeType = (scope, node) => {
|
|
1218
1393
|
if (node.operator === '!') return TYPES.boolean;
|
1219
1394
|
if (node.operator === 'void') return TYPES.undefined;
|
1220
1395
|
if (node.operator === 'delete') return TYPES.boolean;
|
1221
|
-
if (node.operator === 'typeof') return TYPES.string;
|
1396
|
+
if (node.operator === 'typeof') return Prefs.bytestring ? TYPES._bytestring : TYPES.string;
|
1222
1397
|
|
1223
1398
|
return TYPES.number;
|
1224
1399
|
}
|
@@ -1227,11 +1402,23 @@ const getNodeType = (scope, node) => {
|
|
1227
1402
|
// hack: if something.length, number type
|
1228
1403
|
if (node.property.name === 'length') return TYPES.number;
|
1229
1404
|
|
1230
|
-
//
|
1405
|
+
// ts hack
|
1406
|
+
if (scope.locals[node.object.name]?.metadata?.type === TYPES.string) return TYPES.string;
|
1407
|
+
if (scope.locals[node.object.name]?.metadata?.type === TYPES._bytestring) return TYPES._bytestring;
|
1408
|
+
if (scope.locals[node.object.name]?.metadata?.type === TYPES._array) return TYPES.number;
|
1409
|
+
|
1410
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1411
|
+
|
1412
|
+
// presume
|
1231
1413
|
return TYPES.number;
|
1232
1414
|
}
|
1233
1415
|
|
1234
|
-
if (
|
1416
|
+
if (node.type === 'TaggedTemplateExpression') {
|
1417
|
+
// hack
|
1418
|
+
if (node.tag.name.startsWith('__Porffor_')) return TYPES.number;
|
1419
|
+
}
|
1420
|
+
|
1421
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1235
1422
|
|
1236
1423
|
// presume
|
1237
1424
|
// todo: warn here?
|
@@ -1244,28 +1431,11 @@ const getNodeType = (scope, node) => {
|
|
1244
1431
|
return ret;
|
1245
1432
|
};
|
1246
1433
|
|
1247
|
-
const toString = (scope, wasm, type) => {
|
1248
|
-
const tmp = localTmp(scope, '#tostring_tmp');
|
1249
|
-
return [
|
1250
|
-
...wasm,
|
1251
|
-
[ Opcodes.local_set, tmp ],
|
1252
|
-
|
1253
|
-
...typeSwitch(scope, type, {
|
1254
|
-
[TYPES.string]: [
|
1255
|
-
[ Opcodes.local_get, tmp ]
|
1256
|
-
],
|
1257
|
-
[TYPES.undefined]: [
|
1258
|
-
// [ Opcodes.]
|
1259
|
-
]
|
1260
|
-
})
|
1261
|
-
]
|
1262
|
-
};
|
1263
|
-
|
1264
1434
|
const generateLiteral = (scope, decl, global, name) => {
|
1265
1435
|
if (decl.value === null) return number(NULL);
|
1266
1436
|
|
1437
|
+
// hack: just return 1 for regex literals
|
1267
1438
|
if (decl.regex) {
|
1268
|
-
scope.regex[name] = decl.regex;
|
1269
1439
|
return number(1);
|
1270
1440
|
}
|
1271
1441
|
|
@@ -1278,19 +1448,10 @@ const generateLiteral = (scope, decl, global, name) => {
|
|
1278
1448
|
return number(decl.value ? 1 : 0);
|
1279
1449
|
|
1280
1450
|
case 'string':
|
1281
|
-
|
1282
|
-
const rawElements = new Array(str.length);
|
1283
|
-
let j = 0;
|
1284
|
-
for (let i = 0; i < str.length; i++) {
|
1285
|
-
rawElements[i] = str.charCodeAt(i);
|
1286
|
-
}
|
1287
|
-
|
1288
|
-
return makeArray(scope, {
|
1289
|
-
rawElements
|
1290
|
-
}, global, name, false, 'i16')[0];
|
1451
|
+
return makeString(scope, decl.value, global, name);
|
1291
1452
|
|
1292
1453
|
default:
|
1293
|
-
return todo(`cannot generate literal of type ${typeof decl.value}
|
1454
|
+
return todo(scope, `cannot generate literal of type ${typeof decl.value}`, true);
|
1294
1455
|
}
|
1295
1456
|
};
|
1296
1457
|
|
@@ -1299,6 +1460,8 @@ const countLeftover = wasm => {
|
|
1299
1460
|
|
1300
1461
|
for (let i = 0; i < wasm.length; i++) {
|
1301
1462
|
const inst = wasm[i];
|
1463
|
+
if (inst[0] == null) continue;
|
1464
|
+
|
1302
1465
|
if (depth === 0 && (inst[0] === Opcodes.if || inst[0] === Opcodes.block || inst[0] === Opcodes.loop)) {
|
1303
1466
|
if (inst[0] === Opcodes.if) count--;
|
1304
1467
|
if (inst[1] !== Blocktype.void) count++;
|
@@ -1307,18 +1470,25 @@ const countLeftover = wasm => {
|
|
1307
1470
|
if (inst[0] === Opcodes.end) depth--;
|
1308
1471
|
|
1309
1472
|
if (depth === 0)
|
1310
|
-
if ([Opcodes.throw,Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
|
1311
|
-
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.memory_grow].includes(inst[0]) && (inst[0] !== 0xfc || inst[1] < 0x0a)) {}
|
1473
|
+
if ([Opcodes.throw, Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
|
1474
|
+
else if ([null, Opcodes.i32_eqz, Opcodes.i64_eqz, Opcodes.f64_ceil, Opcodes.f64_floor, Opcodes.f64_trunc, Opcodes.f64_nearest, Opcodes.f64_sqrt, Opcodes.local_tee, Opcodes.i32_wrap_i64, Opcodes.i64_extend_i32_s, Opcodes.i64_extend_i32_u, Opcodes.f32_demote_f64, Opcodes.f64_promote_f32, Opcodes.f64_convert_i32_s, Opcodes.f64_convert_i32_u, Opcodes.i32_clz, Opcodes.i32_ctz, Opcodes.i32_popcnt, Opcodes.f64_neg, Opcodes.end, Opcodes.i32_trunc_sat_f64_s[0], Opcodes.i32x4_extract_lane, Opcodes.i16x8_extract_lane, Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load, Opcodes.v128_load, Opcodes.i32_load16_u, Opcodes.i32_load16_s, Opcodes.i32_load8_u, Opcodes.i32_load8_s, Opcodes.memory_grow].includes(inst[0]) && (inst[0] !== 0xfc || inst[1] < 0x0a)) {}
|
1312
1475
|
else if ([Opcodes.local_get, Opcodes.global_get, Opcodes.f64_const, Opcodes.i32_const, Opcodes.i64_const, Opcodes.v128_const].includes(inst[0])) count++;
|
1313
|
-
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16].includes(inst[0])) count -= 2;
|
1476
|
+
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
|
1314
1477
|
else if (Opcodes.memory_copy[0] === inst[0] && Opcodes.memory_copy[1] === inst[1]) count -= 3;
|
1315
1478
|
else if (inst[0] === Opcodes.return) count = 0;
|
1316
1479
|
else if (inst[0] === Opcodes.call) {
|
1317
1480
|
let func = funcs.find(x => x.index === inst[1]);
|
1318
|
-
if (
|
1319
|
-
count
|
1320
|
-
} else
|
1321
|
-
|
1481
|
+
if (inst[1] === -1) {
|
1482
|
+
// todo: count for calling self
|
1483
|
+
} else if (!func && inst[1] < importedFuncs.length) {
|
1484
|
+
count -= importedFuncs[inst[1]].params;
|
1485
|
+
count += importedFuncs[inst[1]].returns;
|
1486
|
+
} else {
|
1487
|
+
if (func) {
|
1488
|
+
count -= func.params.length;
|
1489
|
+
} else count--;
|
1490
|
+
if (func) count += func.returns.length;
|
1491
|
+
}
|
1322
1492
|
} else count--;
|
1323
1493
|
|
1324
1494
|
// console.log(count, decompile([ inst ]).slice(0, -1));
|
@@ -1336,7 +1506,7 @@ const disposeLeftover = wasm => {
|
|
1336
1506
|
const generateExp = (scope, decl) => {
|
1337
1507
|
const expression = decl.expression;
|
1338
1508
|
|
1339
|
-
const out = generate(scope, expression);
|
1509
|
+
const out = generate(scope, expression, undefined, undefined, true);
|
1340
1510
|
disposeLeftover(out);
|
1341
1511
|
|
1342
1512
|
return out;
|
@@ -1394,7 +1564,7 @@ const RTArrayUtil = {
|
|
1394
1564
|
]
|
1395
1565
|
};
|
1396
1566
|
|
1397
|
-
const generateCall = (scope, decl, _global, _name) => {
|
1567
|
+
const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
1398
1568
|
/* const callee = decl.callee;
|
1399
1569
|
const args = decl.arguments;
|
1400
1570
|
|
@@ -1410,10 +1580,21 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1410
1580
|
name = func.name;
|
1411
1581
|
}
|
1412
1582
|
|
1413
|
-
if (name === 'eval' && decl.arguments[0]
|
1583
|
+
if (name === 'eval' && decl.arguments[0]?.type === 'Literal') {
|
1414
1584
|
// literal eval hack
|
1415
|
-
const code = decl.arguments[0]
|
1416
|
-
|
1585
|
+
const code = decl.arguments[0]?.value ?? '';
|
1586
|
+
|
1587
|
+
let parsed;
|
1588
|
+
try {
|
1589
|
+
parsed = parse(code, []);
|
1590
|
+
} catch (e) {
|
1591
|
+
if (e.name === 'SyntaxError') {
|
1592
|
+
// throw syntax errors of evals at runtime instead
|
1593
|
+
return internalThrow(scope, 'SyntaxError', e.message, true);
|
1594
|
+
}
|
1595
|
+
|
1596
|
+
throw e;
|
1597
|
+
}
|
1417
1598
|
|
1418
1599
|
const out = generate(scope, {
|
1419
1600
|
type: 'BlockStatement',
|
@@ -1427,13 +1608,13 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1427
1608
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1428
1609
|
out.push(
|
1429
1610
|
...getNodeType(scope, finalStatement),
|
1430
|
-
setLastType(scope)
|
1611
|
+
...setLastType(scope)
|
1431
1612
|
);
|
1432
1613
|
} else if (countLeftover(out) === 0) {
|
1433
1614
|
out.push(...number(UNDEFINED));
|
1434
1615
|
out.push(
|
1435
1616
|
...number(TYPES.undefined, Valtype.i32),
|
1436
|
-
setLastType(scope)
|
1617
|
+
...setLastType(scope)
|
1437
1618
|
);
|
1438
1619
|
}
|
1439
1620
|
|
@@ -1455,13 +1636,16 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1455
1636
|
|
1456
1637
|
target = { ...decl.callee };
|
1457
1638
|
target.name = spl.slice(0, -1).join('_');
|
1639
|
+
|
1640
|
+
// failed to lookup name, abort
|
1641
|
+
if (!lookupName(scope, target.name)[0]) protoName = null;
|
1458
1642
|
}
|
1459
1643
|
|
1460
1644
|
// literal.func()
|
1461
1645
|
if (!name && decl.callee.type === 'MemberExpression') {
|
1462
1646
|
// megahack for /regex/.func()
|
1463
|
-
|
1464
|
-
|
1647
|
+
const funcName = decl.callee.property.name;
|
1648
|
+
if (decl.callee.object.regex && Object.hasOwn(Rhemyn, funcName)) {
|
1465
1649
|
const func = Rhemyn[funcName](decl.callee.object.regex.pattern, currentFuncIndex++);
|
1466
1650
|
|
1467
1651
|
funcIndex[func.name] = func.index;
|
@@ -1477,7 +1661,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1477
1661
|
Opcodes.i32_from_u,
|
1478
1662
|
|
1479
1663
|
...number(TYPES.boolean, Valtype.i32),
|
1480
|
-
setLastType(scope)
|
1664
|
+
...setLastType(scope)
|
1481
1665
|
];
|
1482
1666
|
}
|
1483
1667
|
|
@@ -1502,23 +1686,47 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1502
1686
|
// }
|
1503
1687
|
|
1504
1688
|
if (protoName) {
|
1689
|
+
const protoBC = {};
|
1690
|
+
|
1691
|
+
const builtinProtoCands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + protoName));
|
1692
|
+
|
1693
|
+
if (!decl._protoInternalCall && builtinProtoCands.length > 0) {
|
1694
|
+
for (const x of builtinProtoCands) {
|
1695
|
+
const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
|
1696
|
+
if (type == null) continue;
|
1697
|
+
|
1698
|
+
protoBC[type] = generateCall(scope, {
|
1699
|
+
callee: {
|
1700
|
+
name: x
|
1701
|
+
},
|
1702
|
+
arguments: [ target, ...decl.arguments ],
|
1703
|
+
_protoInternalCall: true
|
1704
|
+
});
|
1705
|
+
}
|
1706
|
+
}
|
1707
|
+
|
1505
1708
|
const protoCands = Object.keys(prototypeFuncs).reduce((acc, x) => {
|
1506
|
-
|
1507
|
-
if (f) acc[x] = f;
|
1709
|
+
if (Object.hasOwn(prototypeFuncs[x], protoName)) acc[x] = prototypeFuncs[x][protoName];
|
1508
1710
|
return acc;
|
1509
1711
|
}, {});
|
1510
1712
|
|
1511
|
-
// no prototype function candidates, ignore
|
1512
1713
|
if (Object.keys(protoCands).length > 0) {
|
1513
1714
|
// use local for cached i32 length as commonly used
|
1514
1715
|
const lengthLocal = localTmp(scope, '__proto_length_cache', Valtype.i32);
|
1515
1716
|
const pointerLocal = localTmp(scope, '__proto_pointer_cache', Valtype.i32);
|
1516
|
-
const getPointer = [ [ Opcodes.local_get, pointerLocal ] ];
|
1517
1717
|
|
1518
1718
|
// TODO: long-term, prototypes should be their individual separate funcs
|
1519
1719
|
|
1720
|
+
const rawPointer = [
|
1721
|
+
...generate(scope, target),
|
1722
|
+
Opcodes.i32_to_u
|
1723
|
+
];
|
1724
|
+
|
1725
|
+
const usePointerCache = !Object.values(protoCands).every(x => x.noPointerCache === true);
|
1726
|
+
const getPointer = usePointerCache ? [ [ Opcodes.local_get, pointerLocal ] ] : rawPointer;
|
1727
|
+
|
1728
|
+
let allOptUnused = true;
|
1520
1729
|
let lengthI32CacheUsed = false;
|
1521
|
-
const protoBC = {};
|
1522
1730
|
for (const x in protoCands) {
|
1523
1731
|
const protoFunc = protoCands[x];
|
1524
1732
|
if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
|
@@ -1526,7 +1734,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1526
1734
|
...RTArrayUtil.getLength(getPointer),
|
1527
1735
|
|
1528
1736
|
...number(TYPES.number, Valtype.i32),
|
1529
|
-
setLastType(scope)
|
1737
|
+
...setLastType(scope)
|
1530
1738
|
];
|
1531
1739
|
continue;
|
1532
1740
|
}
|
@@ -1536,6 +1744,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1536
1744
|
const protoLocal = protoFunc.local ? localTmp(scope, `__${protoName}_tmp`, protoFunc.local) : -1;
|
1537
1745
|
const protoLocal2 = protoFunc.local2 ? localTmp(scope, `__${protoName}_tmp2`, protoFunc.local2) : -1;
|
1538
1746
|
|
1747
|
+
let optUnused = false;
|
1539
1748
|
const protoOut = protoFunc(getPointer, {
|
1540
1749
|
getCachedI32: () => {
|
1541
1750
|
lengthI32CacheUsed = true;
|
@@ -1550,23 +1759,30 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1550
1759
|
return makeArray(scope, {
|
1551
1760
|
rawElements: new Array(length)
|
1552
1761
|
}, _global, _name, true, itemType);
|
1762
|
+
}, () => {
|
1763
|
+
optUnused = true;
|
1764
|
+
return unusedValue;
|
1553
1765
|
});
|
1554
1766
|
|
1767
|
+
if (!optUnused) allOptUnused = false;
|
1768
|
+
|
1555
1769
|
protoBC[x] = [
|
1556
|
-
[ Opcodes.block, valtypeBinary ],
|
1770
|
+
[ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
|
1557
1771
|
...protoOut,
|
1558
1772
|
|
1559
1773
|
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1560
|
-
setLastType(scope),
|
1774
|
+
...setLastType(scope),
|
1561
1775
|
[ Opcodes.end ]
|
1562
1776
|
];
|
1563
1777
|
}
|
1564
1778
|
|
1565
|
-
|
1566
|
-
...generate(scope, target),
|
1779
|
+
// todo: if some cands use optUnused and some don't, we will probably crash
|
1567
1780
|
|
1568
|
-
|
1569
|
-
|
1781
|
+
return [
|
1782
|
+
...(usePointerCache ? [
|
1783
|
+
...rawPointer,
|
1784
|
+
[ Opcodes.local_set, pointerLocal ],
|
1785
|
+
] : []),
|
1570
1786
|
|
1571
1787
|
...(!lengthI32CacheUsed ? [] : [
|
1572
1788
|
...RTArrayUtil.getLengthI32(getPointer),
|
@@ -1578,13 +1794,22 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1578
1794
|
|
1579
1795
|
// TODO: error better
|
1580
1796
|
default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`)
|
1581
|
-
}, valtypeBinary),
|
1797
|
+
}, allOptUnused && unusedValue ? Blocktype.void : valtypeBinary),
|
1582
1798
|
];
|
1583
1799
|
}
|
1800
|
+
|
1801
|
+
if (Object.keys(protoBC).length > 0) {
|
1802
|
+
return typeSwitch(scope, getNodeType(scope, target), {
|
1803
|
+
...protoBC,
|
1804
|
+
|
1805
|
+
// TODO: error better
|
1806
|
+
default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`)
|
1807
|
+
}, valtypeBinary);
|
1808
|
+
}
|
1584
1809
|
}
|
1585
1810
|
|
1586
1811
|
// TODO: only allows callee as literal
|
1587
|
-
if (!name) return todo(`only literal callees (got ${decl.callee.type})`);
|
1812
|
+
if (!name) return todo(scope, `only literal callees (got ${decl.callee.type})`);
|
1588
1813
|
|
1589
1814
|
let idx = funcIndex[name] ?? importedFuncs[name];
|
1590
1815
|
if (idx === undefined && builtinFuncs[name]) {
|
@@ -1617,16 +1842,68 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1617
1842
|
idx = -1;
|
1618
1843
|
}
|
1619
1844
|
|
1845
|
+
if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
|
1846
|
+
const wasmOps = {
|
1847
|
+
// pointer, align, offset
|
1848
|
+
i32_load: { imms: 2, args: 1, returns: 1 },
|
1849
|
+
// pointer, value, align, offset
|
1850
|
+
i32_store: { imms: 2, args: 2, returns: 0 },
|
1851
|
+
// pointer, align, offset
|
1852
|
+
i32_load8_u: { imms: 2, args: 1, returns: 1 },
|
1853
|
+
// pointer, value, align, offset
|
1854
|
+
i32_store8: { imms: 2, args: 2, returns: 0 },
|
1855
|
+
// pointer, align, offset
|
1856
|
+
i32_load16_u: { imms: 2, args: 1, returns: 1 },
|
1857
|
+
// pointer, value, align, offset
|
1858
|
+
i32_store16: { imms: 2, args: 2, returns: 0 },
|
1859
|
+
|
1860
|
+
// pointer, align, offset
|
1861
|
+
f64_load: { imms: 2, args: 1, returns: 1 },
|
1862
|
+
// pointer, value, align, offset
|
1863
|
+
f64_store: { imms: 2, args: 2, returns: 0 },
|
1864
|
+
|
1865
|
+
// value
|
1866
|
+
i32_const: { imms: 1, args: 0, returns: 1 },
|
1867
|
+
|
1868
|
+
// a, b
|
1869
|
+
i32_or: { imms: 0, args: 2, returns: 1 },
|
1870
|
+
|
1871
|
+
add: { imms: 0, args: 2, returns: 1 },
|
1872
|
+
i32_to_u: { imms: 0, args: 1, returns: 1 }
|
1873
|
+
};
|
1874
|
+
|
1875
|
+
const opName = name.slice('__Porffor_wasm_'.length);
|
1876
|
+
|
1877
|
+
if (wasmOps[opName]) {
|
1878
|
+
const op = wasmOps[opName];
|
1879
|
+
|
1880
|
+
const argOut = [];
|
1881
|
+
for (let i = 0; i < op.args; i++) argOut.push(
|
1882
|
+
...generate(scope, decl.arguments[i]),
|
1883
|
+
Opcodes.i32_to
|
1884
|
+
);
|
1885
|
+
|
1886
|
+
// literals only
|
1887
|
+
const imms = decl.arguments.slice(op.args).map(x => x.value);
|
1888
|
+
|
1889
|
+
return [
|
1890
|
+
...argOut,
|
1891
|
+
[ Opcodes[opName], ...imms ],
|
1892
|
+
...(new Array(op.returns).fill(Opcodes.i32_from))
|
1893
|
+
];
|
1894
|
+
}
|
1895
|
+
}
|
1896
|
+
|
1620
1897
|
if (idx === undefined) {
|
1621
|
-
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function
|
1622
|
-
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined
|
1898
|
+
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
|
1899
|
+
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
1623
1900
|
}
|
1624
1901
|
|
1625
1902
|
const func = funcs.find(x => x.index === idx);
|
1626
1903
|
|
1627
1904
|
const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
|
1628
1905
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1629
|
-
const
|
1906
|
+
const typedReturns = (func ? func.returnType == null : userFunc) || builtinFuncs[name]?.typedReturns;
|
1630
1907
|
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1631
1908
|
|
1632
1909
|
let args = decl.arguments;
|
@@ -1643,14 +1920,24 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1643
1920
|
if (func && func.throws) scope.throws = true;
|
1644
1921
|
|
1645
1922
|
let out = [];
|
1646
|
-
for (
|
1923
|
+
for (let i = 0; i < args.length; i++) {
|
1924
|
+
const arg = args[i];
|
1647
1925
|
out = out.concat(generate(scope, arg));
|
1926
|
+
|
1927
|
+
if (builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1928
|
+
out.push(Opcodes.i32_to);
|
1929
|
+
}
|
1930
|
+
|
1931
|
+
if (importedFuncs[name] && name.startsWith('profile')) {
|
1932
|
+
out.push(Opcodes.i32_to);
|
1933
|
+
}
|
1934
|
+
|
1648
1935
|
if (typedParams) out = out.concat(getNodeType(scope, arg));
|
1649
1936
|
}
|
1650
1937
|
|
1651
1938
|
out.push([ Opcodes.call, idx ]);
|
1652
1939
|
|
1653
|
-
if (!
|
1940
|
+
if (!typedReturns) {
|
1654
1941
|
// let type;
|
1655
1942
|
// if (builtinFuncs[name]) type = TYPES[builtinFuncs[name].returnType ?? 'number'];
|
1656
1943
|
// if (internalConstrs[name]) type = internalConstrs[name].type;
|
@@ -1660,7 +1947,11 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1660
1947
|
// ...number(type, Valtype.i32),
|
1661
1948
|
// [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
|
1662
1949
|
// );
|
1663
|
-
} else out.push(setLastType(scope));
|
1950
|
+
} else out.push(...setLastType(scope));
|
1951
|
+
|
1952
|
+
if (builtinFuncs[name] && builtinFuncs[name].returns?.[0] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1953
|
+
out.push(Opcodes.i32_from);
|
1954
|
+
}
|
1664
1955
|
|
1665
1956
|
return out;
|
1666
1957
|
};
|
@@ -1668,8 +1959,21 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1668
1959
|
const generateNew = (scope, decl, _global, _name) => {
|
1669
1960
|
// hack: basically treat this as a normal call for builtins for now
|
1670
1961
|
const name = mapName(decl.callee.name);
|
1962
|
+
|
1671
1963
|
if (internalConstrs[name] && !internalConstrs[name].notConstr) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1672
|
-
|
1964
|
+
|
1965
|
+
if (builtinFuncs[name + '$constructor']) {
|
1966
|
+
// custom ...$constructor override builtin func
|
1967
|
+
return generateCall(scope, {
|
1968
|
+
...decl,
|
1969
|
+
callee: {
|
1970
|
+
type: 'Identifier',
|
1971
|
+
name: name + '$constructor'
|
1972
|
+
}
|
1973
|
+
}, _global, _name);
|
1974
|
+
}
|
1975
|
+
|
1976
|
+
if (!builtinFuncs[name]) return todo(scope, `new statement is not supported yet`); // return todo(scope, `new statement is not supported yet (new ${unhackName(name)})`);
|
1673
1977
|
|
1674
1978
|
return generateCall(scope, decl, _global, _name);
|
1675
1979
|
};
|
@@ -1786,12 +2090,14 @@ const brTable = (input, bc, returns) => {
|
|
1786
2090
|
};
|
1787
2091
|
|
1788
2092
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
2093
|
+
if (!Prefs.bytestring) delete bc[TYPES._bytestring];
|
2094
|
+
|
1789
2095
|
const known = knownType(scope, type);
|
1790
2096
|
if (known != null) {
|
1791
2097
|
return bc[known] ?? bc.default;
|
1792
2098
|
}
|
1793
2099
|
|
1794
|
-
if (
|
2100
|
+
if (Prefs.typeswitchUseBrtable)
|
1795
2101
|
return brTable(type, bc, returns);
|
1796
2102
|
|
1797
2103
|
const tmp = localTmp(scope, '#typeswitch_tmp', Valtype.i32);
|
@@ -1801,8 +2107,6 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
1801
2107
|
[ Opcodes.block, returns ]
|
1802
2108
|
];
|
1803
2109
|
|
1804
|
-
// todo: use br_table?
|
1805
|
-
|
1806
2110
|
for (const x in bc) {
|
1807
2111
|
if (x === 'default') continue;
|
1808
2112
|
|
@@ -1826,7 +2130,7 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
1826
2130
|
return out;
|
1827
2131
|
};
|
1828
2132
|
|
1829
|
-
const allocVar = (scope, name, global = false) => {
|
2133
|
+
const allocVar = (scope, name, global = false, type = true) => {
|
1830
2134
|
const target = global ? globals : scope.locals;
|
1831
2135
|
|
1832
2136
|
// already declared
|
@@ -1840,8 +2144,10 @@ const allocVar = (scope, name, global = false) => {
|
|
1840
2144
|
let idx = global ? globalInd++ : scope.localInd++;
|
1841
2145
|
target[name] = { idx, type: valtypeBinary };
|
1842
2146
|
|
1843
|
-
|
1844
|
-
|
2147
|
+
if (type) {
|
2148
|
+
let typeIdx = global ? globalInd++ : scope.localInd++;
|
2149
|
+
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
|
2150
|
+
}
|
1845
2151
|
|
1846
2152
|
return idx;
|
1847
2153
|
};
|
@@ -1856,11 +2162,14 @@ const addVarMetadata = (scope, name, global = false, metadata = {}) => {
|
|
1856
2162
|
};
|
1857
2163
|
|
1858
2164
|
const typeAnnoToPorfType = x => {
|
1859
|
-
if (
|
1860
|
-
if (TYPES[
|
2165
|
+
if (!x) return null;
|
2166
|
+
if (TYPES[x.toLowerCase()] != null) return TYPES[x.toLowerCase()];
|
2167
|
+
if (TYPES['_' + x.toLowerCase()] != null) return TYPES['_' + x.toLowerCase()];
|
1861
2168
|
|
1862
2169
|
switch (x) {
|
1863
2170
|
case 'i32':
|
2171
|
+
case 'i64':
|
2172
|
+
case 'f64':
|
1864
2173
|
return TYPES.number;
|
1865
2174
|
}
|
1866
2175
|
|
@@ -1871,7 +2180,7 @@ const extractTypeAnnotation = decl => {
|
|
1871
2180
|
let a = decl;
|
1872
2181
|
while (a.typeAnnotation) a = a.typeAnnotation;
|
1873
2182
|
|
1874
|
-
let type, elementType;
|
2183
|
+
let type = null, elementType = null;
|
1875
2184
|
if (a.typeName) {
|
1876
2185
|
type = a.typeName.name;
|
1877
2186
|
} else if (a.type.endsWith('Keyword')) {
|
@@ -1884,6 +2193,8 @@ const extractTypeAnnotation = decl => {
|
|
1884
2193
|
const typeName = type;
|
1885
2194
|
type = typeAnnoToPorfType(type);
|
1886
2195
|
|
2196
|
+
if (type === TYPES._bytestring && !Prefs.bytestring) type = TYPES.string;
|
2197
|
+
|
1887
2198
|
// if (decl.name) console.log(decl.name, { type, elementType });
|
1888
2199
|
|
1889
2200
|
return { type, typeName, elementType };
|
@@ -1900,6 +2211,8 @@ const generateVar = (scope, decl) => {
|
|
1900
2211
|
for (const x of decl.declarations) {
|
1901
2212
|
const name = mapName(x.id.name);
|
1902
2213
|
|
2214
|
+
if (!name) return todo(scope, 'destructuring is not supported yet');
|
2215
|
+
|
1903
2216
|
if (x.init && isFuncType(x.init.type)) {
|
1904
2217
|
// hack for let a = function () { ... }
|
1905
2218
|
x.init.id = { name };
|
@@ -1915,9 +2228,10 @@ const generateVar = (scope, decl) => {
|
|
1915
2228
|
continue; // always ignore
|
1916
2229
|
}
|
1917
2230
|
|
1918
|
-
|
2231
|
+
const typed = typedInput && x.id.typeAnnotation;
|
2232
|
+
let idx = allocVar(scope, name, global, !typed);
|
1919
2233
|
|
1920
|
-
if (
|
2234
|
+
if (typed) {
|
1921
2235
|
addVarMetadata(scope, name, global, extractTypeAnnotation(x.id));
|
1922
2236
|
}
|
1923
2237
|
|
@@ -1935,7 +2249,8 @@ const generateVar = (scope, decl) => {
|
|
1935
2249
|
return out;
|
1936
2250
|
};
|
1937
2251
|
|
1938
|
-
|
2252
|
+
// todo: optimize this func for valueUnused
|
2253
|
+
const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
1939
2254
|
const { type, name } = decl.left;
|
1940
2255
|
|
1941
2256
|
if (type === 'ObjectPattern') {
|
@@ -1953,9 +2268,9 @@ const generateAssign = (scope, decl) => {
|
|
1953
2268
|
// hack: .length setter
|
1954
2269
|
if (decl.left.type === 'MemberExpression' && decl.left.property.name === 'length') {
|
1955
2270
|
const name = decl.left.object.name;
|
1956
|
-
const pointer = arrays
|
2271
|
+
const pointer = scope.arrays?.get(name);
|
1957
2272
|
|
1958
|
-
const aotPointer = pointer != null;
|
2273
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
1959
2274
|
|
1960
2275
|
const newValueTmp = localTmp(scope, '__length_setter_tmp');
|
1961
2276
|
|
@@ -1980,9 +2295,9 @@ const generateAssign = (scope, decl) => {
|
|
1980
2295
|
// arr[i]
|
1981
2296
|
if (decl.left.type === 'MemberExpression' && decl.left.computed) {
|
1982
2297
|
const name = decl.left.object.name;
|
1983
|
-
const pointer = arrays
|
2298
|
+
const pointer = scope.arrays?.get(name);
|
1984
2299
|
|
1985
|
-
const aotPointer = pointer != null;
|
2300
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
1986
2301
|
|
1987
2302
|
const newValueTmp = localTmp(scope, '__member_setter_val_tmp');
|
1988
2303
|
const pointerTmp = op === '=' ? -1 : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
|
@@ -2038,6 +2353,8 @@ const generateAssign = (scope, decl) => {
|
|
2038
2353
|
];
|
2039
2354
|
}
|
2040
2355
|
|
2356
|
+
if (!name) return todo(scope, 'destructuring is not supported yet', true);
|
2357
|
+
|
2041
2358
|
const [ local, isGlobal ] = lookupName(scope, name);
|
2042
2359
|
|
2043
2360
|
if (local === undefined) {
|
@@ -2084,9 +2401,7 @@ const generateAssign = (scope, decl) => {
|
|
2084
2401
|
], getType(scope, name), getNodeType(scope, decl.right), isGlobal, name, true),
|
2085
2402
|
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
2086
2403
|
|
2087
|
-
getLastType(scope)
|
2088
|
-
// hack: type is idx+1
|
2089
|
-
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx + 1 ],
|
2404
|
+
...setType(scope, name, getLastType(scope))
|
2090
2405
|
];
|
2091
2406
|
}
|
2092
2407
|
|
@@ -2097,9 +2412,7 @@ const generateAssign = (scope, decl) => {
|
|
2097
2412
|
|
2098
2413
|
// todo: string concat types
|
2099
2414
|
|
2100
|
-
|
2101
|
-
...number(TYPES.number, Valtype.i32),
|
2102
|
-
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx + 1 ],
|
2415
|
+
...setType(scope, name, TYPES.number)
|
2103
2416
|
];
|
2104
2417
|
};
|
2105
2418
|
|
@@ -2145,7 +2458,7 @@ const generateUnary = (scope, decl) => {
|
|
2145
2458
|
return out;
|
2146
2459
|
}
|
2147
2460
|
|
2148
|
-
case 'delete':
|
2461
|
+
case 'delete': {
|
2149
2462
|
let toReturn = true, toGenerate = true;
|
2150
2463
|
|
2151
2464
|
if (decl.argument.type === 'Identifier') {
|
@@ -2167,38 +2480,60 @@ const generateUnary = (scope, decl) => {
|
|
2167
2480
|
|
2168
2481
|
out.push(...number(toReturn ? 1 : 0));
|
2169
2482
|
return out;
|
2483
|
+
}
|
2170
2484
|
|
2171
|
-
case 'typeof':
|
2172
|
-
|
2485
|
+
case 'typeof': {
|
2486
|
+
let overrideType, toGenerate = true;
|
2487
|
+
|
2488
|
+
if (decl.argument.type === 'Identifier') {
|
2489
|
+
const out = generateIdent(scope, decl.argument);
|
2490
|
+
|
2491
|
+
// if ReferenceError (undeclared var), ignore and return undefined
|
2492
|
+
if (out[1]) {
|
2493
|
+
// does not exist (2 ops from throw)
|
2494
|
+
overrideType = number(TYPES.undefined, Valtype.i32);
|
2495
|
+
toGenerate = false;
|
2496
|
+
}
|
2497
|
+
}
|
2498
|
+
|
2499
|
+
const out = toGenerate ? generate(scope, decl.argument) : [];
|
2500
|
+
disposeLeftover(out);
|
2501
|
+
|
2502
|
+
out.push(...typeSwitch(scope, overrideType ?? getNodeType(scope, decl.argument), {
|
2173
2503
|
[TYPES.number]: makeString(scope, 'number', false, '#typeof_result'),
|
2174
2504
|
[TYPES.boolean]: makeString(scope, 'boolean', false, '#typeof_result'),
|
2175
2505
|
[TYPES.string]: makeString(scope, 'string', false, '#typeof_result'),
|
2176
2506
|
[TYPES.undefined]: makeString(scope, 'undefined', false, '#typeof_result'),
|
2177
2507
|
[TYPES.function]: makeString(scope, 'function', false, '#typeof_result'),
|
2178
2508
|
|
2509
|
+
[TYPES._bytestring]: makeString(scope, 'string', false, '#typeof_result'),
|
2510
|
+
|
2179
2511
|
// object and internal types
|
2180
2512
|
default: makeString(scope, 'object', false, '#typeof_result'),
|
2181
|
-
});
|
2513
|
+
}));
|
2514
|
+
|
2515
|
+
return out;
|
2516
|
+
}
|
2182
2517
|
|
2183
2518
|
default:
|
2184
|
-
return todo(`unary operator ${decl.operator} not implemented yet
|
2519
|
+
return todo(scope, `unary operator ${decl.operator} not implemented yet`, true);
|
2185
2520
|
}
|
2186
2521
|
};
|
2187
2522
|
|
2188
|
-
const generateUpdate = (scope, decl) => {
|
2523
|
+
const generateUpdate = (scope, decl, _global, _name, valueUnused = false) => {
|
2189
2524
|
const { name } = decl.argument;
|
2190
2525
|
|
2191
2526
|
const [ local, isGlobal ] = lookupName(scope, name);
|
2192
2527
|
|
2193
2528
|
if (local === undefined) {
|
2194
|
-
return todo(`update expression with undefined variable
|
2529
|
+
return todo(scope, `update expression with undefined variable`, true);
|
2195
2530
|
}
|
2196
2531
|
|
2197
2532
|
const idx = local.idx;
|
2198
2533
|
const out = [];
|
2199
2534
|
|
2200
2535
|
out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2201
|
-
if (!decl.prefix) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2536
|
+
if (!decl.prefix && !valueUnused) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2202
2537
|
|
2203
2538
|
switch (decl.operator) {
|
2204
2539
|
case '++':
|
@@ -2211,7 +2546,7 @@ const generateUpdate = (scope, decl) => {
|
|
2211
2546
|
}
|
2212
2547
|
|
2213
2548
|
out.push([ isGlobal ? Opcodes.global_set : Opcodes.local_set, idx ]);
|
2214
|
-
if (decl.prefix) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2549
|
+
if (decl.prefix && !valueUnused) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2215
2550
|
|
2216
2551
|
return out;
|
2217
2552
|
};
|
@@ -2251,7 +2586,7 @@ const generateConditional = (scope, decl) => {
|
|
2251
2586
|
// note type
|
2252
2587
|
out.push(
|
2253
2588
|
...getNodeType(scope, decl.consequent),
|
2254
|
-
setLastType(scope)
|
2589
|
+
...setLastType(scope)
|
2255
2590
|
);
|
2256
2591
|
|
2257
2592
|
out.push([ Opcodes.else ]);
|
@@ -2260,7 +2595,7 @@ const generateConditional = (scope, decl) => {
|
|
2260
2595
|
// note type
|
2261
2596
|
out.push(
|
2262
2597
|
...getNodeType(scope, decl.alternate),
|
2263
|
-
setLastType(scope)
|
2598
|
+
...setLastType(scope)
|
2264
2599
|
);
|
2265
2600
|
|
2266
2601
|
out.push([ Opcodes.end ]);
|
@@ -2274,15 +2609,17 @@ const generateFor = (scope, decl) => {
|
|
2274
2609
|
const out = [];
|
2275
2610
|
|
2276
2611
|
if (decl.init) {
|
2277
|
-
out.push(...generate(scope, decl.init));
|
2612
|
+
out.push(...generate(scope, decl.init, false, undefined, true));
|
2278
2613
|
disposeLeftover(out);
|
2279
2614
|
}
|
2280
2615
|
|
2281
2616
|
out.push([ Opcodes.loop, Blocktype.void ]);
|
2282
2617
|
depth.push('for');
|
2283
2618
|
|
2284
|
-
out.push(...generate(scope, decl.test));
|
2285
|
-
|
2619
|
+
if (decl.test) out.push(...generate(scope, decl.test), Opcodes.i32_to);
|
2620
|
+
else out.push(...number(1, Valtype.i32));
|
2621
|
+
|
2622
|
+
out.push([ Opcodes.if, Blocktype.void ]);
|
2286
2623
|
depth.push('if');
|
2287
2624
|
|
2288
2625
|
out.push([ Opcodes.block, Blocktype.void ]);
|
@@ -2290,8 +2627,7 @@ const generateFor = (scope, decl) => {
|
|
2290
2627
|
out.push(...generate(scope, decl.body));
|
2291
2628
|
out.push([ Opcodes.end ]);
|
2292
2629
|
|
2293
|
-
out.push(...generate(scope, decl.update));
|
2294
|
-
depth.pop();
|
2630
|
+
if (decl.update) out.push(...generate(scope, decl.update, false, undefined, true));
|
2295
2631
|
|
2296
2632
|
out.push([ Opcodes.br, 1 ]);
|
2297
2633
|
out.push([ Opcodes.end ], [ Opcodes.end ]);
|
@@ -2319,6 +2655,36 @@ const generateWhile = (scope, decl) => {
|
|
2319
2655
|
return out;
|
2320
2656
|
};
|
2321
2657
|
|
2658
|
+
const generateDoWhile = (scope, decl) => {
|
2659
|
+
const out = [];
|
2660
|
+
|
2661
|
+
out.push([ Opcodes.loop, Blocktype.void ]);
|
2662
|
+
depth.push('dowhile');
|
2663
|
+
|
2664
|
+
// block for break (includes all)
|
2665
|
+
out.push([ Opcodes.block, Blocktype.void ]);
|
2666
|
+
depth.push('block');
|
2667
|
+
|
2668
|
+
// block for continue
|
2669
|
+
// includes body but not test+loop so we can exit body at anytime
|
2670
|
+
// and still test+loop after
|
2671
|
+
out.push([ Opcodes.block, Blocktype.void ]);
|
2672
|
+
depth.push('block');
|
2673
|
+
|
2674
|
+
out.push(...generate(scope, decl.body));
|
2675
|
+
|
2676
|
+
out.push([ Opcodes.end ]);
|
2677
|
+
depth.pop();
|
2678
|
+
|
2679
|
+
out.push(...generate(scope, decl.test), Opcodes.i32_to);
|
2680
|
+
out.push([ Opcodes.br_if, 1 ]);
|
2681
|
+
|
2682
|
+
out.push([ Opcodes.end ], [ Opcodes.end ]);
|
2683
|
+
depth.pop(); depth.pop();
|
2684
|
+
|
2685
|
+
return out;
|
2686
|
+
};
|
2687
|
+
|
2322
2688
|
const generateForOf = (scope, decl) => {
|
2323
2689
|
const out = [];
|
2324
2690
|
|
@@ -2348,8 +2714,17 @@ const generateForOf = (scope, decl) => {
|
|
2348
2714
|
// setup local for left
|
2349
2715
|
generate(scope, decl.left);
|
2350
2716
|
|
2351
|
-
|
2717
|
+
let leftName = decl.left.declarations?.[0]?.id?.name;
|
2718
|
+
if (!leftName && decl.left.name) {
|
2719
|
+
leftName = decl.left.name;
|
2720
|
+
|
2721
|
+
generateVar(scope, { kind: 'var', _bare: true, declarations: [ { id: { name: leftName } } ] })
|
2722
|
+
}
|
2723
|
+
|
2724
|
+
// if (!leftName) console.log(decl.left?.declarations?.[0]?.id ?? decl.left);
|
2725
|
+
|
2352
2726
|
const [ local, isGlobal ] = lookupName(scope, leftName);
|
2727
|
+
if (!local) return todo(scope, 'for of failed to get left local (probably destructure)');
|
2353
2728
|
|
2354
2729
|
depth.push('block');
|
2355
2730
|
depth.push('block');
|
@@ -2357,13 +2732,15 @@ const generateForOf = (scope, decl) => {
|
|
2357
2732
|
// // todo: we should only do this for strings but we don't know at compile-time :(
|
2358
2733
|
// hack: this is naughty and will break things!
|
2359
2734
|
let newOut = number(0, Valtype.f64), newPointer = -1;
|
2360
|
-
if (pages.
|
2735
|
+
if (pages.hasAnyString) {
|
2736
|
+
// todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
|
2361
2737
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2362
2738
|
rawElements: new Array(1)
|
2363
2739
|
}, isGlobal, leftName, true, 'i16');
|
2364
2740
|
}
|
2365
2741
|
|
2366
2742
|
// set type for local
|
2743
|
+
// todo: optimize away counter and use end pointer
|
2367
2744
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2368
2745
|
[TYPES._array]: [
|
2369
2746
|
...setType(scope, leftName, TYPES.number),
|
@@ -2448,6 +2825,56 @@ const generateForOf = (scope, decl) => {
|
|
2448
2825
|
[ Opcodes.end ],
|
2449
2826
|
[ Opcodes.end ]
|
2450
2827
|
],
|
2828
|
+
[TYPES._bytestring]: [
|
2829
|
+
...setType(scope, leftName, TYPES._bytestring),
|
2830
|
+
|
2831
|
+
[ Opcodes.loop, Blocktype.void ],
|
2832
|
+
|
2833
|
+
// setup new/out array
|
2834
|
+
...newOut,
|
2835
|
+
[ Opcodes.drop ],
|
2836
|
+
|
2837
|
+
...number(0, Valtype.i32), // base 0 for store after
|
2838
|
+
|
2839
|
+
// load current string ind {arg}
|
2840
|
+
[ Opcodes.local_get, pointer ],
|
2841
|
+
[ Opcodes.local_get, counter ],
|
2842
|
+
[ Opcodes.i32_add ],
|
2843
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
2844
|
+
|
2845
|
+
// store to new string ind 0
|
2846
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
2847
|
+
|
2848
|
+
// return new string (page)
|
2849
|
+
...number(newPointer),
|
2850
|
+
|
2851
|
+
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2852
|
+
|
2853
|
+
[ Opcodes.block, Blocktype.void ],
|
2854
|
+
[ Opcodes.block, Blocktype.void ],
|
2855
|
+
...generate(scope, decl.body),
|
2856
|
+
[ Opcodes.end ],
|
2857
|
+
|
2858
|
+
// increment iter pointer
|
2859
|
+
// [ Opcodes.local_get, pointer ],
|
2860
|
+
// ...number(1, Valtype.i32),
|
2861
|
+
// [ Opcodes.i32_add ],
|
2862
|
+
// [ Opcodes.local_set, pointer ],
|
2863
|
+
|
2864
|
+
// increment counter by 1
|
2865
|
+
[ Opcodes.local_get, counter ],
|
2866
|
+
...number(1, Valtype.i32),
|
2867
|
+
[ Opcodes.i32_add ],
|
2868
|
+
[ Opcodes.local_tee, counter ],
|
2869
|
+
|
2870
|
+
// loop if counter != length
|
2871
|
+
[ Opcodes.local_get, length ],
|
2872
|
+
[ Opcodes.i32_ne ],
|
2873
|
+
[ Opcodes.br_if, 1 ],
|
2874
|
+
|
2875
|
+
[ Opcodes.end ],
|
2876
|
+
[ Opcodes.end ]
|
2877
|
+
],
|
2451
2878
|
default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
|
2452
2879
|
}, Blocktype.void));
|
2453
2880
|
|
@@ -2458,28 +2885,65 @@ const generateForOf = (scope, decl) => {
|
|
2458
2885
|
return out;
|
2459
2886
|
};
|
2460
2887
|
|
2888
|
+
// find the nearest loop in depth map by type
|
2461
2889
|
const getNearestLoop = () => {
|
2462
2890
|
for (let i = depth.length - 1; i >= 0; i--) {
|
2463
|
-
if (
|
2891
|
+
if (['while', 'dowhile', 'for', 'forof'].includes(depth[i])) return i;
|
2464
2892
|
}
|
2465
2893
|
|
2466
2894
|
return -1;
|
2467
2895
|
};
|
2468
2896
|
|
2469
2897
|
const generateBreak = (scope, decl) => {
|
2470
|
-
const
|
2898
|
+
const target = decl.label ? scope.labels.get(decl.label.name) : getNearestLoop();
|
2899
|
+
const type = depth[target];
|
2900
|
+
|
2901
|
+
// different loop types have different branch offsets
|
2902
|
+
// as they have different wasm block/loop/if structures
|
2903
|
+
// we need to use the right offset by type to branch to the one we want
|
2904
|
+
// for a break: exit the loop without executing anything else inside it
|
2905
|
+
const offset = ({
|
2906
|
+
for: 2, // loop > if (wanted branch) > block (we are here)
|
2907
|
+
while: 2, // loop > if (wanted branch) (we are here)
|
2908
|
+
dowhile: 2, // loop > block (wanted branch) > block (we are here)
|
2909
|
+
forof: 2, // loop > block (wanted branch) > block (we are here)
|
2910
|
+
if: 1 // break inside if, branch 0 to skip the rest of the if
|
2911
|
+
})[type];
|
2912
|
+
|
2471
2913
|
return [
|
2472
|
-
[ Opcodes.br, ...signedLEB128(
|
2914
|
+
[ Opcodes.br, ...signedLEB128(depth.length - target - offset) ]
|
2473
2915
|
];
|
2474
2916
|
};
|
2475
2917
|
|
2476
2918
|
const generateContinue = (scope, decl) => {
|
2477
|
-
const
|
2919
|
+
const target = decl.label ? scope.labels.get(decl.label.name) : getNearestLoop();
|
2920
|
+
const type = depth[target];
|
2921
|
+
|
2922
|
+
// different loop types have different branch offsets
|
2923
|
+
// as they have different wasm block/loop/if structures
|
2924
|
+
// we need to use the right offset by type to branch to the one we want
|
2925
|
+
// for a continue: do test for the loop, and then loop depending on that success
|
2926
|
+
const offset = ({
|
2927
|
+
for: 3, // loop (wanted branch) > if > block (we are here)
|
2928
|
+
while: 1, // loop (wanted branch) > if (we are here)
|
2929
|
+
dowhile: 3, // loop > block > block (wanted branch) (we are here)
|
2930
|
+
forof: 3 // loop > block > block (wanted branch) (we are here)
|
2931
|
+
})[type];
|
2932
|
+
|
2478
2933
|
return [
|
2479
|
-
[ Opcodes.br, ...signedLEB128(
|
2934
|
+
[ Opcodes.br, ...signedLEB128(depth.length - target - offset) ]
|
2480
2935
|
];
|
2481
2936
|
};
|
2482
2937
|
|
2938
|
+
const generateLabel = (scope, decl) => {
|
2939
|
+
scope.labels ??= new Map();
|
2940
|
+
|
2941
|
+
const name = decl.label.name;
|
2942
|
+
scope.labels.set(name, depth.length);
|
2943
|
+
|
2944
|
+
return generate(scope, decl.body);
|
2945
|
+
};
|
2946
|
+
|
2483
2947
|
const generateThrow = (scope, decl) => {
|
2484
2948
|
scope.throws = true;
|
2485
2949
|
|
@@ -2488,7 +2952,7 @@ const generateThrow = (scope, decl) => {
|
|
2488
2952
|
// hack: throw new X("...") -> throw "..."
|
2489
2953
|
if (!message && (decl.argument.type === 'NewExpression' || decl.argument.type === 'CallExpression')) {
|
2490
2954
|
constructor = decl.argument.callee.name;
|
2491
|
-
message = decl.argument.arguments[0]
|
2955
|
+
message = decl.argument.arguments[0]?.value ?? '';
|
2492
2956
|
}
|
2493
2957
|
|
2494
2958
|
if (tags.length === 0) tags.push({
|
@@ -2500,6 +2964,9 @@ const generateThrow = (scope, decl) => {
|
|
2500
2964
|
let exceptId = exceptions.push({ constructor, message }) - 1;
|
2501
2965
|
let tagIdx = tags[0].idx;
|
2502
2966
|
|
2967
|
+
scope.exceptions ??= [];
|
2968
|
+
scope.exceptions.push(exceptId);
|
2969
|
+
|
2503
2970
|
// todo: write a description of how this works lol
|
2504
2971
|
|
2505
2972
|
return [
|
@@ -2509,7 +2976,7 @@ const generateThrow = (scope, decl) => {
|
|
2509
2976
|
};
|
2510
2977
|
|
2511
2978
|
const generateTry = (scope, decl) => {
|
2512
|
-
if (decl.finalizer) return todo('try finally not implemented yet');
|
2979
|
+
if (decl.finalizer) return todo(scope, 'try finally not implemented yet');
|
2513
2980
|
|
2514
2981
|
const out = [];
|
2515
2982
|
|
@@ -2540,29 +3007,35 @@ const generateAssignPat = (scope, decl) => {
|
|
2540
3007
|
// TODO
|
2541
3008
|
// if identifier declared, use that
|
2542
3009
|
// else, use default (right)
|
2543
|
-
return todo('assignment pattern (optional arg)');
|
3010
|
+
return todo(scope, 'assignment pattern (optional arg)');
|
2544
3011
|
};
|
2545
3012
|
|
2546
3013
|
let pages = new Map();
|
2547
|
-
const allocPage = (reason, type) => {
|
3014
|
+
const allocPage = (scope, reason, type) => {
|
2548
3015
|
if (pages.has(reason)) return pages.get(reason).ind;
|
2549
3016
|
|
2550
3017
|
if (reason.startsWith('array:')) pages.hasArray = true;
|
2551
3018
|
if (reason.startsWith('string:')) pages.hasString = true;
|
3019
|
+
if (reason.startsWith('bytestring:')) pages.hasByteString = true;
|
3020
|
+
if (reason.includes('string:')) pages.hasAnyString = true;
|
2552
3021
|
|
2553
3022
|
const ind = pages.size;
|
2554
3023
|
pages.set(reason, { ind, type });
|
2555
3024
|
|
2556
|
-
|
3025
|
+
scope.pages ??= new Map();
|
3026
|
+
scope.pages.set(reason, { ind, type });
|
3027
|
+
|
3028
|
+
if (Prefs.allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
|
2557
3029
|
|
2558
3030
|
return ind;
|
2559
3031
|
};
|
2560
3032
|
|
3033
|
+
// todo: add scope.pages
|
2561
3034
|
const freePage = reason => {
|
2562
3035
|
const { ind } = pages.get(reason);
|
2563
3036
|
pages.delete(reason);
|
2564
3037
|
|
2565
|
-
if (allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
3038
|
+
if (Prefs.allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
2566
3039
|
|
2567
3040
|
return ind;
|
2568
3041
|
};
|
@@ -2582,38 +3055,51 @@ const StoreOps = {
|
|
2582
3055
|
f64: Opcodes.f64_store,
|
2583
3056
|
|
2584
3057
|
// expects i32 input!
|
2585
|
-
|
3058
|
+
i8: Opcodes.i32_store8,
|
3059
|
+
i16: Opcodes.i32_store16,
|
2586
3060
|
};
|
2587
3061
|
|
2588
3062
|
let data = [];
|
2589
3063
|
|
2590
|
-
const compileBytes = (val, itemType
|
3064
|
+
const compileBytes = (val, itemType) => {
|
2591
3065
|
// todo: this is a mess and needs confirming / ????
|
2592
3066
|
switch (itemType) {
|
2593
3067
|
case 'i8': return [ val % 256 ];
|
2594
|
-
case 'i16': return [ val % 256,
|
2595
|
-
|
2596
|
-
case 'i32':
|
2597
|
-
|
2598
|
-
return enforceFourBytes(signedLEB128(val));
|
3068
|
+
case 'i16': return [ val % 256, (val / 256 | 0) % 256 ];
|
3069
|
+
case 'i16': return [ val % 256, (val / 256 | 0) % 256 ];
|
3070
|
+
case 'i32': return [...new Uint8Array(new Int32Array([ val ]).buffer)];
|
3071
|
+
// todo: i64
|
2599
3072
|
|
2600
3073
|
case 'f64': return ieee754_binary64(val);
|
2601
3074
|
}
|
2602
3075
|
};
|
2603
3076
|
|
3077
|
+
const getAllocType = itemType => {
|
3078
|
+
switch (itemType) {
|
3079
|
+
case 'i8': return 'bytestring';
|
3080
|
+
case 'i16': return 'string';
|
3081
|
+
|
3082
|
+
default: return 'array';
|
3083
|
+
}
|
3084
|
+
};
|
3085
|
+
|
2604
3086
|
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
2605
3087
|
const out = [];
|
2606
3088
|
|
3089
|
+
scope.arrays ??= new Map();
|
3090
|
+
|
2607
3091
|
let firstAssign = false;
|
2608
|
-
if (!arrays.has(name) || name === '$undeclared') {
|
3092
|
+
if (!scope.arrays.has(name) || name === '$undeclared') {
|
2609
3093
|
firstAssign = true;
|
2610
3094
|
|
2611
3095
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
2612
3096
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
2613
|
-
|
3097
|
+
|
3098
|
+
if (Prefs.scopedPageNames) scope.arrays.set(name, allocPage(scope, `${scope.name} | ${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
|
3099
|
+
else scope.arrays.set(name, allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
|
2614
3100
|
}
|
2615
3101
|
|
2616
|
-
const pointer = arrays.get(name);
|
3102
|
+
const pointer = scope.arrays.get(name);
|
2617
3103
|
|
2618
3104
|
const useRawElements = !!decl.rawElements;
|
2619
3105
|
const elements = useRawElements ? decl.rawElements : decl.elements;
|
@@ -2621,19 +3107,25 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2621
3107
|
const valtype = itemTypeToValtype[itemType];
|
2622
3108
|
const length = elements.length;
|
2623
3109
|
|
2624
|
-
if (firstAssign && useRawElements) {
|
2625
|
-
|
3110
|
+
if (firstAssign && useRawElements && !Prefs.noData) {
|
3111
|
+
// if length is 0 memory/data will just be 0000... anyway
|
3112
|
+
if (length !== 0) {
|
3113
|
+
let bytes = compileBytes(length, 'i32');
|
2626
3114
|
|
2627
|
-
|
2628
|
-
|
3115
|
+
if (!initEmpty) for (let i = 0; i < length; i++) {
|
3116
|
+
if (elements[i] == null) continue;
|
2629
3117
|
|
2630
|
-
|
2631
|
-
|
3118
|
+
bytes.push(...compileBytes(elements[i], itemType));
|
3119
|
+
}
|
2632
3120
|
|
2633
|
-
|
2634
|
-
|
2635
|
-
|
2636
|
-
|
3121
|
+
const ind = data.push({
|
3122
|
+
offset: pointer,
|
3123
|
+
bytes
|
3124
|
+
}) - 1;
|
3125
|
+
|
3126
|
+
scope.data ??= [];
|
3127
|
+
scope.data.push(ind);
|
3128
|
+
}
|
2637
3129
|
|
2638
3130
|
// local value as pointer
|
2639
3131
|
out.push(...number(pointer));
|
@@ -2656,7 +3148,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2656
3148
|
out.push(
|
2657
3149
|
...number(0, Valtype.i32),
|
2658
3150
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
2659
|
-
[ storeOp, Math.log2(ValtypeSize[itemType]) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32 + i * ValtypeSize[itemType]) ]
|
3151
|
+
[ storeOp, (Math.log2(ValtypeSize[itemType]) || 1) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32 + i * ValtypeSize[itemType]) ]
|
2660
3152
|
);
|
2661
3153
|
}
|
2662
3154
|
|
@@ -2666,31 +3158,56 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2666
3158
|
return [ out, pointer ];
|
2667
3159
|
};
|
2668
3160
|
|
2669
|
-
const
|
3161
|
+
const byteStringable = str => {
|
3162
|
+
if (!Prefs.bytestring) return false;
|
3163
|
+
|
3164
|
+
for (let i = 0; i < str.length; i++) {
|
3165
|
+
if (str.charCodeAt(i) > 0xFF) return false;
|
3166
|
+
}
|
3167
|
+
|
3168
|
+
return true;
|
3169
|
+
};
|
3170
|
+
|
3171
|
+
const makeString = (scope, str, global = false, name = '$undeclared', forceBytestring = undefined) => {
|
2670
3172
|
const rawElements = new Array(str.length);
|
3173
|
+
let byteStringable = Prefs.bytestring;
|
2671
3174
|
for (let i = 0; i < str.length; i++) {
|
2672
|
-
|
3175
|
+
const c = str.charCodeAt(i);
|
3176
|
+
rawElements[i] = c;
|
3177
|
+
|
3178
|
+
if (byteStringable && c > 0xFF) byteStringable = false;
|
2673
3179
|
}
|
2674
3180
|
|
3181
|
+
if (byteStringable && forceBytestring === false) byteStringable = false;
|
3182
|
+
|
2675
3183
|
return makeArray(scope, {
|
2676
3184
|
rawElements
|
2677
|
-
}, global, name, false, 'i16')[0];
|
3185
|
+
}, global, name, false, byteStringable ? 'i8' : 'i16')[0];
|
2678
3186
|
};
|
2679
3187
|
|
2680
|
-
let arrays = new Map();
|
2681
3188
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
2682
3189
|
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
2683
3190
|
};
|
2684
3191
|
|
2685
3192
|
export const generateMember = (scope, decl, _global, _name) => {
|
2686
3193
|
const name = decl.object.name;
|
2687
|
-
const pointer = arrays
|
3194
|
+
const pointer = scope.arrays?.get(name);
|
2688
3195
|
|
2689
|
-
const aotPointer = pointer != null;
|
3196
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2690
3197
|
|
2691
3198
|
// hack: .length
|
2692
3199
|
if (decl.property.name === 'length') {
|
2693
|
-
|
3200
|
+
const func = funcs.find(x => x.name === name);
|
3201
|
+
if (func) {
|
3202
|
+
const userFunc = funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name];
|
3203
|
+
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
3204
|
+
return number(typedParams ? func.params.length / 2 : func.params.length);
|
3205
|
+
}
|
3206
|
+
|
3207
|
+
if (builtinFuncs[name]) return number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length);
|
3208
|
+
if (importedFuncs[name]) return number(importedFuncs[name].params);
|
3209
|
+
if (internalConstrs[name]) return number(internalConstrs[name].length ?? 0);
|
3210
|
+
|
2694
3211
|
return [
|
2695
3212
|
...(aotPointer ? number(0, Valtype.i32) : [
|
2696
3213
|
...generate(scope, decl.object),
|
@@ -2702,10 +3219,13 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2702
3219
|
];
|
2703
3220
|
}
|
2704
3221
|
|
3222
|
+
const object = generate(scope, decl.object);
|
3223
|
+
const property = generate(scope, decl.property);
|
3224
|
+
|
2705
3225
|
// // todo: we should only do this for strings but we don't know at compile-time :(
|
2706
3226
|
// hack: this is naughty and will break things!
|
2707
3227
|
let newOut = number(0, valtypeBinary), newPointer = -1;
|
2708
|
-
if (pages.
|
3228
|
+
if (pages.hasAnyString) {
|
2709
3229
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2710
3230
|
rawElements: new Array(1)
|
2711
3231
|
}, _global, _name, true, 'i16');
|
@@ -2714,7 +3234,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2714
3234
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
2715
3235
|
[TYPES._array]: [
|
2716
3236
|
// get index as valtype
|
2717
|
-
...
|
3237
|
+
...property,
|
2718
3238
|
|
2719
3239
|
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
2720
3240
|
Opcodes.i32_to_u,
|
@@ -2722,7 +3242,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2722
3242
|
[ Opcodes.i32_mul ],
|
2723
3243
|
|
2724
3244
|
...(aotPointer ? [] : [
|
2725
|
-
...
|
3245
|
+
...object,
|
2726
3246
|
Opcodes.i32_to_u,
|
2727
3247
|
[ Opcodes.i32_add ]
|
2728
3248
|
]),
|
@@ -2731,7 +3251,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2731
3251
|
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2732
3252
|
|
2733
3253
|
...number(TYPES.number, Valtype.i32),
|
2734
|
-
setLastType(scope)
|
3254
|
+
...setLastType(scope)
|
2735
3255
|
],
|
2736
3256
|
|
2737
3257
|
[TYPES.string]: [
|
@@ -2741,14 +3261,14 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2741
3261
|
|
2742
3262
|
...number(0, Valtype.i32), // base 0 for store later
|
2743
3263
|
|
2744
|
-
...
|
2745
|
-
|
3264
|
+
...property,
|
2746
3265
|
Opcodes.i32_to_u,
|
3266
|
+
|
2747
3267
|
...number(ValtypeSize.i16, Valtype.i32),
|
2748
3268
|
[ Opcodes.i32_mul ],
|
2749
3269
|
|
2750
3270
|
...(aotPointer ? [] : [
|
2751
|
-
...
|
3271
|
+
...object,
|
2752
3272
|
Opcodes.i32_to_u,
|
2753
3273
|
[ Opcodes.i32_add ]
|
2754
3274
|
]),
|
@@ -2763,10 +3283,38 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2763
3283
|
...number(newPointer),
|
2764
3284
|
|
2765
3285
|
...number(TYPES.string, Valtype.i32),
|
2766
|
-
setLastType(scope)
|
3286
|
+
...setLastType(scope)
|
3287
|
+
],
|
3288
|
+
[TYPES._bytestring]: [
|
3289
|
+
// setup new/out array
|
3290
|
+
...newOut,
|
3291
|
+
[ Opcodes.drop ],
|
3292
|
+
|
3293
|
+
...number(0, Valtype.i32), // base 0 for store later
|
3294
|
+
|
3295
|
+
...property,
|
3296
|
+
Opcodes.i32_to_u,
|
3297
|
+
|
3298
|
+
...(aotPointer ? [] : [
|
3299
|
+
...object,
|
3300
|
+
Opcodes.i32_to_u,
|
3301
|
+
[ Opcodes.i32_add ]
|
3302
|
+
]),
|
3303
|
+
|
3304
|
+
// load current string ind {arg}
|
3305
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3306
|
+
|
3307
|
+
// store to new string ind 0
|
3308
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
3309
|
+
|
3310
|
+
// return new string (page)
|
3311
|
+
...number(newPointer),
|
3312
|
+
|
3313
|
+
...number(TYPES._bytestring, Valtype.i32),
|
3314
|
+
...setLastType(scope)
|
2767
3315
|
],
|
2768
3316
|
|
2769
|
-
default:
|
3317
|
+
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
|
2770
3318
|
});
|
2771
3319
|
};
|
2772
3320
|
|
@@ -2776,25 +3324,36 @@ const objectHack = node => {
|
|
2776
3324
|
if (!node) return node;
|
2777
3325
|
|
2778
3326
|
if (node.type === 'MemberExpression') {
|
2779
|
-
|
3327
|
+
const out = (() => {
|
3328
|
+
if (node.computed || node.optional) return;
|
2780
3329
|
|
2781
|
-
|
3330
|
+
let objectName = node.object.name;
|
2782
3331
|
|
2783
|
-
|
2784
|
-
|
3332
|
+
// if object is not identifier or another member exp, give up
|
3333
|
+
if (node.object.type !== 'Identifier' && node.object.type !== 'MemberExpression') return;
|
3334
|
+
if (objectName && ['undefined', 'null', 'NaN', 'Infinity'].includes(objectName)) return;
|
2785
3335
|
|
2786
|
-
|
3336
|
+
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
2787
3337
|
|
2788
|
-
|
2789
|
-
|
3338
|
+
// if .length, give up (hack within a hack!)
|
3339
|
+
if (node.property.name === 'length') {
|
3340
|
+
node.object = objectHack(node.object);
|
3341
|
+
return;
|
3342
|
+
}
|
2790
3343
|
|
2791
|
-
|
2792
|
-
|
3344
|
+
// no object name, give up
|
3345
|
+
if (!objectName) return;
|
2793
3346
|
|
2794
|
-
|
2795
|
-
|
2796
|
-
|
2797
|
-
|
3347
|
+
const name = '__' + objectName + '_' + node.property.name;
|
3348
|
+
if (Prefs.codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
3349
|
+
|
3350
|
+
return {
|
3351
|
+
type: 'Identifier',
|
3352
|
+
name
|
3353
|
+
};
|
3354
|
+
})();
|
3355
|
+
|
3356
|
+
if (out) return out;
|
2798
3357
|
}
|
2799
3358
|
|
2800
3359
|
for (const x in node) {
|
@@ -2808,8 +3367,8 @@ const objectHack = node => {
|
|
2808
3367
|
};
|
2809
3368
|
|
2810
3369
|
const generateFunc = (scope, decl) => {
|
2811
|
-
if (decl.async) return todo('async functions are not supported');
|
2812
|
-
if (decl.generator) return todo('generator functions are not supported');
|
3370
|
+
if (decl.async) return todo(scope, 'async functions are not supported');
|
3371
|
+
if (decl.generator) return todo(scope, 'generator functions are not supported');
|
2813
3372
|
|
2814
3373
|
const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
|
2815
3374
|
const params = decl.params ?? [];
|
@@ -2825,6 +3384,11 @@ const generateFunc = (scope, decl) => {
|
|
2825
3384
|
name
|
2826
3385
|
};
|
2827
3386
|
|
3387
|
+
if (typedInput && decl.returnType) {
|
3388
|
+
innerScope.returnType = extractTypeAnnotation(decl.returnType).type;
|
3389
|
+
innerScope.returns = [ valtypeBinary ];
|
3390
|
+
}
|
3391
|
+
|
2828
3392
|
for (let i = 0; i < params.length; i++) {
|
2829
3393
|
allocVar(innerScope, params[i].name, false);
|
2830
3394
|
|
@@ -2846,13 +3410,13 @@ const generateFunc = (scope, decl) => {
|
|
2846
3410
|
const func = {
|
2847
3411
|
name,
|
2848
3412
|
params: Object.values(innerScope.locals).slice(0, params.length * 2).map(x => x.type),
|
2849
|
-
|
2850
|
-
|
2851
|
-
throws: innerScope.throws,
|
2852
|
-
index: currentFuncIndex++
|
3413
|
+
index: currentFuncIndex++,
|
3414
|
+
...innerScope
|
2853
3415
|
};
|
2854
3416
|
funcIndex[name] = func.index;
|
2855
3417
|
|
3418
|
+
if (name === 'main') func.gotLastType = true;
|
3419
|
+
|
2856
3420
|
// quick hack fixes
|
2857
3421
|
for (const inst of wasm) {
|
2858
3422
|
if (inst[0] === Opcodes.call && inst[1] === -1) {
|
@@ -2904,7 +3468,7 @@ const internalConstrs = {
|
|
2904
3468
|
|
2905
3469
|
// todo: check in wasm instead of here
|
2906
3470
|
const literalValue = arg.value ?? 0;
|
2907
|
-
if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length');
|
3471
|
+
if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length', true);
|
2908
3472
|
|
2909
3473
|
return [
|
2910
3474
|
...number(0, Valtype.i32),
|
@@ -2915,7 +3479,8 @@ const internalConstrs = {
|
|
2915
3479
|
...number(pointer)
|
2916
3480
|
];
|
2917
3481
|
},
|
2918
|
-
type: TYPES._array
|
3482
|
+
type: TYPES._array,
|
3483
|
+
length: 1
|
2919
3484
|
},
|
2920
3485
|
|
2921
3486
|
__Array_of: {
|
@@ -2927,7 +3492,94 @@ const internalConstrs = {
|
|
2927
3492
|
}, global, name);
|
2928
3493
|
},
|
2929
3494
|
type: TYPES._array,
|
3495
|
+
notConstr: true,
|
3496
|
+
length: 0
|
3497
|
+
},
|
3498
|
+
|
3499
|
+
__Porffor_fastOr: {
|
3500
|
+
generate: (scope, decl) => {
|
3501
|
+
const out = [];
|
3502
|
+
|
3503
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3504
|
+
out.push(
|
3505
|
+
...generate(scope, decl.arguments[i]),
|
3506
|
+
Opcodes.i32_to_u,
|
3507
|
+
...(i > 0 ? [ [ Opcodes.i32_or ] ] : [])
|
3508
|
+
);
|
3509
|
+
}
|
3510
|
+
|
3511
|
+
return out;
|
3512
|
+
},
|
3513
|
+
type: TYPES.boolean,
|
2930
3514
|
notConstr: true
|
3515
|
+
},
|
3516
|
+
|
3517
|
+
__Porffor_fastAnd: {
|
3518
|
+
generate: (scope, decl) => {
|
3519
|
+
const out = [];
|
3520
|
+
|
3521
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3522
|
+
out.push(
|
3523
|
+
...generate(scope, decl.arguments[i]),
|
3524
|
+
Opcodes.i32_to_u,
|
3525
|
+
...(i > 0 ? [ [ Opcodes.i32_and ] ] : [])
|
3526
|
+
);
|
3527
|
+
}
|
3528
|
+
|
3529
|
+
return out;
|
3530
|
+
},
|
3531
|
+
type: TYPES.boolean,
|
3532
|
+
notConstr: true
|
3533
|
+
},
|
3534
|
+
|
3535
|
+
Boolean: {
|
3536
|
+
generate: (scope, decl) => {
|
3537
|
+
// todo: boolean object when used as constructor
|
3538
|
+
const arg = decl.arguments[0] ?? DEFAULT_VALUE;
|
3539
|
+
return truthy(scope, generate(scope, arg), getNodeType(scope, arg));
|
3540
|
+
},
|
3541
|
+
type: TYPES.boolean,
|
3542
|
+
length: 1
|
3543
|
+
},
|
3544
|
+
|
3545
|
+
__Math_max: {
|
3546
|
+
generate: (scope, decl) => {
|
3547
|
+
const out = [
|
3548
|
+
...number(-Infinity)
|
3549
|
+
];
|
3550
|
+
|
3551
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3552
|
+
out.push(
|
3553
|
+
...generate(scope, decl.arguments[i]),
|
3554
|
+
[ Opcodes.f64_max ]
|
3555
|
+
);
|
3556
|
+
}
|
3557
|
+
|
3558
|
+
return out;
|
3559
|
+
},
|
3560
|
+
type: TYPES.number,
|
3561
|
+
notConstr: true,
|
3562
|
+
length: 2
|
3563
|
+
},
|
3564
|
+
|
3565
|
+
__Math_min: {
|
3566
|
+
generate: (scope, decl) => {
|
3567
|
+
const out = [
|
3568
|
+
...number(Infinity)
|
3569
|
+
];
|
3570
|
+
|
3571
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3572
|
+
out.push(
|
3573
|
+
...generate(scope, decl.arguments[i]),
|
3574
|
+
[ Opcodes.f64_min ]
|
3575
|
+
);
|
3576
|
+
}
|
3577
|
+
|
3578
|
+
return out;
|
3579
|
+
},
|
3580
|
+
type: TYPES.number,
|
3581
|
+
notConstr: true,
|
3582
|
+
length: 2
|
2931
3583
|
}
|
2932
3584
|
};
|
2933
3585
|
|
@@ -2956,7 +3608,6 @@ export default program => {
|
|
2956
3608
|
funcs = [];
|
2957
3609
|
funcIndex = {};
|
2958
3610
|
depth = [];
|
2959
|
-
arrays = new Map();
|
2960
3611
|
pages = new Map();
|
2961
3612
|
data = [];
|
2962
3613
|
currentFuncIndex = importedFuncs.length;
|
@@ -2970,6 +3621,10 @@ export default program => {
|
|
2970
3621
|
|
2971
3622
|
const valtypeInd = ['i32', 'i64', 'f64'].indexOf(valtype);
|
2972
3623
|
|
3624
|
+
globalThis.pageSize = PageSize;
|
3625
|
+
const pageSizeOpt = process.argv.find(x => x.startsWith('-page-size='));
|
3626
|
+
if (pageSizeOpt) pageSize = parseInt(pageSizeOpt.split('=')[1]) * 1024;
|
3627
|
+
|
2973
3628
|
// set generic opcodes for current valtype
|
2974
3629
|
Opcodes.const = [ Opcodes.i32_const, Opcodes.i64_const, Opcodes.f64_const ][valtypeInd];
|
2975
3630
|
Opcodes.eq = [ Opcodes.i32_eq, Opcodes.i64_eq, Opcodes.f64_eq ][valtypeInd];
|
@@ -2978,10 +3633,10 @@ export default program => {
|
|
2978
3633
|
Opcodes.add = [ Opcodes.i32_add, Opcodes.i64_add, Opcodes.f64_add ][valtypeInd];
|
2979
3634
|
Opcodes.sub = [ Opcodes.i32_sub, Opcodes.i64_sub, Opcodes.f64_sub ][valtypeInd];
|
2980
3635
|
|
2981
|
-
Opcodes.i32_to = [ [
|
2982
|
-
Opcodes.i32_to_u = [ [
|
2983
|
-
Opcodes.i32_from = [ [
|
2984
|
-
Opcodes.i32_from_u = [ [
|
3636
|
+
Opcodes.i32_to = [ [], [ Opcodes.i32_wrap_i64 ], Opcodes.i32_trunc_sat_f64_s ][valtypeInd];
|
3637
|
+
Opcodes.i32_to_u = [ [], [ Opcodes.i32_wrap_i64 ], Opcodes.i32_trunc_sat_f64_u ][valtypeInd];
|
3638
|
+
Opcodes.i32_from = [ [], [ Opcodes.i64_extend_i32_s ], [ Opcodes.f64_convert_i32_s ] ][valtypeInd];
|
3639
|
+
Opcodes.i32_from_u = [ [], [ Opcodes.i64_extend_i32_u ], [ Opcodes.f64_convert_i32_u ] ][valtypeInd];
|
2985
3640
|
|
2986
3641
|
Opcodes.load = [ Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load ][valtypeInd];
|
2987
3642
|
Opcodes.store = [ Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store ][valtypeInd];
|
@@ -2994,10 +3649,6 @@ export default program => {
|
|
2994
3649
|
|
2995
3650
|
program.id = { name: 'main' };
|
2996
3651
|
|
2997
|
-
globalThis.pageSize = PageSize;
|
2998
|
-
const pageSizeOpt = process.argv.find(x => x.startsWith('-page-size='));
|
2999
|
-
if (pageSizeOpt) pageSize = parseInt(pageSizeOpt.split('=')[1]) * 1024;
|
3000
|
-
|
3001
3652
|
const scope = {
|
3002
3653
|
locals: {},
|
3003
3654
|
localInd: 0
|
@@ -3008,7 +3659,7 @@ export default program => {
|
|
3008
3659
|
body: program.body
|
3009
3660
|
};
|
3010
3661
|
|
3011
|
-
if (
|
3662
|
+
if (Prefs.astLog) console.log(JSON.stringify(program.body.body, null, 2));
|
3012
3663
|
|
3013
3664
|
generateFunc(scope, program);
|
3014
3665
|
|
@@ -3025,7 +3676,11 @@ export default program => {
|
|
3025
3676
|
}
|
3026
3677
|
|
3027
3678
|
if (lastInst[0] === Opcodes.end || lastInst[0] === Opcodes.local_set || lastInst[0] === Opcodes.global_set) {
|
3028
|
-
|
3679
|
+
if (lastInst[0] === Opcodes.local_set && lastInst[1] === main.locals['#last_type'].idx) {
|
3680
|
+
main.wasm.splice(main.wasm.length - 1, 1);
|
3681
|
+
} else {
|
3682
|
+
main.returns = [];
|
3683
|
+
}
|
3029
3684
|
}
|
3030
3685
|
|
3031
3686
|
if (lastInst[0] === Opcodes.call) {
|