diff --git a/ets2panda/checker/ets/helpers.cpp b/ets2panda/checker/ets/helpers.cpp index b662b6c757870346db686ff0efa2da6da4c697bf..efadb050558ac7a62b8232efb7791e193a584a63 100644 --- a/ets2panda/checker/ets/helpers.cpp +++ b/ets2panda/checker/ets/helpers.cpp @@ -1123,9 +1123,7 @@ void ETSChecker::SetPropertiesForModuleObject(checker::ETSObjectType *module_obj for (auto [_, var] : res->second.front()->GlobalClassScope()->InstanceDeclScope()->Bindings()) { (void)_; - if (var->AsLocalVariable()->Declaration()->Node()->IsExported() || - (var->AsLocalVariable()->Declaration()->Node()->IsClassDefinition() && - var->AsLocalVariable()->Declaration()->Node()->Parent()->IsExported())) { + if (var->AsLocalVariable()->Declaration()->Node()->IsExported()) { module_obj_type->AddProperty(var->AsLocalVariable()); } } diff --git a/ets2panda/ir/astNode.h b/ets2panda/ir/astNode.h index 52ae35ba718fd0b4f9d12517448c610915e4a431..33136e2c0a02d563f8224897ef8646e77352ff8e 100644 --- a/ets2panda/ir/astNode.h +++ b/ets2panda/ir/astNode.h @@ -371,11 +371,19 @@ public: [[nodiscard]] bool IsExported() const noexcept { + if (UNLIKELY(IsClassDefinition())) { + return parent_->IsExported(); + } + return (flags_ & ModifierFlags::EXPORT) != 0; } [[nodiscard]] bool IsDefaultExported() const noexcept { + if (UNLIKELY(IsClassDefinition())) { + return parent_->IsDefaultExported(); + } + return (flags_ & ModifierFlags::DEFAULT_EXPORT) != 0; } diff --git a/ets2panda/parser/ETSparser.cpp b/ets2panda/parser/ETSparser.cpp index e588da27c2d3acc43b375c32e62e113be3417c62..4f6723419a9fd48c472476daf3af4bbd04358b40 100644 --- a/ets2panda/parser/ETSparser.cpp +++ b/ets2panda/parser/ETSparser.cpp @@ -637,14 +637,30 @@ ir::ScriptFunction *ETSParser::AddInitMethod(ArenaVector &global_ return init_func; } +void ETSParser::MarkNodeAsExported(ir::AstNode *node, lexer::SourcePosition start_pos, bool default_export, + std::size_t num_of_elements) +{ + ir::ModifierFlags flag = default_export ? ir::ModifierFlags::DEFAULT_EXPORT : ir::ModifierFlags::EXPORT; + + if (UNLIKELY(flag == ir::ModifierFlags::DEFAULT_EXPORT)) { + if (VarBinder()->AsETSBinder()->DefaultExport() != nullptr || num_of_elements > 1) { + ThrowSyntaxError("Only one default export is allowed in a module", start_pos); + } + + VarBinder()->AsETSBinder()->SetDefaultExport(node); + } + + node->AddModifier(flag); +} + ArenaVector ETSParser::ParseTopLevelStatements(ArenaVector &statements) { ArenaVector global_properties(Allocator()->Adapter()); bool default_export = false; using ParserFunctionPtr = std::function; - auto const parse_type = [this, &statements](std::size_t const current_pos, - ParserFunctionPtr const &parser_function) -> void { + auto const parse_type = [this, &statements, &default_export](std::size_t const current_pos, + ParserFunctionPtr const &parser_function) -> void { ir::Statement *node = nullptr; { @@ -653,7 +669,8 @@ ArenaVector ETSParser::ParseTopLevelStatements(ArenaVector::max()) { - node->AddModifier(ir::ModifierFlags::EXPORT); + MarkNodeAsExported(node, node->Start(), default_export); + default_export = false; } statements.push_back(node); } @@ -792,19 +809,10 @@ ArenaVector ETSParser::ParseTopLevelStatements(ArenaVectorAsETSBinder()->DefaultExport() != nullptr || - global_properties.size() - current_pos != 1) { - ThrowSyntaxError("Only one default export is allowed in a module"); - } - - auto current_export = global_properties[current_pos++]; - current_export->AddModifier(ir::ModifierFlags::DEFAULT_EXPORT); - VarBinder()->AsETSBinder()->SetDefaultExport(current_export); - default_export = false; - } else { - global_properties[current_pos++]->AddModifier(ir::ModifierFlags::EXPORT); - } + MarkNodeAsExported(global_properties[current_pos], start_loc, default_export, + global_properties.size() - current_pos); + default_export = false; + current_pos++; } } diff --git a/ets2panda/parser/ETSparser.h b/ets2panda/parser/ETSparser.h index 91146099b9cbddd623fa0f6bd88fea058b45f670..3af548662904f89596329d0d3240eafb4f20be01 100644 --- a/ets2panda/parser/ETSparser.h +++ b/ets2panda/parser/ETSparser.h @@ -277,6 +277,8 @@ private: const lexer::SourcePosition &start_loc, bool ignore_call_expression) override; bool ParsePotentialNonNullExpression(ir::Expression **expression, lexer::SourcePosition start_loc) override; + void MarkNodeAsExported(ir::AstNode *node, lexer::SourcePosition start_pos, bool default_export, + std::size_t num_of_elements = 1); varbinder::Decl *BindClassName([[maybe_unused]] ir::Identifier *ident_node) override { return nullptr; diff --git a/ets2panda/test/parser/ets/import_tests/check_exported_default_class-expected.txt b/ets2panda/test/parser/ets/import_tests/check_exported_default_class-expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..d13c5c166f781ef1cddef635b08527d0286b3a61 --- /dev/null +++ b/ets2panda/test/parser/ets/import_tests/check_exported_default_class-expected.txt @@ -0,0 +1,445 @@ +{ + "type": "Program", + "statements": [ + { + "type": "ImportDeclaration", + "source": { + "type": "StringLiteral", + "value": "import_tests/modules", + "loc": { + "start": { + "line": 16, + "column": 32 + }, + "end": { + "line": 16, + "column": 76 + } + } + }, + "specifiers": [ + { + "type": "ImportDefaultSpecifier", + "local": { + "type": "Identifier", + "name": "ExportDefaultClass", + "decorators": [], + "loc": { + "start": { + "line": 16, + "column": 8 + }, + "end": { + "line": 16, + "column": 26 + } + } + }, + "loc": { + "start": { + "line": 16, + "column": 8 + }, + "end": { + "line": 16, + "column": 26 + } + } + } + ], + "loc": { + "start": { + "line": 16, + "column": 1 + }, + "end": { + "line": 16, + "column": 77 + } + } + }, + { + "type": "ClassDeclaration", + "definition": { + "id": { + "type": "Identifier", + "name": "ETSGLOBAL", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "superClass": null, + "implements": [], + "body": [ + { + "type": "MethodDefinition", + "key": { + "type": "Identifier", + "name": "_$init$_", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "kind": "method", + "accessibility": "public", + "static": true, + "optional": false, + "computed": false, + "value": { + "type": "FunctionExpression", + "function": { + "type": "ScriptFunction", + "id": { + "type": "Identifier", + "name": "_$init$_", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "generator": false, + "async": false, + "expression": false, + "params": [], + "body": { + "type": "BlockStatement", + "statements": [], + "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 + } + } + }, + "overloads": [], + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + { + "type": "MethodDefinition", + "key": { + "type": "Identifier", + "name": "main", + "decorators": [], + "loc": { + "start": { + "line": 18, + "column": 10 + }, + "end": { + "line": 18, + "column": 14 + } + } + }, + "kind": "method", + "accessibility": "public", + "static": true, + "optional": false, + "computed": false, + "value": { + "type": "FunctionExpression", + "function": { + "type": "ScriptFunction", + "id": { + "type": "Identifier", + "name": "main", + "decorators": [], + "loc": { + "start": { + "line": 18, + "column": 10 + }, + "end": { + "line": 18, + "column": 14 + } + } + }, + "generator": false, + "async": false, + "expression": false, + "params": [], + "body": { + "type": "BlockStatement", + "statements": [ + { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "test_class", + "typeAnnotation": { + "type": "ETSTypeReference", + "part": { + "type": "ETSTypeReferencePart", + "name": { + "type": "Identifier", + "name": "ExportDefaultClass", + "decorators": [], + "loc": { + "start": { + "line": 19, + "column": 19 + }, + "end": { + "line": 19, + "column": 37 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 19 + }, + "end": { + "line": 19, + "column": 39 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 19 + }, + "end": { + "line": 19, + "column": 39 + } + } + }, + "decorators": [], + "loc": { + "start": { + "line": 19, + "column": 7 + }, + "end": { + "line": 19, + "column": 17 + } + } + }, + "init": { + "type": "ETSNewClassInstanceExpression", + "typeReference": { + "type": "ETSTypeReference", + "part": { + "type": "ETSTypeReferencePart", + "name": { + "type": "Identifier", + "name": "ExportDefaultClass", + "decorators": [], + "loc": { + "start": { + "line": 19, + "column": 44 + }, + "end": { + "line": 19, + "column": 62 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 44 + }, + "end": { + "line": 19, + "column": 63 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 44 + }, + "end": { + "line": 19, + "column": 63 + } + } + }, + "arguments": [], + "loc": { + "start": { + "line": 19, + "column": 40 + }, + "end": { + "line": 19, + "column": 65 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 7 + }, + "end": { + "line": 19, + "column": 65 + } + } + } + ], + "kind": "let", + "loc": { + "start": { + "line": 19, + "column": 3 + }, + "end": { + "line": 19, + "column": 65 + } + } + } + ], + "loc": { + "start": { + "line": 18, + "column": 17 + }, + "end": { + "line": 20, + "column": 2 + } + } + }, + "loc": { + "start": { + "line": 18, + "column": 14 + }, + "end": { + "line": 20, + "column": 2 + } + } + }, + "loc": { + "start": { + "line": 18, + "column": 14 + }, + "end": { + "line": 20, + "column": 2 + } + } + }, + "overloads": [], + "decorators": [], + "loc": { + "start": { + "line": 18, + "column": 1 + }, + "end": { + "line": 20, + "column": 2 + } + } + } + ], + "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": 21, + "column": 1 + } + } +} diff --git a/ets2panda/test/parser/ets/import_tests/check_exported_default_class.ets b/ets2panda/test/parser/ets/import_tests/check_exported_default_class.ets new file mode 100644 index 0000000000000000000000000000000000000000..03c02fe1ec2157fb38b15c0536a251300e284f19 --- /dev/null +++ b/ets2panda/test/parser/ets/import_tests/check_exported_default_class.ets @@ -0,0 +1,20 @@ +/* + * 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. + */ + +import ExportDefaultClass from "import_tests/modules/struct_default_module"; + +function main() { + let test_class: ExportDefaultClass = new ExportDefaultClass(); +} diff --git a/ets2panda/test/parser/ets/import_tests/check_exported_default_struct-expected.txt b/ets2panda/test/parser/ets/import_tests/check_exported_default_struct-expected.txt index 574e2ca98e6617a8e74b2962893aefe199d9d74e..c3916604dc2b91f9ba83fd474431eeb1dac23898 100644 --- a/ets2panda/test/parser/ets/import_tests/check_exported_default_struct-expected.txt +++ b/ets2panda/test/parser/ets/import_tests/check_exported_default_struct-expected.txt @@ -443,4 +443,3 @@ } } } -SyntaxError: Cannot find default imported element in the target [check_exported_default_struct.ets:16:33] diff --git a/ets2panda/test/parser/ets/import_tests/modules/class_default_module-expected.txt b/ets2panda/test/parser/ets/import_tests/modules/class_default_module-expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..03868c42f84d31d9909f3581ff6d7dbf48b8c043 --- /dev/null +++ b/ets2panda/test/parser/ets/import_tests/modules/class_default_module-expected.txt @@ -0,0 +1,290 @@ +{ + "type": "Program", + "statements": [ + { + "type": "ClassDeclaration", + "definition": { + "id": { + "type": "Identifier", + "name": "ExportDefaultClass", + "decorators": [], + "loc": { + "start": { + "line": 16, + "column": 22 + }, + "end": { + "line": 16, + "column": 40 + } + } + }, + "superClass": null, + "implements": [], + "body": [ + { + "type": "MethodDefinition", + "key": { + "type": "Identifier", + "name": "constructor", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "kind": "constructor", + "static": false, + "optional": false, + "computed": false, + "value": { + "type": "FunctionExpression", + "function": { + "type": "ScriptFunction", + "id": { + "type": "Identifier", + "name": "constructor", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "generator": false, + "async": false, + "expression": false, + "params": [], + "body": { + "type": "BlockStatement", + "statements": [], + "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 + } + } + }, + "overloads": [], + "decorators": [], + "loc": { + "start": { + "line": 16, + "column": 43 + }, + "end": { + "line": 16, + "column": 43 + } + } + } + ], + "loc": { + "start": { + "line": 16, + "column": 41 + }, + "end": { + "line": 16, + "column": 43 + } + } + }, + "loc": { + "start": { + "line": 16, + "column": 16 + }, + "end": { + "line": 16, + "column": 43 + } + } + }, + { + "type": "ClassDeclaration", + "definition": { + "id": { + "type": "Identifier", + "name": "ETSGLOBAL", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "superClass": null, + "implements": [], + "body": [ + { + "type": "MethodDefinition", + "key": { + "type": "Identifier", + "name": "_$init$_", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "kind": "method", + "accessibility": "public", + "static": true, + "optional": false, + "computed": false, + "value": { + "type": "FunctionExpression", + "function": { + "type": "ScriptFunction", + "id": { + "type": "Identifier", + "name": "_$init$_", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "generator": false, + "async": false, + "expression": false, + "params": [], + "body": { + "type": "BlockStatement", + "statements": [], + "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 + } + } + }, + "overloads": [], + "decorators": [], + "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 + } + } + } + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 17, + "column": 1 + } + } +} diff --git a/ets2panda/test/parser/ets/import_tests/modules/class_default_module.ets b/ets2panda/test/parser/ets/import_tests/modules/class_default_module.ets new file mode 100644 index 0000000000000000000000000000000000000000..a45efed7cd8ed9e86928fdee107e5fc83108d1de --- /dev/null +++ b/ets2panda/test/parser/ets/import_tests/modules/class_default_module.ets @@ -0,0 +1,16 @@ +/* + * 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. + */ + +export default class ExportDefaultClass {} diff --git a/ets2panda/test/parser/ets/import_tests/modules/too_many_default_exports-expected.txt b/ets2panda/test/parser/ets/import_tests/modules/too_many_default_exports-expected.txt index 2e053464dcbdb8995b7ba7494775cc2a9080ddba..cbe1ad1976360842392a169ca3257c41f6189dca 100644 --- a/ets2panda/test/parser/ets/import_tests/modules/too_many_default_exports-expected.txt +++ b/ets2panda/test/parser/ets/import_tests/modules/too_many_default_exports-expected.txt @@ -1 +1 @@ -SyntaxError: Only one default export is allowed in a module [too_many_default_exports.ets:18:1] +SyntaxError: Only one default export is allowed in a module [too_many_default_exports.ets:17:16] diff --git a/ets2panda/test/parser/ets/import_tests/modules/too_many_default_exports_2-expected.txt b/ets2panda/test/parser/ets/import_tests/modules/too_many_default_exports_2-expected.txt index cc262638677ef5aedb72b461c9e9db4e671fb6da..286371d4e678e0daf56cc6ef689e020591ec9a97 100644 --- a/ets2panda/test/parser/ets/import_tests/modules/too_many_default_exports_2-expected.txt +++ b/ets2panda/test/parser/ets/import_tests/modules/too_many_default_exports_2-expected.txt @@ -1 +1 @@ -SyntaxError: Only one default export is allowed in a module [too_many_default_exports_2.ets:16:32] +SyntaxError: Only one default export is allowed in a module [too_many_default_exports_2.ets:16:16] diff --git a/ets2panda/varbinder/scope.cpp b/ets2panda/varbinder/scope.cpp index 7d1e614da8bbcaec66a418514e48f1aa1ffdeb9c..f8b00268beb00e7086ff27168b4cd0b4c45406d5 100644 --- a/ets2panda/varbinder/scope.cpp +++ b/ets2panda/varbinder/scope.cpp @@ -477,11 +477,8 @@ Scope::InsertResult GlobalScope::InsertImpl(const util::StringView &name, Variab if (!is_dynamic && is_foreign && !var->Declaration()->Name().Is(compiler::Signatures::ETS_GLOBAL)) { const auto *const node = var->Declaration()->Node(); - if (const bool exported = node->IsClassDefinition() ? node->Parent()->IsExported() : node->IsExported(); - !exported) { - if (!node->IsDefaultExported()) { - return Scope::InsertResult {Bindings().end(), false}; - } + if (!(node->IsExported() || node->IsDefaultExported())) { + return Scope::InsertResult {Bindings().end(), false}; } }