From 7947b4a4815c79778f51c6435d359402d5aa0bf2 Mon Sep 17 00:00:00 2001 From: Shimenkov Mikhail Date: Mon, 30 Jun 2025 19:01:31 +0300 Subject: [PATCH] Fixng bytecode degradation Current ets2panda implementation brings bytecode degradation in case of common access in union Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICIYU1 Tests: ninja all tests Signed-off-by: Shimenkov Mikhail Change-Id: Id537c6a1d7a1b82fa1249b40b24f114e696dea71 --- ets2panda/compiler/core/ETSCompiler.cpp | 11 +- ets2panda/compiler/core/ETSGen.h | 2 +- .../compiler/lowering/ets/unionLowering.cpp | 184 ++++++++++++------ ets2panda/test/CMakeLists.txt | 1 + ets2panda/test/unit/CMakeLists.txt | 4 + .../unit/union_common_property_access.cpp | 169 ++++++++++++++++ ets2panda/test/utils/asm_test.cpp | 5 +- 7 files changed, 302 insertions(+), 74 deletions(-) create mode 100644 ets2panda/test/unit/union_common_property_access.cpp diff --git a/ets2panda/compiler/core/ETSCompiler.cpp b/ets2panda/compiler/core/ETSCompiler.cpp index 15549ccb50..678963c7d6 100644 --- a/ets2panda/compiler/core/ETSCompiler.cpp +++ b/ets2panda/compiler/core/ETSCompiler.cpp @@ -731,13 +731,13 @@ void ETSCompiler::EmitCall(const ir::CallExpression *expr, compiler::VReg &calle } else if (expr->Callee()->IsMemberExpression()) { auto me = expr->Callee()->AsMemberExpression(); auto obj = me->Object(); + auto *objType = etsg->Checker()->GetApparentType(me->Object()->TsType()); if (obj->IsSuperExpression()) { etsg->CallExact(expr, signature, calleeReg, expr->Arguments()); // NOTE: need to refactor: type of member expression object can be obtained via // me->ObjType() or me->Object()->TsType() and they may differ!!!! - } else if (me->ObjType() == etsg->Checker()->GlobalETSObjectType() && - (etsg->Checker()->GetApparentType(me->Object()->TsType()) != nullptr) && - (etsg->Checker()->GetApparentType(me->Object()->TsType())->IsETSUnionType())) { + } else if (me->ObjType() == etsg->Checker()->GlobalETSObjectType() && (objType != nullptr) && + (objType->IsETSUnionType())) { etsg->CallByName(expr, signature, calleeReg, expr->Arguments()); } else { etsg->CallVirtual(expr, signature, calleeReg, expr->Arguments()); @@ -928,8 +928,7 @@ void ETSCompiler::Compile(const ir::MemberExpression *expr) const return; } - auto *const objectType = etsg->Checker()->GetApparentType(expr->Object()->TsType()); - auto &propName = expr->Property()->AsIdentifier()->Name(); + auto *objectType = etsg->Checker()->GetApparentType(expr->Object()->TsType()); auto ottctx = compiler::TargetTypeContext(etsg, expr->Object()->TsType()); etsg->CompileAndCheck(expr->Object()); @@ -951,7 +950,7 @@ void ETSCompiler::Compile(const ir::MemberExpression *expr) const } else if (objectType->IsETSUnionType()) { etsg->LoadPropertyByName(expr, objReg, checker::ETSChecker::FormNamedAccessMetadata(expr->PropVar())); } else { - const auto fullName = etsg->FormClassPropReference(objectType->AsETSObjectType(), propName); + const auto fullName = etsg->FormClassPropReference(expr->PropVar()); etsg->LoadProperty(expr, variableType, objReg, fullName); } etsg->GuardUncheckedType(expr, expr->UncheckedType(), expr->TsType()); diff --git a/ets2panda/compiler/core/ETSGen.h b/ets2panda/compiler/core/ETSGen.h index 1fd7c62350..137992f21f 100644 --- a/ets2panda/compiler/core/ETSGen.h +++ b/ets2panda/compiler/core/ETSGen.h @@ -64,6 +64,7 @@ public: void StoreStaticOwnProperty(const ir::AstNode *node, const checker::Type *propType, const util::StringView &name); [[nodiscard]] util::StringView FormClassPropReference(const checker::ETSObjectType *classType, const util::StringView &name); + util::StringView FormClassPropReference(varbinder::Variable const *var); void StoreProperty(const ir::AstNode *node, const checker::Type *propType, VReg objReg, const util::StringView &name); @@ -478,7 +479,6 @@ private: void StringBuilderAppend(const ir::AstNode *node, VReg builder); void AppendString(const ir::Expression *binExpr, VReg builder); void StringBuilder(const ir::Expression *left, const ir::Expression *right, VReg builder); - util::StringView FormClassPropReference(varbinder::Variable const *var); void UnaryMinus(const ir::AstNode *node); void UnaryTilde(const ir::AstNode *node); diff --git a/ets2panda/compiler/lowering/ets/unionLowering.cpp b/ets2panda/compiler/lowering/ets/unionLowering.cpp index aba0545179..31bab6cff0 100644 --- a/ets2panda/compiler/lowering/ets/unionLowering.cpp +++ b/ets2panda/compiler/lowering/ets/unionLowering.cpp @@ -36,7 +36,7 @@ std::string GetAccessClassName(const checker::ETSUnionType *unionType) { std::stringstream ss; ss << PREFIX; - unionType->ToString(ss, false); + ss << unionType->ToStringAsSrc(); std::string res(ss.str()); std::replace(res.begin(), res.end(), '.', '-'); std::replace(res.begin(), res.end(), '|', '_'); @@ -44,50 +44,63 @@ std::string GetAccessClassName(const checker::ETSUnionType *unionType) return res; } -static ir::ClassDefinition *GetUnionAccessClass(public_lib::Context *ctx, varbinder::VarBinder *varbinder, - std::string const &name) +static ir::ClassDefinition *GetOrCreateNamedAccessClass(public_lib::Context *ctx, varbinder::VarBinder *vbind, + const checker::ETSUnionType *unionType) { auto *checker = ctx->GetChecker()->AsETSChecker(); auto *allocator = ctx->Allocator(); + + auto className = util::UString(GetAccessClassName(unionType), allocator); + auto globScope = vbind->Program()->GlobalClassScope(); // Create the name for the synthetic class node - if (auto foundVar = checker->Scope()->FindLocal(util::StringView(name), varbinder::ResolveBindingOptions::BINDINGS); + if (auto foundVar = globScope->FindLocal(className.View(), varbinder::ResolveBindingOptions::DECLARATION); foundVar != nullptr) { return foundVar->Declaration()->Node()->AsClassDefinition(); } - util::UString unionFieldClassName(util::StringView(name), allocator); - auto *ident = ctx->AllocNode(unionFieldClassName.View(), allocator); - auto [decl, var] = varbinder->NewVarDecl(ident->Start(), ident->Name()); - ident->SetVariable(var); - - auto classCtx = varbinder::LexicalScope(varbinder); - auto *classDef = ctx->AllocNode(ctx->Allocator(), ident, ir::ClassDefinitionModifiers::GLOBAL, - ir::ModifierFlags::ABSTRACT, Language(Language::Id::ETS)); - ES2PANDA_ASSERT(classDef != nullptr); - classDef->SetScope(classCtx.GetScope()); + + auto *ident = ctx->AllocNode(className.View(), allocator); + + auto *classDef = + ctx->AllocNode(ctx->Allocator(), ident, ir::ClassDefinitionModifiers::CLASS_DECL, + ir::ModifierFlags::ABSTRACT, Language(Language::Id::ETS)); auto *classDecl = ctx->AllocNode(classDef, allocator); - classDef->Scope()->BindNode(classDecl->Definition()); - decl->BindNode(classDef); - var->SetScope(classDef->Scope()); - varbinder->AsETSBinder()->BuildClassDefinition(classDef); + vbind->Program()->Ast()->AddStatement(classDecl); + + { + auto clsCtx = varbinder::LexicalScope::Enter(vbind, globScope); + CheckLoweredNode(vbind->AsETSBinder(), checker, classDecl); + } - auto globalBlock = varbinder->Program()->Ast(); - classDecl->SetParent(globalBlock); - globalBlock->AddStatement(classDecl); - classDecl->Check(checker); return classDef; } -static std::tuple CreateNamedAccessMethod( - public_lib::Context *ctx, varbinder::VarBinder *varbinder, ir::MemberExpression *expr, - checker::Signature *signature) +static varbinder::LocalVariable *CheckIfPropertyExists(const checker::Type *t, util::StringView name) +{ + if (t == nullptr || !t->IsETSObjectType()) { + return nullptr; + } + + return t->AsETSObjectType()->GetProperty(name, checker::PropertySearchFlags::SEARCH_INSTANCE_METHOD | + checker::PropertySearchFlags::SEARCH_INSTANCE_FIELD | + checker::PropertySearchFlags::SEARCH_IN_BASE | + checker::PropertySearchFlags::DISALLOW_SYNTHETIC_METHOD_CREATION); +} + +static varbinder::LocalVariable *CreateNamedAccessMethod(public_lib::Context *ctx, varbinder::VarBinder *vbind, + ir::MemberExpression *expr, checker::Signature *signature) { auto *allocator = ctx->Allocator(); auto *checker = ctx->GetChecker()->AsETSChecker(); + auto methodName = expr->Property()->AsIdentifier()->Name(); auto unionType = checker->GetApparentType(checker->GetNonNullishType(expr->Object()->TsType()))->AsETSUnionType(); - auto *const accessClass = GetUnionAccessClass(ctx, varbinder, GetAccessClassName(unionType)); - auto methodName = expr->TsType()->AsETSFunctionType()->Name(); + + auto accessClass = GetOrCreateNamedAccessClass(ctx, vbind, unionType); + auto accessClassScope = accessClass->Scope()->AsClassScope(); + if (auto var = CheckIfPropertyExists(accessClass->TsType(), methodName); var != nullptr) { + return var; + } // Create method name for synthetic class auto *methodIdent = ctx->AllocNode(methodName, allocator); @@ -116,67 +129,80 @@ static std::tuple CreateNamedA ir::ModifierFlags::PUBLIC | ir::ModifierFlags::ABSTRACT, allocator, false); ArenaVector methodDecl {allocator->Adapter()}; methodDecl.push_back(method); - accessClass->AddProperties(std::move(methodDecl)); + accessClassScope->Node()->AsClassDefinition()->AddProperties(std::move(methodDecl)); { - auto clsCtx = - varbinder::LexicalScope::Enter(varbinder, accessClass->Scope()->AsClassScope()); - auto boundCtx = varbinder::BoundContext(varbinder->AsETSBinder()->GetRecordTable(), accessClass, true); - CheckLoweredNode(varbinder->AsETSBinder(), checker, method); + auto clsCtx = varbinder::LexicalScope::Enter(vbind, accessClassScope); + varbinder::BoundContext boundCtx {vbind->AsETSBinder()->GetRecordTable(), accessClass, true}; + CheckLoweredNode(vbind->AsETSBinder(), checker, method); } - return {method->Id()->Variable()->AsLocalVariable(), - method->TsType()->AsETSFunctionType()->CallSignatures().front()}; + auto res = method->Id()->Variable()->AsLocalVariable(); + accessClass->TsType()->AsETSObjectType()->AddProperty(res); + return res; } -static varbinder::LocalVariable *CreateNamedAccessProperty(public_lib::Context *ctx, varbinder::VarBinder *varbinder, - ir::MemberExpression *expr) +static varbinder::LocalVariable *CreateNamedAccessField(public_lib::Context *ctx, varbinder::VarBinder *vbind, + ir::MemberExpression *expr) { auto *const allocator = ctx->Allocator(); auto *checker = ctx->GetChecker()->AsETSChecker(); + auto fieldName = expr->Property()->AsIdentifier()->Name(); auto unionType = checker->GetApparentType(checker->GetNonNullishType(expr->Object()->TsType()))->AsETSUnionType(); - auto *const accessClass = GetUnionAccessClass(ctx, varbinder, GetAccessClassName(unionType)); - auto propName = expr->Property()->AsIdentifier()->Name(); + + auto accessClass = GetOrCreateNamedAccessClass(ctx, vbind, unionType); + auto accessClassScope = accessClass->Scope()->AsClassScope(); + if (auto var = CheckIfPropertyExists(accessClass->TsType(), fieldName); var != nullptr) { + return var; + } + auto fieldType = expr->TsType(); auto uncheckedType = expr->UncheckedType(); auto *typeToSet = uncheckedType == nullptr ? fieldType : uncheckedType; + auto typeAnno = ctx->AllocNode(typeToSet, allocator); // Create field name for synthetic class - auto *fieldIdent = ctx->AllocNode(propName, allocator); + auto *fieldIdent = ctx->AllocNode(fieldName, allocator); // Create the synthetic class property node auto *field = - ctx->AllocNode(fieldIdent, nullptr, nullptr, ir::ModifierFlags::NONE, allocator, false); - ES2PANDA_ASSERT(field != nullptr); - // Add the declaration to the scope - auto [decl, var] = varbinder->NewVarDecl(fieldIdent->Start(), fieldIdent->Name()); - var->AddFlag(varbinder::VariableFlags::PROPERTY); - var->SetTsType(typeToSet); - fieldIdent->SetVariable(var); - field->SetTsType(typeToSet); - decl->BindNode(field); + ctx->AllocNode(fieldIdent, nullptr, typeAnno, ir::ModifierFlags::NONE, allocator, false); ArenaVector fieldDecl {allocator->Adapter()}; fieldDecl.push_back(field); accessClass->AddProperties(std::move(fieldDecl)); - return var->AsLocalVariable(); + + { + auto clsCtx = varbinder::LexicalScope::Enter(vbind, accessClassScope); + CheckLoweredNode(vbind->AsETSBinder(), checker, field); + } + + auto res = field->Key()->Variable()->AsLocalVariable(); + accessClass->TsType()->AsETSObjectType()->AddProperty(res); + return res; } static varbinder::LocalVariable *CreateNamedAccess(public_lib::Context *ctx, varbinder::VarBinder *varbinder, ir::MemberExpression *expr) { auto type = expr->TsType(); - auto name = expr->Property()->AsIdentifier()->Name(); + auto propName = expr->Property()->AsIdentifier()->Name(); auto *checker = ctx->GetChecker()->AsETSChecker(); - auto *apparentType = checker->GetApparentType(checker->GetNonNullishType(expr->Object()->TsType())); - ES2PANDA_ASSERT(apparentType != nullptr); - auto unionType = apparentType->AsETSUnionType(); - auto *const accessClass = GetUnionAccessClass(ctx, varbinder, GetAccessClassName(unionType)); - auto *classScope = accessClass->Scope()->AsClassScope(); - - if (auto *var = classScope->FindLocal(name, varbinder::ResolveBindingOptions::ALL_NON_STATIC); var != nullptr) { - return var->AsLocalVariable(); + + auto unionType = checker->GetApparentType(checker->GetNonNullishType(expr->Object()->TsType()))->AsETSUnionType(); + + if (auto found = CheckIfPropertyExists(unionType->GetAssemblerLUB(), propName); found != nullptr) { + auto foundType = found->TsType(); + // Update signatures of call expression since current signature belongs to one of the constituent types + // of the union type + if (foundType->IsETSMethodType() && !foundType->IsETSArrowType()) { + auto parent = expr->Parent()->AsCallExpression(); + if (parent->Callee() == expr) { + parent->SetSignature(foundType->AsETSFunctionType()->CallSignatures().front()); + } + } + return found; } // arrow type fields should be processed as property access not method invocation @@ -184,14 +210,41 @@ static varbinder::LocalVariable *CreateNamedAccess(public_lib::Context *ctx, var auto parent = expr->Parent()->AsCallExpression(); ES2PANDA_ASSERT(parent->Callee() == expr && parent->Signature()->HasFunction()); - auto [var, sig] = CreateNamedAccessMethod(ctx, varbinder, expr, parent->Signature()); + auto var = CreateNamedAccessMethod(ctx, varbinder, expr, parent->Signature()); + // Update signatures of call expression since current signature belongs to one of the constituent types + // of the union type + auto sig = var->TsType()->AsETSFunctionType()->CallSignatures().front(); parent->AsCallExpression()->SetSignature(sig); return var; } - // Enter the union filed class instance field scope - auto fieldCtx = varbinder::LexicalScope::Enter(varbinder, classScope->InstanceFieldScope()); - return CreateNamedAccessProperty(ctx, varbinder, expr); + return CreateNamedAccessField(ctx, varbinder, expr); +} + +static void UpdateObjectEffectiveRuntimeRepresentation(ir::MemberExpression *expr) +{ + auto propVar = expr->PropVar(); + auto containingClass = propVar->Declaration()->Node()->AsClassElement()->Parent(); + // In case of 'abstract class $NamedAccessMeta...' + if (containingClass->IsAbstract()) { + // Nothing to update + // Object type is etsUnionType + // 'ets.call.name'/'ldobj' will be generated + return; + } + + auto getType = [](ir::AstNode *node) -> checker::ETSObjectType * { + if (node->IsTSInterfaceBody()) { + return node->Parent()->AsTSInterfaceDeclaration()->TsType()->AsETSObjectType(); + } else if (node->IsClassDefinition()) { + return node->AsClassDefinition()->TsType()->AsETSObjectType(); + } + ES2PANDA_UNREACHABLE(); + }; + + // Object type is etsObjectType + // 'call.virt'/'ldobj' will be generated + expr->Object()->SetTsType(getType(containingClass)); } static void HandleUnionPropertyAccess(public_lib::Context *ctx, varbinder::VarBinder *vbind, ir::MemberExpression *expr) @@ -201,8 +254,11 @@ static void HandleUnionPropertyAccess(public_lib::Context *ctx, varbinder::VarBi } [[maybe_unused]] auto const *const parent = expr->Parent(); - expr->SetPropVar(CreateNamedAccess(ctx, vbind, expr)); - ES2PANDA_ASSERT(expr->PropVar() != nullptr); + auto var = CreateNamedAccess(ctx, vbind, expr); + ES2PANDA_ASSERT(var != nullptr); + expr->SetPropVar(var); + // Update object type in order to generate optimal bytecode + UpdateObjectEffectiveRuntimeRepresentation(expr); } bool UnionLowering::PerformForModule(public_lib::Context *ctx, parser::Program *program) diff --git a/ets2panda/test/CMakeLists.txt b/ets2panda/test/CMakeLists.txt index 7694d4828f..2684371a51 100644 --- a/ets2panda/test/CMakeLists.txt +++ b/ets2panda/test/CMakeLists.txt @@ -73,6 +73,7 @@ function(ets2panda_add_gtest TARGET) ${PANDA_SANITIZERS_LIST} ${ARG_UNPARSED_ARGUMENTS} ) + panda_target_compile_definitions(${TARGET} PRIVATE -DBINARY_ROOT="${CMAKE_BINARY_DIR}/bin") endfunction(ets2panda_add_gtest) diff --git a/ets2panda/test/unit/CMakeLists.txt b/ets2panda/test/unit/CMakeLists.txt index 78467d1449..e4719f99f1 100644 --- a/ets2panda/test/unit/CMakeLists.txt +++ b/ets2panda/test/unit/CMakeLists.txt @@ -67,3 +67,7 @@ add_subdirectory(ets_specific_optimizer) ets2panda_add_gtest(es2panda_checker_tests CPP_SOURCES checker_test.cpp ) + +ets2panda_add_gtest(es2panda_union_common_property_access_test + CPP_SOURCES union_common_property_access.cpp +) diff --git a/ets2panda/test/unit/union_common_property_access.cpp b/ets2panda/test/unit/union_common_property_access.cpp new file mode 100644 index 0000000000..75fe14ff42 --- /dev/null +++ b/ets2panda/test/unit/union_common_property_access.cpp @@ -0,0 +1,169 @@ +/** + * 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. + */ + +#include "test/utils/asm_test.h" + +namespace ark::es2panda::compiler::test { + +class UnionCommonPropertyAccessTest : public ::test::utils::AsmTest { +protected: + pandasm::Ins *FindInst(pandasm::Function *func, pandasm::Opcode opcode) + { + auto &ins = func->ins; + auto found = std::find_if(ins.begin(), ins.end(), [opcode](pandasm::Ins &i) { return i.opcode == opcode; }); + return (found != ins.end()) ? &*(found) : nullptr; + } + + bool HasId(pandasm::Ins *ins, std::string_view sig) + { + auto &ids = ins->ids; + auto found = std::find_if(ids.begin(), ids.end(), [sig](std::string &id) { return id == sig; }); + return found != ids.end(); + } +}; + +TEST_F(UnionCommonPropertyAccessTest, CommonBaseClass1) +{ + std::string_view src = R"( + class A { + meth(): number { return 1 } + fld: number = 2 + } + + class B1 extends A { + meth(): number { return 3 } + fld: number = 4 + } + + class B2 extends A { + meth(): number { return 5 } + fld: number = 6 + } + + function foo(a: B1|B2) { + return a.fld + a.meth(); + } + )"; + + auto program = GetCurrentProgram(src); + ASSERT_NE(program, nullptr); + + auto func = GetFunction("ETSGLOBAL.foo:A;f64;", program->functionStaticTable); + ASSERT_NE(func, nullptr); + + auto ldobj = FindInst(func, pandasm::Opcode::LDOBJ_64); + ASSERT_NE(ldobj, nullptr); + ASSERT_TRUE(HasId(ldobj, "A.fld")); + + auto call = FindInst(func, pandasm::Opcode::CALL_VIRT_SHORT); + ASSERT_NE(call, nullptr); + ASSERT_TRUE(HasId(call, "A.meth:f64;")); +} + +TEST_F(UnionCommonPropertyAccessTest, CommonBaseClass2) +{ + std::string_view src = R"( + class A { + meth(): number { return 1 } + fld: number = 2 + } + + class B extends A { + meth(): number { return 3 } + fld: number = 4 + } + + function foo(a: B|B) { + return a.fld + a.meth(); + } + )"; + + auto program = GetCurrentProgram(src); + ASSERT_NE(program, nullptr); + + auto func = GetFunction("ETSGLOBAL.foo:B;f64;", program->functionStaticTable); + ASSERT_NE(func, nullptr); + + auto ldobj = FindInst(func, pandasm::Opcode::LDOBJ_64); + ASSERT_NE(ldobj, nullptr); + ASSERT_TRUE(HasId(ldobj, "B.fld")); + + auto call = FindInst(func, pandasm::Opcode::CALL_VIRT_SHORT); + ASSERT_NE(call, nullptr); + ASSERT_TRUE(HasId(call, "B.meth:f64;")); +} + +TEST_F(UnionCommonPropertyAccessTest, CommonBaseClass3) +{ + std::string_view src = R"( + class A { + meth(): number { return 1 } + fld: number = 2 + } + + class B1 extends A { + meth(): number { return 3 } + fld: number = 4 + } + + class B2 extends A { + meth(): number { return 5 } + fld: number = 6 + } + + function foo(a: B1|B2) { + return a.fld + a.meth(); + } + )"; + + auto program = GetCurrentProgram(src); + ASSERT_NE(program, nullptr); + + auto func = GetFunction("ETSGLOBAL.foo:std.core.Object;f64;", program->functionStaticTable); + ASSERT_NE(func, nullptr); + + auto ldobj = FindInst(func, pandasm::Opcode::ETS_LDOBJ_NAME_64); + ASSERT_NE(ldobj, nullptr); + ASSERT_TRUE(HasId(ldobj, "$NamedAccessMeta-B1_B2.fld")); + + auto call = FindInst(func, pandasm::Opcode::ETS_CALL_NAME_SHORT); + ASSERT_NE(call, nullptr); + ASSERT_TRUE(HasId(call, "$NamedAccessMeta-B1_B2.meth:f64;")); +} + +TEST_F(UnionCommonPropertyAccessTest, CommonBaseClass4) +{ + std::string_view src = R"( + + class A {} + class B {} + + function foo(a: A|B) { + return a.toString(); + } + )"; + + auto program = GetCurrentProgram(src); + ASSERT_NE(program, nullptr); + + auto func = GetFunction("ETSGLOBAL.foo:std.core.Object;std.core.String;", program->functionStaticTable); + ASSERT_NE(func, nullptr); + + auto call = FindInst(func, pandasm::Opcode::CALL_VIRT_SHORT); + ASSERT_NE(call, nullptr); + ASSERT_TRUE(HasId(call, "std.core.Object.toString:std.core.String;")); +} + +} // namespace ark::es2panda::compiler::test diff --git a/ets2panda/test/utils/asm_test.cpp b/ets2panda/test/utils/asm_test.cpp index 1f8e1c6d59..7e96bee547 100644 --- a/ets2panda/test/utils/asm_test.cpp +++ b/ets2panda/test/utils/asm_test.cpp @@ -289,7 +289,7 @@ void AsmTest::CheckClassFieldWithoutAnnotations(ark::pandasm::Program *program, void AsmTest::SetCurrentProgram(std::string_view src) { int argc = 1; - const char *argv = "../../../../bin/es2panda"; // NOLINT(modernize-avoid-c-arrays) + const char *argv = BINARY_ROOT "/es2panda"; // NOLINT(modernize-avoid-c-arrays) static constexpr std::string_view FILE_NAME = "dummy.ets"; program_ = GetProgram(argc, &argv, FILE_NAME, src); @@ -299,8 +299,7 @@ void AsmTest::SetCurrentProgram(std::string_view src) std::unique_ptr AsmTest::GetCurrentProgram(std::string_view src) { static constexpr std::string_view FILE_NAME = "dummy.ets"; - std::array args = {"../../../../../bin/es2panda", - "--ets-unnamed"}; // NOLINT(modernize-avoid-c-arrays) + std::array args = {BINARY_ROOT "/es2panda", "--ets-unnamed"}; // NOLINT(modernize-avoid-c-arrays) auto program = GetProgram(args.size(), args.data(), FILE_NAME, src); return program; -- Gitee