porffor 0.41.5 → 0.41.7
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 +13 -14
- package/compiler/builtins/__internal_object.ts +12 -6
- package/compiler/builtins/_internal_object.ts +96 -147
- package/compiler/builtins/object.ts +39 -21
- package/compiler/builtins.js +9 -71
- package/compiler/builtins_objects.js +10 -1
- package/compiler/builtins_precompiled.js +340 -336
- package/compiler/codegen.js +179 -228
- package/compiler/precompile.js +37 -23
- package/package.json +1 -1
- package/runner/index.js +2 -2
package/compiler/codegen.js
CHANGED
@@ -49,19 +49,81 @@ const cacheAst = (decl, wasm) => {
|
|
49
49
|
};
|
50
50
|
|
51
51
|
const funcRef = func => {
|
52
|
-
// generate func
|
53
52
|
func.generate?.();
|
54
53
|
|
55
54
|
if (globalThis.precompile) return [
|
56
55
|
[ Opcodes.const, 'funcref', func.name ]
|
57
56
|
];
|
58
57
|
|
58
|
+
const wrapperArgc = Prefs.indirectWrapperArgc ?? 6;
|
59
|
+
if (func.returns.length === 2 && countParams(func) < wrapperArgc && !func.wrapperFunc) {
|
60
|
+
const locals = {}, params = [];
|
61
|
+
for (let i = 0; i < wrapperArgc + (func.constr ? 2 : 0); i++) {
|
62
|
+
params.push(valtypeBinary, Valtype.i32);
|
63
|
+
locals[i * 2] = { idx: i * 2, type: valtypeBinary };
|
64
|
+
locals[i * 2 + 1] = { idx: i * 2 + 1, type: Valtype.i32 };
|
65
|
+
}
|
66
|
+
let localInd = (wrapperArgc + (func.constr ? 2 : 0)) * 2;
|
67
|
+
|
68
|
+
const wasm = [];
|
69
|
+
for (let i = 0; i < func.params.length; i++) {
|
70
|
+
wasm.push(
|
71
|
+
[ Opcodes.local_get, i ],
|
72
|
+
...(i % 2 === 0 && func.params[i] === Valtype.i32 ? [ Opcodes.i32_to ]: [])
|
73
|
+
);
|
74
|
+
}
|
75
|
+
|
76
|
+
wasm.push([ Opcodes.call, func.index ]);
|
77
|
+
|
78
|
+
if (func.returns[0] === Valtype.i32) {
|
79
|
+
const localIdx = localInd++;
|
80
|
+
locals[localIdx] = { idx: localIdx, type: Valtype.i32 };
|
81
|
+
|
82
|
+
wasm.push(
|
83
|
+
[ Opcodes.local_set, localIdx ],
|
84
|
+
Opcodes.i32_from,
|
85
|
+
[ Opcodes.local_get, localIdx ]
|
86
|
+
);
|
87
|
+
}
|
88
|
+
|
89
|
+
const name = '#indirect_' + func.name;
|
90
|
+
const wrapperFunc = {
|
91
|
+
name,
|
92
|
+
params,
|
93
|
+
locals, localInd,
|
94
|
+
returns: [ valtypeBinary, Valtype.i32 ],
|
95
|
+
wasm,
|
96
|
+
constr: func.constr,
|
97
|
+
internal: true,
|
98
|
+
index: currentFuncIndex++
|
99
|
+
};
|
100
|
+
|
101
|
+
funcs.push(wrapperFunc);
|
102
|
+
funcIndex[name] = wrapperFunc.index;
|
103
|
+
|
104
|
+
wrapperFunc.jsLength = countLength(func);
|
105
|
+
func.wrapperFunc = wrapperFunc;
|
106
|
+
}
|
107
|
+
|
59
108
|
return [
|
60
109
|
[ Opcodes.const, func.index - importedFuncs.length ]
|
61
|
-
// [ Opcodes.const, func.index - importedFuncs.length, 'funcref' ]
|
62
110
|
];
|
63
111
|
};
|
64
112
|
|
113
|
+
const forceDuoValtype = (scope, wasm, forceValtype) => [
|
114
|
+
...wasm,
|
115
|
+
...(valtypeBinary === Valtype.i32 && forceValtype === Valtype.f64 ? [
|
116
|
+
[ Opcodes.local_set, localTmp(scope, '#swap', Valtype.i32) ],
|
117
|
+
[ Opcodes.f64_convert_i32_s ],
|
118
|
+
[ Opcodes.local_get, localTmp(scope, '#swap', Valtype.i32) ]
|
119
|
+
] : []),
|
120
|
+
...(valtypeBinary === Valtype.f64 && forceValtype === Valtype.i32 ? [
|
121
|
+
[ Opcodes.local_set, localTmp(scope, '#swap', Valtype.i32) ],
|
122
|
+
Opcodes.i32_trunc_sat_f64_s,
|
123
|
+
[ Opcodes.local_get, localTmp(scope, '#swap', Valtype.i32) ]
|
124
|
+
] : [])
|
125
|
+
];
|
126
|
+
|
65
127
|
const generate = (scope, decl, global = false, name = undefined, valueUnused = false) => {
|
66
128
|
if (astCache.has(decl)) return astCache.get(decl);
|
67
129
|
|
@@ -245,7 +307,7 @@ const internalThrow = (scope, constructor, message, expectsValue = Prefs.alwaysV
|
|
245
307
|
]
|
246
308
|
}
|
247
309
|
}),
|
248
|
-
...(expectsValue ? number(UNDEFINED) : [])
|
310
|
+
...(expectsValue ? number(UNDEFINED, typeof expectsValue === 'number' ? expectsValue : valtypeBinary) : [])
|
249
311
|
];
|
250
312
|
|
251
313
|
const generateIdent = (scope, decl) => {
|
@@ -893,7 +955,7 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
893
955
|
|
894
956
|
const asmFuncToAsm = (scope, func) => {
|
895
957
|
return func(scope, {
|
896
|
-
TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage, internalThrow,
|
958
|
+
Valtype, Opcodes, TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage, internalThrow,
|
897
959
|
getNodeType, generate, generateIdent,
|
898
960
|
builtin: (n, offset = false) => {
|
899
961
|
let idx = funcIndex[n] ?? importedFuncs[n];
|
@@ -910,6 +972,7 @@ const asmFuncToAsm = (scope, func) => {
|
|
910
972
|
|
911
973
|
return idx;
|
912
974
|
},
|
975
|
+
hasFunc: x => funcIndex[x] != null,
|
913
976
|
funcRef: name => {
|
914
977
|
if (funcIndex[name] == null && builtinFuncs[name]) {
|
915
978
|
includeBuiltin(scope, name);
|
@@ -2166,102 +2229,45 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
2166
2229
|
} else {
|
2167
2230
|
if (!Prefs.indirectCalls) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
|
2168
2231
|
|
2169
|
-
// todo: only works when function uses typedParams and typedReturns
|
2170
|
-
|
2171
|
-
const indirectMode = Prefs.indirectCallMode ?? 'vararg';
|
2172
|
-
// options: vararg, strict
|
2173
|
-
// - strict: simpler, smaller size usage, no func lut needed.
|
2174
|
-
// ONLY works when arg count of call == arg count of function being called
|
2175
|
-
// - vararg: large size usage, cursed.
|
2176
|
-
// works when arg count of call != arg count of function being called*
|
2177
|
-
// * most of the time, some edgecases
|
2178
|
-
|
2179
2232
|
funcs.table = true;
|
2180
2233
|
scope.table = true;
|
2181
2234
|
|
2182
2235
|
let args = decl.arguments;
|
2183
|
-
|
2184
|
-
|
2185
|
-
|
2186
|
-
const minArgc = Prefs.indirectCallMinArgc ?? 5;
|
2187
|
-
|
2188
|
-
if (args.length < minArgc) {
|
2189
|
-
args = args.concat(new Array(minArgc - args.length).fill(DEFAULT_VALUE()));
|
2190
|
-
}
|
2236
|
+
const wrapperArgc = Prefs.indirectWrapperArgc ?? 6;
|
2237
|
+
if (args.length < wrapperArgc) {
|
2238
|
+
args = args.concat(new Array(wrapperArgc - args.length).fill(DEFAULT_VALUE()));
|
2191
2239
|
}
|
2192
2240
|
|
2193
2241
|
for (let i = 0; i < args.length; i++) {
|
2194
2242
|
const arg = args[i];
|
2195
|
-
out = out.concat(generate(scope, arg));
|
2196
|
-
out = out.concat(getNodeType(scope, arg));
|
2197
|
-
|
2198
|
-
if (indirectMode === 'vararg') {
|
2199
|
-
const typeLocal = localTmp(scope, `#indirect_arg${i}_type`, Valtype.i32);
|
2200
|
-
const valLocal = localTmp(scope, `#indirect_arg${i}_val`);
|
2201
|
-
|
2202
|
-
locals.push([valLocal, typeLocal]);
|
2203
|
-
|
2204
|
-
out.push(
|
2205
|
-
[ Opcodes.local_set, typeLocal ],
|
2206
|
-
[ Opcodes.local_set, valLocal ]
|
2207
|
-
);
|
2208
|
-
}
|
2243
|
+
out = out.concat(generate(scope, arg), valtypeBinary === Valtype.i32 && scope.locals[arg.name]?.type !== Valtype.f64 ? [ [ Opcodes.f64_convert_i32_s ] ] : [], getNodeType(scope, arg));
|
2209
2244
|
}
|
2210
2245
|
|
2211
|
-
|
2212
|
-
|
2213
|
-
...generate(scope, decl.callee),
|
2214
|
-
[ Opcodes.local_set, localTmp(scope, '#indirect_callee') ],
|
2246
|
+
let knownThis = undefined, getCallee = undefined;
|
2247
|
+
const calleeLocal = localTmp(scope, '#indirect_callee');
|
2215
2248
|
|
2216
|
-
|
2217
|
-
|
2218
|
-
|
2249
|
+
// hack: this should be more thorough, Function.bind, etc
|
2250
|
+
if (decl.callee.type === 'MemberExpression' && !decl._new) {
|
2251
|
+
const thisLocal = localTmp(scope, '#indirect_caller');
|
2252
|
+
const thisLocalType = localTmp(scope, '#indirect_caller#type', Valtype.i32);
|
2219
2253
|
|
2220
|
-
|
2221
|
-
|
2222
|
-
|
2223
|
-
[ Opcodes.call_indirect, args.length, 0 ],
|
2224
|
-
...setLastType(scope)
|
2225
|
-
],
|
2226
|
-
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
|
2227
|
-
})
|
2254
|
+
knownThis = [
|
2255
|
+
[ Opcodes.local_get, thisLocal ],
|
2256
|
+
[ Opcodes.local_get, thisLocalType ]
|
2228
2257
|
];
|
2229
|
-
|
2230
|
-
|
2231
|
-
// hi, I will now explain how vararg mode works:
|
2232
|
-
// wasm's indirect_call instruction requires you know the func type at compile-time
|
2233
|
-
// since we have varargs (variable argument count), we do not know it.
|
2234
|
-
// we could just store args in memory and not use wasm func args,
|
2235
|
-
// but that is slow (probably) and breaks js exports.
|
2236
|
-
// instead, we generate every* possibility of argc and use different indirect_call
|
2237
|
-
// ops for each one, with type depending on argc for that branch.
|
2238
|
-
// then we load the argc for the wanted function from a memory lut,
|
2239
|
-
// and call the branch with the matching argc we require.
|
2240
|
-
// sorry, yes it is very cursed (and size inefficient), but indirect calls
|
2241
|
-
// are kind of rare anyway (mostly callbacks) so I am not concerned atm.
|
2242
|
-
// *for argc 0-3, in future (todo:) the max number should be
|
2243
|
-
// dynamically changed to the max argc of any func in the js file.
|
2244
|
-
|
2245
|
-
const funcLocal = localTmp(scope, '#indirect_func', Valtype.i32);
|
2246
|
-
const flags = localTmp(scope, '#indirect_flags', Valtype.i32);
|
2247
|
-
|
2248
|
-
let knownThis = undefined;
|
2249
|
-
let getCalleeObj = undefined;
|
2250
|
-
let initCalleeObj = undefined;
|
2251
|
-
|
2252
|
-
// hack: this should be more thorough, Function.bind, etc
|
2253
|
-
if (decl.callee.type == 'MemberExpression' && !decl._new) {
|
2254
|
-
const callee = localTmp(scope, '#indirect_callee_obj', Valtype.f64);
|
2255
|
-
initCalleeObj = [
|
2258
|
+
getCallee = [
|
2256
2259
|
...generate(scope, decl.callee.object),
|
2257
|
-
[ Opcodes.local_set,
|
2258
|
-
|
2259
|
-
|
2260
|
-
|
2261
|
-
|
2262
|
-
|
2263
|
-
|
2264
|
-
|
2260
|
+
[ Opcodes.local_set, thisLocal ],
|
2261
|
+
...getNodeType(scope, decl.callee.object),
|
2262
|
+
[ Opcodes.local_set, thisLocalType ],
|
2263
|
+
|
2264
|
+
...generate(scope, {
|
2265
|
+
type: 'MemberExpression',
|
2266
|
+
object: { type: 'Identifier', name: '#indirect_caller' },
|
2267
|
+
property: decl.callee.property,
|
2268
|
+
computed: decl.callee.computed,
|
2269
|
+
optional: decl.callee.optional
|
2270
|
+
})
|
2265
2271
|
];
|
2266
2272
|
}
|
2267
2273
|
|
@@ -2277,152 +2283,54 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
2277
2283
|
}
|
2278
2284
|
|
2279
2285
|
const newTargetWasm = decl._newTargetWasm ?? createNewTarget(scope, decl, [
|
2280
|
-
[ Opcodes.local_get,
|
2281
|
-
Opcodes.i32_from_u
|
2286
|
+
[ Opcodes.local_get, calleeLocal ]
|
2282
2287
|
], callAsNew);
|
2283
|
-
const thisWasm = knownThis ?? createThisArg(scope, decl);
|
2284
|
-
|
2285
|
-
const gen = argc => {
|
2286
|
-
const argsOut = [];
|
2287
|
-
for (let i = 0; i < argc; i++) {
|
2288
|
-
argsOut.push(
|
2289
|
-
[ Opcodes.local_get, locals[i][0] ],
|
2290
|
-
[ Opcodes.local_get, locals[i][1] ]
|
2291
|
-
);
|
2292
|
-
}
|
2293
|
-
|
2294
|
-
const checkFlag = (flag, pass, fail) => [
|
2295
|
-
[ Opcodes.local_get, flags ],
|
2296
|
-
...number(flag, Valtype.i32),
|
2297
|
-
[ Opcodes.i32_and ],
|
2298
|
-
[ Opcodes.if, valtypeBinary ],
|
2299
|
-
...pass,
|
2300
|
-
[ Opcodes.else ],
|
2301
|
-
...fail,
|
2302
|
-
[ Opcodes.end ]
|
2303
|
-
];
|
2304
|
-
|
2305
|
-
// todo: i'm sure this could be made better somehow, probably only with #96?
|
2306
|
-
return checkFlag(0b1,
|
2307
|
-
// no type return
|
2308
|
-
checkFlag(0b10, [
|
2309
|
-
// no type return & constr
|
2310
|
-
...newTargetWasm,
|
2311
|
-
...thisWasm,
|
2312
|
-
...argsOut,
|
2313
|
-
[ Opcodes.local_get, funcLocal ],
|
2314
|
-
[ Opcodes.call_indirect, argc + 2, 0, 'no_type_return' ],
|
2315
|
-
], [
|
2316
|
-
// no type return & not constr
|
2317
|
-
...argsOut,
|
2318
|
-
[ Opcodes.local_get, funcLocal ],
|
2319
|
-
[ Opcodes.call_indirect, argc, 0, 'no_type_return' ]
|
2320
|
-
]),
|
2321
|
-
|
2322
|
-
// type return
|
2323
|
-
checkFlag(0b10, [
|
2324
|
-
// type return & constr
|
2325
|
-
...newTargetWasm,
|
2326
|
-
...thisWasm,
|
2327
|
-
...argsOut,
|
2328
|
-
[ Opcodes.local_get, funcLocal ],
|
2329
|
-
[ Opcodes.call_indirect, argc + 2, 0 ],
|
2330
|
-
...setLastType(scope),
|
2331
|
-
], [
|
2332
|
-
// type return
|
2333
|
-
...argsOut,
|
2334
|
-
[ Opcodes.local_get, funcLocal ],
|
2335
|
-
[ Opcodes.call_indirect, argc, 0 ],
|
2336
|
-
...setLastType(scope),
|
2337
|
-
])
|
2338
|
-
);
|
2339
|
-
};
|
2340
|
-
|
2341
|
-
const tableBc = {};
|
2342
|
-
for (let i = 0; i <= args.length; i++) {
|
2343
|
-
tableBc[i] = gen(i);
|
2344
|
-
}
|
2345
|
-
|
2346
|
-
// todo/perf: check if we should use br_table here or just generate our own big if..elses
|
2288
|
+
const thisWasm = decl._thisWasm ?? knownThis ?? createThisArg(scope, decl);
|
2347
2289
|
|
2348
2290
|
return [
|
2349
|
-
...(
|
2350
|
-
|
2351
|
-
...generate(scope, callee, false, undefined, getCalleeObj)
|
2352
|
-
]: generate(scope, callee)),
|
2353
|
-
[ Opcodes.local_set, localTmp(scope, '#indirect_callee') ],
|
2291
|
+
...(getCallee ? getCallee : generate(scope, callee)),
|
2292
|
+
[ Opcodes.local_set, calleeLocal ],
|
2354
2293
|
|
2355
2294
|
...typeSwitch(scope, getNodeType(scope, callee), {
|
2356
2295
|
[TYPES.function]: () => [
|
2357
|
-
...out,
|
2358
|
-
|
2359
|
-
[ Opcodes.local_get, localTmp(scope, '#indirect_callee') ],
|
2360
|
-
Opcodes.i32_to_u,
|
2361
|
-
[ Opcodes.local_set, funcLocal ],
|
2362
|
-
|
2363
2296
|
// get if func we are calling is a constructor or not
|
2364
|
-
[ Opcodes.local_get,
|
2365
|
-
|
2297
|
+
[ Opcodes.local_get, calleeLocal ],
|
2298
|
+
Opcodes.i32_to_u,
|
2299
|
+
...number(48, Valtype.i32),
|
2366
2300
|
[ Opcodes.i32_mul ],
|
2367
|
-
...number(
|
2301
|
+
...number(2, Valtype.i32),
|
2368
2302
|
[ Opcodes.i32_add ],
|
2369
2303
|
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(allocPage(scope, 'func lut')), 'read func lut' ],
|
2370
|
-
[ Opcodes.local_set, flags ],
|
2371
|
-
|
2372
|
-
// check if non-constructor was called with new, if so throw
|
2373
|
-
...(callAsNew ? [
|
2374
|
-
[ Opcodes.local_get, flags ],
|
2375
|
-
...number(0b10, Valtype.i32),
|
2376
|
-
[ Opcodes.i32_and ],
|
2377
|
-
[ Opcodes.i32_eqz ],
|
2378
|
-
[ Opcodes.if, Blocktype.void ],
|
2379
|
-
...internalThrow(scope, 'TypeError', `${unhackName(name)} is not a constructor`),
|
2380
|
-
[ Opcodes.end ],
|
2381
|
-
] : []),
|
2382
|
-
|
2383
|
-
// [ Opcodes.local_get, funcLocal ],
|
2384
|
-
// Opcodes.i32_from_u,
|
2385
|
-
// [ Opcodes.call, 0 ],
|
2386
|
-
|
2387
|
-
// ...number(32),
|
2388
|
-
// [ Opcodes.call, 1 ],
|
2389
|
-
|
2390
|
-
// [ Opcodes.local_get, funcLocal ],
|
2391
|
-
// ...number(64, Valtype.i32),
|
2392
|
-
// [ Opcodes.i32_mul ],
|
2393
|
-
// [ Opcodes.i32_load16_u, 0, ...unsignedLEB128(allocPage(scope, 'func lut')), 'read func lut' ],
|
2394
|
-
// Opcodes.i32_from_u,
|
2395
|
-
// [ Opcodes.call, 0 ],
|
2396
|
-
|
2397
|
-
// [ Opcodes.local_get, funcLocal ],
|
2398
|
-
// [ Opcodes.call, includeBuiltin(scope, '__Porffor_funcLut_name').index ],
|
2399
|
-
// Opcodes.i32_from_u,
|
2400
|
-
// ...number(TYPES.bytestring, Valtype.i32),
|
2401
|
-
// [ Opcodes.call, includeBuiltin(scope, '__Porffor_printString').index ],
|
2402
|
-
// [ Opcodes.drop ],
|
2403
|
-
// [ Opcodes.drop ],
|
2404
|
-
|
2405
|
-
// ...number(10),
|
2406
|
-
// [ Opcodes.call, 1 ],
|
2407
|
-
|
2408
|
-
...brTable([
|
2409
|
-
// get argc of func we are calling
|
2410
|
-
[ Opcodes.local_get, funcLocal ],
|
2411
|
-
...number(64, Valtype.i32),
|
2412
|
-
[ Opcodes.i32_mul ],
|
2413
|
-
[ Opcodes.i32_load16_u, 0, ...unsignedLEB128(allocPage(scope, 'func lut')), 'read func lut' ]
|
2414
|
-
], tableBc, valtypeBinary)
|
2415
|
-
],
|
2416
2304
|
|
2417
|
-
|
2418
|
-
[
|
2419
|
-
|
2420
|
-
...
|
2421
|
-
|
2422
|
-
|
2305
|
+
...number(0b10, Valtype.i32),
|
2306
|
+
[ Opcodes.i32_and ],
|
2307
|
+
[ Opcodes.if, Valtype.f64 ],
|
2308
|
+
...forceDuoValtype(scope, newTargetWasm, Valtype.f64),
|
2309
|
+
...forceDuoValtype(scope, thisWasm, Valtype.f64),
|
2310
|
+
...out,
|
2423
2311
|
|
2424
|
-
|
2425
|
-
|
2312
|
+
[ Opcodes.local_get, calleeLocal ],
|
2313
|
+
Opcodes.i32_to_u,
|
2314
|
+
[ Opcodes.call_indirect, args.length + 2, 0 ],
|
2315
|
+
...setLastType(scope),
|
2316
|
+
[ Opcodes.else ],
|
2317
|
+
// throw if non-constructor called with new
|
2318
|
+
...(callAsNew ? [
|
2319
|
+
...internalThrow(scope, 'TypeError', `${unhackName(name)} is not a constructor`, Valtype.f64)
|
2320
|
+
] : [
|
2321
|
+
...out,
|
2322
|
+
|
2323
|
+
[ Opcodes.local_get, calleeLocal ],
|
2324
|
+
Opcodes.i32_to_u,
|
2325
|
+
[ Opcodes.call_indirect, args.length, 0 ],
|
2326
|
+
...setLastType(scope)
|
2327
|
+
]),
|
2328
|
+
[ Opcodes.end ]
|
2329
|
+
],
|
2330
|
+
|
2331
|
+
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, Valtype.f64)
|
2332
|
+
}, Valtype.f64),
|
2333
|
+
...(valtypeBinary === Valtype.i32 && scope.returns[0] !== Valtype.f64 ? [ [ Opcodes.f64_convert_i32_s ] ] : [])
|
2426
2334
|
];
|
2427
2335
|
}
|
2428
2336
|
|
@@ -3854,8 +3762,12 @@ const generateUpdate = (scope, decl, _global, _name, valueUnused = false) => {
|
|
3854
3762
|
};
|
3855
3763
|
|
3856
3764
|
const generateIf = (scope, decl) => {
|
3857
|
-
|
3765
|
+
if (globalThis.precompile && decl.test?.tag?.name === '__Porffor_comptime_flag') {
|
3766
|
+
const flag = decl.test.quasi.quasis[0].value.raw;
|
3767
|
+
return [ [ null, 'comptime_flag', flag, decl.consequent, '#', Prefs ] ];
|
3768
|
+
}
|
3858
3769
|
|
3770
|
+
const out = truthy(scope, generate(scope, decl.test), getNodeType(scope, decl.test), false, true);
|
3859
3771
|
|
3860
3772
|
out.push([ Opcodes.if, Blocktype.void ]);
|
3861
3773
|
depth.push('if');
|
@@ -5283,18 +5195,18 @@ const countLength = (func, name = undefined) => {
|
|
5283
5195
|
|
5284
5196
|
const generateMember = (scope, decl, _global, _name, _objectWasm = undefined) => {
|
5285
5197
|
let final = [], finalEnd, extraBC = {};
|
5286
|
-
|
5198
|
+
let name = decl.object.name;
|
5287
5199
|
|
5288
5200
|
// todo: handle globalThis.foo
|
5289
5201
|
|
5290
5202
|
// hack: .name
|
5291
5203
|
if (decl.property.name === 'name' && hasFuncWithName(name) && !scope.noFastFuncMembers) {
|
5292
|
-
let nameProp = name;
|
5293
|
-
|
5294
5204
|
// eg: __String_prototype_toLowerCase -> toLowerCase
|
5295
|
-
if (
|
5205
|
+
if (name.startsWith('__')) name = name.split('_').pop();
|
5206
|
+
if (name.startsWith('#indirect_')) name = name.slice(10);
|
5207
|
+
if (name.startsWith('#')) name = '';
|
5296
5208
|
|
5297
|
-
return withType(scope, makeString(scope,
|
5209
|
+
return withType(scope, makeString(scope, name, _global, _name, true), TYPES.bytestring);
|
5298
5210
|
}
|
5299
5211
|
|
5300
5212
|
const object = decl.object;
|
@@ -5414,17 +5326,40 @@ const generateMember = (scope, decl, _global, _name, _objectWasm = undefined) =>
|
|
5414
5326
|
|
5415
5327
|
const known = knownType(scope, getNodeType(scope, decl.object));
|
5416
5328
|
for (const x of prototypes) {
|
5417
|
-
|
5329
|
+
let type = TYPES[x.split('_prototype')[0].slice(2).toLowerCase()];
|
5418
5330
|
if (type == null) continue;
|
5419
5331
|
|
5420
5332
|
// do not __proto__ primitive hack for objects or functions
|
5421
5333
|
if (type === TYPES.object || type === TYPES.function) continue;
|
5422
5334
|
|
5335
|
+
// hack: do not support primitives for Object.prototype.isPrototypeOf
|
5336
|
+
if (scope.name === '__Object_prototype_isPrototypeOf') {
|
5337
|
+
switch (type) {
|
5338
|
+
case TYPES.boolean:
|
5339
|
+
type = TYPES.booleanobject;
|
5340
|
+
break;
|
5341
|
+
|
5342
|
+
case TYPES.number:
|
5343
|
+
type = TYPES.numberobject;
|
5344
|
+
break;
|
5345
|
+
|
5346
|
+
case TYPES.string:
|
5347
|
+
type = TYPES.stringobject;
|
5348
|
+
break;
|
5349
|
+
|
5350
|
+
case TYPES.bytestring:
|
5351
|
+
continue;
|
5352
|
+
}
|
5353
|
+
}
|
5354
|
+
|
5423
5355
|
const ident = {
|
5424
5356
|
type: 'Identifier',
|
5425
5357
|
name: x
|
5426
5358
|
};
|
5427
5359
|
|
5360
|
+
// hack: bytestrings should return string prototype
|
5361
|
+
if (type === TYPES.bytestring) ident.name = '__String_prototype';
|
5362
|
+
|
5428
5363
|
bc[type] = () => [
|
5429
5364
|
...generate(scope, ident),
|
5430
5365
|
...setLastType(scope, getNodeType(scope, ident))
|
@@ -5926,6 +5861,10 @@ let objectHackers = [];
|
|
5926
5861
|
const objectHack = node => {
|
5927
5862
|
if (!node) return node;
|
5928
5863
|
|
5864
|
+
// delete .end, .loc while here
|
5865
|
+
delete node.end;
|
5866
|
+
delete node.loc;
|
5867
|
+
|
5929
5868
|
if (node.type === 'MemberExpression') {
|
5930
5869
|
const out = (() => {
|
5931
5870
|
const abortOut = { ...node, object: objectHack(node.object) };
|
@@ -5950,7 +5889,7 @@ const objectHack = node => {
|
|
5950
5889
|
if (objectName !== 'Object_prototype' && (node.property.name === 'propertyIsEnumerable' || node.property.name === 'hasOwnProperty' || node.property.name === 'isPrototypeOf')) return abortOut;
|
5951
5890
|
|
5952
5891
|
const name = '__' + objectName + '_' + node.property.name;
|
5953
|
-
if ((!hasFuncWithName(name) && !Object.hasOwn(builtinVars, name)) && (hasFuncWithName(objectName) || Object.hasOwn(builtinVars, objectName))) return abortOut;
|
5892
|
+
if ((!hasFuncWithName(name) && !Object.hasOwn(builtinVars, name) && !isExistingProtoFunc(name) && !hasFuncWithName(name + '$get')) && (hasFuncWithName(objectName) || Object.hasOwn(builtinVars, objectName) || hasFuncWithName('__' + objectName) || Object.hasOwn(builtinVars, '__' + objectName))) return abortOut;
|
5954
5893
|
|
5955
5894
|
if (Prefs.codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
5956
5895
|
|
@@ -6246,6 +6185,18 @@ const generateFunc = (scope, decl) => {
|
|
6246
6185
|
func.params = Object.values(func.locals).map(x => x.type);
|
6247
6186
|
func.jsLength = jsLength;
|
6248
6187
|
|
6188
|
+
if (globalThis.valtypeOverrides) {
|
6189
|
+
if (globalThis.valtypeOverrides.returns[name]) func.returns = globalThis.valtypeOverrides.returns[name];
|
6190
|
+
if (globalThis.valtypeOverrides.params[name]) {
|
6191
|
+
func.params = globalThis.valtypeOverrides.params[name];
|
6192
|
+
|
6193
|
+
const localsVals = Object.values(func.locals);
|
6194
|
+
for (let i = 0; i < func.params.length; i++) {
|
6195
|
+
localsVals[i].type = func.params[i];
|
6196
|
+
}
|
6197
|
+
}
|
6198
|
+
}
|
6199
|
+
|
6249
6200
|
// force generate for main
|
6250
6201
|
if (name === 'main') func.generate();
|
6251
6202
|
|
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,23 +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_set: [ Valtype.f64, Valtype.i32 ],
|
56
|
-
__Porffor_object_setStrict: [ Valtype.f64, Valtype.i32 ],
|
57
|
-
__Porffor_object_packAccessor: [ Valtype.f64, Valtype.i32 ]
|
58
|
-
};
|
59
|
-
|
60
|
-
const paramOverrides = {
|
61
|
-
__Porffor_object_set: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
|
62
|
-
__Porffor_object_setStrict: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
|
63
|
-
__Porffor_object_expr_init: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
|
64
|
-
__Porffor_object_expr_initWithFlags: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32, Valtype.i32, Valtype.i32 ],
|
65
|
-
__Porffor_object_class_value: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
|
66
|
-
__Porffor_object_class_method: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
|
67
|
-
__Porffor_object_define: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32, Valtype.i32, Valtype.i32 ],
|
68
|
-
};
|
69
|
-
|
70
73
|
const main = funcs.find(x => x.name === 'main');
|
71
74
|
const exports = funcs.filter(x => x.export && x.name !== 'main');
|
72
75
|
for (const x of exports) {
|
@@ -82,9 +85,6 @@ const compile = async (file, _funcs) => {
|
|
82
85
|
}).filter(x => x);
|
83
86
|
}
|
84
87
|
|
85
|
-
if (returnOverrides[x.name]) x.returns = returnOverrides[x.name];
|
86
|
-
if (paramOverrides[x.name]) x.params = paramOverrides[x.name];
|
87
|
-
|
88
88
|
const rewriteWasm = (x, wasm, rewriteLocals = false) => {
|
89
89
|
const locals = Object.keys(x.locals).reduce((acc, y) => {
|
90
90
|
acc[x.locals[y].idx] = { ...x.locals[y], name: y };
|
@@ -201,6 +201,10 @@ const precompile = async () => {
|
|
201
201
|
const total = performance.now() - t;
|
202
202
|
console.log(`\r${' '.repeat(100)}\r\u001b[90m${`[${total.toFixed(2)}ms]`.padEnd(12, ' ')}\u001b[0m\u001b[92mcompiled ${fileCount} files (${funcs.length} funcs)\u001b[0m \u001b[90m(${['parse', 'codegen', 'opt'].map(x => `${x}: ${((timing[x] / total) * 100).toFixed(0)}%`).join(', ')})\u001b[0m`);
|
203
203
|
|
204
|
+
const comptimeFlagChecks = {
|
205
|
+
hasFunc: x => `hasFunc('${x}')`
|
206
|
+
};
|
207
|
+
|
204
208
|
return `// autogenerated by compiler/precompile.js
|
205
209
|
import { number } from './embedding.js';
|
206
210
|
|
@@ -219,9 +223,19 @@ ${funcs.map(x => {
|
|
219
223
|
.replace(/\["throw","(.*?)","(.*?)"\]/g, (_, constructor, message) => `...internalThrow(_,'${constructor}',\`${message}\`)`)
|
220
224
|
.replace(/\["get object","(.*?)"\]/g, (_, objName) => `...generateIdent(_,{name:'${objName}'})`)
|
221
225
|
.replace(/\[null,"typeswitch case start",\[(.*?)\]\],/g, (_, types) => `...t([${types}],()=>[`)
|
222
|
-
.replaceAll(',[null,"typeswitch case end"]', '])')
|
223
|
-
|
224
|
-
|
226
|
+
.replaceAll(',[null,"typeswitch case end"]', '])')
|
227
|
+
.replace(/\[null,"comptime_flag","(.*?)",(\{.*?\}),"#",(\{.*?\})\]/g, (_, flag, ast, prefs) => {
|
228
|
+
ast = JSON.parse(ast.replaceAll('\n', '\\n'));
|
229
|
+
ast = JSON.stringify(ast, (k, v) => {
|
230
|
+
if (k === 'loc' || k === 'start' || k === 'end') return undefined;
|
231
|
+
return v;
|
232
|
+
});
|
233
|
+
|
234
|
+
const [ id, extra ] = flag.split('.');
|
235
|
+
return `[null,()=>{if(${comptimeFlagChecks[id](extra)}){const r=()=>{valtype=Prefs.valtype??'f64';valtypeBinary=Valtype[valtype];const valtypeInd=['i32','i64','f64'].indexOf(valtype);Opcodes.i32_to=[[],[Opcodes.i32_wrap_i64],Opcodes.i32_trunc_sat_f64_s][valtypeInd];Opcodes.i32_to_u=[[],[Opcodes.i32_wrap_i64],Opcodes.i32_trunc_sat_f64_u][valtypeInd];Opcodes.i32_from=[[],[Opcodes.i64_extend_i32_s],[Opcodes.f64_convert_i32_s]][valtypeInd];Opcodes.i32_from_u=[[],[Opcodes.i64_extend_i32_u],[ Opcodes.f64_convert_i32_u]][valtypeInd]};const a=Prefs;Prefs=${prefs};r();const b=generate(_,${ast});Prefs=a;r();return b;}return []}]`;
|
236
|
+
});
|
237
|
+
|
238
|
+
return `(_,{${str.includes('hasFunc(') ? 'hasFunc,' : ''}${str.includes('Valtype[') ? 'Valtype,' : ''}${str.includes('Opcodes.') ? 'Opcodes,' : ''}${str.includes('...t(') ? 't,' : ''}${`${str.includes('allocPage(') ? 'allocPage,' : ''}${str.includes('glbl(') ? 'glbl,' : ''}${str.includes('loc(') ? 'loc,' : ''}${str.includes('builtin(') ? 'builtin,' : ''}${str.includes('funcRef(') ? 'funcRef,' : ''}${str.includes('internalThrow(') ? 'internalThrow,' : ''}${str.includes('generateIdent(') ? 'generateIdent,' : ''}${str.includes('generate(') ? 'generate,' : ''}`.slice(0, -1)}})=>`.replace('_,{}', '') + str;
|
225
239
|
};
|
226
240
|
|
227
241
|
const locals = Object.entries(x.locals).sort((a,b) => a[1].idx - b[1].idx)
|
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.41.
|
3
|
+
globalThis.version = '0.41.7+89be416c1';
|
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
|
|