diff --git a/es2panda/parser/commonjs.cpp b/es2panda/parser/commonjs.cpp index d60656615b95bf49b6f43eec8744637e115fa6b9..ea4303635273a41f987967f475345c8febc72ec6 100644 --- a/es2panda/parser/commonjs.cpp +++ b/es2panda/parser/commonjs.cpp @@ -16,9 +16,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -44,19 +46,31 @@ void ParserImpl::AddCommonjsParams(ArenaVector ¶ms) } } -void ParserImpl::AddCommonjsArgs(ArenaVector &args) +void ParserImpl::AddReflectApplyArgs(ArenaVector &args, ir::FunctionExpression *wrapper) { + ASSERT(wrapper != nullptr); + // wrapper + args.push_back(wrapper); + // thisValue + ir::Expression *thisValue = AllocNode(binder::Binder::CJS_MANDATORY_PARAM_EXPORTS, Allocator()); + thisValue->AsIdentifier()->SetReference(); + args.push_back(thisValue); + // wrapper's arguments + ArenaVector elements(Allocator()->Adapter()); for (auto argName : cjsMandatoryParams) { ir::Expression *arg = AllocNode(argName, Allocator()); arg->AsIdentifier()->SetReference(); - args.push_back(arg); + elements.push_back(arg); } + ir::ArrayExpression *wrapperArgsArray = + AllocNode(ir::AstNodeType::ARRAY_EXPRESSION, std::move(elements), false); + args.push_back(wrapperArgsArray); } void ParserImpl::ParseCommonjs() { // create FunctionExpression as callee - ir::FunctionExpression *funcExpr = nullptr; + ir::FunctionExpression *wrapper = nullptr; { FunctionContext functionContext(this, ParserStatus::FUNCTION | ParserStatus::ALLOW_NEW_TARGET); FunctionParameterContext funcParamContext(&context_, Binder()); @@ -78,13 +92,19 @@ void ParserImpl::ParseCommonjs() functionScope->BindNode(funcNode); funcParamScope->BindNode(funcNode); - funcExpr = AllocNode(funcNode); + wrapper = AllocNode(funcNode); } // create CallExpression ArenaVector arguments(Allocator()->Adapter()); - AddCommonjsArgs(arguments); - auto *callExpr = AllocNode(funcExpr, std::move(arguments), nullptr, false); + AddReflectApplyArgs(arguments, wrapper); + + auto *apply = AllocNode("apply", Allocator()); + auto *reflect = AllocNode("Reflect", Allocator()); + auto *reflectApply = AllocNode(reflect, apply, + ir::MemberExpression::MemberExpressionKind::PROPERTY_ACCESS, false, false); + + auto *callExpr = AllocNode(reflectApply, std::move(arguments), nullptr, false); // create ExpressionStatement auto *exprStatementNode = AllocNode(callExpr); diff --git a/es2panda/parser/parserImpl.h b/es2panda/parser/parserImpl.h index 008933c30af5bc064b0945eb5990d96917ceb8dc..046749c56c38e3a135ed4d3000f6820d8c8d0e2f 100644 --- a/es2panda/parser/parserImpl.h +++ b/es2panda/parser/parserImpl.h @@ -216,15 +216,18 @@ private: [[nodiscard]] std::unique_ptr InitLexer(const std::string &fileName, const std::string &source); void ParseScript(); void ParseModule(); + /* - * Transform the commonjs's AST by wrapping the sourceCode - * e.g. (function (exports, require, module, __filename, __dirname) { - * [Origin_SourceCode] - * })(exports, require, module, __filename, __dirname); + * Transform the commonjs module's AST by wrap the sourceCode & use Reflect.apply to invoke this wrapper with [this] + * pointing to [exports] object + * + * Reflect.apply(function (exports, require, module, __filename, __dirname) { + * [SourceCode] + * }, exports, [exports, require, module, __filename, __dirname]); */ void ParseCommonjs(); void AddCommonjsParams(ArenaVector ¶ms); - void AddCommonjsArgs(ArenaVector &args); + void AddReflectApplyArgs(ArenaVector &args, ir::FunctionExpression *wrapper); void ParseProgram(ScriptKind kind); static ExpressionParseFlags CarryExpressionParserFlag(ExpressionParseFlags origin, ExpressionParseFlags carry); static ExpressionParseFlags CarryPatternFlags(ExpressionParseFlags flags); diff --git a/es2panda/test/compiler/commonjs/test-top-level-this-value-expected.txt b/es2panda/test/compiler/commonjs/test-top-level-this-value-expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..25f789b0f9a8de1f293db2976740990dc955cc30 --- /dev/null +++ b/es2panda/test/compiler/commonjs/test-top-level-this-value-expected.txt @@ -0,0 +1,2 @@ +123 +true diff --git a/es2panda/test/compiler/commonjs/test-top-level-this-value.js b/es2panda/test/compiler/commonjs/test-top-level-this-value.js new file mode 100644 index 0000000000000000000000000000000000000000..c897f3012982b4af3fc5b30dff4708a54d1be281 --- /dev/null +++ b/es2panda/test/compiler/commonjs/test-top-level-this-value.js @@ -0,0 +1,4 @@ +let a = 123; +module.exports.a = a; +print(this.a); +print(exports === this); \ No newline at end of file diff --git a/es2panda/test/parser/commonjs/test-commonjs-wrapper-expected.txt b/es2panda/test/parser/commonjs/test-commonjs-wrapper-expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..898b2bf36d9070318ac57d9b3ba875b22cf836c6 --- /dev/null +++ b/es2panda/test/parser/commonjs/test-commonjs-wrapper-expected.txt @@ -0,0 +1,473 @@ +{ + "type": "Program", + "statements": [ + { + "type": "ExpressionStatement", + "expression": { + "type": "CallExpression", + "callee": { + "type": "MemberExpression", + "object": { + "type": "Identifier", + "name": "Reflect", + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "property": { + "type": "Identifier", + "name": "apply", + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "computed": false, + "optional": false, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "arguments": [ + { + "type": "FunctionExpression", + "function": { + "type": "ScriptFunction", + "id": null, + "generator": false, + "async": false, + "expression": false, + "params": [ + { + "type": "Identifier", + "name": "exports", + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + { + "type": "Identifier", + "name": "require", + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + { + "type": "Identifier", + "name": "module", + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + { + "type": "Identifier", + "name": "__filename", + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + { + "type": "Identifier", + "name": "__dirname", + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + } + ], + "body": { + "type": "BlockStatement", + "statements": [ + { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "a", + "loc": { + "start": { + "line": 1, + "column": 5 + }, + "end": { + "line": 1, + "column": 6 + } + } + }, + "init": { + "type": "NumberLiteral", + "value": 123, + "loc": { + "start": { + "line": 1, + "column": 9 + }, + "end": { + "line": 1, + "column": 12 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 5 + }, + "end": { + "line": 1, + "column": 12 + } + } + } + ], + "kind": "let", + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 13 + } + } + }, + { + "type": "ExpressionStatement", + "expression": { + "type": "AssignmentExpression", + "operator": "=", + "left": { + "type": "MemberExpression", + "object": { + "type": "MemberExpression", + "object": { + "type": "Identifier", + "name": "module", + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 7 + } + } + }, + "property": { + "type": "Identifier", + "name": "exports", + "loc": { + "start": { + "line": 2, + "column": 8 + }, + "end": { + "line": 2, + "column": 15 + } + } + }, + "computed": false, + "optional": false, + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 15 + } + } + }, + "property": { + "type": "Identifier", + "name": "a", + "loc": { + "start": { + "line": 2, + "column": 16 + }, + "end": { + "line": 2, + "column": 17 + } + } + }, + "computed": false, + "optional": false, + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 17 + } + } + }, + "right": { + "type": "Identifier", + "name": "a", + "loc": { + "start": { + "line": 2, + "column": 20 + }, + "end": { + "line": 2, + "column": 21 + } + } + }, + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 21 + } + } + }, + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 22 + } + } + } + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 2, + "column": 22 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + { + "type": "Identifier", + "name": "exports", + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + { + "type": "ArrayExpression", + "elements": [ + { + "type": "Identifier", + "name": "exports", + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + { + "type": "Identifier", + "name": "require", + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + { + "type": "Identifier", + "name": "module", + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + { + "type": "Identifier", + "name": "__filename", + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + { + "type": "Identifier", + "name": "__dirname", + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + } + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + } + ], + "optional": false, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + } + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } +} diff --git a/es2panda/test/parser/commonjs/test-commonjs-wrapper.js b/es2panda/test/parser/commonjs/test-commonjs-wrapper.js new file mode 100644 index 0000000000000000000000000000000000000000..3483488aab9a23ecca95ce8822ae31ce7f3efc18 --- /dev/null +++ b/es2panda/test/parser/commonjs/test-commonjs-wrapper.js @@ -0,0 +1,2 @@ +let a = 123; +module.exports.a = a; \ No newline at end of file diff --git a/es2panda/test/runner.py b/es2panda/test/runner.py index 572fcfd1868dff6fe2750ed315832ad261286ae6..d02b38c33918a6908915706649755d6da0c6d1c9 100755 --- a/es2panda/test/runner.py +++ b/es2panda/test/runner.py @@ -846,6 +846,7 @@ def main(): ["--parse-only", "--module", "--extension=ts"]) runner.add_directory("parser/ts/type_checker", "ts", ["--parse-only", "--enable-type-check", "--module", "--extension=ts"]) + runner.add_directory("parser/commonjs", "js", ["--commonjs", "--parse-only", "--dump-ast"]) runners.append(runner) @@ -859,6 +860,7 @@ def main(): runner = CompilerRunner(args) runner.add_directory("compiler/js", "js", []) runner.add_directory("compiler/ts", "ts", ["--extension=ts"]) + runner.add_directory("compiler/commonjs", "js", ["--commonjs"]) runners.append(runner) diff --git a/ts2panda/src/base/util.ts b/ts2panda/src/base/util.ts index 6f5d787e481949215db129dce109bf605f38ae73..24bd25e9a95cce0259a850dfee3faf6802983472 100644 --- a/ts2panda/src/base/util.ts +++ b/ts2panda/src/base/util.ts @@ -342,31 +342,41 @@ export function isBase64Str(input: string): boolean { 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); - */ + * Transform the commonjs module's AST by wrap the sourceCode & use Reflect.apply to invoke this wrapper with [this] + * pointing to [exports] object + * + * Reflect.apply(function (exports, require, module, __filename, __dirname) { + * [SourceCode] + * }, exports, [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) - )), + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier("Reflect"), ts.factory.createIdentifier("apply") + ), undefined, [ + 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) + ), ts.factory.createIdentifier("exports"), - ts.factory.createIdentifier("require"), - ts.factory.createIdentifier("module"), - ts.factory.createIdentifier("__filename"), - ts.factory.createIdentifier("__dirname") + ts.factory.createArrayLiteralExpression( + [ + ts.factory.createIdentifier("exports"), + ts.factory.createIdentifier("require"), + ts.factory.createIdentifier("module"), + ts.factory.createIdentifier("__filename"), + ts.factory.createIdentifier("__dirname") + ] + ) ] ))]; diff --git a/ts2panda/tests/commonjs.test.ts b/ts2panda/tests/commonjs.test.ts index f814112ec480d21082eb8ec814586a1e8b8ba1af..7eb54188e7a39734b44c406521f52dc266f73d3e 100644 --- a/ts2panda/tests/commonjs.test.ts +++ b/ts2panda/tests/commonjs.test.ts @@ -20,14 +20,18 @@ import 'mocha'; import { checkInstructions, SnippetCompiler } from "./utils/base"; import { Callarg1, - Callrange, + Callthis3, + Createemptyarray, Definefunc, Returnundefined, Stobjbyname, + Stownbyindex, + Tryldglobalbyname, Imm, Lda, Ldai, LdaStr, + Ldobjbyname, Mov, Sta, VReg, @@ -49,20 +53,31 @@ describe("CommonJsTest", function () { CmdOptions.isCommonJs = () => {return false}; let funcMainInsns = snippetCompiler.getGlobalInsns(); let expected = [ - new Definefunc(new Imm(0), 'UnitTest.#1#', new Imm(5)), + new Tryldglobalbyname(new Imm(0), "Reflect"), new Sta(new VReg()), new Lda(new VReg()), + new Ldobjbyname(new Imm(1), "apply"), new Sta(new VReg()), - new Lda(new VReg()), + new Definefunc(new Imm(3), 'UnitTest.#1#', new Imm(5)), new Sta(new VReg()), new Lda(new VReg()), new Sta(new VReg()), - new Lda(new VReg()), + new Createemptyarray(new Imm(4)), new Sta(new VReg()), new Lda(new VReg()), + new Stownbyindex(new Imm(5), new VReg(), new Imm(0)), + new Lda(new VReg()), + new Stownbyindex(new Imm(7), new VReg(), new Imm(1)), + new Lda(new VReg()), + new Stownbyindex(new Imm(9), new VReg(), new Imm(2)), + new Lda(new VReg()), + new Stownbyindex(new Imm(11), new VReg(), new Imm(3)), + new Lda(new VReg()), + new Stownbyindex(new Imm(13), new VReg(), new Imm(4)), + new Lda(new VReg()), new Sta(new VReg()), new Lda(new VReg()), - new Callrange(new Imm(1), new Imm(5), [new VReg(), new VReg(), new VReg(), new VReg(), new VReg()]), + new Callthis3(new Imm(15), new VReg(), new VReg(), new VReg(), new VReg()), new Returnundefined(), ]; expect(checkInstructions(funcMainInsns, expected)).to.be.true;