From 96dc04a1e085adaf338deceb32255f823e93359e Mon Sep 17 00:00:00 2001 From: zhaoziming Date: Mon, 9 Jun 2025 17:35:39 +0800 Subject: [PATCH] draft for bytecode Change-Id: I168e9d1078c4f79e1bf89de74ed97573394589b5 --- .../optimizer/ir_builder/inst_builder-inl.h | 8 ++ .../optimizer/ir_builder/inst_builder.h | 1 + .../optimizer/ir_builder/inst_templates.yaml | 2 + static_core/irtoc/scripts/interpreter.irt | 7 ++ static_core/isa/isa.yaml | 16 ++++ .../ets/runtime/ets_platform_types.cpp | 1 + .../plugins/ets/runtime/ets_stubs-inl.h | 6 ++ static_core/plugins/ets/runtime/ets_stubs.cpp | 53 +++++++++++++ static_core/plugins/ets/runtime/ets_stubs.h | 3 + .../interop_js/ets_proxy/ets_proxy.cpp | 1 + .../interop_js/intrinsics_api_impl.cpp | 79 ++++++++++++++++--- .../runtime/interop_js/intrinsics_api_impl.h | 5 +- .../ets/runtime/interop_js/js_convert.h | 22 +++++- .../plugins/ets/runtime/types/ets_string.h | 8 +- .../cmake/interop_js_tests_arkjsvm.cmake | 56 ++++++++----- .../interop_js/gtest_plugin/gtest_launcher.js | 16 +++- .../tests/interop_isa/CMakeLists.txt | 21 +++++ .../interop_js/tests/interop_isa/index.js | 22 ++++++ .../interop_js/tests/interop_isa/test.cpp | 29 +++++++ .../interop_js/tests/interop_isa/test.ets | 0 .../interop_js/tests/interop_isa/test.pa | 26 ++++++ static_core/runtime/include/class.h | 11 +++ .../runtime/interpreter/interpreter-inl.h | 32 +++++++- static_core/verification/absint/abs_int_inl.h | 12 ++- 24 files changed, 395 insertions(+), 42 deletions(-) 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 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..0d9461c65e 100644 --- a/static_core/compiler/optimizer/ir_builder/inst_builder-inl.h +++ b/static_core/compiler/optimizer/ir_builder/inst_builder-inl.h @@ -1682,6 +1682,14 @@ void InstBuilder::BuildAnyCall([[maybe_unused]] const BytecodeInstruction *bcIns UNREACHABLE(); } +// NOLINTNEXTLINE(readability-function-size,misc-definitions-in-headers) +void InstBuilder::BuildIsInstanceAny([[maybe_unused]] const BytecodeInstruction *bcInst, + [[maybe_unused]] DataType::Type type) +{ + // NOTE: handle it + UNREACHABLE(); +} + } // namespace ark::compiler #endif // PANDA_INST_BUILDER_INL_H 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/irtoc/scripts/interpreter.irt b/static_core/irtoc/scripts/interpreter.irt index d168218f63..d790250653 100644 --- a/static_core/irtoc/scripts/interpreter.irt +++ b/static_core/irtoc/scripts/interpreter.irt @@ -2133,6 +2133,11 @@ macro(:handle_any_stbyval_v8_v8) do |vs1, vs2| Intrinsic(:UNREACHABLE).Terminator.void end +macro(:handle_any_isinstance_v8) do |vs| + # NOTE: handle it + Intrinsic(:UNREACHABLE).Terminator.void +end + # Functions: function(:ExecuteImplFast, @@ -2938,6 +2943,8 @@ Panda.instructions.each do |i| 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) include_plugin 'interpreter_main_loop' diff --git a/static_core/isa/isa.yaml b/static_core/isa/isa.yaml index 7753bff028..c64d425814 100644 --- a/static_core/isa/isa.yaml +++ b/static_core/isa/isa.yaml @@ -3345,3 +3345,19 @@ groups: prefix: any format: [pref_op_v1_4_imm_4] opcode_idx: [0xb6] + + - title: Any instanceof check + description: > + TBD + verification: + - v1_object + exceptions: + - x_null + pseudo: | + TBD + instructions: + - sig: any.isinstance v1:in:top + acc: inout:top->u1 + prefix: any + format: [pref_op_v1_8] + opcode_idx: [0xb7] \ No newline at end of file diff --git a/static_core/plugins/ets/runtime/ets_platform_types.cpp b/static_core/plugins/ets/runtime/ets_platform_types.cpp index f488200132..e1e644a36b 100644 --- a/static_core/plugins/ets/runtime/ets_platform_types.cpp +++ b/static_core/plugins/ets/runtime/ets_platform_types.cpp @@ -181,6 +181,7 @@ EtsPlatformTypes::EtsPlatformTypes([[maybe_unused]] EtsCoroutine *coro) findMethod(&EtsPlatformTypes::escompatRecordSetter, escompatRecord, SET_INDEX_METHOD, nullptr, false); findType(&EtsPlatformTypes::interopJSValue, JS_VALUE); + EtsPlatformTypes::interopJSValue->GetRuntimeClass()->SetXRefClass(); findType(&EtsPlatformTypes::coreField, FIELD); findType(&EtsPlatformTypes::coreMethod, METHOD); diff --git a/static_core/plugins/ets/runtime/ets_stubs-inl.h b/static_core/plugins/ets/runtime/ets_stubs-inl.h index 621d3326dd..12feff41cc 100644 --- a/static_core/plugins/ets/runtime/ets_stubs-inl.h +++ b/static_core/plugins/ets/runtime/ets_stubs-inl.h @@ -419,6 +419,12 @@ ALWAYS_INLINE inline Method *GetMethodByName(EtsCoroutine *coro, ETSStubCacheInf return callee; } +ALWAYS_INLINE inline ObjectHeader *PluginAnyCallThis(ManagedThread *thread, ObjectHeader *thisObj, + panda_file::File::StringData name, Spanargs) +{ + return EtsCallThis(EtsCoroutine::CastFromThread(thread), EtsObject::FromCoreType(thisObj), name, args)->GetCoreType(); +} + } // namespace ark::ets #endif // PANDA_PLUGINS_ETS_RUNTIME_STUBS_INL_H diff --git a/static_core/plugins/ets/runtime/ets_stubs.cpp b/static_core/plugins/ets/runtime/ets_stubs.cpp index c6c0ca47d3..aa0fb0accb 100644 --- a/static_core/plugins/ets/runtime/ets_stubs.cpp +++ b/static_core/plugins/ets/runtime/ets_stubs.cpp @@ -20,7 +20,15 @@ #include "plugins/ets/runtime/types/ets_base_enum.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; +#endif template static std::optional GetBoxedNumericValue(EtsPlatformTypes const *ptypes, EtsObject *obj) @@ -250,4 +258,49 @@ bool EtsGetIstrue(EtsCoroutine *coro, EtsObject *obj) UNREACHABLE(); } +EtsObject *EtsLdByVal([[maybe_unused]]EtsCoroutine *coro, EtsObject *thisObj, EtsObject *val) +{ + if (thisObj->GetClass()->GetRuntimeClass()->IsXRefClass()) { +#ifdef PANDA_ETS_INTEROP_JS + ASSERT(thisObj->GetClass() == PlatformTypes(coro)->interopJSValue); + auto thisValue = JSValue::FromEtsObject(thisObj); + if (val->IsStringClass()) { + return interop::js::JSValueNamedGetter(thisValue, EtsString::FromEtsObject(val)); + } else if (val->GetClass()->GetRuntimeClass()->IsXRefClass()) { + return interop::js::GetPropertyObject(thisValue, JSValue::FromEtsObject(val)); + } + auto unboxedValue = GetBoxedNumericValue(PlatformTypes(coro), val); + if (unboxedValue.has_value()) { + return interop::js::JSValueIndexedGetter(thisValue, unboxedValue.value()); + } + LOG(ERROR, RUNTIME) << "input value cannot be casted to valid property key"; + return nullptr; +#else + LOG(FATAL, RUNTIME) << "Find Dynamic Object without interop build"; + UNREACHABLE(); +#endif + } else { + return nullptr; + } +} + +EtsObject *EtsCallThis([[maybe_unused]]EtsCoroutine *coro, EtsObject *thisObj, + panda_file::File::StringData name, Span args) +{ + if (thisObj->GetClass()->GetRuntimeClass()->IsXRefClass()) { +#ifdef PANDA_ETS_INTEROP_JS + auto thisValue = JSValue::FromEtsObject(thisObj); + EtsObject *function = interop::js::GetPropertyObjectByString(thisValue, utf::Mutf8AsCString(name.data)); + if (!function->GetClass()->GetRuntimeClass()->IsXRefClass()) { + return nullptr; + } + return interop::js::InvokeWithObjectReturn(thisValue, JSValue::FromEtsObject(function), args); +#else + LOG(FATAL, RUNTIME) << "Met dynamic object without interop build"; + UNREACHABLE(); +#endif + } else { + 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..554f0ca457 100644 --- a/static_core/plugins/ets/runtime/ets_stubs.h +++ b/static_core/plugins/ets/runtime/ets_stubs.h @@ -47,6 +47,9 @@ EtsString *EtsGetTypeof(EtsCoroutine *coro, EtsObject *obj); bool EtsGetIstrue(EtsCoroutine *coro, EtsObject *obj); +EtsObject *EtsCallThis(EtsCoroutine *coro, EtsObject *thisObj, + panda_file::File::StringData name, Span objects); + template inline void LookUpException(ark::Class *klass, Field *rawField); diff --git a/static_core/plugins/ets/runtime/interop_js/ets_proxy/ets_proxy.cpp b/static_core/plugins/ets/runtime/interop_js/ets_proxy/ets_proxy.cpp index 01836e7f9b..588e2b1ee2 100644 --- a/static_core/plugins/ets/runtime/interop_js/ets_proxy/ets_proxy.cpp +++ b/static_core/plugins/ets/runtime/interop_js/ets_proxy/ets_proxy.cpp @@ -41,6 +41,7 @@ napi_value GetETSFunction(napi_env env, std::string_view packageName, std::strin classDescriptor = packageName; } + std::cout << "zzm: classDescriptor: " << classDescriptor << std::endl; napi_value jsClass = GetETSClass(env, classDescriptor); ASSERT(GetValueType(env, jsClass) == napi_function); 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..c7011204d9 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 @@ -405,8 +405,8 @@ uint8_t JSRuntimeInstanceOfStatic(JSValue *etsJsValue, EtsClass *etsCls) std::pair ResolveModuleName(std::string_view module) { static const std::unordered_set NATIVE_MODULE_LIST = { - "@system.app", "@ohos.app", "@system.router", "@system.curves", - "@ohos.curves", "@system.matrix4", "@ohos.matrix4"}; + "@system.app", "@ohos.app", "@system.router", "@system.curves", + "@ohos.curves", "@system.matrix4", "@ohos.matrix4"}; constexpr std::string_view REQUIRE = "require"; constexpr std::string_view REQUIRE_NAPI = "requireNapi"; @@ -498,7 +498,7 @@ uint8_t JSRuntimeStrictEqual([[maybe_unused]] JSValue *lhs, [[maybe_unused]] JSV bool result = false; NAPI_CHECK_FATAL(napi_strict_equals(env, JSConvertJSValue::WrapWithNullCheck(env, lhs), - JSConvertJSValue::WrapWithNullCheck(env, rhs), &result)); + JSConvertJSValue::WrapWithNullCheck(env, rhs), &result)); return static_cast(result); } @@ -512,10 +512,23 @@ uint8_t JSRuntimeHasProperty(JSValue *object, EtsString *name) bool result = false; NAPI_CHECK_FATAL(napi_has_property(env, JSConvertJSValue::WrapWithNullCheck(env, object), - JSConvertString::WrapWithNullCheck(env, name), &result)); + JSConvertString::WrapWithNullCheck(env, name), &result)); 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,17 +537,65 @@ JSValue *JSRuntimeGetProperty(JSValue *object, JSValue *property) auto env = ctx->GetJSEnv(); NapiScope jsHandleScope(env); - auto jsThis = JSConvertJSValue::WrapWithNullCheck(env, object); - auto key = JSConvertJSValue::WrapWithNullCheck(env, property); + auto result = JSRuntimeGetPropertyImpl(coro, env, object, property); + return JSConvertJSValue::UnwrapWithNullCheck(ctx, env, result).value(); +} - napi_value result; +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 *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 *recv, 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 (size_t i = 0; i < args.size(); i++) { + EtsObject *arg = EtsObject::FromCoreType(args[i]); + auto realArg = ctx->GetEtsClassWrappersCache()->Lookup(arg->GetClass())->Wrap(ctx, arg); + realArgs.push_back(realArg); + } + + napi_value retVal; + napi_status jsStatus; + auto recvEtsObject = JSConvertJSValue::WrapWithNullCheck(env, recv); + auto funcEtsObject = JSConvertJSValue::WrapWithNullCheck(env, func); { ScopedNativeCodeThread nativeScope(coro); - NAPI_CHECK_FATAL(napi_get_property(env, jsThis, key, &result)); + jsStatus = napi_call_function(env, recvEtsObject, funcEtsObject, realArgs.size(), realArgs.data(), &retVal); } - return JSConvertJSValue::UnwrapWithNullCheck(ctx, env, result).value(); + + if (jsStatus != napi_ok) { + ctx->ForwardJSException(coro); + return nullptr; + } + return JSConvertEtsObject::UnwrapWithNullCheck(ctx, env, retVal).value(); } + uint8_t JSRuntimeHasPropertyJSValue(JSValue *object, JSValue *property) { 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..1f4d207a12 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,9 @@ JSValue *JSRuntimeInvoke(JSValue *recv, JSValue *func, EtsArray *args); JSValue *JSRuntimeInstantiate(JSValue *callable, EtsArray *args); EtsString *JSValueToString(JSValue *object); napi_value ToLocal(void *value); +EtsObject *GetPropertyObject(JSValue *object, JSValue *property); +EtsObject *GetPropertyObjectByString(JSValue *object, const char *property); +EtsObject *InvokeWithObjectReturn(JSValue *thisObj, JSValue *function, Span args); void *CompilerGetJSNamedProperty(void *val, char *propStr); void *CompilerGetJSProperty(void *val, void *prop); void *CompilerGetJSElement(void *val, int32_t index); @@ -127,7 +130,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, long index) { auto coro = EtsCoroutine::GetCurrent(); auto ctx = InteropCtx::Current(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..a9e723bbc4 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,20 @@ JSCONVERT_UNWRAP(JSValue) return JSValue::Create(EtsCoroutine::GetCurrent(), ctx, jsVal); } +// NOTE(zhaoziming_hw, #xxxx) Workaround for no converter based on JS source type, remove ASAP +// Should only be used in any-related bytecode with unwrap call +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 +402,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 +419,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/types/ets_string.h b/static_core/plugins/ets/runtime/types/ets_string.h index 5156976846..f192da961c 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..30219239f5 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 @@ -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) 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..fd8e089cbf 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,6 +64,7 @@ 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(); @@ -75,11 +76,20 @@ 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, + 'log-components': 'ets_interop_js:runtime', + 'boot-panda-files': stdlibPath + ':' + userPandaFiles, + 'panda-files': userPandaFiles, 'gc-trigger-type': 'heap-trigger', 'compiler-enable-jit': 'false', }; 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..a7ada2e4dc --- /dev/null +++ b/static_core/plugins/ets/tests/interop_js/tests/interop_isa/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (c) 2024-2025 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +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} +) \ No newline at end of file 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..35259864b6 --- /dev/null +++ b/static_core/plugins/ets/tests/interop_js/tests/interop_isa/index.js @@ -0,0 +1,22 @@ +/** + * 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; +let testInstanceOf = etsVm.getFunction("Ltest-static/ETSGLOBAL;", "testIsInstance"); + +class C { + f = 1; +} +ASSERT_TRUE(testInstanceOf(new C(), C)); +ASSERT_TRUE(testInstanceOf({f: 1}, C)); \ 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..547c8f7499 --- /dev/null +++ b/static_core/plugins/ets/tests/interop_js/tests/interop_isa/test.cpp @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#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 \ No newline at end of file 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..e69de29bb2 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..cab9fdc0f4 --- /dev/null +++ b/static_core/plugins/ets/tests/interop_js/tests/interop_isa/test.pa @@ -0,0 +1,26 @@ +# source binary: ./build/test-static.abc + +.language eTS + +.record std.core.Object + +.record std.core.String + +.record std.interop.js.JSValue + +.record std.interop.js.JSRuntime + +.record test-static.ETSGLOBAL { +} + +.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 +} \ No newline at end of file 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..35012019e8 100644 --- a/static_core/runtime/interpreter/interpreter-inl.h +++ b/static_core/runtime/interpreter/interpreter-inl.h @@ -59,6 +59,10 @@ #include "runtime/mem/vm_handle.h" #include "runtime/handle_base-inl.h" +// TODO: remove this after we have real plugin +#include "plugins/ets/runtime/ets_stubs.h" +#include "plugins/ets/runtime/ets_stubs-inl.h" + // ALWAYS_INLINE is mandatory attribute for handlers. There are cases which will be failed without it. namespace ark::interpreter { @@ -1235,8 +1239,24 @@ public: template ALWAYS_INLINE void HandleAnyCallThisRange() { - LOG_INST() << "Unimplemented instruction any.call.this.range"; - UNREACHABLE(); + uint16_t vs1 = this->GetInst().template GetVReg(); + ObjectHeader *thisObj = this->GetFrame()->GetVReg(vs1).template GetAs(); + // This should be generated from plugin.erb file + if (thisObj->template ClassAddr()->GetSourceLang() == panda_file::SourceLang::ETS) { + auto argc = this->GetInst().template GetImm(); + auto stringId = this->GetInst().template GetId(); + uint16_t arg_start = this->GetInst().template GetVReg(); + PandaVector args; + for (int i = 0; i < argc; i++) { + args.push_back(this->GetFrame()->GetVReg(arg_start).template GetAs()); + } + auto *pf = this->GetFrame()->GetMethod()->GetPandaFile(); + ets::PluginAnyCallThis(this->GetThread(), thisObj, + pf->GetStringData(stringId.AsFileId()), Span(args)); + } else { + LOG_INST() << "Unimplemented instruction any.call.this.range for non-ets plugin"; + UNREACHABLE(); + } } template @@ -1267,6 +1287,14 @@ public: UNREACHABLE(); } + template + ALWAYS_INLINE void HandleAnyIsinstance() + { + std::cout << "handleAnyIsInstance" << std::endl; + LOG_INST() << "Unimplemented instruction any.isinstance"; + UNREACHABLE(); + } + template ALWAYS_INLINE void HandleAddi() { diff --git a/static_core/verification/absint/abs_int_inl.h b/static_core/verification/absint/abs_int_inl.h index 6a7a0637d2..6cd40643d4 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,16 @@ public: return true; } + template + bool HandleAnyIsinstance() + { + LOG_INST(); + DBGBRK(); + Sync(); + // NOTE: @MockMockBlack handle it + return true; + } + private: Type GetCachedType() const { -- Gitee