diff --git a/README.md b/README.md index 209c2671678feccc08f00c94f53325224ad2c6bb..20a4727951bc20d798c2b42689cf160ad3eb5f6c 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Install `node` and `npm` convert JS to ARK bytecode ``` -$ cd out/ohos-arm-release/clang_x64/ark/ark/build +$ cd out//hi3516dv300/clang_x64/ark/ark/build $ npm install $ node --expose-gc src/index.js [options] file.js ``` diff --git a/README_zh.md b/README_zh.md index 447e2c22494165fe3481d277db1900e4dd4674b7..aa001701a561caba60ff45a72005ead08ffcd509 100644 --- a/README_zh.md +++ b/README_zh.md @@ -43,7 +43,7 @@ $ ./build.sh --product-name Hi3516DV300 --build-target ark_ts2abc 使用ts2abc组件将JavaScript文件转换为方舟字节码文件 ``` -$ cd out/ohos-arm-release/clang_x64/ark/ark/build +$ cd out/hi3516dv300/clang_x64/ark/ark/build $ npm install $ node --expose-gc src/index.js [options] file.js ``` diff --git a/test262/config.py b/test262/config.py index f053ac961e47efc2a3a1a1632a4e29275f023327..34670ad68a2d760e44ddc489b0feabafdde6fb97 100755 --- a/test262/config.py +++ b/test262/config.py @@ -29,9 +29,10 @@ BASE_OUT_DIR = os.path.join("out", "test262") CUR_FILE_DIR = os.path.dirname(__file__) 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" +ARK_DIR = f"{CODE_ROOT}/out/hi3516dv300/clang_x64/ark/ark" +ICUI_DIR = f"{CODE_ROOT}/out/hi3516dv300/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/hi3516dv300/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" @@ -83,6 +84,7 @@ ARK_ARCH_LIST = [ "aarch64", "arm" ] + DEFAULT_ARK_ARCH = ARK_ARCH_LIST[0] MODULE_FILES_LIST = [ diff --git a/test262/run_sunspider.py b/test262/run_sunspider.py index 9d958b1c25be6d7a380cddf4e73e70ae81366187..af5cdc6f94a512dd4d4e8473ac7f9ee1fe269ad4 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") parser.add_argument('--ark-arch', @@ -173,7 +182,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] cmd_args = [] diff --git a/test262/skip_tests.json b/test262/skip_tests.json index 09271c54d874aa6095791fba6f531f8c56f58495..c99bd2a5a19ee921ab000c78609e59ef170c825e 100755 --- a/test262/skip_tests.json +++ b/test262/skip_tests.json @@ -12,7 +12,8 @@ "files": [ "language/statements/for/S12.6.3_A2.1.js", "language/statements/for/S12.6.3_A2.2.js", - "language/statements/for/S12.6.3_A2.js" + "language/statements/for/S12.6.3_A2.js", + "language/expressions/template-literal/tv-utf16-escape-sequence.js" ] }, { diff --git a/ts2panda/.gitignore b/ts2panda/.gitignore index 7631277bc0eb17ec9af9a49c8fd436fd4d9e5115..9824f7fc54278d059e52cc142ff5d984dbca2484 100755 --- a/ts2panda/.gitignore +++ b/ts2panda/.gitignore @@ -1,5 +1,2 @@ node_modules/ build -src/builtinsMap.ts -src/irnodes.ts -src/diagnostic.ts diff --git a/ts2panda/BUILD.gn b/ts2panda/BUILD.gn index 80e0055ff0c598e7bebd0a0ef46549a25449bf45..0c7ce27427055915f503efae3d7bf15b79d1e692 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 = "" } @@ -334,5 +342,10 @@ 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 index e18acb061b27108ec6cbaa58cb9e2cb1e6dc877d..646821ba8881cb3f9add02ae06249b613d902356 100644 --- 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..f669e0589b6bd7ff918f4f9903fa33142cfd5123 --- /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://repo.huaweicloud.com/repository/npm/ +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/ || echo "skip copying due to other thread" diff --git a/ts2panda/scripts/run_tests.py b/ts2panda/scripts/run_tests.py index 00c75458ddfa9c8e9eaa06934d4ba04fdd507233..52899474369f1d258ad17092390426c723f93c6a 100755 --- a/ts2panda/scripts/run_tests.py +++ b/ts2panda/scripts/run_tests.py @@ -27,7 +27,7 @@ CUR_FILE_DIR = os.path.dirname(__file__) TS2PANDA_DIR = os.path.abspath(os.path.join(CUR_FILE_DIR, "..")) CODE_ROOT = os.path.abspath(os.path.join(TS2PANDA_DIR, "../../..")) DEFAULT_TARGET_DIR = os.path.join( - CODE_ROOT, "out/ohos-arm-release/clang_x64/obj/ark/ts2abc/ts2panda") + CODE_ROOT, "out/hi3516dv300/clang_x64/obj/ark/ts2abc/ts2panda") DEFAULT_NODE_MODULE = os.path.join( CODE_ROOT, "prebuilts/build-tools/common/ts2abc/node_modules") @@ -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]) @@ -131,16 +126,16 @@ class Ts2abcTests(): if self.args.js_file: tests_args = self.args.js_file else: - tests_args = "**/*.test.js" + tests_args = "tests/**/*.test.js" if plat_form == "linux": - cmd = [mocha, f'build/tests/{tests_args}'] + cmd = [mocha, f'build/{tests_args}'] ret = run_command(cmd, self.dist_dir) elif plat_form == "win": - cmd = [mocha, f'build-win/tests/{tests_args}'] + cmd = [mocha, f'build-win/{tests_args}'] ret = run_command(cmd, self.dist_dir) elif plat_form == 'mac': - cmd = [mocha, f'build-mac/tests/{tests_args}'] + cmd = [mocha, f'build-mac/{tests_args}'] ret = run_command(cmd, self.dist_dir) if ret: raise RuntimeError("Run [" + " ".join(cmd) + "] failed !") 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 c9f59db1642319cf47242a3e007076d922141e05..a4416549993d1fcc20ce215af9ca64a4e04b4a00 100644 --- a/ts2panda/src/addVariable2Scope.ts +++ b/ts2panda/src/addVariable2Scope.ts @@ -30,10 +30,37 @@ import { VariableScope } from "./scope"; import { isGlobalIdentifier } from "./syntaxCheckHelper"; -import { VarDeclarationKind } from "./variable"; +import { + VarDeclarationKind, + Variable +} from "./variable"; +import { TypeRecorder } from "./typeRecorder"; +import { CmdOptions } from "./cmdOptions"; +import { PrimitiveType } from "./base/typeSystem"; + +function setVariableOrParameterType(node: ts.Node, v: Variable | undefined) { + if (v) { + let typeIndex = TypeRecorder.getInstance().tryGetVariable2Type(ts.getOriginalNode(node)); + v.setTypeIndex(typeIndex); + } +} +function setClassOrFunctionType(node: ts.Node, v: Variable | undefined) { + if (v) { + let typeIndex = TypeRecorder.getInstance().tryGetTypeIndex(ts.getOriginalNode(node)); + v.setTypeIndex(typeIndex); + } +} -function addInnerArgs(node: ts.Node, scope: VariableScope): void { +function setTypeIndex(node: ts.Node, v: Variable | undefined, isClassOrFunction: boolean) { + if (isClassOrFunction) { + setClassOrFunctionType(node, v); + } else { + setVariableOrParameterType(node, v); + } +} + +function addInnerArgs(node: ts.Node, scope: VariableScope, enableTypeRecord: boolean): void { // the first argument for js function is func_obj scope.addParameter("4funcObj", VarDeclarationKind.CONST, -1); // the second argument for newTarget @@ -48,7 +75,7 @@ function addInnerArgs(node: ts.Node, scope: VariableScope): void { if (node.kind != ts.SyntaxKind.SourceFile) { let funcNode = node; - addParameters(funcNode, scope); + addParameters(funcNode, scope, enableTypeRecord); } if (scope.getUseArgs()) { @@ -66,25 +93,30 @@ function addInnerArgs(node: ts.Node, scope: VariableScope): void { } } -export function addVariableToScope(recorder: Recorder) { +export function addVariableToScope(recorder: Recorder, enableTypeRecord: boolean) { let scopeMap = recorder.getScopeMap(); let hoistMap = recorder.getHoistMap(); scopeMap.forEach((scope, node) => { let hoistDecls = []; if (scope instanceof VariableScope) { - addInnerArgs(node, scope); + addInnerArgs(node, scope, enableTypeRecord); 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); } else if (hoistDecl instanceof FuncDecl) { - scope.add(hoistDecl.name, VarDeclarationKind.FUNCTION); + v = scope.add(hoistDecl.name, VarDeclarationKind.FUNCTION); } else { throw new Error("Wrong type of declaration to be hoisted") } + + if (enableTypeRecord) { + setTypeIndex(hoistDecl.node, v, hoistDecl instanceof FuncDecl); + } }) } } @@ -98,22 +130,22 @@ 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); } else if (decl instanceof ConstDecl) { - scope.add(decl.name, VarDeclarationKind.CONST, InitStatus.UNINITIALIZED); + v = scope.add(decl.name, VarDeclarationKind.CONST, InitStatus.UNINITIALIZED); } else if (decl instanceof FuncDecl) { - scope.add(decl.name, VarDeclarationKind.FUNCTION); + v = scope.add(decl.name, VarDeclarationKind.FUNCTION); } else if (decl instanceof CatchParameter) { - scope.add(decl.name, VarDeclarationKind.LET); + v = scope.add(decl.name, VarDeclarationKind.LET); } 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); } else { let classScope = recorder.getScopeOfNode(classNode); - classScope.add(decl.name, VarDeclarationKind.CLASS, InitStatus.UNINITIALIZED); + v = classScope.add(decl.name, VarDeclarationKind.CLASS, InitStatus.UNINITIALIZED); } } else { /** @@ -123,14 +155,17 @@ 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); } } + if (enableTypeRecord) { + setTypeIndex(decl.node, v, decl instanceof ClassDecl || decl instanceof FuncDecl); + } } }) } -function addParameters(node: ts.FunctionLikeDeclaration, scope: VariableScope): void { +function addParameters(node: ts.FunctionLikeDeclaration, scope: VariableScope, enableTypeRecord: boolean): void { let patternParams: Array = new Array(); for (let i = 0; i < node.parameters.length; ++i) { let param = node.parameters[i]; @@ -142,7 +177,11 @@ 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); + + if (enableTypeRecord) { + setTypeIndex(param.name, v, false); + } } for (let i = 0; i < patternParams.length; i++) { diff --git a/ts2panda/src/base/typeSystem.ts b/ts2panda/src/base/typeSystem.ts new file mode 100755 index 0000000000000000000000000000000000000000..bd80b4607b440a99cdd7c2103ae2b1bd42a6bdbe --- /dev/null +++ b/ts2panda/src/base/typeSystem.ts @@ -0,0 +1,736 @@ +/* + * 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, + BIGINT, + STRING, + SYMBOL, + NULL, + UNDEFINED, + INT, + _LENGTH = 50 +} + +export enum L2Type { + _COUNTER, + CLASS, + CLASSINST, + FUNCTION, + UNION, + ARRAY, + OBJECT, // object literal + EXTERNAL, + INTERFACE +} + +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) { + this.typeRecorder.setVariable2Type(variableNode, index); + } + + protected tryGetTypeIndex(typeNode: ts.Node) { + return this.typeRecorder.tryGetTypeIndex(typeNode); + } + + protected getOrCreateRecordForDeclNode(typeNode: ts.Node, variableNode?: ts.Node) { + return this.typeChecker.getOrCreateRecordForDeclNode(typeNode, variableNode); + } + + protected getOrCreateRecordForTypeNode(typeNode: ts.TypeNode | undefined, variableNode?: ts.Node) { + return this.typeChecker.getOrCreateRecordForTypeNode(typeNode, variableNode); + } + + protected getIndexFromTypeArrayBuffer(type: BaseType): number { + return PandaGen.appendTypeArrayBuffer(type); + } + + protected setTypeArrayBuffer(type: BaseType, index: number) { + PandaGen.setTypeArrayBuffer(type, index); + } + +} + +export class PlaceHolderType extends BaseType { + transfer2LiteralBuffer(): LiteralBuffer { + return new LiteralBuffer(); + } +} + +export class TypeSummary extends BaseType { + preservedIndex: number = 0; + userDefinedClassNum: number = 0; + anonymousRedirect: Array = new Array(); + constructor() { + super(); + this.preservedIndex = this.getIndexFromTypeArrayBuffer(new PlaceHolderType()); + } + + public setInfo(userDefinedClassNum: number, anonymousRedirect: Array) { + this.userDefinedClassNum = userDefinedClassNum; + this.anonymousRedirect = anonymousRedirect; + this.setTypeArrayBuffer(this, this.preservedIndex); + } + + transfer2LiteralBuffer(): LiteralBuffer { + let countBuf = new LiteralBuffer(); + let summaryLiterals: Array = new Array(); + summaryLiterals.push(new Literal(LiteralTag.INTEGER, L2Type._COUNTER)); + summaryLiterals.push(new Literal(LiteralTag.INTEGER, this.userDefinedClassNum)); + summaryLiterals.push(new Literal(LiteralTag.INTEGER, this.anonymousRedirect.length)); + for (let element of this.anonymousRedirect) { + summaryLiterals.push(new Literal(LiteralTag.STRING, element)); + } + countBuf.addLiterals(...summaryLiterals); + return countBuf; + } +} + +export class ClassType extends BaseType { + modifier: number = ModifierAbstract.NONABSTRACT; // 0 -> unabstract, 1 -> abstract; + extendsHeritage: number = PrimitiveType.ANY; + implementsHeritages: Array = new Array(); + // fileds Array: [typeIndex] [public -> 0, private -> 1, protected -> 2] [readonly -> 1] + staticFields: Map> = new Map>(); + staticMethods: Map = new Map(); + fields: Map> = new Map>(); + methods: Map = new Map(); + typeIndex: number; + shiftedTypeIndex: number; + + constructor(classNode: ts.ClassDeclaration | ts.ClassExpression) { + super(); + this.typeIndex = this.getIndexFromTypeArrayBuffer(new PlaceHolderType()); + this.shiftedTypeIndex = this.typeIndex + 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, this.shiftedTypeIndex); + this.fillInModifiers(classNode); + this.fillInHeritages(classNode); + this.fillInFieldsAndMethods(classNode); + this.setTypeArrayBuffer(this, this.typeIndex); + } + + private fillInModifiers(node: ts.ClassDeclaration | ts.ClassExpression) { + 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 | ts.ClassExpression) { + if (node.heritageClauses) { + for (let heritage of node.heritageClauses) { + let heritageFullName = heritage.getText(); + for (let heritageType of heritage.types) { + let heritageIdentifier = heritageType.expression; + let heritageTypeIndex = this.getOrCreateRecordForDeclNode(heritageIdentifier, heritageIdentifier); + if (heritageFullName.startsWith("extends ")) { + this.extendsHeritage = heritageTypeIndex; + } else if (heritageFullName.startsWith("implements ")) { + this.implementsHeritages.push(heritageTypeIndex); + } + } + } + } + } + + private fillInFields(member: ts.PropertyDeclaration) { + let fieldName = jshelpers.getTextOfIdentifierOrLiteral(member.name); + 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; + } + } + } + } + + let typeNode = member.type + let memberName = member.name + fieldInfo[0] = this.getOrCreateRecordForTypeNode(typeNode, memberName); + + 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); + if (variableNode) { + this.setVariable2Type(variableNode, funcType.shiftedTypeIndex); + } + + // Then, get the typeIndex and fill in the methods array + let typeIndex = this.tryGetTypeIndex(member); + let funcModifier = funcType.getModifier(); + if (funcModifier) { + this.staticMethods.set(funcType.getFunctionName(), typeIndex!); + } else { + this.methods.set(funcType.getFunctionName(), typeIndex!); + } + } + + private fillInFieldsAndMethods(node: ts.ClassDeclaration | ts.ClassExpression) { + 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.extendsHeritage)); + classTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.implementsHeritages.length)); + this.implementsHeritages.forEach(heritage => { + classTypeLiterals.push(new Literal(LiteralTag.INTEGER, heritage)); + }); + + // record unstatic fields and methods + this.transferFields2Literal(classTypeLiterals, false); + this.transferMethods2Literal(classTypeLiterals, false); + + // record static methods and fields; + this.transferFields2Literal(classTypeLiterals, true); + this.transferMethods2Literal(classTypeLiterals, true); + + 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: Map = isStatic ? this.staticMethods : this.methods; + + 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)); + }); + } +} + +export class ClassInstType extends BaseType { + shiftedReferredClassIndex: number; // the referred class in the type system; + typeIndex: number; + shiftedTypeIndex: number; + constructor(referredClassIndex: number) { + super(); + this.shiftedReferredClassIndex = referredClassIndex; + this.typeIndex = this.getIndexFromTypeArrayBuffer(this); + this.shiftedTypeIndex = this.typeIndex + PrimitiveType._LENGTH; + this.typeRecorder.setClass2InstanceMap(this.shiftedReferredClassIndex, this.shiftedTypeIndex); + } + + 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 = ''; + accessFlag: number = AccessFlag.PUBLIC; // 0 -> public -> 0, private -> 1, protected -> 2 + modifierStatic: number = ModifierStatic.NONSTATIC; // 0 -> unstatic, 1 -> static + parameters: Array = new Array(); + returnType: number = PrimitiveType.ANY; + typeIndex: number; + shiftedTypeIndex: number; + + constructor(funcNode: ts.FunctionLikeDeclaration | ts.MethodSignature) { + super(); + this.typeIndex = this.getIndexFromTypeArrayBuffer(new PlaceHolderType()); + this.shiftedTypeIndex = this.typeIndex + 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, this.shiftedTypeIndex); + + if (funcNode.name) { + this.name = jshelpers.getTextOfIdentifierOrLiteral(funcNode.name); + } else { + this.name = "constructor"; + } + this.fillInModifiers(funcNode); + this.fillInParameters(funcNode); + this.fillInReturn(funcNode); + this.setTypeArrayBuffer(this, this.typeIndex); + } + + public getFunctionName() { + return this.name; + } + + private fillInModifiers(node: ts.FunctionLikeDeclaration | ts.MethodSignature) { + 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 | ts.MethodSignature) { + if (node.parameters) { + for (let parameter of node.parameters) { + let typeNode = parameter.type; + let variableNode = parameter.name; + let typeIndex = this.getOrCreateRecordForTypeNode(typeNode, variableNode); + this.parameters.push(typeIndex); + } + } + } + + private fillInReturn(node: ts.FunctionLikeDeclaration | ts.MethodSignature) { + let typeNode = node.type; + let typeIndex = this.getOrCreateRecordForTypeNode(typeNode, typeNode); + 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 ExternalType extends BaseType { + fullRedirectNath: string; + typeIndex: number; + shiftedTypeIndex: number; + + constructor(importName: string, redirectPath: string) { + super(); + this.fullRedirectNath = `#${importName}#${redirectPath}`; + this.typeIndex = this.getIndexFromTypeArrayBuffer(this); + this.shiftedTypeIndex = this.typeIndex + PrimitiveType._LENGTH; + } + + transfer2LiteralBuffer(): LiteralBuffer { + let ImpTypeBuf = new LiteralBuffer(); + let ImpTypeLiterals: Array = new Array(); + ImpTypeLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.EXTERNAL)); + ImpTypeLiterals.push(new Literal(LiteralTag.STRING, this.fullRedirectNath)); + ImpTypeBuf.addLiterals(...ImpTypeLiterals); + return ImpTypeBuf; + } +} + +export class UnionType extends BaseType { + unionedTypeArray: Array = []; + typeIndex: number = PrimitiveType.ANY; + shiftedTypeIndex: number = PrimitiveType.ANY; + + constructor(typeNode: ts.Node) { + super(); + this.setOrReadFromArrayRecord(typeNode); + } + + setOrReadFromArrayRecord(typeNode: ts.Node) { + let unionStr = typeNode.getText(); + if (this.hasUnionTypeMapping(unionStr)) { + this.shiftedTypeIndex = this.getFromUnionTypeMap(unionStr)!; + } else { + this.typeIndex = this.getIndexFromTypeArrayBuffer(new PlaceHolderType()); + this.shiftedTypeIndex = this.typeIndex + PrimitiveType._LENGTH; + this.fillInUnionArray(typeNode, this.unionedTypeArray); + this.setUnionTypeMap(unionStr, this.shiftedTypeIndex); + this.setTypeArrayBuffer(this, this.typeIndex); + } + } + + hasUnionTypeMapping(unionStr: string) { + return this.typeRecorder.hasUnionTypeMapping(unionStr); + } + + getFromUnionTypeMap(unionStr: string) { + return this.typeRecorder.getFromUnionTypeMap(unionStr); + } + + setUnionTypeMap(unionStr: string, shiftedTypeIndex: number) { + return this.typeRecorder.setUnionTypeMap(unionStr, shiftedTypeIndex); + } + + fillInUnionArray(typeNode: ts.Node, unionedTypeArray: Array) { + for (let element of (typeNode).types) { + let elementNode = element; + let typeIndex = this.getOrCreateRecordForTypeNode(elementNode, elementNode); + unionedTypeArray.push(typeIndex!); + } + } + + transfer2LiteralBuffer(): LiteralBuffer { + let UnionTypeBuf = new LiteralBuffer(); + let UnionTypeLiterals: Array = new Array(); + UnionTypeLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.UNION)); + UnionTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.unionedTypeArray.length)); + for (let type of this.unionedTypeArray) { + UnionTypeLiterals.push(new Literal(LiteralTag.INTEGER, type)); + } + UnionTypeBuf.addLiterals(...UnionTypeLiterals); + return UnionTypeBuf; + } +} + +export class ArrayType extends BaseType { + referedTypeIndex: number = PrimitiveType.ANY; + typeIndex: number = PrimitiveType.ANY; + shiftedTypeIndex: number = PrimitiveType.ANY; + constructor(typeNode: ts.Node) { + super(); + let elementNode = (typeNode).elementType; + this.referedTypeIndex = this.getOrCreateRecordForTypeNode(elementNode, elementNode); + this.setOrReadFromArrayRecord(); + } + + setOrReadFromArrayRecord() { + if (this.hasArrayTypeMapping(this.referedTypeIndex)) { + this.shiftedTypeIndex = this.getFromArrayTypeMap(this.referedTypeIndex)!; + } else { + this.typeIndex = this.getIndexFromTypeArrayBuffer(this); + this.shiftedTypeIndex = this.typeIndex + PrimitiveType._LENGTH; + this.setTypeArrayBuffer(this, this.typeIndex); + this.setArrayTypeMap(this.referedTypeIndex, this.shiftedTypeIndex); + } + } + + hasArrayTypeMapping(referedTypeIndex: number) { + return this.typeRecorder.hasArrayTypeMapping(referedTypeIndex); + } + + getFromArrayTypeMap(referedTypeIndex: number) { + return this.typeRecorder.getFromArrayTypeMap(referedTypeIndex); + } + + setArrayTypeMap(referedTypeIndex: number, shiftedTypeIndex: number) { + return this.typeRecorder.setArrayTypeMap(referedTypeIndex, shiftedTypeIndex); + } + + transfer2LiteralBuffer(): LiteralBuffer { + let arrayBuf = new LiteralBuffer(); + let arrayLiterals: Array = new Array(); + arrayLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.ARRAY)); + arrayLiterals.push(new Literal(LiteralTag.INTEGER, this.referedTypeIndex)); + arrayBuf.addLiterals(...arrayLiterals); + return arrayBuf; + } +} + +export class ObjectType extends BaseType { + private properties: Map = new Map(); + typeIndex: number = PrimitiveType.ANY; + shiftedTypeIndex: number = PrimitiveType.ANY; + + constructor(objNode: ts.TypeLiteralNode) { + super(); + this.typeIndex = this.getIndexFromTypeArrayBuffer(new PlaceHolderType()); + this.shiftedTypeIndex = this.typeIndex + PrimitiveType._LENGTH; + this.fillInMembers(objNode); + this.setTypeArrayBuffer(this, this.typeIndex); + } + + fillInMembers(objNode: ts.TypeLiteralNode) { + for (let member of objNode.members) { + let propertySig = member; + let name = member.name ? member.name.getText() : "#undefined"; + let typeIndex = this.getOrCreateRecordForTypeNode(propertySig.type, member.name); + this.properties.set(name, typeIndex); + } + } + + transfer2LiteralBuffer(): LiteralBuffer { + let objTypeBuf = new LiteralBuffer(); + let objLiterals: Array = new Array(); + objLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.OBJECT)); + objLiterals.push(new Literal(LiteralTag.INTEGER, this.properties.size)); + this.properties.forEach((typeIndex, name) => { + objLiterals.push(new Literal(LiteralTag.STRING, name)); + objLiterals.push(new Literal(LiteralTag.INTEGER, typeIndex)); + }); + objTypeBuf.addLiterals(...objLiterals); + return objTypeBuf; + } +} + +export class InterfaceType extends BaseType { + heritages: Array = new Array(); + // fileds Array: [typeIndex] [public -> 0, private -> 1, protected -> 2] [readonly -> 1] + fields: Map> = new Map>(); + methods: Array = new Array(); + typeIndex: number; + shiftedTypeIndex: number; + + constructor(interfaceNode: ts.InterfaceDeclaration) { + super(); + this.typeIndex = this.getIndexFromTypeArrayBuffer(new PlaceHolderType()); + this.shiftedTypeIndex = this.typeIndex + PrimitiveType._LENGTH; + // record type before its initialization, so its index can be recorded + // in case there's recursive reference of this type + this.addCurrentType(interfaceNode, this.shiftedTypeIndex); + this.fillInHeritages(interfaceNode); + this.fillInFieldsAndMethods(interfaceNode); + this.setTypeArrayBuffer(this, this.typeIndex); + } + + private fillInHeritages(node: ts.InterfaceDeclaration) { + if (node.heritageClauses) { + for (let heritage of node.heritageClauses) { + for (let heritageType of heritage.types) { + let heritageIdentifier = heritageType.expression; + let heritageTypeIndex = this.getOrCreateRecordForDeclNode(heritageIdentifier, heritageIdentifier); + this.heritages.push(heritageTypeIndex); + } + } + } + } + + private fillInFields(member: ts.PropertySignature) { + let fieldName = jshelpers.getTextOfIdentifierOrLiteral(member.name); + let fieldInfo = Array(PrimitiveType.ANY, AccessFlag.PUBLIC, ModifierReadonly.NONREADONLY); + if (member.modifiers) { + for (let modifier of member.modifiers) { + switch (modifier.kind) { + 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; + } + } + } + } + let typeNode = member.type; + let memberName = member.name; + fieldInfo[0] = this.getOrCreateRecordForTypeNode(typeNode, memberName); + this.fields.set(fieldName, fieldInfo); + } + + private fillInMethods(member: ts.MethodSignature) { + /** + * 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); + if (variableNode) { + this.setVariable2Type(variableNode, funcType.shiftedTypeIndex); + } + // Then, get the typeIndex and fill in the methods array + let typeIndex = this.tryGetTypeIndex(member); + this.methods.push(typeIndex!); + } + + private fillInFieldsAndMethods(node: ts.InterfaceDeclaration) { + if (node.members) { + for (let member of node.members) { + switch (member.kind) { + case ts.SyntaxKind.MethodSignature: { + this.fillInMethods(member); + break; + } + case ts.SyntaxKind.PropertySignature: { + this.fillInFields(member); + break; + } + } + } + } + } + + transfer2LiteralBuffer() { + let interfaceTypeBuf = new LiteralBuffer(); + let interfaceTypeLiterals: Array = new Array(); + // the first element is to determine the L2 type + interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.INTERFACE)); + + interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.heritages.length)); + this.heritages.forEach(heritage => { + interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, heritage)); + }); + + // record fields and methods + this.transferFields2Literal(interfaceTypeLiterals); + this.transferMethods2Literal(interfaceTypeLiterals); + + interfaceTypeBuf.addLiterals(...interfaceTypeLiterals); + return interfaceTypeBuf; + } + + private transferFields2Literal(interfaceTypeLiterals: Array) { + let transferredTarget: Map> = this.fields; + + interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, transferredTarget.size)); + transferredTarget.forEach((typeInfo, name) => { + interfaceTypeLiterals.push(new Literal(LiteralTag.STRING, name)); + interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, typeInfo[0])); // typeIndex + interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, typeInfo[1])); // accessFlag + interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, typeInfo[2])); // readonly + }); + } + + private transferMethods2Literal(interfaceTypeLiterals: Array) { + let transferredTarget: Array = this.methods; + + interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, transferredTarget.length)); + transferredTarget.forEach(method => { + interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, method)); + }); + } +} diff --git a/ts2panda/src/base/util.ts b/ts2panda/src/base/util.ts index c9a2177cadbb3a7a5f9ef364c1abc81d3f0cbc01..e0f66130a02ba1ba5062e2d9730cea09134ec7b9 100755 --- a/ts2panda/src/base/util.ts +++ b/ts2panda/src/base/util.ts @@ -14,7 +14,7 @@ */ import path = require("path"); -import { isContainConstruct } from "../statement/classStatement"; +import { extractCtorOfClass } from "../statement/classStatement"; import { LocalVariable, Variable } from "../variable"; import * as ts from "typescript"; import { @@ -274,7 +274,7 @@ export function getParamLengthOfFunc(node: ts.FunctionLikeDeclaration) { } export function getParameterLength4Ctor(node: ts.ClassLikeDeclaration) { - if (!isContainConstruct(node)) { + if (!extractCtorOfClass(node)) { return 0; } @@ -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..b84914671e9b7fed69b63a9dd2dbae6d397c9736 100644 --- a/ts2panda/src/cmdOptions.ts +++ b/ts2panda/src/cmdOptions.ts @@ -22,23 +22,29 @@ import path = require("path"); import { execute } from "./base/util"; const ts2pandaOptions = [ - { name: 'variant-bytecode', alias: 'r', type: Boolean, defaultValue: true, description: "emit 2nd bytecode to pandafile."}, - { name: 'modules', alias: 'm', type: Boolean, defaultValue: false, description: "compile as module."}, - { name: 'debug-log', alias: 'l', type: Boolean, defaultValue: false, description: "show info debug log."}, - { name: 'dump-assembly', alias: 'a', type: Boolean, defaultValue: false, description: "dump assembly to file."}, - { name: 'debug', alias: 'd', type: Boolean, defaultValue: false, description: "compile with debug info."}, - { name: 'show-statistics', alias: 's', type: String, lazyMultiple: true, defaultValue: "", description: "show compile statistics(ast, histogram, hoisting, all)."}, - { name: 'output', alias: 'o', type: String, defaultValue: "", description: "set output file."}, - { name: 'timeout', alias: 't', type: Number, defaultValue: 0, description: "js to abc timeout threshold(unit: seconds)."}, - { name: 'opt-log-level', type: String, defaultValue: "error", description: "specifie optimizer log level. Possible values: ['debug', 'info', 'error', 'fatal']"}, - { name: 'opt-level', type: Number, defaultValue: 1, description: "Optimization level. Possible values: [0, 1, 2]. Default: 0\n 0: no optimizations\n \ + { name: 'variant-bytecode', alias: 'r', type: Boolean, defaultValue: true, description: "emit 2nd bytecode to pandafile." }, + { name: 'modules', alias: 'm', type: Boolean, defaultValue: false, description: "compile as module." }, + { name: 'debug-log', alias: 'l', type: Boolean, defaultValue: false, description: "show info debug log." }, + { name: 'dump-assembly', alias: 'a', type: Boolean, defaultValue: false, description: "dump assembly to file." }, + { name: 'debug', alias: 'd', type: Boolean, defaultValue: false, description: "compile with debug info." }, + { name: 'show-statistics', alias: 's', type: String, lazyMultiple: true, defaultValue: "", description: "show compile statistics(ast, histogram, hoisting, all)." }, + { name: 'output', alias: 'o', type: String, defaultValue: "", description: "set output file." }, + { name: 'timeout', alias: 't', type: Number, defaultValue: 0, description: "js to abc timeout threshold(unit: seconds)." }, + { name: 'opt-log-level', type: String, defaultValue: "error", description: "specifie optimizer log level. Possible values: ['debug', 'info', 'error', 'fatal']" }, + { + name: 'opt-level', type: Number, defaultValue: 1, description: "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: other bytecode optimizations, unimplemented yet"}, - { name: 'help', alias: 'h', type: Boolean, description: "Show usage guide."}, - { name: 'bc-version', alias: 'v', type: Boolean, defaultValue: false, description: "Print ark bytecode version"}, - { name: 'bc-min-version', type: Boolean, defaultValue: false, description: "Print ark bytecode minimum supported version"} + { name: 'help', alias: 'h', type: Boolean, description: "Show usage guide." }, + { name: 'bc-version', alias: 'v', type: Boolean, defaultValue: false, description: "Print ark bytecode version" }, + { name: 'bc-min-version', type: Boolean, defaultValue: false, description: "Print ark bytecode minimum supported version" }, + { name: 'included-files', alias: 'i', type: String, lazyMultiple: true, defaultValue: [], description: "The list of dependent files." }, + { name: 'record-type', alias: 'p', type: Boolean, defaultValue: false, description: "Record type info. Default: true" }, + { name: 'dts-type-record', alias: 'q', type: Boolean, defaultValue: false, description: "Record type info for .d.ts files. Default: false" } ] + + export class CmdOptions { private static parsedResult: ts.ParsedCommandLine; private static options: commandLineArgs.CommandLineOptions; @@ -152,7 +158,7 @@ export class CmdOptions { return this.options["bc-version"]; } - static getVersion(isBcVersion : boolean = true) : void { + static getVersion(isBcVersion: boolean = true): void { let js2abc = path.join(path.resolve(__dirname, '../bin'), "js2abc"); let version_arg = isBcVersion ? "--bc-version" : "--bc-min-version" execute(`${js2abc}`, [version_arg]); @@ -165,6 +171,29 @@ export class CmdOptions { return this.options["bc-min-version"]; } + static getIncludedFiles(): string[] { + if (!this.options) { + return []; + } + + return this.options["included-files"]; + } + + static needRecordType(): boolean { + if (!this.options) { + return false; + } + + return !this.options["record-type"]; + } + + static needRecordDtsType(): boolean { + if (!this.options) { + return false; + } + return this.options["dts-type-record"]; + } + static parseUserCmd(args: string[]): ts.ParsedCommandLine | undefined { this.options = commandLineArgs(ts2pandaOptions, { partial: true }); if (this.options.help) { @@ -186,4 +215,5 @@ export class CmdOptions { this.parsedResult = ts.parseCommandLine(this.options._unknown!); return this.parsedResult; } + } diff --git a/ts2panda/src/compiler.ts b/ts2panda/src/compiler.ts index c9230ee088d2dcd083170848c66cb2eec6cf32b7..3a33940bd34a73b9d827a148c692b61c47f62067 100644 --- a/ts2panda/src/compiler.ts +++ b/ts2panda/src/compiler.ts @@ -84,7 +84,11 @@ import { import { checkValidUseSuperBeforeSuper, compileClassDeclaration, - compileConstructor + compileConstructor, + compileDefaultConstructor, + compileDefaultInitClassMembers, + compileReturnThis4Ctor, + extractCtorOfClass } from "./statement/classStatement"; import { compileForOfStatement } from "./statement/forOfStatement"; import { LabelTarget } from "./statement/labelTarget"; @@ -269,6 +273,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) { @@ -277,8 +285,7 @@ export class Compiler { }); if (body.parent && ts.isConstructorDeclaration(body.parent)) { - - compileConstructor(this, body.parent, unreachableFlag); + compileReturnThis4Ctor(this, body.parent, unreachableFlag); return; } @@ -393,6 +400,14 @@ export class Compiler { let pandaGen = this.pandaGen; this.compileFunctionParameterDeclaration(decl); + if (ts.isConstructorDeclaration(decl)) { + let classNode = decl.parent; + if (jshelpers.getClassExtendsHeritageElement(classNode) && !extractCtorOfClass(classNode)) { + compileDefaultConstructor(this, decl); + return; + } + } + if (decl.kind == ts.SyntaxKind.FunctionExpression) { if (decl.name) { let funcName = jshelpers.getTextOfIdentifierOrLiteral(decl.name); @@ -481,6 +496,8 @@ export class Compiler { this.compileExportAssignment(stmt); break; case ts.SyntaxKind.ExportDeclaration: + case ts.SyntaxKind.NotEmittedStatement: + case ts.SyntaxKind.InterfaceDeclaration: break; default: throw new Error("Statement " + this.getNodeName(stmt) + " is unimplemented"); @@ -611,7 +628,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(); @@ -673,7 +690,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); @@ -859,6 +876,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"); } diff --git a/ts2panda/src/compilerDriver.ts b/ts2panda/src/compilerDriver.ts index 46a1533d016b162d758892d949a889b987677d64..a39363e50b5b49b2803866cf1c489f54f695e927 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,7 @@ export class CompilerDriver { }); } - let recorder = this.compilePrologue(node); + let recorder = this.compilePrologue(node, true); // initiate ts2abc if (!CmdOptions.isAssemblyMode()) { @@ -233,7 +240,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]; @@ -261,7 +268,16 @@ export class CompilerDriver { this.compilationUnits.push(pandaGen); } - private compilePrologue(node: ts.SourceFile) { + private isTypeScriptSourceFile(node: ts.SourceFile) { + let fileName = node.fileName; + if (fileName && fileName.endsWith(".ts")) { + return true; + } else { + return false; + } + } + + private compilePrologue(node: ts.SourceFile, recordType: boolean) { let topLevelScope: GlobalScope | ModuleScope; if (CmdOptions.isModules()) { topLevelScope = new ModuleScope(node); @@ -269,12 +285,15 @@ export class CompilerDriver { topLevelScope = new GlobalScope(node); } - let recorder = new Recorder(node, topLevelScope, this); + let isTsFile = this.isTypeScriptSourceFile(node); + let enableTypeRecord = recordType && CmdOptions.needRecordType() && isTsFile; + if (enableTypeRecord) { + TypeRecorder.createInstance(); + } + let recorder = new Recorder(node, topLevelScope, this, enableTypeRecord, isTsFile); recorder.record(); - - checkDuplicateDeclaration(recorder); - checkExportEntries(recorder); - addVariableToScope(recorder); + + addVariableToScope(recorder, enableTypeRecord); let postOrderVariableScopes = this.postOrderAnalysis(topLevelScope); for (let variableScope of postOrderVariableScopes) { @@ -320,7 +339,7 @@ export class CompilerDriver { name = "func_main_0"; } else if (ts.isConstructorDeclaration(node)) { let classNode = node.parent; - name = this.getInternalNameForCtor(classNode); + name = this.getInternalNameForCtor(classNode, node); } else { let funcNode = node; name = (recorder.getScopeOfNode(funcNode)).getFuncName(); @@ -349,11 +368,11 @@ export class CompilerDriver { return name; } - getInternalNameForCtor(node: ts.ClassLikeDeclaration) { + getInternalNameForCtor(node: ts.ClassLikeDeclaration, ctor: ts.ConstructorDeclaration) { let name = getClassNameForConstructor(node); - name = `#${this.getFuncId(node)}#${name}` + name = `#${this.getFuncId(ctor)}#${name}` if (name.lastIndexOf(".") != -1) { - name = `#${this.getFuncId(node)}#` + name = `#${this.getFuncId(ctor)}#` } return name; } 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..dcfb2847bb74cce8c1dcb763684f41ed5635262c 100644 --- a/ts2panda/src/index.ts +++ b/ts2panda/src/index.ts @@ -22,11 +22,60 @@ import { Pass } from "./pass"; import { CacheExpander } from "./pass/cacheExpander"; import { ICPass } from "./pass/ICPass"; import { RegAlloc } from "./regAllocator"; -import { setGlobalStrict } from "./strictMode"; +import { setGlobalStrict, setGlobalDeclare, isGlobalDeclare } 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(); + typeChecker.setTypeChecker(program.getTypeChecker()); + + if (CmdOptions.needRecordDtsType()) { + for (let sourceFile of program.getSourceFiles()) { + if (sourceFile.isDeclarationFile && !program.isSourceFileDefaultLibrary(sourceFile)) { + setGlobalDeclare(checkIsGlobalDeclaration(sourceFile)); + generateDTs(sourceFile, options); + } + } + } + + function checkIsGlobalDeclaration(sourceFile: ts.SourceFile) { + for (let statement of sourceFile.statements) { + if (statement.modifiers) { + for (let modifier of statement.modifiers) { + if (modifier.kind === ts.SyntaxKind.ExportKeyword) { + return false; + } + } + } else if (statement.kind === ts.SyntaxKind.ExportAssignment) { + return false; + } else if (statement.kind === ts.SyntaxKind.ImportKeyword || statement.kind === ts.SyntaxKind.ImportDeclaration) { + return false; + } + } + return true; + } + + function generateDTs(node: ts.SourceFile, options: ts.CompilerOptions) { + let outputBinName = getOutputBinName(node); + let compilerDriver = new CompilerDriver(outputBinName); + setGlobalStrict(jshelpers.isEffectiveStrictModeSourceFile(node, options)); + if (CmdOptions.isVariantBytecode()) { + LOGD("variant bytecode dump"); + let passes: Pass[] = [ + new CacheExpander(), + new ICPass(), + new RegAlloc() + ]; + compilerDriver.setCustomPasses(passes); + } + compilerDriver.compile(node); + compilerDriver.showStatistics(); + } + let emitResult = program.emit( undefined, undefined, @@ -36,11 +85,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 +131,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 +154,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 @@ -98,7 +174,8 @@ function run(args: string[], options?: ts.CompilerOptions): void { } } try { - main(parsed.fileNames, parsed.options); + let files: string[] = parsed.fileNames; + main(files.concat(CmdOptions.getIncludedFiles()), parsed.options); } catch (err) { if (err instanceof diag.DiagnosticError) { let diagnostic = diag.getDiagnostic(err.code); 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/modules.ts b/ts2panda/src/modules.ts index a4a8aff21ebf4aed0b154446cfead353f0e91cef..8e1ea468a7997abc8d56396a510f4141e71c6a66 100644 --- a/ts2panda/src/modules.ts +++ b/ts2panda/src/modules.ts @@ -25,6 +25,7 @@ export class ModuleStmt { private moduleRequest: string; private namespace: string = ""; private bingdingNameMap: Map = new Map(); + private bingdingNodeMap: Map = new Map(); private isCopy: boolean = true; constructor(node: ts.Node, moduleRequest: string = "") { @@ -51,6 +52,14 @@ export class ModuleStmt { return this.bingdingNameMap; } + addNodeMap(name: ts.Node, propertyName: ts.Node) { + this.bingdingNodeMap.set(name, propertyName); + } + + getBindingNodeMap() { + return this.bingdingNodeMap; + } + setNameSpace(namespace: string) { this.namespace = namespace; } diff --git a/ts2panda/src/pandagen.ts b/ts2panda/src/pandagen.ts index 1b56905a963a65d309e517acdcae69fce351f6db..d7b0bfceb7e66f4b1721e37373cd78d29550fcc6 100644 --- a/ts2panda/src/pandagen.ts +++ b/ts2panda/src/pandagen.ts @@ -183,6 +183,8 @@ import { CatchTable } from "./statement/tryStatement"; import { Variable } from "./variable"; +import { BaseType } from "./base/typeSystem"; +import { TypeRecorder } from "./typeRecorder"; export class PandaGen { private debugTag: string = "PandaGen"; @@ -203,7 +205,7 @@ export class PandaGen { private icSize: number = 0; private callType: number = 0; - private static literalArrayBuffer: Array = []; + private static literalArrayBuffer: Array = new Array(); constructor(internalName: string, parametersCount: number, scope: Scope | undefined = undefined) { this.internalName = internalName; @@ -219,6 +221,22 @@ export class PandaGen { public getCallType(): number { return this.callType; } + + static getExportedTypes() { + if (TypeRecorder.getInstance()) { + return TypeRecorder.getInstance().getExportedType(); + } else { + return new Map(); + } + } + + static getDeclaredTypes() { + if (TypeRecorder.getInstance()) { + return TypeRecorder.getInstance().getDeclaredType(); + } else { + return new Map(); + } + } public getSourceCodeDebugInfo() { return this.sourceCodeDebugInfo; @@ -266,6 +284,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 c523f7fa8707ad2171b26d4a696213eafb198090..565615a2f5f02e75553723fec2af9ccf9d17fbee 100644 --- a/ts2panda/src/pandasm.ts +++ b/ts2panda/src/pandasm.ts @@ -73,6 +73,9 @@ export class Function { public sourceFile: string; public sourceCode: string | undefined; public callType: number; + public typeInfo: Array; + public exportedSymbol2Types: Array | undefined; + public declaredSymbol2Types: Array | undefined; constructor( name: string, @@ -83,7 +86,10 @@ export class Function { variables: Array | undefined = undefined, sourceFile: string = "", sourceCode: string | undefined = undefined, - callType: number = 0 + callType: number = 0, + typeInfo: Array, + exportedSymbol2Types: Array | undefined = undefined, + declaredSymbol2Types: Array | undefined = undefined ) { this.name = name; this.signature = signature; @@ -96,6 +102,9 @@ export class Function { this.sourceFile = sourceFile; this.sourceCode = sourceCode; this.callType = callType; + this.typeInfo = typeInfo; + this.exportedSymbol2Types = exportedSymbol2Types; + this.declaredSymbol2Types = declaredSymbol2Types; } } @@ -169,6 +178,36 @@ export class CatchTable { } } +export class TypeOfVreg { + private vregNum: number; + private typeIndex: number; + + constructor(vregNum: number, typeIndex: number) { + this.vregNum = vregNum; + this.typeIndex = typeIndex; + } +} + +export class ExportedSymbol2Type { + private symbol: string; + private type: number; + + constructor(symbol: string, type: number) { + this.symbol = symbol; + this.type = type; + } +} + +export class DeclaredSymbol2Type { + private symbol: string; + private type: number; + + constructor(symbol: string, type: number) { + this.symbol = symbol; + this.type = type; + } +} + export interface Emmiter { generate_program: (filename: string, program: Program) => string; } diff --git a/ts2panda/src/recorder.ts b/ts2panda/src/recorder.ts index 387ddf83d12d1fd0dbc5304ec45c1e65db0a581b..e5b6b119a4a913ce52d54b53c26176bcf42c22c0 100644 --- a/ts2panda/src/recorder.ts +++ b/ts2panda/src/recorder.ts @@ -42,47 +42,80 @@ import { } from "./scope"; import { AddCtor2Class, - isContainConstruct, - getClassNameForConstructor + getClassNameForConstructor, + extractCtorOfClass } from "./statement/classStatement"; import { checkSyntaxError } from "./syntaxChecker"; import { isGlobalIdentifier } from "./syntaxCheckHelper"; +import { TypeChecker } from "./typeChecker"; import { VarDeclarationKind } from "./variable"; +import { TypeRecorder } from "./typeRecorder"; +import { PandaGen } from "./pandagen"; 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(); private funcNameMap: Map; - private ClassGroupOfNoCtor: Array = new Array(); + private class2Ctor: Map = new Map(); private importStmts: Array = []; private exportStmts: Array = []; private defaultUsed: boolean = false; + private isTsFile: boolean; - constructor(node: ts.Node, scope: Scope, compilerDriver: CompilerDriver) { + constructor(node: ts.Node, scope: Scope, compilerDriver: CompilerDriver, recordType: boolean, isTsFile: boolean) { this.node = node; this.scope = scope; this.compilerDriver = compilerDriver; + this.recordType = recordType; this.funcNameMap = new Map(); this.funcNameMap.set("main", 1); + this.isTsFile = isTsFile; } record() { + this.setParent(this.node); this.setScopeMap(this.node, this.scope); this.recordInfo(this.node, this.scope); + if (this.recordType) { + TypeRecorder.getInstance().setTypeSummary(); + TypeRecorder.getInstance().getLog(); + } else { + PandaGen.clearLiteralArrayBuffer(); + } return this.node; } - getClassGroupOfNoCtor() { - return this.ClassGroupOfNoCtor; + getCtorOfClass(node: ts.ClassLikeDeclaration) { + return this.class2Ctor.get(node); + } + + setCtorOfClass(node: ts.ClassLikeDeclaration, ctor: ts.ConstructorDeclaration) { + if (!this.class2Ctor.has(node)) { + this.class2Ctor.set(node, ctor); + } + } + + private setParent(node: ts.Node) { + node.forEachChild(childNode => { + if (!this.isTsFile || 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: @@ -90,16 +123,17 @@ export class Recorder { case ts.SyntaxKind.GetAccessor: case ts.SyntaxKind.SetAccessor: case ts.SyntaxKind.ArrowFunction: { - this.compilerDriver.getFuncId(childNode); let functionScope = this.buildVariableScope(scope, childNode); this.recordFuncInfo(childNode); this.recordInfo(childNode, functionScope); break; } case ts.SyntaxKind.FunctionDeclaration: { - this.compilerDriver.getFuncId(childNode); let functionScope = this.buildVariableScope(scope, childNode); this.recordFuncDecl(childNode, scope); + if (this.recordType) { + TypeChecker.getInstance().formatNodeType(childNode); + } this.recordInfo(childNode, functionScope); break; } @@ -128,6 +162,15 @@ export class Recorder { case ts.SyntaxKind.ClassDeclaration: case ts.SyntaxKind.ClassExpression: { this.recordClassInfo(childNode, scope); + if (this.recordType) { + TypeChecker.getInstance().formatNodeType(childNode); + } + break; + } + case ts.SyntaxKind.InterfaceDeclaration: { + if (this.recordType) { + TypeChecker.getInstance().formatNodeType(childNode); + } break; } case ts.SyntaxKind.Identifier: { @@ -141,7 +184,10 @@ export class Recorder { if (!(scope instanceof ModuleScope)) { throw new Error("SyntaxError: import statement cannot in other scope except ModuleScope"); } - this.recordImportInfo(childNode, scope); + let importStmt = this.recordImportInfo(childNode, scope); + if (this.recordType) { + TypeChecker.getInstance().formatNodeType(childNode, importStmt); + } break; } case ts.SyntaxKind.ExportDeclaration: { @@ -151,7 +197,10 @@ export class Recorder { if (!(scope instanceof ModuleScope)) { throw new Error("SyntaxError: export statement cannot in other scope except ModuleScope"); } - this.recordExportInfo(childNode); + let exportStmt = this.recordExportInfo(childNode); + if (this.recordType) { + TypeChecker.getInstance().formatNodeType(childNode, exportStmt); + } break; } case ts.SyntaxKind.ExportAssignment: { @@ -160,6 +209,16 @@ export class Recorder { } this.defaultUsed = true; this.recordInfo(childNode, scope); + if (this.recordType) { + TypeChecker.getInstance().formatNodeType(childNode); + } + break; + } + case ts.SyntaxKind.VariableStatement: { + if (this.recordType) { + TypeChecker.getInstance().formatNodeType(childNode); + } + this.recordInfo(childNode, scope); break; } default: @@ -171,12 +230,15 @@ export class Recorder { private recordClassInfo(childNode: ts.ClassLikeDeclaration, scope: Scope) { let localScope = new LocalScope(scope); this.setScopeMap(childNode, localScope); - if (!isContainConstruct(childNode)) { + let ctor = extractCtorOfClass(childNode); + if (!ctor) { AddCtor2Class(this, childNode, localScope); + } else { + this.setCtorOfClass(childNode, ctor); } if (childNode.name) { let name = jshelpers.getTextOfIdentifierOrLiteral(childNode.name); - let calssDecl = new ClassDecl(name, childNode, this.compilerDriver.getFuncId(childNode)); + let calssDecl = new ClassDecl(name, childNode); scope.setDecls(calssDecl); } this.recordInfo(childNode, localScope); @@ -196,6 +258,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 @@ -216,7 +279,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; @@ -235,7 +298,7 @@ export class Recorder { if (name == "arguments") { let varialbeScope = scope.getNearestVariableScope(); - varialbeScope ?.setUseArgs(true); + varialbeScope?.setUseArgs(true); } } @@ -245,7 +308,7 @@ export class Recorder { case VarDeclarationKind.VAR: break; case VarDeclarationKind.LET: - if (parent.parent.kind == ts.SyntaxKind.CatchClause) { + if (parent.parent.kind == ts.SyntaxKind.CatchClause) { decl = new CatchParameter(name, node); } else { decl = new LetDecl(name, node); @@ -278,13 +341,17 @@ export class Recorder { } } - private recordImportInfo(node: ts.ImportDeclaration, scope: ModuleScope) { + private recordImportInfo(node: ts.ImportDeclaration, scope: ModuleScope): ModuleStmt { if (!ts.isStringLiteral(node.moduleSpecifier)) { throw new Error("moduleSpecifier must be a stringLiteral"); } - let moduleRequest = jshelpers.getTextOfIdentifierOrLiteral(node.moduleSpecifier); - let importStmt = new ModuleStmt(node, moduleRequest); - + let importStmt: ModuleStmt; + if (node.moduleSpecifier) { + let moduleRequest = jshelpers.getTextOfIdentifierOrLiteral(node.moduleSpecifier); + importStmt = new ModuleStmt(node, moduleRequest); + } else { + importStmt = new ModuleStmt(node); + } if (node.importClause) { let importClause: ts.ImportClause = node.importClause; @@ -293,6 +360,7 @@ export class Recorder { let name = jshelpers.getTextOfIdentifierOrLiteral(importClause.name); scope.setDecls(new ConstDecl(name, importClause.name)); importStmt.addLocalName(name, "default"); + importStmt.addNodeMap(importClause.name, importClause.name); } // import { ... } from "a.js" @@ -314,28 +382,31 @@ export class Recorder { let exoticName: string = element.propertyName ? jshelpers.getTextOfIdentifierOrLiteral(element.propertyName) : name; scope.setDecls(new ConstDecl(name, element)); importStmt.addLocalName(name, exoticName); + importStmt.addNodeMap(element.name, element.propertyName ? element.propertyName : element.name); }); } } } this.importStmts.push(importStmt); + return importStmt; } - private recordExportInfo(node: ts.ExportDeclaration) { + private recordExportInfo(node: ts.ExportDeclaration): ModuleStmt { + let origNode = ts.getOriginalNode(node); let exportStmt: ModuleStmt; - if (node.moduleSpecifier) { - if (!ts.isStringLiteral(node.moduleSpecifier)) { + if (origNode.moduleSpecifier) { + if (!ts.isStringLiteral(origNode.moduleSpecifier)) { throw new Error("moduleSpecifier must be a stringLiteral"); } - exportStmt = new ModuleStmt(node, jshelpers.getTextOfIdentifierOrLiteral(node.moduleSpecifier)); + exportStmt = new ModuleStmt(origNode, jshelpers.getTextOfIdentifierOrLiteral(origNode.moduleSpecifier)); } else { - exportStmt = new ModuleStmt(node); + exportStmt = new ModuleStmt(origNode); } - if (node.exportClause) { + if (origNode.exportClause) { exportStmt.setCopyFlag(false); - let namedBindings: ts.NamedExportBindings = node.exportClause; + let namedBindings: ts.NamedExportBindings = origNode.exportClause; if (ts.isNamespaceExport(namedBindings)) { exportStmt.setNameSpace(jshelpers.getTextOfIdentifierOrLiteral((namedBindings).name)); } @@ -345,18 +416,19 @@ export class Recorder { let name: string = jshelpers.getTextOfIdentifierOrLiteral(element.name); if (name == 'default') { if (this.defaultUsed) { - throw new DiagnosticError(node, DiagnosticCode.Duplicate_identifier_0, jshelpers.getSourceFileOfNode(node), [name]); + throw new DiagnosticError(origNode, DiagnosticCode.Duplicate_identifier_0, jshelpers.getSourceFileOfNode(origNode), [name]); } else { this.defaultUsed = true; } } let exoticName: string = element.propertyName ? jshelpers.getTextOfIdentifierOrLiteral(element.propertyName) : name; exportStmt.addLocalName(name, exoticName); + exportStmt.addNodeMap(element.name, element.propertyName ? element.propertyName : element.name); }); } } - this.exportStmts.push(exportStmt); + return exportStmt; } private recordFuncDecl(node: ts.FunctionDeclaration, scope: Scope) { @@ -368,7 +440,7 @@ export class Recorder { return; } let funcName = jshelpers.getTextOfIdentifierOrLiteral(funcId); - let funcDecl = new FuncDecl(funcName, node, this.compilerDriver.getFuncId(node)); + let funcDecl = new FuncDecl(funcName, node); scope.setDecls(funcDecl); let hoistScope = scope; if (scope instanceof GlobalScope || scope instanceof ModuleScope) { @@ -492,8 +564,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 44cde853d8c9229b20f1454825f981c25bed048b..79a59c04865401021aba827a91071010202e6d23 100644 --- a/ts2panda/src/scope.ts +++ b/ts2panda/src/scope.ts @@ -55,18 +55,14 @@ export class ConstDecl extends Decl { } export class FuncDecl extends Decl { - readonly index: number; - constructor(funcName: string, node: ts.Node, index: number) { + constructor(funcName: string, node: ts.Node) { super(funcName, node); - this.index = index; } } export class ClassDecl extends Decl { - readonly index: number; - constructor(className: string, node: ts.Node, index: number) { + constructor(className: string, node: ts.Node) { super(className, node); - this.index = index; } } diff --git a/ts2panda/src/statement/classStatement.ts b/ts2panda/src/statement/classStatement.ts index d346350f80ccf7e56dc9ad3caa2ea1da3f0c5cea..982d1ab35dd80d43d486a00a5184c8284fd87401 100644 --- a/ts2panda/src/statement/classStatement.ts +++ b/ts2panda/src/statement/classStatement.ts @@ -60,7 +60,7 @@ export function compileClassDeclaration(compiler: Compiler, stmt: ts.ClassLikeDe let classBuffer = new LiteralBuffer(); let propertyIndex = 0; let staticItemsNum = 0; - let hasConstructor = isContainConstruct(stmt); + let hasConstructor = extractCtorOfClass(stmt) == undefined ? false : true; for (; propertyIndex < properties.length; propertyIndex++) { let prop = properties[propertyIndex]; @@ -131,44 +131,25 @@ 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); - ctorNode.parent = classNode; - ctorNode.pos = classNode.pos; - ctorNode.end = classNode.pos; - ctorNode.body!.parent = ctorNode; + let body = ts.factory.createBlock([]); + body = jshelpers.setParent(body, ctorNode)!; + body = ts.setTextRange(body, classNode)!; + + 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); let funcScope = recorder.buildVariableScope(scope, ctorNode); funcScope.setParent(parentScope); @@ -180,6 +161,19 @@ export function AddCtor2Class(recorder: Recorder, classNode: ts.ClassLikeDeclara recorder.recordFuncName(ctorNode); recorder.recordFunctionParameters(ctorNode); + + recorder.setCtorOfClass(classNode, 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) { @@ -234,20 +228,87 @@ function compileUnCompiledVariable(compiler: Compiler, prop: Property, classReg: function createClassLiteralBuf(compiler: Compiler, classBuffer: LiteralBuffer, stmt: ts.ClassLikeDeclaration, vregs: VReg[]) { - let pandaGen = compiler.getPandaGen(); let classLiteralBuf = PandaGen.getLiteralArrayBuffer(); - let buffIdx = classLiteralBuf.length; - let internalName = compiler.getCompilerDriver().getInternalNameForCtor(stmt); classLiteralBuf.push(classBuffer); + let ctorNode = compiler.getRecorder().getCtorOfClass(stmt); + let internalName = compiler.getCompilerDriver().getInternalNameForCtor(stmt, ctorNode); + + let pandaGen = compiler.getPandaGen(); let parameterLength = getParameterLength4Ctor(stmt); + let buffIdx = classLiteralBuf.length - 1; pandaGen.defineClassWithBuffer(stmt, internalName, buffIdx, parameterLength, vregs[0]); 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]; @@ -330,19 +391,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, getVregisterCache(pandaGen, CacheList.FUNC)); } 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++; } @@ -355,16 +423,16 @@ function loadCtorObj(node: ts.CallExpression, compiler: Compiler) { } -export function isContainConstruct(stmt: ts.ClassLikeDeclaration) { +export function extractCtorOfClass(stmt: ts.ClassLikeDeclaration) { let members = stmt.members; for (let index = 0; index < members.length; index++) { let member = members[index]; if (ts.isConstructorDeclaration(member)) { - return true + return member; } } - return false; + return undefined; } export function defineClassMember( @@ -429,7 +497,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); @@ -651,7 +719,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/strictMode.ts b/ts2panda/src/strictMode.ts index 80e27a1dc709f240edfe6204f9e17a3fcac373a0..901098efb5c65b0734d38e87591327c3195dbd9b 100644 --- a/ts2panda/src/strictMode.ts +++ b/ts2panda/src/strictMode.ts @@ -17,6 +17,7 @@ import * as ts from "typescript"; import jshelpers from "./jshelpers"; let globalStrict = true; +let globalDeclare = false; export function checkStrictModeStatementList(node: ts.Node): boolean { let statements; @@ -90,3 +91,11 @@ export function isStrictMode(node: ts.Node): boolean { return checkStrictMode(node); } + +export function setGlobalDeclare(flag: boolean) { + globalDeclare = flag; +} + +export function isGlobalDeclare() { + return globalDeclare; +} \ No newline at end of file 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 e3b60b281e5f4d09892ef2c70a3b5ad98a1893fa..2d5908f6a5510a0b61d4d3a220b12b23412da4c3 100644 --- a/ts2panda/src/ts2panda.ts +++ b/ts2panda/src/ts2panda.ts @@ -24,9 +24,21 @@ import { } from "./irnodes"; import { LOGD } from "./log"; import { PandaGen } from "./pandagen"; -import { CatchTable, Function, Ins, Signature } from "./pandasm"; +import { + CatchTable, + DeclaredSymbol2Type, + ExportedSymbol2Type, + Function, + Ins, + Signature +} from "./pandasm"; import { generateCatchTables } from "./statement/tryStatement"; -import { escapeUnicode, isRangeInst, getRangeStartVregPos } from "./base/util"; +import { + escapeUnicode, + isRangeInst, + getRangeStartVregPos +} from "./base/util"; +import { TypeOfVreg } from "./pandasm"; const dollarSign: RegExp = /\$/g; @@ -35,7 +47,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(); @@ -119,7 +132,7 @@ export class Ts2Panda { Ts2Panda.jsonString += escapeUnicode(JSON.stringify(strings_arr, null, 2)); } - strings_arr.forEach(function(str){ + strings_arr.forEach(function(str) { let strObject = { "type": JsonType.string, "string": str @@ -133,11 +146,16 @@ 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)); } - literalArrays.forEach(function(literalArray){ + literalArrays.forEach(function(literalArray) { let literalArrayObject = { "type": JsonType.literal_arr, "literalArray": literalArray @@ -148,7 +166,7 @@ export class Ts2Panda { }); } - static dumpCmdOptions(ts2abc: any): void { + static dumpCmdOptions(ts2abc: any): void { let options = { "type": JsonType.options, "module_mode": CmdOptions.isModules(), @@ -171,6 +189,33 @@ export class Ts2Panda { let funcInsnsAndRegsNum = Ts2Panda.getFuncInsnsAndRegsNum(pg); let sourceFile = pg.getSourceFileDebugInfo(); let callType = pg.getCallType(); + let typeRecord = pg.getLocals(); + let typeInfo = new Array(); + typeRecord.forEach((vreg) => { + let typeOfVreg = new TypeOfVreg(vreg.num, vreg.getTypeIndex()); + typeInfo.push(typeOfVreg); + // console.log("---------------------------------------"); + // console.log("\\\\\\\\\\\\ vreg name \\\\\\\\\\", vreg.getVariableName()); + // console.log("\\\\\\\\\\\\ vreg type \\\\\\\\\\", vreg.getTypeIndex()); + }); + + let exportedTypes = PandaGen.getExportedTypes(); + let exportedSymbol2Types = exportedTypes.size == 0 ? undefined : new Array(); + if (funcName == "func_main_0") { + exportedTypes.forEach((type: number, symbol: string) => { + let exportedSymbol2Type = new ExportedSymbol2Type(symbol, type); + exportedSymbol2Types!.push(exportedSymbol2Type); + }) + } + + let declareddTypes = PandaGen.getDeclaredTypes(); + let declaredSymbol2Types = declareddTypes.size == 0 ? undefined : new Array(); + if (funcName == "func_main_0") { + declareddTypes.forEach((type: number, symbol: string) => { + let declaredSymbol2Type = new DeclaredSymbol2Type(symbol, type); + declaredSymbol2Types!.push(declaredSymbol2Type); + }) + } let variables, sourceCode; if (CmdOptions.isDebugMode()) { @@ -190,7 +235,10 @@ export class Ts2Panda { variables, sourceFile, sourceCode, - callType + callType, + typeInfo, + exportedSymbol2Types, + declaredSymbol2Types ); let catchTables = generateCatchTables(pg.getCatchMap()); catchTables.forEach((catchTable) => { diff --git a/ts2panda/src/typeChecker.ts b/ts2panda/src/typeChecker.ts new file mode 100755 index 0000000000000000000000000000000000000000..a405d1f179e4514abf51c3d7b9449b6990938100 --- /dev/null +++ b/ts2panda/src/typeChecker.ts @@ -0,0 +1,333 @@ +import ts, { forEachChild } from "typescript"; +import { + ClassType, + ClassInstType, + ExternalType, + UnionType, + ArrayType, + FunctionType, + InterfaceType, + ObjectType +} from "./base/typeSystem"; +import { ModuleStmt } from "./modules"; +import { TypeRecorder } from "./typeRecorder"; +import * as jshelpers from "./jshelpers"; +import { LOGD } from "./log"; +import { PrimitiveType } from "./base/typeSystem"; +import { isGlobalDeclare } from "./strictMode"; +import { syncBuiltinESMExports } from "module"; + +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 getTypeAtLocation(node: ts.Node) { + if (!node) { + return undefined; + } + try { + return this.compiledTypeChecker.getTypeAtLocation(node); + } catch { + LOGD("Get getTypeAtLocation filed for : " + node.getFullText()); + return undefined; + } + } + + public getTypeDeclForIdentifier(node: ts.Node) { + if (!node) { + return undefined; + } + let symbol; + try { + symbol = this.compiledTypeChecker.getSymbolAtLocation(node); + } catch { + LOGD("Get getSymbolAtLocation filed for : " + node.getFullText()); + return undefined; + } + if (symbol && symbol.declarations) { + return symbol.declarations[0]; + } + return undefined; + } + + public hasExportKeyword(node: ts.Node): boolean { + if (node.modifiers) { + for (let modifier of node.modifiers) { + if (modifier.kind === ts.SyntaxKind.ExportKeyword) { + return true; + } + } + } + return false; + } + + public hasDeclareKeyword(node: ts.Node): boolean { + if (node.modifiers) { + for (let modifier of node.modifiers) { + if (modifier.kind === ts.SyntaxKind.DeclareKeyword) { + return true; + } + } + } + return false; + } + + public getDeclNodeForInitializer(initializer: ts.Node) { + switch (initializer.kind) { + case ts.SyntaxKind.Identifier: + return this.getTypeDeclForIdentifier(initializer); + case ts.SyntaxKind.NewExpression: + let initializerExpression = initializer; + let expression = initializerExpression.expression; + if (expression.kind == ts.SyntaxKind.ClassExpression) { + return expression; + } + return this.getTypeDeclForIdentifier(expression); + case ts.SyntaxKind.ClassExpression: + return initializer; + case ts.SyntaxKind.PropertyAccessExpression: + return initializer; + default: + return undefined; + } + } + + public getTypeForClassDeclOrExp(typeDeclNode: ts.Node, getTypeForInstace: boolean): number { + let classTypeIndex = TypeRecorder.getInstance().tryGetTypeIndex(typeDeclNode); + if (classTypeIndex == PrimitiveType.ANY) { + let classType = new ClassType(typeDeclNode); + classTypeIndex = classType.shiftedTypeIndex; + } + if (getTypeForInstace) { + // class type was already created, need to get the classInstance + if (!TypeRecorder.getInstance().hasClass2InstanceMap(classTypeIndex)) { + new ClassInstType(classTypeIndex); + } + classTypeIndex = TypeRecorder.getInstance().getClass2InstanceMap(classTypeIndex)!; + } + return classTypeIndex; + } + + public getTypeForPropertyAccessExpression(typeDeclNode: ts.Node) { + let propertyAccessExpression = typeDeclNode; + let localName = jshelpers.getTextOfIdentifierOrLiteral(propertyAccessExpression.expression); + let externalName = jshelpers.getTextOfIdentifierOrLiteral(propertyAccessExpression.name); + if (TypeRecorder.getInstance().inNampespaceMap(localName)) { + let redirectPath = TypeRecorder.getInstance().getPathForNamespace(localName)!; + let externalType = new ExternalType(externalName, redirectPath); + let ImportTypeIndex = externalType.shiftedTypeIndex; + return ImportTypeIndex; + } + return PrimitiveType.ANY; + } + + public getInterfaceDeclaration(typeDeclNode: ts.Node) { + let interfaceTypeIndex = TypeRecorder.getInstance().tryGetTypeIndex(typeDeclNode); + if (interfaceTypeIndex == PrimitiveType.ANY) { + let interefaceType = new InterfaceType(typeDeclNode); + interfaceTypeIndex = interefaceType.shiftedTypeIndex; + } + return interfaceTypeIndex; + } + + public getTypeFromDecl(typeDeclNode: ts.Node, getTypeForInstace: boolean): number { + if (!typeDeclNode) { + return PrimitiveType.ANY; + } + switch (typeDeclNode.kind) { + // Type found to be defined a classDeclaration or classExpression + case ts.SyntaxKind.ClassDeclaration: + case ts.SyntaxKind.ClassExpression: + return this.getTypeForClassDeclOrExp(typeDeclNode, getTypeForInstace); + case ts.SyntaxKind.ImportSpecifier: + case ts.SyntaxKind.ImportClause: + let ImportTypeIndex = TypeRecorder.getInstance().tryGetTypeIndex(typeDeclNode); + if (ImportTypeIndex != PrimitiveType.ANY) { + return ImportTypeIndex; + } + return PrimitiveType.ANY; + case ts.SyntaxKind.PropertyAccessExpression: + return this.getTypeForPropertyAccessExpression(typeDeclNode); + case ts.SyntaxKind.InterfaceDeclaration: + return this.getInterfaceDeclaration(typeDeclNode); + } + return PrimitiveType.ANY; + } + + public getTypeFromAnotation(typeNode: ts.TypeNode | undefined) { + if (!typeNode) { + return PrimitiveType.ANY; + } + switch (typeNode.kind) { + case ts.SyntaxKind.StringKeyword: + case ts.SyntaxKind.NumberKeyword: + case ts.SyntaxKind.BooleanKeyword: + case ts.SyntaxKind.SymbolKeyword: + case ts.SyntaxKind.UndefinedKeyword: + case ts.SyntaxKind.BigIntKeyword: + case ts.SyntaxKind.LiteralType: + let typeName = typeNode.getText().toUpperCase(); + let typeIndex = PrimitiveType.ANY; + if (typeName && typeName in PrimitiveType) { + typeIndex = PrimitiveType[typeName as keyof typeof PrimitiveType]; + } + return typeIndex; + case ts.SyntaxKind.UnionType: + let unionType = new UnionType(typeNode); + return unionType.shiftedTypeIndex; + case ts.SyntaxKind.ArrayType: + let arrayType = new ArrayType(typeNode); + return arrayType.shiftedTypeIndex; + case ts.SyntaxKind.ParenthesizedType: + let subType = (typeNode).type + if (subType.kind == ts.SyntaxKind.UnionType) { + let unionType = new UnionType(subType); + return unionType.shiftedTypeIndex; + } + return PrimitiveType.ANY; + case ts.SyntaxKind.TypeLiteral: + let objectType = new ObjectType(typeNode); + return objectType.shiftedTypeIndex; + default: + return PrimitiveType.ANY; + } + } + + public getOrCreateRecordForDeclNode(initializer: ts.Node | undefined, variableNode?: ts.Node) { + if (!initializer) { + return PrimitiveType.ANY; + } + let typeIndex = PrimitiveType.ANY; + let declNode = this.getDeclNodeForInitializer(initializer); + typeIndex = this.getTypeFromDecl(declNode, initializer.kind == ts.SyntaxKind.NewExpression); + if (variableNode) { + TypeRecorder.getInstance().setVariable2Type(variableNode, typeIndex); + } + return typeIndex; + } + + public getOrCreateRecordForTypeNode(typeNode: ts.TypeNode | undefined, variableNode?: ts.Node) { + if (!typeNode) { + return PrimitiveType.ANY; + } + let typeIndex = PrimitiveType.ANY; + typeIndex = this.getTypeFromAnotation(typeNode); + if (typeIndex == PrimitiveType.ANY && typeNode.kind == ts.SyntaxKind.TypeReference) { + let typeName = typeNode.getChildAt(0); + let typeDecl = this.getDeclNodeForInitializer(typeName); + typeIndex = this.getTypeFromDecl(typeDecl, true); + } + if (variableNode) { + TypeRecorder.getInstance().setVariable2Type(variableNode, typeIndex); + } + return typeIndex; + } + + public formatVariableStatement(variableStatementNode: ts.VariableStatement) { + let decList = variableStatementNode.declarationList; + decList.declarations.forEach(declaration => { + let variableNode = declaration.name; + let typeNode = declaration.type; + let initializer = declaration.initializer; + let typeIndex = this.getOrCreateRecordForTypeNode(typeNode, variableNode); + if (typeIndex == PrimitiveType.ANY) { + typeIndex = this.getOrCreateRecordForDeclNode(initializer, variableNode); + } + if (this.hasExportKeyword(variableStatementNode) && typeIndex != PrimitiveType.ANY) { + let exportedName = jshelpers.getTextOfIdentifierOrLiteral(variableNode); + TypeRecorder.getInstance().setExportedType(exportedName, typeIndex); + } + }); + } + + public formatClassDeclaration(classDeclNode: ts.ClassDeclaration) { + let classType = new ClassType(classDeclNode); + let typeIndex = classType.shiftedTypeIndex; + let className = classDeclNode.name; + let exportedName = "default"; + if (className) { + exportedName = jshelpers.getTextOfIdentifierOrLiteral(className); + } + if (this.hasExportKeyword(classDeclNode)) { + TypeRecorder.getInstance().setExportedType(exportedName, typeIndex); + } else if (this.hasDeclareKeyword(classDeclNode) && isGlobalDeclare()) { + TypeRecorder.getInstance().setDeclaredType(exportedName, typeIndex); + } + } + + // Entry for type recording + public formatNodeType(node: ts.Node, importOrExportStmt?: ModuleStmt) { + if (this.compiledTypeChecker === null) { + return; + } + switch (node.kind) { + case ts.SyntaxKind.VariableStatement: + let variableStatementNode = ts.getOriginalNode(node); + this.formatVariableStatement(variableStatementNode); + break; + case ts.SyntaxKind.FunctionDeclaration: + let functionDeclNode = ts.getOriginalNode(node); + let functionName = functionDeclNode.name ? functionDeclNode.name : undefined; + let funcType = new FunctionType(functionDeclNode); + if (functionName) { + TypeRecorder.getInstance().setVariable2Type(functionName, funcType.shiftedTypeIndex); + } + break; + case ts.SyntaxKind.ClassDeclaration: + // Create the type if it is exported or declared. Otherwise, waite until it gets instantiated + let classDeclNode = ts.getOriginalNode(node); + if (this.hasExportKeyword(node) || this.hasDeclareKeyword(node)) { + this.formatClassDeclaration(classDeclNode); + } + break; + case ts.SyntaxKind.InterfaceDeclaration: + if (isGlobalDeclare()) { + let interfaceDeclNode = ts.getOriginalNode(node); + let interfaceType = new InterfaceType(interfaceDeclNode); + let interfaceName = interfaceDeclNode.name; + if (interfaceName) { + let name = jshelpers.getTextOfIdentifierOrLiteral(interfaceName); + TypeRecorder.getInstance().setDeclaredType(name, interfaceType.shiftedTypeIndex); + } + } + break; + case ts.SyntaxKind.ExportDeclaration: + if (importOrExportStmt) { + TypeRecorder.getInstance().addExportedType(importOrExportStmt); + } + break; + case ts.SyntaxKind.ImportDeclaration: + if (importOrExportStmt) { + TypeRecorder.getInstance().addImportedType(importOrExportStmt); + } + break; + case ts.SyntaxKind.ExportAssignment: + let expression = (node).expression; + let exportedName = "default"; + let expressionType = this.getTypeAtLocation(expression); + if (expressionType) { + let typeNode = expressionType.getSymbol().valueDeclaration; + TypeRecorder.getInstance().addNonReExportedType(exportedName, typeNode, expression); + } + break; + } + } + + +} diff --git a/ts2panda/src/typeRecorder.ts b/ts2panda/src/typeRecorder.ts new file mode 100755 index 0000000000000000000000000000000000000000..73f5018552a8a0fc380d7540bc773babe7f12550 --- /dev/null +++ b/ts2panda/src/typeRecorder.ts @@ -0,0 +1,298 @@ +/* + * 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, { IndexedAccessType } from "typescript"; +import { + ClassType, + ExternalType, + TypeSummary +} from "./base/typeSystem"; +import { ModuleStmt } from "./modules"; +import { TypeChecker } from "./typeChecker"; +import { PrimitiveType } from "./base/typeSystem"; +import * as jshelpers from "./jshelpers"; + +export class TypeRecorder { + private static instance: TypeRecorder; + private type2Index: Map = new Map(); + private variable2Type: Map = new Map(); + private userDefinedTypeSet: Set = new Set();; + private typeSummary: TypeSummary = new TypeSummary(); + private class2InstanceMap: Map = new Map(); + private arrayTypeMap: Map = new Map(); + private unionTypeMap: Map = new Map(); + private exportedType: Map = new Map(); + private declaredType: Map = new Map(); + // namespace mapping: namepace -> filepath (import * as sth from "...") + // In PropertyAccessExpression we'll need this to map the symbol to filepath + private namespaceMap: Map = new Map(); + // (export * from "..."), if the symbol isn't in the reExportedType map, search here. + private anonymousReExport: Array = new Array(); + + private constructor() { } + + public static getInstance(): TypeRecorder { + return TypeRecorder.instance; + } + + public static createInstance(): TypeRecorder { + TypeRecorder.instance = new TypeRecorder(); + return TypeRecorder.instance; + } + + public setTypeSummary() { + this.typeSummary.setInfo(this.countUserDefinedTypeSet(), this.anonymousReExport); + } + + public addUserDefinedTypeSet(index: number) { + this.userDefinedTypeSet.add(index); + } + + public countUserDefinedTypeSet(): number { + return this.userDefinedTypeSet.size; + } + + public addType2Index(typeNode: ts.Node, index: number) { + this.type2Index.set(typeNode, index); + this.addUserDefinedTypeSet(index); + } + + public setVariable2Type(variableNode: ts.Node, index: number) { + this.variable2Type.set(variableNode, index); + if (index > PrimitiveType._LENGTH) { + this.addUserDefinedTypeSet(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 PrimitiveType.ANY; + } + } + + public tryGetVariable2Type(variableNode: ts.Node): number { + if (this.variable2Type.has(variableNode)) { + return this.variable2Type.get(variableNode)!; + } else { + return PrimitiveType.ANY; + } + } + + public setArrayTypeMap(contentTypeIndex: number, arrayTypeIndex: number) { + this.arrayTypeMap.set(contentTypeIndex, arrayTypeIndex) + } + + public hasArrayTypeMapping(contentTypeIndex: number) { + return this.arrayTypeMap.has(contentTypeIndex); + } + + public getFromArrayTypeMap(contentTypeIndex: number) { + return this.arrayTypeMap.get(contentTypeIndex); + } + + public setUnionTypeMap(unionStr: string, unionTypeIndex: number) { + this.unionTypeMap.set(unionStr, unionTypeIndex) + } + + public hasUnionTypeMapping(unionStr: string) { + return this.unionTypeMap.has(unionStr); + } + + public getFromUnionTypeMap(unionStr: string) { + return this.unionTypeMap.get(unionStr); + } + + public setClass2InstanceMap(classIndex: number, instanceIndex: number) { + this.class2InstanceMap.set(classIndex, instanceIndex) + } + + public hasClass2InstanceMap(classIndex: number) { + return this.class2InstanceMap.has(classIndex); + } + + public getClass2InstanceMap(classIndex: number) { + return this.class2InstanceMap.get(classIndex); + } + + // exported/imported + public addImportedType(moduleStmt: ModuleStmt) { + moduleStmt.getBindingNodeMap().forEach((externalNode, localNode) => { + let externalName = jshelpers.getTextOfIdentifierOrLiteral(externalNode); + let importDeclNode = TypeChecker.getInstance().getTypeDeclForIdentifier(localNode); + let externalType = new ExternalType(externalName, moduleStmt.getModuleRequest()); + this.addType2Index(importDeclNode, externalType.shiftedTypeIndex); + this.setVariable2Type(localNode, externalType.shiftedTypeIndex); + }); + + if (moduleStmt.getNameSpace() != "") { + this.setNamespaceMap(moduleStmt.getNameSpace(), moduleStmt.getModuleRequest()); + let externalType = new ExternalType("*", moduleStmt.getNameSpace()); + let ImportTypeIndex = externalType.shiftedTypeIndex; + this.addUserDefinedTypeSet(ImportTypeIndex); + } + } + + public addExportedType(moduleStmt: ModuleStmt) { + if (moduleStmt.getModuleRequest() != "") { + // re-export, no need to search in typeRecord cause it must not be there + if (moduleStmt.getNameSpace() != "") { + // re-export * as namespace + let externalType = new ExternalType("*", moduleStmt.getModuleRequest()); + let typeIndex = externalType.shiftedTypeIndex; + this.setExportedType(moduleStmt.getNameSpace(), typeIndex); + this.addUserDefinedTypeSet(typeIndex); + } else if (moduleStmt.getBindingNameMap().size != 0) { + // re-export via clause + moduleStmt.getBindingNameMap().forEach((originalName, exportedName) => { + let externalType = new ExternalType(originalName, moduleStmt.getModuleRequest()); + let typeIndex = externalType.shiftedTypeIndex; + this.setExportedType(exportedName, typeIndex); + this.addUserDefinedTypeSet(typeIndex); + }); + } else { + // re-export * with anonymuse namespace + this.addAnonymousReExport(moduleStmt.getModuleRequest()); + } + } else { + // named export via clause, could came from imported or local + moduleStmt.getBindingNodeMap().forEach((localNode, externalNode) => { + let exportedName = jshelpers.getTextOfIdentifierOrLiteral(externalNode); + let nodeType = TypeChecker.getInstance().getTypeAtLocation(localNode); + let typeNode = nodeType.getSymbol()?.valueDeclaration; + if (typeNode) { + this.addNonReExportedType(exportedName, typeNode!, localNode); + } + }); + } + } + + public addNonReExportedType(exportedName: string, typeNode: ts.Node, localNode: ts.Node) { + // Check if type of localName was already stroed in typeRecord + // Imported type should already be stored in typeRecord by design + let typeIndexForType = this.tryGetTypeIndex(typeNode); + let typeIndexForVariable = this.tryGetVariable2Type(typeNode); + if (typeIndexForType != PrimitiveType.ANY) { + this.setExportedType(exportedName, typeIndexForType); + } else if (typeIndexForVariable != PrimitiveType.ANY) { + this.setExportedType(exportedName, typeIndexForVariable); + } else { + // not found in typeRecord. Need to create the type and + // add to typeRecord with its localName and to exportedType with its exportedName + let typeIndex = TypeChecker.getInstance().getTypeFromDecl(typeNode, localNode.kind == ts.SyntaxKind.NewExpression); + this.setExportedType(exportedName, typeIndex); + } + } + + public setExportedType(exportedName: string, typeIndex: number) { + this.exportedType.set(exportedName, typeIndex); + } + + public setDeclaredType(exportedName: string, typeIndex: number) { + this.declaredType.set(exportedName, typeIndex); + } + + public addAnonymousReExport(redirectName: string) { + this.anonymousReExport.push(redirectName); + } + + public setNamespaceMap(namespace: string, filePath: string) { + this.namespaceMap.set(namespace, filePath); + } + + public inNampespaceMap(targetName: string) { + return this.namespaceMap.has(targetName); + } + + public getPathForNamespace(targetName: string) { + return this.namespaceMap.get(targetName); + } + + // for log + public getType2Index(): Map { + return this.type2Index; + } + + public getVariable2Type(): Map { + return this.variable2Type; + } + + public getTypeSet() { + return this.userDefinedTypeSet; + } + + public getExportedType() { + return this.exportedType; + } + + public getDeclaredType() { + return this.declaredType; + } + + public getAnonymousReExport() { + return this.anonymousReExport; + } + + public getNamespaceMap() { + return this.namespaceMap; + } + + public printNodeMap(map: Map) { + map.forEach((value, key) => { + console.log(jshelpers.getTextOfNode(key) + ": " + value); + }); + } + + public printExportMap(map: Map) { + map.forEach((value, key) => { + console.log(key + " : " + value); + }); + } + + public printReExportMap(map: Map) { + map.forEach((value, key) => { + console.log(key + " : " + value); + }); + } + + public getLog() { + // console.log("=========== getLog ===========: " + node.kind); + // console.log(jshelpers.getTextOfNode(node)); + // console.log("=========== currIndex ===========: ", currIndex); + // console.log(PandaGen.getLiteralArrayBuffer()[currIndex]); + // console.log("=============================="); + // console.log("type2Index: "); + // console.log(this.printNodeMap(this.getType2Index())); + // console.log("variable2Type: "); + // console.log(this.printNodeMap(this.getVariable2Type())); + // console.log("getTypeSet: "); + // console.log(this.getTypeSet()); + // console.log("class instance Map:"); + // console.log(this.class2InstanceMap); + // console.log("=============================="); + // console.log("exportedType:"); + // console.log(this.printExportMap(this.getExportedType())); + // console.log("AnoymousRedirect:"); + // console.log(this.getAnonymousReExport()); + // console.log("namespace Map:"); + // console.log(this.getNamespaceMap()); + // console.log("=============================="); + } +} \ No newline at end of file diff --git a/ts2panda/src/variable.ts b/ts2panda/src/variable.ts index eba622a9ea94d46218f7addbd89a14a644339723..5563f6dfead7b6694efec3e7d6c0ad4f026be703 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( @@ -41,11 +42,13 @@ export abstract class Variable { ) { this.name = name; this.vreg = undefined; - this.name = name; + this.typeIndex = 0; } bindVreg(vreg: VReg) { this.vreg = vreg; + this.vreg.setTypeIndex(this.typeIndex); + this.vreg.setVariableName(this.name); } hasAlreadyBinded(): boolean { @@ -63,6 +66,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(); 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..bbe51f5d1f63d45df2e16e879710d7e807dbb627 100755 --- a/ts2panda/templates/irnodes.ts.erb +++ b/ts2panda/templates/irnodes.ts.erb @@ -185,6 +185,8 @@ export abstract class Intrinsic extends IRNode { export class VReg { private static global_id = 0; + private typeIndex: number = 0; + private variableName: string = ""; readonly id: number; // used for debug purpose to distinguish one instance from another num: number = -1; @@ -225,6 +227,22 @@ export class VReg { return; } } + + getTypeIndex() { + return this.typeIndex; + } + + setTypeIndex(typeIndex: number) { + this.typeIndex = typeIndex; + } + + getVariableName() { + return this.variableName; + } + + setVariableName(variableName: string) { + this.variableName = variableName; + } } 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/tests/expression/functionExpression.test.ts b/ts2panda/tests/expression/functionExpression.test.ts index f7cdaa4e5a76581dcea683aac75c72b2a482399b..c7743c9a4ba48fc3660bf9b4f312dda258262303 100755 --- a/ts2panda/tests/expression/functionExpression.test.ts +++ b/ts2panda/tests/expression/functionExpression.test.ts @@ -30,6 +30,7 @@ import { EcmaEqdyn, EcmaGetresumemode, EcmaLdlexenvdyn, + EcmaLdfunction, EcmaResumegenerator, EcmaReturnundefined, EcmaSuspendgenerator, @@ -60,6 +61,8 @@ describe("compileFunctionExpression", function () { let expected_func = [ new EcmaLdlexenvdyn(), new StaDyn(new VReg()), + new EcmaLdfunction(), + new StaDyn(new VReg()), new LdaDyn(new VReg()), new StaDyn(new VReg()), new EcmaCallarg0dyn(new VReg()), @@ -369,4 +372,4 @@ describe("compileFunctionExpression", function () { } expect(errorThrown).to.be.true; }); -}) \ No newline at end of file +}) diff --git a/ts2panda/tests/lexenv.test.ts b/ts2panda/tests/lexenv.test.ts index 634973e7365d51fdc8d9562b28af96ec5a031220..64bd73f9d4065c49f6ce477b14ae48779665b5af 100644 --- a/ts2panda/tests/lexenv.test.ts +++ b/ts2panda/tests/lexenv.test.ts @@ -118,7 +118,7 @@ describe("lexenv-compile-testcase in lexenv.test.ts", function () { let sourceFile = creatAstFromSnippet(source); let compilerDriver = new CompilerDriver('UnitTest'); let globalScope = new GlobalScope(sourceFile); - let recorder = new Recorder(sourceFile, globalScope, compilerDriver); + let recorder = new Recorder(sourceFile, globalScope, compilerDriver, false, false); recorder.record(); expect(globalScope, "root is null!").to.not.equal(null); @@ -146,7 +146,7 @@ describe("lexenv-compile-testcase in lexenv.test.ts", function () { let sourceFile = creatAstFromSnippet(source); let compilerDriver = new CompilerDriver('UnitTest'); let globalScope = new GlobalScope(sourceFile); - let recorder = new Recorder(sourceFile, globalScope, compilerDriver); + let recorder = new Recorder(sourceFile, globalScope, compilerDriver, false, false); recorder.record(); let children = globalScope.getChildVariableScope(); @@ -194,7 +194,7 @@ describe("lexenv-compile-testcase in lexenv.test.ts", function () { let compilerDriver = new CompilerDriver('UnitTest'); let globalScope = new GlobalScope(sourceFile); - let recorder = new Recorder(sourceFile, globalScope, compilerDriver); + let recorder = new Recorder(sourceFile, globalScope, compilerDriver, false, false); recorder.record(); let postOrderVariableScopes = compilerDriver.postOrderAnalysis(globalScope); @@ -218,7 +218,7 @@ describe("lexenv-compile-testcase in lexenv.test.ts", function () { let compilerDriver = new CompilerDriver('UnitTest'); let globalScope = new GlobalScope(sourceFile); - let recorder = new Recorder(sourceFile, globalScope, compilerDriver); + let recorder = new Recorder(sourceFile, globalScope, compilerDriver, false, false); recorder.record(); let postOrderVariableScopes = compilerDriver.postOrderAnalysis(globalScope); @@ -272,7 +272,7 @@ describe("lexenv-compile-testcase in lexenv.test.ts", function () { let compilerDriver = new CompilerDriver('UnitTest'); let globalScope = new GlobalScope(sourceFile); - let recorder = new Recorder(sourceFile, globalScope, compilerDriver); + let recorder = new Recorder(sourceFile, globalScope, compilerDriver, false, false); recorder.record(); let postOrderVariableScopes = compilerDriver.postOrderAnalysis(globalScope); @@ -505,7 +505,7 @@ describe("lexenv-compile-testcase in lexenv.test.ts", function () { new LdaDyn(new VReg()), new StaDyn(new VReg()), ...insnsStoreLexVar_outer_2, - new EcmaDefinefuncdyn("#2#", new Imm(ResultType.Int, 0), new VReg()), + new EcmaDefinefuncdyn("#1#", new Imm(ResultType.Int, 0), new VReg()), // returnStatement new StaDyn(new VReg()), new LdaDyn(new VReg()), @@ -542,7 +542,7 @@ describe("lexenv-compile-testcase in lexenv.test.ts", function () { expect(outerA instanceof LocalVariable, "a in outer is local variable").to.be.true; // expect((outerScope).hasLexEnv(), "outer scope need to create lex env").to.be.true; expect((outerScope).getNumLexEnv(), "number of lexvar at outer scope").to.be.equal(2); - let anonymousPg = snippetCompiler.getPandaGenByName("#2#"); + let anonymousPg = snippetCompiler.getPandaGenByName("#1#"); let anonymousScope = anonymousPg!.getScope(); let anonymousA = anonymousScope!.findLocal("a"); let searchRlt = anonymousScope!.find("a"); @@ -559,4 +559,4 @@ describe("lexenv-compile-testcase in lexenv.test.ts", function () { expect(checkInstructions(anonymousPg!.getInsns(), expect_anonymous), "check annonymous func ins").to.be.true; expect(checkInstructions(outerPg!.getInsns(), expect_outer), "check outer func ins").to.be.true; }); -}); \ No newline at end of file +}); diff --git a/ts2panda/ts2abc/BUILD.gn b/ts2panda/ts2abc/BUILD.gn index 77d34e3a711d64785e6a7d93b5a382e6d295a274..7e9b058c9141f7bc07f31dbe4813b2b2e7e531cc 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) { @@ -46,7 +47,10 @@ config("ts2abc_config") { } ohos_executable("ts2abc") { - sources = [ "ts2abc.cpp" ] + sources = [ + "main.cpp", + "ts2abc.cpp", + ] configs = [ ":ts2abc_config" ] diff --git a/ts2panda/ts2abc/main.cpp b/ts2panda/ts2abc/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..724a6536d26de45f8b9f5638ddd96c2d2cee7141 --- /dev/null +++ b/ts2panda/ts2abc/main.cpp @@ -0,0 +1,104 @@ +/* + * 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 Preprocess(const panda::ts2abc::Options &options, const panda::PandArgParser &argParser, std::string &output, + std::string &data, std::string &usage) +{ + std::string input; + 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; + } + } + return RETURN_SUCCESS; +} + +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() < static_cast(OptLevel::O_LEVEL0)) || + (options.GetOptLevelArg() > static_cast(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 output; + std::string data = ""; + + if (Preprocess(options, argParser, output, data, usage) == RETURN_FAILED) { + 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..4875b12ea78e6fa8d1bfa27aaeb8682b019aa578 --- /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..71a6f8795579b774adbb5138a5a172ef25585b33 --- /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() {} + const void DebugLogTest::SetUp() {} + const 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); + } +} diff --git a/ts2panda/ts2abc/tests/debugmode_test.cpp b/ts2panda/ts2abc/tests/debugmode_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dd75cdbcc4ecfd26ee5d3eae4437669d033e9012 --- /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() {} + const void DebugModeTest::SetUp() {} + const 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); + } +} diff --git a/ts2panda/ts2abc/tests/functions_test.cpp b/ts2panda/ts2abc/tests/functions_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..02b71d91e6c5cbc2bb95dc6f06f0f96dde6000b0 --- /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() {} + const 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"]; + } + const 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()); + } +} 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..1505caf54a76c1d50279375d144357ff5a7123dc --- /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() {} + const void StringArrTest::SetUp() {} + const 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); + } +} diff --git a/ts2panda/ts2abc/ts2abc.cpp b/ts2panda/ts2abc/ts2abc.cpp index 0e72bf4f5e2a78fea7daadd96d2a1a05c8211bc5..41e2a6d19076a6871259a6fe2187d75279334c85 100644 --- a/ts2panda/ts2abc/ts2abc.cpp +++ b/ts2panda/ts2abc/ts2abc.cpp @@ -1,4 +1,5 @@ -/* * Copyright (c) 2021 Huawei Device Co., Ltd. +/* + * 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 @@ -25,6 +26,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 +54,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,15 +112,36 @@ 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]; int ret = vsnprintf_s(logMsg, sizeof(logMsg) - 1, sizeof(logMsg) - 1, format, valist); if (ret == -1) { + va_end(valist); return; } std::cout << logMsg << std::endl; @@ -195,7 +207,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 +391,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 +427,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 +476,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 +508,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()) { @@ -610,12 +622,164 @@ static void ParseFunctionCallType(const Json::Value &function, panda::pandasm::F } panda::pandasm::AnnotationData callTypeAnnotation("_ESCallTypeAnnotation"); std::string annotationName = "callType"; - panda::pandasm::AnnotationElement callTypeAnnotationElement(annotationName, - std::make_unique( + panda::pandasm::AnnotationElement callTypeAnnotationElement( + annotationName, std::make_unique( panda::pandasm::ScalarValue::Create(callType))); callTypeAnnotation.AddElement(std::move(callTypeAnnotationElement)); - const_cast&>(pandaFunc.metadata->GetAnnotations()).push_back( - std::move(callTypeAnnotation)); + const_cast&>( + pandaFunc.metadata->GetAnnotations()).push_back(std::move(callTypeAnnotation)); +} + +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; + } + + 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 void ParseFunctionExportedType(const Json::Value &function, panda::pandasm::Function &pandaFunc) +{ + std::string funcName = ""; + if (function.isMember("name") && function["name"].isString()) { + funcName = function["name"].asString(); + if (funcName != "func_main_0") { + return; + } + } + + if (function.isMember("exportedSymbol2Types") && function["exportedSymbol2Types"].isArray()) { + auto exportedTypes = function["exportedSymbol2Types"]; + panda::pandasm::AnnotationData funcAnnotation("_ESTypeAnnotation"); + std::vector symbolElements; + std::vector symbolTypeElements; + for (Json::ArrayIndex i = 0; i < exportedTypes.size(); i++) { + auto exportedType = exportedTypes[i]; + if (!exportedType.isObject()) { + continue; + } + + std::string exportedSymbol = ""; + if (exportedType.isMember("symbol") && exportedType["symbol"].isString()) { + exportedSymbol = exportedType["symbol"].asString(); + } + + uint32_t typeIndex = 0; + if (exportedType.isMember("type") && exportedType["type"].isInt()) { + typeIndex = exportedType["type"].asUInt(); + } + + panda::pandasm::ScalarValue symbol( + panda::pandasm::ScalarValue::Create(exportedSymbol)); + symbolElements.emplace_back(std::move(symbol)); + panda::pandasm::ScalarValue tIndex( + panda::pandasm::ScalarValue::Create(typeIndex)); + symbolTypeElements.emplace_back(std::move(tIndex)); + } + + std::string symbolAnnotationName = "exportedSymbols"; + panda::pandasm::AnnotationElement exportedSymbolsElement(symbolAnnotationName, + std::make_unique(panda::pandasm::ArrayValue( + panda::pandasm::Value::Type::STRING, symbolElements))); + funcAnnotation.AddElement(std::move(exportedSymbolsElement)); + + std::string symbolTypeAnnotationName = "exportedSymbolTypes"; + panda::pandasm::AnnotationElement exportedSymbolTypesElement(symbolTypeAnnotationName, + std::make_unique(panda::pandasm::ArrayValue( + panda::pandasm::Value::Type::U32, symbolTypeElements))); + funcAnnotation.AddElement(std::move(exportedSymbolTypesElement)); + + const_cast&>( + pandaFunc.metadata->GetAnnotations()).push_back(std::move(funcAnnotation)); + } +} + +static void ParseFunctionDeclaredType(const Json::Value &function, panda::pandasm::Function &pandaFunc) +{ + std::string funcName = ""; + if (function.isMember("name") && function["name"].isString()) { + funcName = function["name"].asString(); + if (funcName != "func_main_0") { + return; + } + } + + if (function.isMember("declaredSymbol2Types") && function["declaredSymbol2Types"].isArray()) { + auto declaredTypes = function["declaredSymbol2Types"]; + panda::pandasm::AnnotationData funcAnnotation("_ESTypeAnnotation"); + std::vector symbolElements; + std::vector symbolTypeElements; + for (Json::ArrayIndex i = 0; i < declaredTypes.size(); i++) { + auto declaredType = declaredTypes[i]; + if (!declaredType.isObject()) { + continue; + } + + std::string declaredSymbol = ""; + if (declaredType.isMember("symbol") && declaredType["symbol"].isString()) { + declaredSymbol = declaredType["symbol"].asString(); + } + + uint32_t typeIndex = 0; + if (declaredType.isMember("type") && declaredType["type"].isInt()) { + typeIndex = declaredType["type"].asUInt(); + } + + panda::pandasm::ScalarValue symbol( + panda::pandasm::ScalarValue::Create(declaredSymbol)); + symbolElements.emplace_back(std::move(symbol)); + panda::pandasm::ScalarValue tIndex( + panda::pandasm::ScalarValue::Create(typeIndex)); + symbolTypeElements.emplace_back(std::move(tIndex)); + } + + std::string symbolAnnotationName = "declaredSymbols"; + panda::pandasm::AnnotationElement declaredSymbolsElement(symbolAnnotationName, + std::make_unique(panda::pandasm::ArrayValue( + panda::pandasm::Value::Type::STRING, symbolElements))); + funcAnnotation.AddElement(std::move(declaredSymbolsElement)); + + std::string symbolTypeAnnotationName = "declaredSymbolTypes"; + panda::pandasm::AnnotationElement declaredSymbolTypesElement(symbolTypeAnnotationName, + std::make_unique(panda::pandasm::ArrayValue( + panda::pandasm::Value::Type::U32, symbolTypeElements))); + funcAnnotation.AddElement(std::move(declaredSymbolTypesElement)); + + const_cast&>(pandaFunc.metadata->GetAnnotations()).push_back( + std::move(funcAnnotation)); + } } static panda::pandasm::Function ParseFunction(const Json::Value &function) @@ -623,16 +787,15 @@ static panda::pandasm::Function ParseFunction(const Json::Value &function) auto pandaFunc = GetFunctionDefintion(function); ParseFunctionMetadata(function, pandaFunc); ParseFunctionInstructions(function, pandaFunc); - // parsing variables debug info ParseVariablesDebugInfo(function, pandaFunc); - // parsing source file debug info ParseSourceFileDebugInfo(function, pandaFunc); - // parsing labels ParseFunctionLabels(function, pandaFunc); - // parsing catch blocks ParseFunctionCatchTables(function, pandaFunc); // parsing call opt type ParseFunctionCallType(function, pandaFunc); + ParseFunctionTypeInfo(function, pandaFunc); + ParseFunctionExportedType(function, pandaFunc); + ParseFunctionDeclaredType(function, pandaFunc); return pandaFunc; } @@ -644,6 +807,13 @@ static void GenerateESCallTypeAnnotationRecord(panda::pandasm::Program &prog) callTypeAnnotationRecord.metadata->SetAccessFlags(panda::ACC_ANNOTATION); prog.record_table.emplace(callTypeAnnotationRecord.name, std::move(callTypeAnnotationRecord)); } +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) { @@ -661,7 +831,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; @@ -692,18 +862,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()); } } @@ -713,7 +883,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; } } @@ -740,6 +910,7 @@ static void ReplaceAllDistinct(std::string &str, const std::string &oldValue, co static void ParseOptions(const Json::Value &rootValue, panda::pandasm::Program &prog) { GenerateESCallTypeAnnotationRecord(prog); + GenerateESTypeAnnotationRecord(prog); ParseModuleMode(rootValue, prog); ParseLogEnable(rootValue); ParseDebugMode(rootValue); @@ -789,31 +960,31 @@ static int ParseSmallPieceJson(const std::string &subJson, panda::pandasm::Progr type = rootValue["type"].asInt(); } switch (type) { - case JsonType::FUNCTION: { + case static_cast(JsonType::FUNCTION): { if (rootValue.isMember("func_body") && rootValue["func_body"].isObject()) { ParseSingleFunc(rootValue, prog); } break; } - case JsonType::RECORD: { + case static_cast(JsonType::RECORD): { if (rootValue.isMember("rec_body") && rootValue["rec_body"].isObject()) { ParseSingleRec(rootValue, prog); } break; } - case JsonType::STRING: { + case static_cast(JsonType::STRING): { if (rootValue.isMember("string") && rootValue["string"].isString()) { ParseSingleStr(rootValue, prog); } break; } - case JsonType::LITERALBUFFER: { + case static_cast(JsonType::LITERALBUFFER): { if (rootValue.isMember("literalArray") && rootValue["literalArray"].isObject()) { ParseSingleLiteralBuf(rootValue, prog); } break; } - case JsonType::OPTIONS: { + case static_cast(JsonType::OPTIONS): { ParseOptions(rootValue, prog); break; } @@ -856,9 +1027,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; @@ -870,8 +1039,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 != static_cast(OptLevel::O_LEVEL0) || optLevel != static_cast(OptLevel::O_LEVEL0)) { + 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; @@ -905,7 +1074,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) { @@ -939,7 +1108,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; @@ -964,101 +1133,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..691e87a0de11bf81af137d734610d46d65c92005 --- /dev/null +++ b/ts2panda/ts2abc/ts2abc.h @@ -0,0 +1,61 @@ +/* + * 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 class JsonType { + FUNCTION = 0, + RECORD, + STRING, + LITERALBUFFER, + OPTIONS +}; + +constexpr int RETURN_SUCCESS = 0; +constexpr int RETURN_FAILED = 1; + +enum class 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_ diff --git a/ts2panda/ts2abc/ts2abc_options.h b/ts2panda/ts2abc/ts2abc_options.h index d1d9e9f8efae336b165869c2cca9d606a0cae979..577166e624a5373486aeaffd5fbfe5f5e12b765d 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 +#endif // PANDA_TS2ABC_OPTIONS_GEN_H_ diff --git a/ts2panda/ts2abc_config.gni b/ts2panda/ts2abc_config.gni index a86ae8032eb04bbe23de2eeda0b41bf5a8f4c4cb..aa2b3f9fc3a1a1dd04a869e5df30a4c2ad7a7165 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" @@ -27,12 +28,11 @@ declare_args() { ts2abc_build_path = "" } +node_modules = "//prebuilts/build-tools/common/ts2abc/node_modules" if (build_public_version) { nodejs_dir = "//prebuilts/build-tools/common/nodejs" - node_modules = "//prebuilts/build-tools/common/ts2abc/node_modules" } else { nodejs_dir = "//prebuilts/ace-toolkit/nodejs" - node_modules = "//prebuilts/ace-toolkit/ace-loader/panda/node_modules" } if (host_toolchain == buildtool_mac) { @@ -151,3 +151,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}/" ] + } +}