diff --git a/es2panda/parser/transformer/transformer.cpp b/es2panda/parser/transformer/transformer.cpp index 0a2b7f0f7571dcfaabd17ddd9b7b60cc4636bccc..47c69969b813b77a9e5022da41bc0a056cd12494 100644 --- a/es2panda/parser/transformer/transformer.cpp +++ b/es2panda/parser/transformer/transformer.cpp @@ -768,7 +768,16 @@ ir::UpdateNodes Transformer::VisitClassDeclaration(ir::ClassDeclaration *node) if (it->IsMethodDefinition()) { auto *definition = it->AsMethodDefinition(); bool isStatic = definition->IsStatic(); - auto paramDecorators = CreateParamDecorators(name, definition, false, isStatic); + + auto variableDeclarations = CreateVariableDeclarationForDecorators(definition); + if (isStatic) { + staticMemberDecorators.insert(staticMemberDecorators.end(), + variableDeclarations.begin(), variableDeclarations.end()); + } else { + res.insert(res.end(), variableDeclarations.begin(), variableDeclarations.end()); + } + + auto paramDecorators = CreateParamDecorators(name, definition, variableDeclarations, false, isStatic); if (isStatic) { staticMemberDecorators.insert(staticMemberDecorators.end(), paramDecorators.begin(), paramDecorators.end()); @@ -778,7 +787,7 @@ ir::UpdateNodes Transformer::VisitClassDeclaration(ir::ClassDeclaration *node) if (!definition->HasDecorators()) { continue; } - auto methodDecorators = CreateMethodDecorators(name, definition, isStatic); + auto methodDecorators = CreateMethodDecorators(name, definition, variableDeclarations, isStatic); if (isStatic) { staticMemberDecorators.insert(staticMemberDecorators.end(), methodDecorators.begin(), methodDecorators.end()); @@ -791,7 +800,20 @@ ir::UpdateNodes Transformer::VisitClassDeclaration(ir::ClassDeclaration *node) if (!classProperty->HasDecorators()) { continue; } - auto propertyDecorators = CreatePropertyDecorators(name, classProperty, isStatic); + + if (classProperty->IsComputed() && !isStatic && classProperty->Value() == nullptr) { + res.push_back(AllocNode(classProperty->Key())); + } + + auto variableDeclarations = CreateVariableDeclarationForDecorators(classProperty); + if (isStatic) { + staticMemberDecorators.insert(staticMemberDecorators.end(), + variableDeclarations.begin(), variableDeclarations.end()); + } else { + res.insert(res.end(), variableDeclarations.begin(), variableDeclarations.end()); + } + + auto propertyDecorators = CreatePropertyDecorators(name, classProperty, variableDeclarations, isStatic); if (isStatic) { staticMemberDecorators.insert(staticMemberDecorators.end(), propertyDecorators.begin(), propertyDecorators.end()); @@ -805,14 +827,17 @@ ir::UpdateNodes Transformer::VisitClassDeclaration(ir::ClassDeclaration *node) res.insert(res.end(), staticMemberDecorators.begin(), staticMemberDecorators.end()); } + auto variableDeclarationsForCtorOrClass = CreateVariableDeclarationForDecorators(node); + res.insert(res.end(), variableDeclarationsForCtorOrClass.begin(), variableDeclarationsForCtorOrClass.end()); + // constructor decorators auto *ctor = node->Definition()->Ctor(); - auto ctorParamDecorators = CreateParamDecorators(name, ctor, true, false); + auto ctorParamDecorators = CreateParamDecorators(name, ctor, variableDeclarationsForCtorOrClass, true, false); res.insert(res.end(), ctorParamDecorators.begin(), ctorParamDecorators.end()); // class decorators if (hasClassDecorators) { - auto classDecorators = CreateClassDecorators(node); + auto classDecorators = CreateClassDecorators(node, variableDeclarationsForCtorOrClass); res.insert(res.end(), classDecorators.begin(), classDecorators.end()); } if (res.size() == 1) { @@ -821,8 +846,67 @@ ir::UpdateNodes Transformer::VisitClassDeclaration(ir::ClassDeclaration *node) return res; } +std::vector Transformer::CreateVariableDeclarationForDecorators(ir::AstNode *node) +{ + std::vector res; + + switch (node->Type()) { + case ir::AstNodeType::METHOD_DEFINITION: { + auto methodDecorators = node->AsMethodDefinition()->Decorators(); + for (size_t i = 0; i < methodDecorators.size(); i++) { + util::StringView varName = CreateNewVariable(false); + res.push_back(CreateVariableDeclarationWithIdentify(varName, VariableParsingFlags::LET, nullptr, + false, methodDecorators[i]->Expr(), true)); + } + + auto paramsDecorators = node->AsMethodDefinition()->GetParamDecorators(); + for (size_t i = 0; i < paramsDecorators.size(); i++) { + auto paramDecorators = paramsDecorators[i].decorators; + for (size_t j = 0; j < paramDecorators.size(); j++) { + util::StringView varName = CreateNewVariable(false); + res.push_back(CreateVariableDeclarationWithIdentify(varName, VariableParsingFlags::LET, nullptr, + false, paramDecorators[j]->Expr(), true)); + } + } + return res; + } + case ir::AstNodeType::CLASS_PROPERTY: { + auto propDecorators = node->AsClassProperty()->Decorators(); + for (size_t i = 0; i < propDecorators.size(); i++) { + util::StringView varName = CreateNewVariable(false); + res.push_back(CreateVariableDeclarationWithIdentify(varName, VariableParsingFlags::LET, nullptr, + false, propDecorators[i]->Expr(), true)); + } + return res; + } + case ir::AstNodeType::CLASS_DECLARATION: { + auto classDecorators = node->AsClassDeclaration()->Decorators(); + for (size_t i = 0; i < classDecorators.size(); i++) { + util::StringView varName = CreateNewVariable(false); + res.push_back(CreateVariableDeclarationWithIdentify(varName, VariableParsingFlags::LET, nullptr, + false, classDecorators[i]->Expr(), true)); + } + + auto ctorParamsDecorators = node->AsClassDeclaration()->Definition()->Ctor()->GetParamDecorators(); + for (size_t i = 0; i < ctorParamsDecorators.size(); i++) { + auto ctorParamDecorators = ctorParamsDecorators[i].decorators; + for (size_t j = 0; j < ctorParamDecorators.size(); j++) { + util::StringView varName = CreateNewVariable(false); + res.push_back(CreateVariableDeclarationWithIdentify(varName, VariableParsingFlags::LET, nullptr, + false, ctorParamDecorators[j]->Expr(), true)); + } + } + return res; + } + default: { + UNREACHABLE(); + } + } +} + std::vector Transformer::CreateParamDecorators(util::StringView className, ir::MethodDefinition *node, + const std::vector &variableDeclarations, bool isConstructor, bool isStatic) { @@ -842,6 +926,7 @@ std::vector Transformer::CreateParamDecorators(util::StringView c * Static method or constructor will use constructor function of the class instead of prototype of class */ std::vector res; + int pos = variableDeclarations.size(); auto paramsDecorators = node->GetParamDecorators(); for (int i = paramsDecorators.size() - 1; i >= 0; i--) { auto paramIndex = paramsDecorators[i].paramIndex; @@ -853,7 +938,8 @@ std::vector Transformer::CreateParamDecorators(util::StringView c CreateReferenceIdentifier(CONSTRUCTOR_NAME) : GetClassMemberName(node->Key(), node->Computed(), node)); arguments.push_back(AllocNode(paramIndex)); - auto *callExpr = AllocNode(decorators[j]->Expr(), + auto *callExpr = AllocNode( + variableDeclarations[--pos]->AsVariableDeclaration()->Declarators().front()->Id(), std::move(arguments), nullptr, false); res.push_back(AllocNode(callExpr)); } @@ -863,6 +949,7 @@ std::vector Transformer::CreateParamDecorators(util::StringView c std::vector Transformer::CreatePropertyDecorators(util::StringView className, ir::ClassProperty *node, + const std::vector &variableDeclarations, bool isStatic) { /* @@ -886,7 +973,9 @@ std::vector Transformer::CreatePropertyDecorators(util::StringVie ArenaVector arguments(Allocator()->Adapter()); arguments.push_back(CreateDecoratorTarget(className, isStatic)); arguments.push_back(GetClassMemberName(node->Key(), node->IsComputed(), node)); - auto *callExpr = AllocNode(decorators[i]->Expr(), std::move(arguments), nullptr, false); + auto *callExpr = AllocNode( + variableDeclarations[i]->AsVariableDeclaration()->Declarators().front()->Id(), + std::move(arguments), nullptr, false); res.push_back(AllocNode(callExpr)); } @@ -895,6 +984,7 @@ std::vector Transformer::CreatePropertyDecorators(util::StringVie std::vector Transformer::CreateMethodDecorators(util::StringView className, ir::MethodDefinition *node, + const std::vector &variableDeclarations, bool isStatic) { /* @@ -909,26 +999,31 @@ std::vector Transformer::CreateMethodDecorators(util::StringView * class C { * f(){} * } + * var ###a = Object.getOwnPropertyDescriptor(C.prototype, "f"); * Object.defineProperty(C.prototype, "f", - * g(C.prototype, "f", Object.getOwnPropertyDescriptor(C.prototype, "f")) || - * Object.getOwnPropertyDescriptor(C.prototype, "f")); + * g(C.prototype, "f", ###a) || ###a); * * static method will use constructor function of the class instead of prototype of class * If the decorator has a return value, it will be set as the new property of the method */ std::vector res; + int pos = node->Decorators().size(); auto decorators = node->Decorators(); for (int i = decorators.size() - 1; i >= 0; i--) { ArenaVector arguments(Allocator()->Adapter()); arguments.push_back(CreateDecoratorTarget(className, isStatic)); arguments.push_back(GetClassMemberName(node->Key(), node->Computed(), node)); - arguments.push_back(CreateGetOwnPropertyDescriptorCall(CreateDecoratorTarget(className, isStatic), - GetClassMemberName(node->Key(), node->Computed(), node))); - auto *callExpr = AllocNode(decorators[i]->Expr(), std::move(arguments), nullptr, false); - - auto *getProperty = CreateGetOwnPropertyDescriptorCall(CreateDecoratorTarget(className, isStatic), - GetClassMemberName(node->Key(), node->Computed(), node)); - auto newValue = AllocNode(callExpr, getProperty, + util::StringView varName = CreateNewVariable(false); + auto getOwnPropertyDescriptorCall = CreateGetOwnPropertyDescriptorCall( + CreateDecoratorTarget(className, isStatic), GetClassMemberName(node->Key(), node->Computed(), node)); + res.push_back(CreateVariableDeclarationWithIdentify(varName, VariableParsingFlags::LET, nullptr, + false, getOwnPropertyDescriptorCall, true)); + arguments.push_back(AllocNode(varName)); + auto *callExpr = AllocNode( + variableDeclarations[--pos]->AsVariableDeclaration()->Declarators().front()->Id(), + std::move(arguments), nullptr, false); + + auto newValue = AllocNode(callExpr, AllocNode(varName), lexer::TokenType::PUNCTUATOR_LOGICAL_OR); auto *defineProperty = CreateDefinePropertyCall(CreateDecoratorTarget(className, isStatic), @@ -1004,7 +1099,8 @@ ir::Expression *Transformer::GetClassMemberName(ir::Expression *key, bool isComp return nullptr; } -std::vector Transformer::CreateClassDecorators(ir::ClassDeclaration *node) +std::vector Transformer::CreateClassDecorators(ir::ClassDeclaration *node, + const std::vector &variableDeclarations) { /* * Class decorators @@ -1023,11 +1119,14 @@ std::vector Transformer::CreateClassDecorators(ir::ClassDeclarati auto name = node->Definition()->GetName(); auto decorators = node->Decorators(); auto size = decorators.size(); + int pos = size; std::vector res; for (int i = size - 1; i >= 0; i--) { ArenaVector arguments(Allocator()->Adapter()); arguments.push_back(CreateReferenceIdentifier(name)); - auto *callExpr = AllocNode(decorators[i]->Expr(), std::move(arguments), nullptr, false); + auto *callExpr = AllocNode( + variableDeclarations[--pos]->AsVariableDeclaration()->Declarators().front()->Id(), + std::move(arguments), nullptr, false); auto left = CreateReferenceIdentifier(name); auto id = CreateReferenceIdentifier(name); diff --git a/es2panda/parser/transformer/transformer.h b/es2panda/parser/transformer/transformer.h index f9b2153dd4a7b2499ab00812b6f82d4f817bc63d..5c6d4098eb85f6a6d5d4e12825fcb22116a1a2ca 100644 --- a/es2panda/parser/transformer/transformer.h +++ b/es2panda/parser/transformer/transformer.h @@ -124,17 +124,22 @@ private: ir::Expression *CreateTsModuleParam(util::StringView paramName, bool isExport); ir::ExpressionStatement *CreateTsModuleAssignment(util::StringView name); ir::Expression *CreateMemberExpressionFromQualified(ir::Expression *node); - std::vector CreateClassDecorators(ir::ClassDeclaration *node); + std::vector CreateClassDecorators(ir::ClassDeclaration *node, + const std::vector &variableDeclarations); std::vector CreateMethodDecorators(util::StringView className, ir::MethodDefinition *node, + const std::vector &variableDeclarations, bool isStatic); std::vector CreatePropertyDecorators(util::StringView className, ir::ClassProperty *node, + const std::vector &variableDeclarations, bool isStatic); ir::CallExpression *CreateGetOwnPropertyDescriptorCall(ir::Expression *target, ir::Expression *key); ir::CallExpression *CreateDefinePropertyCall(ir::Expression *target, ir::Expression *key, ir::Expression *value); + std::vector CreateVariableDeclarationForDecorators(ir::AstNode *node); std::vector CreateParamDecorators(util::StringView className, ir::MethodDefinition *node, + const std::vector &variableDeclarations, bool isConstructor, bool isStatic); ir::MemberExpression *CreateClassPrototype(util::StringView className); diff --git a/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-16-expected.txt b/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-16-expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..03a2f70e67342c9d582f586e91811ce7eed4437f --- /dev/null +++ b/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-16-expected.txt @@ -0,0 +1,28 @@ +propDec- prop1 +inner-propDec- prop1 +propDec- prop3 +inner-propDec- prop3 +methodDec- classMethod2 +methodDec- classMethod22 +paramDec- start2 +paramDec- start22 +paramDec- end2 +inner-paramDec-idx= 1 +inner-paramDec-idx= 0 +inner-paramDec-idx= 0 +inner-methodDec- classMethod2 +inner-methodDec- classMethod2 +propDec- prop2 +inner-propDec- prop2 +methodDec- classMethod1 +paramDec- start1 +paramDec- end1 +inner-paramDec-idx= 1 +inner-paramDec-idx= 0 +inner-methodDec- classMethod1 +classDec +paramDec- start3 +paramDec- end3 +inner-paramDec-idx= 1 +inner-paramDec-idx= 0 +inner-classDec diff --git a/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-16.ts b/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-16.ts new file mode 100644 index 0000000000000000000000000000000000000000..3a72b664443876c67e6427ce2d1c435cf96bb8d0 --- /dev/null +++ b/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-16.ts @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 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. + */ + + +function classDec() { + print("classDec"); + return function (func: Function) { print("inner-classDec"); } +} + +function propDec(p: any) { + print("propDec-", p); + return function (target: any, propName: any) { print("inner-propDec-", propName); } +} + +function methodDec(p: any) { + print("methodDec-", p); + return function (target: any, methodName: any, desc: any) { print("inner-methodDec-", methodName); } +} + +function paramDec(value: any) { + print("paramDec-", value); + return function (target: any, methodName: any, idx: any) { print("inner-paramDec-idx=", idx); } +} + +@classDec() +class A { + @propDec("prop1") + prop1 = 1; + + constructor(@paramDec('start3') start: number, @paramDec('end3') end: number) { } + + @propDec("prop2") + static prop2 = 2; + + @propDec("prop3") + prop3 = 3; + + @methodDec("classMethod1") + static classMethod1(@paramDec('start1') start: number, @paramDec('end1') end: number) { + return {}; + } + + @methodDec("classMethod2") + @methodDec("classMethod22") + classMethod2(@paramDec('start2') @paramDec('start22') start: number, @paramDec('end2') end: number) { + return {}; + } +} diff --git a/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-17-expected.txt b/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-17-expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..3b6d8d0d3b2ddc5ba76d5b0decaebd3d8aebfb80 --- /dev/null +++ b/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-17-expected.txt @@ -0,0 +1,3 @@ +exec POST, url= /xxx.com +exec POST, propertyKey= method1 +exec descriptor, args diff --git a/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-17.ts b/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-17.ts new file mode 100644 index 0000000000000000000000000000000000000000..a6853a931afbbdb4ab207a35e5c5f15442b824df --- /dev/null +++ b/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-17.ts @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 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. + */ + + +function POST(url:string):MethodDecorator { + print("exec POST, url=", url); + return function (target: any, propertyKey: any, descriptor: PropertyDescriptor): void { + print("exec POST, propertyKey=", propertyKey); + descriptor.value = function (...args:any):void { + print("exec descriptor, args"); + } + } + } + + class A { + @POST('/xxx.com') + method1() { + print("method1"); + } + } + + let a = new A(); + a.method1(); diff --git a/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-18-expected.txt b/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-18-expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..5a94befcd95c4a547938281f62aa9f662ab1888f --- /dev/null +++ b/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-18-expected.txt @@ -0,0 +1,8 @@ +outer-- prop1 +outer-- prop +inner +inner +outer-- prop3 +inner +outer-- prop2 +inner diff --git a/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-18.ts b/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-18.ts new file mode 100644 index 0000000000000000000000000000000000000000..d089286c3f598bc5a468e2a9fc65fcbd4894e7d1 --- /dev/null +++ b/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-18.ts @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023 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. + */ + + +function Dec(v: string) { + print("outer--", v); + return function (t: any, p: any) { + print("inner"); + } +} + + +class A { + @Dec('prop1') + @Dec('prop') + ["prop1"]: number = 1; + + @Dec("prop2") + static prop2: number = 2; + + @Dec("prop3") + prop3: number = 3; +} + +var a = new A(); diff --git a/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-19-expected.txt b/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-19-expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..e2b8064c15fbe3aed388a38a46ae66d20b6b9896 --- /dev/null +++ b/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-19-expected.txt @@ -0,0 +1,4 @@ +prop1 +method1 +prop2 +method2 diff --git a/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-19.ts b/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-19.ts new file mode 100644 index 0000000000000000000000000000000000000000..55739b7098609d1ad2da7a1ade0a3fb89c6a188d --- /dev/null +++ b/es2panda/test/compiler/ts/cases/conformance/decorators/test-ts-decorators-19.ts @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 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. + */ + + +function inject1() { + return function (t: any, key: any) { + print(key); + } +} + +function inject2() { + return function (t: any, key: any, dec: any) { + print(key); + } +} + +class A { + @inject1() + ["prop1"]: number; + + @inject1() + static ["prop2"]: number; + + @inject2() + ["method1"]() { } + + @inject2() + static ["method2"]() { } +}