!233 Support compiling commonjs module

Merge pull request !233 from hufeng/master
This commit is contained in:
openharmony_ci
2022-06-06 05:10:43 +00:00
committed by Gitee
12 changed files with 244 additions and 10 deletions
+13 -2
View File
@@ -64,11 +64,22 @@ If no parameter is specified for **\[options\]**, an ARK binary file is generat
</th>
</tr>
</thead>
<tbody><tr id="row1435412465598"><td class="cellrowborder" valign="top" width="12.898710128987101%" headers="mcps1.1.6.1.1 "><p id="p881325510017"><a name="p881325510017"></a><a name="p881325510017"></a>--modules</p>
<tbody><tr id="row1435412465598"><td class="cellrowborder" valign="top" width="12.898710128987101%" headers="mcps1.1.6.1.1 "><p id="p881325510017"><a name="p881325510017"></a><a name="p881325510017"></a>--commonjs</p>
</td>
<td class="cellrowborder" valign="top" width="6.869313068693131%" headers="mcps1.1.6.1.2 "><p id="p148431189013"><a name="p148431189013"></a><a name="p148431189013"></a>-c</p>
</td>
<td class="cellrowborder" valign="top" width="19.33806619338066%" headers="mcps1.1.6.1.3 "><p id="p072882813015"><a name="p072882813015"></a><a name="p072882813015"></a>Compiles the code based on the commonjs.</p>
</td>
<td class="cellrowborder" valign="top" width="25.82741725827417%" headers="mcps1.1.6.1.4 "><p id="p10327833305"><a name="p10327833305"></a><a name="p10327833305"></a>-</p>
</td>
<td class="cellrowborder" valign="top" width="35.066493350664935%" headers="mcps1.1.6.1.5 "><p id="p076075115014"><a name="p076075115014"></a><a name="p076075115014"></a>-</p>
</td>
</tr>
<tr id="row1435412465598"><td class="cellrowborder" valign="top" width="12.898710128987101%" headers="mcps1.1.6.1.1 "><p id="p881325510017"><a name="p881325510017"></a><a name="p881325510017"></a>--modules</p>
</td>
<td class="cellrowborder" valign="top" width="6.869313068693131%" headers="mcps1.1.6.1.2 "><p id="p148431189013"><a name="p148431189013"></a><a name="p148431189013"></a>-m</p>
</td>
<td class="cellrowborder" valign="top" width="19.33806619338066%" headers="mcps1.1.6.1.3 "><p id="p072882813015"><a name="p072882813015"></a><a name="p072882813015"></a>Compiles the code based on the module.</p>
<td class="cellrowborder" valign="top" width="19.33806619338066%" headers="mcps1.1.6.1.3 "><p id="p072882813015"><a name="p072882813015"></a><a name="p072882813015"></a>Compiles the code based on the ecmascript standard module.</p>
</td>
<td class="cellrowborder" valign="top" width="25.82741725827417%" headers="mcps1.1.6.1.4 "><p id="p10327833305"><a name="p10327833305"></a><a name="p10327833305"></a>-</p>
</td>
+13 -2
View File
@@ -65,11 +65,22 @@ $ node --expose-gc src/index.js [options] file.js
</th>
</tr>
</thead>
<tbody><tr id="row1435412465598"><td class="cellrowborder" valign="top" width="12.898710128987101%" headers="mcps1.1.6.1.1 "><p id="p881325510017"><a name="p881325510017"></a><a name="p881325510017"></a>--modules</p>
<tbody><tr id="row1435412465598"><td class="cellrowborder" valign="top" width="12.898710128987101%" headers="mcps1.1.6.1.1 "><p id="p881325510017"><a name="p881325510017"></a><a name="p881325510017"></a>--commonjs</p>
</td>
<td class="cellrowborder" valign="top" width="6.869313068693131%" headers="mcps1.1.6.1.2 "><p id="p148431189013"><a name="p148431189013"></a><a name="p148431189013"></a>-c</p>
</td>
<td class="cellrowborder" valign="top" width="19.33806619338066%" headers="mcps1.1.6.1.3 "><p id="p072882813015"><a name="p072882813015"></a><a name="p072882813015"></a>按照commonjs模式编译</p>
</td>
<td class="cellrowborder" valign="top" width="25.82741725827417%" headers="mcps1.1.6.1.4 "><p id="p10327833305"><a name="p10327833305"></a><a name="p10327833305"></a>-</p>
</td>
<td class="cellrowborder" valign="top" width="35.066493350664935%" headers="mcps1.1.6.1.5 "><p id="p076075115014"><a name="p076075115014"></a><a name="p076075115014"></a>-</p>
</td>
</tr>
<tr id="row1435412465598"><td class="cellrowborder" valign="top" width="12.898710128987101%" headers="mcps1.1.6.1.1 "><p id="p881325510017"><a name="p881325510017"></a><a name="p881325510017"></a>--modules</p>
</td>
<td class="cellrowborder" valign="top" width="6.869313068693131%" headers="mcps1.1.6.1.2 "><p id="p148431189013"><a name="p148431189013"></a><a name="p148431189013"></a>-m</p>
</td>
<td class="cellrowborder" valign="top" width="19.33806619338066%" headers="mcps1.1.6.1.3 "><p id="p072882813015"><a name="p072882813015"></a><a name="p072882813015"></a>按照module模式编译</p>
<td class="cellrowborder" valign="top" width="19.33806619338066%" headers="mcps1.1.6.1.3 "><p id="p072882813015"><a name="p072882813015"></a><a name="p072882813015"></a>按照ESM模式编译</p>
</td>
<td class="cellrowborder" valign="top" width="25.82741725827417%" headers="mcps1.1.6.1.4 "><p id="p10327833305"><a name="p10327833305"></a><a name="p10327833305"></a>-</p>
</td>
+8
View File
@@ -72,6 +72,14 @@ function addInnerArgs(node: ts.Node, scope: VariableScope, enableTypeRecord: boo
scope.addParameter("this", VarDeclarationKind.CONST, 0);
}
if (CmdOptions.isCommonJs() && node.kind === ts.SyntaxKind.SourceFile) {
scope.addParameter("exports", VarDeclarationKind.LET, 1);
scope.addParameter("require", VarDeclarationKind.LET, 2);
scope.addParameter("module", VarDeclarationKind.LET, 3);
scope.addParameter("__filename", VarDeclarationKind.LET, 4);
scope.addParameter("__dirname", VarDeclarationKind.LET, 5);
}
if (node.kind != ts.SyntaxKind.SourceFile) {
let funcNode = <ts.FunctionLikeDeclaration>node;
addParameters(funcNode, scope, enableTypeRecord);
+33
View File
@@ -306,3 +306,36 @@ export function isBase64Str(input: string): boolean {
}
return Buffer.from(Buffer.from(input, 'base64').toString()).toString('base64') == input;
}
export function transformCommonjsModule(sourceFile: ts.SourceFile) {
/*
Transform the commonjs module's AST by wrap the sourceCode.
(function (exports, require, module, __filename, __dirname) {
[SourceCode]
})(exports, require, module, __filename, __dirname);
*/
let newStatements = [ts.factory.createExpressionStatement(ts.factory.createCallExpression(
ts.factory.createParenthesizedExpression(ts.factory.createFunctionExpression(
undefined, undefined, undefined, undefined,
[
ts.factory.createParameterDeclaration(undefined, undefined, undefined, ts.factory.createIdentifier("exports"), undefined, undefined, undefined),
ts.factory.createParameterDeclaration(undefined, undefined, undefined, ts.factory.createIdentifier("require"), undefined, undefined, undefined),
ts.factory.createParameterDeclaration(undefined, undefined, undefined, ts.factory.createIdentifier("module"), undefined, undefined, undefined),
ts.factory.createParameterDeclaration(undefined, undefined, undefined, ts.factory.createIdentifier("__filename"), undefined, undefined, undefined),
ts.factory.createParameterDeclaration(undefined, undefined, undefined, ts.factory.createIdentifier("__dirname"), undefined, undefined, undefined)
],
undefined,
ts.factory.createBlock(sourceFile.statements)
)),
undefined,
[
ts.factory.createIdentifier("exports"),
ts.factory.createIdentifier("require"),
ts.factory.createIdentifier("module"),
ts.factory.createIdentifier("__filename"),
ts.factory.createIdentifier("__dirname")
]
))];
return ts.factory.updateSourceFile(sourceFile, newStatements);
}
+19 -1
View File
@@ -22,8 +22,9 @@ import * as path from "path";
import { execute } from "./base/util";
const ts2pandaOptions = [
{ name: 'commonjs', alias: 'c', type: Boolean, defaultValue: false, description: "compile as commonJs module." },
{ 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 and generate the json file."},
{ name: 'debug-log', alias: 'l', type: Boolean, defaultValue: false, description: "show info debug log and generate the json file." },
{ 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: 'debug-add-watch', alias: 'w', type: String, lazyMultiple: true, defaultValue: [], description: "watch expression and abc file path in debug mode." },
@@ -106,10 +107,27 @@ export class CmdOptions {
return args.length == 2 && args[0] == "stop";
}
static isCommonJs(): boolean {
if (!this.options) {
return false;
}
if (this.options["commonjs"] && this.options["modules"]) {
throw new Error("Can not compile with [-c] and [-m] options at the same time");
}
return this.options["commonjs"];
}
static isModules(): boolean {
if (!this.options) {
return false;
}
if (this.options["modules"] && this.options["commonjs"]) {
throw new Error("Can not compile with [-m] and [-c] options at the same time");
}
return this.options["modules"];
}
+1 -1
View File
@@ -1586,7 +1586,7 @@ export class Compiler {
loadTarget(node: ts.Node, variable: { scope: Scope | undefined, level: number, v: Variable | undefined }) {
if (variable.v instanceof LocalVariable) {
if (variable.v.isLetOrConst() || variable.v.isClass()) {
if (!CmdOptions.isCommonJs() && (variable.v.isLetOrConst() || variable.v.isClass())) {
if (variable.scope instanceof GlobalScope) {
this.pandaGen.tryLoadGlobalByName(node, variable.v.getName());
return;
+6
View File
@@ -400,6 +400,12 @@ export class CompilerDriver {
// the runtime passes these to global scope when calls it
let parametersCount = 3;
if (node.kind == ts.SyntaxKind.SourceFile) {
if (CmdOptions.isCommonJs()) {
// global scope accepts 5 additional parameters:
// "exports", "require", "module", "__filename","__dirname"
// when compiled as commonjs
parametersCount += 5;
}
return parametersCount;
}
let decl = <ts.FunctionLikeDeclaration>node;
+6 -3
View File
@@ -23,7 +23,7 @@ import * as jshelpers from "./jshelpers";
import { LOGE } from "./log";
import { setGlobalDeclare, setGlobalStrict } from "./strictMode";
import { TypeChecker } from "./typeChecker";
import { setPos, isBase64Str } from "./base/util";
import { setPos, isBase64Str, transformCommonjsModule } from "./base/util";
function checkIsGlobalDeclaration(sourceFile: ts.SourceFile) {
for (let statement of sourceFile.statements) {
@@ -100,6 +100,9 @@ function main(fileNames: string[], options: ts.CompilerOptions) {
newStatements.push(...node.statements);
node = ts.factory.updateSourceFile(node, newStatements);
}
if (CmdOptions.isCommonJs()) {
node = transformCommonjsModule(node);
}
let outputBinName = getOutputBinName(node);
let compilerDriver = new CompilerDriver(outputBinName);
setGlobalStrict(jshelpers.isEffectiveStrictModeSourceFile(node, options));
@@ -286,7 +289,8 @@ function run(args: string[], options?: ts.CompilerOptions): void {
updateWatchedFile();
return;
}
main(parsed.fileNames.concat(CmdOptions.getIncludedFiles()), parsed.options);
main(parsed.fileNames.concat(dtsFiles).concat(CmdOptions.getIncludedFiles()), parsed.options);
} catch (err) {
if (err instanceof diag.DiagnosticError) {
let diagnostic = diag.getDiagnostic(err.code);
@@ -303,6 +307,5 @@ function run(args: string[], options?: ts.CompilerOptions): void {
}
let dtsFiles = getDtsFiles(path["join"](__dirname, "../node_modules/typescript/lib"));
process.argv.push(...dtsFiles);
run(process.argv.slice(2), Compiler.Options.Default);
global.gc();
+1
View File
@@ -190,6 +190,7 @@ export class Ts2Panda {
let options = {
"t": JsonType.options,
"module_mode": CmdOptions.isModules(),
"commonjs_module": CmdOptions.isCommonJs(),
"debug_mode": CmdOptions.isDebugMode(),
"log_enabled": CmdOptions.isEnableDebugLog(),
"opt_level": CmdOptions.getOptLevel(),
+108
View File
@@ -0,0 +1,108 @@
/*
* 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 {
expect
} from 'chai';
import 'mocha';
import { checkInstructions, SnippetCompiler } from "./utils/base";
import {
EcmaCallarg1dyn,
EcmaCallirangedyn,
EcmaDefinefuncdyn,
EcmaReturnundefined,
EcmaStobjbyname,
Imm,
LdaDyn,
LdaiDyn,
LdaStr,
MovDyn,
StaDyn,
VReg
} from "../src/irnodes";
import { CmdOptions } from '../src/cmdOptions';
describe("CommonJsTest", function () {
it("mainFunc", function() {
CmdOptions.isCommonJs = () => {return true};
let snippetCompiler = new SnippetCompiler();
snippetCompiler.compileCommonjs(`let a = 1`, 'cjs.js');
CmdOptions.isCommonJs = () => {return false};
let funcMainInsns = snippetCompiler.getGlobalInsns();
let expected = [
new EcmaDefinefuncdyn('#1#', new Imm(5), new VReg()),
new StaDyn(new VReg()),
new LdaDyn(new VReg()),
new StaDyn(new VReg()),
new LdaDyn(new VReg()),
new StaDyn(new VReg()),
new LdaDyn(new VReg()),
new StaDyn(new VReg()),
new LdaDyn(new VReg()),
new StaDyn(new VReg()),
new LdaDyn(new VReg()),
new StaDyn(new VReg()),
new EcmaCallirangedyn(new Imm(5), [new VReg(), new VReg(), new VReg(), new VReg(), new VReg(), new VReg()]),
new EcmaReturnundefined(),
];
expect(checkInstructions(funcMainInsns, expected)).to.be.true;
});
it("requireTest", function() {
CmdOptions.isCommonJs = () => {return true};
let snippetCompiler = new SnippetCompiler();
snippetCompiler.compileCommonjs(`let a = require('a.js')`, 'cjs.js');
CmdOptions.isCommonJs = () => {return false};
let execInsns = snippetCompiler.getPandaGenByName('#1#')!.getInsns();
let requirePara = new VReg();
let requireReg = new VReg();
let moduleRequest = new VReg();
let expected = [
new LdaDyn(requirePara),
new StaDyn(requireReg),
new LdaStr("a.js"),
new StaDyn(moduleRequest),
new EcmaCallarg1dyn(requireReg, moduleRequest),
new StaDyn(new VReg()),
new EcmaReturnundefined()
];
expect(checkInstructions(execInsns, expected)).to.be.true;
});
it("exportTest", function() {
CmdOptions.isCommonJs = () => {return true};
let snippetCompiler = new SnippetCompiler();
snippetCompiler.compileCommonjs(`let a = 1; exports.a = a;`, 'cjs.js');
CmdOptions.isCommonJs = () => {return false};
let execInsns = snippetCompiler.getPandaGenByName('#1#')!.getInsns();
let exportsPara = new VReg();
let exportsReg = new VReg();
let tmpReg = new VReg();
let a = new VReg();
let expected = [
new LdaiDyn(new Imm(1)),
new StaDyn(a),
new LdaDyn(exportsPara),
new StaDyn(exportsReg),
new MovDyn(tmpReg, exportsReg),
new LdaDyn(a),
new EcmaStobjbyname("a", tmpReg),
new EcmaReturnundefined()
];
expect(checkInstructions(execInsns, expected)).to.be.true;
});
});
+10 -1
View File
@@ -32,6 +32,7 @@ import { setGlobalStrict } from "../../src/strictMode";
import { creatAstFromSnippet } from "./asthelper";
import { LiteralBuffer } from "../../src/base/literal";
import { CmdOptions } from "../../src/cmdOptions";
import { transformCommonjsModule } from "../../src/base/util";
const compileOptions = {
outDir: "../tmp/build",
@@ -172,7 +173,7 @@ export function compileMainSnippet(snippet: string, pandaGen?: PandaGen, scope?:
return compileUnits[0].getInsns();
}
export function compileAfterSnippet(snippet: string, name:string) {
export function compileAfterSnippet(snippet: string, name:string, isCommonJs: boolean = false) {
let compileUnits = null;
ts.transpileModule(
snippet,
@@ -185,6 +186,9 @@ export function compileAfterSnippet(snippet: string, name:string) {
after : [
(ctx: ts.TransformationContext) => {
return (sourceFile: ts.SourceFile) => {
if (isCommonJs) {
sourceFile = transformCommonjsModule(sourceFile);
}
jshelpers.bindSourceFile(sourceFile, {});
setGlobalStrict(jshelpers.isEffectiveStrictModeSourceFile(sourceFile, compileOptions));
let compilerDriver = new CompilerDriver('UnitTest');
@@ -218,6 +222,11 @@ export class SnippetCompiler {
return this.pandaGens;
}
compileCommonjs(snippet: string, name: string) {
this.pandaGens = compileAfterSnippet(snippet, name, true);
return this.pandaGens;
}
getGlobalInsns(): IRNode[] {
let root = this.getPandaGenByName("func_main_0");
if (root) {
+26
View File
@@ -794,6 +794,21 @@ static void GenerateESTypeAnnotationRecord(panda::pandasm::Program &prog)
prog.record_table.emplace(tsTypeAnnotationRecord.name, std::move(tsTypeAnnotationRecord));
}
static void GenerateCommonJsRecord(panda::pandasm::Program &prog, bool isCommonJs)
{
// when multi-abc file get merged, field should be inserted in abc's own record
auto commonjsRecord = panda::pandasm::Record("_CommonJsRecord", LANG_EXT);
commonjsRecord.metadata->SetAccessFlags(panda::ACC_PUBLIC);
auto isCommonJsField = panda::pandasm::Field(LANG_EXT);
isCommonJsField.name = "isCommonJs";
isCommonJsField.type = panda::pandasm::Type("u8", 0);
isCommonJsField.metadata->SetValue(panda::pandasm::ScalarValue::Create<panda::pandasm::Value::Type::U8>(
static_cast<uint8_t>(isCommonJs)));
commonjsRecord.field_list.emplace_back(std::move(isCommonJsField));
prog.record_table.emplace(commonjsRecord.name, std::move(commonjsRecord));
}
static void GenerateESModuleRecord(panda::pandasm::Program &prog)
{
auto ecmaModuleRecord = panda::pandasm::Record("_ESModuleRecord", LANG_EXT);
@@ -846,6 +861,16 @@ static void ParseModuleMode(const Json::Value &rootValue, panda::pandasm::Progra
}
}
static void ParseCommonJsModuleMode(const Json::Value &rootValue, panda::pandasm::Program &prog)
{
Logd("------------parse commonjs_module_mode-------------");
if (rootValue.isMember("commonjs_module") && rootValue["commonjs_module"].isBool()) {
if (rootValue["commonjs_module"].asBool()) {
GenerateCommonJsRecord(prog, true);
}
}
}
void ParseLogEnable(const Json::Value &rootValue)
{
if (rootValue.isMember("log_enabled") && rootValue["log_enabled"].isBool()) {
@@ -896,6 +921,7 @@ static void ParseOptions(const Json::Value &rootValue, panda::pandasm::Program &
GenerateESCallTypeAnnotationRecord(prog);
GenerateESTypeAnnotationRecord(prog);
ParseModuleMode(rootValue, prog);
ParseCommonJsModuleMode(rootValue, prog);
ParseLogEnable(rootValue);
ParseDebugMode(rootValue);
ParseOptLevel(rootValue);