diff --git a/compiler/codegen.js b/compiler/codegen.js index 0a0433ea..5dde060d 100644 --- a/compiler/codegen.js +++ b/compiler/codegen.js @@ -347,11 +347,16 @@ const createVar = (scope, kind, name, global, type = true) => { } const pushScope = (scope) => { - return { ...scope, upper: scope, variables: {} }; + scope.scopeQueue ??= []; + const vars = scope.variables; + scope.scopeQueue.push(vars); + scope.variables = {}; + return scope; } -const popScope = (scope, inner) => { - scope.localInd = inner.localInd; +const popScope = (scope) => { + const vars = scope.scopeQueue.pop() ?? {}; + scope.variables = vars; } const setVar = (scope, name, wasm, typeWasm, tee = false, initalizing = false) => { @@ -4107,18 +4112,18 @@ const generateIf = (scope, decl) => { out.push([ Opcodes.if, Blocktype.void ]); depth.push('if'); - const newScope = pushScope(scope); - const consOut = generate(newScope, decl.consequent); - popScope(scope, newScope); + pushScope(scope); + const consOut = generate(scope, decl.consequent); + popScope(scope); disposeLeftover(consOut); out.push(...consOut); if (decl.alternate) { out.push([ Opcodes.else ]); - const newScope = pushScope(scope); - const altOut = generate(newScope, decl.alternate); - popScope(scope, newScope); + pushScope(scope); + const altOut = generate(scope, decl.alternate); + popScope(scope); disposeLeftover(altOut); out.push(...altOut); @@ -4294,22 +4299,22 @@ const generateForOf = (scope, decl) => { const tmpName = '#forof_tmp' + count; const tmp = allocVar(scope, tmpName, false); - const newScope = pushScope(scope); + pushScope(scope); // setup local for left let initVar; if (decl.left.type !== 'VariableDeclaration') { - if (scope.strict) return internalThrow(newScope, 'ReferenceError', `${decl.left.name} is not defined`); - initVar = generateVarDstr(newScope, 'bare', decl.left, { type: 'Identifier', name: tmpName }, undefined, true); + if (scope.strict) return internalThrow(scope, 'ReferenceError', `${decl.left.name} is not defined`); + initVar = generateVarDstr(scope, 'bare', decl.left, { type: 'Identifier', name: tmpName }, undefined, true); } else { // todo: verify this is correct const global = scope.name === 'main' && decl.left.kind === 'var'; - initVar = generateVarDstr(newScope, decl.left.kind, decl.left.declarations[0].id, { type: 'Identifier', name: tmpName }, undefined, global); + initVar = generateVarDstr(scope, decl.left.kind, decl.left.declarations[0].id, { type: 'Identifier', name: tmpName }, undefined, global); } // set type for local // todo: optimize away counter and use end pointer - out.push(...typeSwitch(newScope, iterType, { + out.push(...typeSwitch(scope, iterType, { [TYPES.array]: [ [ Opcodes.loop, Blocktype.void ], @@ -4318,7 +4323,7 @@ const generateForOf = (scope, decl) => { [ Opcodes.local_set, tmp ], - ...setType(newScope, tmpName, [ + ...setType(scope, tmpName, [ [ Opcodes.local_get, pointer ], [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ], ]), @@ -4327,7 +4332,7 @@ const generateForOf = (scope, decl) => { [ Opcodes.block, Blocktype.void ], [ Opcodes.block, Blocktype.void ], - ...generate(newScope, decl.body), + ...generate(scope, decl.body), [ Opcodes.end ], // increment iter pointer by valtype size + 1 @@ -4352,11 +4357,11 @@ const generateForOf = (scope, decl) => { ], [TYPES.string]: [ - ...setType(newScope, tmpName, TYPES.string), + ...setType(scope, tmpName, TYPES.string), // allocate out string - [ Opcodes.call, includeBuiltin(newScope, '__Porffor_allocate').index ], - [ Opcodes.local_tee, localTmp(newScope, '#forof_allocd', Valtype.i32) ], + [ Opcodes.call, includeBuiltin(scope, '__Porffor_allocate').index ], + [ Opcodes.local_tee, localTmp(scope, '#forof_allocd', Valtype.i32) ], // set length to 1 ...number(1, Valtype.i32), @@ -4365,7 +4370,7 @@ const generateForOf = (scope, decl) => { [ Opcodes.loop, Blocktype.void ], // use as pointer for store later - [ Opcodes.local_get, localTmp(newScope, '#forof_allocd', Valtype.i32) ], + [ Opcodes.local_get, localTmp(scope, '#forof_allocd', Valtype.i32) ], // load current string ind {arg} [ Opcodes.local_get, pointer ], @@ -4375,7 +4380,7 @@ const generateForOf = (scope, decl) => { [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ], // return new string (page) - [ Opcodes.local_get, localTmp(newScope, '#forof_allocd', Valtype.i32) ], + [ Opcodes.local_get, localTmp(scope, '#forof_allocd', Valtype.i32) ], Opcodes.i32_from_u, [ Opcodes.local_set, tmp ], @@ -4383,7 +4388,7 @@ const generateForOf = (scope, decl) => { [ Opcodes.block, Blocktype.void ], [ Opcodes.block, Blocktype.void ], - ...generate(newScope, decl.body), + ...generate(scope, decl.body), [ Opcodes.end ], // increment iter pointer by valtype size @@ -4407,11 +4412,11 @@ const generateForOf = (scope, decl) => { [ Opcodes.end ] ], [TYPES.bytestring]: [ - ...setType(newScope, tmpName, TYPES.bytestring), + ...setType(scope, tmpName, TYPES.bytestring), // allocate out string - [ Opcodes.call, includeBuiltin(newScope, '__Porffor_allocate').index ], - [ Opcodes.local_tee, localTmp(newScope, '#forof_allocd', Valtype.i32) ], + [ Opcodes.call, includeBuiltin(scope, '__Porffor_allocate').index ], + [ Opcodes.local_tee, localTmp(scope, '#forof_allocd', Valtype.i32) ], // set length to 1 ...number(1, Valtype.i32), @@ -4420,7 +4425,7 @@ const generateForOf = (scope, decl) => { [ Opcodes.loop, Blocktype.void ], // use as pointer for store later - [ Opcodes.local_get, localTmp(newScope, '#forof_allocd', Valtype.i32) ], + [ Opcodes.local_get, localTmp(scope, '#forof_allocd', Valtype.i32) ], // load current string ind {arg} [ Opcodes.local_get, pointer ], @@ -4432,7 +4437,7 @@ const generateForOf = (scope, decl) => { [ Opcodes.i32_store8, 0, ValtypeSize.i32 ], // return new string (page) - [ Opcodes.local_get, localTmp(newScope, '#forof_allocd', Valtype.i32) ], + [ Opcodes.local_get, localTmp(scope, '#forof_allocd', Valtype.i32) ], Opcodes.i32_from_u, [ Opcodes.local_set, tmp ], @@ -4440,7 +4445,7 @@ const generateForOf = (scope, decl) => { [ Opcodes.block, Blocktype.void ], [ Opcodes.block, Blocktype.void ], - ...generate(newScope, decl.body), + ...generate(scope, decl.body), [ Opcodes.end ], // increment counter by 1 @@ -4466,7 +4471,7 @@ const generateForOf = (scope, decl) => { [ Opcodes.local_set, tmp ], - ...setType(newScope, tmpName, [ + ...setType(scope, tmpName, [ [ Opcodes.local_get, pointer ], [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ], ]), @@ -4475,7 +4480,7 @@ const generateForOf = (scope, decl) => { [ Opcodes.block, Blocktype.void ], [ Opcodes.block, Blocktype.void ], - ...generate(newScope, decl.body), + ...generate(scope, decl.body), [ Opcodes.end ], // increment iter pointer by valtype size + 1 @@ -4567,7 +4572,7 @@ const generateForOf = (scope, decl) => { ], }, { prelude: [ - ...setType(newScope, tmpName, TYPES.number), + ...setType(scope, tmpName, TYPES.number), [ Opcodes.loop, Blocktype.void ], @@ -4582,7 +4587,7 @@ const generateForOf = (scope, decl) => { [ Opcodes.block, Blocktype.void ], [ Opcodes.block, Blocktype.void ], - ...generate(newScope, decl.body), + ...generate(scope, decl.body), [ Opcodes.end ], // increment counter by 1 @@ -4602,12 +4607,12 @@ const generateForOf = (scope, decl) => { }), // note: should be impossible to reach? - default: internalThrow(newScope, 'TypeError', `Tried for..of on non-iterable type`) + default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`) }, Blocktype.void)); out.push([ Opcodes.end ]); // end if - popScope(scope, newScope); + popScope(scope); depth.pop(); depth.pop(); @@ -4665,21 +4670,21 @@ const generateForIn = (scope, decl) => { const tmp = localTmp(scope, tmpName, Valtype.i32); localTmp(scope, tmpName + '#type', Valtype.i32); - const newScope = pushScope(scope); + pushScope(scope); let initVar; if (decl.left.type === 'Identifier') { - if (scope.strict) return internalThrow(newScope, 'ReferenceError', `${decl.left.name} is not defined`); - initVar = generateVarDstr(newScope, 'var', decl.left, { type: 'Identifier', name: tmpName }, undefined, true); + if (scope.strict) return internalThrow(scope, 'ReferenceError', `${decl.left.name} is not defined`); + initVar = generateVarDstr(scope, 'var', decl.left, { type: 'Identifier', name: tmpName }, undefined, true); } else { // todo: verify this is correct const global = scope.name === 'main' && decl.left.kind === 'var'; - initVar = generateVarDstr(newScope, decl.left.kind, decl.left?.declarations?.[0]?.id ?? decl.left, { type: 'Identifier', name: tmpName }, undefined, global); + initVar = generateVarDstr(scope, decl.left.kind, decl.left?.declarations?.[0]?.id ?? decl.left, { type: 'Identifier', name: tmpName }, undefined, global); } // set type for local // todo: optimize away counter and use end pointer - out.push(...typeSwitch(newScope, iterType, { + out.push(...typeSwitch(scope, iterType, { [TYPES.object]: [ [ Opcodes.loop, Blocktype.void ], @@ -4688,7 +4693,7 @@ const generateForIn = (scope, decl) => { [ Opcodes.i32_load, 0, 5 ], [ Opcodes.local_tee, tmp ], - ...setType(newScope, tmpName, [ + ...setType(scope, tmpName, [ [ Opcodes.i32_const, 31 ], [ Opcodes.i32_shr_u ], [ Opcodes.if, Valtype.i32 ], @@ -4715,7 +4720,7 @@ const generateForIn = (scope, decl) => { [ Opcodes.i32_const, 0b0100 ], [ Opcodes.i32_and ], [ Opcodes.if, Blocktype.void ], - ...generate(newScope, decl.body), + ...generate(scope, decl.body), [ Opcodes.end ], // increment pointer by 14 @@ -4741,12 +4746,12 @@ const generateForIn = (scope, decl) => { // todo: use Object.keys as fallback // should be unreachable? - default: internalThrow(newScope, 'TypeError', `Tried for..in on unsupported type`) + default: internalThrow(scope, 'TypeError', `Tried for..in on unsupported type`) }, Blocktype.void)); out.push([ Opcodes.end ]); // end if - popScope(scope, newScope); + popScope(scope); depth.pop(); depth.pop(); @@ -4809,7 +4814,7 @@ const generateSwitch = (scope, decl) => { cases.push(cases.splice(defaultCase, 1)[0]); } - const newScope = pushScope(scope); + pushScope(scope); for (let i = 0; i < cases.length; i++) { out.push([ Opcodes.block, Blocktype.void ]); @@ -4822,7 +4827,7 @@ const generateSwitch = (scope, decl) => { // todo: this should use same value zero out.push( [ Opcodes.local_get, tmp ], - ...generate(newScope, x.test), + ...generate(scope, x.test), [ Opcodes.eq ], [ Opcodes.br_if, i ] ); @@ -4837,14 +4842,14 @@ const generateSwitch = (scope, decl) => { depth.pop(); out.push( [ Opcodes.end ], - ...generateCode(newScope, { body: cases[i].consequent }) + ...generateCode(scope, { body: cases[i].consequent }) ); } out.push([ Opcodes.end ]); depth.pop(); - popScope(scope, newScope); + popScope(scope); return out; }; @@ -6371,7 +6376,7 @@ const generateFunc = (scope, decl) => { const generateCode = (scope, decl) => { let out = []; - const newScope = decl._funcBody ? scope : pushScope(scope); + const blockScope = decl._funcBody ? scope : pushScope(scope); const body = decl.body; // let eager = []; @@ -6424,19 +6429,19 @@ const generateCode = (scope, decl) => { } for (const name of names) { - newScope.variables[name] = { nonLocal: false, kind: decl.kind, scope, name }; + blockScope.variables[name] = { nonLocal: false, kind: decl.kind, scope, name }; } } // for (const x of eager) { - // out = out.concat(generate(newScope, x)); + // out = out.concat(generate(blockScope, x)); // } for (const x of body) { - out = out.concat(generate(newScope, x)); + out = out.concat(generate(blockScope, x)); } - popScope(scope, newScope); + if (!decl._funcBody) popScope(blockScope); return out; };