porffor 0.28.13 → 0.30.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/builtins_precompiled.js +104 -104
- package/compiler/codegen.js +321 -273
- package/package.json +1 -1
- package/runner/index.js +1 -1
package/compiler/codegen.js
CHANGED
@@ -358,7 +358,12 @@ const generateIdent = (scope, decl) => {
|
|
358
358
|
|
359
359
|
if (local?.idx === undefined) return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
360
360
|
|
361
|
-
return [
|
361
|
+
return [
|
362
|
+
[ Opcodes.local_get, local.idx ],
|
363
|
+
// todo: no support for i64
|
364
|
+
...(valtypeBinary === Valtype.f64 && local.type === Valtype.i32 ? [ Opcodes.i32_from_u ] : []),
|
365
|
+
...(valtypeBinary === Valtype.i32 && local.type === Valtype.f64 ? [ Opcodes.i32_to_u ] : [])
|
366
|
+
];
|
362
367
|
};
|
363
368
|
|
364
369
|
return lookup(decl.name);
|
@@ -3000,263 +3005,69 @@ const setLocalWithType = (scope, name, isGlobal, decl, tee = false, overrideType
|
|
3000
3005
|
return out;
|
3001
3006
|
};
|
3002
3007
|
|
3003
|
-
const
|
3004
|
-
let out = [];
|
3005
|
-
|
3008
|
+
const generateVarDstr = (scope, kind, pattern, init, defaultValue, global) => {
|
3006
3009
|
const topLevel = scope.name === 'main';
|
3007
3010
|
|
3008
|
-
|
3009
|
-
|
3010
|
-
|
3011
|
-
for (const x of decl.declarations) {
|
3012
|
-
if (x.id.type === 'ArrayPattern') {
|
3013
|
-
const decls = [];
|
3014
|
-
const tmpName = '#destructure' + uniqId();
|
3015
|
-
|
3016
|
-
let i = 0;
|
3017
|
-
const elements = [...x.id.elements];
|
3018
|
-
for (const e of elements) {
|
3019
|
-
switch (e?.type) {
|
3020
|
-
case 'RestElement': { // let [ ...foo ] = []
|
3021
|
-
if (e.argument.type === 'ArrayPattern') {
|
3022
|
-
// let [ ...[a, b, c] ] = []
|
3023
|
-
elements.push(...e.argument.elements);
|
3024
|
-
} else {
|
3025
|
-
decls.push({
|
3026
|
-
type: 'VariableDeclarator',
|
3027
|
-
id: { type: 'Identifier', name: e.argument.name },
|
3028
|
-
init: {
|
3029
|
-
type: 'CallExpression',
|
3030
|
-
callee: {
|
3031
|
-
type: 'Identifier',
|
3032
|
-
name: '__Array_prototype_slice'
|
3033
|
-
},
|
3034
|
-
arguments: [
|
3035
|
-
{ type: 'Identifier', name: tmpName },
|
3036
|
-
{ type: 'Literal', value: i },
|
3037
|
-
{
|
3038
|
-
type: 'MemberExpression',
|
3039
|
-
object: { type: 'Identifier', name: tmpName, },
|
3040
|
-
property: { type: 'Identifier', name: 'length', }
|
3041
|
-
}
|
3042
|
-
],
|
3043
|
-
_protoInternalCall: true
|
3044
|
-
}
|
3045
|
-
});
|
3046
|
-
}
|
3047
|
-
|
3048
|
-
continue; // skip i++
|
3049
|
-
}
|
3050
|
-
|
3051
|
-
case 'AssignmentPattern': { // let [ foo = defaultValue ] = []
|
3052
|
-
decls.push({
|
3053
|
-
type: 'VariableDeclarator',
|
3054
|
-
id: e.left,
|
3055
|
-
init: {
|
3056
|
-
type: 'MemberExpression',
|
3057
|
-
object: { type: 'Identifier', name: tmpName },
|
3058
|
-
property: { type: 'Literal', value: i },
|
3059
|
-
computed: true
|
3060
|
-
},
|
3061
|
-
_default: e.right
|
3062
|
-
});
|
3063
|
-
|
3064
|
-
break;
|
3065
|
-
}
|
3066
|
-
|
3067
|
-
case 'ArrayPattern': // let [ [ foo, bar ] ] = [ [ 2, 4 ] ]
|
3068
|
-
case 'Identifier': // let [ foo ] = []
|
3069
|
-
case 'ObjectPattern': { // let [ { foo } ] = [ { foo: true } ]
|
3070
|
-
decls.push({
|
3071
|
-
type: 'VariableDeclarator',
|
3072
|
-
id: e,
|
3073
|
-
init: {
|
3074
|
-
type: 'MemberExpression',
|
3075
|
-
object: { type: 'Identifier', name: tmpName },
|
3076
|
-
property: { type: 'Literal', value: i },
|
3077
|
-
computed: true
|
3078
|
-
}
|
3079
|
-
});
|
3080
|
-
|
3081
|
-
break;
|
3082
|
-
}
|
3083
|
-
}
|
3084
|
-
|
3085
|
-
i++;
|
3086
|
-
}
|
3087
|
-
|
3088
|
-
out = out.concat([
|
3089
|
-
...generateVar(scope, {
|
3090
|
-
type: 'VariableDeclaration',
|
3091
|
-
declarations: [{
|
3092
|
-
type: 'VariableDeclarator',
|
3093
|
-
id: { type: 'Identifier', name: tmpName },
|
3094
|
-
init: x.init,
|
3095
|
-
_default: x._default
|
3096
|
-
}],
|
3097
|
-
kind: decl.kind,
|
3098
|
-
_global: false
|
3099
|
-
}),
|
3100
|
-
|
3101
|
-
// check tmp is iterable
|
3102
|
-
// array or string or bytestring
|
3103
|
-
...typeIsOneOf(getType(scope, tmpName), [ TYPES.array, TYPES.string, TYPES.bytestring ]),
|
3104
|
-
// typed array
|
3105
|
-
...getType(scope, tmpName),
|
3106
|
-
...number(TYPES.uint8array, Valtype.i32),
|
3107
|
-
[ Opcodes.i32_ge_s ],
|
3108
|
-
...getType(scope, tmpName),
|
3109
|
-
...number(TYPES.float64array, Valtype.i32),
|
3110
|
-
[ Opcodes.i32_le_s ],
|
3111
|
-
[ Opcodes.i32_and ],
|
3112
|
-
[ Opcodes.i32_or ],
|
3113
|
-
[ Opcodes.i32_eqz ],
|
3114
|
-
[ Opcodes.if, Blocktype.void ],
|
3115
|
-
...internalThrow(scope, 'TypeError', 'Cannot array destructure a non-iterable'),
|
3116
|
-
[ Opcodes.end ],
|
3117
|
-
|
3118
|
-
...generateVar(scope, {
|
3119
|
-
type: 'VariableDeclaration',
|
3120
|
-
declarations: decls,
|
3121
|
-
kind: decl.kind
|
3122
|
-
})
|
3123
|
-
]);
|
3124
|
-
|
3125
|
-
continue;
|
3126
|
-
}
|
3127
|
-
|
3128
|
-
if (x.id.type === 'ObjectPattern') {
|
3129
|
-
const decls = [];
|
3130
|
-
const tmpName = '#destructure' + uniqId();
|
3131
|
-
|
3132
|
-
const properties = [...x.id.properties];
|
3133
|
-
for (const prop of properties) {
|
3134
|
-
if (prop.type == 'Property') { // let { foo } = {}
|
3135
|
-
if (prop.value.type === 'AssignmentPattern') { // let { foo = defaultValue } = {}
|
3136
|
-
decls.push({
|
3137
|
-
type: 'VariableDeclarator',
|
3138
|
-
id: prop.value.left,
|
3139
|
-
init: {
|
3140
|
-
type: 'MemberExpression',
|
3141
|
-
object: { type: 'Identifier', name: tmpName },
|
3142
|
-
property: prop.key,
|
3143
|
-
computed: prop.computed
|
3144
|
-
},
|
3145
|
-
_default: prop.value.right
|
3146
|
-
});
|
3147
|
-
} else {
|
3148
|
-
decls.push({
|
3149
|
-
type: 'VariableDeclarator',
|
3150
|
-
id: prop.value,
|
3151
|
-
init: {
|
3152
|
-
type: 'MemberExpression',
|
3153
|
-
object: { type: 'Identifier', name: tmpName },
|
3154
|
-
property: prop.key,
|
3155
|
-
computed: prop.computed
|
3156
|
-
}
|
3157
|
-
});
|
3158
|
-
}
|
3159
|
-
} else { // let { ...foo } = {}
|
3160
|
-
return todo(scope, 'object rest destructuring is not supported yet')
|
3161
|
-
}
|
3162
|
-
}
|
3163
|
-
|
3164
|
-
out = out.concat([
|
3165
|
-
...generateVar(scope, {
|
3166
|
-
type: 'VariableDeclaration',
|
3167
|
-
declarations: [{
|
3168
|
-
type: 'VariableDeclarator',
|
3169
|
-
id: { type: 'Identifier', name: tmpName },
|
3170
|
-
init: x.init,
|
3171
|
-
_default: x._default
|
3172
|
-
}],
|
3173
|
-
kind: decl.kind,
|
3174
|
-
_global: false
|
3175
|
-
}),
|
3176
|
-
|
3177
|
-
// check tmp is valid object
|
3178
|
-
// not undefined or empty type
|
3179
|
-
...typeIsOneOf(getType(scope, tmpName), [ TYPES.undefined, TYPES.empty ]),
|
3180
|
-
|
3181
|
-
// not null
|
3182
|
-
...getType(scope, tmpName),
|
3183
|
-
...number(TYPES.object, Valtype.i32),
|
3184
|
-
[ Opcodes.i32_eq ],
|
3185
|
-
[ Opcodes.local_get, scope.locals[tmpName].idx ],
|
3186
|
-
...number(0),
|
3187
|
-
[ Opcodes.eq ],
|
3188
|
-
[ Opcodes.i32_and ],
|
3189
|
-
|
3190
|
-
[ Opcodes.i32_or ],
|
3191
|
-
[ Opcodes.if, Blocktype.void ],
|
3192
|
-
...internalThrow(scope, 'TypeError', 'Cannot object destructure undefined or null'),
|
3193
|
-
[ Opcodes.end ],
|
3194
|
-
|
3195
|
-
...generateVar(scope, {
|
3196
|
-
type: 'VariableDeclaration',
|
3197
|
-
declarations: decls,
|
3198
|
-
kind: decl.kind
|
3199
|
-
})
|
3200
|
-
]);
|
3201
|
-
|
3202
|
-
continue;
|
3203
|
-
}
|
3011
|
+
if (typeof pattern === 'string') {
|
3012
|
+
pattern = { type: 'Identifier', name: pattern };
|
3013
|
+
}
|
3204
3014
|
|
3205
|
-
|
3206
|
-
|
3015
|
+
if (pattern.type === 'Identifier') {
|
3016
|
+
let out = [];
|
3017
|
+
const name = mapName(pattern.name);
|
3207
3018
|
|
3208
|
-
if (
|
3019
|
+
if (init && isFuncType(init.type)) {
|
3209
3020
|
// hack for let a = function () { ... }
|
3210
|
-
if (!
|
3211
|
-
|
3212
|
-
generateFunc(scope,
|
3213
|
-
|
3021
|
+
if (!init.id) {
|
3022
|
+
init.id = { name };
|
3023
|
+
generateFunc(scope, init);
|
3024
|
+
return out;
|
3214
3025
|
}
|
3215
3026
|
}
|
3216
3027
|
|
3217
3028
|
if (topLevel && Object.hasOwn(builtinVars, name)) {
|
3218
3029
|
// cannot redeclare
|
3219
|
-
if (
|
3030
|
+
if (kind !== 'var') return internalThrow(scope, 'SyntaxError', `Identifier '${unhackName(name)}' has already been declared`);
|
3220
3031
|
|
3221
|
-
|
3032
|
+
return out; // always ignore
|
3222
3033
|
}
|
3223
3034
|
|
3224
3035
|
// // generate init before allocating var
|
3225
3036
|
// let generated;
|
3226
|
-
// if (
|
3037
|
+
// if (init) generated = generate(scope, init, global, name);
|
3227
3038
|
|
3228
|
-
const typed = typedInput &&
|
3229
|
-
let idx = allocVar(scope, name, global, !(typed && extractTypeAnnotation(
|
3039
|
+
const typed = typedInput && pattern.typeAnnotation;
|
3040
|
+
let idx = allocVar(scope, name, global, !(typed && extractTypeAnnotation(pattern).type != null));
|
3230
3041
|
|
3231
3042
|
if (typed) {
|
3232
|
-
addVarMetadata(scope, name, global, extractTypeAnnotation(
|
3043
|
+
addVarMetadata(scope, name, global, extractTypeAnnotation(pattern));
|
3233
3044
|
}
|
3234
3045
|
|
3235
|
-
if (
|
3046
|
+
if (init) {
|
3236
3047
|
const alreadyArray = scope.arrays?.get(name) != null;
|
3237
3048
|
|
3238
|
-
let newOut = generate(scope,
|
3049
|
+
let newOut = generate(scope, init, global, name);
|
3239
3050
|
if (!alreadyArray && scope.arrays?.get(name) != null) {
|
3240
3051
|
// hack to set local as pointer before
|
3241
3052
|
newOut.unshift(...number(scope.arrays.get(name)), [ global ? Opcodes.global_set : Opcodes.local_set, idx ]);
|
3242
3053
|
if (newOut.at(-1) == Opcodes.i32_from_u) newOut.pop();
|
3243
3054
|
newOut.push(
|
3244
3055
|
[ Opcodes.drop ],
|
3245
|
-
...setType(scope, name, getNodeType(scope,
|
3056
|
+
...setType(scope, name, getNodeType(scope, init))
|
3246
3057
|
);
|
3247
3058
|
} else {
|
3248
|
-
newOut = setLocalWithType(scope, name, global, newOut, false, getNodeType(scope,
|
3059
|
+
newOut = setLocalWithType(scope, name, global, newOut, false, getNodeType(scope, init));
|
3249
3060
|
}
|
3250
3061
|
|
3251
3062
|
out = out.concat(newOut);
|
3252
3063
|
|
3253
|
-
if (
|
3064
|
+
if (defaultValue) {
|
3254
3065
|
out.push(
|
3255
3066
|
...typeIsOneOf(getType(scope, name), [ TYPES.undefined, TYPES.empty ]),
|
3256
3067
|
[ Opcodes.if, Blocktype.void ],
|
3257
|
-
...generate(scope,
|
3068
|
+
...generate(scope, defaultValue, global, name),
|
3258
3069
|
[ global ? Opcodes.global_set : Opcodes.local_set, idx ],
|
3259
|
-
...setType(scope, name, getNodeType(scope,
|
3070
|
+
...setType(scope, name, getNodeType(scope, defaultValue)),
|
3260
3071
|
[ Opcodes.end ],
|
3261
3072
|
);
|
3262
3073
|
}
|
@@ -3266,6 +3077,174 @@ const generateVar = (scope, decl) => {
|
|
3266
3077
|
scope.globalInits[name] = newOut;
|
3267
3078
|
}
|
3268
3079
|
}
|
3080
|
+
|
3081
|
+
return out;
|
3082
|
+
}
|
3083
|
+
|
3084
|
+
if (pattern.type === 'ArrayPattern') {
|
3085
|
+
const decls = [];
|
3086
|
+
const tmpName = '#destructure' + uniqId();
|
3087
|
+
let out = generateVarDstr(scope, 'const', tmpName, init, defaultValue, false);
|
3088
|
+
|
3089
|
+
let i = 0;
|
3090
|
+
const elements = [...pattern.elements];
|
3091
|
+
for (const e of elements) {
|
3092
|
+
switch (e?.type) {
|
3093
|
+
case 'RestElement': { // let [ ...foo ] = []
|
3094
|
+
if (e.argument.type === 'ArrayPattern') {
|
3095
|
+
// let [ ...[a, b, c] ] = []
|
3096
|
+
elements.push(...e.argument.elements);
|
3097
|
+
} else {
|
3098
|
+
decls.push(
|
3099
|
+
...generateVarDstr(scope, kind, e.argument.name, {
|
3100
|
+
type: 'CallExpression',
|
3101
|
+
callee: {
|
3102
|
+
type: 'Identifier',
|
3103
|
+
name: '__Array_prototype_slice'
|
3104
|
+
},
|
3105
|
+
arguments: [
|
3106
|
+
{ type: 'Identifier', name: tmpName },
|
3107
|
+
{ type: 'Literal', value: i },
|
3108
|
+
{
|
3109
|
+
type: 'MemberExpression',
|
3110
|
+
object: { type: 'Identifier', name: tmpName, },
|
3111
|
+
property: { type: 'Identifier', name: 'length', }
|
3112
|
+
}
|
3113
|
+
],
|
3114
|
+
_protoInternalCall: true
|
3115
|
+
}, undefined, global)
|
3116
|
+
);
|
3117
|
+
}
|
3118
|
+
|
3119
|
+
continue; // skip i++
|
3120
|
+
}
|
3121
|
+
|
3122
|
+
case 'AssignmentPattern': { // let [ foo = defaultValue ] = []
|
3123
|
+
decls.push(
|
3124
|
+
...generateVarDstr(scope, kind, e.left, {
|
3125
|
+
type: 'MemberExpression',
|
3126
|
+
object: { type: 'Identifier', name: tmpName },
|
3127
|
+
property: { type: 'Literal', value: i },
|
3128
|
+
computed: true
|
3129
|
+
}, e.right, global)
|
3130
|
+
);
|
3131
|
+
|
3132
|
+
break;
|
3133
|
+
}
|
3134
|
+
|
3135
|
+
case 'ArrayPattern': // let [ [ foo, bar ] ] = [ [ 2, 4 ] ]
|
3136
|
+
case 'Identifier': // let [ foo ] = []
|
3137
|
+
case 'ObjectPattern': { // let [ { foo } ] = [ { foo: true } ]
|
3138
|
+
decls.push(
|
3139
|
+
...generateVarDstr(scope, kind, e, {
|
3140
|
+
type: 'MemberExpression',
|
3141
|
+
object: { type: 'Identifier', name: tmpName },
|
3142
|
+
property: { type: 'Literal', value: i },
|
3143
|
+
computed: true
|
3144
|
+
}, undefined, global)
|
3145
|
+
);
|
3146
|
+
|
3147
|
+
break;
|
3148
|
+
}
|
3149
|
+
}
|
3150
|
+
|
3151
|
+
i++;
|
3152
|
+
}
|
3153
|
+
|
3154
|
+
out = out.concat([
|
3155
|
+
// check tmp is iterable
|
3156
|
+
// array or string or bytestring
|
3157
|
+
...typeIsOneOf(getType(scope, tmpName), [ TYPES.array, TYPES.string, TYPES.bytestring ]),
|
3158
|
+
// typed array
|
3159
|
+
...getType(scope, tmpName),
|
3160
|
+
...number(TYPES.uint8array, Valtype.i32),
|
3161
|
+
[ Opcodes.i32_ge_s ],
|
3162
|
+
...getType(scope, tmpName),
|
3163
|
+
...number(TYPES.float64array, Valtype.i32),
|
3164
|
+
[ Opcodes.i32_le_s ],
|
3165
|
+
[ Opcodes.i32_and ],
|
3166
|
+
[ Opcodes.i32_or ],
|
3167
|
+
[ Opcodes.i32_eqz ],
|
3168
|
+
[ Opcodes.if, Blocktype.void ],
|
3169
|
+
...internalThrow(scope, 'TypeError', 'Cannot array destructure a non-iterable'),
|
3170
|
+
[ Opcodes.end ],
|
3171
|
+
|
3172
|
+
...decls
|
3173
|
+
]);
|
3174
|
+
|
3175
|
+
return out;
|
3176
|
+
}
|
3177
|
+
|
3178
|
+
if (pattern.type === 'ObjectPattern') {
|
3179
|
+
const decls = [];
|
3180
|
+
const tmpName = '#destructure' + uniqId();
|
3181
|
+
let out = generateVarDstr(scope, 'const', tmpName, init, defaultValue, false);
|
3182
|
+
|
3183
|
+
const properties = [...pattern.properties];
|
3184
|
+
for (const prop of properties) {
|
3185
|
+
if (prop.type == 'Property') { // let { foo } = {}
|
3186
|
+
if (prop.value.type === 'AssignmentPattern') { // let { foo = defaultValue } = {}
|
3187
|
+
decls.push(
|
3188
|
+
...generateVarDstr(scope, kind, prop.value.left, {
|
3189
|
+
type: 'MemberExpression',
|
3190
|
+
object: { type: 'Identifier', name: tmpName },
|
3191
|
+
property: prop.key,
|
3192
|
+
computed: prop.computed
|
3193
|
+
}, prop.value.right, global)
|
3194
|
+
);
|
3195
|
+
} else {
|
3196
|
+
decls.push(
|
3197
|
+
...generateVarDstr(scope, kind, prop.value, {
|
3198
|
+
type: 'MemberExpression',
|
3199
|
+
object: { type: 'Identifier', name: tmpName },
|
3200
|
+
property: prop.key,
|
3201
|
+
computed: prop.computed
|
3202
|
+
}, undefined, global)
|
3203
|
+
);
|
3204
|
+
}
|
3205
|
+
} else { // let { ...foo } = {}
|
3206
|
+
return todo(scope, 'object rest destructuring is not supported yet')
|
3207
|
+
}
|
3208
|
+
}
|
3209
|
+
|
3210
|
+
out = out.concat([
|
3211
|
+
// check tmp is valid object
|
3212
|
+
// not undefined or empty type
|
3213
|
+
...typeIsOneOf(getType(scope, tmpName), [ TYPES.undefined, TYPES.empty ]),
|
3214
|
+
|
3215
|
+
// not null
|
3216
|
+
...getType(scope, tmpName),
|
3217
|
+
...number(TYPES.object, Valtype.i32),
|
3218
|
+
[ Opcodes.i32_eq ],
|
3219
|
+
[ Opcodes.local_get, scope.locals[tmpName].idx ],
|
3220
|
+
...number(0),
|
3221
|
+
[ Opcodes.eq ],
|
3222
|
+
[ Opcodes.i32_and ],
|
3223
|
+
|
3224
|
+
[ Opcodes.i32_or ],
|
3225
|
+
[ Opcodes.if, Blocktype.void ],
|
3226
|
+
...internalThrow(scope, 'TypeError', 'Cannot object destructure undefined or null'),
|
3227
|
+
[ Opcodes.end ],
|
3228
|
+
|
3229
|
+
...decls
|
3230
|
+
]);
|
3231
|
+
|
3232
|
+
return out;
|
3233
|
+
}
|
3234
|
+
|
3235
|
+
return todo(scope, `variable declarators of type ${pattern.type} are not supported yet`);
|
3236
|
+
}
|
3237
|
+
|
3238
|
+
const generateVar = (scope, decl) => {
|
3239
|
+
let out = [];
|
3240
|
+
|
3241
|
+
const topLevel = scope.name === 'main';
|
3242
|
+
|
3243
|
+
// global variable if in top scope (main) or if internally wanted
|
3244
|
+
const global = decl._global ?? (topLevel || decl._bare);
|
3245
|
+
|
3246
|
+
for (const x of decl.declarations) {
|
3247
|
+
out = out.concat(generateVarDstr(scope, decl.kind, x.id, x.init, undefined, global));
|
3269
3248
|
}
|
3270
3249
|
|
3271
3250
|
return out;
|
@@ -3305,7 +3284,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
3305
3284
|
const op = decl.operator.slice(0, -1) || '=';
|
3306
3285
|
|
3307
3286
|
// hack: .length setter
|
3308
|
-
if (
|
3287
|
+
if (type === 'MemberExpression' && decl.left.property.name === 'length') {
|
3309
3288
|
const newValueTmp = localTmp(scope, '__length_setter_tmp');
|
3310
3289
|
const pointerTmp = op === '=' ? null : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
|
3311
3290
|
|
@@ -3329,7 +3308,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
3329
3308
|
}
|
3330
3309
|
|
3331
3310
|
// arr[i]
|
3332
|
-
if (
|
3311
|
+
if (type === 'MemberExpression') {
|
3333
3312
|
const newValueTmp = localTmp(scope, '#member_setter_val_tmp');
|
3334
3313
|
const pointerTmp = localTmp(scope, '#member_setter_ptr_tmp', Valtype.i32);
|
3335
3314
|
|
@@ -3585,14 +3564,22 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
3585
3564
|
];
|
3586
3565
|
}
|
3587
3566
|
|
3588
|
-
if (!name) return todo(scope, 'destructuring is not supported yet', true);
|
3589
|
-
|
3590
3567
|
if (local === undefined) {
|
3591
3568
|
// todo: this should be a sloppy mode only thing
|
3592
3569
|
|
3593
3570
|
// only allow = for this
|
3594
3571
|
if (op !== '=') return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`);
|
3595
3572
|
|
3573
|
+
if (type != 'Identifier') {
|
3574
|
+
const tmpName = '#rhs' + uniqId();
|
3575
|
+
return [
|
3576
|
+
...generateVarDstr(scope, 'const', tmpName, decl.right, undefined, true),
|
3577
|
+
...generateVarDstr(scope, 'var', decl.left, { type: 'Identifier', name: tmpName }, undefined, true),
|
3578
|
+
...generate(scope, { type: 'Identifier', name: tmpName }),
|
3579
|
+
...setLastType(scope, getNodeType(scope, decl.right))
|
3580
|
+
];
|
3581
|
+
}
|
3582
|
+
|
3596
3583
|
if (Object.hasOwn(builtinVars, name)) {
|
3597
3584
|
// just return rhs (eg `NaN = 2`)
|
3598
3585
|
return generate(scope, decl.right);
|
@@ -3600,7 +3587,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
3600
3587
|
|
3601
3588
|
// set global and return (eg a = 2)
|
3602
3589
|
return [
|
3603
|
-
...
|
3590
|
+
...generateVarDstr(scope, 'var', name, decl.right, undefined, true),
|
3604
3591
|
...generate(scope, decl.left)
|
3605
3592
|
];
|
3606
3593
|
}
|
@@ -3935,10 +3922,16 @@ const generateForOf = (scope, decl) => {
|
|
3935
3922
|
|
3936
3923
|
const out = [];
|
3937
3924
|
|
3938
|
-
|
3939
|
-
|
3940
|
-
|
3941
|
-
|
3925
|
+
let count = 0;
|
3926
|
+
for (let i = 0; i < depth.length; i++) {
|
3927
|
+
if (depth[i] === 'forof') count++;
|
3928
|
+
}
|
3929
|
+
|
3930
|
+
const iterType = getNodeType(scope, decl.right);
|
3931
|
+
|
3932
|
+
const pointer = localTmp(scope, '#forof_base_pointer' + count, Valtype.i32);
|
3933
|
+
const length = localTmp(scope, '#forof_length' + count, Valtype.i32);
|
3934
|
+
const counter = localTmp(scope, '#forof_counter' + count, Valtype.i32);
|
3942
3935
|
|
3943
3936
|
out.push(
|
3944
3937
|
// set pointer as right
|
@@ -3950,6 +3943,23 @@ const generateForOf = (scope, decl) => {
|
|
3950
3943
|
...number(0, Valtype.i32),
|
3951
3944
|
[ Opcodes.local_set, counter ],
|
3952
3945
|
|
3946
|
+
// check tmp is iterable
|
3947
|
+
// array or string or bytestring
|
3948
|
+
...typeIsOneOf(iterType, [ TYPES.array, TYPES.set, TYPES.string, TYPES.bytestring ]),
|
3949
|
+
// typed array
|
3950
|
+
...iterType,
|
3951
|
+
...number(TYPES.uint8array, Valtype.i32),
|
3952
|
+
[ Opcodes.i32_ge_s ],
|
3953
|
+
...iterType,
|
3954
|
+
...number(TYPES.float64array, Valtype.i32),
|
3955
|
+
[ Opcodes.i32_le_s ],
|
3956
|
+
[ Opcodes.i32_and ],
|
3957
|
+
[ Opcodes.i32_or ],
|
3958
|
+
[ Opcodes.i32_eqz ],
|
3959
|
+
[ Opcodes.if, Blocktype.void ],
|
3960
|
+
...internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`),
|
3961
|
+
[ Opcodes.end ],
|
3962
|
+
|
3953
3963
|
// get length
|
3954
3964
|
[ Opcodes.local_get, pointer ],
|
3955
3965
|
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
@@ -3963,35 +3973,40 @@ const generateForOf = (scope, decl) => {
|
|
3963
3973
|
depth.push('block');
|
3964
3974
|
depth.push('block');
|
3965
3975
|
|
3976
|
+
const tmpName = '#forof_tmp' + count;
|
3977
|
+
const tmp = localTmp(scope, tmpName, valtypeBinary);
|
3978
|
+
localTmp(scope, tmpName + "#type", Valtype.i32);
|
3979
|
+
|
3966
3980
|
// setup local for left
|
3967
|
-
|
3981
|
+
let setVar;
|
3968
3982
|
|
3969
|
-
|
3970
|
-
if (!leftName && decl.left.name) {
|
3983
|
+
if (decl.left.type === 'Identifier') {
|
3971
3984
|
// todo: should be sloppy mode only
|
3972
|
-
|
3973
|
-
|
3974
|
-
|
3985
|
+
setVar = generateVarDstr(scope, 'var', decl.left.name, { type: 'Identifier', name: tmpName }, undefined, true);
|
3986
|
+
} else {
|
3987
|
+
// todo: verify this is correct
|
3988
|
+
const global = scope.name === 'main' && decl.left.kind === 'var';
|
3989
|
+
setVar = generateVarDstr(scope, 'var', decl.left?.declarations?.[0]?.id ?? decl.left, { type: 'Identifier', name: tmpName }, undefined, global);
|
3975
3990
|
}
|
3976
3991
|
|
3977
|
-
const [ local, isGlobal ] = lookupName(scope, leftName);
|
3978
|
-
if (!local) return todo(scope, 'for of failed to get left local (probably destructure)');
|
3979
3992
|
|
3980
3993
|
// set type for local
|
3981
3994
|
// todo: optimize away counter and use end pointer
|
3982
|
-
out.push(...typeSwitch(scope,
|
3995
|
+
out.push(...typeSwitch(scope, iterType, {
|
3983
3996
|
[TYPES.array]: [
|
3984
3997
|
[ Opcodes.loop, Blocktype.void ],
|
3985
3998
|
|
3986
3999
|
[ Opcodes.local_get, pointer ],
|
3987
4000
|
[ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
3988
4001
|
|
3989
|
-
|
4002
|
+
[ Opcodes.local_set, tmp ],
|
4003
|
+
|
4004
|
+
...setType(scope, tmpName, [
|
3990
4005
|
[ Opcodes.local_get, pointer ],
|
3991
4006
|
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
|
3992
4007
|
]),
|
3993
4008
|
|
3994
|
-
|
4009
|
+
...setVar,
|
3995
4010
|
|
3996
4011
|
[ Opcodes.block, Blocktype.void ],
|
3997
4012
|
[ Opcodes.block, Blocktype.void ],
|
@@ -4020,7 +4035,7 @@ const generateForOf = (scope, decl) => {
|
|
4020
4035
|
],
|
4021
4036
|
|
4022
4037
|
[TYPES.string]: [
|
4023
|
-
...setType(scope,
|
4038
|
+
...setType(scope, tmpName, TYPES.string),
|
4024
4039
|
|
4025
4040
|
// allocate out string
|
4026
4041
|
[ Opcodes.call, includeBuiltin(scope, '__Porffor_allocate').index ],
|
@@ -4044,9 +4059,10 @@ const generateForOf = (scope, decl) => {
|
|
4044
4059
|
|
4045
4060
|
// return new string (page)
|
4046
4061
|
[ Opcodes.local_get, localTmp(scope, '#forof_allocd', Valtype.i32) ],
|
4047
|
-
|
4062
|
+
Opcodes.i32_from_u,
|
4063
|
+
[ Opcodes.local_set, tmp ],
|
4048
4064
|
|
4049
|
-
|
4065
|
+
...setVar,
|
4050
4066
|
|
4051
4067
|
[ Opcodes.block, Blocktype.void ],
|
4052
4068
|
[ Opcodes.block, Blocktype.void ],
|
@@ -4074,7 +4090,7 @@ const generateForOf = (scope, decl) => {
|
|
4074
4090
|
[ Opcodes.end ]
|
4075
4091
|
],
|
4076
4092
|
[TYPES.bytestring]: [
|
4077
|
-
...setType(scope,
|
4093
|
+
...setType(scope, tmpName, TYPES.bytestring),
|
4078
4094
|
|
4079
4095
|
// allocate out string
|
4080
4096
|
[ Opcodes.call, includeBuiltin(scope, '__Porffor_allocate').index ],
|
@@ -4101,8 +4117,9 @@ const generateForOf = (scope, decl) => {
|
|
4101
4117
|
// return new string (page)
|
4102
4118
|
[ Opcodes.local_get, localTmp(scope, '#forof_allocd', Valtype.i32) ],
|
4103
4119
|
Opcodes.i32_from_u,
|
4120
|
+
[ Opcodes.local_set, tmp ],
|
4104
4121
|
|
4105
|
-
|
4122
|
+
...setVar,
|
4106
4123
|
|
4107
4124
|
[ Opcodes.block, Blocktype.void ],
|
4108
4125
|
[ Opcodes.block, Blocktype.void ],
|
@@ -4130,12 +4147,14 @@ const generateForOf = (scope, decl) => {
|
|
4130
4147
|
[ Opcodes.local_get, pointer ],
|
4131
4148
|
[ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
4132
4149
|
|
4133
|
-
|
4150
|
+
[ Opcodes.local_set, tmp ],
|
4151
|
+
|
4152
|
+
...setType(scope, tmpName, [
|
4134
4153
|
[ Opcodes.local_get, pointer ],
|
4135
4154
|
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
|
4136
4155
|
]),
|
4137
4156
|
|
4138
|
-
|
4157
|
+
...setVar,
|
4139
4158
|
|
4140
4159
|
[ Opcodes.block, Blocktype.void ],
|
4141
4160
|
[ Opcodes.block, Blocktype.void ],
|
@@ -4231,7 +4250,7 @@ const generateForOf = (scope, decl) => {
|
|
4231
4250
|
],
|
4232
4251
|
}, {
|
4233
4252
|
prelude: [
|
4234
|
-
...setType(scope,
|
4253
|
+
...setType(scope, tmpName, TYPES.number),
|
4235
4254
|
|
4236
4255
|
[ Opcodes.loop, Blocktype.void ],
|
4237
4256
|
|
@@ -4240,7 +4259,9 @@ const generateForOf = (scope, decl) => {
|
|
4240
4259
|
[ Opcodes.local_get, counter ]
|
4241
4260
|
],
|
4242
4261
|
postlude: [
|
4243
|
-
[
|
4262
|
+
[ Opcodes.local_set, tmp ],
|
4263
|
+
|
4264
|
+
...setVar,
|
4244
4265
|
|
4245
4266
|
[ Opcodes.block, Blocktype.void ],
|
4246
4267
|
[ Opcodes.block, Blocktype.void ],
|
@@ -4263,6 +4284,7 @@ const generateForOf = (scope, decl) => {
|
|
4263
4284
|
]
|
4264
4285
|
}),
|
4265
4286
|
|
4287
|
+
// note: should be impossible to reach?
|
4266
4288
|
default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
|
4267
4289
|
}, Blocktype.void));
|
4268
4290
|
|
@@ -4278,10 +4300,16 @@ const generateForOf = (scope, decl) => {
|
|
4278
4300
|
const generateForIn = (scope, decl) => {
|
4279
4301
|
const out = [];
|
4280
4302
|
|
4281
|
-
|
4282
|
-
|
4283
|
-
|
4284
|
-
|
4303
|
+
let count = 0;
|
4304
|
+
for (let i = 0; i < depth.length; i++) {
|
4305
|
+
if (depth[i] === 'forin') count++;
|
4306
|
+
}
|
4307
|
+
|
4308
|
+
const iterType = getNodeType(scope, decl.right);
|
4309
|
+
|
4310
|
+
const pointer = localTmp(scope, '#forin_base_pointer' + count, Valtype.i32);
|
4311
|
+
const length = localTmp(scope, '#forin_length' + count, Valtype.i32);
|
4312
|
+
const counter = localTmp(scope, '#forin_counter' + count, Valtype.i32);
|
4285
4313
|
|
4286
4314
|
out.push(
|
4287
4315
|
// set pointer as right
|
@@ -4293,6 +4321,14 @@ const generateForIn = (scope, decl) => {
|
|
4293
4321
|
...number(0, Valtype.i32),
|
4294
4322
|
[ Opcodes.local_set, counter ],
|
4295
4323
|
|
4324
|
+
...iterType,
|
4325
|
+
...number(TYPES.object, Valtype.i32),
|
4326
|
+
[ Opcodes.i32_eq ],
|
4327
|
+
[ Opcodes.i32_eqz ],
|
4328
|
+
[ Opcodes.if, Blocktype.void ],
|
4329
|
+
...internalThrow(scope, 'TypeError', `Tried for..in on unsupported type`),
|
4330
|
+
[ Opcodes.end ],
|
4331
|
+
|
4296
4332
|
// get length
|
4297
4333
|
[ Opcodes.local_get, pointer ],
|
4298
4334
|
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
@@ -4306,40 +4342,41 @@ const generateForIn = (scope, decl) => {
|
|
4306
4342
|
depth.push('block');
|
4307
4343
|
depth.push('if');
|
4308
4344
|
|
4309
|
-
|
4310
|
-
|
4345
|
+
const tmpName = '#forin_tmp' + count;
|
4346
|
+
const tmp = localTmp(scope, tmpName, Valtype.i32);
|
4347
|
+
localTmp(scope, tmpName + '#type', Valtype.i32);
|
4311
4348
|
|
4312
|
-
let
|
4313
|
-
if (!leftName && decl.left.name) {
|
4314
|
-
// todo: should be sloppy mode only
|
4315
|
-
leftName = decl.left.name;
|
4349
|
+
let setVar;
|
4316
4350
|
|
4317
|
-
|
4351
|
+
if (decl.left.type === 'Identifier') {
|
4352
|
+
// todo: should be sloppy mode only
|
4353
|
+
setVar = generateVarDstr(scope, 'var', decl.left.name, { type: 'Identifier', name: tmpName }, undefined, true);
|
4354
|
+
} else {
|
4355
|
+
// todo: verify this is correct
|
4356
|
+
const global = scope.name === 'main' && decl.left.kind === 'var';
|
4357
|
+
setVar = generateVarDstr(scope, 'var', decl.left.declarations[0].id, { type: 'Identifier', name: tmpName }, undefined, global);
|
4318
4358
|
}
|
4319
4359
|
|
4320
|
-
const [ local, isGlobal ] = lookupName(scope, leftName);
|
4321
|
-
if (!local) return todo(scope, 'for of failed to get left local (probably destructure)');
|
4322
|
-
|
4323
4360
|
// set type for local
|
4324
4361
|
// todo: optimize away counter and use end pointer
|
4325
|
-
out.push(...typeSwitch(scope,
|
4362
|
+
out.push(...typeSwitch(scope, iterType, {
|
4326
4363
|
[TYPES.object]: [
|
4327
4364
|
[ Opcodes.loop, Blocktype.void ],
|
4328
4365
|
|
4329
4366
|
// read key
|
4330
4367
|
[ Opcodes.local_get, pointer ],
|
4331
4368
|
[ Opcodes.i32_load, 0, 5 ],
|
4332
|
-
[ Opcodes.local_tee,
|
4369
|
+
[ Opcodes.local_tee, tmp ],
|
4333
4370
|
|
4334
|
-
...setType(scope,
|
4371
|
+
...setType(scope, tmpName, [
|
4335
4372
|
[ Opcodes.i32_const, 31 ],
|
4336
4373
|
[ Opcodes.i32_shr_u ],
|
4337
4374
|
[ Opcodes.if, Valtype.i32 ],
|
4338
4375
|
// unset MSB in tmp
|
4339
|
-
[ Opcodes.local_get,
|
4376
|
+
[ Opcodes.local_get, tmp ],
|
4340
4377
|
...number(0x7fffffff, Valtype.i32),
|
4341
4378
|
[ Opcodes.i32_and ],
|
4342
|
-
[ Opcodes.local_set,
|
4379
|
+
[ Opcodes.local_set, tmp ],
|
4343
4380
|
|
4344
4381
|
[ Opcodes.i32_const, ...unsignedLEB128(TYPES.string) ],
|
4345
4382
|
[ Opcodes.else ],
|
@@ -4347,9 +4384,7 @@ const generateForIn = (scope, decl) => {
|
|
4347
4384
|
[ Opcodes.end ]
|
4348
4385
|
]),
|
4349
4386
|
|
4350
|
-
|
4351
|
-
Opcodes.i32_from_u,
|
4352
|
-
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
4387
|
+
...setVar,
|
4353
4388
|
|
4354
4389
|
[ Opcodes.block, Blocktype.void ],
|
4355
4390
|
|
@@ -4385,6 +4420,7 @@ const generateForIn = (scope, decl) => {
|
|
4385
4420
|
],
|
4386
4421
|
|
4387
4422
|
// todo: use Object.keys as fallback
|
4423
|
+
// should be unreachable?
|
4388
4424
|
default: internalThrow(scope, 'TypeError', `Tried for..in on unsupported type`)
|
4389
4425
|
}, Blocktype.void));
|
4390
4426
|
|
@@ -4665,6 +4701,8 @@ const generateTry = (scope, decl) => {
|
|
4665
4701
|
out.push(...finalizer);
|
4666
4702
|
|
4667
4703
|
if (decl.handler) {
|
4704
|
+
// todo: allow catching error values
|
4705
|
+
// todo: when we can do that, allow destructuring error values
|
4668
4706
|
depth.pop();
|
4669
4707
|
depth.push('catch');
|
4670
4708
|
|
@@ -5569,6 +5607,7 @@ const generateFunc = (scope, decl) => {
|
|
5569
5607
|
|
5570
5608
|
const prelude = [];
|
5571
5609
|
const defaultValues = {};
|
5610
|
+
const destructuredArgs = {};
|
5572
5611
|
for (let i = 0; i < params.length; i++) {
|
5573
5612
|
let name;
|
5574
5613
|
const x = params[i];
|
@@ -5589,9 +5628,12 @@ const generateFunc = (scope, decl) => {
|
|
5589
5628
|
func.hasRestArgument = true;
|
5590
5629
|
break;
|
5591
5630
|
}
|
5592
|
-
}
|
5593
5631
|
|
5594
|
-
|
5632
|
+
default:
|
5633
|
+
name = '#arg_dstr' + i;
|
5634
|
+
destructuredArgs[name] = x;
|
5635
|
+
break;
|
5636
|
+
}
|
5595
5637
|
|
5596
5638
|
allocVar(func, name, false);
|
5597
5639
|
if (typedInput && params[i].typeAnnotation) {
|
@@ -5644,6 +5686,12 @@ const generateFunc = (scope, decl) => {
|
|
5644
5686
|
);
|
5645
5687
|
}
|
5646
5688
|
|
5689
|
+
for (const x in destructuredArgs) {
|
5690
|
+
prelude.push(
|
5691
|
+
...generateVarDstr(func, 'var', destructuredArgs[x], { type: 'Identifier', name: x }, undefined, false)
|
5692
|
+
);
|
5693
|
+
}
|
5694
|
+
|
5647
5695
|
if (decl.async) {
|
5648
5696
|
// make out promise local
|
5649
5697
|
allocVar(func, '#async_out_promise', false, false);
|