From cbd5710b22876a625aaccb2b237b66d6320f0464 Mon Sep 17 00:00:00 2001 From: Tatiana Pivovarova Date: Thu, 29 Feb 2024 14:14:58 +0300 Subject: [PATCH] Fix codecheck in ets2panda Signed-off-by: Tatiana Pivovarova --- ets2panda/compiler/core/ETSCompiler.cpp | 138 ++++++++++-------- ets2panda/compiler/core/ETSCompiler.h | 5 + .../compiler/lowering/ets/opAssignment.cpp | 100 ++++++------- ets2panda/varbinder/ETSBinder.cpp | 128 ++++++++-------- ets2panda/varbinder/ETSBinder.h | 6 + 5 files changed, 210 insertions(+), 167 deletions(-) diff --git a/ets2panda/compiler/core/ETSCompiler.cpp b/ets2panda/compiler/core/ETSCompiler.cpp index c95eceb6d3..06220722ed 100644 --- a/ets2panda/compiler/core/ETSCompiler.cpp +++ b/ets2panda/compiler/core/ETSCompiler.cpp @@ -965,76 +965,62 @@ static bool CompileComputed(compiler::ETSGen *etsg, const ir::MemberExpression * return false; } -void ETSCompiler::Compile(const ir::MemberExpression *expr) const +void ETSCompiler::CompilePropertyLength(const ir::MemberExpression *expr, const checker::Type *objectType) const { ETSGen *etsg = GetETSGen(); - auto lambda = etsg->VarBinder()->LambdaObjects().find(expr); - if (lambda != etsg->VarBinder()->LambdaObjects().end()) { - etsg->CreateLambdaObjectFromMemberReference(expr, expr->object_, lambda->second.first); - etsg->SetAccumulatorType(expr->TsType()); - return; - } - - compiler::RegScope rs(etsg); - - auto *const objectType = - checker::ETSChecker::GetApparentType(etsg->Checker()->GetNonNullishType(expr->Object()->TsType())); - - if (CompileComputed(etsg, expr)) { - return; - } + auto ottctx = compiler::TargetTypeContext(etsg, objectType); + etsg->CompileAndCheck(expr->Object()); - auto &propName = expr->Property()->AsIdentifier()->Name(); + auto const loadLength = [expr, etsg]() { + compiler::VReg objReg = etsg->AllocReg(); + etsg->StoreAccumulator(expr, objReg); - if (objectType->IsETSArrayType() && propName.Is("length")) { - auto ottctx = compiler::TargetTypeContext(etsg, objectType); - etsg->CompileAndCheck(expr->Object()); + auto ttctx = compiler::TargetTypeContext(etsg, expr->OptionalType()); + etsg->LoadArrayLength(expr, objReg); + etsg->ApplyConversion(expr, expr->TsType()); + }; - auto const loadLength = [expr, etsg]() { - compiler::VReg objReg = etsg->AllocReg(); - etsg->StoreAccumulator(expr, objReg); + etsg->EmitMaybeOptional(expr, loadLength, expr->IsOptional()); +} - auto ttctx = compiler::TargetTypeContext(etsg, expr->OptionalType()); - etsg->LoadArrayLength(expr, objReg); - etsg->ApplyConversion(expr, expr->TsType()); - }; +void ETSCompiler::CompilePropertyEnum(const ir::MemberExpression *expr, const checker::Type *objectType) const +{ + ETSGen *etsg = GetETSGen(); + auto const *const enumInterface = [objectType, expr]() -> checker::ETSEnumInterface const * { + if (objectType->IsETSEnumType()) { + return expr->OptionalType()->AsETSEnumType(); + } + return expr->OptionalType()->AsETSStringEnumType(); + }(); - etsg->EmitMaybeOptional(expr, loadLength, expr->IsOptional()); - return; - } + auto ttctx = compiler::TargetTypeContext(etsg, expr->OptionalType()); + etsg->LoadAccumulatorInt(expr, enumInterface->GetOrdinal()); +} - if (objectType->IsETSEnumType() || objectType->IsETSStringEnumType()) { - auto const *const enumInterface = [objectType, expr]() -> checker::ETSEnumInterface const * { - if (objectType->IsETSEnumType()) { - return expr->OptionalType()->AsETSEnumType(); - } - return expr->OptionalType()->AsETSStringEnumType(); - }(); +void ETSCompiler::CompilePropertyStatic(const ir::MemberExpression *expr, const util::StringView &name) const +{ + ETSGen *etsg = GetETSGen(); + auto ttctx = compiler::TargetTypeContext(etsg, expr->OptionalType()); - auto ttctx = compiler::TargetTypeContext(etsg, expr->OptionalType()); - etsg->LoadAccumulatorInt(expr, enumInterface->GetOrdinal()); + if (expr->PropVar()->TsType()->HasTypeFlag(checker::TypeFlag::GETTER_SETTER)) { + checker::Signature *sig = expr->PropVar()->TsType()->AsETSFunctionType()->FindGetter(); + etsg->CallStatic0(expr, sig->InternalName()); + etsg->SetAccumulatorType(expr->TsType()); return; } - if (etsg->Checker()->IsVariableStatic(expr->PropVar())) { - auto ttctx = compiler::TargetTypeContext(etsg, expr->OptionalType()); - - if (expr->PropVar()->TsType()->HasTypeFlag(checker::TypeFlag::GETTER_SETTER)) { - checker::Signature *sig = expr->PropVar()->TsType()->AsETSFunctionType()->FindGetter(); - etsg->CallStatic0(expr, sig->InternalName()); - etsg->SetAccumulatorType(expr->TsType()); - return; - } - - util::StringView fullName = etsg->FormClassPropReference(expr->Object()->TsType()->AsETSObjectType(), propName); - etsg->LoadStaticProperty(expr, expr->OptionalType(), fullName); - return; - } + util::StringView fullName = etsg->FormClassPropReference(expr->Object()->TsType()->AsETSObjectType(), name); + etsg->LoadStaticProperty(expr, expr->OptionalType(), fullName); +} +void ETSCompiler::CompileProperty(const ir::MemberExpression *expr, const checker::Type *objectType, + const util::StringView &name) const +{ + ETSGen *etsg = GetETSGen(); auto ottctx = compiler::TargetTypeContext(etsg, expr->Object()->TsType()); etsg->CompileAndCheck(expr->Object()); - auto const loadProperty = [expr, etsg, propName, objectType]() { + auto const loadProperty = [expr, etsg, name, objectType]() { etsg->ApplyConversion(expr->Object()); compiler::VReg objReg = etsg->AllocReg(); etsg->StoreAccumulator(expr, objReg); @@ -1045,11 +1031,11 @@ void ETSCompiler::Compile(const ir::MemberExpression *expr) const checker::Signature *sig = expr->PropVar()->TsType()->AsETSFunctionType()->FindGetter(); etsg->CallThisVirtual0(expr, objReg, sig->InternalName()); } else if (objectType->IsETSDynamicType()) { - etsg->LoadPropertyDynamic(expr, expr->OptionalType(), objReg, propName); + etsg->LoadPropertyDynamic(expr, expr->OptionalType(), objReg, name); } else if (objectType->IsETSUnionType()) { - etsg->LoadUnionProperty(expr, expr->OptionalType(), objReg, propName); + etsg->LoadUnionProperty(expr, expr->OptionalType(), objReg, name); } else { - const auto fullName = etsg->FormClassPropReference(objectType->AsETSObjectType(), propName); + const auto fullName = etsg->FormClassPropReference(objectType->AsETSObjectType(), name); etsg->LoadProperty(expr, expr->OptionalType(), objReg, fullName); } etsg->GuardUncheckedType(expr, expr->UncheckedType(), expr->OptionalType()); @@ -1058,6 +1044,44 @@ void ETSCompiler::Compile(const ir::MemberExpression *expr) const etsg->EmitMaybeOptional(expr, loadProperty, expr->IsOptional()); } +void ETSCompiler::Compile(const ir::MemberExpression *expr) const +{ + ETSGen *etsg = GetETSGen(); + auto lambda = etsg->VarBinder()->LambdaObjects().find(expr); + if (lambda != etsg->VarBinder()->LambdaObjects().end()) { + etsg->CreateLambdaObjectFromMemberReference(expr, expr->object_, lambda->second.first); + etsg->SetAccumulatorType(expr->TsType()); + return; + } + + compiler::RegScope rs(etsg); + + auto *const objectType = + checker::ETSChecker::GetApparentType(etsg->Checker()->GetNonNullishType(expr->Object()->TsType())); + + if (CompileComputed(etsg, expr)) { + return; + } + + auto &propName = expr->Property()->AsIdentifier()->Name(); + + if (objectType->IsETSArrayType() && propName.Is("length")) { + CompilePropertyLength(expr, objectType); + return; + } + + if (objectType->IsETSEnumType() || objectType->IsETSStringEnumType()) { + CompilePropertyEnum(expr, objectType); + return; + } + + if (etsg->Checker()->IsVariableStatic(expr->PropVar())) { + CompilePropertyStatic(expr, propName); + return; + } + CompileProperty(expr, objectType, propName); +} + void ETSCompiler::Compile([[maybe_unused]] const ir::NewExpression *expr) const { UNREACHABLE(); diff --git a/ets2panda/compiler/core/ETSCompiler.h b/ets2panda/compiler/core/ETSCompiler.h index 5421dde41f..e97b2e5e0e 100644 --- a/ets2panda/compiler/core/ETSCompiler.h +++ b/ets2panda/compiler/core/ETSCompiler.h @@ -39,6 +39,11 @@ private: void CompileCastUnboxable(const ir::TSAsExpression *expr) const; void CompileCast(const ir::TSAsExpression *expr) const; void EmitCall(const ir::CallExpression *expr, compiler::VReg &calleeReg, bool isStatic) const; + void CompilePropertyLength(const ir::MemberExpression *expr, const checker::Type *objectType) const; + void CompilePropertyEnum(const ir::MemberExpression *expr, const checker::Type *objectType) const; + void CompilePropertyStatic(const ir::MemberExpression *expr, const util::StringView &name) const; + void CompileProperty(const ir::MemberExpression *expr, const checker::Type *objectType, + const util::StringView &name) const; ETSGen *GetETSGen() const; }; diff --git a/ets2panda/compiler/lowering/ets/opAssignment.cpp b/ets2panda/compiler/lowering/ets/opAssignment.cpp index 9b4bfa606b..547faa5bfd 100644 --- a/ets2panda/compiler/lowering/ets/opAssignment.cpp +++ b/ets2panda/compiler/lowering/ets/opAssignment.cpp @@ -71,8 +71,24 @@ static lexer::TokenType OpEqualToOp(const lexer::TokenType opEqual) UNREACHABLE(); } -void AdjustBoxingUnboxingFlags(ir::Expression *newExpr, const ir::Expression *oldExpr) +void AdjustBoxingUnboxingFlags(ir::Expression *loweringResult, const ir::Expression *oldExpr) { + // Adjust [un]boxing flag + ir::AssignmentExpression *newExpr = nullptr; + if (loweringResult->IsAssignmentExpression()) { + newExpr = loweringResult->AsAssignmentExpression(); + } else if (loweringResult->IsBlockExpression() && !loweringResult->AsBlockExpression()->Statements().empty()) { + auto *statement = loweringResult->AsBlockExpression()->Statements().back(); + if (statement->IsExpressionStatement() && + statement->AsExpressionStatement()->GetExpression()->IsAssignmentExpression()) { + newExpr = statement->AsExpressionStatement()->GetExpression()->AsAssignmentExpression(); + } else { + UNREACHABLE(); + } + } else { + UNREACHABLE(); + } + // NOTE: gogabr. make sure that the checker never puts both a boxing and an unboxing flag on the same node. // Then this function will become unnecessary. const ir::BoxingUnboxingFlags oldBoxingFlag {oldExpr->GetBoxingUnboxingFlags() & @@ -87,6 +103,37 @@ void AdjustBoxingUnboxingFlags(ir::Expression *newExpr, const ir::Expression *ol } } +static ir::OpaqueTypeNode *CreateProxyTypeNode(checker::ETSChecker *checker, ir::Expression *expr) +{ + auto *lcType = expr->TsType(); + if (auto *lcTypeAsPrimitive = checker->ETSBuiltinTypeAsPrimitiveType(lcType); lcTypeAsPrimitive != nullptr) { + lcType = lcTypeAsPrimitive; + } + return checker->AllocNode(lcType); +} + +static std::string GenerateStringForLoweredAssignment(lexer::TokenType opEqual, bool hasProperty, ir::Expression *expr) +{ + std::string leftHand = "@@I5"; + std::string rightHand = "@@I7"; + + if (hasProperty) { + auto const kind = expr->AsMemberExpression()->Kind(); + if (kind == ir::MemberExpressionKind::PROPERTY_ACCESS) { + leftHand += ".@@I6"; + rightHand += ".@@I8"; + } else if (kind == ir::MemberExpressionKind::ELEMENT_ACCESS) { + leftHand += "[@@I6]"; + rightHand += "[@@I8]"; + } else { + UNREACHABLE(); + } + } + + return leftHand + " = (" + rightHand + ' ' + std::string {lexer::TokenToString(OpEqualToOp(opEqual))} + + " (@@E9)) as @@T10"; +} + ir::Expression *HandleOpAssignment(public_lib::Context *ctx, checker::ETSChecker *checker, parser::ETSParser *parser, ir::AssignmentExpression *assignment) { @@ -137,31 +184,10 @@ ir::Expression *HandleOpAssignment(public_lib::Context *ctx, checker::ETSChecker } // Create proxy TypeNode for left hand of assignment expression - auto *lcType = left->TsType(); - if (auto *lcTypeAsPrimitive = checker->ETSBuiltinTypeAsPrimitiveType(lcType); lcTypeAsPrimitive != nullptr) { - lcType = lcTypeAsPrimitive; - } - auto *exprType = checker->AllocNode(lcType); + auto *exprType = CreateProxyTypeNode(checker, left); // Generate ArkTS code string for new lowered assignment expression: - std::string leftHand = "@@I5"; - std::string rightHand = "@@I7"; - - if (ident2 != nullptr) { - if (auto const kind = left->AsMemberExpression()->Kind(); kind == ir::MemberExpressionKind::PROPERTY_ACCESS) { - leftHand += ".@@I6"; - rightHand += ".@@I8"; - } else if (kind == ir::MemberExpressionKind::ELEMENT_ACCESS) { - leftHand += "[@@I6]"; - rightHand += "[@@I8]"; - } else { - UNREACHABLE(); - } - } - - newAssignmentStatements += leftHand + " = (" + rightHand + ' ' + - std::string {lexer::TokenToString(OpEqualToOp(opEqual))} + " (@@E9)) as @@T10"; - // std::cout << "Lowering statements: " << new_assignment_statements << std::endl; + newAssignmentStatements += GenerateStringForLoweredAssignment(opEqual, ident2 != nullptr, left); // Parse ArkTS code string and create and process corresponding AST node(s) auto expressionCtx = varbinder::LexicalScope::Enter(checker->VarBinder(), scope); @@ -176,31 +202,7 @@ ir::Expression *HandleOpAssignment(public_lib::Context *ctx, checker::ETSChecker checker->VarBinder()->AsETSBinder()->ResolveReferencesForScope(loweringResult, scope); loweringResult->Check(checker); - // Adjust [un]boxing flag - ir::AssignmentExpression *newAssignment; - if (loweringResult->IsAssignmentExpression()) { - newAssignment = loweringResult->AsAssignmentExpression(); - } else if (loweringResult->IsBlockExpression() && !loweringResult->AsBlockExpression()->Statements().empty() && - loweringResult->AsBlockExpression()->Statements().back()->IsExpressionStatement() && - loweringResult->AsBlockExpression() - ->Statements() - .back() - ->AsExpressionStatement() - ->GetExpression() - ->IsAssignmentExpression()) { - newAssignment = loweringResult->AsBlockExpression() - ->Statements() - .back() - ->AsExpressionStatement() - ->GetExpression() - ->AsAssignmentExpression(); - } else { - UNREACHABLE(); - } - - // NOTE(gogabr): make sure that the checker never puts both a boxing and an unboxing flag on the same node. - // Then this code will become unnecessary. - AdjustBoxingUnboxingFlags(newAssignment, assignment); + AdjustBoxingUnboxingFlags(loweringResult, assignment); return loweringResult; } diff --git a/ets2panda/varbinder/ETSBinder.cpp b/ets2panda/varbinder/ETSBinder.cpp index 3df41bd5eb..dadbb26a27 100644 --- a/ets2panda/varbinder/ETSBinder.cpp +++ b/ets2panda/varbinder/ETSBinder.cpp @@ -479,6 +479,24 @@ void ETSBinder::ImportAllForeignBindings(ir::AstNode *const specifier, } } +void ETSBinder::AddSpecifiers(const ir::ETSImportDeclaration *decl, const util::StringView &specifierName, + ir::StringLiteral *dirName, std::unordered_set &exportedNames, + const lexer::SourcePosition &pos) +{ + for (auto it : decl->Specifiers()) { + if (it->IsImportNamespaceSpecifier() && !specifierName.Empty()) { + std::cerr << "Warning: import with alias cannot be used with re-export\n"; + continue; + } + + AddSpecifiersToTopBindings(it, decl, dirName); + if (it->IsImportSpecifier() && !exportedNames.insert(it->AsImportSpecifier()->Local()->Name().Mutf8()).second) { + ThrowError(pos, "Ambiguous import \"" + it->AsImportSpecifier()->Local()->Name().Mutf8() + + "\" has multiple matching exports"); + } + } +} + bool ETSBinder::AddImportNamespaceSpecifiersToTopBindings(ir::AstNode *const specifier, const varbinder::Scope::VariableMap &globalBindings, const parser::Program *const importProgram, @@ -507,23 +525,9 @@ bool ETSBinder::AddImportNamespaceSpecifiersToTopBindings(ir::AstNode *const spe // clang-format on dirName.SetStart(item->GetETSImportDeclarations()->Source()->Start()); - for (auto it : item->GetETSImportDeclarations()->Specifiers()) { - if (it->IsImportNamespaceSpecifier() && - !specifier->AsImportNamespaceSpecifier()->Local()->Name().Empty()) { - std::cerr << "Warning: import with alias cannot be used with re-export\n"; - continue; - } - - AddSpecifiersToTopBindings(it, item->GetETSImportDeclarations(), - dirName.Str().Is(".") ? item->GetETSImportDeclarations()->Source() - : &dirName); - if (it->IsImportSpecifier() && - !exportedNames.insert(it->AsImportSpecifier()->Local()->Name().Mutf8()).second) { - ThrowError(import->Start(), "Ambiguous import \"" + - it->AsImportSpecifier()->Local()->Name().Mutf8() + - "\" has multiple matching exports"); - } - } + auto *dirPath = dirName.Str().Is(".") ? item->GetETSImportDeclarations()->Source() : &dirName; + AddSpecifiers(item->GetETSImportDeclarations(), specifier->AsImportNamespaceSpecifier()->Local()->Name(), + dirPath, exportedNames, import->Start()); } } @@ -559,6 +563,26 @@ Variable *ETSBinder::FindImportSpecifiersVariable(const util::StringView &import return foundVar->second; } +const util::StringView &ETSBinder::GetLocalName(const ir::ImportSpecifier *importSpecifier, + const util::StringView &imported, + const ir::StringLiteral *const importPath) +{ + if (importSpecifier->Local() != nullptr) { + auto fnc = [&importPath, &imported](const auto &savedSpecifier) { + return importPath->Str() != savedSpecifier.first && imported == savedSpecifier.second; + }; + if (!std::any_of(importSpecifiers_.begin(), importSpecifiers_.end(), fnc)) { + TopScope()->EraseBinding(imported); + } + + importSpecifiers_.push_back(std::make_pair(importPath->Str(), imported)); + + return importSpecifier->Local()->Name(); + } + + return imported; +} + bool ETSBinder::AddImportSpecifiersToTopBindings(ir::AstNode *const specifier, const varbinder::Scope::VariableMap &globalBindings, const ir::ETSImportDeclaration *const import, @@ -578,31 +602,13 @@ bool ETSBinder::AddImportSpecifiersToTopBindings(ir::AstNode *const specifier, }; const auto *const importSpecifier = specifier->AsImportSpecifier(); - if (!importSpecifier->Imported()->IsIdentifier()) { return true; } const auto &imported = importSpecifier->Imported()->AsIdentifier()->Name(); - auto *const var = FindImportSpecifiersVariable(imported, globalBindings, recordRes); - - const auto &localName = [this, importSpecifier, &imported, &importPath]() { - if (importSpecifier->Local() != nullptr) { - auto fnc = [&importPath, &imported](const auto &savedSpecifier) { - return importPath->Str() != savedSpecifier.first && imported == savedSpecifier.second; - }; - if (!std::any_of(importSpecifiers_.begin(), importSpecifiers_.end(), fnc)) { - TopScope()->EraseBinding(imported); - } - - importSpecifiers_.push_back(std::make_pair(importPath->Str(), imported)); - - return importSpecifier->Local()->Name(); - } - - return imported; - }(); + const auto &localName = GetLocalName(importSpecifier, imported, importPath); if (var == nullptr) { for (auto item : ReExportImports()) { @@ -681,6 +687,30 @@ ArenaVector ETSBinder::GetExternalProgram(const util::StringV return recordRes->second; } +util::StringView ETSBinder::GetSourceName(const ir::ETSImportDeclaration *const import, ir::StringLiteral *path) +{ + if (import->Module() == nullptr) { + return path->Str(); + } + char pathDelimiter = panda::os::file::File::GetPathDelim().at(0); + auto strImportPath = path->Str().Mutf8(); + if (strImportPath.find(pathDelimiter) == (strImportPath.size() - 1)) { + return util::UString(strImportPath + import->Module()->Str().Mutf8(), Allocator()).View(); + } + + std::string importFilePath; + if (!import->Source()->Str().Is(path->Str().Mutf8()) && !import->Source()->Str().Empty() && + import->Source()->Str().Mutf8().substr(0, 1) == ".") { + importFilePath = import->Source()->Str().Mutf8().substr(import->Source()->Str().Mutf8().find_first_not_of('.')); + if (importFilePath.size() == 1) { + importFilePath = ""; + } + } + + return util::UString(strImportPath + importFilePath + pathDelimiter + import->Module()->Str().Mutf8(), Allocator()) + .View(); +} + void ETSBinder::AddSpecifiersToTopBindings(ir::AstNode *const specifier, const ir::ETSImportDeclaration *const import, ir::StringLiteral *path, std::vector viewedReExport) @@ -692,31 +722,7 @@ void ETSBinder::AddSpecifiersToTopBindings(ir::AstNode *const specifier, const i return; } - const util::StringView sourceName = [import, importPath, this, &path]() { - if (import->Module() == nullptr) { - return importPath->Str(); - } - char pathDelimiter = panda::os::file::File::GetPathDelim().at(0); - auto strImportPath = importPath->Str().Mutf8(); - if (strImportPath.find(pathDelimiter) == (strImportPath.size() - 1)) { - return util::UString(strImportPath + import->Module()->Str().Mutf8(), Allocator()).View(); - } - - std::string importFilePath; - if (!import->Source()->Str().Is(path->Str().Mutf8()) && !import->Source()->Str().Empty() && - import->Source()->Str().Mutf8().substr(0, 1) == ".") { - importFilePath = - import->Source()->Str().Mutf8().substr(import->Source()->Str().Mutf8().find_first_not_of('.')); - if (importFilePath.size() == 1) { - importFilePath = ""; - } - } - - return util::UString(strImportPath + importFilePath + pathDelimiter + import->Module()->Str().Mutf8(), - Allocator()) - .View(); - }(); - + auto sourceName = GetSourceName(import, path); auto record = GetExternalProgram(sourceName, importPath); const auto *const importProgram = record.front(); const auto *const importGlobalScope = importProgram->GlobalScope(); diff --git a/ets2panda/varbinder/ETSBinder.h b/ets2panda/varbinder/ETSBinder.h index edd27f9a43..4835adf9cd 100644 --- a/ets2panda/varbinder/ETSBinder.h +++ b/ets2panda/varbinder/ETSBinder.h @@ -137,10 +137,16 @@ public: const varbinder::Scope::VariableMap &globalBindings, const ArenaVector &recordRes); Variable *FindStaticBinding(const ArenaVector &recordRes, const ir::StringLiteral *importPath); + util::StringView GetSourceName(const ir::ETSImportDeclaration *import, ir::StringLiteral *path); + const util::StringView &GetLocalName(const ir::ImportSpecifier *importSpecifier, const util::StringView &imported, + const ir::StringLiteral *importPath); void AddSpecifiersToTopBindings( ir::AstNode *specifier, const ir::ETSImportDeclaration *import, ir::StringLiteral *path, std::vector viewedReExport = std::vector()); void AddDynamicSpecifiersToTopBindings(ir::AstNode *specifier, const ir::ETSImportDeclaration *import); + void AddSpecifiers(const ir::ETSImportDeclaration *decl, const util::StringView &specifierName, + ir::StringLiteral *dirName, std::unordered_set &exportedNames, + const lexer::SourcePosition &pos); void ResolveInterfaceDeclaration(ir::TSInterfaceDeclaration *decl); void ResolveMethodDefinition(ir::MethodDefinition *methodDef); -- Gitee