diff --git a/test262/config.py b/test262/config.py index c521b973dd530584ae87c384f91dde836def4731..20e874022987e9c14e5d4c3b15055d7ebeb0f9ce 100755 --- a/test262/config.py +++ b/test262/config.py @@ -32,6 +32,7 @@ CODE_ROOT = os.path.abspath(os.path.join(CUR_FILE_DIR, "../../..")) ARK_DIR = f"{CODE_ROOT}/out/ohos-arm-release/clang_x64/ark/ark" ICUI_DIR = f"{CODE_ROOT}/out/ohos-arm-release/clang_x64/global/i18n_standard" LLVM_DIR = f"{CODE_ROOT}/prebuilts/clang/ohos/linux-x86_64/llvm/lib/" +ARK_JS_RUNTIME_DIR = f"{CODE_ROOT}/out/ohos-arm-release/clang_x64/ark/ark_js_runtime" # " mode_type": { # "1": "only default", @@ -45,8 +46,8 @@ TEST_ES2015_DIR = os.path.join(DATA_DIR, "test_es2015") TEST_CI_DIR = os.path.join(DATA_DIR, "test_CI") DEFAULT_ARK_FRONTEND_TOOL = os.path.join(ARK_DIR, "build", "src", "index.js") -DEFAULT_ARK_TOOL = os.path.join(ARK_DIR, "..", "ark_js_runtime", "ark_js_vm") -DEFAULT_LIBS_DIR = f"{ARK_DIR}:{ICUI_DIR}:{LLVM_DIR}" +DEFAULT_ARK_TOOL = os.path.join(ARK_JS_RUNTIME_DIR, "ark_js_vm") +DEFAULT_LIBS_DIR = f"{ARK_DIR}:{ICUI_DIR}:{LLVM_DIR}:{ARK_JS_RUNTIME_DIR}" DEFAULT_HOST_TYPE = "panda" DEFAULT_HOST_PATH = "python3" diff --git a/test262/run_sunspider.py b/test262/run_sunspider.py index 97752ef074164566fb3186cad224d9c84d6cc53e..7f9bce461f465f4b0e9774aae94e6b0be02f3254 100755 --- a/test262/run_sunspider.py +++ b/test262/run_sunspider.py @@ -31,14 +31,23 @@ from config import * def parse_args(): parser = argparse.ArgumentParser() parser.add_argument('--ark-tool', + default=DEFAULT_ARK_TOOL, + required=False, help="ark's binary tool") parser.add_argument('--ark-frontend-tool', + default=DEFAULT_ARK_FRONTEND_TOOL, + required=False, help="ark frontend conversion tool") parser.add_argument("--libs-dir", + default=DEFAULT_LIBS_DIR, + required=False, help="The path collection of dependent so has been divided by':'") parser.add_argument("--js-file", + required=True, help="js file") parser.add_argument('--ark-frontend', + default=DEFAULT_ARK_FRONTEND, + required=False, nargs='?', choices=ARK_FRONTEND_LIST, type=str, help="Choose one of them") arguments = parser.parse_args() @@ -155,7 +164,7 @@ class ArkProgram(): return retcode def execute(self): - + os.environ["LD_LIBRARY_PATH"] = self.libs_dir file_name_pre = os.path.splitext(self.js_file)[0] diff --git a/ts2panda/BUILD.gn b/ts2panda/BUILD.gn index fcde4bc32fb482da88e9743670732de1d4acc77d..f24086274b31e9248010edbac10585e7ea3b474a 100755 --- a/ts2panda/BUILD.gn +++ b/ts2panda/BUILD.gn @@ -25,9 +25,17 @@ ohos_copy("ts2abc_src") { module_install_name = "" } +action("npm_install") { + visibility = [ ":*" ] + script = "scripts/npm-install.sh" + args = [ rebase_path(node_path) ] + outputs = [ target_out_dir + "/$target_name/node_modules" ] + inputs = [ "${ts2abc_root}/package.json" ] +} + ohos_copy("node_modules") { sources = [ rebase_path("${node_modules}") ] - + deps = [ ":npm_install" ] outputs = [ target_out_dir + "/node_modules" ] module_install_name = "" } @@ -324,5 +332,9 @@ ohos_copy("copy_ts2abc_tests") { } group("ts2abc_unittests") { + testonly = true deps = [ "tests:ts2abc_tests(${buildtool_linux})" ] + if (host_os == "linux") { + deps += [ "${ts2abc_root}/ts2abc/tests:unittest" ] + } } diff --git a/ts2panda/package-lock.json b/ts2panda/package-lock.json old mode 100644 new mode 100755 index e18acb061b27108ec6cbaa58cb9e2cb1e6dc877d..646821ba8881cb3f9add02ae06249b613d902356 --- a/ts2panda/package-lock.json +++ b/ts2panda/package-lock.json @@ -2319,9 +2319,9 @@ "dev": true }, "typescript": { - "version": "3.9.10", - "resolved": "https://repo.huaweicloud.com/repository/npm/typescript/-/typescript-3.9.10.tgz", - "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "version": "4.4.3", + "resolved": "https://registry.npmmirror.com/typescript/download/typescript-4.4.3.tgz?cache=0&sync_timestamp=1632381565165&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Ftypescript%2Fdownload%2Ftypescript-4.4.3.tgz", + "integrity": "sha1-vcVAfKorEJ79T4L+EwZW+XeikyQ=", "dev": true }, "typical": { diff --git a/ts2panda/package.json b/ts2panda/package.json index 2fc7bed3c1bea20b335152cec5b95574d1ef154e..f37f0d41b643c30529a895a3837a4db118232e35 100755 --- a/ts2panda/package.json +++ b/ts2panda/package.json @@ -33,7 +33,7 @@ "mocha": "^8.1.1", "sinon": "^9.0.3", "ts-sinon": "^1.2.1", - "typescript": "^3.9.7" + "typescript": "^4.1.3" }, "dependencies": { "@babel/core": "^7.12.10", diff --git a/ts2panda/scripts/npm-install.sh b/ts2panda/scripts/npm-install.sh new file mode 100755 index 0000000000000000000000000000000000000000..f806d55df01d96557ff8b668c4090dc52b11d790 --- /dev/null +++ b/ts2panda/scripts/npm-install.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# Copyright (c) 2021 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. +set -e + +script_path=$(cd $(dirname $0);pwd) +ts2panda_dir=$(dirname ${script_path}) +code_dir=${ts2panda_dir}/../../.. + +nodejs_dir=$1 + +cd ${ts2panda_dir} +export PATH=${nodejs_dir}:$PATH +npm config set registry http://registry.npm.taobao.org +if [ "X${SKIP_SSL}" == "XYES" ];then + npm config set strict-ssl false +fi +npm cache clean -f +npm install + +cd ${code_dir} +if [ -d "${code_dir}/prebuilts/build-tools/common/ts2abc" ]; then + echo -e "\n" + echo "${code_dir}/prebuilts/build-tools/common/ts2abc already exist, it will be replaced" + /bin/rm -rf ${code_dir}/prebuilts/build-tools/common/ts2abc + echo -e "\n" +fi + +mkdir -p ${code_dir}/prebuilts/build-tools/common/ts2abc +/bin/cp -rf ${code_dir}/ark/ts2abc/ts2panda/node_modules ${code_dir}/prebuilts/build-tools/common/ts2abc/ \ No newline at end of file diff --git a/ts2panda/scripts/run_tests.py b/ts2panda/scripts/run_tests.py index 00c75458ddfa9c8e9eaa06934d4ba04fdd507233..7bba08388b80ec97344246cbe68bad3f45cea10d 100755 --- a/ts2panda/scripts/run_tests.py +++ b/ts2panda/scripts/run_tests.py @@ -47,9 +47,6 @@ def parse_args(): parser.add_argument('--platform', default="linux", help='platform, as: linux, mac, win') - parser.add_argument('--gn-build', - action='store_true', - help='Whether it is GN compilation') parser.add_argument('--js-file', metavar='FILE', help='The name of the test use case file to execute') @@ -99,8 +96,6 @@ class Ts2abcTests(): run_command(['npm', 'install'], dist_dir) def copy_tests(self): - if self.args.gn_build: - return if os.path.exists(f'{self.dist_dir}/tests'): run_command(['rm', '-rf', f'{self.dist_dir}/tests']) run_command(['cp', '-rf', f'{self.src_dir}/tests', self.dist_dir]) diff --git a/ts2panda/scripts/run_tests_executable.sh b/ts2panda/scripts/run_tests_executable.sh new file mode 100755 index 0000000000000000000000000000000000000000..7b91c76a61f148cca50428d92f25c803f80d7d12 --- /dev/null +++ b/ts2panda/scripts/run_tests_executable.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# Copyright (c) 2021 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. + +set -e + +prog_name=$1 +${prog_name} + +if [ $? -ne 0 ]; then + echo "Run [" ${prog_name} "] failed!" + exit 1 +else + echo "Run [" ${prog_name} "] success!" +fi diff --git a/ts2panda/src/addVariable2Scope.ts b/ts2panda/src/addVariable2Scope.ts index 8ecc7a1d3fcf59e2b3bad1383a79e4c53e8a7dd8..074d99a4b4b55022f5180a92cd64450424f609e6 100644 --- a/ts2panda/src/addVariable2Scope.ts +++ b/ts2panda/src/addVariable2Scope.ts @@ -25,15 +25,39 @@ import { FuncDecl, InitStatus, LetDecl, - ModDecl, - ModuleScope, Scope, VarDecl, VariableScope } from "./scope"; import { isGlobalIdentifier } from "./syntaxCheckHelper"; -import { VarDeclarationKind } from "./variable"; +import { + VarDeclarationKind, + Variable +} from "./variable"; +import { TypeRecorder } from "./typeRecorder"; +import { PrimitiveType } from "./base/typeSystem"; + +function setVariableOrParameterType(node: ts.Node, v: Variable | undefined) { + if (v) { + let typeIndex = TypeRecorder.getInstance().tryGetVariable2Type(ts.getOriginalNode(node)); + if (typeIndex != -1) { + v.setTypeIndex(typeIndex); + } + // console.log("--node--", jshelpers.getTextOfNode(ts.getOriginalNode(node))); + // console.log("--node.type--", v.getTypeIndex()); + } +} +function setClassOrFunctionType(node: ts.Node, v: Variable | undefined) { + if (v) { + let typeIndex = TypeRecorder.getInstance().tryGetTypeIndex(ts.getOriginalNode(node)); + if (typeIndex != -1) { + v.setTypeIndex(typeIndex); + } + // console.log("--node--", jshelpers.getTextOfNode(ts.getOriginalNode(node))); + // console.log("--node.type--", v.getTypeIndex()); + } +} function addInnerArgs(node: ts.Node, scope: VariableScope): void { // the first argument for js function is func_obj @@ -80,10 +104,13 @@ export function addVariableToScope(recorder: Recorder) { hoistDecls = hoistMap.get(scope); if (hoistDecls) { hoistDecls.forEach(hoistDecl => { + let v: Variable | undefined; if (hoistDecl instanceof VarDecl) { - scope.add(hoistDecl.name, VarDeclarationKind.VAR); + v = scope.add(hoistDecl.name, VarDeclarationKind.VAR); + setVariableOrParameterType(hoistDecl.node, v); } else if (hoistDecl instanceof FuncDecl) { - scope.add(hoistDecl.name, VarDeclarationKind.FUNCTION); + v = scope.add(hoistDecl.name, VarDeclarationKind.FUNCTION); + setClassOrFunctionType(hoistDecl.node, v); } else { throw new Error("Wrong type of declaration to be hoisted") } @@ -100,27 +127,28 @@ export function addVariableToScope(recorder: Recorder) { if (hoistDecls && hoistDecls.includes(decl)) { continue; } - + let v: Variable | undefined; if (decl instanceof LetDecl) { - scope.add(decl.name, VarDeclarationKind.LET, InitStatus.UNINITIALIZED); + v = scope.add(decl.name, VarDeclarationKind.LET, InitStatus.UNINITIALIZED); + setVariableOrParameterType(decl.node, v); } else if (decl instanceof ConstDecl) { - scope.add(decl.name, VarDeclarationKind.CONST, InitStatus.UNINITIALIZED); + v = scope.add(decl.name, VarDeclarationKind.CONST, InitStatus.UNINITIALIZED); + setVariableOrParameterType(decl.node, v); } else if (decl instanceof FuncDecl) { - scope.add(decl.name, VarDeclarationKind.FUNCTION); + v = scope.add(decl.name, VarDeclarationKind.FUNCTION); + setClassOrFunctionType(decl.node, v); } else if (decl instanceof CatchParameter) { - scope.add(decl.name, VarDeclarationKind.LET); - } else if (decl instanceof ModDecl) { - if (!(scope instanceof ModuleScope)) { - throw new Error("ModuleVariable can't exist without ModuleScope"); - } - scope.add(decl.name, VarDeclarationKind.MODULE); + v = scope.add(decl.name, VarDeclarationKind.LET); + setVariableOrParameterType(decl.node, v); } else if (decl instanceof ClassDecl) { let classNode = decl.node; if (ts.isClassDeclaration(classNode)) { - scope.add(decl.name, VarDeclarationKind.CLASS, InitStatus.UNINITIALIZED); + v = scope.add(decl.name, VarDeclarationKind.CLASS, InitStatus.UNINITIALIZED); + setClassOrFunctionType(decl.node, v); } else { let classScope = recorder.getScopeOfNode(classNode); - classScope.add(decl.name, VarDeclarationKind.CLASS, InitStatus.UNINITIALIZED); + v = classScope.add(decl.name, VarDeclarationKind.CLASS, InitStatus.UNINITIALIZED); + setClassOrFunctionType(decl.node, v); } } else { /** @@ -130,7 +158,7 @@ export function addVariableToScope(recorder: Recorder) { * but it should be added to scope */ if (isGlobalIdentifier(decls[j].name)) { - scope.add(decls[j].name, VarDeclarationKind.VAR); + v = scope.add(decls[j].name, VarDeclarationKind.VAR); } } } @@ -149,7 +177,8 @@ function addParameters(node: ts.FunctionLikeDeclaration, scope: VariableScope): name = jshelpers.getTextOfIdentifierOrLiteral(param.name); } - scope.addParameter(name, VarDeclarationKind.VAR, i + 1); + let v = scope.addParameter(name, VarDeclarationKind.VAR, i + 1); + setVariableOrParameterType(param.name, v); } for (let i = 0; i < patternParams.length; i++) { diff --git a/ts2panda/src/assemblyDumper.ts b/ts2panda/src/assemblyDumper.ts index c425d876fd61d73eab8fac56a20e11827d81adf6..17892d53162edfd7137e0e0994ab3b201294a557 100644 --- a/ts2panda/src/assemblyDumper.ts +++ b/ts2panda/src/assemblyDumper.ts @@ -188,12 +188,12 @@ export class AssemblyDumper { this.writeFunctionCatchTable(); this.writeFunctionTail(); - console.log(this.output); + // console.log(this.output); } static dumpHeader(): void { let out = { str: "" }; AssemblyDumper.writeLanguageTag(out); - console.log(out.str) + // console.log(out.str) } } diff --git a/ts2panda/src/base/literal.ts b/ts2panda/src/base/literal.ts old mode 100755 new mode 100644 diff --git a/ts2panda/src/base/typeSystem.ts b/ts2panda/src/base/typeSystem.ts new file mode 100644 index 0000000000000000000000000000000000000000..273c0ec5d6d44ea0fb7f5a4f584c6e7b6975eec9 --- /dev/null +++ b/ts2panda/src/base/typeSystem.ts @@ -0,0 +1,556 @@ +/* + * Copyright (c) 2021 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 { + Literal, + LiteralBuffer, + LiteralTag +} from "./literal"; +import { LOGD } from "../log"; +import { TypeChecker } from "../typeChecker"; +import { TypeRecorder } from "../typeRecorder"; +import { PandaGen } from "../pandagen"; +import * as jshelpers from "../jshelpers"; +import { access } from "fs"; + +export enum PrimitiveType { + ANY, + NUMBER, + BOOLEAN, + STRING, + SYMBOL, + VOID, + NULL, + UNDEFINED, + _LENGTH = 50 +} + +export enum L2Type { + CLASS, + CLASSINST, + FUNCTION, + OBJECT, // object literal + _COUNTER +} + +export enum ModifierAbstract { + NONABSTRACT, + ABSTRACT +} + +export enum ModifierStatic { + NONSTATIC, + STATIC +} + +export enum ModifierReadonly { + NONREADONLY, + READONLY +} + +export enum AccessFlag { + PUBLIC, + PRIVATE, + PROTECTED +} + +type ClassMemberFunction = ts.MethodDeclaration | ts.ConstructorDeclaration | ts.GetAccessorDeclaration | ts.SetAccessorDeclaration; + +export abstract class BaseType { + + abstract transfer2LiteralBuffer(): LiteralBuffer; + protected typeChecker = TypeChecker.getInstance(); + protected typeRecorder = TypeRecorder.getInstance(); + + protected addCurrentType(node: ts.Node, index: number) { + this.typeRecorder.addType2Index(node, index); + } + + protected setVariable2Type(variableNode: ts.Node, index: number, isUserDefinedType: boolean) { + this.typeRecorder.setVariable2Type(variableNode, index, isUserDefinedType); + } + + protected createType(node: ts.Node, newExpressionFlag: boolean, variableNode?: ts.Node) { + switch (node.kind) { + case ts.SyntaxKind.MethodDeclaration: + case ts.SyntaxKind.Constructor: + case ts.SyntaxKind.GetAccessor: + case ts.SyntaxKind.SetAccessor: { + new FunctionType(node, variableNode); + break; + } + case ts.SyntaxKind.ClassDeclaration: { + new ClassType(node, newExpressionFlag, variableNode); + break; + } + // create other type as project goes on; + default: + LOGD("Error: Currently this type is not supported"); + // throw new Error("Currently this type is not supported"); + } + } + + protected getOrCreateUserDefinedType(node: ts.Node, newExpressionFlag: boolean, variableNode?: ts.Node) { + let typeNode = this.typeChecker.getTypeDeclAtLocation(node); + let typeIndex = this.typeRecorder.tryGetTypeIndex(typeNode); + if (typeIndex == -1) { + this.createType(typeNode, newExpressionFlag, variableNode); + typeIndex = this.typeRecorder.tryGetTypeIndex(typeNode); + } + return typeIndex; + } + + protected getTypeIndexForDeclWithType( + node: ts.FunctionLikeDeclaration | ts.ParameterDeclaration | ts.PropertyDeclaration, variableNode?: ts.Node): number { + if (node.type) { + // check for newExpression + let newExpressionFlag = false; + if (node.kind == ts.SyntaxKind.PropertyDeclaration && node.initializer && node.initializer.kind == ts.SyntaxKind.NewExpression) { + newExpressionFlag = true; + } + // get typeFlag to check if its a primitive type + let typeRef = node.type; + let typeFlagName = this.typeChecker.getTypeFlagsAtLocation(typeRef); + let typeIndex = -1; + let isUserDefinedType = false; + if (typeFlagName in PrimitiveType) { + typeIndex = PrimitiveType[typeFlagName as keyof typeof PrimitiveType]; + } else { + let identifier = typeRef.getChildAt(0); + typeIndex = this.getOrCreateUserDefinedType(identifier, newExpressionFlag, variableNode); + isUserDefinedType = true; + } + // set variable if variable node is given; + if (variableNode) { + this.setVariable2Type(variableNode, typeIndex, isUserDefinedType); + } + if (typeIndex == -1) { + LOGD("ERROR: Type cannot be found for: " + jshelpers.getTextOfNode(node)); + } + return typeIndex!; + } + LOGD("WARNING: node type not found for: " + jshelpers.getTextOfNode(node)); + return -1; + } + + protected getIndexFromTypeArrayBuffer(type: BaseType): number { + return PandaGen.appendTypeArrayBuffer(type); + } + + protected setTypeArrayBuffer(type: BaseType, index: number) { + PandaGen.setTypeArrayBuffer(type, index); + } + + protected getTypeNum() { + return this.typeRecorder.countTypeSet(); + } + + // temp for test + + protected printMap(map: Map) { + map.forEach((value, key) => { + console.log(jshelpers.getTextOfNode(key) + ": " + value); + }); + } + + protected getLog(node: ts.Node, currIndex: number) { + // console.log("=========== NodeKind ===========: " + node.kind); + // console.log(jshelpers.getTextOfNode(node)); + // console.log("=========== currIndex ===========: ", currIndex); + // console.log(PandaGen.getLiteralArrayBuffer()[currIndex]); + // console.log("=============================="); + // console.log("type2Index: "); + // console.log(this.printMap(this.typeRecorder.getType2Index())); + // console.log("variable2Type: "); + // console.log(this.printMap(this.typeRecorder.getVariable2Type())); + // console.log("getTypeSet: "); + // console.log(this.typeRecorder.getTypeSet()); + // console.log("=============================="); + } +} + +export class PlaceHolderType extends BaseType { + transfer2LiteralBuffer(): LiteralBuffer { + return new LiteralBuffer(); + } +} + +export class typeNumCounter extends BaseType { + preservedIndex: number = 0; + constructor() { + super(); + this.preservedIndex = this.getIndexFromTypeArrayBuffer(new PlaceHolderType()); + } + + public setNumCount() { + this.setTypeArrayBuffer(this, this.preservedIndex); + } + + transfer2LiteralBuffer(): LiteralBuffer { + let countBuf = new LiteralBuffer(); + let countLiterals: Array = new Array(); + countLiterals.push(new Literal(LiteralTag.INTEGER, L2Type._COUNTER)); + countLiterals.push(new Literal(LiteralTag.INTEGER, TypeRecorder.getInstance().countTypeSet())); + + countBuf.addLiterals(...countLiterals); + return countBuf; + } +} + +export class ClassType extends BaseType { + modifier: number = 0; // 0 -> unabstract, 1 -> abstract; + heritages: Array = new Array(); + // fileds Array: [typeIndex] [public -> 0, private -> 1, protected -> 2] [readonly -> 1] + staticFields: Map> = new Map>(); + staticMethods: Array = new Array(); + fields: Map> = new Map>(); + methods: Array = new Array(); + + constructor(classNode: ts.ClassDeclaration, newExpressionFlag: boolean, variableNode?: ts.Node) { + super(); + let currIndex = this.getIndexFromTypeArrayBuffer(new PlaceHolderType()); + let shiftedIndex = currIndex + PrimitiveType._LENGTH; + // record type before its initialization, so its index can be recorded + // in case there's recursive reference of this type + this.addCurrentType(classNode, shiftedIndex); + + this.fillInModifiers(classNode); + this.fillInHeritages(classNode); + this.fillInFieldsAndMethods(classNode); + + // initialization finished, add variable to type if variable is given + if (variableNode) { + // if the variable is a instance, create another classInstType instead of current classType itself + if (newExpressionFlag) { + new ClassInstType(variableNode, currIndex); + } else { + this.setVariable2Type(variableNode, shiftedIndex, true); + } + } + this.setTypeArrayBuffer(this, currIndex); + // check typeRecorder + this.getLog(classNode, currIndex); + } + + private fillInModifiers(node: ts.ClassDeclaration) { + if (node.modifiers) { + for (let modifier of node.modifiers) { + switch (modifier.kind) { + case ts.SyntaxKind.AbstractKeyword: { + this.modifier = ModifierAbstract.ABSTRACT; + break; + } + case ts.SyntaxKind.ExportKeyword: { + break; + } + } + } + } + } + + private fillInHeritages(node: ts.ClassDeclaration) { + if (node.heritageClauses) { + for (let heritage of node.heritageClauses) { + for (let heritageType of heritage.types) { + let heritageIdentifier = heritageType.expression; + let heritageTypeIndex = this.getOrCreateUserDefinedType(heritageIdentifier, false); + this.heritages.push(heritageTypeIndex); + } + } + } + } + + private fillInFields(member: ts.PropertyDeclaration) { + // collect modifier info + let fieldName: string = ""; + switch (member.name.kind) { + case ts.SyntaxKind.Identifier: + case ts.SyntaxKind.StringLiteral: + case ts.SyntaxKind.NumericLiteral: + fieldName = jshelpers.getTextOfIdentifierOrLiteral(member.name); + break; + case ts.SyntaxKind.ComputedPropertyName: + fieldName = "#computed"; + break; + default: + throw new Error("Invalid proerty name"); + } + + // Array: [typeIndex] [public -> 0, private -> 1, protected -> 2] [readonly -> 1] + let fieldInfo = Array(PrimitiveType.ANY, AccessFlag.PUBLIC, ModifierReadonly.NONREADONLY); + let isStatic: boolean = false; + if (member.modifiers) { + for (let modifier of member.modifiers) { + switch (modifier.kind) { + case ts.SyntaxKind.StaticKeyword: { + isStatic = true; + break; + } + case ts.SyntaxKind.PrivateKeyword: { + fieldInfo[1] = AccessFlag.PRIVATE; + break; + } + case ts.SyntaxKind.ProtectedKeyword: { + fieldInfo[1] = AccessFlag.PROTECTED; + break; + } + case ts.SyntaxKind.ReadonlyKeyword: { + fieldInfo[2] = ModifierReadonly.READONLY; + break; + } + } + } + } + // collect type info + let variableNode = member.name ? member.name : undefined; + fieldInfo[0] = this.getTypeIndexForDeclWithType(member, variableNode); + + if (isStatic) { + this.staticFields.set(fieldName, fieldInfo); + } else { + this.fields.set(fieldName, fieldInfo); + } + } + + private fillInMethods(member: ClassMemberFunction) { + /** + * a method like declaration in a new class must be a new type, + * create this type and add it into typeRecorder + */ + let variableNode = member.name ? member.name : undefined; + let funcType = new FunctionType(member, variableNode); + + // Then, get the typeIndex and fill in the methods array + let typeIndex = this.typeRecorder.tryGetTypeIndex(member); + let funcModifier = funcType.getModifier(); + if (funcModifier) { + this.staticMethods.push(typeIndex!); + } else { + this.methods.push(typeIndex!); + } + } + + private fillInFieldsAndMethods(node: ts.ClassDeclaration) { + if (node.members) { + for (let member of node.members) { + switch (member.kind) { + case ts.SyntaxKind.MethodDeclaration: + case ts.SyntaxKind.Constructor: + case ts.SyntaxKind.GetAccessor: + case ts.SyntaxKind.SetAccessor: { + this.fillInMethods(member); + break; + } + case ts.SyntaxKind.PropertyDeclaration: { + this.fillInFields(member); + break; + } + } + } + } + } + + transfer2LiteralBuffer() { + let classTypeBuf = new LiteralBuffer(); + let classTypeLiterals: Array = new Array(); + // the first element is to determine the L2 type + classTypeLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.CLASS)); + classTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.modifier)); + + classTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.heritages.length)); + this.heritages.forEach(heritage => { + classTypeLiterals.push(new Literal(LiteralTag.INTEGER, heritage)); + }); + + // record static methods and fields; + this.transferFields2Literal(classTypeLiterals, true); + this.transferMethods2Literal(classTypeLiterals, true); + + // record unstatic fields and methods + this.transferFields2Literal(classTypeLiterals, false); + this.transferMethods2Literal(classTypeLiterals, false); + + classTypeBuf.addLiterals(...classTypeLiterals); + return classTypeBuf; + } + + private transferFields2Literal(classTypeLiterals: Array, isStatic: boolean) { + let transferredTarget: Map> = isStatic ? this.staticFields : this.fields; + + classTypeLiterals.push(new Literal(LiteralTag.INTEGER, transferredTarget.size)); + transferredTarget.forEach((typeInfo, name) => { + classTypeLiterals.push(new Literal(LiteralTag.STRING, name)); + classTypeLiterals.push(new Literal(LiteralTag.INTEGER, typeInfo[0])); // typeIndex + classTypeLiterals.push(new Literal(LiteralTag.INTEGER, typeInfo[1])); // accessFlag + classTypeLiterals.push(new Literal(LiteralTag.INTEGER, typeInfo[2])); // readonly + }); + } + + private transferMethods2Literal(classTypeLiterals: Array, isStatic: boolean) { + let transferredTarget: Array = isStatic ? this.staticMethods : this.methods; + + classTypeLiterals.push(new Literal(LiteralTag.INTEGER, transferredTarget.length)); + transferredTarget.forEach(method => { + classTypeLiterals.push(new Literal(LiteralTag.INTEGER, method)); + }); + } +} + +export class ClassInstType extends BaseType { + shiftedReferredClassIndex: number = 0; // the referred class in the type system; + constructor(variableNode: ts.Node, referredClassIndex: number) { + super(); + // use referedClassIndex to point to the actually class type of this instance + this.shiftedReferredClassIndex = referredClassIndex + PrimitiveType._LENGTH; + + // map variable to classInstType, which has a newly generated index + let currIndex = this.getIndexFromTypeArrayBuffer(this); + let shiftedIndex = currIndex + PrimitiveType._LENGTH; + this.setVariable2Type(variableNode, shiftedIndex, true); + } + + transfer2LiteralBuffer(): LiteralBuffer { + let classInstBuf = new LiteralBuffer(); + let classInstLiterals: Array = new Array(); + + classInstLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.CLASSINST)); + classInstLiterals.push(new Literal(LiteralTag.INTEGER, this.shiftedReferredClassIndex)); + classInstBuf.addLiterals(...classInstLiterals); + + return classInstBuf; + } +} + +export class FunctionType extends BaseType { + name: string | undefined = ''; + accessFlag: number = 0; // 0 -> public -> 0, private -> 1, protected -> 2 + modifierStatic: number = 0; // 0 -> unstatic, 1 -> static + parameters: Array = new Array(); + returnType: number = 0; + + constructor(funcNode: ts.FunctionLikeDeclaration, variableNode?: ts.Node) { + super(); + + let currIndex = this.getIndexFromTypeArrayBuffer(new PlaceHolderType()); + let shiftedIndex = currIndex + PrimitiveType._LENGTH; + // record type before its initialization, so its index can be recorded + // in case there's recursive reference of this type + this.addCurrentType(funcNode, shiftedIndex); + + if (funcNode.name) { + this.name = jshelpers.getTextOfIdentifierOrLiteral(funcNode.name); + } else { + this.name = "constructor"; + } + this.fillInModifiers(funcNode); + this.fillInParameters(funcNode); + this.fillInReturn(funcNode); + + // initialization finished, add variable to type if variable is given + if (variableNode) { + this.setVariable2Type(variableNode, shiftedIndex, true); + } + this.setTypeArrayBuffer(this, currIndex); + + // check typeRecorder + this.getLog(funcNode, currIndex); + } + + private fillInModifiers(node: ts.FunctionLikeDeclaration) { + if (node.modifiers) { + for (let modifier of node.modifiers) { + switch (modifier.kind) { + case ts.SyntaxKind.PrivateKeyword: { + this.accessFlag = AccessFlag.PRIVATE; + break; + } + case ts.SyntaxKind.ProtectedKeyword: { + this.accessFlag = AccessFlag.PROTECTED; + break; + } + case ts.SyntaxKind.StaticKeyword: { + this.modifierStatic = ModifierStatic.STATIC; + } + } + } + } + } + + private fillInParameters(node: ts.FunctionLikeDeclaration) { + if (node.parameters) { + for (let parameter of node.parameters) { + let variableNode = parameter.name; + let typeIndex = this.getTypeIndexForDeclWithType(parameter, variableNode); + this.parameters.push(typeIndex); + } + } + } + + private fillInReturn(node: ts.FunctionLikeDeclaration) { + let typeIndex = this.getTypeIndexForDeclWithType(node); + if (typeIndex != -1) { + this.returnType = typeIndex; + } + } + + getModifier() { + return this.modifierStatic; + } + + transfer2LiteralBuffer(): LiteralBuffer { + let funcTypeBuf = new LiteralBuffer(); + let funcTypeLiterals: Array = new Array(); + funcTypeLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.FUNCTION)); + funcTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.accessFlag)); + funcTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.modifierStatic)); + funcTypeLiterals.push(new Literal(LiteralTag.STRING, this.name)); + funcTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.parameters.length)); + this.parameters.forEach((type) => { + funcTypeLiterals.push(new Literal(LiteralTag.INTEGER, type)); + }); + + funcTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.returnType)); + funcTypeBuf.addLiterals(...funcTypeLiterals); + return funcTypeBuf; + } +} + +export class ObjectLiteralType extends BaseType { + private properties: Map = new Map(); + private methods: Array = new Array(); + + constructor(obj: ts.ObjectLiteralExpression) { + super(); + + // TODO extract object info here + } + + transfer2LiteralBuffer(): LiteralBuffer { + let objTypeBuf = new LiteralBuffer(); + + return objTypeBuf; + } +} + +export class TypeOfVreg { + private vregNum: number; + private typeIndex: number; + + constructor(vregNum: number, typeIndex: number) { + this.vregNum = vregNum; + this.typeIndex = typeIndex; + } +} diff --git a/ts2panda/src/base/util.ts b/ts2panda/src/base/util.ts index c9a2177cadbb3a7a5f9ef364c1abc81d3f0cbc01..3c141cb82403db7c6b019471f02fdc7356eba619 100755 --- a/ts2panda/src/base/util.ts +++ b/ts2panda/src/base/util.ts @@ -295,4 +295,4 @@ export function getRangeStartVregPos(ins: IRNode): number { return -1; } return ins instanceof EcmaCreateobjectwithexcludedkeys ? 2 : 1; -} +} \ No newline at end of file diff --git a/ts2panda/src/cmdOptions.ts b/ts2panda/src/cmdOptions.ts index 6a14324b7f9888be4733769f9c83c00aa53d944f..dbe0e8bde33abc7ad7356fc713b8edb55086773c 100644 --- a/ts2panda/src/cmdOptions.ts +++ b/ts2panda/src/cmdOptions.ts @@ -39,6 +39,8 @@ const ts2pandaOptions = [ { name: 'bc-min-version', type: Boolean, defaultValue: false, description: "Print ark bytecode minimum supported version"} ] + + export class CmdOptions { private static parsedResult: ts.ParsedCommandLine; private static options: commandLineArgs.CommandLineOptions; diff --git a/ts2panda/src/compiler.ts b/ts2panda/src/compiler.ts index b87ad4cc6a300f0aacccc882a40714e3d05e25dd..4531bfb811f1cc6170ba056ab7ba610703864020 100644 --- a/ts2panda/src/compiler.ts +++ b/ts2panda/src/compiler.ts @@ -83,7 +83,11 @@ import { import { checkValidUseSuperBeforeSuper, compileClassDeclaration, - compileConstructor + compileConstructor, + compileDefaultConstructor, + compileDefaultInitClassMembers, + compileReturnThis4Ctor, + isContainConstruct } from "./statement/classStatement"; import { compileForOfStatement } from "./statement/forOfStatement"; import { LabelTarget } from "./statement/labelTarget"; @@ -109,7 +113,6 @@ import { isAssignmentOperator } from "./syntaxCheckHelper"; import { GlobalVariable, LocalVariable, - ModuleVariable, VarDeclarationKind, Variable } from "./variable"; @@ -216,6 +219,10 @@ export class Compiler { let statements = body.statements; let unreachableFlag = false; + if (body.parent && ts.isConstructorDeclaration(body.parent)) { + compileDefaultInitClassMembers(this, body.parent) + } + statements.forEach((stmt) => { this.compileStatement(stmt); if (stmt.kind == ts.SyntaxKind.ReturnStatement) { @@ -224,8 +231,7 @@ export class Compiler { }); if (body.parent && ts.isConstructorDeclaration(body.parent)) { - - compileConstructor(this, body.parent, unreachableFlag); + compileReturnThis4Ctor(this, body.parent, unreachableFlag); return; } @@ -336,6 +342,14 @@ export class Compiler { let pandaGen = this.pandaGen; this.compileFunctionParameterDeclaration(decl); + if (ts.isConstructorDeclaration(decl)) { + let classNode = decl.parent; + if (jshelpers.getClassExtendsHeritageElement(classNode) && !isContainConstruct(classNode)) { + compileDefaultConstructor(this, decl); + return; + } + } + if (decl.kind == ts.SyntaxKind.FunctionExpression) { if (decl.name) { let funcName = jshelpers.getTextOfIdentifierOrLiteral(decl.name); @@ -554,7 +568,7 @@ export class Compiler { compileFinallyBeforeCFC(endTry: TryStatement | undefined, cfc: ControlFlowChange, continueTargetLabel: Label | undefined) {// compile finally before control flow change let startTry = TryStatement.getCurrentTryStatement(); let originTry = startTry; - for (; startTry != endTry; startTry = startTry ?.getOuterTryStatement()) { + for (; startTry != endTry; startTry = startTry?.getOuterTryStatement()) { if (startTry && startTry.trybuilder) { let inlineFinallyBegin = new Label(); @@ -616,7 +630,7 @@ export class Compiler { // try-catch-finally statements must have been transformed into // two nested try statements with only "catch" or "finally" each. if (stmt.catchClause && stmt.finallyBlock) { - transformTryCatchFinally(stmt, this.recorder); + stmt = transformTryCatchFinally(stmt, this.recorder); } let tryBuilder = new TryBuilder(this, this.pandaGen, stmt); @@ -802,6 +816,8 @@ export class Compiler { case ts.SyntaxKind.ClassExpression: compileClassDeclaration(this, expr); break; + case ts.SyntaxKind.PartiallyEmittedExpression: + break; default: throw new Error("Expression of type " + this.getNodeName(expr) + " is unimplemented"); } @@ -1355,27 +1371,21 @@ export class Compiler { variable: { scope: Scope | undefined, level: number, v: Variable | undefined }, isDeclaration: boolean) { if (variable.v instanceof LocalVariable) { - if (isDeclaration) { - if (variable.v.isLet()) { - variable.v.initialize(); - if (variable.scope instanceof GlobalScope || variable.scope instanceof ModuleScope) { + if (isDeclaration && variable.v.isLetOrConst()) { + variable.v.initialize(); + if (variable.scope instanceof GlobalScope) { + if (variable.v.isLet()) { this.pandaGen.stLetToGlobalRecord(node, variable.v.getName()); - return; - } - } else if (variable.v.isConst()) { - variable.v.initialize(); - if (variable.scope instanceof GlobalScope || variable.scope instanceof ModuleScope) { + } else { this.pandaGen.stConstToGlobalRecord(node, variable.v.getName()); - return; } + return; } } - if (variable.v.isLetOrConst()) { - if (variable.scope instanceof GlobalScope || variable.scope instanceof ModuleScope) { - this.pandaGen.tryStoreGlobalByName(node, variable.v.getName()); - return; - } + if (variable.v.isLetOrConst() && variable.scope instanceof GlobalScope) { + this.pandaGen.tryStoreGlobalByName(node, variable.v.getName()); + return; } if (variable.scope && variable.level >= 0) { // inner most function will load outer env instead of new a lex env @@ -1407,11 +1417,9 @@ export class Compiler { } loadTarget(node: ts.Node, variable: { scope: Scope | undefined, level: number, v: Variable | undefined }) { - if (variable.v instanceof ModuleVariable) { - this.pandaGen.loadModuleVariable(node, variable.v.getModule(), variable.v.getExoticName()); - } else if (variable.v instanceof LocalVariable) { + if (variable.v instanceof LocalVariable) { if (variable.v.isLetOrConst() || variable.v.isClass()) { - if (variable.scope instanceof GlobalScope || variable.scope instanceof ModuleScope) { + if (variable.scope instanceof GlobalScope) { this.pandaGen.tryLoadGlobalByName(node, variable.v.getName()); return; } diff --git a/ts2panda/src/compilerDriver.ts b/ts2panda/src/compilerDriver.ts index e8fa59c59695974570ff6e896c80f7103e2c54b5..95fea85d3edf41781729dda9f75cea821962b8ed 100644 --- a/ts2panda/src/compilerDriver.ts +++ b/ts2panda/src/compilerDriver.ts @@ -43,6 +43,7 @@ import { import { getClassNameForConstructor } from "./statement/classStatement"; import { checkDuplicateDeclaration, checkExportEntries } from "./syntaxChecker"; import { Ts2Panda } from "./ts2panda"; +import { TypeRecorder } from "./typeRecorder"; export class PendingCompilationUnit { constructor( @@ -143,6 +144,12 @@ export class CompilerDriver { return spArray.reverse(); } + compileForSyntaxCheck(node: ts.SourceFile): void { + let recorder = this.compilePrologue(node, false); + checkDuplicateDeclaration(recorder); + checkExportEntries(recorder); + } + compile(node: ts.SourceFile): void { if (CmdOptions.showASTStatistics()) { let statics: number[] = new Array(ts.SyntaxKind.Count).fill(0); @@ -155,7 +162,8 @@ export class CompilerDriver { }); } - let recorder = this.compilePrologue(node); + let recorder = this.compilePrologue(node, true); + TypeRecorder.getInstance().getTypeCounter().setNumCount(); // initiate ts2abc if (!CmdOptions.isAssemblyMode()) { @@ -206,7 +214,7 @@ export class CompilerDriver { let compiler = new Compiler(node, pandaGen, this, recorder); if (CmdOptions.isModules() && ts.isSourceFile(node) && scope instanceof ModuleScope) { - setImport(recorder.getImportStmts(), scope, pandaGen, compiler); + setImport(recorder.getImportStmts(), scope, pandaGen); setExportBinding(recorder.getExportStmts(), scope, pandaGen); } @@ -233,7 +241,7 @@ export class CompilerDriver { } compileUnitTest(node: ts.SourceFile): void { - let recorder = this.compilePrologue(node); + let recorder = this.compilePrologue(node, true); for (let i = 0; i < this.pendingCompilationUnits.length; i++) { let unit: PendingCompilationUnit = this.pendingCompilationUnits[i]; @@ -249,7 +257,7 @@ export class CompilerDriver { let compiler = new Compiler(node, pandaGen, this, recorder); if (CmdOptions.isModules() && ts.isSourceFile(node) && scope instanceof ModuleScope) { - setImport(recorder.getImportStmts(), scope, pandaGen, compiler); + setImport(recorder.getImportStmts(), scope, pandaGen); setExportBinding(recorder.getExportStmts(), scope, pandaGen); } @@ -261,7 +269,7 @@ export class CompilerDriver { this.compilationUnits.push(pandaGen); } - private compilePrologue(node: ts.SourceFile) { + private compilePrologue(node: ts.SourceFile, recordType: boolean) { let topLevelScope: GlobalScope | ModuleScope; if (CmdOptions.isModules()) { topLevelScope = new ModuleScope(node); @@ -269,11 +277,9 @@ export class CompilerDriver { topLevelScope = new GlobalScope(node); } - let recorder = new Recorder(node, topLevelScope, this); + let recorder = new Recorder(node, topLevelScope, this, recordType); recorder.record(); - - checkDuplicateDeclaration(recorder); - checkExportEntries(recorder); + addVariableToScope(recorder); let postOrderVariableScopes = this.postOrderAnalysis(topLevelScope); diff --git a/ts2panda/src/debuginfo.ts b/ts2panda/src/debuginfo.ts index 527b8f0d50b933fcff00b9dff70631f05455dbf1..688eac267eeab3910fac3bb1d4784b2387f64719 100644 --- a/ts2panda/src/debuginfo.ts +++ b/ts2panda/src/debuginfo.ts @@ -171,8 +171,28 @@ export class DebugInfo { let firstStmt = pandaGen.getFirstStmt(); if (firstStmt) { let file = jshelpers.getSourceFileOfNode(firstStmt); - let loc = file.getLineAndCharacterOfPosition(firstStmt.getStart()); - let wholeLineText = firstStmt.getText(); + if (!file) { + return; + } + + let pos : number = 0; + let tempWholeLineText : string = "" + if (firstStmt.pos === -1 || firstStmt.end === -1) { + let parent = firstStmt.parent; + while (parent) { + if (parent.pos !== -1 && parent.end !== -1) { + pos = parent.pos; + tempWholeLineText = parent.getText(); + break; + } + parent = parent.parent; + } + } else { + pos = firstStmt.getStart(); + } + + let loc = file.getLineAndCharacterOfPosition(pos); + let wholeLineText = tempWholeLineText || firstStmt.getText(); posInfo.setSourecLineNum(loc.line); posInfo.setSourecColumnNum(loc.character); posInfo.setWholeLine(wholeLineText); @@ -199,12 +219,29 @@ export class DebugInfo { let wholeLineText = ""; if (DebugInfo.isNode(node)) { let tsNode = (node); - let file = jshelpers.getSourceFileOfNode(node); + let file = jshelpers.getSourceFileOfNode(tsNode); if (!file) { return; } - let loc = file.getLineAndCharacterOfPosition(tsNode.getStart()); - wholeLineText = tsNode.getText(); + + let pos : number = 0; + let tempWholeLineText : string = "" + if (tsNode.pos === -1 || tsNode.end === -1) { + let parent = tsNode.parent; + while (parent) { + if (parent.pos !== -1 && parent.end !== -1) { + pos = parent.pos; + tempWholeLineText = parent.getText(); + break; + } + parent = parent.parent; + } + } else { + pos = tsNode.getStart(); + } + + let loc = file.getLineAndCharacterOfPosition(pos); + wholeLineText = tempWholeLineText || tsNode.getText(); lineNumber = loc.line; columnNumber = loc.character; } @@ -381,4 +418,4 @@ export class DebugInfo { scope.setScopeEndIns(placeHolder); } } -} \ No newline at end of file +} diff --git a/ts2panda/src/index.ts b/ts2panda/src/index.ts index f778f0a75e74d54f8c02c1aacde5dfda9e89ed7e..36cbf1d8c1c5fff2e5f20b3620b9dbd241e1196f 100644 --- a/ts2panda/src/index.ts +++ b/ts2panda/src/index.ts @@ -23,10 +23,16 @@ import { CacheExpander } from "./pass/cacheExpander"; import { ICPass } from "./pass/ICPass"; import { RegAlloc } from "./regAllocator"; import { setGlobalStrict } from "./strictMode"; +import { TypeChecker } from "./typeChecker"; +import { TypeRecorder } from "./typeRecorder"; import jshelpers = require("./jshelpers"); +import path = require("path"); function main(fileNames: string[], options: ts.CompilerOptions) { let program = ts.createProgram(fileNames, options); + let typeChecker = TypeChecker.getInstance(); + TypeRecorder.createInstance(); + typeChecker.setTypeChecker(program.getTypeChecker()); let emitResult = program.emit( undefined, undefined, @@ -36,11 +42,23 @@ function main(fileNames: string[], options: ts.CompilerOptions) { before: [ (ctx: ts.TransformationContext) => { return (node: ts.SourceFile) => { - let outputBinName = CmdOptions.getOutputBinName(); - let fileName = node.fileName.substring(0, node.fileName.lastIndexOf('.')); - if (fileName != CmdOptions.getInputFileName()) { - outputBinName = fileName + ".abc"; + let outputBinName = getOutputBinName(node); + let compilerDriver = new CompilerDriver(outputBinName); + compilerDriver.compileForSyntaxCheck(node); + return node; + } + } + ], + after: [ + (ctx: ts.TransformationContext) => { + return (node: ts.SourceFile) => { + if (ts.getEmitHelpers(node)) { + const printer: ts.Printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); + const text: string = printer.printNode(ts.EmitHint.Unspecified, node, node); + let newNode = ts.createSourceFile(node.fileName, text, options.target!); + node = newNode; } + let outputBinName = getOutputBinName(node); let compilerDriver = new CompilerDriver(outputBinName); setGlobalStrict(jshelpers.isEffectiveStrictModeSourceFile(node, options)); if (CmdOptions.isVariantBytecode()) { @@ -70,6 +88,21 @@ function main(fileNames: string[], options: ts.CompilerOptions) { }); } +function getOutputBinName(node: ts.SourceFile) { + let outputBinName = CmdOptions.getOutputBinName(); + let fileName = node.fileName.substring(0, node.fileName.lastIndexOf('.')); + let inputFileName = CmdOptions.getInputFileName(); + if (/^win/.test(require('os').platform())) { + var inputFileTmps = inputFileName.split(path.sep); + inputFileName = path.posix.join(...inputFileTmps); + } + + if (fileName != inputFileName) { + outputBinName = fileName + ".abc"; + } + return outputBinName; +} + namespace Compiler { export namespace Options { export let Default: ts.CompilerOptions = { @@ -78,7 +111,7 @@ namespace Compiler { noEmitOnError: true, noImplicitAny: true, target: ts.ScriptTarget.ES2015, - module: ts.ModuleKind.CommonJS, + module: ts.ModuleKind.ES2015, strictNullChecks: true, skipLibCheck: true, alwaysStrict: true diff --git a/ts2panda/src/jshelpers.d.ts b/ts2panda/src/jshelpers.d.ts index 5e4203d763b9c1b65c72a2f0fd558f8a7bad91da..7138633f30c10086bc2a3b57ff16ae3391172ba3 100644 --- a/ts2panda/src/jshelpers.d.ts +++ b/ts2panda/src/jshelpers.d.ts @@ -13,6 +13,8 @@ * limitations under the License. */ +import ts from "typescript"; + export function getSymbol(node: ts.Node): ts.Symbol; export function tsStringToString(str: ts.__String): string; export function getTextOfIdentifierOrLiteral(node: ts.Node): string; @@ -22,7 +24,6 @@ export function getFlowNode(stmt: ts.Statement): ts.Node; export function bindSourceFile(sourceFile: ts.SourceFile, options: ts.CompilerOptions); export function createDiagnosticForNode(node: ts.Node, message: ts.DiagnosticMessage, ...args: (string | number | undefined)[]): ts.DiagnosticWithLocation; export function createCompilerDiagnostic(message: ts.DiagnosticMessage, ...args: (string | number | undefined)[]): ts.Diagnostic; -export function createCompilerDiagnostic(message: ts.DiagnosticMessage, ...args: (string | number | undefined)[]): ts.Diagnostic; export function createFileDiagnostic(file: ts.SourceFile, start: number, length: number, message: ts.DiagnosticMessage, ...args: (string | number | undefined)[]): ts.DiagnosticWithLocation; export function isEffectiveStrictModeSourceFile(node: ts.SourceFile, compilerOptions: ts.CompilerOptions): boolean; export function getErrorSpanForNode(sourceFile: ts.SourceFile, node: ts.Node): ts.TextSpan; @@ -70,5 +71,5 @@ export function skipOuterExpressions(node: ts.Node, kinds?: ts.OuterExpressionKi export function isSuperCall(n: ts.Node); export function isThisProperty(node: ts.Node): boolean; export function isThisIdentifier(node: ts.Node | undefined): boolean; -export function getClassExtendsHeritageElement(node: ts.ClassLikeDeclaration | ts.InterfaceDeclaration); -export function isSuperProperty(node: ts.Node); \ No newline at end of file +export function isSuperProperty(node: ts.Node); +export function setParent(child: T | undefined, parent: T["parent"] | undefined): T | undefined; \ No newline at end of file diff --git a/ts2panda/src/jshelpers.js b/ts2panda/src/jshelpers.js index a958271e22bad6749ab86f42f2d5b206e8e343c9..e3c2960d8d8a8f3201a57ba2e2f9638f0cf0dacc 100755 --- a/ts2panda/src/jshelpers.js +++ b/ts2panda/src/jshelpers.js @@ -103,10 +103,6 @@ function getTextOfNode(node, includeTrivia) { return ts.getTextOfNode(node, includeTrivia); } -function getContainingClass(node) { - return ts.getContainingClass(node); -} - function nodePosToString(node) { return ts.nodePosToString(node); } @@ -247,8 +243,8 @@ function isSuperProperty(node) { return ts.isSuperProperty(node); } -function getClassExtendsHeritageElement(node) { - return ts.getClassExtendsHeritageElement(node); +function setParent(child, parent) { + return ts.setParent(child, parent); } module.exports = { @@ -274,7 +270,6 @@ module.exports = { getSourceFileOfNode: getSourceFileOfNode, isIterationStatement: isIterationStatement, getTextOfNode: getTextOfNode, - getContainingClass: getContainingClass, nodePosToString: nodePosToString, getContainingFunctionDeclaration: getContainingFunctionDeclaration, tokenToString: tokenToString, @@ -310,5 +305,5 @@ module.exports = { isThisIdentifier: isThisIdentifier, isThisProperty: isThisProperty, isSuperProperty: isSuperProperty, - getClassExtendsHeritageElement: getClassExtendsHeritageElement, + setParent: setParent, }; diff --git a/ts2panda/src/lexenv.ts b/ts2panda/src/lexenv.ts index 3193042838a25f81ce0067cc1dbe716719d86ac2..eec0a290460ee1aae43f98b87d60f4f76f3685ff 100644 --- a/ts2panda/src/lexenv.ts +++ b/ts2panda/src/lexenv.ts @@ -35,7 +35,6 @@ import { PandaGen } from "./pandagen"; import { Scope } from "./scope"; import { LocalVariable, - ModuleVariable, Variable } from "./variable"; import jshelpers from "./jshelpers"; @@ -161,7 +160,7 @@ export class VariableAcessStore extends VariableAccessBase { insns.push(storeAccumulator(bindVreg)); - if (v.isExportVar() && !(v instanceof ModuleVariable)) { + if (v.isExportVar()) { insns.push(storeModuleVariable(v.getExportedName())); } @@ -197,7 +196,7 @@ export class VariableAcessStore extends VariableAccessBase { insns.push(storeLexicalVar(this.level, slot, valueReg)); insns.push(loadAccumulator(valueReg)); - if (v.isExportVar() && !(v instanceof ModuleVariable)) { + if (v.isExportVar()) { insns.push(storeModuleVariable(v.getExportedName())); } pandaGen.freeTemps(valueReg); diff --git a/ts2panda/src/modules.ts b/ts2panda/src/modules.ts index 705cba7b642e6009c39a8908af94fc0a0a296769..533725e41c22054169fb52223ea68df49b150875 100644 --- a/ts2panda/src/modules.ts +++ b/ts2panda/src/modules.ts @@ -16,10 +16,9 @@ import * as ts from "typescript"; import { PandaGen } from "./pandagen"; import jshelpers from "./jshelpers"; -import { LocalVariable, ModuleVariable } from "./variable"; +import { LocalVariable } from "./variable"; import { DiagnosticCode, DiagnosticError } from "./diagnostic"; import { ModuleScope } from "./scope"; -import { Compiler } from "./compiler"; export class ModuleStmt { private node: ts.Node @@ -69,23 +68,27 @@ export class ModuleStmt { } } -export function setImport(importStmts: Array, moduleScope: ModuleScope, pandagen: PandaGen, compiler: Compiler) { +export function setImport(importStmts: Array, moduleScope: ModuleScope, pandagen: PandaGen) { importStmts.forEach((importStmt) => { pandagen.importModule(importStmt.getNode(), importStmt.getModuleRequest()); - let moduleReg = pandagen.allocLocalVreg(); - pandagen.storeAccumulator(importStmt.getNode(), moduleReg); - + // import * as xxx from "a.js" if (importStmt.getNameSpace()) { let v = moduleScope.findLocal(importStmt.getNameSpace())!; pandagen.storeAccToLexEnv(importStmt.getNode(), moduleScope, 0, v, true); (v).initialize(); } + // import { ... } from "a.js" + // import defaultExport, * as a from "a.js" + let moduleReg = pandagen.allocLocalVreg(); + pandagen.storeAccumulator(importStmt.getNode(), moduleReg); + let bindingNameMap = importStmt.getBindingNameMap(); bindingNameMap.forEach((value: string, key: string) => { - let v = moduleScope.findLocal(key)!; - v.bindModuleVreg(moduleReg); - v.setExoticName(value); + let v = moduleScope.findLocal(key)!; + pandagen.loadObjProperty(importStmt.getNode(), moduleReg, value); + pandagen.storeAccToLexEnv(importStmt.getNode(), moduleScope, 0, v, true); + (v).initialize(); }); }) } @@ -118,13 +121,8 @@ export function setExportBinding(exportStmts: Array, moduleScope: Mo throw new DiagnosticError(exportStmt.getNode(), DiagnosticCode.Cannot_export_0_Only_local_declarations_can_be_exported_from_a_module, jshelpers.getSourceFileOfNode(exportStmt.getNode()), [value]); } - if (v instanceof ModuleVariable) { - pandagen.loadModuleVariable(exportStmt.getNode(), v.getModule(), v.getName()); - pandagen.storeModuleVar(exportStmt.getNode(), key); - } else { - (v).setExport(); - (v).setExportedName(key); - } + (v).setExport(); + (v).setExportedName(key); }); } }) diff --git a/ts2panda/src/pandagen.ts b/ts2panda/src/pandagen.ts index e823a721742aab76baff208acd0fb57a56475554..76355805f712ffcae164b79a6007181f1817fc05 100644 --- a/ts2panda/src/pandagen.ts +++ b/ts2panda/src/pandagen.ts @@ -183,6 +183,7 @@ import { CatchTable } from "./statement/tryStatement"; import { Variable } from "./variable"; +import { BaseType } from "./base/typeSystem"; export class PandaGen { private debugTag: string = "PandaGen"; @@ -202,7 +203,7 @@ export class PandaGen { private sourceCodeDebugInfo: string | undefined; private icSize: number = 0; - private static literalArrayBuffer: Array = []; + private static literalArrayBuffer: Array = new Array(); constructor(internalName: string, parametersCount: number, scope: Scope | undefined = undefined) { this.internalName = internalName; @@ -257,6 +258,16 @@ export class PandaGen { this.icSize = total; } + static appendTypeArrayBuffer(type: BaseType): number { + let index = PandaGen.literalArrayBuffer.length; + PandaGen.literalArrayBuffer.push(type.transfer2LiteralBuffer()); + return index; + } + + static setTypeArrayBuffer(type: BaseType, index: number) { + PandaGen.literalArrayBuffer[index] = type.transfer2LiteralBuffer(); + } + getFirstStmt() { return this.firstStmt; } diff --git a/ts2panda/src/pandasm.ts b/ts2panda/src/pandasm.ts index f4ca2651ae12ef281ecff503794687a19fa6ae0f..154d51ad5d2d30afc57c23ad9eec219f8c8a95eb 100644 --- a/ts2panda/src/pandasm.ts +++ b/ts2panda/src/pandasm.ts @@ -15,6 +15,7 @@ import { DebugPosInfo, VariableDebugInfo } from "./debuginfo"; import { LiteralBuffer } from "./base/literal"; +import { TypeOfVreg } from "./base/typeSystem"; export class Metadata { public attribute: string; @@ -72,6 +73,7 @@ export class Function { public variables: Array | undefined; public sourceFile: string; public sourceCode: string | undefined; + public typeInfo: Array; constructor( name: string, @@ -82,6 +84,7 @@ export class Function { variables: Array | undefined = undefined, sourceFile: string = "", sourceCode: string | undefined = undefined, + typeInfo: Array ) { this.name = name; this.signature = signature; @@ -93,6 +96,7 @@ export class Function { this.variables = variables; this.sourceFile = sourceFile; this.sourceCode = sourceCode; + this.typeInfo = typeInfo; } } diff --git a/ts2panda/src/patch.diff b/ts2panda/src/patch.diff new file mode 100644 index 0000000000000000000000000000000000000000..5c308190ca6b9ef05154cc6b960efd7ed38f5fa1 --- /dev/null +++ b/ts2panda/src/patch.diff @@ -0,0 +1,60 @@ +diff --git a/ts2panda/src/debuginfo.ts b/ts2panda/src/debuginfo.ts +index 6d05c0a..2fdd8a1 100644 +--- a/ts2panda/src/debuginfo.ts ++++ b/ts2panda/src/debuginfo.ts +@@ -174,8 +174,25 @@ export class DebugInfo { + if (!file) { + return; + } +- let loc = file.getLineAndCharacterOfPosition(firstStmt.getStart()); +- let wholeLineText = firstStmt.getText(); ++ ++ let pos : number = 0; ++ let tempWholeLineText : string = "" ++ if (firstStmt.pos === -1 || firstStmt.end === -1) { ++ let parent = firstStmt.parent; ++ while (parent) { ++ if (parent.pos !== -1 && parent.end !== -1) { ++ pos = parent.pos; ++ tempWholeLineText = parent.getText(); ++ break; ++ } ++ parent = parent.parent; ++ } ++ } else { ++ pos = firstStmt.getStart(); ++ } ++ ++ let loc = file.getLineAndCharacterOfPosition(pos); ++ let wholeLineText = tempWholeLineText || firstStmt.getText(); + posInfo.setSourecLineNum(loc.line); + posInfo.setSourecColumnNum(loc.character); + posInfo.setWholeLine(wholeLineText); +@@ -206,8 +223,25 @@ export class DebugInfo { + if (!file) { + return; + } +- let loc = file.getLineAndCharacterOfPosition(tsNode.getStart()); +- wholeLineText = tsNode.getText(); ++ ++ let pos : number = 0; ++ let tempWholeLineText : string = "" ++ if (tsNode.pos === -1 || tsNode.end === -1) { ++ let parent = tsNode.parent; ++ while (parent) { ++ if (parent.pos !== -1 && parent.end !== -1) { ++ pos = parent.pos; ++ tempWholeLineText = parent.getText(); ++ break; ++ } ++ parent = parent.parent; ++ } ++ } else { ++ pos = tsNode.getStart(); ++ } ++ ++ let loc = file.getLineAndCharacterOfPosition(pos); ++ wholeLineText = tempWholeLineText || tsNode.getText(); + lineNumber = loc.line; + columnNumber = loc.character; + } diff --git a/ts2panda/src/recorder.ts b/ts2panda/src/recorder.ts index d74afb9d1c3378feb0bff1e318a1e1610b83215a..a2a0570c4b0b0bfec2cef685ec15a1fd3fe874ff 100644 --- a/ts2panda/src/recorder.ts +++ b/ts2panda/src/recorder.ts @@ -35,25 +35,24 @@ import { LetDecl, LocalScope, LoopScope, - ModDecl, ModuleScope, Scope, VarDecl, VariableScope } from "./scope"; import { - AddCtor2Class, - isContainConstruct, - getClassNameForConstructor + AddCtor2Class, getClassNameForConstructor, isContainConstruct } from "./statement/classStatement"; import { checkSyntaxError } from "./syntaxChecker"; import { isGlobalIdentifier } from "./syntaxCheckHelper"; +import { TypeChecker } from "./typeChecker"; import { VarDeclarationKind } from "./variable"; export class Recorder { node: ts.Node; scope: Scope; compilerDriver: CompilerDriver; + recordType: boolean; private scopeMap: Map = new Map(); private hoistMap: Map = new Map(); private parametersMap: Map = new Map(); @@ -63,15 +62,17 @@ export class Recorder { private exportStmts: Array = []; private defaultUsed: boolean = false; - constructor(node: ts.Node, scope: Scope, compilerDriver: CompilerDriver) { + constructor(node: ts.Node, scope: Scope, compilerDriver: CompilerDriver, recordType: boolean) { this.node = node; this.scope = scope; this.compilerDriver = compilerDriver; + this.recordType = recordType; this.funcNameMap = new Map(); this.funcNameMap.set("main", 1); } record() { + this.setParent(this.node) this.setScopeMap(this.node, this.scope); this.recordInfo(this.node, this.scope); return this.node; @@ -81,9 +82,22 @@ export class Recorder { return this.ClassGroupOfNoCtor; } + private setParent(node: ts.Node) { + node.forEachChild(childNode => { + if (childNode!.parent == undefined || childNode.parent.kind != node.kind) { + childNode = jshelpers.setParent(childNode, node)!; + let originNode = ts.getOriginalNode(childNode); + childNode = ts.setTextRange(childNode, originNode); + } + this.setParent(childNode); + }); + } + private recordInfo(node: ts.Node, scope: Scope) { node.forEachChild(childNode => { - checkSyntaxError(childNode); + if (!this.recordType) { + checkSyntaxError(childNode); + } switch (childNode.kind) { case ts.SyntaxKind.FunctionExpression: case ts.SyntaxKind.MethodDeclaration: @@ -132,6 +146,7 @@ export class Recorder { break; } case ts.SyntaxKind.Identifier: { + // getTypeFlagsForIdentifier(childNode); this.recordVariableDecl(childNode, scope); break; } @@ -163,6 +178,13 @@ export class Recorder { this.recordInfo(childNode, scope); break; } + case ts.SyntaxKind.VariableStatement: { + if (this.recordType) { + TypeChecker.getInstance().formatNodeType(childNode); + } + this.recordInfo(childNode, scope); + break; + } default: this.recordInfo(childNode, scope); } @@ -197,6 +219,7 @@ export class Recorder { let parent = this.getDeclarationNodeOfId(id); if (parent) { + // console.log(id.getText()); let declKind = astutils.getVarDeclarationKind(parent); // collect declaration information to corresponding scope @@ -217,7 +240,7 @@ export class Recorder { let tmp: Scope | undefined = nearestRefVariableScope.getNearestLexicalScope(); let needCreateLoopEnv: boolean = false; if (nearestDefLexicalScope instanceof LoopScope) { - while(tmp) { + while (tmp) { if (tmp == nearestDefLexicalScope) { needCreateLoopEnv = true; break; @@ -236,7 +259,7 @@ export class Recorder { if (name == "arguments") { let varialbeScope = scope.getNearestVariableScope(); - varialbeScope ?.setUseArgs(true); + varialbeScope?.setUseArgs(true); } } @@ -292,7 +315,7 @@ export class Recorder { // import defaultExport from "a.js" if (importClause.name) { let name = jshelpers.getTextOfIdentifierOrLiteral(importClause.name); - scope.setDecls(new ModDecl(name, importClause.name)); + scope.setDecls(new ConstDecl(name, importClause.name)); importStmt.addLocalName(name, "default"); } @@ -313,7 +336,7 @@ export class Recorder { namedBindings.elements.forEach((element) => { let name: string = jshelpers.getTextOfIdentifierOrLiteral(element.name); let exoticName: string = element.propertyName ? jshelpers.getTextOfIdentifierOrLiteral(element.propertyName) : name; - scope.setDecls(new ModDecl(name, element)); + scope.setDecls(new ConstDecl(name, element)); importStmt.addLocalName(name, exoticName); }); } @@ -493,8 +516,11 @@ export class Recorder { // if variable share a same name with the parameter of its contained function, it should not be hoisted if (scope instanceof FunctionScope) { - let nearestFunc = jshelpers.getContainingFunction(node); - let functionParameters = this.getParametersOfFunction(nearestFunc); + let nearestFunc = jshelpers.getContainingFunctionDeclaration(node); + if (!nearestFunc) { + return; + } + let functionParameters = this.getParametersOfFunction(nearestFunc); if (functionParameters) { for (let i = 0; i < functionParameters.length; i++) { if (functionParameters[i].name == declName) { diff --git a/ts2panda/src/scope.ts b/ts2panda/src/scope.ts index e2aa29a06262b379970d0abd4e2ca518590cdc75..3885a0da561db32c1d368be2d19f3034afc80054 100644 --- a/ts2panda/src/scope.ts +++ b/ts2panda/src/scope.ts @@ -19,7 +19,6 @@ import { LOGD, LOGE } from "./log"; import { GlobalVariable, LocalVariable, - ModuleVariable, VarDeclarationKind, Variable } from "./variable"; @@ -55,12 +54,6 @@ export class ConstDecl extends Decl { } } -export class ModDecl extends Decl { - constructor(localName: string, node: ts.Node) { - super(localName, node); - } -} - export class FuncDecl extends Decl { readonly index: number; constructor(funcName: string, node: ts.Node, index: number) { @@ -393,9 +386,6 @@ export class ModuleScope extends VariableScope { } else if (declKind == VarDeclarationKind.VAR || declKind == VarDeclarationKind.FUNCTION) { v = new LocalVariable(declKind, name); this.locals.push(v); - } else if (declKind == VarDeclarationKind.MODULE) { - v = new ModuleVariable(VarDeclarationKind.CONST, name, InitStatus.INITIALIZED); - this.locals.push(v); } else { v = new LocalVariable(declKind, name, status); this.locals.push(v); diff --git a/ts2panda/src/statement/classStatement.ts b/ts2panda/src/statement/classStatement.ts index 1163c9e0b86aadac244721c152ca0e0f6375d031..7672f88486c3a10f1e2abbc8240132e3a030ecad 100644 --- a/ts2panda/src/statement/classStatement.ts +++ b/ts2panda/src/statement/classStatement.ts @@ -132,41 +132,23 @@ export function compileClassDeclaration(compiler: Compiler, stmt: ts.ClassLikeDe export function AddCtor2Class(recorder: Recorder, classNode: ts.ClassLikeDeclaration, scope: Scope) { let ctorNode; - let hasHeritage = classNode.heritageClauses && classNode.heritageClauses.length; - let statement: ts.Statement | undefined; - let superCallNode = ts.createSuper(); - if (hasHeritage) { - let parameter = ts.createParameter(undefined, undefined, ts.createToken(ts.SyntaxKind.DotDotDotToken), "args"); - ctorNode = ts.createConstructor(undefined, undefined, [parameter], undefined); - let callNode = ts.createCall( - superCallNode, - undefined, - [ts.createSpread(ts.createIdentifier("args"))] - ); - superCallNode.parent = callNode; - superCallNode.pos = classNode.pos; - superCallNode.end = classNode.pos; - statement = ts.createExpressionStatement(callNode); - callNode.parent = statement; - callNode.pos = classNode.pos; - callNode.end = classNode.pos; + if (jshelpers.getClassExtendsHeritageElement(classNode)) { + let parameter = ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createToken(ts.SyntaxKind.DotDotDotToken), "args"); + ctorNode = ts.factory.createConstructorDeclaration(undefined, undefined, [parameter], undefined); } else { - ctorNode = ts.createConstructor(undefined, undefined, [], undefined); + ctorNode = ts.factory.createConstructorDeclaration(undefined, undefined, [], undefined); } - if (statement) { - ctorNode.body = ts.createBlock([statement]); - statement.parent = ctorNode; - statement.pos = classNode.pos; - statement.end = classNode.pos; - } else { - ctorNode.body = ts.createBlock([]); - } + ctorNode = jshelpers.setParent(ctorNode, classNode)!; + ctorNode = ts.setTextRange(ctorNode, classNode); + + let body = ts.factory.createBlock([]); + body = jshelpers.setParent(body, ctorNode)!; + body = ts.setTextRange(body, classNode)!; - ctorNode.parent = classNode; - ctorNode.pos = classNode.pos; - ctorNode.end = classNode.pos; - ctorNode.body!.parent = ctorNode; + ctorNode = ts.factory.updateConstructorDeclaration(ctorNode, undefined, undefined, ctorNode.parameters, body); + ctorNode = jshelpers.setParent(ctorNode, classNode)!; + ctorNode = ts.setTextRange(ctorNode, classNode); let parentScope = recorder.getScopeOfNode(classNode); recorder.compilerDriver.getFuncId(classNode); @@ -183,6 +165,17 @@ export function AddCtor2Class(recorder: Recorder, classNode: ts.ClassLikeDeclara recorder.recordFunctionParameters(ctorNode); } +export function compileDefaultConstructor(compiler: Compiler, ctrNode: ts.ConstructorDeclaration) { + let callNode = ts.factory.createCallExpression(ts.factory.createSuper(), undefined, + [ts.factory.createSpreadElement(ts.factory.createIdentifier("args"))]); + + callNode = jshelpers.setParent(callNode, ctrNode)!; + callNode = ts.setTextRange(callNode, ctrNode)! + + compileSuperCall(compiler, callNode, [], true); + compileConstructor(compiler, ctrNode, false); +} + function compileUnCompiledProperty(compiler: Compiler, properties: Property[], classReg: VReg) { let pandaGen = compiler.getPandaGen(); for (let propertyIndex = 0; propertyIndex < properties.length; propertyIndex++) { @@ -246,9 +239,74 @@ function createClassLiteralBuf(compiler: Compiler, classBuffer: LiteralBuffer, pandaGen.storeAccumulator(stmt, vregs[1]); } +export function compileDefaultInitClassMembers(compiler: Compiler, node: ts.ConstructorDeclaration) { + let pandaGen = compiler.getPandaGen(); + let members = node.parent!.members; + for (let index = 0; index < members.length; index++) { + let decl = members[index]; + if (ts.isPropertyDeclaration(decl) && !jshelpers.hasStaticModifier(decl)) { + if (!decl.initializer) { + continue; + } + + let prop: VReg | string = ""; + let thisReg: VReg = pandaGen.getTemp(); + compiler.getThis(node, thisReg); + + compiler.compileExpression(decl.initializer); + + switch (decl.name.kind) { + case ts.SyntaxKind.Identifier: + case ts.SyntaxKind.StringLiteral: + case ts.SyntaxKind.NumericLiteral: { + prop = jshelpers.getTextOfIdentifierOrLiteral(decl.name); + pandaGen.storeObjProperty(node, thisReg, prop); + break; + } + case ts.SyntaxKind.ComputedPropertyName: { + // need to store the init value first + let initVal: VReg = pandaGen.getTemp(); + pandaGen.storeAccumulator(node, initVal); + + prop = pandaGen.getTemp(); + compiler.compileExpression(decl.name.expression); + pandaGen.storeAccumulator(node, prop); + + pandaGen.loadAccumulator(node, initVal); + pandaGen.storeObjProperty(node, thisReg, prop); + pandaGen.freeTemps(initVal, prop); + break; + } + default: + throw new Error("Private Identifier has not been supported") + + } + + pandaGen.freeTemps(thisReg); + } + } +} + +export function compileReturnThis4Ctor(compiler: Compiler, node: ts.ConstructorDeclaration, unreachableFlag: boolean) { + let pandaGen = compiler.getPandaGen(); + + if (unreachableFlag) { + return; + } + + let thisReg = pandaGen.getTemp(); + compiler.getThis(node, thisReg); + pandaGen.loadAccumulator(node, thisReg); + + checkValidUseSuperBeforeSuper(compiler, node); + + pandaGen.return(node); + pandaGen.freeTemps(thisReg); +} + export function compileConstructor(compiler: Compiler, node: ts.ConstructorDeclaration, unreachableFlag: boolean) { let pandaGen = compiler.getPandaGen(); - let members = node.parent.members; + let members = node.parent!.members; for (let index = 0; index < members.length; index++) { let decl = members[index]; @@ -325,19 +383,26 @@ export function compileSuperCall(compiler: Compiler, node: ts.CallExpression, ar function loadCtorObj(node: ts.CallExpression, compiler: Compiler) { let recorder = compiler.getRecorder(); let pandaGen = compiler.getPandaGen(); - let nearestFunc = jshelpers.getContainingFunction(node); + let nearestFunc = jshelpers.getContainingFunctionDeclaration(node); + if (!nearestFunc) { + return; + } + let nearestFuncScope = recorder.getScopeOfNode(nearestFunc); + if (!nearestFuncScope) { + return; + } if (ts.isConstructorDeclaration(nearestFunc)) { let funcObj = nearestFuncScope.findLocal("4funcObj"); pandaGen.loadAccumulator(node, pandaGen.getVregForVariable(funcObj)); } else { - let outerFunc = jshelpers.getContainingFunction(nearestFunc); - let outerFuncScope = recorder.getScopeOfNode(outerFunc); + let outerFunc = jshelpers.getContainingFunctionDeclaration(nearestFunc); + let outerFuncScope = recorder.getScopeOfNode(outerFunc!); outerFuncScope.pendingCreateEnv(); let level = 1; - while (!ts.isConstructorDeclaration(outerFunc)) { - outerFunc = jshelpers.getContainingFunction(outerFunc); + while (!ts.isConstructorDeclaration(outerFunc!)) { + outerFunc = jshelpers.getContainingFunctionDeclaration(outerFunc!); outerFuncScope.pendingCreateEnv(); level++; } @@ -424,7 +489,7 @@ export function getClassNameForConstructor(classNode: ts.ClassLikeDeclaration) { let className = ""; if (!isAnonymousClass(classNode)) { - className = jshelpers.getTextOfIdentifierOrLiteral(classNode.name); + className = jshelpers.getTextOfIdentifierOrLiteral(classNode.name!); } else { let outerNode = findOuterNodeOfParenthesis(classNode); @@ -646,7 +711,7 @@ function scalarArrayEquals(node1: ts.Node | undefined, node2: ts.Node | undefine let val1Modifs = node1.modifiers; let val2Modifs = node2.modifiers; if (val1Modifs && val2Modifs) { - return val1Modifs.length == val2Modifs.length && val1Modifs.every(function(v, i) { return v === val2Modifs![i] });; + return val1Modifs.length == val2Modifs.length && val1Modifs.every(function (v, i) { return v === val2Modifs![i] });; } if (!val1Modifs && !val2Modifs) { diff --git a/ts2panda/src/statement/returnStatement.ts b/ts2panda/src/statement/returnStatement.ts index 221db0bb2ea960d4c493bc41513e150f2f13867e..90510ba9b302d69ed5beac92d4b79c98e2a3bfe0 100644 --- a/ts2panda/src/statement/returnStatement.ts +++ b/ts2panda/src/statement/returnStatement.ts @@ -134,8 +134,8 @@ function compileNormalReturn(stmt: ts.ReturnStatement, returnValue: VReg, compil } function isReturnInDerivedCtor(stmt: ts.ReturnStatement) { - let funcNode = jshelpers.getContainingFunction(stmt); - if (!ts.isConstructorDeclaration(funcNode)) { + let funcNode = jshelpers.getContainingFunctionDeclaration(stmt); + if (!funcNode || !ts.isConstructorDeclaration(funcNode)) { return false; } diff --git a/ts2panda/src/statement/tryStatement.ts b/ts2panda/src/statement/tryStatement.ts index 6107c014533de7d417e574d15db0263670f81620..d720e5420da8226cb338697f6cb146f30bf40d77 100644 --- a/ts2panda/src/statement/tryStatement.ts +++ b/ts2panda/src/statement/tryStatement.ts @@ -28,9 +28,10 @@ import { import { IteratorType, IteratorRecord } from "./forOfStatement"; import * as astutils from "../astutils"; import { VarDeclarationKind } from "../variable"; +import jshelpers from "../jshelpers"; // adjust the try...catch...finally into nested try(try...catch) finally -export function transformTryCatchFinally(tryStmt: ts.TryStatement, recorder: Recorder) { +export function transformTryCatchFinally(tryStmt: ts.TryStatement, recorder: Recorder): ts.TryStatement { // after create new try block node, mapped with new scope, and adjust parent node let tryStmtScope = recorder.getScopeOfNode(tryStmt); let newTryBlockScope = new LocalScope(tryStmtScope); @@ -38,18 +39,16 @@ export function transformTryCatchFinally(tryStmt: ts.TryStatement, recorder: Rec (recorder.getScopeOfNode(tryStmt.tryBlock)).setParent(newTryStmtScope); (recorder.getScopeOfNode(tryStmt.catchClause!)).setParent(newTryStmtScope); - const newTryStmt = ts.createTry(tryStmt.tryBlock, tryStmt.catchClause, undefined); + const newTryStmt = ts.factory.createTryStatement(tryStmt.tryBlock, tryStmt.catchClause, undefined); recorder.setScopeMap(newTryStmt, newTryStmtScope); const newTryBlock = [newTryStmt]; - newTryBlock[0].pos = tryStmt.tryBlock.pos; - newTryBlock[0].end = tryStmt.tryBlock.end; - tryStmt.tryBlock = ts.updateBlock(tryStmt.tryBlock, newTryBlock); - newTryBlock[0].parent = tryStmt.tryBlock; - + newTryBlock[0] = jshelpers.setParent(newTryBlock[0], tryStmt)!; + newTryBlock[0] = ts.setTextRange(newTryBlock[0], tryStmt.tryBlock)!; + let tryBlock = ts.factory.updateBlock(tryStmt.tryBlock, newTryBlock); + tryStmt = ts.factory.updateTryStatement(tryStmt, tryBlock, undefined, tryStmt.finallyBlock); recorder.setScopeMap(tryStmt.tryBlock, newTryBlockScope); - tryStmt.catchClause = undefined; - tryStmt = ts.updateTry(tryStmt, tryStmt.tryBlock, undefined, tryStmt.finallyBlock); + return tryStmt; } export class LabelPair { @@ -359,7 +358,7 @@ function compileCatchClauseVariableDeclaration(compiler: Compiler, param: ts.Var } export function updateCatchTables(inlinedTry: TryStatement | undefined, targetTry: TryStatement, inlinedLabelPair: LabelPair) { - for (; inlinedTry != targetTry; inlinedTry = inlinedTry ?.getOuterTryStatement()) { + for (; inlinedTry != targetTry; inlinedTry = inlinedTry?.getOuterTryStatement()) { inlinedTry!.getCatchTable().splitLabelPair(inlinedLabelPair); } targetTry.getCatchTable().splitLabelPair(inlinedLabelPair); diff --git a/ts2panda/src/syntaxCheckHelper.ts b/ts2panda/src/syntaxCheckHelper.ts index 2c719c38f4175b8691a4eb8a4f3f505d39aea41d..8439b27263c0053e12b7543c7300efa8f5c99d11 100644 --- a/ts2panda/src/syntaxCheckHelper.ts +++ b/ts2panda/src/syntaxCheckHelper.ts @@ -256,6 +256,10 @@ export function isOptionalParameter(node: ts.ParameterDeclaration): boolean { return true; } + if (!node.parent || !node.parent.parent) { + return false; + } + let iife = jshelpers.getImmediatelyInvokedFunctionExpression(node.parent); if (iife) { return !node.type && !node.dotDotDotToken && node.parent.parameters.indexOf(node) >= iife.arguments.length; diff --git a/ts2panda/src/syntaxChecker.ts b/ts2panda/src/syntaxChecker.ts index a9fe751ddab39b9e7381d23f1d016fc1a18b7791..185e3a2df366f5fa3369b44b7e2d68d8dd127b38 100644 --- a/ts2panda/src/syntaxChecker.ts +++ b/ts2panda/src/syntaxChecker.ts @@ -21,7 +21,7 @@ import { } from "./diagnostic"; import { hasExportKeywordModifier } from "./base/util"; import { findInnerExprOfParenthesis } from "./expression/parenthesizedExpression"; -import jshelpers, { getContainingFunction, getSourceFileOfNode } from "./jshelpers"; +import jshelpers, { getContainingFunction, getContainingFunctionDeclaration, getSourceFileOfNode } from "./jshelpers"; import { LOGE } from "./log"; import { Recorder } from "./recorder"; import { @@ -73,7 +73,7 @@ export function checkDuplicateDeclaration(recorder: Recorder) { } // implement catchParameter-related duplicate-entry check - if ((node.kind == ts.SyntaxKind.Block) && (node.parent.kind == ts.SyntaxKind.CatchClause)) { + if ((node.kind == ts.SyntaxKind.Block) && (node.parent != undefined && node.parent.kind == ts.SyntaxKind.CatchClause)) { let catchScope = scopeMap.get(node.parent); checkDuplicateInCatch(scope, catchScope); } @@ -355,12 +355,11 @@ function checkMetaProperty(node: ts.MetaProperty) { if (!getContainingFunction(node)) { throw new DiagnosticError(node, DiagnosticCode.Meta_property_0_is_only_allowed_in_the_body_of_a_function_declaration_function_expression_or_constructor, file, args); } else { - let func = getContainingFunction(node); - while (getContainingFunction(func)) { - func = getContainingFunction(func); + let func = getContainingFunctionDeclaration(node); + while (getContainingFunctionDeclaration(func!)) { + func = getContainingFunctionDeclaration(func!); } - - if (ts.isArrowFunction(func)) { + if (func && ts.isArrowFunction(func)) { throw new DiagnosticError(node, DiagnosticCode.Meta_property_0_is_only_allowed_in_the_body_of_a_function_declaration_function_expression_or_constructor, file, args); } } @@ -1051,7 +1050,7 @@ function getPropertieDeclaration(node: ts.Node, name: ts.Node) { function checkDisallowedTrailingComma(list: ts.NodeArray | undefined) { if (list && list.hasTrailingComma) { - let file = jshelpers.getSourceFileOfNode(list); + let file = jshelpers.getSourceFileOfNode(list[0]); throw new DiagnosticError(list[0], DiagnosticCode.A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma, file); } } @@ -1059,10 +1058,10 @@ function checkDisallowedTrailingComma(list: ts.NodeArray | undefined) { function checkParameters(parameters: ts.NodeArray) { let count = parameters.length; let optionalParameter = false; - let file = jshelpers.getSourceFileOfNode(parameters); for (let i = 0; i < count; i++) { let parameter = parameters[i]; + let file = jshelpers.getSourceFileOfNode(parameter); if (parameter.dotDotDotToken) { if (i != count - 1) { throw new DiagnosticError(parameter.dotDotDotToken, DiagnosticCode.A_rest_parameter_must_be_last_in_a_parameter_list, file); @@ -1205,6 +1204,12 @@ function checkRegularExpression(regexp: ts.RegularExpressionLiteral) { new regexpParse().parseLiteral(regexpText); } +function checkThrowStatement(node: ts.ThrowStatement) { + if (ts.isIdentifier(node.expression) && (node.expression).text === '') { + throw new DiagnosticError(node, DiagnosticCode.Line_break_not_permitted_here, jshelpers.getSourceFileOfNode(node)); + } +} + function checkSyntaxErrorForSloppyAndStrictMode(node: ts.Node) { switch (node.kind) { case ts.SyntaxKind.BreakStatement: @@ -1270,6 +1275,9 @@ function checkSyntaxErrorForSloppyAndStrictMode(node: ts.Node) { case ts.SyntaxKind.RegularExpressionLiteral: checkRegularExpression(node); break; + case ts.SyntaxKind.ThrowStatement: + checkThrowStatement(node); + break; default: break; } diff --git a/ts2panda/src/ts2panda.ts b/ts2panda/src/ts2panda.ts index 58e7765b7952b67007b31d14407726cd213460d0..93a0ea256b27b1ed331eac1663324a46c9849607 100644 --- a/ts2panda/src/ts2panda.ts +++ b/ts2panda/src/ts2panda.ts @@ -27,6 +27,7 @@ import { PandaGen } from "./pandagen"; import { CatchTable, Function, Ins, Signature } from "./pandasm"; import { generateCatchTables } from "./statement/tryStatement"; import { escapeUnicode, isRangeInst, getRangeStartVregPos } from "./base/util"; +import { TypeOfVreg } from "./base/typeSystem"; const dollarSign: RegExp = /\$/g; @@ -35,7 +36,8 @@ const JsonType = { "record": 1, "string": 2, "literal_arr": 3, - "options": 4 + "options": 4, + "type_arr": 5 }; export class Ts2Panda { static strings: Set = new Set(); @@ -133,6 +135,11 @@ export class Ts2Panda { static dumpConstantPool(ts2abc: any): void { let literalArrays = PandaGen.getLiteralArrayBuffer(); + console.log("-=-=-=- LiteralArrayBuffer =-=-=-=-=-="); + for (let e of PandaGen.getLiteralArrayBuffer()) { + console.log(JSON.parse(JSON.stringify(e))); + } + if (CmdOptions.isEnableDebugLog()) { Ts2Panda.jsonString += escapeUnicode(JSON.stringify(literalArrays, null, 2)); } @@ -170,6 +177,16 @@ export class Ts2Panda { let funcSignature = Ts2Panda.getFuncSignature(pg); let funcInsnsAndRegsNum = Ts2Panda.getFuncInsnsAndRegsNum(pg); let sourceFile = pg.getSourceFileDebugInfo(); + let typeRecord = pg.getLocals(); + // console.log("\\\\\\-= funcNmae =-\\\\\\ - ", funcName); + let typeInfo = new Array(); + typeRecord.forEach((vreg) => { + let typeOfVreg = new TypeOfVreg(vreg.num, vreg.getTypeIndex()); + typeInfo.push(typeOfVreg); + + console.log("\\\\\\\\\\\\ vreg num \\\\\\\\\\", vreg.num); + console.log("\\\\\\\\\\\\ vreg type \\\\\\\\\\", vreg.getTypeIndex()); + }); let variables, sourceCode; if (CmdOptions.isDebugMode()) { @@ -189,6 +206,7 @@ export class Ts2Panda { variables, sourceFile, sourceCode, + typeInfo ); let catchTables = generateCatchTables(pg.getCatchMap()); catchTables.forEach((catchTable) => { diff --git a/ts2panda/src/typeChecker.ts b/ts2panda/src/typeChecker.ts new file mode 100644 index 0000000000000000000000000000000000000000..e08490bb08cd8247cf047eef0a3ef4309a9e10e4 --- /dev/null +++ b/ts2panda/src/typeChecker.ts @@ -0,0 +1,63 @@ +import ts from "typescript"; +import { ClassType } from "./base/typeSystem"; + +export class TypeChecker { + private static instance: TypeChecker; + private compiledTypeChecker: any = null; + private constructor() { } + + public static getInstance(): TypeChecker { + if (!TypeChecker.instance) { + TypeChecker.instance = new TypeChecker(); + } + return TypeChecker.instance; + } + + public setTypeChecker(typeChecker: ts.TypeChecker) { + this.compiledTypeChecker = typeChecker; + } + + public getTypeChecker(): ts.TypeChecker { + return this.compiledTypeChecker; + } + + public getTypeDeclAtLocation(node: ts.Node): ts.Node { + let identifierSymbol = this.compiledTypeChecker.getSymbolAtLocation(node); + if (identifierSymbol && identifierSymbol.declarations) { + return identifierSymbol!.declarations[0]; + } + return node; + } + + public getTypeFlagsAtLocation(node: ts.Node): string { + let typeFlag = this.compiledTypeChecker.getTypeAtLocation(node).getFlags(); + return ts.TypeFlags[typeFlag].toUpperCase(); + } + + public formatNodeType(node: ts.Node) { + if (this.compiledTypeChecker === null) { + return; + } + if (node.kind === ts.SyntaxKind.VariableStatement) { + const variableStatementNode = node; + const decList = variableStatementNode.declarationList; + decList.declarations.forEach(declaration => { + const nameNode = declaration.name; + let newExpressionFlag = false; + if (declaration.initializer && declaration.initializer.kind == ts.SyntaxKind.NewExpression) { + newExpressionFlag = true; + } + // let symbol: ts.Symbol = this.compiledTypeChecker.getSymbolAtLocation(nameNode); + // let targetNode = symbol?.valueDeclaration; + let type: ts.Type = this.compiledTypeChecker.getTypeAtLocation(nameNode); + let targetNode = type.getSymbol()?.valueDeclaration; + if (targetNode) { + if (ts.isClassDeclaration(targetNode!)) { + let testClassType = new ClassType(targetNode, newExpressionFlag, nameNode); + } + } + // console.log(type.getSymbol()?.valueDeclaration); + }) + } + } +} diff --git a/ts2panda/src/typeRecorder.ts b/ts2panda/src/typeRecorder.ts new file mode 100644 index 0000000000000000000000000000000000000000..86cc5e3379e14f0825224d2b15e5fec645478fc9 --- /dev/null +++ b/ts2panda/src/typeRecorder.ts @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2021 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 ts from "typescript"; +import { + BaseType, + PrimitiveType, + typeNumCounter +} from "./base/typeSystem"; + +export class TypeRecorder { + private static instance: TypeRecorder; + private type2Index: Map = new Map(); + private variable2Type: Map = new Map(); + private userDefinedTypeSet: Set = new Set();; + private typeCounter: typeNumCounter = new typeNumCounter(); + + private constructor() {} + + public static getInstance(): TypeRecorder { + return TypeRecorder.instance; + } + + public static createInstance(): TypeRecorder{ + TypeRecorder.instance = new TypeRecorder(); + return TypeRecorder.instance; + } + + public getTypeCounter(): typeNumCounter { + return this.typeCounter; + } + + public addTypeSet(index: number) { + this.userDefinedTypeSet.add(index); + } + + public countTypeSet(): number { + return this.userDefinedTypeSet.size; + } + + public addType2Index(typeNode: ts.Node, index: number) { + this.type2Index.set(typeNode, index); + this.addTypeSet(index); + } + + public setVariable2Type(variableNode: ts.Node, index: number, isUserDefinedType: boolean) { + this.variable2Type.set(variableNode, index); + if (isUserDefinedType) { + this.addTypeSet(index); + } + } + + public hasType(typeNode: ts.Node): boolean { + return this.type2Index.has(typeNode); + } + + public tryGetTypeIndex(typeNode: ts.Node): number { + if (this.type2Index.has(typeNode)) { + return this.type2Index.get(typeNode)!; + } else { + return -1; + } + } + + public tryGetVariable2Type(variableNode: ts.Node): number { + if (this.variable2Type.has(variableNode)) { + return this.variable2Type.get(variableNode)!; + } else { + return -1; + } + } + + // for log + public getType2Index(): Map { + return this.type2Index; + } + + public getVariable2Type(): Map { + return this.variable2Type; + } + + public getTypeSet() { + return this.userDefinedTypeSet; + } +} \ No newline at end of file diff --git a/ts2panda/src/variable.ts b/ts2panda/src/variable.ts index 966cb9191bab1bad699eff7fa377ce18dccad5d9..f56e7ad31085701be87eee7ed486a370ac8b04af 100644 --- a/ts2panda/src/variable.ts +++ b/ts2panda/src/variable.ts @@ -33,6 +33,7 @@ export enum VarDeclarationKind { export abstract class Variable { private vreg: VReg | undefined; private name: string; + private typeIndex: number; isLexVar: boolean = false; idxLex: number = 0; constructor( @@ -42,10 +43,14 @@ export abstract class Variable { this.name = name; this.vreg = undefined; this.name = name; + this.typeIndex = 77; } bindVreg(vreg: VReg) { + // console.log("=> variable name --", this.name); + // console.log("=> variable index --", this.typeIndex); this.vreg = vreg; + this.vreg.setTypeIndex(this.typeIndex); } hasAlreadyBinded(): boolean { @@ -63,6 +68,14 @@ export abstract class Variable { return this.name; } + getTypeIndex() { + return this.typeIndex; + } + + setTypeIndex(typeIndex: number) { + return this.typeIndex = typeIndex; + } + setLexVar(scope: VariableScope | LoopScope) { this.idxLex = scope.getLexVarIdx() scope.pendingCreateEnv(); @@ -140,37 +153,6 @@ export class LocalVariable extends Variable { } } -export class ModuleVariable extends LocalVariable { - private module: VReg | undefined; - private exoticName: string = ""; - - constructor(declKind: VarDeclarationKind, name: string, status: InitStatus) { - super(declKind, name, status); - } - - bindModuleVreg(vreg: VReg) { - this.module = vreg; - } - - setExoticName(exoticName: string) { - this.exoticName = exoticName; - } - - getExoticName() { - if (this.exoticName == "") { - throw new Error("Variable doesn't have exotic name"); - } - return this.exoticName; - } - - getModule() { - if (!this.module) { - throw new Error("Variable's module has not been binded"); - } - return this.module; - } -} - export class GlobalVariable extends Variable { constructor(declKind: VarDeclarationKind, name: string) { super(declKind, name); diff --git a/ts2panda/templates/diagnostic.ts.erb b/ts2panda/templates/diagnostic.ts.erb index a6df2d57301a1cf1c44f7573d347b324f96101de..795fda099aa820287af95bc61ec1e981a6c8638a 100755 --- a/ts2panda/templates/diagnostic.ts.erb +++ b/ts2panda/templates/diagnostic.ts.erb @@ -17,10 +17,10 @@ import { LOGE } from "./log" export class DiagnosticError { irnode:ts.Node | undefined; code:number; - file: ts.Node | undefined; + file: ts.SourceFile | undefined; args: (string | number | undefined)[]; - constructor(irnode:ts.Node | undefined, code:number,file?:ts.Node | undefined ,args?:(string | number | undefined)[]) { + constructor(irnode:ts.Node | undefined, code:number, file?:ts.SourceFile | undefined, args?:(string | number | undefined)[]) { this.code = code this.irnode = irnode this.file = file ? file : undefined @@ -40,13 +40,13 @@ export function printDiagnostic(diagnostic: ts.Diagnostic) { } -export function createDiagnosticOnFirstToken(file:ts.Node | undefined,node: ts.Node ,message: ts.DiagnosticMessage|ts.DiagnosticMessageChain,...args:(string | number | undefined)[]) { +export function createDiagnosticOnFirstToken(file:ts.SourceFile, node: ts.Node ,message: ts.DiagnosticMessage,...args:(string | number | undefined)[]) { let span = jshelpers.getSpanOfTokenAtPosition(file, node.pos); let diagnostic = jshelpers.createFileDiagnostic(file,span.start,span.length,message,...args); return diagnostic; } -export function createFileDiagnostic(file:ts.Node|undefined,node: ts.Node , message: ts.DiagnosticMessage|ts.DiagnosticMessageChain,...args:(string | number | undefined)[]) { +export function createFileDiagnostic(file:ts.SourceFile, node: ts.Node, message: ts.DiagnosticMessage,...args:(string | number | undefined)[]) { let diagnostic; let span = jshelpers.getErrorSpanForNode(file, node); @@ -68,7 +68,7 @@ export function createFileDiagnostic(file:ts.Node|undefined,node: ts.Node , mess return diagnostic; } -export function createDiagnostic(file:ts.Node|undefined, location: ts.Node | undefined, message: ts.DiagnosticMessage|ts.DiagnosticMessageChain,...args:(string | number | undefined)[]) { +export function createDiagnostic(file:ts.SourceFile | undefined, location: ts.Node | undefined, message: ts.DiagnosticMessage,...args:(string | number | undefined)[]) { var diagnostic; if (!location) { @@ -76,7 +76,7 @@ export function createDiagnostic(file:ts.Node|undefined, location: ts.Node | und } if (file) { - diagnostic = createFileDiagnostic(file,location,message,...args); + diagnostic = createFileDiagnostic(file, location, message, ...args); } else { diagnostic = jshelpers.createDiagnosticForNode(location, message, ...args) } diff --git a/ts2panda/templates/irnodes.ts.erb b/ts2panda/templates/irnodes.ts.erb index 40e6936c0e791ff33e6eb8a8075838784a55383e..2e73c9cee4799f148a372f65c730ebaa85e5d24f 100755 --- a/ts2panda/templates/irnodes.ts.erb +++ b/ts2panda/templates/irnodes.ts.erb @@ -185,6 +185,7 @@ export abstract class Intrinsic extends IRNode { export class VReg { private static global_id = 0; + private typeIndex: number; readonly id: number; // used for debug purpose to distinguish one instance from another num: number = -1; @@ -201,6 +202,7 @@ export class VReg { constructor() { this.id = VReg.global_id++; + this.typeIndex = 0; // for debug purposes this.setStackTrace(null); @@ -225,6 +227,14 @@ export class VReg { return; } } + + getTypeIndex() { + return this.typeIndex; + } + + setTypeIndex(typeIndex: number) { + this.typeIndex = typeIndex; + } } export class Imm extends IRNode { diff --git a/ts2panda/tests/BUILD.gn b/ts2panda/tests/BUILD.gn index b8a513f60cd36df96477d703e6ac3786537c6c6d..e8202851181070a481c49924225cd673ff758766 100644 --- a/ts2panda/tests/BUILD.gn +++ b/ts2panda/tests/BUILD.gn @@ -26,7 +26,6 @@ action("ts2abc_tests") { rebase_path(target_out_dir + "/.."), "--node-modules", rebase_path("${node_modules}"), - "--gn-build", ] if (host_toolchain == buildtool_linux) { diff --git a/ts2panda/ts2abc/BUILD.gn b/ts2panda/ts2abc/BUILD.gn index 77d34e3a711d64785e6a7d93b5a382e6d295a274..138df5b0a8486ce88237bf70b328d54017ca535f 100755 --- a/ts2panda/ts2abc/BUILD.gn +++ b/ts2panda/ts2abc/BUILD.gn @@ -21,6 +21,7 @@ config("ts2abc_config") { include_dirs = [ ".", "$jsoncpp_root/include", + "$ark_root/libpandabase", ] if (enable_bytecode_optimizer) { @@ -45,7 +46,7 @@ config("ts2abc_config") { } } -ohos_executable("ts2abc") { +ohos_static_library("ts2abc_static") { sources = [ "ts2abc.cpp" ] configs = [ ":ts2abc_config" ] @@ -89,6 +90,14 @@ ohos_executable("ts2abc") { libs = [ libcpp_static_lib ] } } +} + +ohos_executable("ts2abc") { + sources = [ "main.cpp" ] + + configs = [ ":ts2abc_config" ] + + deps = [ ":ts2abc_static" ] output_name = "js2abc" install_enable = true diff --git a/ts2panda/ts2abc/main.cpp b/ts2panda/ts2abc/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..587f2310e2e552492c407ecd659b06be9cbcbb9d --- /dev/null +++ b/ts2panda/ts2abc/main.cpp @@ -0,0 +1,92 @@ +/* * Copyright (c) 2021 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. + */ + +#include "assembly-type.h" +#include "assembly-program.h" +#include "assembly-emitter.h" +#include "json/json.h" +#include "ts2abc_options.h" +#include "ts2abc.h" + +int main(int argc, const char *argv[]) +{ + panda::PandArgParser argParser; + panda::Span sp(argv, argc); + panda::ts2abc::Options options(sp[0]); + options.AddOptions(&argParser); + + if (!argParser.Parse(argc, argv)) { + std::cerr << argParser.GetErrorString(); + std::cerr << argParser.GetHelpString(); + return RETURN_FAILED; + } + + std::string usage = "Usage: ts2abc [OPTIONS]... [ARGS]..."; + if (options.GetHelpArg()) { + std::cout << usage << std::endl; + std::cout << argParser.GetHelpString(); + return RETURN_SUCCESS; + } + + if (options.GetBcVersionArg() || options.GetBcMinVersionArg()) { + std::string version = options.GetBcVersionArg() ? panda::panda_file::GetVersion(panda::panda_file::version) : + panda::panda_file::GetVersion(panda::panda_file::minVersion); + std::cout << version << std::endl; + return RETURN_SUCCESS; + } + + if ((options.GetOptLevelArg() < OptLevel::O_LEVEL0) || (options.GetOptLevelArg() > OptLevel::O_LEVEL2)) { + std::cerr << "Incorrect optimization level value" << std::endl; + std::cerr << usage << std::endl; + std::cerr << argParser.GetHelpString(); + return RETURN_FAILED; + } + + std::string input, output; + std::string data = ""; + + if (!options.GetCompileByPipeArg()) { + input = options.GetTailArg1(); + output = options.GetTailArg2(); + if (input.empty() || output.empty()) { + std::cerr << "Incorrect args number" << std::endl; + std::cerr << "Usage example: ts2abc test.json test.abc"<< std::endl; + std::cerr << usage << std::endl; + std::cerr << argParser.GetHelpString(); + return RETURN_FAILED; + } + + if (!HandleJsonFile(input, data)) { + return RETURN_FAILED; + } + } else { + output = options.GetTailArg1(); + if (output.empty()) { + std::cerr << usage << std::endl; + std::cerr << argParser.GetHelpString(); + return RETURN_FAILED; + } + + if (!ReadFromPipe(data)) { + return RETURN_FAILED; + } + } + + if (!GenerateProgram(data, output, options.GetOptLevelArg(), options.GetOptLogLevelArg())) { + std::cerr << "call GenerateProgram fail" << std::endl; + return RETURN_FAILED; + } + + return RETURN_SUCCESS; +} diff --git a/ts2panda/ts2abc/tests/BUILD.gn b/ts2panda/ts2abc/tests/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..fc8e1aa25ebd2f9b3eb4a42f2195a5751df64581 --- /dev/null +++ b/ts2panda/ts2abc/tests/BUILD.gn @@ -0,0 +1,41 @@ +# Copyright (c) 2021 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("//ark/runtime_core/ark_config.gni") +import("//ark/ts2abc/ts2panda/ts2abc_config.gni") + +ts2abc_unittest_action("DebugLogTest") { + sources = [ "debuglog_test.cpp" ] +} + +ts2abc_unittest_action("DebugModeTest") { + sources = [ "debugmode_test.cpp" ] +} + +ts2abc_unittest_action("StringArrTest") { + sources = [ "stringarr_test.cpp" ] +} + +ts2abc_unittest_action("FunctionsTest") { + sources = [ "functions_test.cpp" ] +} + +group("unittest") { + testonly = true + deps = [ + ":DebugLogTestAction", + ":DebugModeTestAction", + ":FunctionsTestAction", + ":StringArrTestAction", + ] +} diff --git a/ts2panda/ts2abc/tests/debuglog_test.cpp b/ts2panda/ts2abc/tests/debuglog_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2c829cee4f3b29397ff2121d9edbe7f964d5af00 --- /dev/null +++ b/ts2panda/ts2abc/tests/debuglog_test.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 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. + */ + +#include "gtest/gtest.h" +#include "ts2abc.h" + +using namespace testing; +using namespace testing::ext; + +namespace ARK::Ts2Abc::Ts2Abc { + class DebugLogTest : public testing::Test { + public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp(); + void TearDown(); + }; + + void DebugLogTest::SetUpTestCase() {} + void DebugLogTest::TearDownTestCase() {} + void DebugLogTest::SetUp() {} + void DebugLogTest::TearDown() {} + + HWTEST_F(DebugLogTest, DebugLogTest_True, TestSize.Level0) + { + Json::Value rootValue; + rootValue["log_enabled"] = true; + ParseLogEnable(rootValue); + ASSERT_TRUE(GetDebugLog() == true); + } + + HWTEST_F(DebugLogTest, DebugLogTest_False, TestSize.Level0) + { + Json::Value rootValue; + rootValue["log_enabled"] = false; + ParseLogEnable(rootValue); + ASSERT_TRUE(GetDebugLog() == false); + } +} \ No newline at end of file diff --git a/ts2panda/ts2abc/tests/debugmode_test.cpp b/ts2panda/ts2abc/tests/debugmode_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..39b9099f53454843e2531121fbcb7b1584680aa0 --- /dev/null +++ b/ts2panda/ts2abc/tests/debugmode_test.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 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. + */ + +#include "gtest/gtest.h" +#include "ts2abc.h" + +using namespace testing; +using namespace testing::ext; + +namespace ARK::Ts2Abc::Ts2Abc { + class DebugModeTest : public testing::Test { + public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp(); + void TearDown(); + }; + + void DebugModeTest::SetUpTestCase() {} + void DebugModeTest::TearDownTestCase() {} + void DebugModeTest::SetUp() {} + void DebugModeTest::TearDown() {} + + HWTEST_F(DebugModeTest, DebugModeTest_True, TestSize.Level0) + { + Json::Value rootValue; + rootValue["debug_mode"] = true; + ParseDebugMode(rootValue); + ASSERT_TRUE(GetDebugModeEnabled() == true); + } + + HWTEST_F(DebugModeTest, DebugModeTest_False, TestSize.Level0) + { + Json::Value rootValue; + rootValue["debug_mode"] = false; + ParseDebugMode(rootValue); + ASSERT_TRUE(GetDebugModeEnabled() == false); + } +} \ No newline at end of file diff --git a/ts2panda/ts2abc/tests/functions_test.cpp b/ts2panda/ts2abc/tests/functions_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..700b36d6968c316614ad2d4ae2244329c73496b7 --- /dev/null +++ b/ts2panda/ts2abc/tests/functions_test.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 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. + */ + +#include + +#include "gtest/gtest.h" +#include "ts2abc.h" + +using namespace testing; +using namespace testing::ext; +namespace fs = std::filesystem; + +namespace ARK::Ts2Abc::Ts2Abc { + class FunctionTest : public testing::Test { + public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp(); + void TearDown(); + }; + + Json::Value function = {}; + + void FunctionTest::SetUpTestCase() {} + void FunctionTest::TearDownTestCase() {} + void FunctionTest::SetUp() + { + std::string file = "../../ark/ts2abc/ts2panda/ts2abc/tests/sources/add.json"; + std::string data = ""; + int ret = HandleJsonFile(file, data); + EXPECT_EQ(ret, 1); + Json::Value rootValue; + ret = ParseJson(data, rootValue); + EXPECT_EQ(ret, RETURN_SUCCESS); + function = rootValue["func_body"]; + } + void FunctionTest::TearDown() {} + + HWTEST_F(FunctionTest, FunctionTest_GetFunctionDefintion, TestSize.Level0) + { + auto pandaFunc = GetFunctionDefintion(function); + EXPECT_EQ(pandaFunc.name, function["name"].asString()); + EXPECT_EQ(pandaFunc.return_type.GetName(), "any"); + auto signature = function["signature"]; + EXPECT_EQ(pandaFunc.params.size(), signature["params"].asUInt()); + EXPECT_EQ(pandaFunc.regs_num, function["regs_num"].asUInt()); + } +} \ No newline at end of file diff --git a/ts2panda/ts2abc/tests/sources/add.json b/ts2panda/ts2abc/tests/sources/add.json new file mode 100644 index 0000000000000000000000000000000000000000..a5328fd5b3ef8d21d6d7006fbd4d5a29c07d377c --- /dev/null +++ b/ts2panda/ts2abc/tests/sources/add.json @@ -0,0 +1,157 @@ +{ + "type": 0, + "func_body": { + "name": "Add", + "signature": { + "params": 5 + }, + "ins": [ + { + "op": "mov.dyn", + "regs": [ + 4, + 12 + ], + "debug_pos_info": { + "lineNum": 1, + "columnNum": 4 + } + }, + { + "op": "mov.dyn", + "regs": [ + 3, + 11 + ], + "debug_pos_info": { + "lineNum": 1, + "columnNum": 4 + } + }, + { + "op": "mov.dyn", + "regs": [ + 2, + 10 + ], + "debug_pos_info": { + "lineNum": 1, + "columnNum": 4 + } + }, + { + "op": "mov.dyn", + "regs": [ + 1, + 9 + ], + "debug_pos_info": { + "lineNum": 1, + "columnNum": 4 + } + }, + { + "op": "mov.dyn", + "regs": [ + 0, + 8 + ], + "debug_pos_info": { + "lineNum": 1, + "columnNum": 4 + } + }, + { + "op": "ecma.ldlexenvdyn", + "debug_pos_info": { + "lineNum": 1, + "columnNum": 4 + } + }, + { + "op": "sta.dyn", + "regs": [ + 7 + ], + "debug_pos_info": { + "lineNum": 1, + "columnNum": 4 + } + }, + { + "op": "lda.dyn", + "regs": [ + 3 + ], + "debug_pos_info": { + "lineNum": 1, + "columnNum": 11 + } + }, + { + "op": "sta.dyn", + "regs": [ + 6 + ], + "debug_pos_info": { + "lineNum": 1, + "columnNum": 11 + } + }, + { + "op": "lda.dyn", + "regs": [ + 4 + ], + "debug_pos_info": { + "lineNum": 1, + "columnNum": 13 + } + }, + { + "op": "ecma.add2dyn", + "regs": [ + 6 + ], + "debug_pos_info": { + "lineNum": 1, + "columnNum": 11 + } + }, + { + "op": "sta.dyn", + "regs": [ + 5 + ], + "debug_pos_info": { + "lineNum": 1, + "columnNum": 4 + } + }, + { + "op": "lda.dyn", + "regs": [ + 5 + ], + "debug_pos_info": { + "lineNum": 1, + "columnNum": 4 + } + }, + { + "op": "return.dyn", + "debug_pos_info": { + "lineNum": 1, + "columnNum": 4 + } + } + ], + "labels": [], + "regs_num": 8, + "metadata": { + "attribute": "" + }, + "catchTables": [], + "sourceFile": "test.js" + } +} \ No newline at end of file diff --git a/ts2panda/ts2abc/tests/stringarr_test.cpp b/ts2panda/ts2abc/tests/stringarr_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bc08e8698e5e548dea0d0e55d9ca8f027e7a7e1b --- /dev/null +++ b/ts2panda/ts2abc/tests/stringarr_test.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 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. + */ + +#include "gtest/gtest.h" +#include "ts2abc.h" + +using namespace testing; +using namespace testing::ext; + +namespace ARK::Ts2Abc::Ts2Abc { + class StringArrTest : public testing::Test { + public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp(); + void TearDown(); + }; + + void StringArrTest::SetUpTestCase() {} + void StringArrTest::TearDownTestCase() {} + void StringArrTest::SetUp() {} + void StringArrTest::TearDown() {} + + HWTEST_F(StringArrTest, StringArrTest_With0, TestSize.Level0) + { + std::string input = "Hello 000World"; + std::string expected = "Hello 000World"; + std::string output = ParseString(input); + EXPECT_EQ(output, expected); + } + + HWTEST_F(StringArrTest, StringArrTest_HalfUnicodeChar, TestSize.Level0) + { + std::string input = "Hello\\UD834World"; + std::string expected = "Hello\\UD834World"; + std::string output = ParseString(input); + EXPECT_EQ(output, expected); + } +} \ No newline at end of file diff --git a/ts2panda/ts2abc/ts2abc.cpp b/ts2panda/ts2abc/ts2abc.cpp index 8f131798fb1334cc8f335f1a0881c63b052004d8..c77239887eb28385ec2a55c624fa370d591678df 100644 --- a/ts2panda/ts2abc/ts2abc.cpp +++ b/ts2panda/ts2abc/ts2abc.cpp @@ -25,6 +25,7 @@ #include "json/json.h" #include "ts2abc_options.h" #include "securec.h" +#include "ts2abc.h" #ifdef ENABLE_BYTECODE_OPT #include "optimize_bytecode.h" @@ -52,22 +53,11 @@ namespace { constexpr bool IS_DEFINED = true; // Temprorary map to simplify debuging std::unordered_map g_opcodeMap = { -#define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs) { name, panda::pandasm::Opcode::opcode }, +#define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs) {name, panda::pandasm::Opcode::opcode}, PANDA_INSTRUCTION_LIST(OPLIST) #undef OPLIST - { "", panda::pandasm::Opcode::INVALID }, + {"", panda::pandasm::Opcode::INVALID}, }; - - enum JsonType { - FUNCTION = 0, - RECORD, - STRING, - LITERALBUFFER, - OPTIONS - }; - - const int RETURN_SUCCESS = 0; - const int RETURN_FAILED = 1; } // pandasm hellpers @@ -121,10 +111,30 @@ static bool IsValidInt32(double value) value >= static_cast(std::numeric_limits::min())); } +bool GetDebugLog() +{ + return g_debugLogEnabled; +} + +static void SetDebugLog(bool debugLog) +{ + g_debugLogEnabled = debugLog; +} + +bool GetDebugModeEnabled() +{ + return g_debugModeEnabled; +} + +static void SetDebugModeEnabled(bool value) +{ + g_debugModeEnabled = value; +} + // Unified interface for debug log print static void Logd(const char *format, ...) { - if (g_debugLogEnabled) { + if (GetDebugLog()) { va_list valist; va_start(valist, format); char logMsg[LOG_BUFFER_SIZE]; @@ -195,7 +205,7 @@ static std::string ParseUnicodeEscapeString(const std::string &data) return newData; } -static std::string ParseString(const std::string &data) +std::string ParseString(const std::string &data) { if (data.find("\\u") != std::string::npos) { return ParseUnicodeEscapeString(data); @@ -379,7 +389,7 @@ static void ParseInstructionDebugInfo(const Json::Value &ins, panda::pandasm::In panda::pandasm::debuginfo::Ins insDebug; if (ins.isMember("debug_pos_info") && ins["debug_pos_info"].isObject()) { auto debugPosInfo = ins["debug_pos_info"]; - if (g_debugModeEnabled) { + if (GetDebugModeEnabled()) { if (debugPosInfo.isMember("boundLeft") && debugPosInfo["boundLeft"].isInt()) { insDebug.bound_left = debugPosInfo["boundLeft"].asInt(); } @@ -415,7 +425,7 @@ static panda::pandasm::Ins ParseInstruction(const Json::Value &ins) static int ParseVariablesDebugInfo(const Json::Value &function, panda::pandasm::Function &pandaFunc) { - if (!g_debugModeEnabled) { + if (!GetDebugModeEnabled()) { return RETURN_SUCCESS; } @@ -464,7 +474,7 @@ static int ParseSourceFileDebugInfo(const Json::Value &function, panda::pandasm: pandaFunc.source_file = function["sourceFile"].asString(); } - if (g_debugModeEnabled) { + if (GetDebugModeEnabled()) { if (function.isMember("sourceCode") && function["sourceCode"].isString()) { pandaFunc.source_code = function["sourceCode"].asString(); } @@ -496,7 +506,7 @@ static panda::pandasm::Function::CatchBlock ParsecatchBlock(const Json::Value &c return pandaCatchBlock; } -static panda::pandasm::Function GetFunctionDefintion(const Json::Value &function) +panda::pandasm::Function GetFunctionDefintion(const Json::Value &function) { std::string funcName = ""; if (function.isMember("name") && function["name"].isString()) { @@ -594,6 +604,42 @@ static void ParseFunctionCatchTables(const Json::Value &function, panda::pandasm } } +static void ParseFunctionTypeInfo(const Json::Value &function, panda::pandasm::Function &pandaFunc) +{ + if (function.isMember("typeInfo") && function["typeInfo"].isArray()) { + auto typeInfo = function["typeInfo"]; + panda::pandasm::AnnotationData funcAnnotation("_ESTypeAnnotation"); + std::vector elements; + for (Json::ArrayIndex i = 0; i < typeInfo.size(); i++) { + auto type = typeInfo[i]; + if (!type.isObject()) { + continue; + } + + // TODO add type-vreg info to function annotation + uint32_t vregNum = 0; + if (type.isMember("vregNum") && type["vregNum"].isInt()) { + vregNum = type["vregNum"].asUInt(); + } + + uint32_t typeIndex = 0; + if (type.isMember("typeIndex") && type["typeIndex"].isInt()) { + typeIndex = type["typeIndex"].asUInt(); + } + + panda::pandasm::ScalarValue vNum(panda::pandasm::ScalarValue::Create(vregNum)); + elements.emplace_back(std::move(vNum)); + panda::pandasm::ScalarValue tIndex(panda::pandasm::ScalarValue::Create(typeIndex)); + elements.emplace_back(std::move(tIndex)); + } + + std::string annotationName = "typeOfVreg"; + panda::pandasm::AnnotationElement typeOfVregElement(annotationName, std::make_unique(panda::pandasm::ArrayValue(panda::pandasm::Value::Type::U32, elements))); + funcAnnotation.AddElement(std::move(typeOfVregElement)); + const_cast&>(pandaFunc.metadata->GetAnnotations()).push_back(std::move(funcAnnotation)); + } +} + static panda::pandasm::Function ParseFunction(const Json::Value &function) { auto pandaFunc = GetFunctionDefintion(function); @@ -607,10 +653,20 @@ static panda::pandasm::Function ParseFunction(const Json::Value &function) ParseFunctionLabels(function, pandaFunc); // parsing catch blocks ParseFunctionCatchTables(function, pandaFunc); + // parsing type info + ParseFunctionTypeInfo(function, pandaFunc); return pandaFunc; } +static void GenerateESTypeAnnotationRecord(panda::pandasm::Program &prog) +{ + auto tsTypeAnnotationRecord = panda::pandasm::Record("_ESTypeAnnotation", LANG_EXT); + tsTypeAnnotationRecord.metadata->SetAttribute("external"); + tsTypeAnnotationRecord.metadata->SetAccessFlags(panda::ACC_ANNOTATION); + prog.record_table.emplace(tsTypeAnnotationRecord.name, std::move(tsTypeAnnotationRecord)); +} + static void GenrateESModuleModeRecord(panda::pandasm::Program &prog, bool moduleMode) { auto ecmaModuleModeRecord = panda::pandasm::Record("_ESModuleMode", LANG_EXT); @@ -627,7 +683,7 @@ static void GenrateESModuleModeRecord(panda::pandasm::Program &prog, bool module prog.record_table.emplace(ecmaModuleModeRecord.name, std::move(ecmaModuleModeRecord)); } -static int ParseJson(const std::string &data, Json::Value &rootValue) +int ParseJson(const std::string &data, Json::Value &rootValue) { JSONCPP_STRING errs; Json::CharReaderBuilder readerBuilder; @@ -658,18 +714,18 @@ static void ParseModuleMode(const Json::Value &rootValue, panda::pandasm::Progra GenrateESModuleModeRecord(prog, g_moduleModeEnabled); } -static void ParseLogEnable(const Json::Value &rootValue) +void ParseLogEnable(const Json::Value &rootValue) { if (rootValue.isMember("log_enabled") && rootValue["log_enabled"].isBool()) { - g_debugLogEnabled = rootValue["log_enabled"].asBool(); + SetDebugLog(rootValue["log_enabled"].asBool()); } } -static void ParseDebugMode(const Json::Value &rootValue) +void ParseDebugMode(const Json::Value &rootValue) { Logd("-----------------parse debug_mode-----------------"); if (rootValue.isMember("debug_mode") && rootValue["debug_mode"].isBool()) { - g_debugModeEnabled = rootValue["debug_mode"].asBool(); + SetDebugModeEnabled(rootValue["debug_mode"].asBool()); } } @@ -679,7 +735,7 @@ static void ParseOptLevel(const Json::Value &rootValue) if (rootValue.isMember("opt_level") && rootValue["opt_level"].isInt()) { g_optLevel = rootValue["opt_level"].asInt(); } - if (g_debugModeEnabled) { + if (GetDebugModeEnabled()) { g_optLevel = 0; } } @@ -705,6 +761,7 @@ static void ReplaceAllDistinct(std::string &str, const std::string &oldValue, co static void ParseOptions(const Json::Value &rootValue, panda::pandasm::Program &prog) { + GenerateESTypeAnnotationRecord(prog); ParseModuleMode(rootValue, prog); ParseLogEnable(rootValue); ParseDebugMode(rootValue); @@ -821,9 +878,7 @@ static bool ParseData(const std::string &data, panda::pandasm::Program &prog) return true; } -static bool GenerateProgram(const std::string &data, std::string output, - panda::PandArg optLevelArg, - panda::PandArg optLogLevelArg) +bool GenerateProgram(const std::string &data, std::string output, int optLevel, std::string optLogLevel) { panda::pandasm::Program prog = panda::pandasm::Program(); prog.lang = panda::pandasm::extensions::Language::ECMASCRIPT; @@ -835,8 +890,8 @@ static bool GenerateProgram(const std::string &data, std::string output, Logd("parsing done, calling pandasm\n"); #ifdef ENABLE_BYTECODE_OPT - if (g_optLevel != O_LEVEL0 || optLevelArg.GetValue() != O_LEVEL0) { - std::string optLogLevel = (optLogLevelArg.GetValue() != "error") ? optLogLevelArg.GetValue() : g_optLogLevel; + if (g_optLevel != O_LEVEL0 || optLevel != O_LEVEL0) { + std::string optLogLevel = (optLogLevel != "error") ? optLogLevel : g_optLogLevel; const uint32_t componentMask = panda::Logger::Component::CLASS2PANDA | panda::Logger::Component::ASSEMBLER | panda::Logger::Component::BYTECODE_OPTIMIZER | panda::Logger::Component::COMPILER; @@ -870,7 +925,7 @@ static bool GenerateProgram(const std::string &data, std::string output, return true; } -static bool HandleJsonFile(const std::string &input, std::string &data) +bool HandleJsonFile(const std::string &input, std::string &data) { auto inputAbs = panda::os::file::File::GetAbsolutePath(input); if (!inputAbs) { @@ -904,7 +959,7 @@ static bool HandleJsonFile(const std::string &input, std::string &data) return true; } -static bool ReadFromPipe(std::string &data) +bool ReadFromPipe(std::string &data) { const size_t bufSize = 4096; const size_t fd = 3; @@ -929,101 +984,3 @@ static bool ReadFromPipe(std::string &data) Logd("finish reading from pipe"); return true; } - -int main(int argc, const char *argv[]) -{ - panda::PandArgParser argParser; - panda::Span sp(argv, argc); - panda::ts2abc::Options options(sp[0]); - options.AddOptions(&argParser); - - panda::PandArg sizeStatArg("size-stat", false, "Print panda file size statistic"); - argParser.Add(&sizeStatArg); - panda::PandArg helpArg("help", false, "Print this message and exit"); - argParser.Add(&helpArg); - panda::PandArg optLevelArg("opt-level", 0, - "Optimization level. Possible values: [0, 1, 2]. Default: 0\n 0: no optimizations\n " - "1: basic bytecode optimizations, including valueNumber, lowering, constantResolver, regAccAllocator\n " - "2: (experimental optimizations): Sta/Lda Peephole, Movi/Lda Peephole, Register Coalescing"); - argParser.Add(&optLevelArg); - panda::PandArg optLogLevelArg("opt-log-level", "error", - "Optimization log level. Possible values: ['error', 'debug', 'info', 'fatal']. Default: 'error' "); - argParser.Add(&optLogLevelArg); - panda::PandArg bcVersionArg("bc-version", false, "Print ark bytecode version"); - argParser.Add(&bcVersionArg); - panda::PandArg bcMinVersionArg("bc-min-version", false, "Print ark bytecode minimum supported version"); - argParser.Add(&bcMinVersionArg); - panda::PandArg compileByPipeArg("compile-by-pipe", false, "Compile a json file that is passed by pipe"); - argParser.Add(&compileByPipeArg); - - argParser.EnableTail(); - - panda::PandArg tailArg1("ARG_1", "", "Path to input(json file) or path to output(ark bytecode)" \ - " when 'compile-by-pipe' enabled"); - panda::PandArg tailArg2("ARG_2", "", "Path to output(ark bytecode) or ignore when 'compile-by-pipe'" \ - " enabled"); - argParser.PushBackTail(&tailArg1); - argParser.PushBackTail(&tailArg2); - - if (!argParser.Parse(argc, argv)) { - std::cerr << argParser.GetErrorString(); - std::cerr << argParser.GetHelpString(); - return RETURN_FAILED; - } - - std::string usage = "Usage: ts2abc [OPTIONS]... [ARGS]..."; - if (helpArg.GetValue()) { - std::cout << usage << std::endl; - std::cout << argParser.GetHelpString(); - return RETURN_SUCCESS; - } - - if (bcVersionArg.GetValue() || bcMinVersionArg.GetValue()) { - std::string version = bcVersionArg.GetValue() ? panda::panda_file::GetVersion(panda::panda_file::version) : - panda::panda_file::GetVersion(panda::panda_file::minVersion); - std::cout << version << std::endl; - return RETURN_SUCCESS; - } - - if ((optLevelArg.GetValue() < O_LEVEL0) || (optLevelArg.GetValue() > O_LEVEL2)) { - std::cerr << "Incorrect optimization level value" << std::endl; - std::cerr << usage << std::endl; - std::cerr << argParser.GetHelpString(); - return RETURN_FAILED; - } - - std::string input, output; - std::string data = ""; - - if (!compileByPipeArg.GetValue()) { - input = tailArg1.GetValue(); - output = tailArg2.GetValue(); - if (input.empty() || output.empty()) { - std::cerr << "Incorrect args number" << std::endl; - std::cerr << "Usage example: ts2abc test.json test.abc\n" << std::endl; - std::cerr << usage << std::endl; - std::cerr << argParser.GetHelpString(); - return RETURN_FAILED; - } - if (!HandleJsonFile(input, data)) { - return RETURN_FAILED; - } - } else { - output = tailArg1.GetValue(); - if (output.empty()) { - std::cerr << usage << std::endl; - std::cerr << argParser.GetHelpString(); - return RETURN_FAILED; - } - if (!ReadFromPipe(data)) { - return RETURN_FAILED; - } - } - - if (!GenerateProgram(data, output, optLevelArg, optLogLevelArg)) { - std::cerr << "call GenerateProgram fail" << std::endl; - return RETURN_FAILED; - } - - return RETURN_SUCCESS; -} diff --git a/ts2panda/ts2abc/ts2abc.h b/ts2panda/ts2abc/ts2abc.h new file mode 100644 index 0000000000000000000000000000000000000000..90826ef44882fbf75517aeabee8f70f0235f62c2 --- /dev/null +++ b/ts2panda/ts2abc/ts2abc.h @@ -0,0 +1,60 @@ +/* * Copyright (c) 2021 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. + */ + +#ifndef PANDA_TS2ABC_GEN_H_ +#define PANDA_TS2ABC_GEN_H_ + +#include +#include +#include +#include +#include +#include + +#include "assembly-type.h" +#include "assembly-program.h" +#include "assembly-emitter.h" +#include "json/json.h" + +enum JsonType { + FUNCTION = 0, + RECORD, + STRING, + LITERALBUFFER, + OPTIONS +}; + +constexpr int RETURN_SUCCESS = 0; +constexpr int RETURN_FAILED = 1; + +enum OptLevel { + O_LEVEL0 = 0, + O_LEVEL1, + O_LEVEL2 +}; + +bool HandleJsonFile(const std::string &input, std::string &data); +bool ReadFromPipe(std::string &data); +bool GenerateProgram(const std::string &data, std::string output, + int optLevel, + std::string optLogLevel); +bool GetDebugLog(); +void ParseLogEnable(const Json::Value &rootValue); +bool GetDebugModeEnabled(); +void ParseDebugMode(const Json::Value &rootValue); +std::string ParseString(const std::string &data); +int ParseJson(const std::string &data, Json::Value &rootValue); +panda::pandasm::Function GetFunctionDefintion(const Json::Value &function); + +#endif // PANDA_TS2ABC_GEN_H_ \ No newline at end of file diff --git a/ts2panda/ts2abc/ts2abc_options.h b/ts2panda/ts2abc/ts2abc_options.h index d1d9e9f8efae336b165869c2cca9d606a0cae979..4ec2d2adbf98bb49c1290815956ba55f243096d8 100755 --- a/ts2panda/ts2abc/ts2abc_options.h +++ b/ts2panda/ts2abc/ts2abc_options.h @@ -23,30 +23,195 @@ #include namespace panda::ts2abc { -class Options { -public: + class Options { + public: + explicit Options(const std::string &exePath) : exe_dir_(GetExeDir(exePath)) {} - explicit Options(const std::string &exePath) : exe_dir_(GetExeDir(exePath)) {} + ~Options() = default; - ~Options() = default; + void AddOptions(PandArgParser *parser) + { + parser->Add(&size_stat_arg_); + parser->Add(&help_arg_); + parser->Add(&opt_level_arg_); + parser->Add(&opt_log_level_arg_); + parser->Add(&bc_version_arg_); + parser->Add(&bc_min_version_arg_); + parser->Add(&compile_by_pipe_arg_); + parser->EnableTail(); + parser->PushBackTail(&Tail_Arg1_arg_); + parser->PushBackTail(&Tail_Arg2_arg_); + } - void AddOptions(PandArgParser *parser) {} + bool GetSizeStatArg() const + { + return size_stat_arg_.GetValue(); + } -private: - static std::string GetExeDir(const std::string &exePath) - { - auto pos = exePath.find_last_of('/'); - return exePath.substr(0, pos); - } + void SetSizeStatArg(bool value) + { + size_stat_arg_.SetValue(value); + } - std::string exe_dir_; -}; -} // namespace panda::ts2abc + bool WasSetSizeStatArg() const + { + return size_stat_arg_.WasSet(); + } + + bool GetHelpArg() const + { + return help_arg_.GetValue(); + } + + void SetHelpArg(bool value) + { + help_arg_.SetValue(value); + } + + bool WasSetHelpArg() const + { + return help_arg_.WasSet(); + } + + int GetOptLevelArg() const + { + return opt_level_arg_.GetValue(); + } + + void SetOptLevelArg(int value) + { + opt_level_arg_.SetValue(value); + } + + bool WasSetOptLevelArg() const + { + return opt_level_arg_.WasSet(); + } + + std::string GetOptLogLevelArg() const + { + return opt_log_level_arg_.GetValue(); + } + + void SetOptLogLevelArg(std::string value) + { + opt_log_level_arg_.SetValue(value); + } + + bool WasSetOptLogLevelArg() const + { + return opt_log_level_arg_.WasSet(); + } + + bool GetBcVersionArg() const + { + return bc_version_arg_.GetValue(); + } + + void SetSetBcVersionArg(bool value) + { + bc_version_arg_.SetValue(value); + } + + bool WasBcVersionArg() const + { + return bc_version_arg_.WasSet(); + } + + bool GetBcMinVersionArg() const + { + return bc_min_version_arg_.GetValue(); + } -enum OptLevel { - O_LEVEL0 = 0, - O_LEVEL1, - O_LEVEL2 -}; + void SetBcMinVersionArg(bool value) + { + bc_min_version_arg_.SetValue(value); + } + + bool WasSetBcMinVersionArg() const + { + return bc_min_version_arg_.WasSet(); + } + + bool GetCompileByPipeArg() const + { + return compile_by_pipe_arg_.GetValue(); + } + + void SetCompileByPipeArg(bool value) + { + compile_by_pipe_arg_.SetValue(value); + } + + bool WasSetCompileByPipeArg() const + { + return compile_by_pipe_arg_.WasSet(); + } + + std::string GetTailArg1() const + { + return Tail_Arg1_arg_.GetValue(); + } + + void SetTailArg1(std::string value) + { + Tail_Arg1_arg_.SetValue(value); + } + + bool WasSetTailArg1() const + { + return Tail_Arg1_arg_.WasSet(); + } + + std::string GetTailArg2() const + { + return Tail_Arg2_arg_.GetValue(); + } + + void SetTailArg2(std::string value) + { + Tail_Arg2_arg_.SetValue(value); + } + + bool WasSetTailArg2() const + { + return Tail_Arg2_arg_.WasSet(); + } + + private: + static std::string GetExeDir(const std::string &exePath) + { + auto pos = exePath.find_last_of('/'); + return exePath.substr(0, pos); + } + + std::string exe_dir_; + panda::PandArg size_stat_arg_{ "size-stat", false, + R"(Print panda file size statistic)"}; + panda::PandArg help_arg_{ "help", false, + R"(Print this message and exit)"}; + panda::PandArg opt_level_arg_{ "opt-level", 0, + R"("Optimization level. Possible values: [0, 1, 2]. Default: 0\n" + "0: no optimizations\n " + "1: basic bytecode optimizations, including valueNumber," + "lowering, constantResolver, regAccAllocator\n " + "2: (experimental optimizations): Sta/Lda Peephole, " + "Movi/Lda Peephole, Register Coalescing")"}; + panda::PandArg opt_log_level_arg_{ "opt-log-level", "error", + R"(Optimization log level. Possible values: " + "['error', 'debug', 'info', 'fatal']. Default: 'error' )"}; + panda::PandArg bc_version_arg_{ "bc-version", false, + R"(Print ark bytecode version)"}; + panda::PandArg bc_min_version_arg_{ "bc-min-version", false, + R"(Print ark bytecode minimum supported version)"}; + panda::PandArg compile_by_pipe_arg_{ "compile-by-pipe", false, + R"(Compile a json file that is passed by pipe)"}; + panda::PandArg Tail_Arg1_arg_{ "ARG_1", "", + R"(Path to input(json file) or path to output(ark bytecode)" + " when 'compile-by-pipe' enabled)"}; + panda::PandArg Tail_Arg2_arg_{ "ARG_2", "", + R"(Path to output(ark bytecode) or ignore when 'compile-by-pipe' enabled)"}; + }; +} // namespace panda::ts2abc #endif // PANDA_TS2ABC_OPTIONS_GEN_H_ \ No newline at end of file diff --git a/ts2panda/ts2abc_config.gni b/ts2panda/ts2abc_config.gni index a86ae8032eb04bbe23de2eeda0b41bf5a8f4c4cb..9a1db65616308b05ce13d6eeb7451efc784268bc 100755 --- a/ts2panda/ts2abc_config.gni +++ b/ts2panda/ts2abc_config.gni @@ -12,6 +12,7 @@ # limitations under the License. import("//build/ohos.gni") +import("//build/test.gni") declare_args() { buildtool_linux = "//build/toolchain/linux:clang_x64" @@ -151,3 +152,79 @@ template("ts2abc_gen_abc") { outputs = invoker.out_puts } } + +template("ts2abc_unittest_action") { + _target_name_ = "${target_name}" + invoker.module_out_path = "ark/ts2abc" + + # unittest for phone running + ohos_unittest(_target_name_) { + configs = [ "${ts2abc_root}/ts2abc:ts2abc_config" ] + deps = [ "${ts2abc_root}/ts2abc:ts2abc_static" ] + forward_variables_from(invoker, "*") + } + + _module_out_path_ = invoker.module_out_path + + # unittest for host running + action("${_target_name_}Action") { + testonly = true + _host_test_target_ = ":${_target_name_}(${host_toolchain})" + _root_out_dir_ = get_label_info(_host_test_target_, "root_out_dir") + + deps = [ _host_test_target_ ] + + script = "${ts2abc_root}/scripts/run_tests_executable.sh" + + args = [ rebase_path(_root_out_dir_) + + "/tests/unittest/${_module_out_path_}/${_target_name_}" ] + + inputs = [ + "$_root_out_dir_/tests/unittest/${_module_out_path_}/${_target_name_}", + ] + outputs = [ "$target_out_dir/${_target_name_}/" ] + } +} + +# ts2abc performs the ut test +# +# Mandatory arguments: +# js_file: The name of the test use case file to execute , ex expression/TemplateExpression.test.js +template("ts2abc_unittest") { + assert(defined(invoker.js_file), "js_file is required!") + + action("$target_name") { + script = "${ts2abc_root}/scripts/run_tests.py" + deps = [ "${ts2abc_root}:ts2abc_tests" ] + + args = [ + "--src-dir", + rebase_path("${ts2abc_root}"), + "--dist-dir", + rebase_path(target_out_dir + "/.."), + "--node-modules", + rebase_path("${node_modules}"), + "--js-file", + invoker.js_file, + "--gn-build", + ] + + if (host_toolchain == buildtool_linux) { + args += [ + "--platform", + "linux", + ] + } else if (host_toolchain == buildtool_mac) { + args += [ + "--platform", + "mac", + ] + } else { + args += [ + "--platform", + "win", + ] + } + outputs = [ "$target_out_dir/${target_name}/" ] + } +}