From 5ba50226efd54576b3e1f35a2046ee539a5c5592 Mon Sep 17 00:00:00 2001 From: hushuwang Date: Thu, 12 Jun 2025 18:49:58 +0800 Subject: [PATCH] Support any bytecode Issue: https://gitee.com/openharmony/arkcompiler_runtime_core/issues/ICDT8M Signed-off-by: zhaoziming Change-Id: Ic2d89ebba0f37f23465bfe3a20453839787b2215 --- static_core/codecheck_ignore.json | 1 + .../optimizer/ir_builder/inst_builder-inl.h | 37 +- .../optimizer/ir_builder/inst_builder.h | 1 + .../optimizer/ir_builder/inst_templates.yaml | 2 + .../templates/inst_builder_gen.cpp.erb | 2 +- static_core/irtoc/scripts/interpreter.irt | 161 ++++++- static_core/isa/isa.yaml | 114 ++--- .../lowering/irtoc_function_utils.cpp | 29 +- .../plugins/ets/ets_plugin_options.yaml | 3 + .../runtime/ets_class_linker_extension.cpp | 7 + .../plugins/ets/runtime/ets_exceptions.h | 32 +- static_core/plugins/ets/runtime/ets_stubs.cpp | 350 ++++++++++++++- static_core/plugins/ets/runtime/ets_stubs.h | 21 + static_core/plugins/ets/runtime/ets_utils.cpp | 83 ++++ static_core/plugins/ets/runtime/ets_utils.h | 6 + .../interop_js/intrinsics_api_impl.cpp | 172 +++++++- .../runtime/interop_js/intrinsics_api_impl.h | 13 +- .../ets/runtime/interop_js/js_convert.h | 20 +- .../plugins/ets/runtime/interop_js/js_value.h | 67 +++ .../ets/runtime/interpreter/any_opcodes.inc | 115 +++++ .../ets/runtime/static_object_accessor.cpp | 83 +--- .../plugins/ets/runtime/types/ets_class.cpp | 3 + .../plugins/ets/runtime/types/ets_string.h | 8 +- .../cmake/interop_js_tests_arkjsvm.cmake | 60 ++- .../interop_js/gtest_plugin/gtest_launcher.js | 17 +- .../tests/interop_isa/CMakeLists.txt | 21 + .../interop_js/tests/interop_isa/index.js | 293 +++++++++++++ .../interop_js/tests/interop_isa/test.cpp | 29 ++ .../interop_js/tests/interop_isa/test.ets | 14 + .../interop_js/tests/interop_isa/test.pa | 408 ++++++++++++++++++ static_core/runtime/BUILD.gn | 9 + static_core/runtime/CMakeLists.txt | 10 + static_core/runtime/include/class.h | 11 + .../runtime/interpreter/interpreter-inl.h | 219 ++++++---- .../templates/core_any_intrinsics.inc.erb | 364 ++++++++++++++++ static_core/runtime/intrinsics.cpp | 2 + .../runtime/templates/intrinsics.h.erb | 42 +- static_core/verification/absint/abs_int_inl.h | 11 +- 38 files changed, 2535 insertions(+), 305 deletions(-) create mode 100644 static_core/plugins/ets/runtime/interpreter/any_opcodes.inc create mode 100644 static_core/plugins/ets/tests/interop_js/tests/interop_isa/CMakeLists.txt create mode 100644 static_core/plugins/ets/tests/interop_js/tests/interop_isa/index.js create mode 100644 static_core/plugins/ets/tests/interop_js/tests/interop_isa/test.cpp create mode 100644 static_core/plugins/ets/tests/interop_js/tests/interop_isa/test.ets create mode 100644 static_core/plugins/ets/tests/interop_js/tests/interop_isa/test.pa create mode 100644 static_core/runtime/interpreter/templates/core_any_intrinsics.inc.erb diff --git a/static_core/codecheck_ignore.json b/static_core/codecheck_ignore.json index bc385e8745..e0b16575b5 100644 --- a/static_core/codecheck_ignore.json +++ b/static_core/codecheck_ignore.json @@ -41,6 +41,7 @@ "plugins/ets/plugin.gn": "*", "plugins/ets/README.md": "*", "plugins/ets/RegisterPlugin.cmake": "*", + "plugins/ets/runtime/interpreter/any_opcodes.inc": {"G.INC.02": "*"}, "plugins/ets/runtime/napi": "*", "plugins/ets/sdk/CMakeLists.txt": {"bc-40001": "*"}, "plugins/ets/snippet_verifier": "*", diff --git a/static_core/compiler/optimizer/ir_builder/inst_builder-inl.h b/static_core/compiler/optimizer/ir_builder/inst_builder-inl.h index 95b5b029f0..7fd91a0de9 100644 --- a/static_core/compiler/optimizer/ir_builder/inst_builder-inl.h +++ b/static_core/compiler/optimizer/ir_builder/inst_builder-inl.h @@ -1628,8 +1628,8 @@ template void InstBuilder::BuildLoadFromAnyByName([[maybe_unused]] const BytecodeInstruction *bcInst, [[maybe_unused]] DataType::Type type) { - // NOTE: handle it - UNREACHABLE(); + COMPILER_LOG(DEBUG, IR_BUILDER) << "Any bytecode not supported yet, skip current function"; + failed_ = true; } // NOLINTNEXTLINE(readability-function-size,misc-definitions-in-headers) @@ -1637,49 +1637,56 @@ template void InstBuilder::BuildStoreFromAnyByName([[maybe_unused]] const BytecodeInstruction *bcInst, [[maybe_unused]] DataType::Type type) { - // NOTE: handle it - UNREACHABLE(); + COMPILER_LOG(DEBUG, IR_BUILDER) << "Any bytecode not supported yet, skip current function"; + failed_ = true; } // NOLINTNEXTLINE(readability-function-size,misc-definitions-in-headers) void InstBuilder::BuildLoadFromAnyByIdx([[maybe_unused]] const BytecodeInstruction *bcInst, [[maybe_unused]] DataType::Type type) { - // NOTE: handle it - UNREACHABLE(); + COMPILER_LOG(DEBUG, IR_BUILDER) << "Any bytecode not supported yet, skip current function"; + failed_ = true; } // NOLINTNEXTLINE(readability-function-size,misc-definitions-in-headers) void InstBuilder::BuildStoreFromAnyByIdx([[maybe_unused]] const BytecodeInstruction *bcInst, [[maybe_unused]] DataType::Type type) { - // NOTE: handle it - UNREACHABLE(); + COMPILER_LOG(DEBUG, IR_BUILDER) << "Any bytecode not supported yet, skip current function"; + failed_ = true; } // NOLINTNEXTLINE(readability-function-size,misc-definitions-in-headers) void InstBuilder::BuildLoadFromAnyByVal([[maybe_unused]] const BytecodeInstruction *bcInst, [[maybe_unused]] DataType::Type type) { - // NOTE: handle it - UNREACHABLE(); + COMPILER_LOG(DEBUG, IR_BUILDER) << "Any bytecode not supported yet, skip current function"; + failed_ = true; } // NOLINTNEXTLINE(readability-function-size,misc-definitions-in-headers) void InstBuilder::BuildStoreFromAnyByVal([[maybe_unused]] const BytecodeInstruction *bcInst, [[maybe_unused]] DataType::Type type) { - // NOTE: handle it - UNREACHABLE(); + COMPILER_LOG(DEBUG, IR_BUILDER) << "Any bytecode not supported yet, skip current function"; + failed_ = true; } // NOLINTNEXTLINE(misc-definitions-in-headers) template void InstBuilder::BuildAnyCall([[maybe_unused]] const BytecodeInstruction *bcInst) { - // NOTE(nsizov): issue(26115) support any type in 1.2 bytecode - // handle it - UNREACHABLE(); + COMPILER_LOG(DEBUG, IR_BUILDER) << "Any bytecode not supported yet, skip current function"; + failed_ = true; +} + +// NOLINTNEXTLINE(readability-function-size,misc-definitions-in-headers) +void InstBuilder::BuildIsInstanceAny([[maybe_unused]] const BytecodeInstruction *bcInst, + [[maybe_unused]] DataType::Type type) +{ + COMPILER_LOG(DEBUG, IR_BUILDER) << "Any bytecode not supported yet, skip current function"; + failed_ = true; } } // namespace ark::compiler diff --git a/static_core/compiler/optimizer/ir_builder/inst_builder.h b/static_core/compiler/optimizer/ir_builder/inst_builder.h index 07f93de26c..86f044787e 100644 --- a/static_core/compiler/optimizer/ir_builder/inst_builder.h +++ b/static_core/compiler/optimizer/ir_builder/inst_builder.h @@ -389,6 +389,7 @@ private: void BuildStoreFromAnyByIdx(const BytecodeInstruction *bcInst, DataType::Type type); void BuildLoadFromAnyByVal(const BytecodeInstruction *bcInst, DataType::Type type); void BuildStoreFromAnyByVal(const BytecodeInstruction *bcInst, DataType::Type type); + void BuildIsInstanceAny(const BytecodeInstruction *bcInst, DataType::Type type); template void BuildAnyCall(const BytecodeInstruction *bcInst); diff --git a/static_core/compiler/optimizer/ir_builder/inst_templates.yaml b/static_core/compiler/optimizer/ir_builder/inst_templates.yaml index 06e540de1a..314a8418f1 100644 --- a/static_core/compiler/optimizer/ir_builder/inst_templates.yaml +++ b/static_core/compiler/optimizer/ir_builder/inst_templates.yaml @@ -322,6 +322,8 @@ templates: BuildLoadFromAnyByVal(instruction, <%= get_type(inst.dtype) %>); % when "stbyval" BuildStoreFromAnyByVal(instruction, <%= get_type(inst.type(0)) %>); + % when "isinstance" + BuildIsInstanceAny(instruction, <%= get_type(inst.dtype) %>); % else UNREACHABLE(); % end diff --git a/static_core/compiler/optimizer/templates/inst_builder_gen.cpp.erb b/static_core/compiler/optimizer/templates/inst_builder_gen.cpp.erb index 49e099a93b..22c8d6993f 100644 --- a/static_core/compiler/optimizer/templates/inst_builder_gen.cpp.erb +++ b/static_core/compiler/optimizer/templates/inst_builder_gen.cpp.erb @@ -118,7 +118,7 @@ end /^monitor/ => "monitor", /^nop/ => "nop", /^builtin/ => "builtin", - /^any?$/ => "any", + /^any?$/ => "unimplemented", /^$/ => "unimplemented" } Panda::prefixes.select{|p| !p.public?}.each do |p| diff --git a/static_core/irtoc/scripts/interpreter.irt b/static_core/irtoc/scripts/interpreter.irt index 49b361390c..3c8a6ab293 100644 --- a/static_core/irtoc/scripts/interpreter.irt +++ b/static_core/irtoc/scripts/interpreter.irt @@ -2094,44 +2094,143 @@ include_plugin 'interpreter_handlers' # Any type support macro(:handle_any_ldbyname_v8_id32) do |vs, id| - # NOTE: handle it - Intrinsic(:UNREACHABLE).Terminator.void + ret := call_runtime("AnyLdbyname", %tr, %frame, vs, id).ref + If(exception_val(), 0).NE.Unlikely { + move_to_exception + } + set_acc_object(ret) end macro(:handle_any_ldbyname_v_v8_v8_id32) do |vd, vs, id| - # NOTE: handle it - Intrinsic(:UNREACHABLE).Terminator.void + ret := call_runtime("AnyLdbyname", %tr, %frame, vs, id).ref + If(exception_val(), 0).NE.Unlikely { + move_to_exception + } + set_object(vd, ret).ref end macro(:handle_any_stbyname_v8_id32) do |vs, id| - # NOTE: handle it - Intrinsic(:UNREACHABLE).Terminator.void + call_runtime("AnyStbyname", %tr, %frame, vs, id, acc.ref).void + If(exception_val(), 0).NE.Unlikely { + move_to_exception + } end macro(:handle_any_stbyname_v_v8_v8_id32) do |vs1, vs2, id| - # NOTE: handle it - Intrinsic(:UNREACHABLE).Terminator.void + call_runtime("AnyStbyname", %tr, %frame, vs2, id, vs1).void + If(exception_val(), 0).NE.Unlikely { + move_to_exception + } end macro(:handle_any_ldbyidx_v8) do |vs| - # NOTE: handle it - set_acc_object(vs) # NOTE: fixes build for ARK_HYBRID - Intrinsic(:UNREACHABLE).Terminator.void + ret := call_runtime("AnyLdbyidx", %tr, vs, acc.f64).ref + If(exception_val(), 0).NE.Unlikely { + move_to_exception + } + set_acc_object(ret) end macro(:handle_any_stbyidx_v8_v8) do |vs1, vs2| - # NOTE: handle it - Intrinsic(:UNREACHABLE).Terminator.void + call_runtime("AnyStbyidx", %tr, vs1, vs2, acc.ref).void + If(exception_val(), 0).NE.Unlikely { + move_to_exception + } end macro(:handle_any_ldbyval_v8_v8) do |vs1, vs2| - # NOTE: handle it - Intrinsic(:UNREACHABLE).Terminator.void + ret := call_runtime("AnyLdbyval", %tr, vs1, vs2).ref + If(exception_val(), 0).NE.Unlikely { + move_to_exception + } + set_acc_object(ret) end macro(:handle_any_stbyval_v8_v8) do |vs1, vs2| - # NOTE: handle it - Intrinsic(:UNREACHABLE).Terminator.void + call_runtime("AnyStbyval", %tr, vs1, vs2, acc.ref).void + If(exception_val(), 0).NE.Unlikely { + move_to_exception + } +end + +macro(:handle_any_isinstance_v8) do |vs| + ret := btou8(call_runtime("AnyIsinstance", %tr, acc.ref, vs).b) + If(exception_val(), 0).NE.Unlikely { + move_to_exception + } + set_acc_primitive(ret) +end + +macro(:handle_any_call_0_v8) do |vs| + ret := call_runtime("AnyCall0", %tr, vs).ref + If(exception_val(), 0).NE.Unlikely { + move_to_exception + } + set_acc_object(ret) +end + +macro(:handle_any_call_short_v4_v4) do |vs1, vs2| + ret := call_runtime("AnyCallShort", %tr, vs1, vs2).ref + If(exception_val(), 0).NE.Unlikely { + move_to_exception + } + set_acc_object(ret) +end + +macro(:handle_any_call_range_v8_v8_imm8) do |vs1, first_arg, argc| + ret := call_runtime("AnyCallRange", %tr, %frame, vs1, first_arg, argc).ref + If(exception_val(), 0).NE.Unlikely { + move_to_exception + } + set_acc_object(ret) +end + +macro(:handle_any_call_this_0_v8_id32) do |vs, id| + ret := call_runtime("AnyCallThis0", %tr, %frame, vs, id).ref + If(exception_val(), 0).NE.Unlikely { + move_to_exception + } + set_acc_object(ret) +end + +macro(:handle_any_call_this_short_v4_v4_id32) do |vs1, vs2, id| + ret := call_runtime("AnyCallThisShort", %tr, %frame, vs1, id, vs2).ref + If(exception_val(), 0).NE.Unlikely { + move_to_exception + } + set_acc_object(ret) +end + +macro(:handle_any_call_this_range_v8_v8_imm8_id32) do |vs1, first_arg, argc, id| + ret := call_runtime("AnyCallThisRange", %tr, %frame, vs1, id, first_arg, argc).ref + If(exception_val(), 0).NE.Unlikely { + move_to_exception + } + set_acc_object(ret) +end + +macro(:handle_any_call_new_0_v8) do |vs| + ret := call_runtime("AnyCallNew0", %tr, vs).ref + If(exception_val(), 0).NE.Unlikely { + move_to_exception + } + set_acc_object(ret) +end + +macro(:handle_any_call_new_short_v4_v4) do |vs1, vs2| + ret := call_runtime("AnyCallNewShort", %tr, vs1, vs2).ref + If(exception_val(), 0).NE.Unlikely { + move_to_exception + } + set_acc_object(ret) +end + +macro(:handle_any_call_new_range_v8_v8_imm8) do |vs1, first_arg, argc| + ret := call_runtime("AnyCallNewRange", %tr, %frame, vs1, first_arg, argc).ref + If(exception_val(), 0).NE.Unlikely { + move_to_exception + } + set_acc_object(ret) end # Functions: @@ -2925,20 +3024,40 @@ Panda.instructions.each do |i| # any type support when "ANY_LDBYNAME_PREF_V8_ID32" handle_any_ldbyname_v8_id32(vreg_value(op[0]).ref, as_id(op[1])) - when "ANY_LDBYNAME_PREF_V8_V8_ID32" - handle_any_ldbyname_v_v8_v8_id32(vreg_value(op[0]).ref, vreg_value(op[1]).ref, as_id(op[2])) + when "ANY_LDBYNAME_V_PREF_V8_V8_ID32" + handle_any_ldbyname_v_v8_v8_id32(vreg_ptr(op[0]), vreg_value(op[1]).ref, as_id(op[2])) when "ANY_STBYNAME_PREF_V8_ID32" handle_any_stbyname_v8_id32(vreg_value(op[0]).ref, as_id(op[1])) - when "ANY_STBYNAME_PREF_V8_V8_ID32" + when "ANY_STBYNAME_V_PREF_V8_V8_ID32" handle_any_stbyname_v_v8_v8_id32(vreg_value(op[0]).ref, vreg_value(op[1]).ref, as_id(op[2])) when "ANY_LDBYIDX_PREF_V8" handle_any_ldbyidx_v8(vreg_value(op[0]).ref) when "ANY_STBYIDX_PREF_V8_V8" - handle_any_stbyidx_v8_v8(vreg_value(op[0]).ref, vreg_value(op[1]).i32) + handle_any_stbyidx_v8_v8(vreg_value(op[0]).ref, vreg_value(op[1]).f64) when "ANY_LDBYVAL_PREF_V8_V8" handle_any_ldbyval_v8_v8(vreg_value(op[0]).ref, vreg_value(op[1]).ref) when "ANY_STBYVAL_PREF_V8_V8" handle_any_stbyval_v8_v8(vreg_value(op[0]).ref, vreg_value(op[1]).ref) + when "ANY_ISINSTANCE_PREF_V8" + handle_any_isinstance_v8(vreg_value(op[0]).ref) + when "ANY_CALL_0_PREF_V8" + handle_any_call_0_v8(vreg_value(op[0]).ref) + when "ANY_CALL_SHORT_PREF_V4_V4" + handle_any_call_short_v4_v4(vreg_value(op[0]).ref, vreg_value(op[1]).ref) + when "ANY_CALL_RANGE_PREF_V8_V8_IMM8" + handle_any_call_range_v8_v8_imm8(vreg_value(op[0]).ref, as_vreg_idx(op[1]).u8, as_imm(op[2]).u8) + when "ANY_CALL_THIS_0_PREF_V8_ID32" + handle_any_call_this_0_v8_id32(vreg_value(op[1]).ref, as_id(op[0])) + when "ANY_CALL_THIS_SHORT_PREF_V4_V4_ID32" + handle_any_call_this_short_v4_v4_id32(vreg_value(op[1]).ref, vreg_value(op[2]).ref, as_id(op[0])) + when "ANY_CALL_THIS_RANGE_PREF_V8_V8_IMM8_ID32" + handle_any_call_this_range_v8_v8_imm8_id32(vreg_value(op[1]).ref, as_vreg_idx(op[2]).u8, as_imm(op[3]).u8, as_id(op[0])) + when "ANY_CALL_NEW_0_PREF_V8" + handle_any_call_new_0_v8(vreg_value(op[0]).ref) + when "ANY_CALL_NEW_SHORT_PREF_V4_V4" + handle_any_call_new_short_v4_v4(vreg_value(op[0]).ref, vreg_value(op[1]).ref) + when "ANY_CALL_NEW_RANGE_PREF_V8_V8_IMM8" + handle_any_call_new_range_v8_v8_imm8(vreg_value(op[0]).ref, as_vreg_idx(op[1]).u8, as_imm(op[2]).u8) include_plugin 'interpreter_main_loop' diff --git a/static_core/isa/isa.yaml b/static_core/isa/isa.yaml index 7753bff028..2c0ed296ac 100644 --- a/static_core/isa/isa.yaml +++ b/static_core/isa/isa.yaml @@ -3009,7 +3009,7 @@ groups: acc: out:ref format: [pref_op_v_8_id_32] prefix: any - opcode_idx: [0xa6] + opcode_idx: [0x0] - title: Get field from reference of any type by name description: > @@ -3036,7 +3036,7 @@ groups: acc: none format: [pref_op_v1_8_v2_8_id_32] prefix: any - opcode_idx: [0xa7] + opcode_idx: [0x1] - title: Store accumulator content into reference of any type field by name description: > @@ -3063,7 +3063,7 @@ groups: acc: in:ref format: [pref_op_v_8_id_32] prefix: any - opcode_idx: [0xa8] + opcode_idx: [0x2] - title: Store register content into reference of any type field by name description: > @@ -3076,21 +3076,21 @@ groups: exceptions: - x_null pseudo: | - if v2 == null then + if v1 == null then throw NullPointerException end - field = resolve_field_by_name(v2, string_id) + field = resolve_field_by_name(v1, string_id) if op == any.stbyname.v and size(field) < 32 then - v2.set(field, truncate(field, v1)) + v1.set(field, truncate(field, v2)) else - v2.set(field, v1) + v1.set(field, v2) end instructions: - sig: any.stbyname.v v1:in:ref, v2:in:ref, string_id acc: none format: [pref_op_v1_8_v2_8_id_32] prefix: any - opcode_idx: [0xa9] + opcode_idx: [0x3] - title: Load value to accumulator from reference of any type by index description: > @@ -3107,10 +3107,11 @@ groups: acc = vs[acc] instructions: - sig: any.ldbyidx v:in:ref - acc: inout:i32->ref + acc: inout:f64->ref format: [pref_op_v_8] prefix: any - opcode_idx: [0xaa] + properties: [float] + opcode_idx: [0x4] - title: Store value from accumulator in reference of any type by index description: > @@ -3127,11 +3128,12 @@ groups: end v1[v2] = acc instructions: - - sig: any.stbyidx v1:in:ref, v2:in:i32 + - sig: any.stbyidx v1:in:ref, v2:in:f64 acc: in:ref format: [pref_op_v1_8_v2_8] prefix: any - opcode_idx: [0xab] + properties: [ float ] + opcode_idx: [0x5] - title: Load value to accumulator from reference of any type by value description: > @@ -3151,7 +3153,7 @@ groups: acc: out:ref format: [pref_op_v1_8_v2_8] prefix: any - opcode_idx: [0xac] + opcode_idx: [0x6] - title: Store value from accumulator in reference of any type by value description: > @@ -3172,7 +3174,7 @@ groups: acc: in:ref format: [pref_op_v1_8_v2_8] prefix: any - opcode_idx: [0xad] + opcode_idx: [0x7] - title: Any call 0 description: > @@ -3186,11 +3188,11 @@ groups: pseudo: | TBD instructions: - - sig: any.call.0 v1:in:top - acc: out:top + - sig: any.call.0 v1:in:ref + acc: out:ref prefix: any format: [pref_op_v1_8] - opcode_idx: [0xae] + opcode_idx: [0x8] - title: Any call range description: > @@ -3199,18 +3201,16 @@ groups: - compatible_arguments properties: - any_call - - string_id - - id_shift exceptions: - x_call pseudo: | TBD instructions: - - sig: any.call.range string_id, v1:in:top, v2:in:top, imm:u8 - acc: out:top + - sig: any.call.range v1:in:ref, v2:in:ref, imm:u8 + acc: out:ref prefix: any - format: [pref_op_v1_8_v2_8_imm_8_id_32] - opcode_idx: [0xaf] + format: [pref_op_v1_8_v2_8_imm_8] + opcode_idx: [0x9] - title: Any call short description: > @@ -3219,18 +3219,16 @@ groups: - compatible_arguments properties: - any_call - - string_id - - id_shift exceptions: - x_call pseudo: | TBD instructions: - - sig: any.call.short string_id, v1:in:top, imm:u4 - acc: out:top + - sig: any.call.short v1:in:ref, v2:in:ref + acc: out:ref prefix: any - format: [pref_op_v1_4_imm_4_id_32] - opcode_idx: [0xb0] + format: [pref_op_v1_4_v2_4] + opcode_idx: [0xa] - title: Any call this 0 description: > @@ -3246,11 +3244,11 @@ groups: pseudo: | TBD instructions: - - sig: any.call.this.0 string_id, v1:in:top - acc: out:top + - sig: any.call.this.0 string_id, v1:in:ref + acc: out:ref prefix: any format: [pref_op_v1_8_id_32] - opcode_idx: [0xb1] + opcode_idx: [0xb] - title: Any call this range description: > @@ -3266,13 +3264,13 @@ groups: pseudo: | TBD instructions: - - sig: any.call.this.range string_id, v1:in:top, v2:in:top, imm:u8 - acc: out:top + - sig: any.call.this.range string_id, v1:in:ref, v2:in:ref, imm:u8 + acc: out:ref prefix: any format: [pref_op_v1_8_v2_8_imm_8_id_32] - opcode_idx: [0xb2] + opcode_idx: [0xc] - - title: Any call thils short + - title: Any call this short description: > TBD verification: @@ -3286,11 +3284,11 @@ groups: pseudo: | TBD instructions: - - sig: any.call.this.short string_id, v1:in:top, imm:u4 - acc: out:top + - sig: any.call.this.short string_id, v1:in:ref, v2:in:ref + acc: out:ref prefix: any - format: [pref_op_v1_4_imm_4_id_32] - opcode_idx: [0xb3] + format: [pref_op_v1_4_v2_4_id_32] + opcode_idx: [0xd] - title: Any call new 0 description: > @@ -3304,11 +3302,11 @@ groups: pseudo: | TBD instructions: - - sig: any.call.new.0 v1:in:top - acc: out:top + - sig: any.call.new.0 v1:in:ref + acc: out:ref prefix: any format: [pref_op_v1_8] - opcode_idx: [0xb4] + opcode_idx: [0xe] - title: Any call new range description: > @@ -3322,11 +3320,11 @@ groups: pseudo: | TBD instructions: - - sig: any.call.new.range v1:in:top, v2:in:top, imm:u8 - acc: out:top + - sig: any.call.new.range v1:in:ref, v2:in:ref, imm:u8 + acc: out:ref prefix: any format: [pref_op_v1_8_v2_8_imm_8] - opcode_idx: [0xb5] + opcode_idx: [0xf] - title: Any call new short description: > @@ -3340,8 +3338,24 @@ groups: pseudo: | TBD instructions: - - sig: any.call.new.short v1:in:top, imm:u4 - acc: out:top + - sig: any.call.new.short v1:in:ref, v2:in:ref + acc: out:ref prefix: any - format: [pref_op_v1_4_imm_4] - opcode_idx: [0xb6] + format: [pref_op_v1_4_v2_4] + opcode_idx: [0x10] + + - title: Any instanceof check + description: > + TBD + verification: + - v1_object + exceptions: + - x_null + pseudo: | + TBD + instructions: + - sig: any.isinstance v1:in:ref + acc: inout:top->u1 + prefix: any + format: [pref_op_v1_8] + opcode_idx: [0x11] diff --git a/static_core/libllvmbackend/lowering/irtoc_function_utils.cpp b/static_core/libllvmbackend/lowering/irtoc_function_utils.cpp index dcf28160c4..70da2023c1 100644 --- a/static_core/libllvmbackend/lowering/irtoc_function_utils.cpp +++ b/static_core/libllvmbackend/lowering/irtoc_function_utils.cpp @@ -22,12 +22,33 @@ using namespace std::literals::string_view_literals; // Enables 'sv' suffix constexpr std::array NOALIAS_IRTOC_FUNC = { - "CreateObjectByIdEntrypoint"sv, "CreateObjectByClassInterpreter"sv, "CreateArrayByIdEntrypoint"sv, + "CreateObjectByIdEntrypoint"sv, + "CreateObjectByClassInterpreter"sv, + "CreateArrayByIdEntrypoint"sv, "CreateMultiDimensionalArrayById"sv, + "AnyLdbyname"sv, + "AnyLdbyidx"sv, + "AnyLdbyval"sv, + "AnyLdByValEntrypoint"sv, + "AnyCall0"sv, + "AnyCallRange"sv, + "AnyCallShort"sv, + "AnyCallThis0"sv, + "AnyCallThisRange"sv, + "AnyCallThisShort"sv, + "AnyCallNew0"sv, + "AnyCallNewRange"sv, + "AnyCallNewShort"sv, #ifdef PANDA_WITH_ETS - "LookupGetterByNameShortEntrypoint"sv, "LookupGetterByNameLongEntrypoint"sv, "LookupGetterByNameObjEntrypoint"sv, - "LookupSetterByNameShortEntrypoint"sv, "LookupSetterByNameLongEntrypoint"sv, "LookupSetterByNameObjEntrypoint"sv, - "LookupFieldByNameEntrypoint"sv, "EtsGetTypeofEntrypoint"sv, "EtsGetIstrueEntrypoint"sv, + "LookupGetterByNameShortEntrypoint"sv, + "LookupGetterByNameLongEntrypoint"sv, + "LookupGetterByNameObjEntrypoint"sv, + "LookupSetterByNameShortEntrypoint"sv, + "LookupSetterByNameLongEntrypoint"sv, + "LookupSetterByNameObjEntrypoint"sv, + "LookupFieldByNameEntrypoint"sv, + "EtsGetTypeofEntrypoint"sv, + "EtsGetIstrueEntrypoint"sv, "LookupMethodByNameEntrypoint"sv, #endif }; diff --git a/static_core/plugins/ets/ets_plugin_options.yaml b/static_core/plugins/ets/ets_plugin_options.yaml index 4f68894bd0..39452708f3 100644 --- a/static_core/plugins/ets/ets_plugin_options.yaml +++ b/static_core/plugins/ets/ets_plugin_options.yaml @@ -24,11 +24,14 @@ lang_type: static mt_mode: task has_value_object_types: false + has_any_insturction_handlers: true ctor_name: cctor_name: directive_name: eTS string_class_descriptor: Lstd/core/String; + any: + handlers_path: plugins/ets/runtime/interpreter/any_opcodes.inc Intrinsics: header: plugins/ets/runtime/intrinsics_declaration.h diff --git a/static_core/plugins/ets/runtime/ets_class_linker_extension.cpp b/static_core/plugins/ets/runtime/ets_class_linker_extension.cpp index 0941a2a0ee..8fb4c03e94 100644 --- a/static_core/plugins/ets/runtime/ets_class_linker_extension.cpp +++ b/static_core/plugins/ets/runtime/ets_class_linker_extension.cpp @@ -169,6 +169,13 @@ bool EtsClassLinkerExtension::InitializeImpl(bool compressedStringEnabled) SetClassRoot(ClassRoot::STRING, stringClass); stringClass->SetStringClass(); + auto *jsValueClass = GetClassLinker()->GetClass(utf::CStringAsMutf8(JS_VALUE.data()), false, GetBootContext()); + if (jsValueClass == nullptr) { + LOG(ERROR, CLASS_LINKER) << "Cannot create class '" << JS_VALUE << "'"; + return false; + } + jsValueClass->SetXRefClass(); + InitializeClassRoots(); return true; diff --git a/static_core/plugins/ets/runtime/ets_exceptions.h b/static_core/plugins/ets/runtime/ets_exceptions.h index f84907b62b..1d2265b493 100644 --- a/static_core/plugins/ets/runtime/ets_exceptions.h +++ b/static_core/plugins/ets/runtime/ets_exceptions.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 2023-2024 Huawei Device Co., Ltd. + * Copyright (c) 2023-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 @@ -18,10 +18,13 @@ #include #include "libpandabase/macros.h" +#include "plugins/ets/runtime/ets_panda_file_items.h" +#include "runtime/include/mem/panda_string.h" namespace ark::ets { class EtsCoroutine; + class EtsObject; PANDA_PUBLIC_API EtsObject *SetupEtsException(EtsCoroutine *coroutine, const char *classDescriptor, const char *msg); @@ -41,6 +44,33 @@ inline void ThrowEtsException(EtsCoroutine *coroutine, std::string_view classDes ThrowEtsException(coroutine, classDescriptor.data(), msg.data()); } +inline void ThrowEtsFieldNotFoundException(EtsCoroutine *coroutine, const char *holderClassDescriptor, + const char *fieldName) +{ + PandaString message = "Field " + PandaString(fieldName) + " not found in " + PandaString(holderClassDescriptor); + ThrowEtsException(coroutine, panda_file_items::class_descriptors::TYPE_ERROR, message.c_str()); +} + +inline void ThrowEtsMethodNotFoundException(EtsCoroutine *coroutine, const char *holderClassDescriptor, + const char *methodName, const char *methodSignature) +{ + PandaString message = "Method " + PandaString(methodName) + "(" + methodSignature + ") not found in " + + PandaString(holderClassDescriptor); + ThrowEtsException(coroutine, panda_file_items::class_descriptors::TYPE_ERROR, message.c_str()); +} + +inline void ThrowEtsInvalidKey(EtsCoroutine *coroutine, const char *classSignature) +{ + PandaString message = "Invalid key type: " + PandaString(classSignature); + ThrowEtsException(coroutine, panda_file_items::class_descriptors::TYPE_ERROR, message.c_str()); +} + +inline void ThrowEtsInvalidType(EtsCoroutine *coroutine, const char *classSignature) +{ + PandaString message = "Invalid oprand type: " + PandaString(classSignature); + ThrowEtsException(coroutine, panda_file_items::class_descriptors::TYPE_ERROR, message.c_str()); +} + } // namespace ark::ets #endif // PANDA_PLUGINS_ETS_RUNTIME_ETS_EXCEPTIONS_H_ diff --git a/static_core/plugins/ets/runtime/ets_stubs.cpp b/static_core/plugins/ets/runtime/ets_stubs.cpp index 70ea899b4c..e2a58f606f 100644 --- a/static_core/plugins/ets/runtime/ets_stubs.cpp +++ b/static_core/plugins/ets/runtime/ets_stubs.cpp @@ -16,11 +16,37 @@ #include "plugins/ets/runtime/ets_class_linker_extension.h" #include "plugins/ets/runtime/ets_stubs-inl.h" -#include "plugins/ets/runtime/types/ets_box_primitive.h" +#include "plugins/ets/runtime/ets_utils.h" #include "plugins/ets/runtime/types/ets_base_enum.h" +#include "plugins/ets/runtime/types/ets_box_primitive.h" +#include "plugins/ets/runtime/types/ets_method.h" #include "plugins/ets/runtime/types/ets_string.h" +#ifdef PANDA_ETS_INTEROP_JS +#include "plugins/ets/runtime/interop_js/intrinsics_api_impl.h" +#endif + namespace ark::ets { +#ifdef PANDA_ETS_INTEROP_JS +using JSValue = interop::js::JSValue; +using JSConvertEtsObject = interop::js::JSConvertEtsObject; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define PANDA_ETS_INTEROP_JS_GUARD(code) \ + do { \ + code \ + /* CC-OFFNXT(G.PRE.09) code gen */ \ + } while (0); +#else +// CC-OFFNXT(G.PRE.09) code gen +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define PANDA_ETS_INTEROP_JS_GUARD(code) \ + do { \ + LOG(FATAL, RUNTIME) << "Found dynamic object without interop build"; \ + UNREACHABLE(); \ + /* CC-OFFNXT(G.PRE.09) code gen */ \ + } while (0); +#endif template static std::optional GetBoxedNumericValue(EtsPlatformTypes const *ptypes, EtsObject *obj) @@ -148,6 +174,19 @@ bool EtsValueTypedEquals(EtsCoroutine *coro, EtsObject *obj1, EtsObject *obj2) } return EtsReferenceEquals(coro, instance1, instance2); } + + if (cls1->GetRuntimeClass()->IsXRefClass()) { + PANDA_ETS_INTEROP_JS_GUARD({ + if (UNLIKELY(!cls2->GetRuntimeClass()->IsXRefClass())) { + return false; + } + ASSERT(cls1 == PlatformTypes(coro)->interopJSValue); + ASSERT(cls2 == PlatformTypes(coro)->interopJSValue); + auto jsObj1 = interop::js::JSValue::FromEtsObject(obj1); + auto jsObj2 = interop::js::JSValue::FromEtsObject(obj2); + return jsObj1->StrictEquals(jsObj2); + }); + } UNREACHABLE(); } @@ -173,11 +212,6 @@ EtsString *EtsGetTypeof(EtsCoroutine *coro, EtsObject *obj) } return EtsString::CreateFromMUtf8("object"); } - - if (cls->IsFunctionReference()) { - return EtsString::CreateFromMUtf8("function"); - } - if (cls->IsNullValue()) { return EtsString::CreateFromMUtf8("object"); } @@ -196,6 +230,15 @@ EtsString *EtsGetTypeof(EtsCoroutine *coro, EtsObject *obj) } return EtsGetTypeof(coro, EtsBaseEnum::FromEtsObject(obj)->GetValue()); } + if (cls->IsFunctionReference()) { + return EtsString::CreateFromMUtf8("function"); + } + if (cls->GetRuntimeClass()->IsXRefClass()) { + PANDA_ETS_INTEROP_JS_GUARD({ + ASSERT(cls == PlatformTypes(coro)->interopJSValue); + return EtsString::CreateFromMUtf8(JSValue::FromEtsObject(obj)->TypeOf().c_str()); + }); + } ASSERT(cls->IsBoxed()); @@ -236,18 +279,309 @@ bool EtsGetIstrue(EtsCoroutine *coro, EtsObject *obj) return EtsGetIstrue(coro, value); } - ASSERT(cls->IsBoxed()); + ASSERT(cls->IsBoxed() || cls->GetRuntimeClass()->IsXRefClass()); auto ptypes = PlatformTypes(coro); if (cls == ptypes->coreBoolean) { return EtsBoxPrimitive::FromCoreType(obj)->GetValue() != 0; } - ASSERT(DbgIsBoxedNumericClass(coro, cls)); + ASSERT(DbgIsBoxedNumericClass(coro, cls) || cls->GetRuntimeClass()->IsXRefClass()); if (auto num = GetBoxedNumericValue(ptypes, obj); num.has_value()) { return num.value() != 0 && !std::isnan(num.value()); } + + if (cls->GetRuntimeClass()->IsXRefClass()) { + PANDA_ETS_INTEROP_JS_GUARD({ + auto jsObj = JSValue::FromEtsObject(obj); + return jsObj->IsTrue(); + }); + } UNREACHABLE(); } +EtsObject *EtsLdbyname([[maybe_unused]] EtsCoroutine *coro, EtsObject *thisObj, + [[maybe_unused]] panda_file::File::StringData name) +{ + auto fieldName = utf::Mutf8AsCString(name.data); + if (thisObj->GetClass()->GetRuntimeClass()->IsXRefClass()) { + PANDA_ETS_INTEROP_JS_GUARD({ + ASSERT(thisObj->GetClass() == PlatformTypes(coro)->interopJSValue); + auto thisValue = JSValue::FromEtsObject(thisObj); + return interop::js::GetNamedPropertyObject(thisValue, fieldName); + }); + } else { + auto fieldIndex = thisObj->GetClass()->GetFieldIndexByName(fieldName); + EtsField *field = thisObj->GetClass()->GetFieldByIndex(fieldIndex); + if (field == nullptr) { + ThrowEtsFieldNotFoundException(coro, thisObj->GetClass()->GetDescriptor(), fieldName); + return nullptr; + } + return GetPropertyValue(coro, thisObj, field); + } +} + +void EtsStbyname([[maybe_unused]] EtsCoroutine *coro, EtsObject *obj, + [[maybe_unused]] panda_file::File::StringData propName, [[maybe_unused]] EtsObject *value) +{ + auto fieldName = utf::Mutf8AsCString(propName.data); + if (obj->GetClass()->GetRuntimeClass()->IsXRefClass()) { + PANDA_ETS_INTEROP_JS_GUARD({ + ASSERT(obj->GetClass() == PlatformTypes(coro)->interopJSValue); + auto thisValue = JSValue::FromEtsObject(obj); + interop::js::SetNamedPropertyWithObject(thisValue, fieldName, value); + }); + } else { + // ASSERTION. LHS is not a JSValue + // then it is must a static object + auto fieldIndex = obj->GetClass()->GetFieldIndexByName(fieldName); + EtsField *field = obj->GetClass()->GetFieldByIndex(fieldIndex); + if (field == nullptr) { + ThrowEtsFieldNotFoundException(coro, obj->GetClass()->GetDescriptor(), fieldName); + return; + } + SetPropertyValue(coro, obj, field, value); + } +} + +inline EtsMethod *FindGetMethod(EtsCoroutine *coro, EtsClass *cls) +{ + auto getMethod = cls->GetInstanceMethod(GET_INDEX_METHOD, INDEXED_INT_GET_METHOD_SIGNATURE.data()); + if (getMethod == nullptr) { + ThrowEtsMethodNotFoundException(coro, cls->GetDescriptor(), GET_INDEX_METHOD, + INDEXED_INT_GET_METHOD_SIGNATURE.data()); + } + return getMethod; +} + +inline EtsMethod *FindSetMethod(EtsCoroutine *coro, EtsClass *cls) +{ + auto setMethod = cls->GetInstanceMethod(SET_INDEX_METHOD, INDEXED_INT_SET_METHOD_SIGNATURE.data()); + if (setMethod == nullptr) { + ThrowEtsMethodNotFoundException(coro, cls->GetDescriptor(), SET_INDEX_METHOD, + INDEXED_INT_SET_METHOD_SIGNATURE.data()); + return nullptr; + } + return setMethod; +} + +EtsObject *EtsLdbyidx(EtsCoroutine *coro, EtsObject *thisObj, [[maybe_unused]] uint32_t index) +{ + if (thisObj->GetClass()->GetRuntimeClass()->IsXRefClass()) { + PANDA_ETS_INTEROP_JS_GUARD({ + ASSERT(thisObj->GetClass() == PlatformTypes(coro)->interopJSValue); + auto thisValue = JSValue::FromEtsObject(thisObj); + return interop::js::JSValueIndexedGetter(thisValue, index); + }); + } else { + auto getMethod = FindGetMethod(coro, thisObj->GetClass()); + if (getMethod == nullptr) { + return nullptr; + } + std::array args {ark::Value(thisObj->GetCoreType()), ark::Value(index)}; + Value res = getMethod->GetPandaMethod()->Invoke(coro, args.data()); + return EtsObject::FromCoreType(res.GetAs()); + } +} + +bool EtsStbyidx(EtsCoroutine *coro, EtsObject *obj, uint32_t idx, EtsObject *value) +{ + if (obj->GetClass()->GetRuntimeClass()->IsXRefClass()) { + PANDA_ETS_INTEROP_JS_GUARD({ + ASSERT(obj->GetClass() == PlatformTypes(coro)->interopJSValue); + auto thisValue = JSValue::FromEtsObject(obj); + interop::js::SetIndexedPropertyWithObject(thisValue, idx, value); + return true; + }); + } else { + // ASSERTION. LHS is not a JSValue + // then it is must a static object + auto unboxedValue = GetBoxedNumericValue(PlatformTypes(coro), value); + if (unboxedValue.has_value()) { + auto setMethod = FindSetMethod(coro, obj->GetClass()); + if (setMethod == nullptr) { + return false; + } + std::array args {ark::Value(obj->GetCoreType()), ark::Value(idx), ark::Value(value->GetCoreType())}; + setMethod->GetPandaMethod()->Invoke(coro, args.data()); + return true; + } + } + UNREACHABLE(); +} + +EtsObject *EtsLdbyval(EtsCoroutine *coro, EtsObject *thisObj, EtsObject *valObj) +{ + if (thisObj->GetClass()->GetRuntimeClass()->IsXRefClass()) { + PANDA_ETS_INTEROP_JS_GUARD({ + ASSERT(thisObj->GetClass() == PlatformTypes(coro)->interopJSValue); + auto thisValue = JSValue::FromEtsObject(thisObj); + if (valObj->IsStringClass()) { + return interop::js::JSValueNamedGetter(thisValue, EtsString::FromEtsObject(valObj)); + } + if (valObj->GetClass()->GetRuntimeClass()->IsXRefClass()) { + return interop::js::GetPropertyObject(thisValue, JSValue::FromEtsObject(valObj)); + } + auto unboxedValue = GetBoxedNumericValue(PlatformTypes(coro), valObj); + if (unboxedValue.has_value()) { + return interop::js::JSValueIndexedGetter(thisValue, unboxedValue.value()); + } + ThrowEtsInvalidKey(coro, valObj->GetClass()->GetDescriptor()); + return nullptr; + }); + } else { + // ASSERTION. LHS is not a JSValue + // then it is must a static object + if (valObj->IsStringClass()) { + auto fieldName = utf::Mutf8AsCString(EtsString::FromEtsObject(valObj)->GetDataMUtf8()); + auto fieldIndex = thisObj->GetClass()->GetFieldIndexByName(fieldName); + EtsField *field = thisObj->GetClass()->GetFieldByIndex(fieldIndex); + if (field == nullptr) { + ThrowEtsFieldNotFoundException(coro, thisObj->GetClass()->GetDescriptor(), fieldName); + return nullptr; + } + return GetPropertyValue(coro, thisObj, field); + } + auto unboxedValue = GetBoxedNumericValue(PlatformTypes(coro), valObj); + if (unboxedValue.has_value()) { + auto getMethod = FindGetMethod(coro, thisObj->GetClass()); + if (getMethod == nullptr) { + return nullptr; + } + std::array args {ark::Value(thisObj->GetCoreType()), ark::Value(unboxedValue.value())}; + ark::Value res = getMethod->GetPandaMethod()->Invoke(coro, args.data()); + return EtsObject::FromCoreType(res.GetAs()); + } + ThrowEtsInvalidKey(coro, valObj->GetClass()->GetDescriptor()); + return nullptr; + } +} + +bool EtsStbyval(EtsCoroutine *coro, EtsObject *obj, EtsObject *key, EtsObject *value) +{ + if (obj->GetClass()->GetRuntimeClass()->IsXRefClass()) { + PANDA_ETS_INTEROP_JS_GUARD({ + ASSERT(obj->GetClass() == PlatformTypes(coro)->interopJSValue); + auto thisValue = JSValue::FromEtsObject(obj); + if (key->IsStringClass()) { + interop::js::SetNamedPropertyWithObject( + thisValue, utf::Mutf8AsCString(EtsString::FromEtsObject(key)->GetDataMUtf8()), value); + return true; + } + if (key->GetClass()->GetRuntimeClass()->IsXRefClass()) { + interop::js::SetPropertyWithObject(thisValue, JSValue::FromEtsObject(key), value); + return true; + } + auto unboxedValue = GetBoxedNumericValue(PlatformTypes(coro), key); + if (unboxedValue.has_value()) { + interop::js::SetIndexedPropertyWithObject(thisValue, unboxedValue.value(), value); + return true; + } + ThrowEtsInvalidKey(coro, key->GetClass()->GetDescriptor()); + return false; + }); + } else { + // ASSERTION. LHS is not a JSValue + // then it is must a static object + if (key->IsStringClass()) { + auto fieldName = EtsString::FromEtsObject(key)->GetDataMUtf8(); + auto fieldIndex = obj->GetClass()->GetFieldIndexByName(utf::Mutf8AsCString(fieldName)); + EtsField *field = obj->GetClass()->GetFieldByIndex(fieldIndex); + if (field == nullptr) { + return false; + } + return SetPropertyValue(coro, obj, field, value); + } + auto unboxedValue = GetBoxedNumericValue(PlatformTypes(coro), key); + if (unboxedValue.has_value()) { + auto setMethod = FindSetMethod(coro, obj->GetClass()); + if (setMethod == nullptr) { + return false; + } + std::array args {ark::Value(obj->GetCoreType()), ark::Value(unboxedValue.value()), + ark::Value(value->GetCoreType())}; + setMethod->GetPandaMethod()->Invoke(coro, args.data()); + return true; + } + ThrowEtsInvalidKey(coro, key->GetClass()->GetDescriptor()); + return false; + } +} + +bool EtsIsinstance([[maybe_unused]] EtsCoroutine *coro, EtsObject *lhsObj, EtsObject *rhsObj) +{ + if (lhsObj->GetClass()->GetRuntimeClass()->IsXRefClass() && rhsObj->GetClass()->GetRuntimeClass()->IsXRefClass()) { + PANDA_ETS_INTEROP_JS_GUARD({ + ASSERT(interop::js::JSRuntimeIsJSValue(rhsObj)); + if (interop::js::JSRuntimeIsJSValue(lhsObj) == 0) { + return false; + } + return interop::js::JSRuntimeInstanceOfDynamic(JSValue::FromEtsObject(lhsObj), + JSValue::FromEtsObject(rhsObj)) != 0; + }); + } + return false; +} + +EtsObject *EtsCall([[maybe_unused]] EtsCoroutine *coro, EtsObject *funcObj, + [[maybe_unused]] Span> args) +{ + if (funcObj->GetClass()->GetRuntimeClass()->IsXRefClass()) { + PANDA_ETS_INTEROP_JS_GUARD({ + auto function = interop::js::JSValue::FromEtsObject(funcObj); + return interop::js::InvokeWithObjectReturn(nullptr, function, args); + }); + } else { + if (funcObj->GetClass()->IsFunction()) { + ThrowEtsInvalidType(coro, funcObj->GetClass()->GetDescriptor()); + return nullptr; + } + constexpr size_t UNSAFE_CALL_PARAM_COUNT = 2; + auto method = funcObj->GetClass()->GetInstanceMethod("unsafeCall", nullptr); + EtsHandle funcObjHandle(coro, funcObj); + auto restParam = EtsObjectArray::Create(PlatformTypes(coro)->coreObject, args.size()); + for (size_t i = 0; i < args.size(); i++) { + restParam->Set(i, EtsObject::FromCoreType(args[i].GetPtr())); + } + std::array realArgs {Value(funcObjHandle.GetPtr()->GetCoreType()), + Value(restParam->GetCoreType())}; + ark::Value res = method->GetPandaMethod()->Invoke(coro, realArgs.data()); + return EtsObject::FromCoreType(res.GetAs()); + } +} + +EtsObject *EtsCallThis([[maybe_unused]] EtsCoroutine *coro, EtsObject *thisObj, + [[maybe_unused]] panda_file::File::StringData name, + [[maybe_unused]] Span> args) +{ + [[maybe_unused]] auto fieldName = utf::Mutf8AsCString(name.data); + if (thisObj->GetClass()->GetRuntimeClass()->IsXRefClass()) { + PANDA_ETS_INTEROP_JS_GUARD({ + auto thisValue = JSValue::FromEtsObject(thisObj); + EtsObject *function = interop::js::GetPropertyObjectByString(thisValue, fieldName); + if (!function->GetClass()->GetRuntimeClass()->IsXRefClass()) { + return nullptr; + } + return interop::js::InvokeWithObjectReturn(thisValue, JSValue::FromEtsObject(function), args); + }); + } else { + // Not supported yet, need rethink for overload case + ThrowEtsInvalidType(coro, thisObj->GetClass()->GetDescriptor()); + return nullptr; + } +} + +EtsObject *EtsCallNew([[maybe_unused]] EtsCoroutine *coro, EtsObject *ctor, + [[maybe_unused]] Span> args) +{ + if (ctor->GetClass()->GetRuntimeClass()->IsXRefClass()) { + PANDA_ETS_INTEROP_JS_GUARD({ + auto initFunction = interop::js::JSValue::FromEtsObject(ctor); + return interop::js::CreateObject(initFunction, args); + }); + } else { + ThrowEtsInvalidType(coro, ctor->GetClass()->GetDescriptor()); + return nullptr; + } +} } // namespace ark::ets diff --git a/static_core/plugins/ets/runtime/ets_stubs.h b/static_core/plugins/ets/runtime/ets_stubs.h index 13481ce713..8e05713372 100644 --- a/static_core/plugins/ets/runtime/ets_stubs.h +++ b/static_core/plugins/ets/runtime/ets_stubs.h @@ -47,6 +47,27 @@ EtsString *EtsGetTypeof(EtsCoroutine *coro, EtsObject *obj); bool EtsGetIstrue(EtsCoroutine *coro, EtsObject *obj); +EtsObject *EtsLdbyname(EtsCoroutine *coro, EtsObject *thisObj, panda_file::File::StringData name); + +void EtsStbyname(EtsCoroutine *coro, EtsObject *obj, panda_file::File::StringData propName, EtsObject *value); + +EtsObject *EtsLdbyidx(EtsCoroutine *coro, EtsObject *thisObj, uint32_t index); + +bool EtsStbyidx(EtsCoroutine *coro, EtsObject *obj, uint32_t idx, EtsObject *value); + +bool EtsStbyval(EtsCoroutine *coro, EtsObject *obj, EtsObject *key, EtsObject *value); + +EtsObject *EtsLdbyval(EtsCoroutine *coro, EtsObject *thisObj, EtsObject *valObj); + +bool EtsIsinstance(EtsCoroutine *coro, EtsObject *lhsObj, EtsObject *rhsObj); + +EtsObject *EtsCall(EtsCoroutine *coro, EtsObject *funcObj, Span> args); + +EtsObject *EtsCallThis(EtsCoroutine *coro, EtsObject *thisObj, panda_file::File::StringData name, + Span> args); + +EtsObject *EtsCallNew(EtsCoroutine *coro, EtsObject *ctor, Span> args); + template inline void LookUpException(ark::Class *klass, Field *rawField); diff --git a/static_core/plugins/ets/runtime/ets_utils.cpp b/static_core/plugins/ets/runtime/ets_utils.cpp index fcf69e25bc..3f0810e758 100644 --- a/static_core/plugins/ets/runtime/ets_utils.cpp +++ b/static_core/plugins/ets/runtime/ets_utils.cpp @@ -139,4 +139,87 @@ EtsField *ManglingUtils::GetFieldIDByDisplayName(EtsClass *klass, const PandaStr return field; } + +EtsObject *GetPropertyValue(EtsCoroutine *coro, const EtsObject *etsObj, EtsField *field) +{ + EtsType type = field->GetEtsType(); + switch (type) { + case EtsType::BOOLEAN: { + auto etsVal = etsObj->GetFieldPrimitive(field); + return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); + } + case EtsType::BYTE: { + auto etsVal = etsObj->GetFieldPrimitive(field); + return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); + } + case EtsType::CHAR: { + auto etsVal = etsObj->GetFieldPrimitive(field); + return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); + } + case EtsType::SHORT: { + auto etsVal = etsObj->GetFieldPrimitive(field); + return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); + } + case EtsType::INT: { + auto etsVal = etsObj->GetFieldPrimitive(field); + return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); + } + case EtsType::LONG: { + auto etsVal = etsObj->GetFieldPrimitive(field); + return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); + } + case EtsType::FLOAT: { + auto etsVal = etsObj->GetFieldPrimitive(field); + return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); + } + case EtsType::DOUBLE: { + auto etsVal = etsObj->GetFieldPrimitive(field); + return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); + } + case EtsType::OBJECT: { + return reinterpret_cast(etsObj->GetFieldObject(field)); + } + default: + return nullptr; + } +} + +bool SetPropertyValue(EtsCoroutine *coro, EtsObject *etsObj, EtsField *field, EtsObject *valToSet) +{ + ark::Value etsVal = GetUnboxedValue(coro, valToSet); + EtsType type = field->GetEtsType(); + switch (type) { + case EtsType::BOOLEAN: + etsObj->SetFieldPrimitive(field, etsVal.GetAs()); + break; + case EtsType::BYTE: + etsObj->SetFieldPrimitive(field, etsVal.GetAs()); + break; + case EtsType::CHAR: + etsObj->SetFieldPrimitive(field, etsVal.GetAs()); + break; + case EtsType::SHORT: + etsObj->SetFieldPrimitive(field, etsVal.GetAs()); + break; + case EtsType::INT: + etsObj->SetFieldPrimitive(field, etsVal.GetAs()); + break; + case EtsType::LONG: + etsObj->SetFieldPrimitive(field, etsVal.GetAs()); + break; + case EtsType::FLOAT: + etsObj->SetFieldPrimitive(field, etsVal.GetAs()); + break; + case EtsType::DOUBLE: + etsObj->SetFieldPrimitive(field, etsVal.GetAs()); + break; + case EtsType::OBJECT: + etsObj->SetFieldObject(field, reinterpret_cast(valToSet)); + break; + default: + return false; + } + + return true; +} } // namespace ark::ets diff --git a/static_core/plugins/ets/runtime/ets_utils.h b/static_core/plugins/ets/runtime/ets_utils.h index 755519fa16..e640934798 100644 --- a/static_core/plugins/ets/runtime/ets_utils.h +++ b/static_core/plugins/ets/runtime/ets_utils.h @@ -26,12 +26,18 @@ namespace ark::ets { static constexpr char const ETSGLOBAL_CLASS_NAME[] = "ETSGLOBAL"; // NOLINTEND(modernize-avoid-c-arrays) +static constexpr std::string_view INDEXED_INT_GET_METHOD_SIGNATURE = "I:Lstd/core/Object;"; +static constexpr std::string_view INDEXED_INT_SET_METHOD_SIGNATURE = "ILstd/core/Object;:V"; + bool IsEtsGlobalClassName(const std::string &descriptor); EtsObject *GetBoxedValue(EtsCoroutine *coro, Value value, EtsType type); Value GetUnboxedValue(EtsCoroutine *coro, EtsObject *obj); +EtsObject *GetPropertyValue(EtsCoroutine *coro, const EtsObject *etsObj, EtsField *field); +bool SetPropertyValue(EtsCoroutine *coro, EtsObject *etsObj, EtsField *field, EtsObject *valToSet); + class LambdaUtils { public: PANDA_PUBLIC_API static void InvokeVoid(EtsCoroutine *coro, EtsObject *lambda); diff --git a/static_core/plugins/ets/runtime/interop_js/intrinsics_api_impl.cpp b/static_core/plugins/ets/runtime/interop_js/intrinsics_api_impl.cpp index 824b87898d..1e950f9a80 100644 --- a/static_core/plugins/ets/runtime/interop_js/intrinsics_api_impl.cpp +++ b/static_core/plugins/ets/runtime/interop_js/intrinsics_api_impl.cpp @@ -516,6 +516,19 @@ uint8_t JSRuntimeHasProperty(JSValue *object, EtsString *name) return static_cast(result); } +static napi_value JSRuntimeGetPropertyImpl(EtsCoroutine *coro, napi_env env, JSValue *object, JSValue *property) +{ + auto jsThis = JSConvertJSValue::WrapWithNullCheck(env, object); + auto key = JSConvertJSValue::WrapWithNullCheck(env, property); + + napi_value result; + { + ScopedNativeCodeThread nativeScope(coro); + NAPI_CHECK_FATAL(napi_get_property(env, jsThis, key, &result)); + } + return result; +} + JSValue *JSRuntimeGetProperty(JSValue *object, JSValue *property) { auto coro = EtsCoroutine::GetCurrent(); @@ -524,15 +537,136 @@ JSValue *JSRuntimeGetProperty(JSValue *object, JSValue *property) auto env = ctx->GetJSEnv(); NapiScope jsHandleScope(env); + auto result = JSRuntimeGetPropertyImpl(coro, env, object, property); + return JSConvertJSValue::UnwrapWithNullCheck(ctx, env, result).value(); +} + +void SetPropertyWithObject(JSValue *object, JSValue *property, EtsObject *value) +{ + auto coro = EtsCoroutine::GetCurrent(); + auto ctx = InteropCtx::Current(coro); + INTEROP_CODE_SCOPE_ETS(coro); + auto env = ctx->GetJSEnv(); + NapiScope jsHandleScope(env); + auto jsThis = JSConvertJSValue::WrapWithNullCheck(env, object); auto key = JSConvertJSValue::WrapWithNullCheck(env, property); + auto jsValue = JSRefConvertResolve(ctx, value->GetClass()->GetRuntimeClass())->Wrap(ctx, value); - napi_value result; { ScopedNativeCodeThread nativeScope(coro); - NAPI_CHECK_FATAL(napi_get_property(env, jsThis, key, &result)); + NAPI_CHECK_FATAL(napi_set_property(env, jsThis, key, jsValue)); } - return JSConvertJSValue::UnwrapWithNullCheck(ctx, env, result).value(); +} + +void SetIndexedPropertyWithObject(JSValue *object, uint32_t index, EtsObject *value) +{ + auto coro = EtsCoroutine::GetCurrent(); + auto ctx = InteropCtx::Current(coro); + INTEROP_CODE_SCOPE_ETS(coro); + auto env = ctx->GetJSEnv(); + NapiScope jsHandleScope(env); + + auto jsThis = JSConvertJSValue::WrapWithNullCheck(env, object); + auto jsValue = JSRefConvertResolve(ctx, value->GetClass()->GetRuntimeClass())->Wrap(ctx, value); + + { + ScopedNativeCodeThread nativeScope(coro); + NAPI_CHECK_FATAL(napi_set_element(env, jsThis, index, jsValue)); + } +} + +void SetNamedPropertyWithObject(JSValue *object, const char *key, EtsObject *value) +{ + auto coro = EtsCoroutine::GetCurrent(); + auto ctx = InteropCtx::Current(coro); + INTEROP_CODE_SCOPE_ETS(coro); + auto env = ctx->GetJSEnv(); + NapiScope jsHandleScope(env); + + auto jsThis = JSConvertJSValue::WrapWithNullCheck(env, object); + auto jsValue = JSRefConvertResolve(ctx, value->GetClass()->GetRuntimeClass())->Wrap(ctx, value); + + { + ScopedNativeCodeThread nativeScope(coro); + NAPI_CHECK_FATAL(napi_set_named_property(env, jsThis, key, jsValue)); + } +} + +EtsObject *GetPropertyObject(JSValue *object, JSValue *property) +{ + auto coro = EtsCoroutine::GetCurrent(); + auto ctx = InteropCtx::Current(coro); + INTEROP_CODE_SCOPE_ETS(coro); + auto env = ctx->GetJSEnv(); + NapiScope jsHandleScope(env); + + auto result = JSRuntimeGetPropertyImpl(coro, env, object, property); + return JSConvertEtsObject::UnwrapWithNullCheck(ctx, env, result).value(); +} + +EtsObject *GetNamedPropertyObject(JSValue *object, const char *property) +{ + auto coro = EtsCoroutine::GetCurrent(); + auto ctx = InteropCtx::Current(coro); + INTEROP_CODE_SCOPE_ETS(coro); + auto env = ctx->GetJSEnv(); + NapiScope jsHandleScope(env); + + return JSValueGetByName(ctx, object, property).value(); +} + +JSValue *GetNamedPropertyJSValue(JSValue *object, const char *property) +{ + auto coro = EtsCoroutine::GetCurrent(); + auto ctx = InteropCtx::Current(coro); + INTEROP_CODE_SCOPE_ETS(coro); + auto env = ctx->GetJSEnv(); + NapiScope jsHandleScope(env); + + return JSValueGetByName(ctx, object, property).value(); +} + +EtsObject *GetPropertyObjectByString(JSValue *object, const char *property) +{ + auto coro = EtsCoroutine::GetCurrent(); + auto ctx = InteropCtx::Current(coro); + INTEROP_CODE_SCOPE_ETS(coro); + auto env = ctx->GetJSEnv(); + NapiScope jsHandleScope(env); + return JSValueGetByName(ctx, object, property).value(); +} + +EtsObject *InvokeWithObjectReturn(JSValue *thisObj, JSValue *func, Span> args) +{ + auto coro = EtsCoroutine::GetCurrent(); + auto ctx = InteropCtx::Current(coro); + INTEROP_CODE_SCOPE_ETS(coro); + auto env = ctx->GetJSEnv(); + NapiScope jsHandleScope(env); + + PandaVector realArgs; + + for (auto &objHeader : args) { + EtsObject *arg = EtsObject::FromCoreType(objHeader.GetPtr()); + auto refconv = JSRefConvertResolve(ctx, arg->GetClass()->GetRuntimeClass()); + auto realArg = refconv->Wrap(ctx, arg); + realArgs.push_back(realArg); + } + + napi_value retVal; + napi_status jsStatus; + auto recvEtsObject = JSConvertJSValue::WrapWithNullCheck(env, thisObj); + auto funcEtsObject = JSConvertJSValue::WrapWithNullCheck(env, func); + { + ScopedNativeCodeThread nativeScope(coro); + jsStatus = napi_call_function(env, recvEtsObject, funcEtsObject, realArgs.size(), realArgs.data(), &retVal); + } + if (jsStatus != napi_ok) { + ctx->ForwardJSException(coro); + return nullptr; + } + return JSConvertEtsObject::UnwrapWithNullCheck(ctx, env, retVal).value(); } uint8_t JSRuntimeHasPropertyJSValue(JSValue *object, JSValue *property) @@ -698,6 +832,38 @@ JSValue *JSRuntimeInstantiate(JSValue *callable, EtsArray *args) return JSConvertJSValue::UnwrapWithNullCheck(ctx, env, retVal).value(); } +EtsObject *CreateObject(JSValue *ctor, Span> args) +{ + auto coro = EtsCoroutine::GetCurrent(); + auto ctx = InteropCtx::Current(coro); + INTEROP_CODE_SCOPE_ETS(coro); + auto env = ctx->GetJSEnv(); + NapiScope jsHandleScope(env); + + PandaVector realArgs; + + for (auto &objHeader : args) { + EtsObject *arg = EtsObject::FromCoreType(objHeader.GetPtr()); + auto refconv = JSRefConvertResolve(ctx, arg->GetClass()->GetRuntimeClass()); + auto realArg = refconv->Wrap(ctx, arg); + realArgs.push_back(realArg); + } + + auto initFunc = JSConvertJSValue::WrapWithNullCheck(env, ctor); + napi_status jsStatus; + napi_value retVal; + { + ScopedNativeCodeThread nativeScope(coro); + jsStatus = napi_new_instance(env, initFunc, realArgs.size(), realArgs.data(), &retVal); + } + + if (jsStatus != napi_ok) { + ctx->ForwardJSException(coro); + return nullptr; + } + return JSConvertEtsObject::UnwrapWithNullCheck(ctx, env, retVal).value(); +} + uint8_t JSRuntimeIsPromise(JSValue *object) { auto coro = EtsCoroutine::GetCurrent(); diff --git a/static_core/plugins/ets/runtime/interop_js/intrinsics_api_impl.h b/static_core/plugins/ets/runtime/interop_js/intrinsics_api_impl.h index b7bb49a8db..f4b298447b 100644 --- a/static_core/plugins/ets/runtime/interop_js/intrinsics_api_impl.h +++ b/static_core/plugins/ets/runtime/interop_js/intrinsics_api_impl.h @@ -60,6 +60,15 @@ JSValue *JSRuntimeInvoke(JSValue *recv, JSValue *func, EtsArray *args); JSValue *JSRuntimeInstantiate(JSValue *callable, EtsArray *args); EtsString *JSValueToString(JSValue *object); napi_value ToLocal(void *value); +void SetPropertyWithObject(JSValue *object, JSValue *property, EtsObject *value); +void SetIndexedPropertyWithObject(JSValue *object, uint32_t index, EtsObject *value); +void SetNamedPropertyWithObject(JSValue *object, const char *key, EtsObject *value); +EtsObject *GetPropertyObject(JSValue *object, JSValue *property); +EtsObject *GetPropertyObjectByString(JSValue *object, const char *property); +EtsObject *GetNamedPropertyObject(JSValue *object, const char *property); +JSValue *GetNamedPropertyJSValue(JSValue *object, const char *property); +EtsObject *InvokeWithObjectReturn(JSValue *thisObj, JSValue *function, Span> args); +EtsObject *CreateObject(JSValue *ctor, Span> args); void *CompilerGetJSNamedProperty(void *val, char *propStr); void *CompilerGetJSProperty(void *val, void *prop); void *CompilerGetJSElement(void *val, int32_t index); @@ -127,7 +136,7 @@ void JSValueNamedSetter(JSValue *etsJsValue, EtsString *etsPropName, typename T: } template -typename T::cpptype JSValueIndexedGetter(JSValue *etsJsValue, int32_t index) +typename T::cpptype JSValueIndexedGetter(JSValue *etsJsValue, int64_t index) { auto coro = EtsCoroutine::GetCurrent(); auto ctx = InteropCtx::Current(coro); @@ -166,7 +175,7 @@ void JSValueIndexedSetter(JSValue *etsJsValue, int32_t index, typename T::cpptyp NapiScope jsHandleScope(env); auto rec = napi_set_element(env, JSConvertJSValue::WrapWithNullCheck(env, etsJsValue), index, - JSConvertJSValue::WrapWithNullCheck(env, value)); + T::WrapWithNullCheck(env, value)); if (rec != napi_ok) { ctx->ForwardJSException(coro); } diff --git a/static_core/plugins/ets/runtime/interop_js/js_convert.h b/static_core/plugins/ets/runtime/interop_js/js_convert.h index 4b2250bb7f..6c327dd006 100644 --- a/static_core/plugins/ets/runtime/interop_js/js_convert.h +++ b/static_core/plugins/ets/runtime/interop_js/js_convert.h @@ -242,6 +242,18 @@ JSCONVERT_UNWRAP(JSValue) return JSValue::Create(EtsCoroutine::GetCurrent(), ctx, jsVal); } +JSCONVERT_DEFINE_TYPE(EtsObject, EtsObject *); +JSCONVERT_WRAP(EtsObject) +{ + InteropFatal("Wrap of EtsObject should be done with relevant converter"); + UNREACHABLE(); +} +JSCONVERT_UNWRAP(EtsObject) +{ + auto objectConverter = ctx->GetEtsClassWrappersCache()->Lookup(PlatformTypes()->coreObject); + return objectConverter->Unwrap(ctx, jsVal); +} + // ESError convertors are supposed to box JSValue objects, do not treat them in any other way JSCONVERT_DEFINE_TYPE(ESError, EtsObject *); JSCONVERT_WRAP(ESError) @@ -388,8 +400,8 @@ JSCONVERT_UNWRAP(EtsNull) #undef JSCONVERT_UNWRAP template -static ALWAYS_INLINE inline std::optional JSValueGetByName(InteropCtx *ctx, JSValue *jsvalue, - const char *name) +ALWAYS_INLINE inline std::optional JSValueGetByName(InteropCtx *ctx, JSValue *jsvalue, + const char *name) { auto env = ctx->GetJSEnv(); napi_value jsVal = jsvalue->GetNapiValue(env); @@ -405,8 +417,8 @@ static ALWAYS_INLINE inline std::optional JSValueGetByName( } template -[[nodiscard]] static ALWAYS_INLINE inline bool JSValueSetByName(InteropCtx *ctx, JSValue *jsvalue, const char *name, - typename T::cpptype etsPropVal) +[[nodiscard]] ALWAYS_INLINE inline bool JSValueSetByName(InteropCtx *ctx, JSValue *jsvalue, const char *name, + typename T::cpptype etsPropVal) { auto env = ctx->GetJSEnv(); napi_value jsVal = jsvalue->GetNapiValue(env); diff --git a/static_core/plugins/ets/runtime/interop_js/js_value.h b/static_core/plugins/ets/runtime/interop_js/js_value.h index d45b46f84d..71209efa71 100644 --- a/static_core/plugins/ets/runtime/interop_js/js_value.h +++ b/static_core/plugins/ets/runtime/interop_js/js_value.h @@ -166,6 +166,73 @@ public: return MEMBER_OFFSET(JSValue, type_); } + bool StrictEquals(JSValue *that) + { + if (this->GetType() != that->GetType()) { + return false; + } + switch (this->GetType()) { + case napi_string: + return this->GetString().Data() == that->GetString().Data(); + case napi_undefined: + case napi_null: + return true; + case napi_boolean: + return this->GetBoolean() == that->GetBoolean(); + case napi_number: + return this->GetNumber() == that->GetNumber(); + case napi_bigint: + return this->GetBigInt() == that->GetBigInt(); + default: { + ASSERT(IsRefType(GetType())); +#if defined(PANDA_TARGET_OHOS) || defined(PANDA_JS_ETS_HYBRID_MODE) + auto env = InteropCtx::Current()->GetJSEnv(); + return *((uintptr_t *)(this->GetRefValue(env))) == *((uintptr_t *)(that->GetRefValue(env))); +#else + INTEROP_LOG(ERROR) << "unable to perform gc-safe strict equal without hybrid VM"; + return false; +#endif + } + } + } + + bool IsTrue() + { + switch (this->GetType()) { + case napi_string: + return !this->GetString().Data()->empty(); + case napi_undefined: + case napi_null: + return true; + case napi_number: + return this->GetNumber() != 0; + case napi_bigint: + return this->GetBigInt()->second != 0; + default: + return true; + } + } + + PandaString TypeOf() + { + switch (this->GetType()) { + case napi_string: + return "string"; + case napi_undefined: + return "undefined"; + case napi_null: + return "object"; + case napi_number: + return "number"; + case napi_bigint: + return "bigint"; + case napi_function: + return "function"; + default: + return "object"; + } + } + JSValue() = delete; private: diff --git a/static_core/plugins/ets/runtime/interpreter/any_opcodes.inc b/static_core/plugins/ets/runtime/interpreter/any_opcodes.inc new file mode 100644 index 0000000000..029dadc314 --- /dev/null +++ b/static_core/plugins/ets/runtime/interpreter/any_opcodes.inc @@ -0,0 +1,115 @@ +/** + * 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. + */ + +#ifndef ETS_RUNTIME_INTERPRETER_ANY_OPCODES_H +#define ETS_RUNTIME_INTERPRETER_ANY_OPCODES_H + +#include "plugins/ets/runtime/ets_stubs.h" + +namespace ark::intrinsics { + +ObjectHeader *ETSAnyLdbyname(ManagedThread *thread, Frame *frame, ObjectHeader *thisObj, FileEntityId id) +{ + auto pf = frame->GetMethod()->GetPandaFile(); + auto ldObj = ets::EtsLdbyname(ets::EtsCoroutine::CastFromThread(thread), ets::EtsObject::FromCoreType(thisObj), + pf->GetStringData(panda_file::File::EntityId(id))) + ->GetCoreType(); + if (ldObj == nullptr) { + interpreter::RuntimeInterface::ThrowNullPointerException(); + } + return ldObj; +} + +void ETSAnyStbyname(ManagedThread *thread, Frame *frame, ObjectHeader *thisObj, FileEntityId id, ObjectHeader *val) +{ + auto pf = frame->GetMethod()->GetPandaFile(); + ets::EtsStbyname(ets::EtsCoroutine::CastFromThread(thread), ets::EtsObject::FromCoreType(thisObj), + pf->GetStringData(panda_file::File::EntityId(id)), ets::EtsObject::FromCoreType(val)); +} + +// NOTE(nsizov): remove maybe_unused when implementations delivered +ObjectHeader *ETSAnyLdbyidx(ManagedThread *thread, ObjectHeader *thisObj, double key) +{ + auto idx = static_cast(key); + auto ldObj = ets::EtsLdbyidx(ets::EtsCoroutine::CastFromThread(thread), ets::EtsObject::FromCoreType(thisObj), idx) + ->GetCoreType(); + if (ldObj == nullptr) { + interpreter::RuntimeInterface::ThrowNullPointerException(); + } + return ldObj; +} + +// NOTE(nsizov): remove maybe_unused when implementations delivered +void ETSAnyStbyidx(ManagedThread *thread, ObjectHeader *thisObj, double key, ObjectHeader *val) +{ + auto idx = static_cast(key); + ets::EtsStbyidx(ets::EtsCoroutine::CastFromThread(thread), ets::EtsObject::FromCoreType(thisObj), idx, + ets::EtsObject::FromCoreType(val)); +} + +ObjectHeader *ETSAnyLdbyval(ManagedThread *thread, ObjectHeader *thisObj, ObjectHeader *key) +{ + auto ldObj = ets::EtsLdbyval(ets::EtsCoroutine::CastFromThread(thread), ets::EtsObject::FromCoreType(thisObj), + ets::EtsObject::FromCoreType(key)) + ->GetCoreType(); + if (ldObj == nullptr) { + interpreter::RuntimeInterface::ThrowNullPointerException(); + } + return ldObj; +} + +void ETSAnyStbyval(ManagedThread *thread, ObjectHeader *thisObj, ObjectHeader *key, ObjectHeader *val) +{ + if (key == nullptr) { + interpreter::RuntimeInterface::ThrowNullPointerException(); + return; + } + ets::EtsStbyval(ets::EtsCoroutine::CastFromThread(thread), ets::EtsObject::FromCoreType(thisObj), + ets::EtsObject::FromCoreType(key), ets::EtsObject::FromCoreType(val)); +} + +bool ETSAnyIsinstance(ManagedThread *thread, ObjectHeader *lhs, ObjectHeader *rhs) +{ + if (rhs == nullptr) { + interpreter::RuntimeInterface::ThrowNullPointerException(); + return false; + } + return ets::EtsIsinstance(ets::EtsCoroutine::CastFromThread(thread), ets::EtsObject::FromCoreType(lhs), + ets::EtsObject::FromCoreType(rhs)); +} + +ObjectHeader *ETSAnyCall(ManagedThread *thread, ObjectHeader *func, Span> args) +{ + return ets::EtsCall(ets::EtsCoroutine::CastFromThread(thread), ets::EtsObject::FromCoreType(func), args) + ->GetCoreType(); +} + +ObjectHeader *ETSAnyCallThis(ManagedThread *thread, Frame *frame, ObjectHeader *thisObj, FileEntityId name, + Span> args) +{ + auto pf = frame->GetMethod()->GetPandaFile(); + return ets::EtsCallThis(ets::EtsCoroutine::CastFromThread(thread), ets::EtsObject::FromCoreType(thisObj), + pf->GetStringData(panda_file::File::EntityId(name)), args) + ->GetCoreType(); +} + +ObjectHeader *ETSAnyCallNew(ManagedThread *thread, ObjectHeader *ctor, Span> args) +{ + return ets::EtsCallNew(ets::EtsCoroutine::CastFromThread(thread), ets::EtsObject::FromCoreType(ctor), args) + ->GetCoreType(); +} + +} // namespace ark::intrinsics +#endif // ETS_RUNTIME_INTERPRETER_ANY_OPCODES_H \ No newline at end of file diff --git a/static_core/plugins/ets/runtime/static_object_accessor.cpp b/static_core/plugins/ets/runtime/static_object_accessor.cpp index 0569ba5398..54cf528fb4 100644 --- a/static_core/plugins/ets/runtime/static_object_accessor.cpp +++ b/static_core/plugins/ets/runtime/static_object_accessor.cpp @@ -34,50 +34,6 @@ bool StaticObjectAccessor::HasProperty([[maybe_unused]] panda::ThreadHolder *thr return etsObj->GetClass()->GetFieldIDByName(name) != nullptr; } -panda::BoxedValue GetPropertyValue(EtsCoroutine *coro, const EtsObject *etsObj, EtsField *field) -{ - EtsType type = field->GetEtsType(); - switch (type) { - case EtsType::BOOLEAN: { - auto etsVal = etsObj->GetFieldPrimitive(field); - return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); - } - case EtsType::BYTE: { - auto etsVal = etsObj->GetFieldPrimitive(field); - return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); - } - case EtsType::CHAR: { - auto etsVal = etsObj->GetFieldPrimitive(field); - return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); - } - case EtsType::SHORT: { - auto etsVal = etsObj->GetFieldPrimitive(field); - return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); - } - case EtsType::INT: { - auto etsVal = etsObj->GetFieldPrimitive(field); - return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); - } - case EtsType::LONG: { - auto etsVal = etsObj->GetFieldPrimitive(field); - return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); - } - case EtsType::FLOAT: { - auto etsVal = etsObj->GetFieldPrimitive(field); - return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); - } - case EtsType::DOUBLE: { - auto etsVal = etsObj->GetFieldPrimitive(field); - return reinterpret_cast(GetBoxedValue(coro, ark::Value(etsVal), field->GetEtsType())); - } - case EtsType::OBJECT: { - return reinterpret_cast(etsObj->GetFieldObject(field)); - } - default: - return nullptr; - } -} - panda::BoxedValue StaticObjectAccessor::GetProperty([[maybe_unused]] panda::ThreadHolder *thread, const panda::BaseObject *obj, const char *name) { @@ -92,7 +48,7 @@ panda::BoxedValue StaticObjectAccessor::GetProperty([[maybe_unused]] panda::Thre if (field == nullptr) { return nullptr; } - return GetPropertyValue(coro, etsObj, field); + return reinterpret_cast(GetPropertyValue(coro, etsObj, field)); } bool StaticObjectAccessor::SetProperty([[maybe_unused]] panda::ThreadHolder *thread, panda::BaseObject *obj, @@ -104,45 +60,14 @@ bool StaticObjectAccessor::SetProperty([[maybe_unused]] panda::ThreadHolder *thr if (etsObj == nullptr) { return false; } - ark::Value etsVal = GetUnboxedValue(coro, reinterpret_cast(value)); + auto fieldIndex = etsObj->GetClass()->GetFieldIndexByName(name); EtsField *field = etsObj->GetClass()->GetFieldByIndex(fieldIndex); if (field == nullptr) { return false; } - EtsType type = field->GetEtsType(); - switch (type) { - case EtsType::BOOLEAN: - etsObj->SetFieldPrimitive(field, etsVal.GetAs()); - break; - case EtsType::BYTE: - etsObj->SetFieldPrimitive(field, etsVal.GetAs()); - break; - case EtsType::CHAR: - etsObj->SetFieldPrimitive(field, etsVal.GetAs()); - break; - case EtsType::SHORT: - etsObj->SetFieldPrimitive(field, etsVal.GetAs()); - break; - case EtsType::INT: - etsObj->SetFieldPrimitive(field, etsVal.GetAs()); - break; - case EtsType::LONG: - etsObj->SetFieldPrimitive(field, etsVal.GetAs()); - break; - case EtsType::FLOAT: - etsObj->SetFieldPrimitive(field, etsVal.GetAs()); - break; - case EtsType::DOUBLE: - etsObj->SetFieldPrimitive(field, etsVal.GetAs()); - break; - case EtsType::OBJECT: - etsObj->SetFieldObject(field, reinterpret_cast(value)); - break; - default: - return false; - } - return true; + + return SetPropertyValue(coro, etsObj, field, reinterpret_cast(value)); } bool StaticObjectAccessor::HasElementByIdx([[maybe_unused]] panda::ThreadHolder *thread, diff --git a/static_core/plugins/ets/runtime/types/ets_class.cpp b/static_core/plugins/ets/runtime/types/ets_class.cpp index 62557fc19f..666d2fc171 100644 --- a/static_core/plugins/ets/runtime/types/ets_class.cpp +++ b/static_core/plugins/ets/runtime/types/ets_class.cpp @@ -489,6 +489,9 @@ void EtsClass::Initialize(EtsClass *superClass, uint16_t accessFlags, bool isPri if (UNLIKELY(GetBase() != nullptr && GetBase()->IsEtsEnum())) { flags |= (IS_ETS_ENUM | IS_VALUE_TYPED); } + if (UNLIKELY(GetRuntimeClass()->IsXRefClass())) { + flags |= IS_VALUE_TYPED; + } auto *runtimeClass = GetRuntimeClass(); auto *pfile = runtimeClass->GetPandaFile(); diff --git a/static_core/plugins/ets/runtime/types/ets_string.h b/static_core/plugins/ets/runtime/types/ets_string.h index 2355b097cc..1f3ffc7784 100644 --- a/static_core/plugins/ets/runtime/types/ets_string.h +++ b/static_core/plugins/ets/runtime/types/ets_string.h @@ -16,13 +16,15 @@ #ifndef PANDA_PLUGINS_ETS_RUNTIME_FFI_CLASSES_ETS_STRING_H_ #define PANDA_PLUGINS_ETS_RUNTIME_FFI_CLASSES_ETS_STRING_H_ -#include "runtime/include/runtime.h" -#include "runtime/include/coretypes/string-inl.h" +#include + +#include "libpandabase/utils/utf.h" #include "plugins/ets/runtime/types/ets_array.h" #include "plugins/ets/runtime/types/ets_box_primitive.h" #include "plugins/ets/runtime/types/ets_object.h" #include "plugins/ets/runtime/napi/ets_napi.h" -#include +#include "runtime/include/runtime.h" +#include "runtime/include/coretypes/string-inl.h" namespace ark::ets { diff --git a/static_core/plugins/ets/tests/interop_js/cmake/interop_js_tests_arkjsvm.cmake b/static_core/plugins/ets/tests/interop_js/cmake/interop_js_tests_arkjsvm.cmake index 276025f4bb..4fae23fe68 100644 --- a/static_core/plugins/ets/tests/interop_js/cmake/interop_js_tests_arkjsvm.cmake +++ b/static_core/plugins/ets/tests/interop_js/cmake/interop_js_tests_arkjsvm.cmake @@ -91,7 +91,7 @@ endfunction(compile_dynamic_file) # ETS_CONFIG # path/to/arktsconfig.json # PACKAGE_NAME -# unit1_test +# unit1_test # ) function(panda_ets_interop_js_gtest TARGET) # Parse arguments @@ -99,7 +99,7 @@ function(panda_ets_interop_js_gtest TARGET) ARG "COMPILATION_JS_WITH_CJS_ON;COMPILATION_WITH_RUNTIMELINKER" "ETS_CONFIG;PACKAGE_NAME" - "CPP_SOURCES;ETS_SOURCES;JS_SOURCES;TS_SOURCES;JS_TEST_SOURCE;LIBRARIES" + "CPP_SOURCES;ETS_SOURCES;JS_SOURCES;TS_SOURCES;JS_TEST_SOURCE;ASM_SOURCE;LIBRARIES" ${ARGN} ) @@ -112,12 +112,14 @@ function(panda_ets_interop_js_gtest TARGET) OUTPUT_SUFFIX ".so" ) - set(TARGET_GTEST_PACKAGE ${TARGET}_gtest_package) - panda_ets_package_gtest(${TARGET_GTEST_PACKAGE} - ETS_SOURCES ${ARG_ETS_SOURCES} - ETS_CONFIG ${ARG_ETS_CONFIG} - ) - add_dependencies(${TARGET} ${TARGET_GTEST_PACKAGE}) + if(DEFINED ARG_ETS_SOURCES) + set(TARGET_GTEST_PACKAGE ${TARGET}_gtest_package) + panda_ets_package_gtest(${TARGET_GTEST_PACKAGE} + ETS_SOURCES ${ARG_ETS_SOURCES} + ETS_CONFIG ${ARG_ETS_CONFIG} + ) + add_dependencies(${TARGET} ${TARGET_GTEST_PACKAGE}) + endif() set(JS_COMPILATION_OPTIONS --module --merge-abc --enable-ets-implements) if(ARG_COMPILATION_JS_WITH_CJS_ON) @@ -141,21 +143,32 @@ function(panda_ets_interop_js_gtest TARGET) ) endif() - # if not set PACKAGE_NAME, using first ets file as its name; - set(ETS_SOURCES_NUM) - list(LENGTH ARG_ETS_SOURCES ETS_SOURCES_NUM) - if(NOT DEFINED ARG_PACKAGE_NAME AND ${ETS_SOURCES_NUM} EQUAL 1) - list(GET ARG_ETS_SOURCES 0 PACKATE_FILE) - get_filename_component(ARG_PACKAGE_NAME ${PACKATE_FILE} NAME_WE) - elseif(NOT DEFINED ARG_PACKAGE_NAME) - message(FATAL_ERROR "Please provide PACKAGE_NAME for ${TARGET}") + if(DEFINED ARG_ASM_SOURCE) + get_filename_component(ASM_DIR_PATH ${ARG_ASM_SOURCE} DIRECTORY) + get_filename_component(ASM_FILE_NAME ${ARG_ASM_SOURCE} NAME) + add_panda_assembly(TARGET ${TARGET}_asm_abc INDIR ${ASM_DIR_PATH} SOURCE ${ASM_FILE_NAME} + OUTDIR ${CMAKE_CURRENT_BINARY_DIR} TARGETNAME ${TARGET}_asm) + set(ARK_ETS_INTEROP_JS_GTEST_ASM_ABC_PATH "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_asm.abc") endif() - # Add launcher <${TARGET}_gtests> target - set(ARK_ETS_INTEROP_JS_GTEST_ABC_PATH_PATH ${PANDA_BINARY_ROOT}/abc-gtests/${TARGET_GTEST_PACKAGE}.zip) - if(ARG_COMPILATION_WITH_RUNTIMELINKER) - set(ARK_ETS_INTEROP_JS_GTEST_ABC_PATH_PATH "") + # if not set PACKAGE_NAME, using first ets file as its name; + if(DEFINED ARG_ETS_SOURCES) + set(ETS_SOURCES_NUM) + list(LENGTH ARG_ETS_SOURCES ETS_SOURCES_NUM) + if(NOT DEFINED ARG_PACKAGE_NAME AND ${ETS_SOURCES_NUM} EQUAL 1) + list(GET ARG_ETS_SOURCES 0 PACKATE_FILE) + get_filename_component(ARG_PACKAGE_NAME ${PACKATE_FILE} NAME_WE) + elseif(NOT DEFINED ARG_PACKAGE_NAME) + message(FATAL_ERROR "Please provide PACKAGE_NAME for ${TARGET}") + endif() + + # Add launcher <${TARGET}_gtests> target + set(ARK_ETS_INTEROP_JS_GTEST_ABC_PATH_PATH ${PANDA_BINARY_ROOT}/abc-gtests/${TARGET_GTEST_PACKAGE}.zip) + if(ARG_COMPILATION_WITH_RUNTIMELINKER) + set(ARK_ETS_INTEROP_JS_GTEST_ABC_PATH_PATH "") + endif() endif() + panda_ets_add_gtest( NAME ${TARGET} NO_EXECUTABLE @@ -166,6 +179,7 @@ function(panda_ets_interop_js_gtest TARGET) "INTEROP_TEST_BUILD_DIR=${PANDA_BINARY_ROOT}/tests/ets_interop_js" "ARK_ETS_STDLIB_PATH=${PANDA_BINARY_ROOT}/plugins/ets/etsstdlib.abc" "ARK_ETS_INTEROP_JS_GTEST_ABC_PATH=${ARK_ETS_INTEROP_JS_GTEST_ABC_PATH_PATH}" + "ARK_ETS_INTEROP_JS_GTEST_ASM_ABC_PATH=${ARK_ETS_INTEROP_JS_GTEST_ASM_ABC_PATH}" "ARK_ETS_INTEROP_JS_GTEST_SOURCES=${CMAKE_CURRENT_SOURCE_DIR}" "ARK_ETS_INTEROP_JS_GTEST_DIR=${INTEROP_TESTS_DIR}" "FIXED_ISSUES=${FIXED_ISSUES}" @@ -187,6 +201,10 @@ function(panda_ets_interop_js_gtest TARGET) add_dependencies(${TARGET}_gtests ${TARGET}_dynamic_modules) endif() + if(DEFINED ARG_ASM_SOURCE) + add_dependencies(${TARGET}_gtests ${TARGET}_asm_abc) + endif() + add_dependencies(ets_interop_js_gtests ${TARGET}_gtests) endfunction(panda_ets_interop_js_gtest) @@ -288,4 +306,4 @@ function(panda_ets_interop_js_test TARGET) add_dependencies(${TARGET} ${TARGET}_js_modules) endif() add_dependencies(ets_interop_js_tests ${TARGET}) -endfunction(panda_ets_interop_js_test) +endfunction(panda_ets_interop_js_test) \ No newline at end of file diff --git a/static_core/plugins/ets/tests/interop_js/gtest_plugin/gtest_launcher.js b/static_core/plugins/ets/tests/interop_js/gtest_plugin/gtest_launcher.js index 0b205282a2..716bad76b0 100644 --- a/static_core/plugins/ets/tests/interop_js/gtest_plugin/gtest_launcher.js +++ b/static_core/plugins/ets/tests/interop_js/gtest_plugin/gtest_launcher.js @@ -64,8 +64,9 @@ function main() { let stdlibPath = helper.getEnvironmentVar('ARK_ETS_STDLIB_PATH'); let gtestAbcPath = helper.getEnvironmentVar('ARK_ETS_INTEROP_JS_GTEST_ABC_PATH'); + let asmAbcPath = helper.getEnvironmentVar('ARK_ETS_INTEROP_JS_GTEST_ASM_ABC_PATH'); + - let argv = helper.getArgv(); const arkJsNapiCliLastArgIdx = 5; @@ -75,13 +76,23 @@ function main() { return 1; } + let userPandaFiles = gtestAbcPath; + if (asmAbcPath !== '') { + if (userPandaFiles === '') { + userPandaFiles += asmAbcPath; + } else { + userPandaFiles += (':' + asmAbcPath); + } + } + let createRuntimeOptions = { 'log-level': 'info', 'log-components': 'ets_interop_js', - 'boot-panda-files': stdlibPath + ':' + gtestAbcPath, - 'panda-files': gtestAbcPath, + 'boot-panda-files': stdlibPath + ':' + userPandaFiles, + 'panda-files': userPandaFiles, 'gc-trigger-type': 'heap-trigger', 'compiler-enable-jit': 'false', + 'interpreter-type': 'irtoc' }; if (gtestName === 'ets_interop_ts_to_ets_taskpool') { diff --git a/static_core/plugins/ets/tests/interop_js/tests/interop_isa/CMakeLists.txt b/static_core/plugins/ets/tests/interop_js/tests/interop_isa/CMakeLists.txt new file mode 100644 index 0000000000..2e36b5d66f --- /dev/null +++ b/static_core/plugins/ets/tests/interop_js/tests/interop_isa/CMakeLists.txt @@ -0,0 +1,21 @@ +# 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. + + +panda_ets_interop_js_gtest(ets_interop_js__interop_isa + CPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test.cpp + ASM_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/test.pa + JS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/index.js + ETS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test.ets + ETS_CONFIG ${ETS_CONFIG} +) diff --git a/static_core/plugins/ets/tests/interop_js/tests/interop_isa/index.js b/static_core/plugins/ets/tests/interop_js/tests/interop_isa/index.js new file mode 100644 index 0000000000..dae24860c9 --- /dev/null +++ b/static_core/plugins/ets/tests/interop_js/tests/interop_isa/index.js @@ -0,0 +1,293 @@ +/** + * 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. + */ + +const etsVm = globalThis.gtest.etsVm; + +function testCallRange(a, b) { + return a + b; +} + +function testCall0() { + return 1; +} + +function testCallShort(a) { + return a + 1; +} + +class C { + f = 1; +} + +class TestNew0 { + field1 = 0; + constructor() { + } +} + +class TestNewRange { + field1; + field2; + + constructor(field1, field2) { + this.field1 = field1; + this.field2 = field2; + } + + baz() { + return 1; + } + + foo(a, b) { + return a + b; + } + + bar(a) { + return a + 1; + } +} + +class TestNewShort { + field1; + + constructor(field1) { + this.field1 = field1; + } +} + +let testInstanceOf = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'testIsInstance'); + +let doTestCallRange = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestCallRange'); +let doTestCall0 = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestCall0'); +let doTestNew0 = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestNew0'); +let doTestNewRange = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestNewRange'); +let doTestCallThis0 = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestCallThis0'); +let doTestCallThisRange = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestCallThisRange'); +let doTestCallShort = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestCallShort'); +let doTestCallThisShort = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestCallThisShort'); +let doTestNewShort = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestNewShort'); + +let doTestVal = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestVal'); +let doTestValName = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestValName'); +let doTestValNameV = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestValNameV'); +let doTestValIdx = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestValIdx'); +let doTestStVal = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestStVal'); +let doTestStValName = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestStValName'); +let doTestStValNameV = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestStValNameV'); +let doTestStValIdx = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestStValIdx'); + +let doTestLdbyvalFooStatic = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestLdbyvalFooStatic'); +let doTestLdbyvalArrayStatic = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestLdbyvalArrayStatic'); +let doTestLdByIdxArrayStatic = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestLdByIdxArrayStatic'); +let doTestStByValArrayStatic = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestStByValArrayStatic'); +let doTestStByValFooStatic = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestStByValFooStatic'); +let doTestStByIdxArrayStatic = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestStByIdxArrayStatic'); + +let doTestIsTrue = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestIsTrue'); +let doTestTypeOf = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestTypeOf'); +let doTestEquals = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestEquals'); +let doTestStrictEquals = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestStrictEquals'); + +let doTestCallStatic = etsVm.getFunction('Ltest-static/ETSGLOBAL;', 'doTestCallStatic'); + +function anyInstanceOfTest() { + ASSERT_TRUE(testInstanceOf(new C(), C)); + ASSERT_TRUE(!testInstanceOf({f: 1}, C)); +} + +function anyCallRangeTest() { + ASSERT_EQ(doTestCallRange(testCallRange, 0, 1), 1); +} + +function anyCall0Test() { + ASSERT_EQ(doTestCall0(testCall0), 1); +} + +function anyCallShortTest() { + ASSERT_EQ(doTestCallShort(testCallShort, 0), 1); +} + +function anyCallThisRangeTest() { + let obj = new TestNewRange(0, 1); + ASSERT_EQ(doTestCallThisRange(obj, 0, 1), 1); +} + +function anyCallThis0Test() { + let obj = new TestNewRange(0, 1); + ASSERT_EQ(doTestCallThis0(obj), 1); +} + +function anyCallThisShortTest() { + let obj = new TestNewRange(0, 1); + ASSERT_EQ(doTestCallThisShort(obj, 0), 1); +} + +function anyCallNewRangeTest() { + let obj = doTestNewRange(TestNewRange, 0, 1); + ASSERT_EQ(obj.field1, 0); + ASSERT_EQ(obj.field2, 1); +} + +function anyCallNew0Test() { + let obj = doTestNew0(TestNew0); + ASSERT_EQ(obj.field1, 0); +} + +function anyCallNewShortTest() { + let obj = doTestNewShort(TestNewShort, 1); + ASSERT_EQ(obj.field1, 1); +} + +function anyLdByValTest() { + ASSERT_EQ(doTestVal({a: 1}, 'a'), 1); +} + +function anyLdByNameTest() { + ASSERT_EQ(doTestValName({a: 1}), 1); +} + +function anyLdByNameVTest() { + ASSERT_EQ(doTestValNameV({a: 1}), 1); +} + +function anyLdByIdxTest() { + ASSERT_EQ(doTestValIdx({1: 1}), 1); +} + +function anyStByValTest() { + let o = {a: 0}; + doTestStVal(o, 'a', 1); + ASSERT_EQ(o.a, 1); +} + +function anyStByNameTest() { + let o = {a: 0}; + doTestStValName(o, 1); + ASSERT_EQ(o.a, 1); +} + +function anyStByNameVTest() { + let o = {a: 0}; + doTestStValNameV(o, 1); + ASSERT_EQ(o.a, 1); +} + +function anyStByIdxTest() { + let o = {1:0}; + doTestStValIdx(o, 1); + ASSERT_EQ(o[1], 1); +} + +// Enable once FE create any bytecode for pure static code +function doTestLdbyvalFooStaticTest() { + let o = doTestLdbyvalFooStatic(); + ASSERT_EQ(o, 0x55aa); +} + +// Enable once FE create any bytecode for pure static code +function doTestLdbyvalArrayStaticTest() { + let o = doTestLdbyvalArrayStatic(); + ASSERT_EQ(o, 0x7c00); +} + +// Enable once FE create any bytecode for pure static code +function doTestLdByIdxArrayStaticTest() { + let o = doTestLdByIdxArrayStatic(); + ASSERT_EQ(o, 0x7c00); +} + +// Enable once FE create any bytecode for pure static code +function doTestStByValArrayStaticTest() { + let o = doTestStByValArrayStatic(); + ASSERT_EQ(o, 0xcafe); +} + +// Enable once FE create any bytecode for pure static code +function doTestStByValFooStaticTest() { + let o = doTestStByValFooStatic(); + ASSERT_EQ(o, 0xcafe); +} + +// Enable once FE create any bytecode for pure static code +function doTestStByIdxArrayStaticTest() { + let o = doTestStByIdxArrayStatic(); + ASSERT_EQ(o, 0xcafe); +} + +function doTestAnyCallStaticTest() { + ASSERT_EQ(doTestCallStatic(), 1); +} + +function etsIsTrueTest() { + let o = new C(); + ASSERT_TRUE(doTestIsTrue(o)); + ASSERT_TRUE(!doTestIsTrue('')); + ASSERT_TRUE(!doTestIsTrue(0)); +} + +function etsTypeOfTest() { + let o = new C(); + ASSERT_EQ(doTestTypeOf(o), 'object'); +} + +function etsEqualsTest() { + let o1 = new C(); + let o2 = new C(); + ASSERT_TRUE(!doTestEquals(o1, o2)); + ASSERT_TRUE(!doTestEquals(o1, 'aaa')); + ASSERT_TRUE(doTestEquals(o1, o1)); +} + +function etsStrictEqualsTest() { + let o1 = new C(); + let o2 = new C(); + ASSERT_TRUE(!doTestStrictEquals(o1, o2)); + ASSERT_TRUE(!doTestEquals(o1, 'aaa')); + ASSERT_TRUE(doTestStrictEquals(o1, o1)); +} + +anyInstanceOfTest(); + +anyCallRangeTest(); +anyCall0Test(); +anyCallShortTest(); +anyCallThisRangeTest(); +anyCallThis0Test(); +anyCallThisShortTest(); +anyCallNewRangeTest(); +anyCallNew0Test(); +anyCallNewShortTest(); + +anyLdByValTest(); +anyLdByNameTest(); +anyLdByNameVTest(); +anyLdByIdxTest(); +anyStByValTest(); +anyStByNameTest(); +anyStByNameVTest(); +anyStByIdxTest(); + +etsIsTrueTest(); +etsTypeOfTest(); +etsEqualsTest(); +etsStrictEqualsTest(); + +doTestAnyCallStaticTest(); +doTestLdbyvalFooStaticTest(); +doTestLdbyvalArrayStaticTest(); +doTestLdByIdxArrayStaticTest(); +doTestStByValArrayStaticTest(); +doTestStByValFooStaticTest(); +doTestStByIdxArrayStaticTest(); \ No newline at end of file diff --git a/static_core/plugins/ets/tests/interop_js/tests/interop_isa/test.cpp b/static_core/plugins/ets/tests/interop_js/tests/interop_isa/test.cpp new file mode 100644 index 0000000000..7d0ca1bc59 --- /dev/null +++ b/static_core/plugins/ets/tests/interop_js/tests/interop_isa/test.cpp @@ -0,0 +1,29 @@ +/** + * 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 +#include +#include "ets_interop_js_gtest.h" + +namespace ark::ets::interop::js::testing { + +class EtsInteropIsa : public EtsInteropTest {}; + +TEST_F(EtsInteropIsa, test_interop_isa) +{ + ASSERT_TRUE(RunJsTestSuite("index.js")); +} + +} // namespace ark::ets::interop::js::testing diff --git a/static_core/plugins/ets/tests/interop_js/tests/interop_isa/test.ets b/static_core/plugins/ets/tests/interop_js/tests/interop_isa/test.ets new file mode 100644 index 0000000000..7bef8f491f --- /dev/null +++ b/static_core/plugins/ets/tests/interop_js/tests/interop_isa/test.ets @@ -0,0 +1,14 @@ +/** + * 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. + */ diff --git a/static_core/plugins/ets/tests/interop_js/tests/interop_isa/test.pa b/static_core/plugins/ets/tests/interop_js/tests/interop_isa/test.pa new file mode 100644 index 0000000000..900ac02834 --- /dev/null +++ b/static_core/plugins/ets/tests/interop_js/tests/interop_isa/test.pa @@ -0,0 +1,408 @@ +# 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. +# source binary: ./build/test-static.abc + +.language eTS + +.record escompat.Array + +.record std.core.Object + +.record std.core.String + +.record std.core.Int + +.record std.core.Int$partial + +.record std.core.IntBox + +.record std.core.IntBox$partial + +.record std.core.IntType + +.record std.core.IntType$partial + +.record std.core.IntValue + +.record std.core.IntValue$partial + +.record std.core.Double + +.record std.core.Double$partial + +.record std.core.DoubleBox + +.record std.core.DoubleBox$partial + +.record std.core.DoubleToStringCacheElement + +.record std.core.DoubleType + +.record std.core.DoubleType$partial + +.record std.core.DoubleValue + +.record std.core.DoubleValue$partial + +.record std.interop.js.JSValue + +.record std.interop.js.JSRuntime + +.record std.core.Lambda0 + +.record std.core.Function0 + +.record test-static.LambdaObject-ETSGLOBAL$lambda$invoke$0 { +} + +.record test-static.ETSGLOBAL { +} + +.record test-static.Foo { + f64 bar +} + +.function escompat.Array escompat.Array.from(std.core.Object[] a0) + +.function void test-static.Foo._ctor_(test-static.Foo a0) { + call.short std.core.Object._ctor_:(std.core.Object), a0 + fldai.64 0x40d56a8000000000 + stobj.64 a0, test-static.Foo.bar + return.void +} + +.function u1 std.interop.js.JSRuntime.__initJSCallClass() + +.function std.interop.js.JSValue std.interop.js.JSRuntime.getPropertyJSValue(std.interop.js.JSValue a0, std.core.String a1) + +.function std.interop.js.JSValue std.interop.js.JSRuntime.getUndefined() + +.function u1 test-static.ETSGLOBAL.testIsInstance(std.core.Object a0, std.core.Object a1) { + lda.obj a0 + any.isinstance a1 + return +} + +.function std.core.Object test-static.ETSGLOBAL.doTestCallRange(std.core.Object a0, std.core.Object a1, std.core.Object a2) { + lda.obj a1 + sta.obj v1 + lda.obj a2 + sta.obj v2 + any.call.range a0, v1, 0x2 + return.obj +} + +.function std.core.Object test-static.ETSGLOBAL.doTestCallShort(std.core.Object a0, std.core.Object a1) { + lda.obj a1 + sta.obj v1 + any.call.short a0, v1 + return.obj +} + +.function std.core.Object test-static.ETSGLOBAL.doTestCallThisShort(std.core.Object a0, std.core.Object a1) { + lda.obj a1 + sta.obj v1 + any.call.this.short "bar", a0, v1 + return.obj +} + +.function std.core.Object test-static.ETSGLOBAL.doTestCall0(std.core.Object a0) { + any.call.0 a0 + return.obj +} + +.function std.core.Object test-static.ETSGLOBAL.doTestNew0(std.core.Object a0) { + any.call.new.0 a0 + return.obj +} + +.function std.core.Object test-static.ETSGLOBAL.doTestNewRange(std.core.Object a0, std.core.Object a1, std.core.Object a2) { + lda.obj a1 + sta.obj v1 + lda.obj a2 + sta.obj v2 + any.call.new.range a0, v1, 0x2 + return.obj +} + +.function std.core.Object test-static.ETSGLOBAL.doTestNewShort(std.core.Object a0, std.core.Object a1) { + lda.obj a1 + sta.obj v1 + any.call.new.short a0, v1 + return.obj +} + +.function std.core.Object test-static.ETSGLOBAL.doTestCallThis0(std.core.Object a0) { + any.call.this.0 "baz", a0 + return.obj +} + +.function std.core.Object test-static.ETSGLOBAL.doTestCallThisRange(std.core.Object a0, std.core.Object a1, std.core.Object a2) { + lda.obj a1 + sta.obj v1 + lda.obj a2 + sta.obj v2 + any.call.this.range "foo", a0, v1, 0x2 + return.obj +} + +.function std.core.Object test-static.ETSGLOBAL.doTestVal(std.core.Object a0, std.core.Object a1) { + any.ldbyval a0, a1 + return.obj +} + +.function std.core.Object test-static.ETSGLOBAL.doTestValName(std.core.Object a0) { + any.ldbyname a0, "a" + return.obj +} + +.function std.core.Object test-static.ETSGLOBAL.doTestValNameV(std.core.Object a0) { + any.ldbyname.v v1, a0, "a" + lda.obj v1 + return.obj +} + +.function std.core.Object test-static.ETSGLOBAL.doTestValIdx(std.core.Object a0) { + fldai.64 0x3ff0000000000000 + any.ldbyidx a0 + return.obj +} + +.function void test-static.ETSGLOBAL.doTestStVal(std.core.Object a0, std.core.Object a1, std.core.Object a2) { + lda.obj a2 + any.stbyval a0, a1 + return.void +} + +.function void test-static.ETSGLOBAL.doTestStValName(std.core.Object a0, std.core.Object a1) { + lda.obj a1 + any.stbyname a0, "a" + return.void +} + +.function void test-static.ETSGLOBAL.doTestStValNameV(std.core.Object a0, std.core.Object a1) { + any.stbyname.v a1, a0, "a" + return.void +} + +.function void test-static.ETSGLOBAL.doTestStValIdx(std.core.Object a0, std.core.Object a1) { + fldai.64 0x3ff0000000000000 + sta.64 v0 + lda.obj a1 + any.stbyidx a0, v0 + return.void +} + +.function u1 test-static.ETSGLOBAL.doTestCompare(std.core.Object a0, std.core.Object a1) { + mov.obj v0, a0 + lda.obj v0 + sta.obj v0 + mov.obj v1, v0 + mov.obj v0, a1 + lda.obj v0 + mov.obj v0, v1 + sta.obj v1 + ets.equals v0, v1 + jeqz jump_label_0 + ldai 0x1 + jmp jump_label_1 +jump_label_0: + ldai 0x0 +jump_label_1: + return +} + +.function std.core.Object test-static.ETSGLOBAL.doTestLdbyvalFooStatic() { + initobj.short test-static.Foo._ctor_:(test-static.Foo) + sta.obj v0 + lda.str "bar" + sta.obj v1 + any.ldbyval v0, v1 + return.obj +} + +.function std.core.Object test-static.ETSGLOBAL.doTestLdbyvalArrayStatic() { + movi v1, 0x2 + newarr v0, v1, std.core.Double[] + fldai.64 0x40d56a8000000000 + call.acc.short std.core.Double.valueOf:(f64), v0, 0x0 + movi v1, 0x0 + starr.obj v0, v1 + fldai.64 0x40df000000000000 + call.acc.short std.core.Double.valueOf:(f64), v0, 0x0 + movi v1, 0x1 + starr.obj v0, v1 + call.short escompat.Array.from:(std.core.Object[]), v0 + checkcast escompat.Array + sta.obj v0 + newobj v1, std.core.Int + movi v2, 0x1 + call.short std.core.Int._ctor_:(std.core.Int,i32), v1, v2 + any.ldbyval v0, v1 + return.obj +} + +.function std.core.Object test-static.ETSGLOBAL.doTestLdByIdxArrayStatic() { + movi v1, 0x2 + newarr v0, v1, std.core.Double[] + fldai.64 0x40d56a8000000000 + call.acc.short std.core.Double.valueOf:(f64), v0, 0x0 + movi v1, 0x0 + starr.obj v0, v1 + fldai.64 0x40df000000000000 + call.acc.short std.core.Double.valueOf:(f64), v0, 0x0 + movi v1, 0x1 + starr.obj v0, v1 + call.short escompat.Array.from:(std.core.Object[]), v0 + checkcast escompat.Array + sta.obj v0 + fldai.64 0x3ff0000000000000 + any.ldbyidx v0 + return.obj +} + +.function std.core.Object test-static.ETSGLOBAL.doTestStByValArrayStatic() { + movi v1, 0x2 + newarr v0, v1, std.core.Double[] + fldai.64 0x40d56a8000000000 + call.acc.short std.core.Double.valueOf:(f64), v0, 0x0 + movi v1, 0x0 + starr.obj v0, v1 + fldai.64 0x40df000000000000 + call.acc.short std.core.Double.valueOf:(f64), v0, 0x0 + movi v1, 0x1 + starr.obj v0, v1 + call.short escompat.Array.from:(std.core.Object[]), v0 + checkcast escompat.Array + sta.obj v0 + + newobj v1, std.core.Int + movi v2, 0x1 + call.short std.core.Int._ctor_:(std.core.Int,i32), v1, v2 + + newobj v2, std.core.Double + fldai.64 0x40e95fc000000000 + call.acc.short std.core.Double._ctor_:(std.core.Double,f64), v2, 0x1 + lda.obj v2 + any.stbyval v0, v1 + + fldai.64 0x3ff0000000000000 + any.ldbyidx v0 + + return.obj +} + +.function std.core.Object test-static.ETSGLOBAL.doTestStByValFooStatic() { + initobj.short test-static.Foo._ctor_:(test-static.Foo) + sta.obj v0 + lda.str "bar" + sta.obj v1 + newobj v2, std.core.Double + fldai.64 0x40e95fc000000000 + call.acc.short std.core.Double._ctor_:(std.core.Double,f64), v2, 0x1 + lda.obj v2 + any.stbyval v0, v1 + + lda.str "bar" + sta.obj v1 + any.ldbyval v0, v1 + + return.obj +} + +.function std.core.Object test-static.ETSGLOBAL.doTestStByIdxArrayStatic() { + movi v1, 0x2 + newarr v0, v1, std.core.Double[] + fldai.64 0x40d56a8000000000 + call.acc.short std.core.Double.valueOf:(f64), v0, 0x0 + movi v1, 0x0 + starr.obj v0, v1 + fldai.64 0x40df000000000000 + call.acc.short std.core.Double.valueOf:(f64), v0, 0x0 + movi v1, 0x1 + starr.obj v0, v1 + call.short escompat.Array.from:(std.core.Object[]), v0 + checkcast escompat.Array + sta.obj v0 + fmovi.64 v1, 0x3ff0000000000000 + newobj v2, std.core.Double + fldai.64 0x40e95fc000000000 + call.acc.short std.core.Double._ctor_:(std.core.Double,f64), v2, 0x1 + lda.obj v2 + any.stbyidx v0, v1 + fldai.64 0x3ff0000000000000 + any.ldbyidx v0 + return.obj +} + +.function u1 test-static.ETSGLOBAL.doTestIsTrue(std.core.Object a0) { + ets.istrue a0 + return +} + +.function std.core.Object test-static.ETSGLOBAL.doTestTypeOf(std.core.Object a0) { + ets.typeof a0 + return.obj +} + +.function u1 test-static.ETSGLOBAL.doTestEquals(std.core.Object a0, std.core.Object a1) { + ets.equals a0, a1 + return +} + +.function u1 test-static.ETSGLOBAL.doTestStrictEquals(std.core.Object a0, std.core.Object a1) { + ets.strictequals a0, a1 + return +} + +.function void std.core.Object._ctor_(std.core.Object a0) + +.function void std.core.Int._ctor_(std.core.Int a0, i32 a1) + +.function std.core.Double std.core.Double.valueOf(f64 a0) + +.function void std.core.Double._ctor_(std.core.Double a0, f64 a1) + +.function std.core.Object std.core.Function0.invoke0(std.core.Function0 a0) + +.function void std.core.Lambda0._ctor_(std.core.Lambda0 a0) + +.function i32 test-static.LambdaObject-ETSGLOBAL$lambda$invoke$0.$_invoke(test-static.LambdaObject-ETSGLOBAL$lambda$invoke$0 a0) { + call.short test-static.ETSGLOBAL.bar:() + return +} + +.function void test-static.LambdaObject-ETSGLOBAL$lambda$invoke$0._ctor_(test-static.LambdaObject-ETSGLOBAL$lambda$invoke$0 a0) { + mov.obj v0, a0 + call.short std.core.Lambda0._ctor_:(std.core.Lambda0), v0 + return.void +} + +.function std.core.Object test-static.LambdaObject-ETSGLOBAL$lambda$invoke$0.invoke0(test-static.LambdaObject-ETSGLOBAL$lambda$invoke$0 a0) { + call.short test-static.ETSGLOBAL.bar:() + sta v0 + initobj.short std.core.Int._ctor_:(std.core.Int,i32), v0 + return.obj +} + +.function i32 test-static.ETSGLOBAL.bar() { + ldai 0x1 + return +} + +.function std.core.Object test-static.ETSGLOBAL.doTestCallStatic() { + initobj.short test-static.LambdaObject-ETSGLOBAL$lambda$invoke$0._ctor_:(test-static.LambdaObject-ETSGLOBAL$lambda$invoke$0) + sta.obj v0 + any.call.0 v0 + return.obj +} \ No newline at end of file diff --git a/static_core/runtime/BUILD.gn b/static_core/runtime/BUILD.gn index f4198a86ee..188d0317ce 100644 --- a/static_core/runtime/BUILD.gn +++ b/static_core/runtime/BUILD.gn @@ -102,6 +102,7 @@ group("arkruntime_header_deps") { ":isa_gen_libarkruntime_interpreter-inl_gen_h", ":isa_gen_libarkruntime_isa_constants_gen_h", ":language_config_gen_inc", + ":core_any_intrinsics_inc", ":libarkruntime_options_gen_h", ":libarkruntime_shorty_values_h", ":plugin_clear_profile_h", @@ -758,6 +759,14 @@ ark_gen("arkruntime_gen_intrinsics") { } } +ark_gen_file("core_any_intrinsics_inc") { + template_file = "../runtime/interpreter/templates/core_any_intrinsics.inc.erb" + data = [ "$target_gen_dir/../plugin_options.yaml" ] + api = [ "$ark_root/templates/plugin_options.rb" ] + output_file = "$gen_include_dir/core_any_intrinsics.inc" + extra_dependencies = [ "$ark_root:concat_plugins_yamls" ] +} + ark_gen_file("libarkruntime_options_gen_h") { template_file = "../templates/options/options.h.erb" data = [ "$target_gen_dir/../runtime_options.yaml" ] diff --git a/static_core/runtime/CMakeLists.txt b/static_core/runtime/CMakeLists.txt index 8a5005a67c..000666b029 100644 --- a/static_core/runtime/CMakeLists.txt +++ b/static_core/runtime/CMakeLists.txt @@ -277,6 +277,16 @@ panda_isa_gen( DESTINATION ${GEN_INCLUDE_DIR} ) +panda_gen_file( + DATA ${CMAKE_BINARY_DIR}/plugin_options.yaml + TEMPLATE ${CMAKE_CURRENT_LIST_DIR}/interpreter/templates/core_any_intrinsics.inc.erb + API ${PANDA_ROOT}/templates/plugin_options.rb + EXTRA_DEPENDENCIES plugin_options_merge + OUTPUTFILE ${GEN_INCLUDE_DIR}/core_any_intrinsics.inc +) +add_custom_target(core_any_intrinsics_inc DEPENDS ${GEN_INCLUDE_DIR}/core_any_intrinsics.inc) +add_dependencies(arkruntime_interpreter_impl core_any_intrinsics_inc) + set(ISA "${CMAKE_BINARY_DIR}/isa/isa.yaml") set(ISA_API "${PANDA_ROOT}/isa/isapi.rb") set(BRIDGE_DISPATCH_TEMPLATE "${CMAKE_CURRENT_LIST_DIR}/templates/bridge_dispatch.S.erb") diff --git a/static_core/runtime/include/class.h b/static_core/runtime/include/class.h index a6121da597..d2ff358a07 100644 --- a/static_core/runtime/include/class.h +++ b/static_core/runtime/include/class.h @@ -121,6 +121,7 @@ public: using UniqId = uint64_t; static constexpr uint32_t STRING_CLASS = DYNAMIC_CLASS << 1U; static constexpr uint32_t IS_CLONEABLE = STRING_CLASS << 1U; + static constexpr uint32_t XREF_CLASS = IS_CLONEABLE << 1U; static constexpr size_t IMTABLE_SIZE = 32; enum { @@ -355,6 +356,11 @@ public: return (GetFlags() & STRING_CLASS) != 0; } + bool IsXRefClass() const + { + return (GetFlags() & XREF_CLASS) != 0; + } + void SetStringClass() { SetFlags(GetFlags() | STRING_CLASS); @@ -365,6 +371,11 @@ public: SetFlags(GetFlags() | IS_CLONEABLE); } + void SetXRefClass() + { + SetFlags(GetFlags() | XREF_CLASS); + } + bool IsVariableSize() const { return IsArrayClass() || IsStringClass(); diff --git a/static_core/runtime/interpreter/interpreter-inl.h b/static_core/runtime/interpreter/interpreter-inl.h index a93af873b7..014f14996b 100644 --- a/static_core/runtime/interpreter/interpreter-inl.h +++ b/static_core/runtime/interpreter/interpreter-inl.h @@ -28,6 +28,7 @@ #include #include "bytecode_instruction.h" +#include "intrinsics.h" #include "libpandabase/events/events.h" #include "libpandabase/macros.h" #include "libpandabase/utils/logger.h" @@ -1207,64 +1208,159 @@ public: template ALWAYS_INLINE void HandleAnyCall0() { - LOG_INST() << "Unimplemented instruction call.0"; - UNREACHABLE(); + uint16_t vs1 = this->GetInst().template GetVReg(); + LOG_INST() << "any.call.0 v" << vs1; + ObjectHeader *funcObj = this->GetFrame()->GetVReg(vs1).template GetAs(); + auto ret = intrinsics::AnyCall0(this->GetThread(), funcObj); + if (UNLIKELY(this->GetThread()->HasPendingException())) { + this->MoveToExceptionHandler(); + } + this->GetAccAsVReg().SetReference(ret); + this->template MoveToNextInst(); } template ALWAYS_INLINE void HandleAnyCallRange() { - LOG_INST() << "Unimplemented instruction any.call.range"; - UNREACHABLE(); + uint16_t vs1 = this->GetInst().template GetVReg(); + auto argc = this->GetInst().template GetImm(); + uint16_t argStart = this->GetInst().template GetVReg(); + LOG_INST() << "any.call.range v" << vs1 << ", v" << argStart << ", " << argc; + ObjectHeader *funcObj = this->GetFrame()->GetVReg(vs1).template GetAs(); + auto ret = intrinsics::AnyCallRange(this->GetThread(), this->GetFrame(), funcObj, argStart, argc); + if (UNLIKELY(this->GetThread()->HasPendingException())) { + this->MoveToExceptionHandler(); + } + this->GetAccAsVReg().SetReference(ret); + this->template MoveToNextInst(); } template ALWAYS_INLINE void HandleAnyCallShort() { - LOG_INST() << "Unimplemented instruction any.call.short"; - UNREACHABLE(); + // This should be generated from plugin.erb file + uint16_t vs1 = this->GetInst().template GetVReg(); + uint16_t vs2 = this->GetInst().template GetVReg(); + LOG_INST() << "any.call.short v" << vs1 << ", v" << vs2; + ObjectHeader *funcObj = this->GetFrame()->GetVReg(vs1).template GetAs(); + ObjectHeader *arg = this->GetFrame()->GetVReg(vs2).template GetAs(); + auto ret = intrinsics::AnyCallShort(this->GetThread(), funcObj, arg); + if (UNLIKELY(this->GetThread()->HasPendingException())) { + this->MoveToExceptionHandler(); + } + this->GetAccAsVReg().SetReference(ret); + this->template MoveToNextInst(); } template ALWAYS_INLINE void HandleAnyCallThis0() { - LOG_INST() << "Unimplemented instruction any.call.this.0"; - UNREACHABLE(); + uint16_t vs1 = this->GetInst().template GetVReg(); + auto stringId = this->GetInst().template GetId().AsRawValue(); + LOG_INST() << "any.call.this.0 v" << vs1 << ", " << stringId; + ObjectHeader *thisObj = this->GetFrame()->GetVReg(vs1).template GetAs(); + auto ret = intrinsics::AnyCallThis0(this->GetThread(), this->GetFrame(), thisObj, stringId); + if (UNLIKELY(this->GetThread()->HasPendingException())) { + this->MoveToExceptionHandler(); + } + this->GetAccAsVReg().SetReference(ret); + this->template MoveToNextInst(); } template ALWAYS_INLINE void HandleAnyCallThisRange() { - LOG_INST() << "Unimplemented instruction any.call.this.range"; - UNREACHABLE(); + uint16_t vs1 = this->GetInst().template GetVReg(); + auto argc = this->GetInst().template GetImm(); + auto stringId = this->GetInst().template GetId().AsRawValue(); + uint16_t argStart = this->GetInst().template GetVReg(); + LOG_INST() << "any.call.this.range v" << vs1 << ", v" << argStart << ", " << argc << ", " << stringId; + ObjectHeader *thisObj = this->GetFrame()->GetVReg(vs1).template GetAs(); + auto ret = intrinsics::AnyCallThisRange(this->GetThread(), this->GetFrame(), thisObj, stringId, argStart, argc); + if (UNLIKELY(this->GetThread()->HasPendingException())) { + this->MoveToExceptionHandler(); + } + this->GetAccAsVReg().SetReference(ret); + this->template MoveToNextInst(); } template ALWAYS_INLINE void HandleAnyCallThisShort() { - LOG_INST() << "Unimplemented instruction any.call.this.short"; - UNREACHABLE(); + uint16_t vs1 = this->GetInst().template GetVReg(); + uint16_t vs2 = this->GetInst().template GetVReg(); + auto stringId = this->GetInst().template GetId().AsRawValue(); + LOG_INST() << "any.call.this.short v" << vs1 << ", v" << vs2 << ", " << stringId; + ObjectHeader *thisObj = this->GetFrame()->GetVReg(vs1).template GetAs(); + ObjectHeader *arg = this->GetFrame()->GetVReg(vs2).template GetAs(); + auto ret = intrinsics::AnyCallThisShort(this->GetThread(), this->GetFrame(), thisObj, stringId, arg); + if (UNLIKELY(this->GetThread()->HasPendingException())) { + this->MoveToExceptionHandler(); + } + this->GetAccAsVReg().SetReference(ret); + this->template MoveToNextInst(); } template ALWAYS_INLINE void HandleAnyCallNew0() { - LOG_INST() << "Unimplemented instruction any.call.new.0"; - UNREACHABLE(); + uint16_t vs1 = this->GetInst().template GetVReg(); + LOG_INST() << "any.call.new.0 v" << vs1; + ObjectHeader *ctor = this->GetFrame()->GetVReg(vs1).template GetAs(); + auto ret = intrinsics::AnyCallNew0(this->GetThread(), ctor); + if (UNLIKELY(this->GetThread()->HasPendingException())) { + this->MoveToExceptionHandler(); + } + this->GetAccAsVReg().SetReference(ret); + this->template MoveToNextInst(); } template ALWAYS_INLINE void HandleAnyCallNewRange() { - LOG_INST() << "Unimplemented instruction any.call.new.range"; - UNREACHABLE(); + uint16_t vs1 = this->GetInst().template GetVReg(); + auto argc = this->GetInst().template GetImm(); + uint16_t argStart = this->GetInst().template GetVReg(); + LOG_INST() << "any.call.new.range v" << vs1 << ", v" << argStart << ", " << argc; + ObjectHeader *ctor = this->GetFrame()->GetVReg(vs1).template GetAs(); + auto ret = intrinsics::AnyCallNewRange(this->GetThread(), this->GetFrame(), ctor, argStart, argc); + if (UNLIKELY(this->GetThread()->HasPendingException())) { + this->MoveToExceptionHandler(); + } + this->GetAccAsVReg().SetReference(ret); + this->template MoveToNextInst(); } template ALWAYS_INLINE void HandleAnyCallNewShort() { - LOG_INST() << "Unimplemented instruction any.call.new.short"; - UNREACHABLE(); + uint16_t vs1 = this->GetInst().template GetVReg(); + uint16_t vs2 = this->GetInst().template GetVReg(); + LOG_INST() << "any.call.new.short v" << vs1 << ", v" << vs2; + ObjectHeader *ctor = this->GetFrame()->GetVReg(vs1).template GetAs(); + ObjectHeader *arg = this->GetFrame()->GetVReg(vs2).template GetAs(); + auto ret = intrinsics::AnyCallNewShort(this->GetThread(), ctor, arg); + if (UNLIKELY(this->GetThread()->HasPendingException())) { + this->MoveToExceptionHandler(); + } + this->GetAccAsVReg().SetReference(ret); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleAnyIsinstance() + { + uint16_t vs1 = this->GetInst().template GetVReg(); + LOG_INST() << "any.isinstance v" << vs1; + + ObjectHeader *lhsObj = this->GetAcc().template GetAs(); + ObjectHeader *rhsObj = this->GetFrame()->GetVReg(vs1).template GetAs(); + bool ret = intrinsics::AnyIsinstance(this->GetThread(), lhsObj, rhsObj); + if (UNLIKELY(this->GetThread()->HasPendingException())) { + this->MoveToExceptionHandler(); + } + this->GetAccAsVReg().SetPrimitive(ret); + this->template MoveToNextInst(); } template @@ -3850,13 +3946,9 @@ public: LOG_INST() << "any.ldbyname v" << vs << ", " << std::hex << "0x" << id; ObjectHeader *obj = this->GetFrame()->GetVReg(vs).GetReference(); - if (UNLIKELY(obj == nullptr)) { - RuntimeIfaceT::ThrowNullPointerException(); - this->MoveToExceptionHandler(); - } - - // NOTE: handle it - UNREACHABLE(); + auto ret = intrinsics::AnyLdbyname(this->GetThread(), this->GetFrame(), obj, id.AsRawValue()); + this->GetAccAsVReg().SetReference(ret); + this->template MoveToNextInst(); } template @@ -3869,13 +3961,9 @@ public: LOG_INST() << "any.ldbyname.v v" << vd << ", v" << vs << ", " << std::hex << "0x" << id; ObjectHeader *obj = this->GetFrame()->GetVReg(vs).GetReference(); - if (UNLIKELY(obj == nullptr)) { - RuntimeIfaceT::ThrowNullPointerException(); - this->MoveToExceptionHandler(); - } - - // NOTE: handle it - UNREACHABLE(); + auto ret = intrinsics::AnyLdbyname(this->GetThread(), this->GetFrame(), obj, id.AsRawValue()); + this->GetFrameHandler().GetVReg(vd).SetReference(ret); + this->template MoveToNextInst(); } template @@ -3887,13 +3975,9 @@ public: LOG_INST() << "any.stbyname v" << vs << ", " << std::hex << "0x" << id; ObjectHeader *obj = this->GetFrame()->GetVReg(vs).GetReference(); - if (UNLIKELY(obj == nullptr)) { - RuntimeIfaceT::ThrowNullPointerException(); - this->MoveToExceptionHandler(); - } - - // NOTE: handle it - UNREACHABLE(); + ObjectHeader *val = this->GetAccAsVReg().GetReference(); + intrinsics::AnyStbyname(this->GetThread(), this->GetFrame(), obj, id.AsRawValue(), val); + this->template MoveToNextInst(); } template @@ -3906,13 +3990,9 @@ public: LOG_INST() << "any.stbyname.v v" << vs1 << ", v" << vs2 << ", " << std::hex << "0x" << id; ObjectHeader *obj = this->GetFrame()->GetVReg(vs2).GetReference(); - if (UNLIKELY(obj == nullptr)) { - RuntimeIfaceT::ThrowNullPointerException(); - this->MoveToExceptionHandler(); - } - - // NOTE: handle it - UNREACHABLE(); + ObjectHeader *val = this->GetFrame()->GetVReg(vs1).GetReference(); + intrinsics::AnyStbyname(this->GetThread(), this->GetFrame(), obj, id.AsRawValue(), val); + this->template MoveToNextInst(); } template @@ -3923,13 +4003,12 @@ public: LOG_INST() << "any.ldbyidx v" << vs << ", " << std::hex; ObjectHeader *obj = this->GetFrame()->GetVReg(vs).GetReference(); - if (UNLIKELY(obj == nullptr)) { - RuntimeIfaceT::ThrowNullPointerException(); - this->MoveToExceptionHandler(); - } + ASSERT(!this->GetAccAsVReg().HasObject()); - // NOTE: handle it - UNREACHABLE(); + double index = this->GetAccAsVReg().GetDouble(); + auto ldObj = intrinsics::AnyLdbyidx(this->GetThread(), obj, index); + this->GetAccAsVReg().SetReference(ldObj); + this->template MoveToNextInst(); } template @@ -3938,16 +4017,13 @@ public: uint16_t vs1 = this->GetInst().template GetVReg(); uint16_t vs2 = this->GetInst().template GetVReg(); - LOG_INST() << "any.stbyidx v" << vs1 << ", v" << vs2 << ", " << std::hex; + LOG_INST() << "any.stbyidx v" << vs1 << ", " << vs2 << std::hex; ObjectHeader *obj = this->GetFrame()->GetVReg(vs1).GetReference(); - if (UNLIKELY(obj == nullptr)) { - RuntimeIfaceT::ThrowNullPointerException(); - this->MoveToExceptionHandler(); - } - - // NOTE: handle it - UNREACHABLE(); + [[maybe_unused]] double index = this->GetFrame()->GetVReg(vs2).GetDouble(); + ObjectHeader *val = this->GetAccAsVReg().GetReference(); + intrinsics::AnyStbyidx(this->GetThread(), obj, index, val); + this->template MoveToNextInst(); } template @@ -3957,16 +4033,11 @@ public: uint16_t vs2 = this->GetInst().template GetVReg(); LOG_INST() << "any.ldbyval v" << vs1 << ", v" << vs2 << ", " << std::hex; - ObjectHeader *obj = this->GetFrame()->GetVReg(vs1).GetReference(); ObjectHeader *valObj = this->GetFrame()->GetVReg(vs2).GetReference(); - if (UNLIKELY(obj == nullptr || valObj == nullptr)) { - RuntimeIfaceT::ThrowNullPointerException(); - this->MoveToExceptionHandler(); - } - - // NOTE: handle it - UNREACHABLE(); + auto ldObj = intrinsics::AnyLdbyval(this->GetThread(), obj, valObj); + this->GetAccAsVReg().SetReference(ldObj); + this->template MoveToNextInst(); } template @@ -3978,14 +4049,10 @@ public: LOG_INST() << "any.stbyval v" << vs1 << ", v" << vs2 << ", " << std::hex; ObjectHeader *obj = this->GetFrame()->GetVReg(vs1).GetReference(); - ObjectHeader *valObj = this->GetFrame()->GetVReg(vs2).GetReference(); - if (UNLIKELY(obj == nullptr || valObj == nullptr)) { - RuntimeIfaceT::ThrowNullPointerException(); - this->MoveToExceptionHandler(); - } - - // NOTE: handle it - UNREACHABLE(); + ObjectHeader *key = this->GetFrame()->GetVReg(vs2).GetReference(); + ObjectHeader *val = this->GetAccAsVReg().GetReference(); + intrinsics::AnyStbyval(this->GetThread(), obj, key, val); + this->template MoveToNextInst(); } private: diff --git a/static_core/runtime/interpreter/templates/core_any_intrinsics.inc.erb b/static_core/runtime/interpreter/templates/core_any_intrinsics.inc.erb new file mode 100644 index 0000000000..028883cf75 --- /dev/null +++ b/static_core/runtime/interpreter/templates/core_any_intrinsics.inc.erb @@ -0,0 +1,364 @@ +/* + * 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. + */ + +// Autogenerated file -- DO NOT EDIT! + +#ifndef CORE_ANY_INTRINSICS_INL +#define CORE_ANY_INTRINSICS_INL + +<%= Common::include_plugin_files "any", "handlers_path" %> + +namespace ark::intrinsics { +ObjectHeader *AnyLdbyname([[maybe_unused]] ManagedThread *thread, [[maybe_unused]] Frame *frame, ObjectHeader *thisObj, + [[maybe_unused]] FileEntityId id) +{ + if (thisObj == nullptr) { + interpreter::RuntimeInterface::ThrowNullPointerException(); + return nullptr; + } + auto *cls = thisObj->ClassAddr(); + ASSERT(cls != nullptr); + switch (cls->GetSourceLang()) { +% Common::plugins.each_value do |plugin_opts| + case <%= plugin_opts["lang_enum"] %>: +% if plugin_opts.include?('any') + return <%= plugin_opts["directive_name"].upcase %>AnyLdbyname(thread, frame, thisObj, id); +% else + // Any handlers unimplemented for plugin + UNREACHABLE(); +% end + break; +% end + default: + UNREACHABLE(); + } +} + +void AnyStbyname([[maybe_unused]] ManagedThread *thread, [[maybe_unused]] Frame *frame, ObjectHeader *thisObj, + [[maybe_unused]]FileEntityId id, [[maybe_unused]] ObjectHeader *val) +{ + if (thisObj == nullptr) { + interpreter::RuntimeInterface::ThrowNullPointerException(); + return; + } + auto *cls = thisObj->ClassAddr(); + ASSERT(cls != nullptr); + switch (cls->GetSourceLang()) { +% Common::plugins.each_value do |plugin_opts| + case <%= plugin_opts["lang_enum"] %>: +% if plugin_opts.include?('any') + return <%= plugin_opts["directive_name"].upcase %>AnyStbyname(thread, frame, thisObj, id, val); +% else + // Any handlers unimplemented for plugin + UNREACHABLE(); +% end + break; +% end + default: + UNREACHABLE(); + } +} + +ObjectHeader *AnyLdbyidx([[maybe_unused]] ManagedThread *thread, ObjectHeader *thisObj, + [[maybe_unused]] double idxFloat) +{ + if (thisObj == nullptr) { + interpreter::RuntimeInterface::ThrowNullPointerException(); + return nullptr; + } + auto *cls = thisObj->ClassAddr(); + ASSERT(cls != nullptr); + + switch (cls->GetSourceLang()) { +% Common::plugins.each_value do |plugin_opts| + case <%= plugin_opts["lang_enum"] %>: +% if plugin_opts.include?('any') + return <%= plugin_opts["directive_name"].upcase %>AnyLdbyidx(thread, thisObj, idxFloat); +% else + // Any handlers unimplemented for plugin + UNREACHABLE(); +% end + break; +% end + default: + // NOTE(nsizov): implement core handler here if needed + UNREACHABLE(); + } +} + +void AnyStbyidx([[maybe_unused]] ManagedThread *thread, ObjectHeader *thisObj, [[maybe_unused]] double key, + [[maybe_unused]] ObjectHeader *val) +{ + if (thisObj == nullptr) { + interpreter::RuntimeInterface::ThrowNullPointerException(); + return; + } + auto *cls = thisObj->ClassAddr(); + ASSERT(cls != nullptr); + + switch (cls->GetSourceLang()) { +% Common::plugins.each_value do |plugin_opts| + case <%= plugin_opts["lang_enum"] %>: +% if plugin_opts.include?('any') + <%= plugin_opts["directive_name"].upcase %>AnyStbyidx(thread, thisObj, key, val); +% else + // Any handlers unimplemented for plugin + UNREACHABLE(); +% end + break; +% end + default: + // NOTE(nsizov): implement core handler here if needed + UNREACHABLE(); + } +} + +ObjectHeader *AnyLdbyval([[maybe_unused]] ManagedThread *thread, ObjectHeader *thisObj, + [[maybe_unused]] ObjectHeader *key) +{ + if (thisObj == nullptr) { + interpreter::RuntimeInterface::ThrowNullPointerException(); + return nullptr; + } + auto *cls = thisObj->ClassAddr(); + ASSERT(cls != nullptr); + + switch (cls->GetSourceLang()) { +% Common::plugins.each_value do |plugin_opts| + case <%= plugin_opts["lang_enum"] %>: +% if plugin_opts.include?('any') + return <%= plugin_opts["directive_name"].upcase %>AnyLdbyval(thread, thisObj, key); +% else + // Any handlers unimplemented for plugin + UNREACHABLE(); +% end + break; +% end + default: + // NOTE(nsizov): implement core handler here if needed + UNREACHABLE(); + } +} + +void AnyStbyval([[maybe_unused]] ManagedThread *thread, ObjectHeader *thisObj, [[maybe_unused]] ObjectHeader *key, + [[maybe_unused]] ObjectHeader *val) +{ + if (thisObj == nullptr) { + interpreter::RuntimeInterface::ThrowNullPointerException(); + return; + } + auto *cls = thisObj->ClassAddr(); + ASSERT(cls != nullptr); + + switch (cls->GetSourceLang()) { +% Common::plugins.each_value do |plugin_opts| + case <%= plugin_opts["lang_enum"] %>: +% if plugin_opts.include?('any') + <%= plugin_opts["directive_name"].upcase %>AnyStbyval(thread, thisObj, key, val); +% else + // Any handlers unimplemented for plugin + UNREACHABLE(); +% end + break; +% end + default: + // NOTE(nsizov): implement core handler here if needed + UNREACHABLE(); + } +} + +extern "C" bool AnyIsinstance([[maybe_unused]] ManagedThread *thread, ObjectHeader *lhs, + [[maybe_unused]] ObjectHeader *rhs) +{ + if (lhs == nullptr) { + interpreter::RuntimeInterface::ThrowNullPointerException(); + return false; + } + auto *cls = lhs->ClassAddr(); + ASSERT(cls != nullptr); + + switch (cls->GetSourceLang()) { +% Common::plugins.each_value do |plugin_opts| + case <%= plugin_opts["lang_enum"] %>: +% if plugin_opts.include?('any') + return <%= plugin_opts["directive_name"].upcase %>AnyIsinstance(thread, lhs, rhs); +% else + // Any handlers unimplemented for plugin + UNREACHABLE(); +% end + break; +% end + default: + // NOTE(nsizov): implement core handler here if needed + UNREACHABLE(); + } +} + +static inline ObjectHeader *AnyCall([[maybe_unused]] ManagedThread *thread, ObjectHeader *func, + [[maybe_unused]] Span> args) +{ + if (func == nullptr) { + interpreter::RuntimeInterface::ThrowNullPointerException(); + return nullptr; + } + auto *cls = func->ClassAddr(); + switch (cls->GetSourceLang()) { +% Common::plugins.each_value do |plugin_opts| + case <%= plugin_opts["lang_enum"] %>: +% if plugin_opts.include?('any') + return <%= plugin_opts["directive_name"].upcase %>AnyCall(thread, func, args); +% else + // Any handlers unimplemented for plugin + UNREACHABLE(); +% end + break; +% end + default: + // NOTE(nsizov): implement core handler here if needed + UNREACHABLE(); + } +} + +ObjectHeader *AnyCall0(ManagedThread *thread, ObjectHeader *func) +{ + [[maybe_unused]] HandleScope scope(thread); + return AnyCall(thread, func, Span>()); +} + +ObjectHeader *AnyCallRange(ManagedThread *thread, Frame *frame, ObjectHeader *func, + uint8_t argStart, uint8_t argc) +{ + [[maybe_unused]] HandleScope scope(thread); + PandaVector> args; + for (uint8_t i = 0; i < argc; i++) { + args.emplace_back(VMHandle(thread, frame->GetVReg(argStart + i).template GetAs())); + } + return AnyCall(thread, func, Span>(args)); +} + +ObjectHeader *AnyCallShort(ManagedThread *thread, ObjectHeader *func, ObjectHeader *arg) +{ + [[maybe_unused]] HandleScope scope(thread); + std::array, 1> args = {VMHandle(thread, arg)}; + return AnyCall(thread, func, Span>(args)); +} + +static inline ObjectHeader *AnyCallThis([[maybe_unused]] ManagedThread *thread, [[maybe_unused]] Frame *frame, + ObjectHeader *thisObj, [[maybe_unused]] FileEntityId name, + [[maybe_unused]] Span> args) +{ + if (thisObj == nullptr) { + interpreter::RuntimeInterface::ThrowNullPointerException(); + return nullptr; + } + auto *cls = thisObj->ClassAddr(); + ASSERT(cls != nullptr); + + switch (cls->GetSourceLang()) { +% Common::plugins.each_value do |plugin_opts| + case <%= plugin_opts["lang_enum"] %>: +% if plugin_opts.include?('any') + return <%= plugin_opts["directive_name"].upcase %>AnyCallThis(thread, frame, thisObj, name, args); +% else + // Any handlers unimplemented for plugin + UNREACHABLE(); +% end + break; +% end + default: + // NOTE(nsizov): implement core handler here if needed + UNREACHABLE(); + } +} + +extern "C" ObjectHeader *AnyCallThis0(ManagedThread *thread, Frame *frame, ObjectHeader *thisObj, + FileEntityId name) +{ + [[maybe_unused]] HandleScope scope(thread); + return AnyCallThis(thread, frame, thisObj, name, Span>()); +} + +extern "C" ObjectHeader *AnyCallThisRange(ManagedThread *thread, Frame *frame, ObjectHeader *thisObj, + FileEntityId name, uint8_t argStart, uint8_t argc) +{ + [[maybe_unused]] HandleScope scope(thread); + PandaVector> args; + for (uint8_t i = 0; i < argc; i++) { + args.emplace_back(VMHandle(thread, frame->GetVReg(argStart + i).template GetAs())); + } + return AnyCallThis(thread, frame, thisObj, name, Span>(args)); +} + +extern "C" ObjectHeader *AnyCallThisShort(ManagedThread *thread, Frame *frame, ObjectHeader *thisObj, + FileEntityId name, ObjectHeader *arg) +{ + [[maybe_unused]] HandleScope scope(thread); + std::array, 1> args = {VMHandle(thread, arg)}; + return AnyCallThis(thread, frame, thisObj, name, Span>(args)); +} + +static inline ObjectHeader *AnyCallNew([[maybe_unused]] ManagedThread *thread, ObjectHeader *ctor, + [[maybe_unused]] Span> args) +{ + if (ctor == nullptr) { + interpreter::RuntimeInterface::ThrowNullPointerException(); + return nullptr; + } + auto *cls = ctor->ClassAddr(); + ASSERT(cls != nullptr); + + switch (cls->GetSourceLang()) { +% Common::plugins.each_value do |plugin_opts| + case <%= plugin_opts["lang_enum"] %>: +% if plugin_opts.include?('any') + return <%= plugin_opts["directive_name"].upcase %>AnyCallNew(thread, ctor, args); +% else + // Any handlers unimplemented for plugin + UNREACHABLE(); +% end + break; +% end + default: + // NOTE(nsizov): implement core handler here if needed + UNREACHABLE(); + } +} + +extern "C" ObjectHeader *AnyCallNew0(ManagedThread *thread, ObjectHeader *ctor) +{ + [[maybe_unused]] HandleScope scope(thread); + return AnyCallNew(thread, ctor, Span>()); +} + +extern "C" ObjectHeader *AnyCallNewRange(ManagedThread *thread, Frame *frame, ObjectHeader *ctor, + uint8_t argStart, uint8_t argc) +{ + [[maybe_unused]] HandleScope scope(thread); + PandaVector> args; + for (uint8_t i = 0; i < argc; i++) { + args.emplace_back(VMHandle(thread, frame->GetVReg(argStart + i).template GetAs())); + } + return AnyCallNew(thread, ctor, Span>(args)); +} + +extern "C" ObjectHeader *AnyCallNewShort(ManagedThread *thread, ObjectHeader *ctor, ObjectHeader *arg) +{ + [[maybe_unused]] HandleScope scope(thread); + std::array, 1> args = {VMHandle(thread, arg)}; + return AnyCallNew(thread, ctor, Span>(args)); +} +} // namespace ark::intrinsics + +#endif // CORE_ANY_INTRINSICS_INL \ No newline at end of file diff --git a/static_core/runtime/intrinsics.cpp b/static_core/runtime/intrinsics.cpp index fbb7b8b214..a90c807dcc 100644 --- a/static_core/runtime/intrinsics.cpp +++ b/static_core/runtime/intrinsics.cpp @@ -444,4 +444,6 @@ void Memsetf64(ObjectHeader *array, double value, uint32_t initialIndex, uint32_ } } // namespace ark::intrinsics +#include "core_any_intrinsics.inc" + #include diff --git a/static_core/runtime/templates/intrinsics.h.erb b/static_core/runtime/templates/intrinsics.h.erb index 0f7d22383a..136142db65 100644 --- a/static_core/runtime/templates/intrinsics.h.erb +++ b/static_core/runtime/templates/intrinsics.h.erb @@ -1,17 +1,17 @@ /* - * Copyright (c) 2021-2024 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. - */ +* Copyright (c) 2021-2024 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. +*/ // Autogenerated file -- DO NOT EDIT! @@ -48,6 +48,22 @@ extern "C" PANDA_PUBLIC_API void Memset64(ObjectHeader*, uint64_t, uint32_t, uin extern "C" PANDA_PUBLIC_API void Memsetf32(ObjectHeader*, float, uint32_t, uint32_t); // NOLINT(readability-named-parameter, readability-redundant-declaration) extern "C" PANDA_PUBLIC_API void Memsetf64(ObjectHeader*, double, uint32_t, uint32_t); // NOLINT(readability-named-parameter, readability-redundant-declaration) +extern "C" PANDA_PUBLIC_API ObjectHeader* AnyLdbyname(ManagedThread*, Frame*, ObjectHeader*, FileEntityId); // NOLINT(readability-named-parameter, readability-redundant-declaration) +extern "C" PANDA_PUBLIC_API void AnyStbyname(ManagedThread*, Frame*, ObjectHeader*, FileEntityId, ObjectHeader*); // NOLINT(readability-named-parameter, readability-redundant-declaration) +extern "C" PANDA_PUBLIC_API ObjectHeader* AnyLdbyidx(ManagedThread*, ObjectHeader*, double); // NOLINT(readability-named-parameter, readability-redundant-declaration) +extern "C" PANDA_PUBLIC_API void AnyStbyidx(ManagedThread*, ObjectHeader*, double, ObjectHeader*); // NOLINT(readability-named-parameter, readability-redundant-declaration) +extern "C" PANDA_PUBLIC_API ObjectHeader* AnyLdbyval(ManagedThread*, ObjectHeader*, ObjectHeader*); // NOLINT(readability-named-parameter, readability-redundant-declaration) +extern "C" PANDA_PUBLIC_API void AnyStbyval(ManagedThread*, ObjectHeader*, ObjectHeader*, ObjectHeader*); // NOLINT(readability-named-parameter, readability-redundant-declaration) +extern "C" PANDA_PUBLIC_API bool AnyIsinstance(ManagedThread*, ObjectHeader*, ObjectHeader*); // NOLINT(readability-named-parameter, readability-redundant-declaration) +extern "C" PANDA_PUBLIC_API ObjectHeader* AnyCall0(ManagedThread*, ObjectHeader*); // NOLINT(readability-named-parameter, readability-redundant-declaration) +extern "C" PANDA_PUBLIC_API ObjectHeader* AnyCallRange(ManagedThread*, Frame*, ObjectHeader*, uint8_t, uint8_t); // NOLINT(readability-named-parameter, readability-redundant-declaration) +extern "C" PANDA_PUBLIC_API ObjectHeader* AnyCallShort(ManagedThread*, ObjectHeader*, ObjectHeader*); // NOLINT(readability-named-parameter, readability-redundant-declaration) +extern "C" PANDA_PUBLIC_API ObjectHeader* AnyCallThis0(ManagedThread*, Frame*, ObjectHeader*, FileEntityId); // NOLINT(readability-named-parameter, readability-redundant-declaration) +extern "C" PANDA_PUBLIC_API ObjectHeader* AnyCallThisRange(ManagedThread*, Frame*, ObjectHeader*, FileEntityId, uint8_t, uint8_t); // NOLINT(readability-named-parameter, readability-redundant-declaration) +extern "C" PANDA_PUBLIC_API ObjectHeader* AnyCallThisShort(ManagedThread*, Frame*, ObjectHeader*, FileEntityId , ObjectHeader*); // NOLINT(readability-named-parameter, readability-redundant-declaration) +extern "C" PANDA_PUBLIC_API ObjectHeader* AnyCallNew0(ManagedThread*, ObjectHeader*); // NOLINT(readability-named-parameter, readability-redundant-declaration) +extern "C" PANDA_PUBLIC_API ObjectHeader* AnyCallNewRange(ManagedThread*, Frame*, ObjectHeader*, uint8_t, uint8_t); // NOLINT(readability-named-parameter, readability-redundant-declaration) +extern "C" PANDA_PUBLIC_API ObjectHeader* AnyCallNewShort(ManagedThread*, ObjectHeader*, ObjectHeader*); // NOLINT(readability-named-parameter, readability-redundant-declaration) } // namespace <%= ns %> % end diff --git a/static_core/verification/absint/abs_int_inl.h b/static_core/verification/absint/abs_int_inl.h index 6a7a0637d2..13d9e14724 100644 --- a/static_core/verification/absint/abs_int_inl.h +++ b/static_core/verification/absint/abs_int_inl.h @@ -3429,7 +3429,7 @@ public: bool HandleAnyCall0() { LOG_INST(); - return false; + return true; } template @@ -3663,6 +3663,15 @@ public: return true; } + template + bool HandleAnyIsinstance() + { + LOG_INST(); + DBGBRK(); + Sync(); + return true; + } + private: Type GetCachedType() const { -- Gitee