diff --git a/ts2panda/src/base/builtIn.ts b/ts2panda/src/base/builtIn.ts index 61afcb3ecd16faeadb422b6b1fe6a0653bed2a15..160b5f9d2a961ffbc2ba182b0fc64dc61772ead9 100755 --- a/ts2panda/src/base/builtIn.ts +++ b/ts2panda/src/base/builtIn.ts @@ -25,7 +25,8 @@ import { EcmaLdsymbol, EcmaLdtrue, EcmaLdundefined, - StaDyn + StaDyn, + EcmaLdfunction } from "../irnodes"; import { CacheList, getVregisterCache } from "./vregisterCache"; @@ -99,4 +100,12 @@ export function expandFalse(pandaGen: PandaGen): IRNode[] { new EcmaLdfalse(), new StaDyn(vreg) ]; +} + +export function expandFunc(pandaGen: PandaGen): IRNode[] { + let vreg = getVregisterCache(pandaGen, CacheList.FUNC); + return [ + new EcmaLdfunction(), + new StaDyn(vreg) + ]; } \ No newline at end of file diff --git a/ts2panda/src/base/vregisterCache.ts b/ts2panda/src/base/vregisterCache.ts index a48db661b36fdb9646f6d38ffc9cfaf04dd8e5d2..e4b3082acf658539c8f00ab43d2599c4d3189263 100755 --- a/ts2panda/src/base/vregisterCache.ts +++ b/ts2panda/src/base/vregisterCache.ts @@ -33,13 +33,15 @@ import { // expandString, expandSymbol, expandTrue, - expandUndefined + expandUndefined, + expandFunc } from "./builtIn"; import { expandLexEnv } from "./lexEnv"; export enum CacheList { MIN, NaN = MIN, HOLE, + FUNC, // load function Infinity, undefined, // Boolean, @@ -55,7 +57,7 @@ export enum CacheList { LexEnv, // Lex Env must come before True and False, because LexEnv depends on True and False True, False, - MAX, + MAX } let cacheExpandHandlers = new Map([ [CacheList.HOLE, expandHole], @@ -75,6 +77,7 @@ let cacheExpandHandlers = new Map([ [CacheList.LexEnv, expandLexEnv], [CacheList.True, expandTrue], [CacheList.False, expandFalse], + [CacheList.FUNC, expandFunc], ]); class CacheItem { diff --git a/ts2panda/src/compiler.ts b/ts2panda/src/compiler.ts index b87ad4cc6a300f0aacccc882a40714e3d05e25dd..be0e8bb51e69870753c2a00f4f038c40b5b1dd0d 100644 --- a/ts2panda/src/compiler.ts +++ b/ts2panda/src/compiler.ts @@ -74,6 +74,7 @@ import { } from "./pandagen"; import { Recorder } from "./recorder"; import { + FunctionScope, GlobalScope, LoopScope, ModuleScope, @@ -155,6 +156,7 @@ export class Compiler { this.compileSourceFileOrBlock(this.rootNode); } else { this.compileFunctionLikeDeclaration(this.rootNode); + this.callOpt(); } } @@ -170,6 +172,44 @@ export class Compiler { return this.envUnion[this.envUnion.length - 1]; } + private callOpt() { + let CallMap: Map = new Map([ + ["this", 1], + ["4newTarget", 2], + ["0newTarget", 2], + ["argumentsOrRestargs", 4] + ]); + let callType = 0; + let scope = this.pandaGen.getScope(); + + if (scope instanceof FunctionScope) { + let tempLocals: VReg[] = []; + let count = 0; + // 4funcObj/newTarget/this + for (let i = 0; i < 3; i++) { + if (scope.getCallOpt().has(scope.getParameters()[i].getName())) { + tempLocals.push(this.pandaGen.getLocals()[i]); + callType += CallMap.get(scope.getParameters()[i].getName()) ?? 0; + } else { + count++; + } + } + // acutal parameters + for (let i = 3; i < this.pandaGen.getLocals().length; i++) { + tempLocals.push(this.pandaGen.getLocals()[i]); + } + + this.pandaGen.setLocals(tempLocals); + this.pandaGen.setParametersCount(this.pandaGen.getParametersCount()-count); + + if (scope.getArgumentsOrRestargs()) { + callType += CallMap.get("argumentsOrRestargs") ?? 0; + } + + this.pandaGen.setCallType(callType); + } + } + private compileLexicalBindingForArrowFunction() { let rootNode = this.rootNode; @@ -204,8 +244,14 @@ export class Compiler { let v = variableInfo.v; if (v && v.isLexVar) { + if ((arg === "this" || arg === "4newTarget") && variableInfo.scope instanceof FunctionScope) { + variableInfo.scope.setCallOpt(arg); + } + if (arg === "arguments" && variableInfo.scope instanceof FunctionScope) { + variableInfo.scope.setArgumentsOrRestargs(); + } let pandaGen = this.pandaGen; - let vreg = pandaGen.getVregForVariable(variableInfo.v); + let vreg = "4funcObj" === arg ? getVregisterCache(pandaGen, CacheList.FUNC) : pandaGen.getVregForVariable(variableInfo.v); let slot = (variableInfo.v).idxLex; pandaGen.storeLexicalVar(this.rootNode, variableInfo.level, slot, vreg); } @@ -283,6 +329,10 @@ export class Compiler { let paramReg = pandaGen.getVregForVariable(variable!); if (param.dotDotDotToken) { + let scope = this.pandaGen.getScope(); + if (scope instanceof FunctionScope) { + scope.setArgumentsOrRestargs(); + } pandaGen.copyRestArgs(param, index); pandaGen.storeAccumulator(param, paramReg); } @@ -886,6 +936,9 @@ export class Compiler { checkValidUseSuperBeforeSuper(this, node); let { scope, level, v } = this.scope.find("this"); + + this.setCallOpt(scope, "this") + if (!v) { throw new Error("\"this\" not found"); } @@ -1293,6 +1346,9 @@ export class Compiler { let scope = thisInfo.scope; let level = thisInfo.level; let v = thisInfo.v; + + this.setCallOpt(scope, "this") + if (scope && level >= 0) { let needSetLexVar: boolean = false; while (curScope != scope) { @@ -1320,6 +1376,9 @@ export class Compiler { setThis(node: ts.Node) { let pandaGen = this.pandaGen; let thisInfo = this.getCurrentScope().find("this"); + + this.setCallOpt(thisInfo.scope, "this") + if (thisInfo.v!.isLexVar) { let slot = (thisInfo.v).idxLex; let value = pandaGen.getTemp(); @@ -1331,6 +1390,12 @@ export class Compiler { } } + setCallOpt(scope: Scope | undefined, callOptStr: String) { + if (scope instanceof FunctionScope) { + scope.setCallOpt(callOptStr); + } + } + getPandaGen() { return this.pandaGen; } diff --git a/ts2panda/src/expression/metaProperty.ts b/ts2panda/src/expression/metaProperty.ts index 103755473d514e01f1d1f349c3af135de8c4fd01..dfc44034ff0fcef3067510913dc471f00793c2fb 100644 --- a/ts2panda/src/expression/metaProperty.ts +++ b/ts2panda/src/expression/metaProperty.ts @@ -22,6 +22,9 @@ export function compileMetaProperty(expr: ts.MetaProperty, compiler: Compiler) { let id = jshelpers.getTextOfIdentifierOrLiteral(expr.name); if (id == "target") { let { scope, level, v } = curScope.find("4newTarget"); + + compiler.setCallOpt(scope, "4newTarget"); + if (!v) { throw new Error("fail to access new.target"); } else { diff --git a/ts2panda/src/function/generatorFunctionBuilder.ts b/ts2panda/src/function/generatorFunctionBuilder.ts index 85d1d18c1999fdaa7ca73bc091f235cefbb7cf5a..73a26c6be44ff69a8472f50db41a2eea4feec2d8 100755 --- a/ts2panda/src/function/generatorFunctionBuilder.ts +++ b/ts2panda/src/function/generatorFunctionBuilder.ts @@ -48,9 +48,10 @@ export class GeneratorFunctionBuilder { prepare(node: ts.Node, recorder: Recorder) { let pandaGen = this.pandaGen; let scope = recorder.getScopeOfNode(node); - let funcObj = scope.getName2variable().get('4funcObj')!.getVreg(); + let funcObj = scope.getName2variable().get("4funcObj")!.getVreg(); - pandaGen.createGeneratorObj(node, funcObj); + // backend handle funcobj, frontend set undefined + pandaGen.createGeneratorObj(node, getVregisterCache(pandaGen, CacheList.FUNC)); pandaGen.storeAccumulator(node, this.genObj); pandaGen.suspendGenerator(node, this.genObj, getVregisterCache(pandaGen, CacheList.undefined)); pandaGen.resumeGenerator(node, this.genObj); diff --git a/ts2panda/src/lexenv.ts b/ts2panda/src/lexenv.ts index 3193042838a25f81ce0067cc1dbe716719d86ac2..fbdbb2a5fb9f6a0db58766449b61fc34482684c8 100644 --- a/ts2panda/src/lexenv.ts +++ b/ts2panda/src/lexenv.ts @@ -93,7 +93,11 @@ export class VariableAccessLoad extends VariableAccessBase { pandaGen.freeTemps(holeReg); return insns; } - insns.push(loadAccumulator(bindVreg)); + if (v.getName() === "4funcObj") { + insns.push(loadAccumulator(getVregisterCache(pandaGen, CacheList.FUNC))); + } else { + insns.push(loadAccumulator(bindVreg)); + } return insns; } diff --git a/ts2panda/src/pandagen.ts b/ts2panda/src/pandagen.ts index e823a721742aab76baff208acd0fb57a56475554..1b56905a963a65d309e517acdcae69fce351f6db 100644 --- a/ts2panda/src/pandagen.ts +++ b/ts2panda/src/pandagen.ts @@ -201,6 +201,7 @@ export class PandaGen { private sourceFileDebugInfo: string = ""; private sourceCodeDebugInfo: string | undefined; private icSize: number = 0; + private callType: number = 0; private static literalArrayBuffer: Array = []; @@ -211,6 +212,14 @@ export class PandaGen { this.vregisterCache = new VregisterCache(); } + public setCallType(callType: number) { + this.callType = callType; + } + + public getCallType(): number { + return this.callType; + } + public getSourceCodeDebugInfo() { return this.sourceCodeDebugInfo; } @@ -348,10 +357,18 @@ export class PandaGen { return this.totalRegsNum; } + setParametersCount(count: number) { + this.parametersCount = count; + } + getParametersCount(): number { return this.parametersCount; } + setLocals(locals: VReg[]) { + this.locals = locals; + } + getLocals(): VReg[] { return this.locals; } @@ -367,6 +384,9 @@ export class PandaGen { loadAccFromArgs(node: ts.Node) { if ((this.scope).getUseArgs()) { let v = this.scope!.findLocal("arguments"); + if (this.scope instanceof FunctionScope) { + this.scope.setArgumentsOrRestargs(); + } if (v) { let paramVreg = this.getVregForVariable(v); this.getUnmappedArgs(node); diff --git a/ts2panda/src/pandasm.ts b/ts2panda/src/pandasm.ts index f4ca2651ae12ef281ecff503794687a19fa6ae0f..c523f7fa8707ad2171b26d4a696213eafb198090 100644 --- a/ts2panda/src/pandasm.ts +++ b/ts2panda/src/pandasm.ts @@ -72,6 +72,7 @@ export class Function { public variables: Array | undefined; public sourceFile: string; public sourceCode: string | undefined; + public callType: number; constructor( name: string, @@ -82,6 +83,7 @@ export class Function { variables: Array | undefined = undefined, sourceFile: string = "", sourceCode: string | undefined = undefined, + callType: number = 0 ) { this.name = name; this.signature = signature; @@ -93,6 +95,7 @@ export class Function { this.variables = variables; this.sourceFile = sourceFile; this.sourceCode = sourceCode; + this.callType = callType; } } diff --git a/ts2panda/src/scope.ts b/ts2panda/src/scope.ts index e2aa29a06262b379970d0abd4e2ca518590cdc75..e70888f569a61bc2976d86d9233b364577ef9aa3 100644 --- a/ts2panda/src/scope.ts +++ b/ts2panda/src/scope.ts @@ -408,6 +408,8 @@ export class ModuleScope extends VariableScope { export class FunctionScope extends VariableScope { private parameterLength: number = 0; private funcName: string = ""; + private callOpt: Set = new Set(); + private isArgumentsOrRestargs: boolean = false; constructor(parent?: Scope, node?: ts.FunctionLikeDeclaration) { super(); this.parent = parent ? parent : undefined; @@ -430,6 +432,22 @@ export class FunctionScope extends VariableScope { return this.funcName; } + public getCallOpt() { + return this.callOpt; + } + + public setCallOpt(key: String) { + this.callOpt.add(key); + } + + public setArgumentsOrRestargs() { + this.isArgumentsOrRestargs = true; + } + + public getArgumentsOrRestargs() { + return this.isArgumentsOrRestargs; + } + getParent(): Scope | undefined { return this.parent; } diff --git a/ts2panda/src/statement/classStatement.ts b/ts2panda/src/statement/classStatement.ts index 1163c9e0b86aadac244721c152ca0e0f6375d031..bf5aaaaf8a94f22a07f5384a8640621809439110 100644 --- a/ts2panda/src/statement/classStatement.ts +++ b/ts2panda/src/statement/classStatement.ts @@ -282,6 +282,9 @@ export function compileSuperCall(compiler: Compiler, node: ts.CallExpression, ar let curScope = compiler.getCurrentScope(); let { scope, level, v } = curScope.find("this"); + compiler.setCallOpt(scope, "this"); + compiler.setCallOpt(scope, "4newTarget"); + if (scope && level >= 0) { let tmpScope = curScope; let needSetLexVar: boolean = false; @@ -296,6 +299,9 @@ export function compileSuperCall(compiler: Compiler, node: ts.CallExpression, ar if (needSetLexVar) { scope.setLexVar(v, curScope); } + if (needSetLexVar && curScope instanceof FunctionScope) { + curScope.setCallOpt("0newTarget"); + } } if (hasSpread) { @@ -330,7 +336,7 @@ function loadCtorObj(node: ts.CallExpression, compiler: Compiler) { if (ts.isConstructorDeclaration(nearestFunc)) { let funcObj = nearestFuncScope.findLocal("4funcObj"); - pandaGen.loadAccumulator(node, pandaGen.getVregForVariable(funcObj)); + pandaGen.loadAccumulator(node, getVregisterCache(pandaGen, CacheList.FUNC)); } else { let outerFunc = jshelpers.getContainingFunction(nearestFunc); let outerFuncScope = recorder.getScopeOfNode(outerFunc); diff --git a/ts2panda/src/ts2panda.ts b/ts2panda/src/ts2panda.ts index 58e7765b7952b67007b31d14407726cd213460d0..e3b60b281e5f4d09892ef2c70a3b5ad98a1893fa 100644 --- a/ts2panda/src/ts2panda.ts +++ b/ts2panda/src/ts2panda.ts @@ -170,6 +170,7 @@ export class Ts2Panda { let funcSignature = Ts2Panda.getFuncSignature(pg); let funcInsnsAndRegsNum = Ts2Panda.getFuncInsnsAndRegsNum(pg); let sourceFile = pg.getSourceFileDebugInfo(); + let callType = pg.getCallType(); let variables, sourceCode; if (CmdOptions.isDebugMode()) { @@ -189,6 +190,7 @@ export class Ts2Panda { variables, sourceFile, sourceCode, + callType ); let catchTables = generateCatchTables(pg.getCatchMap()); catchTables.forEach((catchTable) => { diff --git a/ts2panda/ts2abc/ts2abc.cpp b/ts2panda/ts2abc/ts2abc.cpp index 8f131798fb1334cc8f335f1a0881c63b052004d8..d9c3cc45400ee89c82208e401991c029040b0c1b 100644 --- a/ts2panda/ts2abc/ts2abc.cpp +++ b/ts2panda/ts2abc/ts2abc.cpp @@ -594,6 +594,27 @@ static void ParseFunctionCatchTables(const Json::Value &function, panda::pandasm } } +static void ParseFunctionCallType(const Json::Value &function, panda::pandasm::Function &pandaFunc) +{ + std::string funcName = ""; + if (function.isMember("name") && function["name"].isString()) { + funcName = function["name"].asString(); + } + if (funcName == "func_main_0") { + return ; + } + + uint32_t callType = 0; + if (function.isMember("callType") && function["callType"].isInt()) { + callType = function["callType"].asUInt(); + } + panda::pandasm::AnnotationData callTypeAnnotation("_ESCallTypeAnnotation"); + std::string annotationName = "callType"; + panda::pandasm::AnnotationElement callTypeAnnotationElement(annotationName, std::make_unique(panda::pandasm::ScalarValue::Create(callType))); + callTypeAnnotation.AddElement(std::move(callTypeAnnotationElement)); + const_cast&>(pandaFunc.metadata->GetAnnotations()).push_back(std::move(callTypeAnnotation)); +} + static panda::pandasm::Function ParseFunction(const Json::Value &function) { auto pandaFunc = GetFunctionDefintion(function); @@ -607,10 +628,20 @@ static panda::pandasm::Function ParseFunction(const Json::Value &function) ParseFunctionLabels(function, pandaFunc); // parsing catch blocks ParseFunctionCatchTables(function, pandaFunc); + // parsing call opt type + ParseFunctionCallType(function, pandaFunc); return pandaFunc; } +static void GenerateESCallTypeAnnotationRecord(panda::pandasm::Program &prog) +{ + auto callTypeAnnotationRecord = panda::pandasm::Record("_ESCallTypeAnnotation", LANG_EXT); + callTypeAnnotationRecord.metadata->SetAttribute("external"); + callTypeAnnotationRecord.metadata->SetAccessFlags(panda::ACC_ANNOTATION); + prog.record_table.emplace(callTypeAnnotationRecord.name, std::move(callTypeAnnotationRecord)); +} + static void GenrateESModuleModeRecord(panda::pandasm::Program &prog, bool moduleMode) { auto ecmaModuleModeRecord = panda::pandasm::Record("_ESModuleMode", LANG_EXT); @@ -705,6 +736,7 @@ static void ReplaceAllDistinct(std::string &str, const std::string &oldValue, co static void ParseOptions(const Json::Value &rootValue, panda::pandasm::Program &prog) { + GenerateESCallTypeAnnotationRecord(prog); ParseModuleMode(rootValue, prog); ParseLogEnable(rootValue); ParseDebugMode(rootValue);