diff --git a/ts2panda/src/addVariable2Scope.ts b/ts2panda/src/addVariable2Scope.ts index a4981ce76c0df394459c704b545314f7ca2ab898..b3b0f87bbf5dbb5ab0269b94fa94a0e1b126bc4c 100644 --- a/ts2panda/src/addVariable2Scope.ts +++ b/ts2panda/src/addVariable2Scope.ts @@ -33,6 +33,10 @@ import { import { isGlobalIdentifier } from "./syntaxCheckHelper"; import { TypeRecorder } from "./typeRecorder"; import { + MandatoryArguments, + MandatoryFuncObj, + MandatoryNewTarget, + MandatoryThis, VarDeclarationKind, Variable } from "./variable"; @@ -61,15 +65,15 @@ function setTypeIndex(node: ts.Node, v: Variable | undefined, isClassOrFunction: function addInnerArgs(node: ts.Node, scope: VariableScope, enableTypeRecord: boolean): void { // the first argument for js function is func_obj - scope.addParameter("4funcObj", VarDeclarationKind.CONST, -1); + scope.addParameter(MandatoryFuncObj, VarDeclarationKind.CONST, -1); // the second argument for newTarget if (node.kind == ts.SyntaxKind.ArrowFunction) { scope.addParameter("0newTarget", VarDeclarationKind.CONST, -1); scope.addParameter("0this", VarDeclarationKind.CONST, 0); } else { - scope.addParameter("4newTarget", VarDeclarationKind.CONST, -1); - scope.addParameter("this", VarDeclarationKind.CONST, 0); + scope.addParameter(MandatoryNewTarget, VarDeclarationKind.CONST, -1); + scope.addParameter(MandatoryThis, VarDeclarationKind.CONST, 0); } if (CmdOptions.isCommonJs() && node.kind === ts.SyntaxKind.SourceFile) { @@ -88,12 +92,12 @@ function addInnerArgs(node: ts.Node, scope: VariableScope, enableTypeRecord: boo if (scope.getUseArgs() || CmdOptions.isDebugMode()) { if (ts.isArrowFunction(node)) { let parentVariableScope = scope.getParentVariableScope(); - parentVariableScope.add("arguments", VarDeclarationKind.CONST, InitStatus.INITIALIZED); + parentVariableScope.add(MandatoryArguments, VarDeclarationKind.CONST, InitStatus.INITIALIZED); parentVariableScope.setUseArgs(true); scope.setUseArgs(false); } else if (scope.getUseArgs()){ - if (!scope.findLocal("arguments")) { - scope.add("arguments", VarDeclarationKind.CONST, InitStatus.INITIALIZED); + if (!scope.findLocal(MandatoryArguments)) { + scope.add(MandatoryArguments, VarDeclarationKind.CONST, InitStatus.INITIALIZED); } } } diff --git a/ts2panda/src/compiler.ts b/ts2panda/src/compiler.ts index a4027cacb97450e3d2eff5e76f4721372e53279d..72fc09ceb2a28722fac8a23699b5ce283e42a54d 100644 --- a/ts2panda/src/compiler.ts +++ b/ts2panda/src/compiler.ts @@ -107,14 +107,17 @@ import { transformTryCatchFinally, TryBuilder, TryBuilderBase, - TryStatement, - updateCatchTables + TryStatement } from "./statement/tryStatement"; import { isStrictMode } from "./strictMode"; import { isAssignmentOperator } from "./syntaxCheckHelper"; import { GlobalVariable, LocalVariable, + MandatoryArguments, + MandatoryFuncObj, + MandatoryNewTarget, + MandatoryThis, ModuleVariable, VarDeclarationKind, Variable @@ -189,7 +192,8 @@ export class Compiler { let funcName = jshelpers.getTextOfIdentifierOrLiteral((rootNode).name); let v = functionScope.find(funcName); if (v.scope == functionScope) { - this.pandaGen.loadAccumulator(NodeKind.FirstNodeOfFunction, getVregisterCache(this.pandaGen, CacheList.FUNC)); + this.pandaGen.loadAccumulator(NodeKind.FirstNodeOfFunction, + getVregisterCache(this.pandaGen, CacheList.FUNC)); this.pandaGen.storeAccToLexEnv(NodeKind.FirstNodeOfFunction, v.scope, v.level, v.v, true); } } @@ -197,53 +201,32 @@ export class Compiler { private compileLexicalBindingForArrowFunction() { let rootNode = this.rootNode; + if (ts.isArrowFunction(rootNode)) { + return; + } - if (!ts.isArrowFunction(rootNode)) { - let childVariableScopes: Array = (this.scope).getChildVariableScope(); - let hasAFChild = false; - - childVariableScopes.forEach(scope => { - let funcNode: ts.Node = scope.getBindingNode(); - - if (ts.isArrowFunction(funcNode)) { - hasAFChild = true; - } - }); - if (!hasAFChild) { - return ; - } - this.storeSpecialArg2LexEnv("4newTarget"); - this.storeSpecialArg2LexEnv("arguments"); - - if (ts.isConstructorDeclaration(rootNode) && rootNode.parent.heritageClauses) { - this.storeSpecialArg2LexEnv("4funcObj"); - return; - } + this.storeMandatoryArgToLexEnv(MandatoryFuncObj); + this.storeMandatoryArgToLexEnv(MandatoryNewTarget); + this.storeMandatoryArgToLexEnv(MandatoryThis); - this.storeSpecialArg2LexEnv("this"); + let rootScope: VariableScope = this.recorder.getScopeOfNode(rootNode); + if (rootScope.getUseArgs()) { + this.storeMandatoryArgToLexEnv(MandatoryArguments); } } - private storeSpecialArg2LexEnv(arg: string) { - let variableInfo = this.scope.find(arg); - let v = variableInfo.v; - let pandaGen = this.pandaGen; - - if (CmdOptions.isDebugMode()) { - variableInfo.scope!.setLexVar(v!, this.scope); - pandaGen.storeLexicalVar(this.rootNode, variableInfo.level, - (variableInfo.v).idxLex, - pandaGen.getVregForVariable(variableInfo.v)); - } else { - if (v && v.isLexVar) { - if (arg === "arguments" && variableInfo.scope instanceof FunctionScope) { - variableInfo.scope.setArgumentsOrRestargs(); - } - let vreg = "4funcObj" === arg ? getVregisterCache(pandaGen, CacheList.FUNC) : - pandaGen.getVregForVariable(variableInfo.v); - pandaGen.storeLexicalVar(this.rootNode, variableInfo.level, v.idxLex, vreg); - } + private storeMandatoryArgToLexEnv(arg: string) { + let v = this.scope.findLocal(arg); + if (!v) { + throw new Error("Mandatory Arguments should be found locally"); } + if (!v.lexical()) { + return; + } + + let vreg = arg == MandatoryFuncObj ? getVregisterCache(this.pandaGen, CacheList.FUNC) : + this.pandaGen.getVregForVariable(v); + this.pandaGen.storeLexicalVar(this.rootNode, 0, v.lexIndex(), vreg); } private compileSourceFileOrBlock(body: ts.SourceFile | ts.Block) { @@ -1005,33 +988,23 @@ export class Compiler { checkValidUseSuperBeforeSuper(this, node); - let { scope, level, v } = this.scope.find("this"); + let { scope, level, v } = this.scope.find(MandatoryThis); if (!v) { throw new Error("\"this\" not found"); } if (v instanceof LocalVariable) { - if (scope && level >= 0) { - let curScope = this.scope; - let needSetLexVar: boolean = false; - while (curScope != scope) { - if (curScope instanceof VariableScope) { - needSetLexVar = true; - break; - } - curScope = curScope.getParent(); - } - - if (needSetLexVar) { - scope.setLexVar(v, this.scope); - } + if (CmdOptions.isWatchEvaluateExpressionMode()) { + pandaGen.loadByNameViaDebugger(node, MandatoryThis, CacheList.True); + return; } - CmdOptions.isWatchEvaluateExpressionMode() ? pandaGen.loadByNameViaDebugger(node, "this", CacheList.True) - : pandaGen.loadAccFromLexEnv(node, scope!, level, v); - } else { - throw new Error("\"this\" must be a local variable"); + + pandaGen.loadAccFromLexEnv(node, scope!, level, v); + return; } + + throw new Error("\"this\" must be a local variable"); } private compileFunctionExpression(expr: ts.FunctionExpression) { @@ -1404,27 +1377,10 @@ export class Compiler { getThis(node: ts.Node, res: VReg) { let pandaGen = this.pandaGen; - let curScope = this.getCurrentScope(); - let thisInfo = this.getCurrentScope().find("this"); - let scope = thisInfo.scope; + let thisInfo = this.getCurrentScope().find(MandatoryThis); let level = thisInfo.level; let v = thisInfo.v; - if (scope && level >= 0) { - let needSetLexVar: boolean = false; - while (curScope != scope) { - if (curScope instanceof VariableScope) { - needSetLexVar = true; - break; - } - curScope = curScope.getParent(); - } - - if (needSetLexVar) { - scope.setLexVar(v, curScope); - } - } - if (v.isLexVar) { let slot = v.idxLex; pandaGen.loadLexicalVar(node, level, slot); @@ -1436,7 +1392,7 @@ export class Compiler { setThis(node: ts.Node) { let pandaGen = this.pandaGen; - let thisInfo = this.getCurrentScope().find("this"); + let thisInfo = this.getCurrentScope().find(MandatoryThis); if (thisInfo.v!.isLexVar) { let slot = (thisInfo.v).idxLex; @@ -1490,22 +1446,6 @@ export class Compiler { return; } - if (variable.scope && variable.level >= 0) { // inner most function will load outer env instead of new a lex env - let scope = this.scope; - let needSetLexVar: boolean = false; - while (scope != variable.scope) { - if (scope instanceof VariableScope) { - needSetLexVar = true; - break; - } - scope = scope.getParent(); - } - - if (needSetLexVar) { - variable.scope.setLexVar(variable.v, this.scope); - } - } - // storeAcc must after setLexVar, because next statement will emit bc intermediately this.pandaGen.storeAccToLexEnv(node, variable.scope!, variable.level, variable.v, isDeclaration); } else if (variable.v instanceof GlobalVariable) { if (variable.v.isNone() && isStrictMode(node)) { @@ -1557,22 +1497,6 @@ export class Compiler { } } - if (variable.scope && variable.level >= 0) { // leaf function will load outer env instead of new a lex env - let scope = this.scope; - let needSetLexVar: boolean = false; - while (scope != variable.scope) { - if (scope instanceof VariableScope) { - needSetLexVar = true; - break; - } - scope = scope.getParent(); - } - - if (needSetLexVar) { - variable.scope.setLexVar((variable.v), this.scope); - } - } - this.pandaGen.loadAccFromLexEnv(node, variable.scope!, variable.level, (variable.v)); } else if (variable.v instanceof GlobalVariable) { if (variable.v.isNone()) { diff --git a/ts2panda/src/compilerDriver.ts b/ts2panda/src/compilerDriver.ts index dd06cadb150ff2ba3865548a8646304e7ce0aa5d..2998d8008f7244324985f331d80168d6117d6bcd 100644 --- a/ts2panda/src/compilerDriver.ts +++ b/ts2panda/src/compilerDriver.ts @@ -49,7 +49,7 @@ import { TypeRecorder } from "./typeRecorder"; import { LiteralBuffer } from "./base/literal"; import { findOuterNodeOfParenthesis } from "./expression/parenthesizedExpression"; import { IRNode } from "./irnodes"; -import { getRecordName } from "./base/util"; +import { LexicalBinder } from "./lexicalBinder"; export class PendingCompilationUnit { constructor( @@ -179,6 +179,8 @@ export class CompilerDriver { } let recorder = this.compilePrologue(node, true, false); + let lexBinder = new LexicalBinder(node, recorder); + lexBinder.resolve(); // initiate ts2abc if (!CmdOptions.isAssemblyMode()) { @@ -278,6 +280,8 @@ export class CompilerDriver { compileUnitTest(node: ts.SourceFile, literalBufferArray?: Array): void { CompilerDriver.isTsFile = CompilerDriver.isTypeScriptSourceFile(node); let recorder = this.compilePrologue(node, true, true); + let lexBinder = new LexicalBinder(node, recorder); + lexBinder.resolve(); for (let i = 0; i < this.pendingCompilationUnits.length; i++) { let unit: PendingCompilationUnit = this.pendingCompilationUnits[i]; diff --git a/ts2panda/src/expression/callExpression.ts b/ts2panda/src/expression/callExpression.ts index 334a6a4a25c11e74d0da2ce89f54a17dfcbf3592..26c5aeea59ea83e1dc8bf0771bbe87ad0d9e5410 100644 --- a/ts2panda/src/expression/callExpression.ts +++ b/ts2panda/src/expression/callExpression.ts @@ -33,7 +33,7 @@ export function compileCallExpression(expr: ts.CallExpression, compiler: Compile return; } - if ((expr.expression.kind == ts.SyntaxKind.CallExpression) || (expr.expression.kind == ts.SyntaxKind.NewExpression)) { + if (ts.isCallExpression(expr.expression) || ts.isNewExpression(expr.expression)) { let processed = compiler.compileFunctionReturnThis(expr.expression); if (processed) { return; diff --git a/ts2panda/src/expression/metaProperty.ts b/ts2panda/src/expression/metaProperty.ts index de0500b3051ae7f37bf677ab3ee0dc292da34912..f9c7447409c802d6c496807641400d750fd3e7b1 100644 --- a/ts2panda/src/expression/metaProperty.ts +++ b/ts2panda/src/expression/metaProperty.ts @@ -13,6 +13,7 @@ * limitations under the License. */ +import { MandatoryNewTarget } from "../variable"; import * as ts from "typescript"; import { Compiler } from "../compiler"; import * as jshelpers from "../jshelpers"; @@ -21,7 +22,7 @@ export function compileMetaProperty(expr: ts.MetaProperty, compiler: Compiler) { let curScope = compiler.getCurrentScope(); let id = jshelpers.getTextOfIdentifierOrLiteral(expr.name); if (id == "target") { - let { scope, level, v } = curScope.find("4newTarget"); + let { scope, level, v } = curScope.find(MandatoryNewTarget); if (!v) { throw new Error("fail to access new.target"); diff --git a/ts2panda/src/lexicalBinder.ts b/ts2panda/src/lexicalBinder.ts new file mode 100644 index 0000000000000000000000000000000000000000..37aaa5809a9a1ec5454793c618aef879b06dcab5 --- /dev/null +++ b/ts2panda/src/lexicalBinder.ts @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +import * as ts from "typescript"; +import { CmdOptions } from "./cmdOptions"; +import * as jshelpers from "./jshelpers"; +import { Recorder } from "./recorder"; +import { + GlobalScope, + Scope, + VariableScope +} from "./scope"; +import { + isMandatoryParam, + MandatoryArguments, + MandatoryFuncObj, + MandatoryNewTarget, + MandatoryThis, + VarDeclarationKind +} from "./variable"; + + +export class LexicalBinder { + private srcFile: ts.SourceFile; + private recorder: Recorder; + constructor(src: ts.SourceFile, recorder: Recorder) { + this.srcFile = src; + this.recorder = recorder; + } + + resolve() { + this.resolveIdentReference(this.srcFile, this.recorder.getScopeOfNode(this.srcFile)); + } + + resolveIdentReference(node: ts.Node, scope: Scope) { + node.forEachChild((child) => { + let tmp = this.recorder.getScopeOfNode(child); + let newScope = tmp ? tmp : scope; + switch (child.kind) { + case ts.SyntaxKind.Identifier: { // 79 + if (this.hasDeclarationParent(child) || this.isPropertyName(child)) { + break; + } + + this.lookUpLexicalReference(jshelpers.getTextOfIdentifierOrLiteral(child), newScope); + break; + } + case ts.SyntaxKind.SuperKeyword: { + let enclosingVariableScope = newScope.getNearestVariableScope(); + if (!ts.isArrowFunction(enclosingVariableScope.getBindingNode())) { + this.resolveIdentReference(child, newScope); + break; + } + enclosingVariableScope = enclosingVariableScope.getParentVariableScope(); + while (enclosingVariableScope) { + if (!ts.isArrowFunction(enclosingVariableScope.getBindingNode())) { + break; + } + + enclosingVariableScope = enclosingVariableScope.getParentVariableScope(); + } + + this.setMandatoryParamLexical(MandatoryFuncObj, enclosingVariableScope); + this.setMandatoryParamLexical(MandatoryThis, enclosingVariableScope); + this.resolveIdentReference(child, newScope); + break; + } + case ts.SyntaxKind.ThisKeyword: { // 108 + let enclosingVariableScope = newScope.getNearestVariableScope(); + if (!ts.isArrowFunction(enclosingVariableScope.getBindingNode())) { + this.resolveIdentReference(child, newScope); + break; + } + + this.lookUpLexicalReference(MandatoryThis, enclosingVariableScope); + this.resolveIdentReference(child, newScope); + break; + } + case ts.SyntaxKind.Constructor: { // 169 + if (!(child).parent.heritageClauses) { + this.resolveIdentReference(child, newScope); + break; + } + + if (!(newScope).hasAfChild() && + ((newScope).getChildVariableScope().length > 0)) { + this.resolveIdentReference(child, newScope); + break; + } + + this.setMandatoryParamLexical(MandatoryFuncObj, newScope); + this.setMandatoryParamLexical(MandatoryThis, newScope); + this.resolveIdentReference(child, newScope); + break; + } + case ts.SyntaxKind.ArrowFunction: { //212 + this.setMandatoryParamLexicalForNCFuncInDebug(newScope); + this.resolveIdentReference(child, newScope); + break; + } + case ts.SyntaxKind.MetaProperty: { // 229 + let id: string = jshelpers.getTextOfIdentifierOrLiteral((child).name); + if (id != "target") { + this.resolveIdentReference(child, newScope); + break; + } + + let enclosingVariableScope = newScope.getNearestVariableScope(); + if (!ts.isArrowFunction(enclosingVariableScope.getBindingNode())) { + this.resolveIdentReference(child, newScope); + break; + } + + this.lookUpLexicalReference(MandatoryNewTarget, enclosingVariableScope); + this.resolveIdentReference(child, newScope); + break; + } + default: { + this.resolveIdentReference(child, newScope); + } + } + }); + } + + lookUpLexicalReference(name: string, scope: Scope) { + let declPosInfo = scope.resolveDeclPos(name); + if (!declPosInfo.isLexical) { // if find declaration position in the current function + return; + } + + if (isMandatoryParam(name)) { + declPosInfo.v.setLexVar(declPosInfo.defLexicalScope); + return; + } + + // Gloabl declaration should not be set lexical, otherwise redundant lexical env will be created in main + if (declPosInfo.scope instanceof GlobalScope) { + return; + } + + declPosInfo.v.setLexVar(declPosInfo.defLexicalScope); + } + + setMandatoryParamLexical(name: string, scope: VariableScope) { + if (ts.isArrowFunction(scope.getBindingNode())) { + throw new Error("Arrow function should not be processed"); + } + + let v = scope.findLocal(name); + v.setLexVar(scope); + } + + setMandatoryParamLexicalForNCFuncInDebug(scope: VariableScope) { + if (!ts.isArrowFunction(scope.getBindingNode())) { + throw new Error("Non-ArrowFunction should not be processed"); + } + + if (!CmdOptions.isDebugMode()) { + return; + } + + let newTargetPosInfo = scope.resolveDeclPos(MandatoryNewTarget); + if (!newTargetPosInfo.isLexical) { + throw new Error("4newTarget must be lexical"); + } + newTargetPosInfo.v.setLexVar(newTargetPosInfo.defLexicalScope); + + let thisPosInfo = scope.resolveDeclPos(MandatoryThis); + if (!thisPosInfo.isLexical) { + throw new Error("This must be lexical"); + } + thisPosInfo.v.setLexVar(thisPosInfo.defLexicalScope); + + let curScope: VariableScope = scope; + while (curScope) { + if (!ts.isArrowFunction(curScope.getBindingNode())) { + break; + } + + curScope = curScope.getParentVariableScope(); + } + + let v = undefined; + if (curScope.getUseArgs()) { + v = curScope.findLocal(MandatoryArguments); + } else { + v = curScope.add(MandatoryArguments, VarDeclarationKind.CONST); + } + v.setLexVar(curScope); + } + + hasDeclarationParent(id: ts.Identifier): boolean { + let parent = id.parent; + if (ts.isBindingElement(parent) && + parent.name == id) { + while (parent && !ts.isVariableDeclaration(parent)) { + parent = parent.parent; + } + + return parent ? true : false; + } + + if ((ts.isVariableDeclaration(parent) || ts.isClassDeclaration(parent) || + ts.isClassExpression(parent) || ts.isFunctionLike(parent)) + && parent.name == id) { + return true; + } + + return false; + } + + isPropertyName(id: ts.Identifier) { + let parent = id.parent; + if (ts.isPropertyAccessExpression(parent) && (parent.name == id)) { // eg. a.b -> b is the property name + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/ts2panda/src/pandagen.ts b/ts2panda/src/pandagen.ts index b168d001560945f61ada3ed5885e41f359d9a671..10669581180dda673f014dd7b9f47e3f7c350765 100644 --- a/ts2panda/src/pandagen.ts +++ b/ts2panda/src/pandagen.ts @@ -186,7 +186,7 @@ import { } from "./scope"; import { CatchTable } from "./statement/tryStatement"; import { TypeRecorder } from "./typeRecorder"; -import { Variable } from "./variable"; +import { MandatoryArguments, Variable } from "./variable"; import * as jshelpers from "./jshelpers"; import { CompilerDriver } from "./compilerDriver"; import { getLiteralKey } from "./base/util"; @@ -509,7 +509,7 @@ export class PandaGen { loadAccFromArgs(node: ts.Node) { if ((this.scope).getUseArgs()) { - let v = this.scope!.findLocal("arguments"); + let v = this.scope!.findLocal(MandatoryArguments); if (this.scope instanceof FunctionScope) { this.scope.setArgumentsOrRestargs(); } diff --git a/ts2panda/src/recorder.ts b/ts2panda/src/recorder.ts index 2f3881b13d95af3b0db8d9d9c08dc39193bcf449..334d3f233b0e3feef134b9da83ddb15508a4bfc1 100644 --- a/ts2panda/src/recorder.ts +++ b/ts2panda/src/recorder.ts @@ -53,8 +53,7 @@ import { import { checkSyntaxError } from "./syntaxChecker"; import { isGlobalIdentifier, isFunctionLikeDeclaration } from "./syntaxCheckHelper"; import { TypeChecker } from "./typeChecker"; -import { VarDeclarationKind } from "./variable"; -import path from "path"; +import { MandatoryArguments, VarDeclarationKind } from "./variable"; export class Recorder { node: ts.Node; @@ -329,7 +328,7 @@ export class Recorder { } } - if (name == "arguments") { + if (name == MandatoryArguments) { let varialbeScope = scope.getNearestVariableScope(); varialbeScope?.setUseArgs(true); } diff --git a/ts2panda/src/scope.ts b/ts2panda/src/scope.ts index 637dc19b6cb07c071b68b62f40e3179a75d522ff..616d899360330fc4ab5c764d190131241c2eb79a 100644 --- a/ts2panda/src/scope.ts +++ b/ts2panda/src/scope.ts @@ -15,15 +15,17 @@ import * as ts from "typescript"; import { SourceTextModuleRecord } from "./ecmaModule"; -import { LOGD, LOGE } from "./log"; +import { LOGD } from "./log"; import { GlobalVariable, LocalVariable, + MandatoryFuncObj, ModuleVariable, VarDeclarationKind, Variable } from "./variable"; + export enum InitStatus { INITIALIZED, UNINITIALIZED } @@ -194,7 +196,7 @@ export abstract class Scope { while (curScope) { let resolve = null; let tmpLevel = curLevel; // to store current level, not impact by ++ - if (curScope instanceof VariableScope || (curScope instanceof LoopScope && curScope.need2CreateLexEnv())) { + if (curScope.isLexicalScope() && (curScope).need2CreateLexEnv()) { curLevel++; } resolve = curScope.findLocal(name); @@ -226,7 +228,31 @@ export abstract class Scope { return declPos; } - abstract setLexVar(v: Variable, srcScope: Scope): void; + resolveDeclPos(name: string) { + let crossFunc: boolean = false; + let curScope: Scope | undefined = this; + let enclosingVariableScope: VariableScope = this.getNearestVariableScope(); + + while (curScope) { + if (curScope == enclosingVariableScope.parent) { + crossFunc = true; + } + + let result = curScope.findLocal(name); + if (result) { + if (!crossFunc) { + return {isLexical: false, scope: curScope, defLexicalScope: undefined, v: result}; + } + + let enclosingDefLexicalScope = curScope.getNearestLexicalScope(); + return {isLexical: true, scope: curScope, defLexicalScope: enclosingDefLexicalScope, v: result}; + } + + curScope = curScope.parent; + } + + return {isLexical: false, scope: undefined, defLexicalScope: undefined, v: undefined}; + } setDecls(decl: Decl) { this.decls.push(decl); @@ -265,6 +291,11 @@ export abstract class Scope { public getArgumentsOrRestargs() { return this.isArgumentsOrRestargs; } + + isLexicalScope() { + let scope = this; + return ((scope instanceof VariableScope) || (scope instanceof LoopScope)); + } } export abstract class VariableScope extends Scope { @@ -281,6 +312,10 @@ export abstract class VariableScope extends Scope { return this.lexVarInfo; } + addLexVarInfo(name: string, slot: number) { + this.lexVarInfo.set(name, slot); + } + getBindingNode() { return this.node; } @@ -312,7 +347,7 @@ export abstract class VariableScope extends Scope { } addFuncName(funcName: string) { - let funcObj = this.name2variable.get('4funcObj'); + let funcObj = this.name2variable.get(MandatoryFuncObj); this.name2variable.set(funcName, funcObj!); } @@ -341,24 +376,6 @@ export abstract class VariableScope extends Scope { return this.startLexIdx++; } - setLexVar(v: Variable, refScope: Scope) { - if (!v.isLexVar) { - let slot = v.setLexVar(this); - this.lexVarInfo.set(v.getName(), slot); - } - - LOGD(this.debugTag, "VariableScope.setLexVar(" + v.idxLex + ")"); - // set all chain to create env - let scope: Scope | undefined = refScope; - while (scope && scope != this) { - if (scope instanceof VariableScope || (scope instanceof LoopScope && scope.need2CreateLexEnv())) { - scope.pendingCreateEnv(); - } - - scope = scope.getParent(); - } - } - setUseArgs(value: boolean) { this.useArgs = value; } @@ -366,6 +383,17 @@ export abstract class VariableScope extends Scope { getUseArgs(): boolean { return this.useArgs; } + + hasAfChild(): boolean { + let childVariableScopes = this.getChildVariableScope(); + for (let child of childVariableScopes) { + if (ts.isArrowFunction(child.getBindingNode())) { + return true; + } + } + + return false; + } } export class GlobalScope extends VariableScope { @@ -489,12 +517,6 @@ export class LocalScope extends Scope { this.parent = parent } - setLexVar(v: Variable, srcScope: Scope) { - let lexicalScope = this.getNearestLexicalScope(); - lexicalScope.setLexVar(v, srcScope); - } - - add(decl: Decl | string, declKind: VarDeclarationKind, status?: InitStatus): Variable | undefined { let name = decl instanceof Decl ? decl.name : decl; let v: Variable | undefined; @@ -528,27 +550,14 @@ export class LoopScope extends LocalScope { super(parent); } - setLexVar(v: Variable, refScope: Scope) { - if (!v.isLexVar) { - let idxLex = v.setLexVar(this); - this.lexVarInfo.set(v.getName(), idxLex); - } - - LOGD(this.debugTag, "LoopScope.setLexVar(" + v.idxLex + ")"); - let scope: Scope | undefined = refScope; - while (scope && scope != this) { - if (scope instanceof VariableScope || (scope instanceof LoopScope && scope.need2CreateLexEnv())) { - scope.pendingCreateEnv(); - } - - scope = scope.getParent(); - } - } - getLexVarInfo() { return this.lexVarInfo; } + addLexVarInfo(name: string, slot: number) { + this.lexVarInfo.set(name, slot); + } + need2CreateLexEnv(): boolean { return this.needCreateLexEnv; } diff --git a/ts2panda/src/statement/classStatement.ts b/ts2panda/src/statement/classStatement.ts index 3b4368dbcff4b822021647b93817a9f5f4350f81..dcd010e08d82f9e705c0a9c90ff817c0fb62a12e 100644 --- a/ts2panda/src/statement/classStatement.ts +++ b/ts2panda/src/statement/classStatement.ts @@ -48,7 +48,13 @@ import { Scope, VariableScope } from "../scope"; -import { LocalVariable, ModuleVariable, Variable } from "../variable"; +import { + LocalVariable, + MandatoryFuncObj, + MandatoryThis, + ModuleVariable, + Variable +} from "../variable"; export function compileClassDeclaration(compiler: Compiler, stmt: ts.ClassLikeDeclaration) { compiler.pushScope(stmt); @@ -360,26 +366,6 @@ export function compileConstructor(compiler: Compiler, node: ts.ConstructorDecla export function compileSuperCall(compiler: Compiler, node: ts.CallExpression, args: VReg[], hasSpread: boolean) { let pandaGen = compiler.getPandaGen(); - // make sure "this" is stored in lexical env if needed - let curScope = compiler.getCurrentScope(); - let { scope, level, v } = curScope.find("this"); - - if (scope && level >= 0) { - let tmpScope = curScope; - let needSetLexVar: boolean = false; - while (tmpScope != scope) { - if (tmpScope instanceof VariableScope) { - needSetLexVar = true; - } - - tmpScope = tmpScope.getParent(); - } - - if (needSetLexVar) { - scope.setLexVar(v, curScope); - } - } - if (hasSpread) { let argArray = pandaGen.getTemp(); createArrayFromElements(node, compiler, >node.arguments, argArray); @@ -411,29 +397,26 @@ function loadCtorObj(node: ts.CallExpression, compiler: Compiler) { return; } - // TODO the design needs to be reconsidered - // let nearestFuncScope = recorder.getScopeOfNode(nearestFunc); - // if (!nearestFuncScope) { - // return; - // } - if (ts.isConstructorDeclaration(nearestFunc)) { pandaGen.loadAccumulator(node, getVregisterCache(pandaGen, CacheList.FUNC)); } else { - let outerFunc = jshelpers.getContainingFunctionDeclaration(nearestFunc); - let outerFuncScope = recorder.getScopeOfNode(outerFunc!); - outerFuncScope.pendingCreateEnv(); - let level = 1; - while (!ts.isConstructorDeclaration(outerFunc!)) { - outerFunc = jshelpers.getContainingFunctionDeclaration(outerFunc!); - outerFuncScope.pendingCreateEnv(); - level++; + let curFuncScope = recorder.getScopeOfNode(nearestFunc); + let level = curFuncScope.need2CreateLexEnv() ? 0 : -1; + + while (curFuncScope) { + if (curFuncScope.need2CreateLexEnv()) { + level += 1; + } + + if (ts.isConstructorDeclaration(curFuncScope.getBindingNode())) { + break; + } + + curFuncScope = curFuncScope.getParentVariableScope(); } - let funcObj = outerFuncScope.findLocal("4funcObj"); - outerFuncScope.setLexVar(funcObj, outerFuncScope); - let slot = funcObj.idxLex; - pandaGen.loadLexicalVar(node, level, slot); + let funcObj = curFuncScope.findLocal(MandatoryFuncObj); + pandaGen.loadLexicalVar(node, level, funcObj.lexIndex()); } } diff --git a/ts2panda/src/syntaxCheckHelper.ts b/ts2panda/src/syntaxCheckHelper.ts index caaa24b09f83e98f90e890293fafb94d5365e469..38a785e6e9699662729b1c736befc4af1ac937f1 100644 --- a/ts2panda/src/syntaxCheckHelper.ts +++ b/ts2panda/src/syntaxCheckHelper.ts @@ -15,6 +15,7 @@ import * as ts from "typescript"; import * as jshelpers from "./jshelpers"; +import { MandatoryArguments } from "./variable"; export function isOctalNumber(num: string): boolean { if (!num || num.length < 2) { @@ -70,7 +71,7 @@ export function isIncludeOctalEscapeSequence(text: string): boolean { } export function isEvalOrArgumentsIdentifier(node: ts.Node): boolean { - return ts.isIdentifier(node) && (node.escapedText === "eval" || node.escapedText === "arguments"); + return ts.isIdentifier(node) && (node.escapedText === "eval" || node.escapedText === MandatoryArguments); } export function isLeftHandSideExpressionKind(kind: ts.SyntaxKind) { diff --git a/ts2panda/src/syntaxChecker.ts b/ts2panda/src/syntaxChecker.ts index afec2a1fe9e92defa0f9cc79117c6394c853d529..10ff9e1498cd7909aebef0e5651f3124199f0c73 100644 --- a/ts2panda/src/syntaxChecker.ts +++ b/ts2panda/src/syntaxChecker.ts @@ -22,7 +22,7 @@ import { import { hasExportKeywordModifier } from "./base/util"; import { findInnerExprOfParenthesis } from "./expression/parenthesizedExpression"; import * as jshelpers from "./jshelpers"; -import { getContainingFunction, getContainingFunctionDeclaration, getSourceFileOfNode } from "./jshelpers"; +import { getContainingFunctionDeclaration, getSourceFileOfNode } from "./jshelpers"; import { LOGE } from "./log"; import { Recorder } from "./recorder"; import { @@ -53,6 +53,7 @@ import { visibilityToString, isInBlockScope } from "./syntaxCheckHelper"; +import { MandatoryArguments } from "./variable"; //*************************************Part 1: Implement early check of declarations*******************************// export function checkDuplicateDeclaration(recorder: Recorder) { @@ -1338,7 +1339,7 @@ function checkDestructuringAssignmentLhs(lhs: ts.Expression) { if (ts.isIdentifier(target)) { let name = jshelpers.getTextOfIdentifierOrLiteral(target); - if (name == "arguments" || name == "eval") { + if (name == MandatoryArguments || name == "eval") { throw new DiagnosticError(target, DiagnosticCode.Property_destructuring_pattern_expected, file); } continue; @@ -1401,7 +1402,7 @@ function checkDestructuringAssignmentLhs(lhs: ts.Expression) { if (ts.isShorthandPropertyAssignment(element)) { let name = jshelpers.getTextOfIdentifierOrLiteral(element.name); - if (name == "arguments" || name == "eval") { + if (name == MandatoryArguments || name == "eval") { throw new DiagnosticError(element, DiagnosticCode.Property_destructuring_pattern_expected, file); } diff --git a/ts2panda/src/variable.ts b/ts2panda/src/variable.ts index 43289eef01df20c6ab68130ba7feda62d777437f..4f51d0af8b5bbfbc4567256641ebfd995f7f6f11 100644 --- a/ts2panda/src/variable.ts +++ b/ts2panda/src/variable.ts @@ -74,9 +74,13 @@ export abstract class Variable { } setLexVar(scope: VariableScope | LoopScope) { + if (this.lexical()) { + return; + } this.idxLex = scope.getLexVarIdx() scope.pendingCreateEnv(); this.isLexVar = true; + scope.addLexVarInfo(this.name, this.idxLex); return this.idxLex; } @@ -85,6 +89,14 @@ export abstract class Variable { this.idxLex = 0; } + lexical(): boolean { + return this.isLexVar; + } + + lexIndex() { + return this.idxLex; + } + isLet(): boolean { return this.declKind == VarDeclarationKind.LET; } @@ -174,3 +186,17 @@ export class GlobalVariable extends Variable { super(declKind, name); } } + +export const MandatoryFuncObj = "4funcObj"; +export const MandatoryNewTarget = "4newTarget"; +export const MandatoryThis = "this"; +export const MandatoryArguments = "arguments"; + +export function isMandatoryParam(name: string) { + if (name == MandatoryFuncObj || name == MandatoryArguments || + name == MandatoryNewTarget || name == MandatoryThis) { + return true; + } + + return false; +} \ No newline at end of file diff --git a/ts2panda/tests/lexenv.test.ts b/ts2panda/tests/lexenv.test.ts index c5d6e49463c73a6cf4406793606d51bbbb29f4ad..c53f3d386f43df40c265567481927fce95fa6ba8 100644 --- a/ts2panda/tests/lexenv.test.ts +++ b/ts2panda/tests/lexenv.test.ts @@ -53,7 +53,8 @@ import { import { GlobalVariable, LocalVariable, - VarDeclarationKind + VarDeclarationKind, + Variable } from "../src/variable"; import { creatAstFromSnippet } from "./utils/asthelper"; import { @@ -471,20 +472,17 @@ describe("lexenv-compile-testcase in lexenv.test.ts", function () { it("test lexenv capture in function", function () { let source: string = ` - var a = 1; - function outer(a, b) { - return function () { - a++; - return a + b; - } - } - var fun = outer(a, 5); - a = 3; - func(); - `; - - let insnsStoreLexVar_outer_1 = MicroStoreLexVar(0, 0); - let insnsStoreLexVar_outer_2 = MicroStoreLexVar(0, 1); + var a = 1; + function outer(a, b) { + return function () { + a++; + return a + b; + } + } + var fun = outer(a, 5); + a = 3; + func(); + `; IRNode.pg = new PandaGen("foo", creatAstFromSnippet(`class C {}; export {C}`), 0, undefined); let expect_outer: IRNode[] = [ @@ -509,21 +507,19 @@ describe("lexenv-compile-testcase in lexenv.test.ts", function () { IRNode.pg = new PandaGen("foo", creatAstFromSnippet(`class C {}; export {C}`), 0, undefined); let expect_anonymous = [ - new Newlexenv(new Imm(0)), - new Sta(new VReg()), - new Ldlexvar(new Imm(1), new Imm(0)), + new Ldlexvar(new Imm(0), new Imm(0)), new Sta(new VReg()), new Lda(new VReg()), new Inc(new Imm(0)), new Sta(new VReg()), new Lda(new VReg()), - new Stlexvar(new Imm(1), new Imm(0)), + new Stlexvar(new Imm(0), new Imm(0)), new Lda(new VReg()), new Lda(new VReg()), new Tonumeric(new Imm(1)), // this is redundant load varialbe - new Ldlexvar(new Imm(1), new Imm(0)), + new Ldlexvar(new Imm(0), new Imm(0)), new Sta(new VReg), - new Ldlexvar(new Imm(1), new Imm(1)), + new Ldlexvar(new Imm(0), new Imm(1)), new Add2(new Imm(2), new VReg()), // returnStatement new Sta(new VReg()), @@ -539,18 +535,17 @@ describe("lexenv-compile-testcase in lexenv.test.ts", function () { let outerPg = snippetCompiler.getPandaGenByName("UnitTest.outer"); let outerScope = outerPg!.getScope(); let outerA = outerScope!.findLocal("a"); + let outerB = outerScope!.findLocal("b"); expect(outerA instanceof LocalVariable, "a in outer is local variable").to.be.true; - // expect((outerScope).hasLexEnv(), "outer scope need to create lex env").to.be.true; + expect(outerB instanceof LocalVariable, "b in outer is local variable").to.be.true; expect((outerScope).getNumLexEnv(), "number of lexvar at outer scope").to.be.equal(2); let anonymousPg = snippetCompiler.getPandaGenByName("UnitTest.#1#"); let anonymousScope = anonymousPg!.getScope(); let anonymousA = anonymousScope!.findLocal("a"); let searchRlt = anonymousScope!.find("a"); - expect(searchRlt!.level).to.be.equal(1); + expect(searchRlt!.level).to.be.equal(0); expect(searchRlt!.scope, "a is defined in outerscope").to.be.deep.equal(outerScope); expect(anonymousA, "no a in anonymous function").to.be.undefined; - // expect((anonymousScope).hasLexEnv(), "anonymous scope had lex env").to.be.true; - expect((anonymousScope).getNumLexEnv()).to.be.equal(0); let globalPg = snippetCompiler.getPandaGenByName("UnitTest.func_main_0"); let globalScope = globalPg!.getScope(); let globalA = globalScope!.findLocal("a"); diff --git a/ts2panda/tests/scope.test.ts b/ts2panda/tests/scope.test.ts index 077c531211909ca134e4d4e269b038681f2040a3..4f4ac7fff7acc84f5460f98417069d4ded3d5d55 100644 --- a/ts2panda/tests/scope.test.ts +++ b/ts2panda/tests/scope.test.ts @@ -83,7 +83,7 @@ describe("ScopeTest", function () { expect(variable instanceof GlobalVariable).to.be.true; let { scope: sp, level: lv, v: outVariable } = scope.find("x"); expect(outVariable === variable).to.be.true; - expect(lv).to.be.equal(1); + expect(lv).to.be.equal(0); expect(sp).to.be.equal(globalScope); }); @@ -228,39 +228,23 @@ describe("ScopeTest", function () { let func32Scope = new FunctionScope(func21Scope); let func4Scope = new FunctionScope(func31Scope); - func1Scope.pendingCreateEnv(); - func21Scope.pendingCreateEnv(); + aV.setLexVar(func1Scope); + p2.setLexVar(func1Scope); let aFindEntry = func32Scope.find("a"); - aFindEntry.scope!.setLexVar(aFindEntry.v!, func32Scope); expect(aFindEntry.v, "check a variable").to.be.equal(aV); - expect(aFindEntry.level, "check level").to.be.equal(2); + expect(aFindEntry.level, "check level").to.be.equal(0); expect(aFindEntry.scope).to.be.equal(func1Scope); let p2FindEntry = func22Scope.find("p2"); - p2FindEntry.scope!.setLexVar(p2FindEntry.v!, func22Scope); expect(p2FindEntry.v, "check p0 parameter").to.be.equal(p2); - expect(p2FindEntry.level, "check level").to.be.equal(1); + expect(p2FindEntry.level, "check level").to.be.equal(0); expect(p2FindEntry.scope).to.be.equal(func1Scope); - // check global - // expect(globalScope.hasLexEnv(), "gloal has lexenv").to.be.false; - // check func1 - // expect(func1Scope.hasLexEnv(), "func1 haslexenv").to.be.true; expect(func1Scope.getNumLexEnv(), "func1 status").to.be.equal(2); - // check fun21 - // expect(func21Scope.hasLexEnv(), "func21 has lexenv").to.be.true; expect(func21Scope.getNumLexEnv(), "func21 status").to.be.equal(0); - // check fun22 - // expect(func22Scope.hasLexEnv(), "func22 has lexenv").to.be.true; expect(func22Scope.getNumLexEnv(), "func22 status").to.be.equal(0); - // check fun31 - // expect(func31Scope.hasLexEnv(), "func31 has lexenv").to.be.false; expect(func31Scope.getNumLexEnv(), "func31 status").to.be.equal(0); - // check fun32 - // expect(func32Scope.hasLexEnv(), "func32 has lexenv").to.be.true; expect(func32Scope.getNumLexEnv(), "func32 status").to.be.equal(0); - // check fun4 - // expect(func4Scope.hasLexEnv(), "func4 has lexenv").to.be.false; expect(func4Scope.getNumLexEnv(), "func4 status").to.be.equal(0); }); })