porffor 0.41.6 → 0.42.0
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 +16 -17
- package/compiler/builtins/_internal_object.ts +3 -60
- package/compiler/builtins.js +8 -10
- package/compiler/builtins_precompiled.js +338 -335
- package/compiler/codegen.js +173 -255
- package/compiler/precompile.js +20 -22
- package/package.json +1 -1
- package/runner/index.js +2 -2
package/compiler/codegen.js
CHANGED
@@ -48,20 +48,111 @@ const cacheAst = (decl, wasm) => {
|
|
48
48
|
return wasm;
|
49
49
|
};
|
50
50
|
|
51
|
+
let indirectFuncs = [];
|
51
52
|
const funcRef = func => {
|
52
|
-
// generate func
|
53
53
|
func.generate?.();
|
54
54
|
|
55
55
|
if (globalThis.precompile) return [
|
56
56
|
[ Opcodes.const, 'funcref', func.name ]
|
57
57
|
];
|
58
58
|
|
59
|
+
const wrapperArgc = Prefs.indirectWrapperArgc ?? 8;
|
60
|
+
if (!func.wrapperFunc) {
|
61
|
+
const locals = {}, params = [];
|
62
|
+
for (let i = 0; i < wrapperArgc + 2; i++) {
|
63
|
+
params.push(valtypeBinary, Valtype.i32);
|
64
|
+
locals[i * 2] = { idx: i * 2, type: valtypeBinary };
|
65
|
+
locals[i * 2 + 1] = { idx: i * 2 + 1, type: Valtype.i32 };
|
66
|
+
}
|
67
|
+
let localInd = (wrapperArgc + 2) * 2;
|
68
|
+
|
69
|
+
const wasm = [];
|
70
|
+
const offset = func.constr ? 0 : 4;
|
71
|
+
for (let i = 0; i < func.params.length; i++) {
|
72
|
+
if (func.internal && func.name.includes('_prototype_') && i < 2) {
|
73
|
+
// special case: use real this for prototype internals
|
74
|
+
wasm.push(
|
75
|
+
[ Opcodes.local_get, 2 + i ],
|
76
|
+
...(i % 2 === 0 && func.params[i] === Valtype.i32 ? [ Opcodes.i32_to ]: [])
|
77
|
+
);
|
78
|
+
} else {
|
79
|
+
wasm.push(
|
80
|
+
[ Opcodes.local_get, offset + (!func.internal || func.typedParams ? i : i * 2) ],
|
81
|
+
...(i % 2 === 0 && func.params[i] === Valtype.i32 ? [ Opcodes.i32_to ]: [])
|
82
|
+
);
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
wasm.push([ Opcodes.call, func.index ]);
|
87
|
+
|
88
|
+
if (func.returns[0] === Valtype.i32) {
|
89
|
+
if (func.returns.length === 2) {
|
90
|
+
const localIdx = localInd++;
|
91
|
+
locals[localIdx] = { idx: localIdx, type: Valtype.i32 };
|
92
|
+
|
93
|
+
wasm.push(
|
94
|
+
[ Opcodes.local_set, localIdx ],
|
95
|
+
Opcodes.i32_from,
|
96
|
+
[ Opcodes.local_get, localIdx ]
|
97
|
+
);
|
98
|
+
} else {
|
99
|
+
wasm.push(Opcodes.i32_from);
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
if (func.returns.length === 1) {
|
104
|
+
// add built-in returnType if only returns a value
|
105
|
+
wasm.push(...number(func.returnType ?? TYPES.number, Valtype.i32));
|
106
|
+
}
|
107
|
+
|
108
|
+
const name = '#indirect_' + func.name;
|
109
|
+
const wrapperFunc = {
|
110
|
+
name,
|
111
|
+
params,
|
112
|
+
locals, localInd,
|
113
|
+
returns: [ valtypeBinary, Valtype.i32 ],
|
114
|
+
wasm,
|
115
|
+
constr: true,
|
116
|
+
internal: true,
|
117
|
+
indirect: true
|
118
|
+
};
|
119
|
+
|
120
|
+
indirectFuncs.push(wrapperFunc);
|
121
|
+
|
122
|
+
wrapperFunc.jsLength = countLength(func);
|
123
|
+
func.wrapperFunc = wrapperFunc;
|
124
|
+
|
125
|
+
if (!func.constr) {
|
126
|
+
// check not being constructed
|
127
|
+
wasm.unshift(
|
128
|
+
[ Opcodes.local_get, 0 ], // new.target value
|
129
|
+
Opcodes.i32_to_u,
|
130
|
+
[ Opcodes.if, Blocktype.void ], // if value is non-zero
|
131
|
+
...internalThrow(wrapperFunc, 'TypeError', `${unhackName(func.name)} is not a constructor`), // throw type error
|
132
|
+
[ Opcodes.end ]
|
133
|
+
);
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
59
137
|
return [
|
60
138
|
[ Opcodes.const, func.index - importedFuncs.length ]
|
61
|
-
// [ Opcodes.const, func.index - importedFuncs.length, 'funcref' ]
|
62
139
|
];
|
63
140
|
};
|
64
141
|
|
142
|
+
const forceDuoValtype = (scope, wasm, forceValtype) => [
|
143
|
+
...wasm,
|
144
|
+
...(valtypeBinary === Valtype.i32 && forceValtype === Valtype.f64 ? [
|
145
|
+
[ Opcodes.local_set, localTmp(scope, '#swap', Valtype.i32) ],
|
146
|
+
[ Opcodes.f64_convert_i32_s ],
|
147
|
+
[ Opcodes.local_get, localTmp(scope, '#swap', Valtype.i32) ]
|
148
|
+
] : []),
|
149
|
+
...(valtypeBinary === Valtype.f64 && forceValtype === Valtype.i32 ? [
|
150
|
+
[ Opcodes.local_set, localTmp(scope, '#swap', Valtype.i32) ],
|
151
|
+
Opcodes.i32_trunc_sat_f64_s,
|
152
|
+
[ Opcodes.local_get, localTmp(scope, '#swap', Valtype.i32) ]
|
153
|
+
] : [])
|
154
|
+
];
|
155
|
+
|
65
156
|
const generate = (scope, decl, global = false, name = undefined, valueUnused = false) => {
|
66
157
|
if (astCache.has(decl)) return astCache.get(decl);
|
67
158
|
|
@@ -245,7 +336,7 @@ const internalThrow = (scope, constructor, message, expectsValue = Prefs.alwaysV
|
|
245
336
|
]
|
246
337
|
}
|
247
338
|
}),
|
248
|
-
...(expectsValue ? number(UNDEFINED) : [])
|
339
|
+
...(expectsValue ? number(UNDEFINED, typeof expectsValue === 'number' ? expectsValue : valtypeBinary) : [])
|
249
340
|
];
|
250
341
|
|
251
342
|
const generateIdent = (scope, decl) => {
|
@@ -2167,102 +2258,45 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
2167
2258
|
} else {
|
2168
2259
|
if (!Prefs.indirectCalls) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
|
2169
2260
|
|
2170
|
-
// todo: only works when function uses typedParams and typedReturns
|
2171
|
-
|
2172
|
-
const indirectMode = Prefs.indirectCallMode ?? 'vararg';
|
2173
|
-
// options: vararg, strict
|
2174
|
-
// - strict: simpler, smaller size usage, no func lut needed.
|
2175
|
-
// ONLY works when arg count of call == arg count of function being called
|
2176
|
-
// - vararg: large size usage, cursed.
|
2177
|
-
// works when arg count of call != arg count of function being called*
|
2178
|
-
// * most of the time, some edgecases
|
2179
|
-
|
2180
2261
|
funcs.table = true;
|
2181
2262
|
scope.table = true;
|
2182
2263
|
|
2183
2264
|
let args = decl.arguments;
|
2184
|
-
|
2185
|
-
|
2186
|
-
|
2187
|
-
const minArgc = Prefs.indirectCallMinArgc ?? 5;
|
2188
|
-
|
2189
|
-
if (args.length < minArgc) {
|
2190
|
-
args = args.concat(new Array(minArgc - args.length).fill(DEFAULT_VALUE()));
|
2191
|
-
}
|
2265
|
+
const wrapperArgc = Prefs.indirectWrapperArgc ?? 8;
|
2266
|
+
if (args.length < wrapperArgc) {
|
2267
|
+
args = args.concat(new Array(wrapperArgc - args.length).fill(DEFAULT_VALUE()));
|
2192
2268
|
}
|
2193
2269
|
|
2194
2270
|
for (let i = 0; i < args.length; i++) {
|
2195
2271
|
const arg = args[i];
|
2196
|
-
out = out.concat(generate(scope, arg));
|
2197
|
-
out = out.concat(getNodeType(scope, arg));
|
2198
|
-
|
2199
|
-
if (indirectMode === 'vararg') {
|
2200
|
-
const typeLocal = localTmp(scope, `#indirect_arg${i}_type`, Valtype.i32);
|
2201
|
-
const valLocal = localTmp(scope, `#indirect_arg${i}_val`);
|
2202
|
-
|
2203
|
-
locals.push([valLocal, typeLocal]);
|
2204
|
-
|
2205
|
-
out.push(
|
2206
|
-
[ Opcodes.local_set, typeLocal ],
|
2207
|
-
[ Opcodes.local_set, valLocal ]
|
2208
|
-
);
|
2209
|
-
}
|
2272
|
+
out = out.concat(generate(scope, arg), valtypeBinary === Valtype.i32 && scope.locals[arg.name]?.type !== Valtype.f64 ? [ [ Opcodes.f64_convert_i32_s ] ] : [], getNodeType(scope, arg));
|
2210
2273
|
}
|
2211
2274
|
|
2212
|
-
|
2213
|
-
|
2214
|
-
...generate(scope, decl.callee),
|
2215
|
-
[ Opcodes.local_set, localTmp(scope, '#indirect_callee') ],
|
2275
|
+
let knownThis = undefined, getCallee = undefined;
|
2276
|
+
const calleeLocal = localTmp(scope, '#indirect_callee');
|
2216
2277
|
|
2217
|
-
|
2218
|
-
|
2219
|
-
|
2278
|
+
// hack: this should be more thorough, Function.bind, etc
|
2279
|
+
if (decl.callee.type === 'MemberExpression' && !decl._new) {
|
2280
|
+
const thisLocal = localTmp(scope, '#indirect_caller');
|
2281
|
+
const thisLocalType = localTmp(scope, '#indirect_caller#type', Valtype.i32);
|
2220
2282
|
|
2221
|
-
|
2222
|
-
|
2223
|
-
|
2224
|
-
[ Opcodes.call_indirect, args.length, 0 ],
|
2225
|
-
...setLastType(scope)
|
2226
|
-
],
|
2227
|
-
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
|
2228
|
-
})
|
2283
|
+
knownThis = [
|
2284
|
+
[ Opcodes.local_get, thisLocal ],
|
2285
|
+
[ Opcodes.local_get, thisLocalType ]
|
2229
2286
|
];
|
2230
|
-
|
2231
|
-
|
2232
|
-
// hi, I will now explain how vararg mode works:
|
2233
|
-
// wasm's indirect_call instruction requires you know the func type at compile-time
|
2234
|
-
// since we have varargs (variable argument count), we do not know it.
|
2235
|
-
// we could just store args in memory and not use wasm func args,
|
2236
|
-
// but that is slow (probably) and breaks js exports.
|
2237
|
-
// instead, we generate every* possibility of argc and use different indirect_call
|
2238
|
-
// ops for each one, with type depending on argc for that branch.
|
2239
|
-
// then we load the argc for the wanted function from a memory lut,
|
2240
|
-
// and call the branch with the matching argc we require.
|
2241
|
-
// sorry, yes it is very cursed (and size inefficient), but indirect calls
|
2242
|
-
// are kind of rare anyway (mostly callbacks) so I am not concerned atm.
|
2243
|
-
// *for argc 0-3, in future (todo:) the max number should be
|
2244
|
-
// dynamically changed to the max argc of any func in the js file.
|
2245
|
-
|
2246
|
-
const funcLocal = localTmp(scope, '#indirect_func', Valtype.i32);
|
2247
|
-
const flags = localTmp(scope, '#indirect_flags', Valtype.i32);
|
2248
|
-
|
2249
|
-
let knownThis = undefined;
|
2250
|
-
let getCalleeObj = undefined;
|
2251
|
-
let initCalleeObj = undefined;
|
2252
|
-
|
2253
|
-
// hack: this should be more thorough, Function.bind, etc
|
2254
|
-
if (decl.callee.type == 'MemberExpression' && !decl._new) {
|
2255
|
-
const callee = localTmp(scope, '#indirect_callee_obj', Valtype.f64);
|
2256
|
-
initCalleeObj = [
|
2287
|
+
getCallee = [
|
2257
2288
|
...generate(scope, decl.callee.object),
|
2258
|
-
[ Opcodes.local_set,
|
2259
|
-
|
2260
|
-
|
2261
|
-
|
2262
|
-
|
2263
|
-
|
2264
|
-
|
2265
|
-
|
2289
|
+
[ Opcodes.local_set, thisLocal ],
|
2290
|
+
...getNodeType(scope, decl.callee.object),
|
2291
|
+
[ Opcodes.local_set, thisLocalType ],
|
2292
|
+
|
2293
|
+
...generate(scope, {
|
2294
|
+
type: 'MemberExpression',
|
2295
|
+
object: { type: 'Identifier', name: '#indirect_caller' },
|
2296
|
+
property: decl.callee.property,
|
2297
|
+
computed: decl.callee.computed,
|
2298
|
+
optional: decl.callee.optional
|
2299
|
+
})
|
2266
2300
|
];
|
2267
2301
|
}
|
2268
2302
|
|
@@ -2278,152 +2312,29 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
2278
2312
|
}
|
2279
2313
|
|
2280
2314
|
const newTargetWasm = decl._newTargetWasm ?? createNewTarget(scope, decl, [
|
2281
|
-
[ Opcodes.local_get,
|
2282
|
-
Opcodes.i32_from_u
|
2315
|
+
[ Opcodes.local_get, calleeLocal ]
|
2283
2316
|
], callAsNew);
|
2284
|
-
const thisWasm = knownThis ?? createThisArg(scope, decl);
|
2285
|
-
|
2286
|
-
const gen = argc => {
|
2287
|
-
const argsOut = [];
|
2288
|
-
for (let i = 0; i < argc; i++) {
|
2289
|
-
argsOut.push(
|
2290
|
-
[ Opcodes.local_get, locals[i][0] ],
|
2291
|
-
[ Opcodes.local_get, locals[i][1] ]
|
2292
|
-
);
|
2293
|
-
}
|
2294
|
-
|
2295
|
-
const checkFlag = (flag, pass, fail) => [
|
2296
|
-
[ Opcodes.local_get, flags ],
|
2297
|
-
...number(flag, Valtype.i32),
|
2298
|
-
[ Opcodes.i32_and ],
|
2299
|
-
[ Opcodes.if, valtypeBinary ],
|
2300
|
-
...pass,
|
2301
|
-
[ Opcodes.else ],
|
2302
|
-
...fail,
|
2303
|
-
[ Opcodes.end ]
|
2304
|
-
];
|
2305
|
-
|
2306
|
-
// todo: i'm sure this could be made better somehow, probably only with #96?
|
2307
|
-
return checkFlag(0b1,
|
2308
|
-
// no type return
|
2309
|
-
checkFlag(0b10, [
|
2310
|
-
// no type return & constr
|
2311
|
-
...newTargetWasm,
|
2312
|
-
...thisWasm,
|
2313
|
-
...argsOut,
|
2314
|
-
[ Opcodes.local_get, funcLocal ],
|
2315
|
-
[ Opcodes.call_indirect, argc + 2, 0, 'no_type_return' ],
|
2316
|
-
], [
|
2317
|
-
// no type return & not constr
|
2318
|
-
...argsOut,
|
2319
|
-
[ Opcodes.local_get, funcLocal ],
|
2320
|
-
[ Opcodes.call_indirect, argc, 0, 'no_type_return' ]
|
2321
|
-
]),
|
2322
|
-
|
2323
|
-
// type return
|
2324
|
-
checkFlag(0b10, [
|
2325
|
-
// type return & constr
|
2326
|
-
...newTargetWasm,
|
2327
|
-
...thisWasm,
|
2328
|
-
...argsOut,
|
2329
|
-
[ Opcodes.local_get, funcLocal ],
|
2330
|
-
[ Opcodes.call_indirect, argc + 2, 0 ],
|
2331
|
-
...setLastType(scope),
|
2332
|
-
], [
|
2333
|
-
// type return
|
2334
|
-
...argsOut,
|
2335
|
-
[ Opcodes.local_get, funcLocal ],
|
2336
|
-
[ Opcodes.call_indirect, argc, 0 ],
|
2337
|
-
...setLastType(scope),
|
2338
|
-
])
|
2339
|
-
);
|
2340
|
-
};
|
2341
|
-
|
2342
|
-
const tableBc = {};
|
2343
|
-
for (let i = 0; i <= args.length; i++) {
|
2344
|
-
tableBc[i] = gen(i);
|
2345
|
-
}
|
2346
|
-
|
2347
|
-
// todo/perf: check if we should use br_table here or just generate our own big if..elses
|
2317
|
+
const thisWasm = decl._thisWasm ?? knownThis ?? createThisArg(scope, decl);
|
2348
2318
|
|
2349
2319
|
return [
|
2350
|
-
...(
|
2351
|
-
|
2352
|
-
...generate(scope, callee, false, undefined, getCalleeObj)
|
2353
|
-
]: generate(scope, callee)),
|
2354
|
-
[ Opcodes.local_set, localTmp(scope, '#indirect_callee') ],
|
2320
|
+
...(getCallee ? getCallee : generate(scope, callee)),
|
2321
|
+
[ Opcodes.local_set, calleeLocal ],
|
2355
2322
|
|
2356
2323
|
...typeSwitch(scope, getNodeType(scope, callee), {
|
2357
2324
|
[TYPES.function]: () => [
|
2325
|
+
...forceDuoValtype(scope, newTargetWasm, Valtype.f64),
|
2326
|
+
...forceDuoValtype(scope, thisWasm, Valtype.f64),
|
2358
2327
|
...out,
|
2359
2328
|
|
2360
|
-
[ Opcodes.local_get,
|
2329
|
+
[ Opcodes.local_get, calleeLocal ],
|
2361
2330
|
Opcodes.i32_to_u,
|
2362
|
-
[ Opcodes.
|
2363
|
-
|
2364
|
-
// get if func we are calling is a constructor or not
|
2365
|
-
[ Opcodes.local_get, funcLocal ],
|
2366
|
-
...number(64, Valtype.i32),
|
2367
|
-
[ Opcodes.i32_mul ],
|
2368
|
-
...number(4, Valtype.i32),
|
2369
|
-
[ Opcodes.i32_add ],
|
2370
|
-
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(allocPage(scope, 'func lut')), 'read func lut' ],
|
2371
|
-
[ Opcodes.local_set, flags ],
|
2372
|
-
|
2373
|
-
// check if non-constructor was called with new, if so throw
|
2374
|
-
...(callAsNew ? [
|
2375
|
-
[ Opcodes.local_get, flags ],
|
2376
|
-
...number(0b10, Valtype.i32),
|
2377
|
-
[ Opcodes.i32_and ],
|
2378
|
-
[ Opcodes.i32_eqz ],
|
2379
|
-
[ Opcodes.if, Blocktype.void ],
|
2380
|
-
...internalThrow(scope, 'TypeError', `${unhackName(name)} is not a constructor`),
|
2381
|
-
[ Opcodes.end ],
|
2382
|
-
] : []),
|
2383
|
-
|
2384
|
-
// [ Opcodes.local_get, funcLocal ],
|
2385
|
-
// Opcodes.i32_from_u,
|
2386
|
-
// [ Opcodes.call, 0 ],
|
2387
|
-
|
2388
|
-
// ...number(32),
|
2389
|
-
// [ Opcodes.call, 1 ],
|
2390
|
-
|
2391
|
-
// [ Opcodes.local_get, funcLocal ],
|
2392
|
-
// ...number(64, Valtype.i32),
|
2393
|
-
// [ Opcodes.i32_mul ],
|
2394
|
-
// [ Opcodes.i32_load16_u, 0, ...unsignedLEB128(allocPage(scope, 'func lut')), 'read func lut' ],
|
2395
|
-
// Opcodes.i32_from_u,
|
2396
|
-
// [ Opcodes.call, 0 ],
|
2397
|
-
|
2398
|
-
// [ Opcodes.local_get, funcLocal ],
|
2399
|
-
// [ Opcodes.call, includeBuiltin(scope, '__Porffor_funcLut_name').index ],
|
2400
|
-
// Opcodes.i32_from_u,
|
2401
|
-
// ...number(TYPES.bytestring, Valtype.i32),
|
2402
|
-
// [ Opcodes.call, includeBuiltin(scope, '__Porffor_printString').index ],
|
2403
|
-
// [ Opcodes.drop ],
|
2404
|
-
// [ Opcodes.drop ],
|
2405
|
-
|
2406
|
-
// ...number(10),
|
2407
|
-
// [ Opcodes.call, 1 ],
|
2408
|
-
|
2409
|
-
...brTable([
|
2410
|
-
// get argc of func we are calling
|
2411
|
-
[ Opcodes.local_get, funcLocal ],
|
2412
|
-
...number(64, Valtype.i32),
|
2413
|
-
[ Opcodes.i32_mul ],
|
2414
|
-
[ Opcodes.i32_load16_u, 0, ...unsignedLEB128(allocPage(scope, 'func lut')), 'read func lut' ]
|
2415
|
-
], tableBc, valtypeBinary)
|
2331
|
+
[ Opcodes.call_indirect, args.length + 2, 0 ],
|
2332
|
+
...setLastType(scope)
|
2416
2333
|
],
|
2417
2334
|
|
2418
|
-
|
2419
|
-
|
2420
|
-
|
2421
|
-
...setLastType(scope, TYPES.undefined)
|
2422
|
-
]
|
2423
|
-
} : {}),
|
2424
|
-
|
2425
|
-
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
|
2426
|
-
})
|
2335
|
+
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, Valtype.f64)
|
2336
|
+
}, Valtype.f64),
|
2337
|
+
...(valtypeBinary === Valtype.i32 && scope.returns[0] !== Valtype.f64 ? [ [ Opcodes.f64_convert_i32_s ] ] : [])
|
2427
2338
|
];
|
2428
2339
|
}
|
2429
2340
|
|
@@ -2455,16 +2366,12 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
2455
2366
|
paramOffset += 4;
|
2456
2367
|
}
|
2457
2368
|
|
2458
|
-
if (func &&
|
2369
|
+
if (func && args.length < paramCount) {
|
2459
2370
|
// too little args, push undefineds
|
2460
|
-
args = args.concat(new Array(paramCount - args.length).fill(DEFAULT_VALUE()));
|
2371
|
+
args = args.concat(new Array(paramCount - (func.hasRestArgument ? 1 : 0) - args.length).fill(DEFAULT_VALUE()));
|
2461
2372
|
}
|
2462
2373
|
|
2463
2374
|
if (func && func.hasRestArgument) {
|
2464
|
-
if (args.length < paramCount) {
|
2465
|
-
args = args.concat(new Array(paramCount - 1 - args.length).fill(DEFAULT_VALUE()));
|
2466
|
-
}
|
2467
|
-
|
2468
2375
|
const restArgs = args.slice(paramCount - 1);
|
2469
2376
|
args = args.slice(0, paramCount - 1);
|
2470
2377
|
args.push({
|
@@ -2478,13 +2385,18 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
2478
2385
|
args = args.slice(0, paramCount);
|
2479
2386
|
}
|
2480
2387
|
|
2481
|
-
if (func && func.throws) scope.throws = true;
|
2482
|
-
|
2483
2388
|
for (let i = 0; i < args.length; i++) {
|
2484
2389
|
const arg = args[i];
|
2485
2390
|
if (Array.isArray(arg)) {
|
2486
2391
|
// if wasm, just append it
|
2487
2392
|
out = out.concat(arg);
|
2393
|
+
|
2394
|
+
if (valtypeBinary !== Valtype.i32 &&
|
2395
|
+
(func && func.params[paramOffset + i * (typedParams ? 2 : 1)] === Valtype.i32)
|
2396
|
+
) {
|
2397
|
+
out.push(...forceDuoValtype(scope, [], Valtype.i32));
|
2398
|
+
}
|
2399
|
+
|
2488
2400
|
continue;
|
2489
2401
|
}
|
2490
2402
|
|
@@ -3755,8 +3667,11 @@ const generateUnary = (scope, decl) => {
|
|
3755
3667
|
case 'delete': {
|
3756
3668
|
if (decl.argument.type === 'MemberExpression') {
|
3757
3669
|
const object = decl.argument.object;
|
3758
|
-
const property = getProperty(decl.argument);
|
3759
3670
|
|
3671
|
+
// disallow `delete super.*`
|
3672
|
+
if (object.type === 'Super') return internalThrow(scope, 'ReferenceError', 'Cannot delete super property', true);
|
3673
|
+
|
3674
|
+
const property = getProperty(decl.argument);
|
3760
3675
|
if (property.value === 'length' || property.value === 'name') scope.noFastFuncMembers = true;
|
3761
3676
|
|
3762
3677
|
return [
|
@@ -4690,8 +4605,6 @@ const generateLabel = (scope, decl) => {
|
|
4690
4605
|
};
|
4691
4606
|
|
4692
4607
|
const generateThrow = (scope, decl) => {
|
4693
|
-
scope.throws = true;
|
4694
|
-
|
4695
4608
|
let exceptionMode = Prefs.exceptionMode ?? 'stack';
|
4696
4609
|
if (globalThis.precompile) exceptionMode = decl.argument.callee != null ? 'lut' : 'stack';
|
4697
4610
|
|
@@ -5149,11 +5062,7 @@ const toPropertyKey = (scope, wasm, type, computed = false, i32Conv = false) =>
|
|
5149
5062
|
...wasm,
|
5150
5063
|
...type,
|
5151
5064
|
[ Opcodes.call, includeBuiltin(scope, '__ecma262_ToPropertyKey').index ],
|
5152
|
-
...(i32Conv ? [
|
5153
|
-
[ Opcodes.local_set, localTmp(scope, '#swap', Valtype.i32) ],
|
5154
|
-
Opcodes.i32_to_u,
|
5155
|
-
[ Opcodes.local_get, localTmp(scope, '#swap', Valtype.i32) ]
|
5156
|
-
] : [])
|
5065
|
+
...(i32Conv ? forceDuoValtype(scope, [], Valtype.i32) : [])
|
5157
5066
|
] : [
|
5158
5067
|
...wasm,
|
5159
5068
|
...(i32Conv ? [ Opcodes.i32_to_u ] : []),
|
@@ -5288,18 +5197,17 @@ const countLength = (func, name = undefined) => {
|
|
5288
5197
|
|
5289
5198
|
const generateMember = (scope, decl, _global, _name, _objectWasm = undefined) => {
|
5290
5199
|
let final = [], finalEnd, extraBC = {};
|
5291
|
-
|
5200
|
+
let name = decl.object.name;
|
5292
5201
|
|
5293
5202
|
// todo: handle globalThis.foo
|
5294
5203
|
|
5295
5204
|
// hack: .name
|
5296
5205
|
if (decl.property.name === 'name' && hasFuncWithName(name) && !scope.noFastFuncMembers) {
|
5297
|
-
let nameProp = name;
|
5298
|
-
|
5299
5206
|
// eg: __String_prototype_toLowerCase -> toLowerCase
|
5300
|
-
if (
|
5207
|
+
if (name.startsWith('__')) name = name.split('_').pop();
|
5208
|
+
if (name.startsWith('#')) name = '';
|
5301
5209
|
|
5302
|
-
return withType(scope, makeString(scope,
|
5210
|
+
return withType(scope, makeString(scope, name, _global, _name, true), TYPES.bytestring);
|
5303
5211
|
}
|
5304
5212
|
|
5305
5213
|
const object = decl.object;
|
@@ -5713,16 +5621,7 @@ const generateClass = (scope, decl) => {
|
|
5713
5621
|
type: 'Identifier',
|
5714
5622
|
name
|
5715
5623
|
};
|
5716
|
-
const proto =
|
5717
|
-
type: 'MemberExpression',
|
5718
|
-
object: root,
|
5719
|
-
property: {
|
5720
|
-
type: 'Identifier',
|
5721
|
-
name: 'prototype'
|
5722
|
-
},
|
5723
|
-
computed: false,
|
5724
|
-
optional: false
|
5725
|
-
};
|
5624
|
+
const proto = getObjProp(root, 'prototype');
|
5726
5625
|
|
5727
5626
|
const [ func, out ] = generateFunc(scope, {
|
5728
5627
|
...(body.find(x => x.kind === 'constructor')?.value ?? {
|
@@ -5749,7 +5648,7 @@ const generateClass = (scope, decl) => {
|
|
5749
5648
|
// Bar.__proto__ = Foo
|
5750
5649
|
// Bar.prototype.__proto__ = Foo.prototype
|
5751
5650
|
...generate(scope, setObjProp(root, '__proto__', decl.superClass)),
|
5752
|
-
...generate(scope, setObjProp(
|
5651
|
+
...generate(scope, setObjProp(proto, '__proto__', getObjProp(decl.superClass, 'prototype')))
|
5753
5652
|
);
|
5754
5653
|
}
|
5755
5654
|
|
@@ -6278,6 +6177,18 @@ const generateFunc = (scope, decl) => {
|
|
6278
6177
|
func.params = Object.values(func.locals).map(x => x.type);
|
6279
6178
|
func.jsLength = jsLength;
|
6280
6179
|
|
6180
|
+
if (globalThis.valtypeOverrides) {
|
6181
|
+
if (globalThis.valtypeOverrides.returns[name]) func.returns = globalThis.valtypeOverrides.returns[name];
|
6182
|
+
if (globalThis.valtypeOverrides.params[name]) {
|
6183
|
+
func.params = globalThis.valtypeOverrides.params[name];
|
6184
|
+
|
6185
|
+
const localsVals = Object.values(func.locals);
|
6186
|
+
for (let i = 0; i < func.params.length; i++) {
|
6187
|
+
localsVals[i].type = func.params[i];
|
6188
|
+
}
|
6189
|
+
}
|
6190
|
+
}
|
6191
|
+
|
6281
6192
|
// force generate for main
|
6282
6193
|
if (name === 'main') func.generate();
|
6283
6194
|
|
@@ -6420,7 +6331,7 @@ export default program => {
|
|
6420
6331
|
};
|
6421
6332
|
tags = [];
|
6422
6333
|
exceptions = [];
|
6423
|
-
funcs = [];
|
6334
|
+
funcs = []; indirectFuncs = [];
|
6424
6335
|
funcIndex = {};
|
6425
6336
|
depth = [];
|
6426
6337
|
pages = new Map();
|
@@ -6475,6 +6386,7 @@ export default program => {
|
|
6475
6386
|
// todo: these should just be deleted once able
|
6476
6387
|
for (let i = 0; i < funcs.length; i++) {
|
6477
6388
|
const f = funcs[i];
|
6389
|
+
|
6478
6390
|
if (f.wasm) {
|
6479
6391
|
// run callbacks
|
6480
6392
|
const wasm = f.wasm;
|
@@ -6531,9 +6443,15 @@ export default program => {
|
|
6531
6443
|
// }
|
6532
6444
|
// }
|
6533
6445
|
|
6534
|
-
|
6446
|
+
// add indirect funcs to end of funcs
|
6447
|
+
for (let i = 0; i < indirectFuncs.length; i++) {
|
6448
|
+
const f = indirectFuncs[i];
|
6449
|
+
f.index = currentFuncIndex++;
|
6450
|
+
}
|
6451
|
+
|
6452
|
+
funcs.push(...indirectFuncs);
|
6535
6453
|
|
6536
|
-
|
6454
|
+
delete globals['#ind'];
|
6537
6455
|
|
6538
6456
|
return { funcs, globals, tags, exceptions, pages, data };
|
6539
6457
|
};
|
package/compiler/precompile.js
CHANGED
@@ -13,6 +13,26 @@ const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
13
13
|
globalThis.precompileCompilerPath = __dirname;
|
14
14
|
globalThis.precompile = true;
|
15
15
|
|
16
|
+
globalThis.valtypeOverrides = {
|
17
|
+
returns: {
|
18
|
+
__Porffor_object_get: [ Valtype.f64, Valtype.i32 ],
|
19
|
+
__Porffor_object_getExplicit: [ Valtype.f64, Valtype.i32 ],
|
20
|
+
__Porffor_object_readValue: [ Valtype.f64, Valtype.i32 ],
|
21
|
+
__Porffor_object_set: [ Valtype.f64, Valtype.i32 ],
|
22
|
+
__Porffor_object_setStrict: [ Valtype.f64, Valtype.i32 ],
|
23
|
+
__Porffor_object_packAccessor: [ Valtype.f64, Valtype.i32 ]
|
24
|
+
},
|
25
|
+
params: {
|
26
|
+
__Porffor_object_set: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
|
27
|
+
__Porffor_object_setStrict: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
|
28
|
+
__Porffor_object_expr_init: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
|
29
|
+
__Porffor_object_expr_initWithFlags: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32, Valtype.i32, Valtype.i32 ],
|
30
|
+
__Porffor_object_class_value: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
|
31
|
+
__Porffor_object_class_method: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
|
32
|
+
__Porffor_object_define: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32, Valtype.i32, Valtype.i32 ],
|
33
|
+
}
|
34
|
+
};
|
35
|
+
|
16
36
|
const argv = process.argv.slice();
|
17
37
|
|
18
38
|
const timing = {};
|
@@ -50,25 +70,6 @@ const compile = async (file, _funcs) => {
|
|
50
70
|
return acc;
|
51
71
|
}, {});
|
52
72
|
|
53
|
-
const returnOverrides = {
|
54
|
-
__Porffor_object_get: [ Valtype.f64, Valtype.i32 ],
|
55
|
-
__Porffor_object_getExplicit: [ Valtype.f64, Valtype.i32 ],
|
56
|
-
__Porffor_object_readValue: [ Valtype.f64, Valtype.i32 ],
|
57
|
-
__Porffor_object_set: [ Valtype.f64, Valtype.i32 ],
|
58
|
-
__Porffor_object_setStrict: [ Valtype.f64, Valtype.i32 ],
|
59
|
-
__Porffor_object_packAccessor: [ Valtype.f64, Valtype.i32 ]
|
60
|
-
};
|
61
|
-
|
62
|
-
const paramOverrides = {
|
63
|
-
__Porffor_object_set: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
|
64
|
-
__Porffor_object_setStrict: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
|
65
|
-
__Porffor_object_expr_init: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
|
66
|
-
__Porffor_object_expr_initWithFlags: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32, Valtype.i32, Valtype.i32 ],
|
67
|
-
__Porffor_object_class_value: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
|
68
|
-
__Porffor_object_class_method: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
|
69
|
-
__Porffor_object_define: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32, Valtype.i32, Valtype.i32 ],
|
70
|
-
};
|
71
|
-
|
72
73
|
const main = funcs.find(x => x.name === 'main');
|
73
74
|
const exports = funcs.filter(x => x.export && x.name !== 'main');
|
74
75
|
for (const x of exports) {
|
@@ -84,9 +85,6 @@ const compile = async (file, _funcs) => {
|
|
84
85
|
}).filter(x => x);
|
85
86
|
}
|
86
87
|
|
87
|
-
if (returnOverrides[x.name]) x.returns = returnOverrides[x.name];
|
88
|
-
if (paramOverrides[x.name]) x.params = paramOverrides[x.name];
|
89
|
-
|
90
88
|
const rewriteWasm = (x, wasm, rewriteLocals = false) => {
|
91
89
|
const locals = Object.keys(x.locals).reduce((acc, y) => {
|
92
90
|
acc[x.locals[y].idx] = { ...x.locals[y], name: y };
|
package/package.json
CHANGED
package/runner/index.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#!/usr/bin/env node
|
2
2
|
import fs from 'node:fs';
|
3
|
-
globalThis.version = '0.
|
3
|
+
globalThis.version = '0.42.0+93da2c806';
|
4
4
|
|
5
5
|
// deno compat
|
6
6
|
if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
|
@@ -167,7 +167,7 @@ try {
|
|
167
167
|
}
|
168
168
|
} catch (e) {
|
169
169
|
let out = e;
|
170
|
-
if (!process.argv.includes('-d') && Object.getPrototypeOf(e).message != null) out = `${e.
|
170
|
+
if (!process.argv.includes('-d') && Object.getPrototypeOf(e).message != null) out = `${e.name}${e.message != null ? `: ${e.message}` : ''}`;
|
171
171
|
console.error(out);
|
172
172
|
}
|
173
173
|
|