From 14c0cb16637fd9cc4c34680eff8fa5df74004007 Mon Sep 17 00:00:00 2001 From: xuxinjie4 Date: Sat, 23 Aug 2025 17:19:28 +0800 Subject: [PATCH] Fix dynamic import bug Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICUGMP?from=project-issue Signed-off-by: xuxinjie4 --- ets2panda/checker/ets/object.cpp | 3 +- .../lowering/ets/objectLiteralLowering.cpp | 4 +- .../compiler/ets/import_export/re_export.ets | 16 + .../dynamic_optional_interface.ets | 22 + .../modules/module-expected.txt | 430 +++++++++++++++++- .../dynamic_import_tests/modules/module.ets | 4 + ets2panda/varbinder/ETSBinder.cpp | 22 +- 7 files changed, 485 insertions(+), 16 deletions(-) create mode 100644 ets2panda/test/ast/compiler/ets/import_export/re_export.ets create mode 100644 ets2panda/test/ast/parser/ets/dynamic_import_tests/dynamic_optional_interface.ets diff --git a/ets2panda/checker/ets/object.cpp b/ets2panda/checker/ets/object.cpp index cd9dfa1e3d..03ff57a90c 100644 --- a/ets2panda/checker/ets/object.cpp +++ b/ets2panda/checker/ets/object.cpp @@ -475,8 +475,7 @@ Type *ETSChecker::MaybeGradualType(ir::AstNode *node, ETSObjectType *type) ES2PANDA_ASSERT(node->IsClassDefinition() || node->IsTSInterfaceDeclaration()); auto isDynamic = node->IsClassDefinition() ? node->AsClassDefinition()->Language().IsDynamic() : node->AsTSInterfaceDeclaration()->Language().IsDynamic(); - // Temporary solution, the struct loses 'language' while being converted to a class through the plugin API. - return isDynamic || Program()->IsDeclForDynamicStaticInterop() ? CreateGradualType(type) : type; + return isDynamic ? CreateGradualType(type) : type; } Type *ETSChecker::BuildBasicInterfaceProperties(ir::TSInterfaceDeclaration *interfaceDecl) diff --git a/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp b/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp index 0a17664868..a497f98264 100644 --- a/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp +++ b/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp @@ -326,7 +326,9 @@ static ir::AstNode *HandleDynamicObjectLiteralLowering(public_lib::Context *ctx, ss << "@@I" << genSymId << ".setProperty('" << property->AsProperty()->Key()->DumpEtsSrc() << "', ESValue.wrap(@@E" << valueId << "));"; } - blockStatements.push_back(parser->CreateFormattedStatement(ss.str(), args)); + if (!objExpr->Properties().empty()) { + blockStatements.push_back(parser->CreateFormattedStatement(ss.str(), args)); + } blockStatements.push_back(parser->CreateFormattedStatement("@@I1.unwrap();", gensym->Clone(allocator, nullptr))); auto *blockExpr = util::NodeAllocator::ForceSetParent(allocator, std::move(blockStatements)); blockExpr->SetParent(objExpr->Parent()); diff --git a/ets2panda/test/ast/compiler/ets/import_export/re_export.ets b/ets2panda/test/ast/compiler/ets/import_export/re_export.ets new file mode 100644 index 0000000000..7988832afb --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/import_export/re_export.ets @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2025 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 { A, A as B } from "./eitest_export_A_1" \ No newline at end of file diff --git a/ets2panda/test/ast/parser/ets/dynamic_import_tests/dynamic_optional_interface.ets b/ets2panda/test/ast/parser/ets/dynamic_import_tests/dynamic_optional_interface.ets new file mode 100644 index 0000000000..460ece0428 --- /dev/null +++ b/ets2panda/test/ast/parser/ets/dynamic_import_tests/dynamic_optional_interface.ets @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024-2025 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. + */ + +/*--- +flags: [dynamic-ast] +---*/ + +import { OptionalInterface } from "dynamic_import_tests/modules/module" + +let a: OptionalInterface = {} diff --git a/ets2panda/test/parser/ets/dynamic_import_tests/modules/module-expected.txt b/ets2panda/test/parser/ets/dynamic_import_tests/modules/module-expected.txt index 0248d70963..8f50df5c43 100644 --- a/ets2panda/test/parser/ets/dynamic_import_tests/modules/module-expected.txt +++ b/ets2panda/test/parser/ets/dynamic_import_tests/modules/module-expected.txt @@ -3975,8 +3975,8 @@ "program": "module.ets" }, "end": { - "line": 61, - "column": 2, + "line": 63, + "column": 7, "program": "module.ets" } } @@ -3988,7 +3988,429 @@ "program": "module.ets" }, "end": { - "line": 61, + "line": 63, + "column": 7, + "program": "module.ets" + } + } + }, + { + "type": "TSInterfaceDeclaration", + "body": { + "type": "TSInterfaceBody", + "body": [ + { + "type": "MethodDefinition", + "key": { + "type": "Identifier", + "name": "s", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1, + "program": null + }, + "end": { + "line": 1, + "column": 1, + "program": null + } + } + }, + "kind": "get", + "accessibility": "public", + "static": false, + "optional": false, + "computed": false, + "value": { + "type": "FunctionExpression", + "function": { + "type": "ScriptFunction", + "id": { + "type": "Identifier", + "name": "s", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1, + "program": "module.ets" + }, + "end": { + "line": 1, + "column": 1, + "program": "module.ets" + } + } + }, + "generator": false, + "async": false, + "expression": false, + "params": [], + "returnType": { + "type": "ETSUnionType", + "types": [ + { + "type": "ETSTypeReference", + "part": { + "type": "ETSTypeReferencePart", + "name": { + "type": "Identifier", + "name": "string", + "decorators": [], + "loc": { + "start": { + "line": 64, + "column": 9, + "program": "module.ets" + }, + "end": { + "line": 64, + "column": 15, + "program": "module.ets" + } + } + }, + "loc": { + "start": { + "line": 64, + "column": 9, + "program": "module.ets" + }, + "end": { + "line": 64, + "column": 15, + "program": "module.ets" + } + } + }, + "loc": { + "start": { + "line": 64, + "column": 9, + "program": "module.ets" + }, + "end": { + "line": 64, + "column": 15, + "program": "module.ets" + } + } + }, + { + "type": "ETSUndefinedType", + "loc": { + "start": { + "line": 1, + "column": 1, + "program": null + }, + "end": { + "line": 1, + "column": 1, + "program": null + } + } + } + ], + "loc": { + "start": { + "line": 1, + "column": 1, + "program": "module.ets" + }, + "end": { + "line": 1, + "column": 1, + "program": "module.ets" + } + } + }, + "loc": { + "start": { + "line": 64, + "column": 5, + "program": "module.ets" + }, + "end": { + "line": 64, + "column": 15, + "program": "module.ets" + } + } + }, + "loc": { + "start": { + "line": 64, + "column": 5, + "program": "module.ets" + }, + "end": { + "line": 64, + "column": 15, + "program": "module.ets" + } + } + }, + "overloads": [ + { + "type": "MethodDefinition", + "key": { + "type": "Identifier", + "name": "s", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1, + "program": null + }, + "end": { + "line": 1, + "column": 1, + "program": null + } + } + }, + "kind": "set", + "accessibility": "public", + "static": false, + "optional": false, + "computed": false, + "value": { + "type": "FunctionExpression", + "function": { + "type": "ScriptFunction", + "id": { + "type": "Identifier", + "name": "s", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1, + "program": "module.ets" + }, + "end": { + "line": 1, + "column": 1, + "program": "module.ets" + } + } + }, + "generator": false, + "async": false, + "expression": false, + "params": [ + { + "type": "ETSParameterExpression", + "name": { + "type": "Identifier", + "name": "s", + "typeAnnotation": { + "type": "ETSUnionType", + "types": [ + { + "type": "ETSTypeReference", + "part": { + "type": "ETSTypeReferencePart", + "name": { + "type": "Identifier", + "name": "string", + "decorators": [], + "loc": { + "start": { + "line": 64, + "column": 9, + "program": "module.ets" + }, + "end": { + "line": 64, + "column": 15, + "program": "module.ets" + } + } + }, + "loc": { + "start": { + "line": 64, + "column": 9, + "program": "module.ets" + }, + "end": { + "line": 64, + "column": 15, + "program": "module.ets" + } + } + }, + "loc": { + "start": { + "line": 64, + "column": 9, + "program": "module.ets" + }, + "end": { + "line": 64, + "column": 15, + "program": "module.ets" + } + } + }, + { + "type": "ETSUndefinedType", + "loc": { + "start": { + "line": 1, + "column": 1, + "program": "module.ets" + }, + "end": { + "line": 1, + "column": 1, + "program": "module.ets" + } + } + } + ], + "loc": { + "start": { + "line": 1, + "column": 1, + "program": "module.ets" + }, + "end": { + "line": 1, + "column": 1, + "program": "module.ets" + } + } + }, + "decorators": [], + "loc": { + "start": { + "line": 64, + "column": 5, + "program": "module.ets" + }, + "end": { + "line": 64, + "column": 6, + "program": "module.ets" + } + } + }, + "loc": { + "start": { + "line": 64, + "column": 5, + "program": "module.ets" + }, + "end": { + "line": 64, + "column": 6, + "program": "module.ets" + } + } + } + ], + "loc": { + "start": { + "line": 64, + "column": 5, + "program": "module.ets" + }, + "end": { + "line": 64, + "column": 15, + "program": "module.ets" + } + } + }, + "loc": { + "start": { + "line": 64, + "column": 5, + "program": "module.ets" + }, + "end": { + "line": 64, + "column": 15, + "program": "module.ets" + } + } + }, + "overloads": [], + "decorators": [], + "loc": { + "start": { + "line": 64, + "column": 5, + "program": "module.ets" + }, + "end": { + "line": 64, + "column": 15, + "program": "module.ets" + } + } + } + ], + "decorators": [], + "loc": { + "start": { + "line": 64, + "column": 5, + "program": "module.ets" + }, + "end": { + "line": 64, + "column": 15, + "program": "module.ets" + } + } + } + ], + "loc": { + "start": { + "line": 63, + "column": 43, + "program": "module.ets" + }, + "end": { + "line": 65, + "column": 2, + "program": "module.ets" + } + } + }, + "id": { + "type": "Identifier", + "name": "OptionalInterface", + "decorators": [], + "loc": { + "start": { + "line": 63, + "column": 26, + "program": "module.ets" + }, + "end": { + "line": 63, + "column": 43, + "program": "module.ets" + } + } + }, + "extends": [], + "loc": { + "start": { + "line": 63, + "column": 16, + "program": "module.ets" + }, + "end": { + "line": 65, "column": 2, "program": "module.ets" } @@ -4781,7 +5203,7 @@ "program": "module.ets" }, "end": { - "line": 61, + "line": 65, "column": 2, "program": "module.ets" } diff --git a/ets2panda/test/parser/ets/dynamic_import_tests/modules/module.ets b/ets2panda/test/parser/ets/dynamic_import_tests/modules/module.ets index 0c92660de3..e36d742f6a 100644 --- a/ets2panda/test/parser/ets/dynamic_import_tests/modules/module.ets +++ b/ets2panda/test/parser/ets/dynamic_import_tests/modules/module.ets @@ -58,4 +58,8 @@ export declare namespace ns { export declare class MyError extends Error { mycode: number +} + +export declare interface OptionalInterface{ + s?: string } \ No newline at end of file diff --git a/ets2panda/varbinder/ETSBinder.cpp b/ets2panda/varbinder/ETSBinder.cpp index 9305148e05..69dc12ca24 100644 --- a/ets2panda/varbinder/ETSBinder.cpp +++ b/ets2panda/varbinder/ETSBinder.cpp @@ -812,7 +812,8 @@ std::pair ETSBinder::FindImportDeclIn const ir::ETSImportDeclaration *const import, [[maybe_unused]] const util::StringView &imported, const ir::StringLiteral *const importPath) { - auto importMapIter = selectiveExportAliasMultimap_.find(import->ImportMetadata().resolvedSource); + auto sourcePath = import->ImportMetadata().HasSpecifiedDeclPath() ? import->DeclPath() : import->ResolvedSource(); + auto importMapIter = selectiveExportAliasMultimap_.find(sourcePath); if (importMapIter == selectiveExportAliasMultimap_.end()) { return std::make_pair(nullptr, nullptr); } @@ -821,7 +822,7 @@ std::pair ETSBinder::FindImportDeclIn return std::make_pair(nullptr, nullptr); } auto [localName, declNode] = pairIter->second; - const auto records = GetExternalProgram(import->ImportMetadata().resolvedSource, importPath); + const auto records = GetExternalProgram(sourcePath, importPath); if (records.empty()) { return std::make_pair(nullptr, nullptr); } @@ -993,7 +994,8 @@ bool ETSBinder::AddImportSpecifiersToTopBindings(Span re } } - util::StringView nameToSearchFor = FindNameInAliasMap(import->ResolvedSource(), imported); + auto sourcePath = import->ImportMetadata().HasSpecifiedDeclPath() ? import->DeclPath() : import->ResolvedSource(); + util::StringView nameToSearchFor = FindNameInAliasMap(sourcePath, imported); if (nameToSearchFor.Empty()) { nameToSearchFor = imported; } @@ -1480,19 +1482,21 @@ void ETSBinder::ValidateReexportDeclaration(ir::ETSReExportDeclaration *decl) // Example: export {foo} from "./A" if (specifier->IsImportSpecifier()) { auto importSpecifier = specifier->AsImportSpecifier(); - const auto reexported = importSpecifier->Imported()->Name(); + auto imported = importSpecifier->Imported(); + auto local = importSpecifier->Local(); + auto *const var = ValidateImportSpecifier(importSpecifier, import); if (var == nullptr) { - ThrowError(import->Start(), diagnostic::EXPORT_INCORRECT, {reexported}); + ThrowError(import->Start(), diagnostic::EXPORT_INCORRECT, {imported->Name()}); continue; } - importSpecifier->Imported()->SetVariable(var); - importSpecifier->Local()->SetVariable(var); + imported->SetVariable(var); + local->SetVariable(var); // Remember reexported name to check for ambiguous reexports - if (!reexportedNames_.insert(reexported).second) { - ThrowError(import->Start(), diagnostic::AMBIGUOUS_EXPORT, {reexported}); + if (!reexportedNames_.insert(imported->Name()).second && local->Name() == imported->Name()) { + ThrowError(import->Start(), diagnostic::AMBIGUOUS_EXPORT, {imported->Name()}); continue; } } -- Gitee