diff --git a/libabckit/tests/BUILD.gn b/libabckit/tests/BUILD.gn index 20c72fde83242eb9b7eac38a6a5709f6299add16..2e3257ee4a7a1401736ce4010bbdcf944530efd8 100644 --- a/libabckit/tests/BUILD.gn +++ b/libabckit/tests/BUILD.gn @@ -246,6 +246,7 @@ abckit_gtests_sources = [ "wrong_mode_tests/wrong_mode_tests_IsaApiDynamicImpl_0.cpp", "wrong_mode_tests/wrong_mode_tests_IsaApiDynamicImpl_1.cpp", "wrong_mode_tests/wrong_mode_tests_IsaApiStaticImpl_0.cpp", + "$target_gen_dir/adapter_static/generated/get_intrinsic_id_static_test.cpp", ] clean_scenario_js_from_ets_files = [ @@ -877,6 +878,15 @@ libabckit_host_unittest_action("abckit_mock_gtests") { deps = [ "$abckit_root/src:libabckit_mock" ] } +ark_gen_file("get_intrinsic_id_static_inc_test") { + extra_dependencies = + [ "$ark_root_static/runtime:arkruntime_gen_intrinsics_yaml" ] + template_file = "adapter_static/templates/get_intrinsic_id_static_test.inc.erb" + data_file = "$target_gen_dir/../../static_core/runtime/intrinsics.yaml" + requires = [ "$ark_root_static/runtime/templates/intrinsics.rb" ] + output_file = "$target_gen_dir/adapter_static/generated/get_intrinsic_id_static_test.cpp" +} + libabckit_host_unittest_action("abckit_gtests") { module_out_path = module_output_path @@ -895,6 +905,7 @@ libabckit_host_unittest_action("abckit_gtests") { deps = [ ":abckit_ets_vm_helpers", ":abckit_js_vm_helpers", + ":get_intrinsic_id_static_inc_test", "$abckit_root/src:libabckit", ] @@ -953,6 +964,7 @@ libabckit_host_unittest_action("arklink_strip_unused_code_gtests") { ":abckit_ets_vm_helpers", ":abckit_gtests_action", ":abckit_js_vm_helpers", + ":get_intrinsic_id_static_inc_test", "$abckit_root/src:libabckit", ] diff --git a/libabckit/tests/adapter_static/templates/get_intrinsic_id_static_test.inc.erb b/libabckit/tests/adapter_static/templates/get_intrinsic_id_static_test.inc.erb new file mode 100755 index 0000000000000000000000000000000000000000..9a639ce4ef371a7d5f54f07aa0af65666690cc45 --- /dev/null +++ b/libabckit/tests/adapter_static/templates/get_intrinsic_id_static_test.inc.erb @@ -0,0 +1,215 @@ +/* + * 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 "gtest/gtest.h" +#include +#include "libabckit/src/adapter_static/runtime_adapter_static.h" +#include "assembly-parser.h" + +namespace libabckit::test::adapter_static { +using namespace ark; +using namespace ark::panda_file; +using namespace ark::pandasm; +using namespace ark::compiler; +class RuntimeAdapterStaticTest : public testing::Test { +public: + RuntimeAdapterStaticTest() {} + ~RuntimeAdapterStaticTest() override {} + + NO_COPY_SEMANTIC(RuntimeAdapterStaticTest); + NO_MOVE_SEMANTIC(RuntimeAdapterStaticTest); +}; + +static const uint8_t *GetTypeDescriptor(const std::string &name, std::string *storage) +{ + *storage = "L" + name + ";"; + std::replace(storage->begin(), storage->end(), '.', '/'); + return utf::CStringAsMutf8(storage->c_str()); +} + +static void MainTestFunc(const std::string &source, RuntimeInterface::IntrinsicId expectId, const std::string &name) +{ + Parser p; + auto res = p.Parse(source); + EXPECT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE); + + auto pf = AsmEmitter::Emit(res.Value()); + EXPECT_NE(pf, nullptr); + + std::string descriptor; + auto class_id = pf->GetClassId(GetTypeDescriptor(name, &descriptor)); + EXPECT_TRUE(class_id.IsValid()); + EXPECT_FALSE(pf->IsExternal(class_id)); + libabckit::AbckitRuntimeAdapterStatic abckitRuntimeAdapterStatic(*pf); + panda_file::ClassDataAccessor cda(*pf, class_id); + cda.EnumerateMethods([&](MethodDataAccessor &mda) { + auto methodPtr = reinterpret_cast(mda.GetMethodId().GetOffset()); + auto intrinsicId = abckitRuntimeAdapterStatic.GetIntrinsicId(methodPtr); + EXPECT_EQ(intrinsicId, expectId); + }); +} + +TEST_F(RuntimeAdapterStaticTest, RuntimeAdapterStaticTestNormal) +{ + std::string source = R"( + .record IO {} + .function void IO.printI64(i64 a0) + )"; + MainTestFunc(source, RuntimeInterface::IntrinsicId::INTRINSIC_IO_PRINT_I64, "IO"); +} + +TEST_F(RuntimeAdapterStaticTest, RuntimeAdapterStaticTestInvalid) +{ + std::string source = R"( + .record IO {} + .function i32 IO.printU32(i32 a0) + )"; + MainTestFunc(source, RuntimeInterface::IntrinsicId::INVALID, "IO"); +} +<% +def format_params(args) + return "" if args.empty? + args.each_with_index.map { |arg, index| + if arg.include?('\\[') && !arg.include?('\\[\\]') + formatted_arg = arg.gsub('\\[', '[]') + elsif arg.include?('{Ustd.core.Null,std.core.Object}') + formatted_arg = arg.gsub('{Ustd.core.Null,std.core.Object}', '{Ustd.core.Null, std.core.Object}') + elsif arg.include?('{Ustd.core.LambdaType,std.core.MethodType}') + formatted_arg = arg.gsub('{Ustd.core.LambdaType,std.core.MethodType}', '{Ustd.core.LambdaType, std.core.MethodType}') + else + formatted_arg = arg + end + "#{formatted_arg} a#{index}" + }.join(', ') +end + +def contains_string_type(intrinsic, argsType) + ret = intrinsic.signature.ret + args = intrinsic.signature.args + all_types = [ret] + args + all_types.any? { |type| type == argsType} +end + +def format_main_test_func(args, max_length=120) + full_call = " MainTestFunc(#{args.join(', ')});" + if full_call.length <= max_length + return "#{full_call}" + end + + two_line1 = " #{args[1]}," + two_line2 = " #{args[1]}," + first_two_line = " MainTestFunc(#{args[0]}, #{args[1]}," + if two_line1.length > max_length + if two_line2.length > max_length + last_colon_pos = args[1].rindex("::") + prefix = args[1][0..last_colon_pos+1] + suffix = args[1][last_colon_pos+2..-1] + formatted = " MainTestFunc(#{args[0]},\n" + + " #{prefix}\n" + + " #{suffix},\n" + + " #{args[2]});" + elsif + formatted = " MainTestFunc(\n" + + " #{args[0]},\n" + + " #{args[1]},\n" + + " #{args[2]});" + end + elsif first_two_line.length <= max_length + formatted = " MainTestFunc(#{args[0]}, #{args[1]},\n" + + " #{args[2]});" + elsif args[1].length + args[2].length + 10 <= max_length + formatted = " MainTestFunc(#{args[0]},\n" + + " #{args[1]}, #{args[2]});" + else + formatted = " MainTestFunc(#{args[0]},\n" + + " #{args[1]},\n" + + " #{args[2]});" + end + return formatted +end + +special_types = ["panda.String", "panda.Class", "panda.Object", "std.core.String", "std.core.RuntimeLinker", "std.core.Type", + "std.core.Long", "std.core.Object", "std.core.Class", "std.core.ClassCastError", "std.core.Method", "std.core.MethodType", + "std.core.Field", "std.interop.js.JSValue", "std.core.LambdaType", "std.core.ClassType", "std.core.Job", "escompat.Array", + "escompat.BigInt", "escompat.ArrayBuffer", "std.interop.ESValue"] +special_class_method = ["std.core.String.concat2", "std.core.String.concat3", "std.core.String.concat4", "escompat.ArrayBuffer.from", + "escompat.ArrayBuffer.stringify"] +first_invalid_source = true +class_num = 0 +%> +% Runtime::intrinsics.select{ |i| i.static && !i.signature.stackrange && !i.is_stub && !i.compiler_only && i.has_impl? && i.need_nullcheck.empty? }.group_by(&:class_name).each do |clazz, intrinsics| +% intrinsics.each do |intrinsic| +TEST_F(RuntimeAdapterStaticTest, <%= intrinsic.name %>Test) +{ + std::string source = R"( +<%- + current_class_method = "#{clazz}.#{intrinsic.method_name}" + is_special = special_class_method.include?(current_class_method) +-%> +% if !is_special || (current_class_method == "escompat.ArrayBuffer.from" && intrinsic.signature.args[0] != "escompat.ArrayBuffer") + .record <%= clazz %> {} +% special_types.each do |type| +% if contains_string_type(intrinsic, type) && type != clazz + .record <%= type %> {} +% end +% end + .function <%= intrinsic.signature.ret %> <%= clazz %>.<%= intrinsic.method_name %>(<%= format_params(intrinsic.signature.args) %>) + )"; +<%= format_main_test_func(["source", "RuntimeInterface::IntrinsicId::INTRINSIC_#{intrinsic.enum_name}", "\"#{clazz}\""]) %> + + source = R"( +% end + .record <%= clazz %> {} + .function <%= intrinsic.signature.ret == "void" ? "i32" : "void" %> <%= clazz %>.<%= intrinsic.method_name %>() + )"; + MainTestFunc(source, RuntimeInterface::IntrinsicId::INVALID, "<%= clazz %>"); +} + +% end +% end +% class_names = Runtime::intrinsics.select { |i| i.static && !i.signature.stackrange && !i.is_stub && !i.compiler_only && i.has_impl? && i.need_nullcheck.empty? }.map(&:class_name).uniq +% class_groups = class_names.each_slice(7).to_a +% class_groups.each_with_index do |group, group_index| +TEST_F(RuntimeAdapterStaticTest, InvalidTest<%= group_index %>) +{ + std::string source = R"( +% group.each_with_index do |clazz, index| +% if index == 0 + .record <%= group.first %> {} +% end +% end + .function void <%= group.first %>.test() + )"; + MainTestFunc(source, RuntimeInterface::IntrinsicId::INVALID, "<%= group.first %>"); +% group.drop(1).each do |clazz| + + source = R"( + .record <%= clazz %> {} + .function void <%= clazz %>.test() + )"; + MainTestFunc(source, RuntimeInterface::IntrinsicId::INVALID, "<%= clazz %>"); +% end +% if group_index == class_groups.length - 1 + + source = R"( + .record test {} + .function void test.test() + )"; + MainTestFunc(source, RuntimeInterface::IntrinsicId::INVALID, "test"); +% end +} + +% end +} // namespace libabckit::test::adapter_static