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.
@@ -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 [ [ Opcodes.local_get, local.idx ] ];
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 generateVar = (scope, decl) => {
3004
- let out = [];
3005
-
3008
+ const generateVarDstr = (scope, kind, pattern, init, defaultValue, global) => {
3006
3009
  const topLevel = scope.name === 'main';
3007
3010
 
3008
- // global variable if in top scope (main) or if internally wanted
3009
- const global = decl._global ?? (topLevel || decl._bare);
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
- const name = mapName(x.id.name);
3206
- if (!name) return todo(scope, `variable declarators of type ${x.id.type} are not supported yet`)
3015
+ if (pattern.type === 'Identifier') {
3016
+ let out = [];
3017
+ const name = mapName(pattern.name);
3207
3018
 
3208
- if (x.init && isFuncType(x.init.type)) {
3019
+ if (init && isFuncType(init.type)) {
3209
3020
  // hack for let a = function () { ... }
3210
- if (!x.init.id) {
3211
- x.init.id = { name };
3212
- generateFunc(scope, x.init);
3213
- continue;
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 (decl.kind !== 'var') return internalThrow(scope, 'SyntaxError', `Identifier '${unhackName(name)}' has already been declared`);
3030
+ if (kind !== 'var') return internalThrow(scope, 'SyntaxError', `Identifier '${unhackName(name)}' has already been declared`);
3220
3031
 
3221
- continue; // always ignore
3032
+ return out; // always ignore
3222
3033
  }
3223
3034
 
3224
3035
  // // generate init before allocating var
3225
3036
  // let generated;
3226
- // if (x.init) generated = generate(scope, x.init, global, name);
3037
+ // if (init) generated = generate(scope, init, global, name);
3227
3038
 
3228
- const typed = typedInput && x.id.typeAnnotation;
3229
- let idx = allocVar(scope, name, global, !(typed && extractTypeAnnotation(x.id).type != null));
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(x.id));
3043
+ addVarMetadata(scope, name, global, extractTypeAnnotation(pattern));
3233
3044
  }
3234
3045
 
3235
- if (x.init) {
3046
+ if (init) {
3236
3047
  const alreadyArray = scope.arrays?.get(name) != null;
3237
3048
 
3238
- let newOut = generate(scope, x.init, global, name);
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, x.init))
3056
+ ...setType(scope, name, getNodeType(scope, init))
3246
3057
  );
3247
3058
  } else {
3248
- newOut = setLocalWithType(scope, name, global, newOut, false, getNodeType(scope, x.init));
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 (x._default) {
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, x._default, global, name),
3068
+ ...generate(scope, defaultValue, global, name),
3258
3069
  [ global ? Opcodes.global_set : Opcodes.local_set, idx ],
3259
- ...setType(scope, name, getNodeType(scope, x._default)),
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 (decl.left.type === 'MemberExpression' && decl.left.property.name === 'length') {
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 (decl.left.type === 'MemberExpression') {
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
- ...generateVar(scope, { kind: 'var', _bare: true, declarations: [ { id: { name }, init: decl.right } ] }),
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
- // todo: for of inside for of might fuck up?
3939
- const pointer = localTmp(scope, 'forof_base_pointer', Valtype.i32);
3940
- const length = localTmp(scope, 'forof_length', Valtype.i32);
3941
- const counter = localTmp(scope, 'forof_counter', Valtype.i32);
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
- generate(scope, decl.left);
3981
+ let setVar;
3968
3982
 
3969
- let leftName = decl.left.declarations?.[0]?.id?.name;
3970
- if (!leftName && decl.left.name) {
3983
+ if (decl.left.type === 'Identifier') {
3971
3984
  // todo: should be sloppy mode only
3972
- leftName = decl.left.name;
3973
-
3974
- generateVar(scope, { kind: 'var', _bare: true, declarations: [ { id: { name: leftName } } ] })
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, getNodeType(scope, decl.right), {
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
- ...setType(scope, leftName, [
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
- [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
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, leftName, TYPES.string),
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
- Opcodes.i32_from_u,
4062
+ Opcodes.i32_from_u,
4063
+ [ Opcodes.local_set, tmp ],
4048
4064
 
4049
- [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
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, leftName, TYPES.bytestring),
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
- [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
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
- ...setType(scope, leftName, [
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
- [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
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, leftName, TYPES.number),
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
- [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
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
- // todo: for in inside for in might fuck up?
4282
- const pointer = localTmp(scope, '#forin_base_pointer', Valtype.i32);
4283
- const length = localTmp(scope, '#forin_length', Valtype.i32);
4284
- const counter = localTmp(scope, '#forin_counter', Valtype.i32);
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
- // setup local for left
4310
- generate(scope, decl.left);
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 leftName = decl.left.declarations?.[0]?.id?.name;
4313
- if (!leftName && decl.left.name) {
4314
- // todo: should be sloppy mode only
4315
- leftName = decl.left.name;
4349
+ let setVar;
4316
4350
 
4317
- generateVar(scope, { kind: 'var', _bare: true, declarations: [ { id: { name: leftName } } ] })
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, getNodeType(scope, decl.right), {
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, localTmp(scope, '#forin_tmp', Valtype.i32) ],
4369
+ [ Opcodes.local_tee, tmp ],
4333
4370
 
4334
- ...setType(scope, leftName, [
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, localTmp(scope, '#forin_tmp', Valtype.i32) ],
4376
+ [ Opcodes.local_get, tmp ],
4340
4377
  ...number(0x7fffffff, Valtype.i32),
4341
4378
  [ Opcodes.i32_and ],
4342
- [ Opcodes.local_set, localTmp(scope, '#forin_tmp', Valtype.i32) ],
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
- [ Opcodes.local_get, localTmp(scope, '#forin_tmp', Valtype.i32) ],
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
- // if (name == null) return todo('non-identifier args are not supported');
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);