porffor 0.24.11 → 0.24.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compiler/assemble.js +6 -7
- package/compiler/builtins/array.ts +51 -34
- package/compiler/builtins/arraybuffer.ts +9 -7
- package/compiler/builtins/console.ts +3 -3
- package/compiler/builtins/object.ts +26 -5
- package/compiler/builtins/porffor.d.ts +1 -0
- package/compiler/builtins/z_ecma262.ts +15 -0
- package/compiler/builtins_objects.js +2 -0
- package/compiler/builtins_precompiled.js +1119 -579
- package/compiler/codegen.js +298 -110
- package/compiler/precompile.js +4 -2
- package/package.json +1 -1
- package/runner/index.js +1 -1
package/compiler/codegen.js
CHANGED
@@ -91,6 +91,9 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
91
91
|
case 'NewExpression':
|
92
92
|
return cacheAst(decl, generateNew(scope, decl, global, name));
|
93
93
|
|
94
|
+
case 'ThisExpression':
|
95
|
+
return cacheAst(decl, generateThis(scope, decl, global, name));
|
96
|
+
|
94
97
|
case 'Literal':
|
95
98
|
return cacheAst(decl, generateLiteral(scope, decl, global, name));
|
96
99
|
|
@@ -351,22 +354,34 @@ const generateIdent = (scope, decl) => {
|
|
351
354
|
};
|
352
355
|
|
353
356
|
const generateReturn = (scope, decl) => {
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
357
|
+
const arg = decl.argument ?? DEFAULT_VALUE();
|
358
|
+
|
359
|
+
const out = [];
|
360
|
+
if (
|
361
|
+
scope.constr && // only do this in constructors
|
362
|
+
scope.newTarget && scope.usesThis && // sanity check
|
363
|
+
!globalThis.precompile // skip in precompiled built-ins, we should not require this and handle it ourselves
|
364
|
+
) {
|
365
|
+
// ignore return value and return this if being constructed
|
366
|
+
out.push(
|
367
|
+
// ...truthy(scope, [ [ Opcodes.local_get, '#newtarget' ] ], [ [ Opcodes.local_get, '#newtarget#type' ] ], false, true),
|
368
|
+
[ Opcodes.local_get, '#newtarget' ],
|
369
|
+
Opcodes.i32_to_u,
|
370
|
+
[ Opcodes.if, Blocktype.void ],
|
371
|
+
[ Opcodes.local_get, '#this' ],
|
372
|
+
...(scope.returnType != null ? [] : [ [ Opcodes.local_get, '#this#type' ] ]),
|
373
|
+
[ Opcodes.return ],
|
374
|
+
[ Opcodes.end ]
|
375
|
+
);
|
363
376
|
}
|
364
377
|
|
365
|
-
|
366
|
-
...generate(scope,
|
367
|
-
...(scope.returnType != null ? [] : getNodeType(scope,
|
378
|
+
out.push(
|
379
|
+
...generate(scope, arg),
|
380
|
+
...(scope.returnType != null ? [] : getNodeType(scope, arg)),
|
368
381
|
[ Opcodes.return ]
|
369
|
-
|
382
|
+
);
|
383
|
+
|
384
|
+
return out;
|
370
385
|
};
|
371
386
|
|
372
387
|
const localTmp = (scope, name, type = valtypeBinary) => {
|
@@ -1227,7 +1242,7 @@ const asmFuncToAsm = (scope, func) => {
|
|
1227
1242
|
});
|
1228
1243
|
};
|
1229
1244
|
|
1230
|
-
const asmFunc = (name, { wasm, params = [], typedParams = false, locals: localTypes = [], globals: globalTypes = [], globalInits = [], returns = [], returnType, localNames = [], globalNames = [], data: _data = [], table = false, constr = false, hasRestArgument = false } = {}) => {
|
1245
|
+
const asmFunc = (name, { wasm, params = [], typedParams = false, locals: localTypes = [], globals: globalTypes = [], globalInits = [], returns = [], returnType, localNames = [], globalNames = [], data: _data = [], table = false, constr = false, newTarget = false, usesThis = false, hasRestArgument = false } = {}) => {
|
1231
1246
|
if (wasm == null) { // called with no builtin
|
1232
1247
|
log.warning('codegen', `${name} has no built-in!`);
|
1233
1248
|
wasm = [];
|
@@ -1264,6 +1279,8 @@ const asmFunc = (name, { wasm, params = [], typedParams = false, locals: localTy
|
|
1264
1279
|
index: currentFuncIndex++,
|
1265
1280
|
table,
|
1266
1281
|
constr,
|
1282
|
+
newTarget,
|
1283
|
+
usesThis,
|
1267
1284
|
globalInits
|
1268
1285
|
};
|
1269
1286
|
|
@@ -1303,26 +1320,6 @@ const asmFunc = (name, { wasm, params = [], typedParams = false, locals: localTy
|
|
1303
1320
|
funcs.table = true;
|
1304
1321
|
}
|
1305
1322
|
|
1306
|
-
if (constr) {
|
1307
|
-
func.params = [...func.params];
|
1308
|
-
func.params.unshift(Valtype.i32);
|
1309
|
-
|
1310
|
-
// move all locals +1 idx (sigh)
|
1311
|
-
func.localInd++;
|
1312
|
-
const locals = func.locals;
|
1313
|
-
for (const x in locals) {
|
1314
|
-
locals[x].idx++;
|
1315
|
-
}
|
1316
|
-
|
1317
|
-
locals['#newtarget'] = { idx: 0, type: Valtype.i32 };
|
1318
|
-
|
1319
|
-
for (const inst of wasm) {
|
1320
|
-
if (inst[0] === Opcodes.local_get || inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) {
|
1321
|
-
inst[1]++;
|
1322
|
-
}
|
1323
|
-
}
|
1324
|
-
}
|
1325
|
-
|
1326
1323
|
if (hasRestArgument) func.hasRestArgument = true;
|
1327
1324
|
|
1328
1325
|
func.wasm = wasm;
|
@@ -1605,6 +1602,22 @@ const getNodeType = (scope, node) => {
|
|
1605
1602
|
}
|
1606
1603
|
}
|
1607
1604
|
|
1605
|
+
if (node.type == 'ThisExpression') {
|
1606
|
+
if (!scope.constr) return getType(scope, 'globalThis');
|
1607
|
+
return [ [ Opcodes.local_get, '#this#type' ] ];
|
1608
|
+
}
|
1609
|
+
|
1610
|
+
if (node.type == 'MetaProperty') {
|
1611
|
+
switch (`${node.meta.name}.${node.property.name}`) {
|
1612
|
+
case 'new.target': {
|
1613
|
+
return [ [ Opcodes.local_get, '#newtarget#type' ] ];
|
1614
|
+
}
|
1615
|
+
|
1616
|
+
default:
|
1617
|
+
return todo(scope, `meta property object ${node.meta.name} is not supported yet`, true);
|
1618
|
+
}
|
1619
|
+
}
|
1620
|
+
|
1608
1621
|
if (scope.locals['#last_type']) return getLastType(scope);
|
1609
1622
|
|
1610
1623
|
// presume
|
@@ -1762,6 +1775,42 @@ const RTArrayUtil = {
|
|
1762
1775
|
]
|
1763
1776
|
};
|
1764
1777
|
|
1778
|
+
const createNewTarget = (scope, decl, idx) => {
|
1779
|
+
if (decl._new) {
|
1780
|
+
return [
|
1781
|
+
...number(idx),
|
1782
|
+
...number(TYPES.function, Valtype.i32)
|
1783
|
+
];
|
1784
|
+
}
|
1785
|
+
|
1786
|
+
return [
|
1787
|
+
...number(UNDEFINED),
|
1788
|
+
...number(TYPES.undefined, Valtype.i32)
|
1789
|
+
];
|
1790
|
+
}
|
1791
|
+
|
1792
|
+
const createThisArg = (scope, decl, getFunc, knownThis = undefined) => {
|
1793
|
+
if (knownThis) {
|
1794
|
+
// todo: check compliance
|
1795
|
+
return knownThis;
|
1796
|
+
}
|
1797
|
+
|
1798
|
+
if (decl._new) {
|
1799
|
+
// todo: created object should have .prototype = func.prototype
|
1800
|
+
// todo: created object should have .constructor = func
|
1801
|
+
return [
|
1802
|
+
// create an empty object
|
1803
|
+
...generateObject(scope, { properties: [] }),
|
1804
|
+
...number(TYPES.object, Valtype.i32)
|
1805
|
+
];
|
1806
|
+
} else {
|
1807
|
+
return [
|
1808
|
+
...generate(scope, { type: 'Identifier', name: 'globalThis' }),
|
1809
|
+
...getType(scope, 'globalThis')
|
1810
|
+
];
|
1811
|
+
}
|
1812
|
+
}
|
1813
|
+
|
1765
1814
|
const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
1766
1815
|
let name = mapName(decl.callee.name);
|
1767
1816
|
if (isFuncType(decl.callee.type)) { // iife
|
@@ -1978,7 +2027,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1978
2027
|
getI32: () => RTArrayUtil.getLengthI32(getPointer),
|
1979
2028
|
set: value => RTArrayUtil.setLength(getPointer, value),
|
1980
2029
|
setI32: value => RTArrayUtil.setLengthI32(getPointer, value)
|
1981
|
-
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
|
2030
|
+
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE()), getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE()), protoLocal, protoLocal2, (length, itemType) => {
|
1982
2031
|
return makeArray(scope, {
|
1983
2032
|
rawElements: new Array(length)
|
1984
2033
|
}, _global, _name, true, itemType, true);
|
@@ -2115,7 +2164,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
2115
2164
|
const minArgc = Prefs.indirectCallMinArgc ?? 3;
|
2116
2165
|
|
2117
2166
|
if (args.length < minArgc) {
|
2118
|
-
args = args.concat(new Array(minArgc - args.length).fill(DEFAULT_VALUE));
|
2167
|
+
args = args.concat(new Array(minArgc - args.length).fill(DEFAULT_VALUE()));
|
2119
2168
|
}
|
2120
2169
|
}
|
2121
2170
|
|
@@ -2182,6 +2231,26 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
2182
2231
|
const funcLocal = localTmp(scope, '#indirect_func', Valtype.i32);
|
2183
2232
|
const flags = localTmp(scope, '#indirect_flags', Valtype.i32);
|
2184
2233
|
|
2234
|
+
let knownThis = undefined;
|
2235
|
+
let getCalleeObj = undefined;
|
2236
|
+
let initCalleeObj = undefined;
|
2237
|
+
|
2238
|
+
// hack: this should be more thorough, Function.bind, etc.
|
2239
|
+
if (decl.callee.type == 'MemberExpression') {
|
2240
|
+
const callee = localTmp(scope, '#indirect_callee_obj', Valtype.f64);
|
2241
|
+
initCalleeObj = [
|
2242
|
+
...generate(scope, decl.callee.object),
|
2243
|
+
[ Opcodes.local_set, callee ]
|
2244
|
+
];
|
2245
|
+
getCalleeObj = [
|
2246
|
+
[ Opcodes.local_get, callee ]
|
2247
|
+
];
|
2248
|
+
knownThis = [
|
2249
|
+
[ Opcodes.local_get, callee ],
|
2250
|
+
...getNodeType(scope, decl.callee.object)
|
2251
|
+
];
|
2252
|
+
}
|
2253
|
+
|
2185
2254
|
const gen = argc => {
|
2186
2255
|
const argsOut = [];
|
2187
2256
|
for (let i = 0; i < argc; i++) {
|
@@ -2202,43 +2271,74 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
2202
2271
|
[ Opcodes.end ]
|
2203
2272
|
];
|
2204
2273
|
|
2205
|
-
//
|
2206
|
-
|
2207
|
-
|
2208
|
-
|
2209
|
-
|
2210
|
-
|
2211
|
-
|
2212
|
-
|
2213
|
-
|
2214
|
-
|
2215
|
-
|
2216
|
-
|
2217
|
-
|
2218
|
-
|
2219
|
-
|
2220
|
-
|
2221
|
-
|
2222
|
-
|
2223
|
-
|
2224
|
-
|
2225
|
-
|
2226
|
-
|
2227
|
-
|
2228
|
-
|
2229
|
-
|
2230
|
-
|
2231
|
-
|
2232
|
-
|
2233
|
-
|
2234
|
-
|
2235
|
-
|
2236
|
-
|
2237
|
-
|
2238
|
-
|
2239
|
-
|
2240
|
-
|
2241
|
-
|
2274
|
+
// todo: i'm sure this could be made better somehow, probably only with #96?
|
2275
|
+
return checkFlag(0b1,
|
2276
|
+
// no type return
|
2277
|
+
checkFlag(0b1000,
|
2278
|
+
// no type return & this
|
2279
|
+
checkFlag(0b100, [
|
2280
|
+
// no type return & this & new.target
|
2281
|
+
...createNewTarget(scope, decl),
|
2282
|
+
...createThisArg(scope, decl, [], knownThis),
|
2283
|
+
...argsOut,
|
2284
|
+
[ Opcodes.local_get, funcLocal ],
|
2285
|
+
[ Opcodes.call_indirect, argc + 2, 0, 'no_type_return' ],
|
2286
|
+
], [
|
2287
|
+
// no type return & this
|
2288
|
+
...createThisArg(scope, decl),
|
2289
|
+
...argsOut,
|
2290
|
+
[ Opcodes.local_get, funcLocal ],
|
2291
|
+
[ Opcodes.call_indirect, argc + 1, 0, 'no_type_return' ]
|
2292
|
+
]),
|
2293
|
+
// no type return
|
2294
|
+
checkFlag(0b100, [
|
2295
|
+
// no type return & new.target
|
2296
|
+
...createNewTarget(scope, decl),
|
2297
|
+
...argsOut,
|
2298
|
+
[ Opcodes.local_get, funcLocal ],
|
2299
|
+
[ Opcodes.call_indirect, argc + 1, 0, 'no_type_return' ],
|
2300
|
+
], [
|
2301
|
+
// no type return
|
2302
|
+
...argsOut,
|
2303
|
+
[ Opcodes.local_get, funcLocal ],
|
2304
|
+
[ Opcodes.call_indirect, argc, 0, 'no_type_return' ]
|
2305
|
+
]),
|
2306
|
+
),
|
2307
|
+
// type return
|
2308
|
+
checkFlag(0b1000,
|
2309
|
+
// type return & this
|
2310
|
+
checkFlag(0b100, [
|
2311
|
+
// type return & this & new.target
|
2312
|
+
...createNewTarget(scope, decl),
|
2313
|
+
...createThisArg(scope, decl, [], knownThis),
|
2314
|
+
...argsOut,
|
2315
|
+
[ Opcodes.local_get, funcLocal ],
|
2316
|
+
[ Opcodes.call_indirect, argc + 2, 0 ],
|
2317
|
+
...setLastType(scope),
|
2318
|
+
], [
|
2319
|
+
// type return & this
|
2320
|
+
...createThisArg(scope, decl),
|
2321
|
+
...argsOut,
|
2322
|
+
[ Opcodes.local_get, funcLocal ],
|
2323
|
+
[ Opcodes.call_indirect, argc + 1, 0 ],
|
2324
|
+
...setLastType(scope),
|
2325
|
+
]),
|
2326
|
+
// type return
|
2327
|
+
checkFlag(0b100, [
|
2328
|
+
// type return & new.target
|
2329
|
+
...createNewTarget(scope, decl),
|
2330
|
+
...argsOut,
|
2331
|
+
[ Opcodes.local_get, funcLocal ],
|
2332
|
+
[ Opcodes.call_indirect, argc + 1, 0 ],
|
2333
|
+
...setLastType(scope),
|
2334
|
+
], [
|
2335
|
+
// type return
|
2336
|
+
...argsOut,
|
2337
|
+
[ Opcodes.local_get, funcLocal ],
|
2338
|
+
[ Opcodes.call_indirect, argc, 0 ],
|
2339
|
+
...setLastType(scope),
|
2340
|
+
]),
|
2341
|
+
)
|
2242
2342
|
);
|
2243
2343
|
};
|
2244
2344
|
|
@@ -2250,7 +2350,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
2250
2350
|
// todo/perf: check if we should use br_table here or just generate our own big if..elses
|
2251
2351
|
|
2252
2352
|
return [
|
2253
|
-
...
|
2353
|
+
...(getCalleeObj ? [
|
2354
|
+
...initCalleeObj,
|
2355
|
+
...generateMember(scope, decl.callee, false, undefined, getCalleeObj)
|
2356
|
+
]: generate(scope, decl.callee)),
|
2254
2357
|
[ Opcodes.local_set, localTmp(scope, '#indirect_callee') ],
|
2255
2358
|
|
2256
2359
|
...typeSwitch(scope, getNodeType(scope, decl.callee), {
|
@@ -2298,26 +2401,32 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
2298
2401
|
const userFunc = func && !func.internal;
|
2299
2402
|
const typedParams = userFunc || func?.typedParams;
|
2300
2403
|
const typedReturns = (userFunc && func.returnType == null) || builtinFuncs[name]?.typedReturns;
|
2301
|
-
let paramCount =
|
2404
|
+
let paramCount = countParams(func, name);
|
2302
2405
|
|
2303
2406
|
let paramOffset = 0;
|
2304
|
-
if (func && func.constr) {
|
2305
|
-
// new.target arg
|
2306
|
-
if (func.internal) paramOffset = 1;
|
2307
|
-
if (!typedParams) paramCount--;
|
2308
|
-
out.push([ Opcodes.i32_const, decl._new ? 1 : 0 ]);
|
2309
|
-
} else if (decl._new)
|
2407
|
+
if (decl._new && func && !func.constr) {
|
2310
2408
|
return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a constructor`, true);
|
2409
|
+
}
|
2410
|
+
|
2411
|
+
if (func && func.newTarget) {
|
2412
|
+
out.push(...createNewTarget(scope, decl, idx - importedFuncs.length));
|
2413
|
+
paramOffset += 2;
|
2414
|
+
}
|
2415
|
+
|
2416
|
+
if (func && func.usesThis) {
|
2417
|
+
out.push(...createThisArg(scope, decl, func));
|
2418
|
+
paramOffset += 2;
|
2419
|
+
}
|
2311
2420
|
|
2312
2421
|
let args = [...decl.arguments];
|
2313
2422
|
if (func && !func.hasRestArgument && args.length < paramCount) {
|
2314
2423
|
// too little args, push undefineds
|
2315
|
-
args = args.concat(new Array(paramCount - args.length).fill(DEFAULT_VALUE));
|
2424
|
+
args = args.concat(new Array(paramCount - args.length).fill(DEFAULT_VALUE()));
|
2316
2425
|
}
|
2317
2426
|
|
2318
2427
|
if (func && func.hasRestArgument) {
|
2319
2428
|
if (args.length < paramCount) {
|
2320
|
-
args = args.concat(new Array(paramCount - 1 - args.length).fill(DEFAULT_VALUE));
|
2429
|
+
args = args.concat(new Array(paramCount - 1 - args.length).fill(DEFAULT_VALUE()));
|
2321
2430
|
}
|
2322
2431
|
|
2323
2432
|
const restArgs = args.slice(paramCount - 1);
|
@@ -2391,11 +2500,28 @@ const generateNew = (scope, decl, _global, _name) => generateCall(scope, {
|
|
2391
2500
|
_new: true
|
2392
2501
|
}, _global, _name);
|
2393
2502
|
|
2503
|
+
const generateThis = (scope, decl, _global, _name) => {
|
2504
|
+
if (!scope.constr) {
|
2505
|
+
// this in a non-constructor context is a reference to globalThis
|
2506
|
+
return [
|
2507
|
+
...generate(scope, { type: 'Identifier', name: 'globalThis' }),
|
2508
|
+
...setLastType(scope, getType(scope, 'globalThis'))
|
2509
|
+
];
|
2510
|
+
}
|
2511
|
+
|
2512
|
+
scope.usesThis = true;
|
2513
|
+
|
2514
|
+
return [
|
2515
|
+
[ Opcodes.local_get, '#this' ],
|
2516
|
+
...setLastType(scope, [ [ Opcodes.local_get, '#this#type' ] ])
|
2517
|
+
];
|
2518
|
+
}
|
2519
|
+
|
2394
2520
|
// bad hack for undefined and null working without additional logic
|
2395
|
-
const DEFAULT_VALUE = {
|
2521
|
+
const DEFAULT_VALUE = () => ({
|
2396
2522
|
type: 'Identifier',
|
2397
2523
|
name: 'undefined'
|
2398
|
-
};
|
2524
|
+
});
|
2399
2525
|
|
2400
2526
|
const codeToSanitizedStr = code => {
|
2401
2527
|
let out = '';
|
@@ -4128,7 +4254,7 @@ const generateThrow = (scope, decl) => {
|
|
4128
4254
|
message = decl.argument.arguments[0];
|
4129
4255
|
}
|
4130
4256
|
|
4131
|
-
message ??= DEFAULT_VALUE;
|
4257
|
+
message ??= DEFAULT_VALUE();
|
4132
4258
|
|
4133
4259
|
if (tags.length === 0) tags.push({
|
4134
4260
|
params: [ valtypeBinary, valtypeBinary, Valtype.i32 ],
|
@@ -4153,7 +4279,7 @@ const generateThrow = (scope, decl) => {
|
|
4153
4279
|
message = decl.argument.arguments[0];
|
4154
4280
|
}
|
4155
4281
|
|
4156
|
-
message ??= DEFAULT_VALUE;
|
4282
|
+
message ??= DEFAULT_VALUE();
|
4157
4283
|
|
4158
4284
|
if (tags.length === 0) tags.push({
|
4159
4285
|
params: [ Valtype.i32, valtypeBinary, Valtype.i32 ],
|
@@ -4209,18 +4335,17 @@ const generateEmpty = (scope, decl) => {
|
|
4209
4335
|
};
|
4210
4336
|
|
4211
4337
|
const generateMeta = (scope, decl) => {
|
4212
|
-
if (decl.meta.name !== 'new') return todo(scope, `meta property object ${decl.meta.name} is not supported yet`, true);
|
4213
|
-
|
4214
4338
|
switch (`${decl.meta.name}.${decl.property.name}`) {
|
4215
4339
|
case 'new.target': {
|
4216
|
-
scope.
|
4340
|
+
scope.newTarget = true;
|
4217
4341
|
|
4218
4342
|
return [
|
4219
|
-
[ Opcodes.local_get,
|
4220
|
-
Opcodes.i32_from_u,
|
4221
|
-
...setLastType(scope, TYPES.boolean)
|
4343
|
+
[ Opcodes.local_get, '#newtarget' ],
|
4222
4344
|
];
|
4223
4345
|
}
|
4346
|
+
|
4347
|
+
default:
|
4348
|
+
return todo(scope, `meta property object ${decl.meta.name} is not supported yet`, true);
|
4224
4349
|
}
|
4225
4350
|
};
|
4226
4351
|
|
@@ -4641,7 +4766,26 @@ const wrapBC = (bc, { prelude = [], postlude = [] } = {}) => {
|
|
4641
4766
|
return out;
|
4642
4767
|
};
|
4643
4768
|
|
4644
|
-
const
|
4769
|
+
const countParams = (func, name = undefined) => {
|
4770
|
+
if (!func) {
|
4771
|
+
if (Object.hasOwn(importedFuncs, name)) {
|
4772
|
+
// reverse lookup then normal lookup
|
4773
|
+
func = importedFuncs[importedFuncs[name]];
|
4774
|
+
if (func) return func.params?.length ?? func.params;
|
4775
|
+
}
|
4776
|
+
return;
|
4777
|
+
}
|
4778
|
+
if (func.argc) return func.argc;
|
4779
|
+
|
4780
|
+
let params = func.params.length;
|
4781
|
+
if (func.newTarget) params -= 2;
|
4782
|
+
if (func.usesThis) params -= 2;
|
4783
|
+
if (!func.internal || builtinFuncs[func.name]?.typedParams) params = Math.floor(params / 2);
|
4784
|
+
|
4785
|
+
return params;
|
4786
|
+
}
|
4787
|
+
|
4788
|
+
const generateMember = (scope, decl, _global, _name, _objectWasm = undefined) => {
|
4645
4789
|
const name = decl.object.name;
|
4646
4790
|
|
4647
4791
|
// hack: .name
|
@@ -4659,12 +4803,9 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
4659
4803
|
// todo: support optional
|
4660
4804
|
|
4661
4805
|
const func = funcs.find(x => x.name === name);
|
4662
|
-
if (func)
|
4663
|
-
const typedParams = !func.internal || func.typedParams;
|
4664
|
-
return withType(scope, number(typedParams ? Math.floor(func.params.length / 2) : (func.constr ? (func.params.length - 1) : func.params.length)), TYPES.number);
|
4665
|
-
}
|
4806
|
+
if (func) return withType(scope, number(countParams(func)), TYPES.number);
|
4666
4807
|
|
4667
|
-
if (Object.hasOwn(builtinFuncs, name)) return withType(scope, number(
|
4808
|
+
if (Object.hasOwn(builtinFuncs, name)) return withType(scope, number(countParams(builtinFuncs[name])), TYPES.number);
|
4668
4809
|
if (Object.hasOwn(importedFuncs, name)) return withType(scope, number(importedFuncs[name].params.length ?? importedFuncs[name].params), TYPES.number);
|
4669
4810
|
if (Object.hasOwn(internalConstrs, name)) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
|
4670
4811
|
|
@@ -4954,7 +5095,7 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
4954
5095
|
if (decl.optional) {
|
4955
5096
|
out.unshift(
|
4956
5097
|
[ Opcodes.block, valtypeBinary ],
|
4957
|
-
...generate(scope, object),
|
5098
|
+
...(_objectWasm ? _objectWasm : generate(scope, object)),
|
4958
5099
|
[ Opcodes.local_tee, localTmp(scope, '#member_obj') ],
|
4959
5100
|
|
4960
5101
|
...nullish(scope, [], getNodeType(scope, object), false, true),
|
@@ -4973,7 +5114,7 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
4973
5114
|
);
|
4974
5115
|
} else {
|
4975
5116
|
out.unshift(
|
4976
|
-
...generate(scope, object),
|
5117
|
+
...(_objectWasm ? _objectWasm : generate(scope, object)),
|
4977
5118
|
[ Opcodes.local_set, localTmp(scope, '#member_obj') ],
|
4978
5119
|
|
4979
5120
|
...generate(scope, property, false, '#member_prop'),
|
@@ -4995,7 +5136,7 @@ const objectHack = node => {
|
|
4995
5136
|
if (node.computed || node.optional) return;
|
4996
5137
|
|
4997
5138
|
// hack: block these properties as they can be accessed on functions
|
4998
|
-
if (node.property.name ==
|
5139
|
+
if (node.property.name == 'length' || node.property.name == 'name') return;
|
4999
5140
|
|
5000
5141
|
let objectName = node.object.name;
|
5001
5142
|
|
@@ -5045,7 +5186,8 @@ const generateFunc = (scope, decl) => {
|
|
5045
5186
|
returns: [ valtypeBinary, Valtype.i32 ],
|
5046
5187
|
throws: false,
|
5047
5188
|
name,
|
5048
|
-
index: currentFuncIndex
|
5189
|
+
index: currentFuncIndex++,
|
5190
|
+
constr: decl.type && decl.type !== 'ArrowFunctionExpression' && decl.type !== 'Program'
|
5049
5191
|
};
|
5050
5192
|
|
5051
5193
|
funcIndex[name] = func.index;
|
@@ -5093,6 +5235,13 @@ const generateFunc = (scope, decl) => {
|
|
5093
5235
|
|
5094
5236
|
func.params = Object.values(func.locals).map(x => x.type);
|
5095
5237
|
|
5238
|
+
func.argc = countParams(func, name);
|
5239
|
+
|
5240
|
+
if (func.constr) {
|
5241
|
+
func.newTarget = true;
|
5242
|
+
func.usesThis = true;
|
5243
|
+
}
|
5244
|
+
|
5096
5245
|
let body = objectHack(decl.body);
|
5097
5246
|
if (decl.type === 'ArrowFunctionExpression' && decl.expression) {
|
5098
5247
|
// hack: () => 0 -> () => return 0
|
@@ -5123,11 +5272,50 @@ const generateFunc = (scope, decl) => {
|
|
5123
5272
|
|
5124
5273
|
// add end return if not found
|
5125
5274
|
if (name !== 'main' && wasm[wasm.length - 1]?.[0] !== Opcodes.return && countLeftover(wasm) === 0) {
|
5126
|
-
wasm.push(
|
5127
|
-
|
5128
|
-
|
5129
|
-
|
5130
|
-
|
5275
|
+
wasm.push(...generateReturn(func, {}));
|
5276
|
+
}
|
5277
|
+
|
5278
|
+
if (func.newTarget || func.usesThis) {
|
5279
|
+
const locals = func.locals;
|
5280
|
+
let idxOffset = 0;
|
5281
|
+
func.params = [...func.params];
|
5282
|
+
if (func.usesThis) {
|
5283
|
+
func.params.unshift(valtypeBinary, Valtype.i32);
|
5284
|
+
idxOffset += 2;
|
5285
|
+
}
|
5286
|
+
if (func.newTarget) {
|
5287
|
+
func.params.unshift(valtypeBinary, Valtype.i32);
|
5288
|
+
idxOffset += 2;
|
5289
|
+
}
|
5290
|
+
|
5291
|
+
// move all local indexes by idxOffset
|
5292
|
+
func.localInd += idxOffset;
|
5293
|
+
for (const x in locals) {
|
5294
|
+
locals[x].idx += idxOffset;
|
5295
|
+
}
|
5296
|
+
|
5297
|
+
let indexes = idxOffset;
|
5298
|
+
// note: make sure to add these in reverse order of location
|
5299
|
+
if (func.usesThis) {
|
5300
|
+
locals['#this#type'] = { idx: --indexes, type: Valtype.i32 };
|
5301
|
+
locals['#this'] = { idx: --indexes, type: valtypeBinary };
|
5302
|
+
}
|
5303
|
+
if (func.newTarget) {
|
5304
|
+
locals['#newtarget#type'] = { idx: --indexes, type: Valtype.i32 };
|
5305
|
+
locals['#newtarget'] = { idx: --indexes, type: valtypeBinary };
|
5306
|
+
}
|
5307
|
+
|
5308
|
+
for (let i = 0; i < wasm.length; i++) {
|
5309
|
+
// note: these needs to be copied, even though they realistically shouldn't
|
5310
|
+
const inst = wasm[i];
|
5311
|
+
if (inst[0] === Opcodes.local_get || inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) {
|
5312
|
+
if (typeof inst[1] == 'string') {
|
5313
|
+
wasm[i] = [ inst[0], locals[inst[1]].idx ];
|
5314
|
+
} else {
|
5315
|
+
wasm[i] = [ inst[0], inst[1] + idxOffset ];
|
5316
|
+
}
|
5317
|
+
}
|
5318
|
+
}
|
5131
5319
|
}
|
5132
5320
|
|
5133
5321
|
return func;
|
@@ -5156,7 +5344,7 @@ const internalConstrs = {
|
|
5156
5344
|
rawElements: new Array(0)
|
5157
5345
|
}, global, name, true, undefined, true, true);
|
5158
5346
|
|
5159
|
-
const arg = decl.arguments[0] ?? DEFAULT_VALUE;
|
5347
|
+
const arg = decl.arguments[0] ?? DEFAULT_VALUE();
|
5160
5348
|
|
5161
5349
|
// todo: check in wasm instead of here
|
5162
5350
|
const literalValue = arg.value ?? 0;
|
@@ -5232,7 +5420,7 @@ const internalConstrs = {
|
|
5232
5420
|
Boolean: {
|
5233
5421
|
generate: (scope, decl) => {
|
5234
5422
|
// todo: boolean object when used as constructor
|
5235
|
-
const arg = decl.arguments[0] ?? DEFAULT_VALUE;
|
5423
|
+
const arg = decl.arguments[0] ?? DEFAULT_VALUE();
|
5236
5424
|
return truthy(scope, generate(scope, arg), getNodeType(scope, arg), false, false, 'full');
|
5237
5425
|
},
|
5238
5426
|
type: TYPES.boolean,
|
package/compiler/precompile.js
CHANGED
@@ -209,13 +209,15 @@ ${funcs.map(x => {
|
|
209
209
|
return `(scope, {${`${str.includes('allocPage(') ? 'allocPage,' : ''}${str.includes('glbl(') ? 'glbl,' : ''}${str.includes('loc(') ? 'loc,' : ''}${str.includes('builtin(') ? 'builtin,' : ''}${str.includes('internalThrow(') ? 'internalThrow,' : ''}`.slice(0, -1)}}) => ` + str;
|
210
210
|
};
|
211
211
|
|
212
|
+
const locals = Object.entries(x.locals).sort((a,b) => a[1].idx - b[1].idx)
|
213
|
+
|
212
214
|
return ` this.${x.name} = {
|
213
215
|
wasm: ${rewriteWasm(x.wasm)},
|
214
216
|
params: ${JSON.stringify(x.params)}, typedParams: 1,
|
215
217
|
returns: ${JSON.stringify(x.returns)}, ${x.returnType != null ? `returnType: ${JSON.stringify(x.returnType)}` : 'typedReturns: 1'},
|
216
|
-
locals: ${JSON.stringify(
|
218
|
+
locals: ${JSON.stringify(locals.slice(x.params.length).map(x => x[1].type))}, localNames: ${JSON.stringify(locals.map(x => x[0]))},
|
217
219
|
${x.globalInits ? ` globalInits: {${Object.keys(x.globalInits).map(y => `${y}: ${rewriteWasm(x.globalInits[y])}`).join(',')}},\n` : ''}${x.data && x.data.length > 0 ? ` data: [${x.data.map(x => `[${x.offset ?? 'null'},[${x.bytes.join(',')}]]`).join(',')}],` : ''}
|
218
|
-
${x.table ? ` table: 1,` : ''}${x.constr ? ` constr: 1,` : ''}${x.hasRestArgument ? ` hasRestArgument: 1,` : ''}
|
220
|
+
${x.table ? ` table: 1,` : ''}${x.constr ? ` constr: 1,` : ''}${x.usesThis ? ` usesThis: 1,` : ''}${x.newTarget ? ` newTarget: 1,` : ''}${x.argc !== undefined ? ` argc: ${x.argc},` : ''}${x.hasRestArgument ? ` hasRestArgument: 1,` : ''}
|
219
221
|
};`.replaceAll('\n\n', '\n').replaceAll('\n\n', '\n').replaceAll('\n\n', '\n');
|
220
222
|
}).join('\n')}
|
221
223
|
};`;
|
package/package.json
CHANGED