From b90f2161d25b26f217f0e92588f45769b6da05eb Mon Sep 17 00:00:00 2001 From: wwwcg Date: Thu, 16 Oct 2025 16:55:46 +0800 Subject: [PATCH 01/11] feat(android): hermes engine support --- framework/android/build.gradle | 10 ++ .../android/connector/driver/js/build.gradle | 50 ++++++ .../connector/driver/js/gradle.properties | 5 +- .../js/src/main/cpp/src/js_driver_jni.cc | 147 ++++++++++++++++++ .../com/openhippy/connector/JsDriver.java | 14 ++ .../com/tencent/mtt/hippy/HippyEngine.java | 4 + .../mtt/hippy/HippyEngineManagerImpl.java | 7 +- .../mtt/hippy/bridge/HippyBridgeImpl.java | 16 +- .../hippy/bridge/HippyBridgeManagerImpl.java | 13 +- .../openhippy/example/HippyEngineHelper.kt | 23 ++- .../openhippy/example/HippyEngineWrapper.kt | 16 ++ .../openhippy/example/PageConfiguration.kt | 50 +++++- 12 files changed, 348 insertions(+), 7 deletions(-) diff --git a/framework/android/build.gradle b/framework/android/build.gradle index e1501eb6b89..8fe6f0e21d6 100644 --- a/framework/android/build.gradle +++ b/framework/android/build.gradle @@ -26,6 +26,16 @@ apply plugin: 'com.android.library' apply plugin: 'com.kezong.fat-aar' apply from: './publish.gradle' +// Load gradle properties for JS engine configuration +Properties gradleProperties = new Properties() +def propFile = new File("gradle.properties") +if (propFile.exists()) { + gradleProperties.load(propFile.newDataInputStream()) +} + +def cppJsEngine = gradleProperties.getProperty('CPP_JS_ENGINE', 'V8') +def cppHermesComponent = gradleProperties.getProperty('CPP_HERMES_COMPONENT', '') + def mergeCppDefinitions() { def cppProps = new Properties() getAllModules().each { diff --git a/framework/android/connector/driver/js/build.gradle b/framework/android/connector/driver/js/build.gradle index 8e80867742e..1dfd8e22e52 100644 --- a/framework/android/connector/driver/js/build.gradle +++ b/framework/android/connector/driver/js/build.gradle @@ -7,6 +7,17 @@ allprojects { } } +// Load gradle properties for JS engine configuration +Properties gradleProperties = new Properties() +def propFile = new File("gradle.properties") +if (propFile.exists()) { + gradleProperties.load(propFile.newDataInputStream()) +} + +def cppJsEngine = gradleProperties.getProperty('CPP_JS_ENGINE', 'V8') +def cppHermesComponent = gradleProperties.getProperty('CPP_HERMES_COMPONENT', '') +def cppEnableInspector = gradleProperties.getProperty('CPP_ENABLE_INSPECTOR', 'true') + android { compileSdkVersion COMPILE_VERSION as int @@ -18,12 +29,44 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" + + // Pass JS engine configuration to CMake + externalNativeBuild { + cmake { + arguments "-DJS_ENGINE=${cppJsEngine}" + if (cppHermesComponent) { + arguments "-DHERMES_COMPONENT=${cppHermesComponent}" + } + arguments "-DENABLE_INSPECTOR=${cppEnableInspector}" + } + } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + externalNativeBuild { + cmake { + arguments "-DJS_ENGINE=${cppJsEngine}" + if (cppHermesComponent) { + arguments "-DHERMES_COMPONENT=${cppHermesComponent}" + } + arguments "-DENABLE_INSPECTOR=${cppEnableInspector}" + } + } + } + debug { + minifyEnabled false + externalNativeBuild { + cmake { + arguments "-DJS_ENGINE=${cppJsEngine}" + if (cppHermesComponent) { + arguments "-DHERMES_COMPONENT=${cppHermesComponent}" + } + arguments "-DENABLE_INSPECTOR=${cppEnableInspector}" + } + } } } @@ -35,6 +78,13 @@ android { buildFeatures { buildConfig = false } + + externalNativeBuild { + cmake { + path "src/main/cpp/CMakeLists.txt" + version "3.18.1" + } + } } dependencies { diff --git a/framework/android/connector/driver/js/gradle.properties b/framework/android/connector/driver/js/gradle.properties index 8b33ee046db..df9dfaede1d 100644 --- a/framework/android/connector/driver/js/gradle.properties +++ b/framework/android/connector/driver/js/gradle.properties @@ -18,8 +18,9 @@ # # Indicates the javascript engine to use: -# * V8 -# * HERMES +# * V8 - Use V8 engine only +# * HERMES_ONLY - Use Hermes engine only +# * HERMES_V8 - Support both Hermes and V8 engines (parallel build) # CPP_JS_ENGINE=V8 diff --git a/framework/android/connector/driver/js/src/main/cpp/src/js_driver_jni.cc b/framework/android/connector/driver/js/src/main/cpp/src/js_driver_jni.cc index 1c78ec3f212..ea655ba7ca3 100644 --- a/framework/android/connector/driver/js/src/main/cpp/src/js_driver_jni.cc +++ b/framework/android/connector/driver/js/src/main/cpp/src/js_driver_jni.cc @@ -78,6 +78,12 @@ REGISTER_JNI("com/openhippy/connector/JsDriver", // NOLINT(cert-err58-cpp) "JILcom/openhippy/connector/JsDriver$V8InitParams;IIZ)I", CreateJsDriver) +REGISTER_JNI("com/openhippy/connector/JsDriver", // NOLINT(cert-err58-cpp) + "onCreate", + "([BZZZLcom/openhippy/connector/NativeCallback;" + "JILcom/openhippy/connector/JsDriver$V8InitParams;IIZZLjava/lang/String;)I", + CreateJsDriverWithEngine) + REGISTER_JNI("com/openhippy/connector/JsDriver", // NOLINT(cert-err58-cpp) "onDestroy", "(IZZLcom/openhippy/connector/NativeCallback;)V", @@ -397,6 +403,147 @@ jint CreateJsDriver(JNIEnv* j_env, return footstone::checked_numeric_cast(scope_id); } +jint CreateJsDriverWithEngine(JNIEnv* j_env, + jobject j_object, + jbyteArray j_global_config, + jboolean j_single_thread_mode, + jboolean j_enable_v8_serialization, + jboolean j_is_dev_module, + jobject j_callback, + jlong j_group_id, + jint j_dom_manager_id, + jobject j_vm_init_param, + jint j_vfs_id, + jint j_devtools_id, + jboolean j_is_reload, + jboolean j_use_hermes_engine, + jstring j_js_engine_type) { + FOOTSTONE_LOG(INFO) << "CreateJsDriverWithEngine begin, j_single_thread_mode = " + << static_cast(j_single_thread_mode) + << ", j_enable_v8_serialization = " + << static_cast(j_enable_v8_serialization) + << ", j_is_dev_module = " + << static_cast(j_is_dev_module) + << ", j_group_id = " << j_group_id + << ", j_use_hermes_engine = " << static_cast(j_use_hermes_engine); + + // perfromance start time + auto perf_start_time = footstone::TimePoint::SystemNow(); + + auto global_config = JniUtils::JByteArrayToStrView(j_env, j_global_config); + auto java_callback = std::make_shared(j_env, j_callback); + + // Convert jstring to std::string + std::string js_engine_type = "v8"; + if (j_js_engine_type) { + const char* engine_type_cstr = j_env->GetStringUTFChars(j_js_engine_type, nullptr); + if (engine_type_cstr) { + js_engine_type = std::string(engine_type_cstr); + j_env->ReleaseStringUTFChars(j_js_engine_type, engine_type_cstr); + } + } + + // Determine engine type based on parameters + std::string vm_type = "v8"; + if (j_use_hermes_engine || js_engine_type == "hermes") { + vm_type = "hermes"; + } + + std::shared_ptr param; +#ifdef JS_V8 + if (vm_type == "v8") { + auto v8_param = std::make_shared(); + v8_param->enable_v8_serialization = static_cast(j_enable_v8_serialization); + v8_param->is_debug = static_cast(j_is_dev_module); + v8_param->group_id = static_cast(j_group_id); + v8_param->vm_type = vm_type; + if (j_vm_init_param) { + jclass cls = j_env->GetObjectClass(j_vm_init_param); + jfieldID init_field = j_env->GetFieldID(cls, "initialHeapSize", "J"); + jlong initial_heap_size_in_bytes = j_env->GetLongField(j_vm_init_param, init_field); + v8_param->initial_heap_size_in_bytes = footstone::check::checked_numeric_cast( + initial_heap_size_in_bytes); + jfieldID max_field = j_env->GetFieldID(cls, "maximumHeapSize", "J"); + jlong maximum_heap_size_in_bytes = j_env->GetLongField(j_vm_init_param, max_field); + v8_param->maximum_heap_size_in_bytes = footstone::check::checked_numeric_cast( + maximum_heap_size_in_bytes); + FOOTSTONE_CHECK(initial_heap_size_in_bytes <= maximum_heap_size_in_bytes); + } + param = v8_param; + } +#endif +#ifdef JS_HERMES + if (vm_type == "hermes") { + auto hermes_param = std::make_shared(); + hermes_param->is_debug = static_cast(j_is_dev_module); + hermes_param->group_id = static_cast(j_group_id); + hermes_param->vm_type = vm_type; + param = hermes_param; + } +#endif + + if (!param) { + FOOTSTONE_LOG(ERROR) << "Unsupported engine type: " << vm_type; + return -1; + } + +#ifdef ENABLE_INSPECTOR + if (param->is_debug) { + auto devtools_data_source = devtools::DevtoolsDataSource::Find( + footstone::checked_numeric_cast(j_devtools_id)); + param->devtools_data_source = devtools_data_source; + } +#endif + auto call_host_callback = [](CallbackInfo& info, void* data) { + hippy::bridge::CallHost(info); + }; + param->uncaught_exception_callback = ([](const std::any& bridge, const string_view& description, const string_view& stack) { + ExceptionHandler::ReportJsException(bridge, description, stack); + }); + auto dom_manager_id = footstone::check::checked_numeric_cast(j_dom_manager_id); + std::any dom_manager; + auto flag = hippy::global_data_holder.Find(dom_manager_id, dom_manager); + FOOTSTONE_CHECK(flag); + auto dom_manager_object = std::any_cast>(dom_manager); + auto dom_task_runner = dom_manager_object->GetTaskRunner(); + auto bridge = std::make_shared(j_env, j_object); + auto scope_id = hippy::global_data_holder_key.fetch_add(1); + scope_initialized_map.insert({scope_id, false}); + scope_cv_map.insert({scope_id, std::make_unique()}); + + auto scope_initialized_callback = [perf_start_time, + scope_id, java_callback, bridge, &holder = hippy::global_data_holder](std::shared_ptr scope) { + scope->SetBridge(bridge); + holder.Insert(scope_id, scope); + + // perfromance end time + auto entry = scope->GetPerformance()->PerformanceNavigation("hippyInit"); + entry->SetHippyJsEngineInitStart(perf_start_time); + entry->SetHippyJsEngineInitEnd(footstone::TimePoint::SystemNow()); + + FOOTSTONE_LOG(INFO) << "run scope cb"; + hippy::bridge::CallJavaMethod(java_callback->GetObj(), INIT_CB_STATE::SUCCESS); + { + std::unique_lock lock(scope_mutex); + scope_initialized_map[scope_id] = true; + scope_cv_map[scope_id]->notify_all(); + } + }; + auto engine = JsDriverUtils::CreateEngineAndAsyncInitialize( + dom_task_runner, param, static_cast(j_group_id), static_cast(j_is_reload)); + { + std::lock_guard lock(holder_mutex); + engine_holder[engine.get()] = engine; + } + JsDriverUtils::InitInstance( + engine, + param, + global_config, + scope_initialized_callback, + call_host_callback); + return footstone::checked_numeric_cast(scope_id); +} + void DestroyJsDriver(__unused JNIEnv* j_env, __unused jobject j_object, jint j_scope_id, diff --git a/framework/android/connector/driver/js/src/main/java/com/openhippy/connector/JsDriver.java b/framework/android/connector/driver/js/src/main/java/com/openhippy/connector/JsDriver.java index 5cdb4d379e4..6bac56e981d 100644 --- a/framework/android/connector/driver/js/src/main/java/com/openhippy/connector/JsDriver.java +++ b/framework/android/connector/driver/js/src/main/java/com/openhippy/connector/JsDriver.java @@ -97,6 +97,15 @@ public void initialize(byte[] globalConfig, boolean useLowMemoryMode, isDevModule, callback, groupId, domManagerId, v8InitParams, vfsId, devtoolsId, isReload); } + public void initialize(byte[] globalConfig, boolean useLowMemoryMode, + boolean enableV8Serialization, boolean isDevModule, NativeCallback callback, + long groupId, int domManagerId, V8InitParams v8InitParams, int vfsId, int devtoolsId, + boolean isReload, boolean useHermesEngine, String jsEngineType) { + mInstanceId = onCreate(globalConfig, useLowMemoryMode, enableV8Serialization, + isDevModule, callback, groupId, domManagerId, v8InitParams, vfsId, devtoolsId, + isReload, useHermesEngine, jsEngineType); + } + public void onDestroy(boolean useLowMemoryMode, boolean isReload, NativeCallback callback) { onDestroy(mInstanceId, useLowMemoryMode, isReload, callback); @@ -149,6 +158,11 @@ private native int onCreate(byte[] globalConfig, boolean useLowMemoryMode, boolean enableV8Serialization, boolean isDevModule, NativeCallback callback, long groupId, int domManagerId, V8InitParams v8InitParams, int vfs_id, int devtoolsId, boolean isReload); + private native int onCreate(byte[] globalConfig, boolean useLowMemoryMode, + boolean enableV8Serialization, boolean isDevModule, NativeCallback callback, + long groupId, int domManagerId, V8InitParams v8InitParams, int vfs_id, int devtoolsId, + boolean isReload, boolean useHermesEngine, String jsEngineType); + private native void onDestroy(int instanceId, boolean useLowMemoryMode, boolean isReload, NativeCallback callback); diff --git a/framework/android/src/main/java/com/tencent/mtt/hippy/HippyEngine.java b/framework/android/src/main/java/com/tencent/mtt/hippy/HippyEngine.java index c15f950782f..a57551aead1 100644 --- a/framework/android/src/main/java/com/tencent/mtt/hippy/HippyEngine.java +++ b/framework/android/src/main/java/com/tencent/mtt/hippy/HippyEngine.java @@ -320,6 +320,10 @@ public static class EngineInitParams { public HippyLogAdapter logAdapter; public V8InitParams v8InitParams; public boolean enableTurbo; + // 可选参数 是否使用Hermes引擎,默认为false使用V8 + public boolean useHermesEngine = false; + // 可选参数 指定JS引擎类型:"v8" 或 "hermes",默认为"v8" + public String jsEngineType = "v8"; protected void check() { if (context == null) { diff --git a/framework/android/src/main/java/com/tencent/mtt/hippy/HippyEngineManagerImpl.java b/framework/android/src/main/java/com/tencent/mtt/hippy/HippyEngineManagerImpl.java index 668525ea1cf..58e1a56bf8d 100644 --- a/framework/android/src/main/java/com/tencent/mtt/hippy/HippyEngineManagerImpl.java +++ b/framework/android/src/main/java/com/tencent/mtt/hippy/HippyEngineManagerImpl.java @@ -134,6 +134,8 @@ public abstract class HippyEngineManagerImpl extends HippyEngineManager implemen private final TimeMonitor mMonitor; private final HippyThirdPartyAdapter mThirdPartyAdapter; private final V8InitParams v8InitParams; + private final boolean mUseHermesEngine; + private final String mJsEngineType; private HashMap mNativeParams; @Nullable private HashMap> mDestroyModuleListeners; @@ -161,6 +163,8 @@ public abstract class HippyEngineManagerImpl extends HippyEngineManager implemen mGroupId = params.groupId; mThirdPartyAdapter = params.thirdPartyAdapter; v8InitParams = params.v8InitParams; + mUseHermesEngine = params.useHermesEngine; + mJsEngineType = params.jsEngineType; mMonitor = new TimeMonitor(getEngineId()); } @@ -893,7 +897,8 @@ public HippyEngineContextImpl(@Nullable DomManager domManager) throws RuntimeExc mJsDriver = new JsDriver(); mBridgeManager = new HippyBridgeManagerImpl(this, mCoreBundleLoader, getBridgeType(), enableV8Serialization, mDebugMode, - mServerHost, mGroupId, mThirdPartyAdapter, v8InitParams, mJsDriver, mInitStartTime); + mServerHost, mGroupId, mThirdPartyAdapter, v8InitParams, mJsDriver, mInitStartTime, + mUseHermesEngine, mJsEngineType); mDomManager = (domManager != null) ? domManager : new DomManager(mGroupId); mRenderer = createRenderer(RenderConnector.NATIVE_RENDERER); mDomManager.attachToRenderer(mRenderer); diff --git a/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeImpl.java b/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeImpl.java index 64e0be25713..8012e18e1c5 100644 --- a/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeImpl.java +++ b/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeImpl.java @@ -66,10 +66,20 @@ public class HippyBridgeImpl implements HippyBridge, JSBridgeProxy, DevRemoteDeb private final V8InitParams mV8InitParams; @NonNull private final JsDriver mJsDriver; + private final boolean mUseHermesEngine; + private final String mJsEngineType; public HippyBridgeImpl(HippyEngineContext engineContext, BridgeCallback callback, boolean singleThreadMode, boolean enableV8Serialization, boolean isDevModule, String debugServerHost, V8InitParams v8InitParams, @NonNull JsDriver jsDriver) { + this(engineContext, callback, singleThreadMode, enableV8Serialization, isDevModule, + debugServerHost, v8InitParams, jsDriver, false, "v8"); + } + + public HippyBridgeImpl(HippyEngineContext engineContext, BridgeCallback callback, + boolean singleThreadMode, boolean enableV8Serialization, boolean isDevModule, + String debugServerHost, V8InitParams v8InitParams, @NonNull JsDriver jsDriver, + boolean useHermesEngine, String jsEngineType) { mBridgeCallback = callback; mSingleThreadMode = singleThreadMode; mEnableV8Serialization = enableV8Serialization; @@ -78,6 +88,8 @@ public HippyBridgeImpl(HippyEngineContext engineContext, BridgeCallback callback mContext = engineContext; mV8InitParams = v8InitParams; mJsDriver = jsDriver; + mUseHermesEngine = useHermesEngine; + mJsEngineType = jsEngineType; mJsDriver.setBridgeProxy(this); if (mCodeCacheRootDir == null) { Context context = mContext.getGlobalConfigs().getContext(); @@ -113,7 +125,9 @@ private void initJSEngine(int groupId, NativeCallback callback, boolean isReload mV8InitParams, mContext.getVfsId(), mContext.getDevtoolsId(), - isReload + isReload, + mUseHermesEngine, + mJsEngineType ); mInit = true; } catch (Throwable e) { diff --git a/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManagerImpl.java b/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManagerImpl.java index fdba7bc0b30..4e37dad43b1 100644 --- a/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManagerImpl.java +++ b/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManagerImpl.java @@ -115,6 +115,16 @@ public HippyBridgeManagerImpl(HippyEngineContext context, HippyBundleLoader core int bridgeType, boolean enableV8Serialization, boolean isDevModule, String debugServerHost, int groupId, HippyThirdPartyAdapter thirdPartyAdapter, V8InitParams v8InitParams, @NonNull JsDriver jsDriver, long initStartTime) { + this(context, coreBundleLoader, bridgeType, enableV8Serialization, isDevModule, + debugServerHost, groupId, thirdPartyAdapter, v8InitParams, jsDriver, initStartTime, + false, "v8"); + } + + public HippyBridgeManagerImpl(HippyEngineContext context, HippyBundleLoader coreBundleLoader, + int bridgeType, boolean enableV8Serialization, boolean isDevModule, + String debugServerHost, int groupId, HippyThirdPartyAdapter thirdPartyAdapter, + V8InitParams v8InitParams, @NonNull JsDriver jsDriver, long initStartTime, + boolean useHermesEngine, String jsEngineType) { mContext = context; mCoreBundleLoader = coreBundleLoader; mGroupId = groupId; @@ -122,7 +132,8 @@ public HippyBridgeManagerImpl(HippyEngineContext context, HippyBundleLoader core mThirdPartyAdapter = thirdPartyAdapter; mEnableV8Serialization = enableV8Serialization; mHippyBridge = new HippyBridgeImpl(context, this, bridgeType == BRIDGE_TYPE_SINGLE_THREAD, - enableV8Serialization, isDevModule, debugServerHost, v8InitParams, jsDriver); + enableV8Serialization, isDevModule, debugServerHost, v8InitParams, jsDriver, + useHermesEngine, jsEngineType); if (enableV8Serialization) { compatibleSerializer = new Serializer(); recommendSerializer = new com.tencent.mtt.hippy.serialization.recommend.Serializer(); diff --git a/framework/examples/android-demo/src/main/java/com/openhippy/example/HippyEngineHelper.kt b/framework/examples/android-demo/src/main/java/com/openhippy/example/HippyEngineHelper.kt index fa5eec8091f..7d5216f6212 100644 --- a/framework/examples/android-demo/src/main/java/com/openhippy/example/HippyEngineHelper.kt +++ b/framework/examples/android-demo/src/main/java/com/openhippy/example/HippyEngineHelper.kt @@ -32,6 +32,26 @@ class HippyEngineHelper { isSnapshotMode: Boolean, debugServerHost: String, context: Context + ): HippyEngineWrapper { + return createHippyEngine( + driverType, + rendererType, + isDebugMode, + isSnapshotMode, + debugServerHost, + context, + PageConfiguration.JSEngineType.V8 + ) + } + + fun createHippyEngine( + driverType: PageConfiguration.DriverMode, + rendererType: PageConfiguration.RenderMode, + isDebugMode: Boolean, + isSnapshotMode: Boolean, + debugServerHost: String, + context: Context, + jsEngineType: PageConfiguration.JSEngineType ): HippyEngineWrapper { val hippyEngineWrapper = HippyEngineWrapper( driverType, @@ -39,7 +59,8 @@ class HippyEngineHelper { isDebugMode, isSnapshotMode, debugServerHost, - context + context, + jsEngineType ) hippyEngineList.add(hippyEngineWrapper) return hippyEngineWrapper diff --git a/framework/examples/android-demo/src/main/java/com/openhippy/example/HippyEngineWrapper.kt b/framework/examples/android-demo/src/main/java/com/openhippy/example/HippyEngineWrapper.kt index 052757b3ea0..fcace70eee7 100644 --- a/framework/examples/android-demo/src/main/java/com/openhippy/example/HippyEngineWrapper.kt +++ b/framework/examples/android-demo/src/main/java/com/openhippy/example/HippyEngineWrapper.kt @@ -43,6 +43,18 @@ class HippyEngineWrapper//TODO: Coming soon debugServerHost: String, context: Context ) { + this(dm, rm, isDebug, useNodeSnapshot, debugServerHost, context, PageConfiguration.JSEngineType.V8) +} + +constructor( + dm: PageConfiguration.DriverMode, + rm: PageConfiguration.RenderMode, + isDebug: Boolean, + useNodeSnapshot: Boolean, + debugServerHost: String, + context: Context, + jsEngineType: PageConfiguration.JSEngineType +) { var hippyEngine: HippyEngine var hippyRootView: ViewGroup? = null @@ -54,6 +66,7 @@ class HippyEngineWrapper//TODO: Coming soon var isSnapshotMode: Boolean = useNodeSnapshot val driverMode: PageConfiguration.DriverMode = dm val renderMode: PageConfiguration.RenderMode = rm + val jsEngineType: PageConfiguration.JSEngineType = jsEngineType val engineId: Int companion object { @@ -83,6 +96,9 @@ class HippyEngineWrapper//TODO: Coming soon } } initParams.codeCacheTag = "common" + // Configure JS engine type + initParams.useHermesEngine = (jsEngineType == PageConfiguration.JSEngineType.HERMES) + initParams.jsEngineType = if (jsEngineType == PageConfiguration.JSEngineType.HERMES) "hermes" else "v8" initParams.exceptionHandler = object : HippyExceptionHandlerAdapter { override fun handleJsException(e: HippyJsException) { LogUtils.e("hippy", e.message) diff --git a/framework/examples/android-demo/src/main/java/com/openhippy/example/PageConfiguration.kt b/framework/examples/android-demo/src/main/java/com/openhippy/example/PageConfiguration.kt index a8659cf7ebf..d5d3ea97aee 100644 --- a/framework/examples/android-demo/src/main/java/com/openhippy/example/PageConfiguration.kt +++ b/framework/examples/android-demo/src/main/java/com/openhippy/example/PageConfiguration.kt @@ -43,6 +43,10 @@ class PageConfiguration : AppCompatActivity(), View.OnClickListener { NATIVE, TDF_CORE, FLUTTER } + enum class JSEngineType { + V8, HERMES + } + companion object { var currentEngineId: Int = -1 } @@ -61,6 +65,7 @@ class PageConfiguration : AppCompatActivity(), View.OnClickListener { private var dialog: Dialog? = null private var driverMode: DriverMode = DriverMode.JS_REACT private var renderMode: RenderMode = RenderMode.NATIVE + private var jsEngineType: JSEngineType = JSEngineType.V8 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -167,6 +172,15 @@ class PageConfiguration : AppCompatActivity(), View.OnClickListener { rendererSettingButton.setOnClickListener { onRendererSettingClick() } + + // Add JS Engine setting button + val jsEngineSettingButton = pageConfigurationRoot.findViewById(R.id.page_configuration_js_engine_setting) + val jsEngineSettingText = pageConfigurationRoot.findViewById(R.id.page_configuration_js_engine_setting_title) + jsEngineSettingButton?.setOnClickListener { + onJSEngineSettingClick() + } + // Update JS engine setting text + updateJSEngineSettingText(jsEngineSettingText) val debugButton = pageConfigurationRoot.findViewById(R.id.page_configuration_debug_setting_image) val debugServerHostParent = @@ -216,7 +230,8 @@ class PageConfiguration : AppCompatActivity(), View.OnClickListener { debugMode, snapshotMode, debugServerHost, - applicationContext + applicationContext, + jsEngineType ) hippyEngineWrapper?.let { currentEngineId = it.engineId @@ -270,6 +285,29 @@ class PageConfiguration : AppCompatActivity(), View.OnClickListener { dialog?.show() } + private fun onJSEngineSettingClick() { + dialog = Dialog(this, R.style.PageConfigurationDialogStyle) + val jsEngineSetting = layoutInflater.inflate(R.layout.js_engine_setting, null) + val jsEngineV8 = jsEngineSetting.findViewById(R.id.page_configuration_js_engine_v8) + val jsEngineHermes = jsEngineSetting.findViewById(R.id.page_configuration_js_engine_hermes) + jsEngineV8.setOnClickListener(this) + jsEngineHermes.setOnClickListener(this) + dialog?.setContentView(jsEngineSetting) + val dialogWindow = dialog?.window + dialogWindow?.setGravity(Gravity.BOTTOM) + val lp = dialogWindow?.attributes + lp?.width = (getScreenWidth(applicationContext) * 0.95).toInt() + dialogWindow?.attributes = lp + dialog?.show() + } + + private fun updateJSEngineSettingText(textView: TextView?) { + textView?.text = when (jsEngineType) { + JSEngineType.V8 -> "V8" + JSEngineType.HERMES -> "Hermes" + } + } + private fun onDriverSettingClick() { dialog = Dialog(this, R.style.PageConfigurationDialogStyle) val driverSetting = layoutInflater.inflate(R.layout.driver_setting, null) @@ -312,6 +350,16 @@ class PageConfiguration : AppCompatActivity(), View.OnClickListener { (rendererSettingText as TextView).text = resources.getText(R.string.renderer_native) dialog?.dismiss() } + R.id.page_configuration_js_engine_v8 -> { + jsEngineType = JSEngineType.V8 + updateJSEngineSettingText(pageConfigurationRoot.findViewById(R.id.page_configuration_js_engine_setting_title)) + dialog?.dismiss() + } + R.id.page_configuration_js_engine_hermes -> { + jsEngineType = JSEngineType.HERMES + updateJSEngineSettingText(pageConfigurationRoot.findViewById(R.id.page_configuration_js_engine_setting_title)) + dialog?.dismiss() + } R.id.page_configuration_renderer_tdf_core, R.id.page_configuration_renderer_flutter, R.id.page_configuration_driver_vl -> { From 3cc5f36fea8d7cf7283c3abd18222d8e64c5ef14 Mon Sep 17 00:00:00 2001 From: wwwcg Date: Thu, 16 Oct 2025 20:29:52 +0800 Subject: [PATCH 02/11] feat(android): hermes support --- .../android/connector/driver/js/build.gradle | 46 ----- .../connector/driver/js/gradle.properties | 4 +- .../cpp/include/connector/js_driver_jni.h | 16 ++ .../js/src/main/cpp/src/js_driver_jni.cc | 19 +- .../com/openhippy/connector/JsDriver.java | 4 +- .../openhippy/example/HippyEngineWrapper.kt | 18 +- .../layout/activity_page_configuration.xml | 179 +++++++++++++----- 7 files changed, 168 insertions(+), 118 deletions(-) diff --git a/framework/android/connector/driver/js/build.gradle b/framework/android/connector/driver/js/build.gradle index 1dfd8e22e52..8ca1091b979 100644 --- a/framework/android/connector/driver/js/build.gradle +++ b/framework/android/connector/driver/js/build.gradle @@ -7,16 +7,6 @@ allprojects { } } -// Load gradle properties for JS engine configuration -Properties gradleProperties = new Properties() -def propFile = new File("gradle.properties") -if (propFile.exists()) { - gradleProperties.load(propFile.newDataInputStream()) -} - -def cppJsEngine = gradleProperties.getProperty('CPP_JS_ENGINE', 'V8') -def cppHermesComponent = gradleProperties.getProperty('CPP_HERMES_COMPONENT', '') -def cppEnableInspector = gradleProperties.getProperty('CPP_ENABLE_INSPECTOR', 'true') android { compileSdkVersion COMPILE_VERSION as int @@ -29,44 +19,15 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" - - // Pass JS engine configuration to CMake - externalNativeBuild { - cmake { - arguments "-DJS_ENGINE=${cppJsEngine}" - if (cppHermesComponent) { - arguments "-DHERMES_COMPONENT=${cppHermesComponent}" - } - arguments "-DENABLE_INSPECTOR=${cppEnableInspector}" - } - } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - externalNativeBuild { - cmake { - arguments "-DJS_ENGINE=${cppJsEngine}" - if (cppHermesComponent) { - arguments "-DHERMES_COMPONENT=${cppHermesComponent}" - } - arguments "-DENABLE_INSPECTOR=${cppEnableInspector}" - } - } } debug { minifyEnabled false - externalNativeBuild { - cmake { - arguments "-DJS_ENGINE=${cppJsEngine}" - if (cppHermesComponent) { - arguments "-DHERMES_COMPONENT=${cppHermesComponent}" - } - arguments "-DENABLE_INSPECTOR=${cppEnableInspector}" - } - } } } @@ -78,13 +39,6 @@ android { buildFeatures { buildConfig = false } - - externalNativeBuild { - cmake { - path "src/main/cpp/CMakeLists.txt" - version "3.18.1" - } - } } dependencies { diff --git a/framework/android/connector/driver/js/gradle.properties b/framework/android/connector/driver/js/gradle.properties index df9dfaede1d..46a42534234 100644 --- a/framework/android/connector/driver/js/gradle.properties +++ b/framework/android/connector/driver/js/gradle.properties @@ -43,12 +43,12 @@ CPP_V8_COMPONENT=10.6.194.26 # Hermes Component # # The following prebuilt Hermes versions are available: -# * hermes-2023-09-26-RNv0.73.0-ee2922a50fb719bdb378025d95dbd32ad93cd679 (current use) +# * hermes-0.12.30302-rn0.76-stable-20251016 (RN 0.76-stable) # # You can also specify the absolute path to the Hermes component to use, # e.g. /opt/Hermes-component # -CPP_HERMES_COMPONENT=hermes-2023-09-26-RNv0.73.0-ee2922a50fb719bdb378025d95dbd32ad93cd679 +CPP_HERMES_COMPONENT=hermes-0.76-test # diff --git a/framework/android/connector/driver/js/src/main/cpp/include/connector/js_driver_jni.h b/framework/android/connector/driver/js/src/main/cpp/include/connector/js_driver_jni.h index 43d145c927c..78aacb7e3a7 100644 --- a/framework/android/connector/driver/js/src/main/cpp/include/connector/js_driver_jni.h +++ b/framework/android/connector/driver/js/src/main/cpp/include/connector/js_driver_jni.h @@ -43,6 +43,22 @@ jint CreateJsDriver(JNIEnv* j_env, jint j_devtools_id, jboolean j_is_reload); +jint CreateJsDriverWithEngine(JNIEnv* j_env, + jobject j_object, + jbyteArray j_global_config, + jboolean j_single_thread_mode, + jboolean j_enable_v8_serialization, + jboolean j_is_dev_module, + jobject j_callback, + jlong j_group_id, + jint j_dom_manager_id, + jobject j_vm_init_param, + jint j_vfs_id, + jint j_devtools_id, + jboolean j_is_reload, + jboolean j_use_hermes_engine, + jstring j_js_engine_type); + void DestroyJsDriver(JNIEnv* j_env, jobject j_object, jint j_runtime_id, diff --git a/framework/android/connector/driver/js/src/main/cpp/src/js_driver_jni.cc b/framework/android/connector/driver/js/src/main/cpp/src/js_driver_jni.cc index ea655ba7ca3..122b1917db9 100644 --- a/framework/android/connector/driver/js/src/main/cpp/src/js_driver_jni.cc +++ b/framework/android/connector/driver/js/src/main/cpp/src/js_driver_jni.cc @@ -33,6 +33,7 @@ #include "connector/js2java.h" #include "connector/turbo_module_manager.h" #include "driver/js_driver_utils.h" +#include "driver/vm/js_vm.h" #include "footstone/check.h" #include "footstone/logging.h" #include "footstone/persistent_object_map.h" @@ -79,7 +80,7 @@ REGISTER_JNI("com/openhippy/connector/JsDriver", // NOLINT(cert-err58-cpp) CreateJsDriver) REGISTER_JNI("com/openhippy/connector/JsDriver", // NOLINT(cert-err58-cpp) - "onCreate", + "onCreateWithEngine", "([BZZZLcom/openhippy/connector/NativeCallback;" "JILcom/openhippy/connector/JsDriver$V8InitParams;IIZZLjava/lang/String;)I", CreateJsDriverWithEngine) @@ -444,14 +445,18 @@ jint CreateJsDriverWithEngine(JNIEnv* j_env, } // Determine engine type based on parameters - std::string vm_type = "v8"; + std::string vm_type = hippy::VM::kJSEngineV8; if (j_use_hermes_engine || js_engine_type == "hermes") { - vm_type = "hermes"; + vm_type = hippy::VM::kJSEngineHermes; } - std::shared_ptr param; + FOOTSTONE_LOG(INFO) << "CreateJsDriverWithEngine: vm_type = " << vm_type + << ", j_use_hermes_engine = " << static_cast(j_use_hermes_engine) + << ", js_engine_type = " << js_engine_type; + + std::shared_ptr param; #ifdef JS_V8 - if (vm_type == "v8") { + if (vm_type == hippy::VM::kJSEngineV8) { auto v8_param = std::make_shared(); v8_param->enable_v8_serialization = static_cast(j_enable_v8_serialization); v8_param->is_debug = static_cast(j_is_dev_module); @@ -473,7 +478,7 @@ jint CreateJsDriverWithEngine(JNIEnv* j_env, } #endif #ifdef JS_HERMES - if (vm_type == "hermes") { + if (vm_type == hippy::VM::kJSEngineHermes) { auto hermes_param = std::make_shared(); hermes_param->is_debug = static_cast(j_is_dev_module); hermes_param->group_id = static_cast(j_group_id); @@ -487,6 +492,8 @@ jint CreateJsDriverWithEngine(JNIEnv* j_env, return -1; } + FOOTSTONE_LOG(INFO) << "CreateJsDriverWithEngine: param created successfully, vm_type = " << param->vm_type; + #ifdef ENABLE_INSPECTOR if (param->is_debug) { auto devtools_data_source = devtools::DevtoolsDataSource::Find( diff --git a/framework/android/connector/driver/js/src/main/java/com/openhippy/connector/JsDriver.java b/framework/android/connector/driver/js/src/main/java/com/openhippy/connector/JsDriver.java index 6bac56e981d..1636c04bcca 100644 --- a/framework/android/connector/driver/js/src/main/java/com/openhippy/connector/JsDriver.java +++ b/framework/android/connector/driver/js/src/main/java/com/openhippy/connector/JsDriver.java @@ -101,7 +101,7 @@ public void initialize(byte[] globalConfig, boolean useLowMemoryMode, boolean enableV8Serialization, boolean isDevModule, NativeCallback callback, long groupId, int domManagerId, V8InitParams v8InitParams, int vfsId, int devtoolsId, boolean isReload, boolean useHermesEngine, String jsEngineType) { - mInstanceId = onCreate(globalConfig, useLowMemoryMode, enableV8Serialization, + mInstanceId = onCreateWithEngine(globalConfig, useLowMemoryMode, enableV8Serialization, isDevModule, callback, groupId, domManagerId, v8InitParams, vfsId, devtoolsId, isReload, useHermesEngine, jsEngineType); } @@ -158,7 +158,7 @@ private native int onCreate(byte[] globalConfig, boolean useLowMemoryMode, boolean enableV8Serialization, boolean isDevModule, NativeCallback callback, long groupId, int domManagerId, V8InitParams v8InitParams, int vfs_id, int devtoolsId, boolean isReload); - private native int onCreate(byte[] globalConfig, boolean useLowMemoryMode, + private native int onCreateWithEngine(byte[] globalConfig, boolean useLowMemoryMode, boolean enableV8Serialization, boolean isDevModule, NativeCallback callback, long groupId, int domManagerId, V8InitParams v8InitParams, int vfs_id, int devtoolsId, boolean isReload, boolean useHermesEngine, String jsEngineType); diff --git a/framework/examples/android-demo/src/main/java/com/openhippy/example/HippyEngineWrapper.kt b/framework/examples/android-demo/src/main/java/com/openhippy/example/HippyEngineWrapper.kt index fcace70eee7..501a316bdcb 100644 --- a/framework/examples/android-demo/src/main/java/com/openhippy/example/HippyEngineWrapper.kt +++ b/framework/examples/android-demo/src/main/java/com/openhippy/example/HippyEngineWrapper.kt @@ -34,26 +34,14 @@ import com.tencent.mtt.hippy.common.HippyMap import com.tencent.mtt.hippy.utils.LogUtils import com.tencent.mtt.hippy.utils.UIThreadUtils -class HippyEngineWrapper//TODO: Coming soon - ( - dm: PageConfiguration.DriverMode, - rm: PageConfiguration.RenderMode, - isDebug: Boolean, - useNodeSnapshot: Boolean, - debugServerHost: String, - context: Context -) { - this(dm, rm, isDebug, useNodeSnapshot, debugServerHost, context, PageConfiguration.JSEngineType.V8) -} - -constructor( +class HippyEngineWrapper ( dm: PageConfiguration.DriverMode, rm: PageConfiguration.RenderMode, isDebug: Boolean, useNodeSnapshot: Boolean, debugServerHost: String, context: Context, - jsEngineType: PageConfiguration.JSEngineType + jsEngineType: PageConfiguration.JSEngineType = PageConfiguration.JSEngineType.V8 ) { var hippyEngine: HippyEngine @@ -274,4 +262,4 @@ constructor( fun onReplaySnapshotViewCompleted(snapshotView: ViewGroup) fun onLoadModuleCompleted(statusCode: ModuleLoadStatus, msg: String?) } -} \ No newline at end of file +} diff --git a/framework/examples/android-demo/src/main/res/layout/activity_page_configuration.xml b/framework/examples/android-demo/src/main/res/layout/activity_page_configuration.xml index 7c046e75a35..995bc568bc6 100644 --- a/framework/examples/android-demo/src/main/res/layout/activity_page_configuration.xml +++ b/framework/examples/android-demo/src/main/res/layout/activity_page_configuration.xml @@ -291,57 +291,142 @@ app:layout_constraintBottom_toBottomOf="parent" /> - + + - - - - + app:layout_constraintTop_toTopOf="parent" /> + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file + From f2cddc3f9d4b8876f3686ef103e089aef7137181 Mon Sep 17 00:00:00 2001 From: wwwcg Date: Thu, 16 Oct 2025 20:54:12 +0800 Subject: [PATCH 03/11] feat(android): hermes support --- driver/js/CMakeLists.txt | 50 ++++++++++++++++++- .../connector/driver/js/gradle.properties | 4 +- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/driver/js/CMakeLists.txt b/driver/js/CMakeLists.txt index 77d3f61d045..ab3474d97e8 100644 --- a/driver/js/CMakeLists.txt +++ b/driver/js/CMakeLists.txt @@ -93,15 +93,61 @@ endif () if ("${JS_ENGINE}" STREQUAL "HERMES_ONLY" OR "${JS_ENGINE}" STREQUAL "HERMES_V8") if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Android") - set(HERMES_REMOTE_FILENAME "android.tgz") + set(HERMES_REMOTE_FILENAME "hermes-0.12.30302-rn0.76-stable-20251016-android.tgz") else () message(FATAL_ERROR "Unsupported system ${CMAKE_SYSTEM_NAME}") endif () InfraPackage_Add(Hermes REMOTE "global_packages/hermes/${HERMES_COMPONENT}/${HERMES_REMOTE_FILENAME}" LOCAL "${HERMES_COMPONENT}") - target_link_libraries(${PROJECT_NAME} PUBLIC hermes) + + # Set Hermes include directories + if(EXISTS "${hermes_SOURCE_DIR}/include") + target_include_directories(${PROJECT_NAME} PUBLIC "${hermes_SOURCE_DIR}/include") + message(STATUS "Added hermes include directory: ${hermes_SOURCE_DIR}/include") + else() + message(FATAL_ERROR "Hermes include directory not found: ${hermes_SOURCE_DIR}/include") + endif() + + # Set Hermes library directories and link libraries + if(ANDROID_ABI STREQUAL "arm64-v8a") + if(EXISTS "${hermes_SOURCE_DIR}/lib/arm64-v8a") + target_link_directories(${PROJECT_NAME} PUBLIC "${hermes_SOURCE_DIR}/lib/arm64-v8a") + message(STATUS "Added hermes library directory: ${hermes_SOURCE_DIR}/lib/arm64-v8a") + endif() + elseif(ANDROID_ABI STREQUAL "armeabi-v7a") + if(EXISTS "${hermes_SOURCE_DIR}/lib/armeabi-v7a") + target_link_directories(${PROJECT_NAME} PUBLIC "${hermes_SOURCE_DIR}/lib/armeabi-v7a") + message(STATUS "Added hermes library directory: ${hermes_SOURCE_DIR}/lib/armeabi-v7a") + endif() + elseif(ANDROID_ABI STREQUAL "x86") + if(EXISTS "${hermes_SOURCE_DIR}/lib/x86") + target_link_directories(${PROJECT_NAME} PUBLIC "${hermes_SOURCE_DIR}/lib/x86") + message(STATUS "Added hermes library directory: ${hermes_SOURCE_DIR}/lib/x86") + endif() + elseif(ANDROID_ABI STREQUAL "x86_64") + if(EXISTS "${hermes_SOURCE_DIR}/lib/x86_64") + target_link_directories(${PROJECT_NAME} PUBLIC "${hermes_SOURCE_DIR}/lib/x86_64") + message(STATUS "Added hermes library directory: ${hermes_SOURCE_DIR}/lib/x86_64") + endif() + endif() + + # Debug: Print the hermes_SOURCE_DIR value + message(STATUS "hermes_SOURCE_DIR: ${hermes_SOURCE_DIR}") + message(STATUS "hermes include path: ${hermes_SOURCE_DIR}/include") + + target_link_libraries(${PROJECT_NAME} PUBLIC hermes jsi) target_compile_definitions(${PROJECT_NAME} PUBLIC "JS_HERMES") + + # Enable RTTI and exceptions for Hermes-related source files + set_source_files_properties( + src/napi/hermes/hermes_ctx.cc + src/napi/hermes/hermes_ctx_value.cc + src/napi/hermes/hermes_try_catch.cc + src/vm/hermes/hermes_vm.cc + src/js_driver_utils.cc + PROPERTIES COMPILE_FLAGS "-frtti -fexceptions" + ) elseif ("${JS_ENGINE}" STREQUAL "JSH") target_compile_definitions(${PROJECT_NAME} PUBLIC "JS_JSH") endif () diff --git a/framework/android/connector/driver/js/gradle.properties b/framework/android/connector/driver/js/gradle.properties index 46a42534234..a5be0dff130 100644 --- a/framework/android/connector/driver/js/gradle.properties +++ b/framework/android/connector/driver/js/gradle.properties @@ -19,10 +19,10 @@ # # Indicates the javascript engine to use: # * V8 - Use V8 engine only -# * HERMES_ONLY - Use Hermes engine only +# * HERMES_ONLY - Use Hermes engine only # * HERMES_V8 - Support both Hermes and V8 engines (parallel build) # -CPP_JS_ENGINE=V8 +CPP_JS_ENGINE=HERMES_ONLY # # V8 Component From ec7e4d97d738bfb774038241daa3b6e4984cc3c1 Mon Sep 17 00:00:00 2001 From: wwwcg Date: Thu, 16 Oct 2025 22:04:41 +0800 Subject: [PATCH 04/11] feat(android): hermes support --- driver/js/CMakeLists.txt | 71 ++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/driver/js/CMakeLists.txt b/driver/js/CMakeLists.txt index ab3474d97e8..e6e739a2633 100644 --- a/driver/js/CMakeLists.txt +++ b/driver/js/CMakeLists.txt @@ -93,7 +93,7 @@ endif () if ("${JS_ENGINE}" STREQUAL "HERMES_ONLY" OR "${JS_ENGINE}" STREQUAL "HERMES_V8") if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Android") - set(HERMES_REMOTE_FILENAME "hermes-0.12.30302-rn0.76-stable-20251016-android.tgz") + set(HERMES_REMOTE_FILENAME "hermes-runtime-android-v0.12.30302.3.tar.gz") else () message(FATAL_ERROR "Unsupported system ${CMAKE_SYSTEM_NAME}") endif () @@ -101,42 +101,51 @@ if ("${JS_ENGINE}" STREQUAL "HERMES_ONLY" OR "${JS_ENGINE}" STREQUAL "HERMES_V8" REMOTE "global_packages/hermes/${HERMES_COMPONENT}/${HERMES_REMOTE_FILENAME}" LOCAL "${HERMES_COMPONENT}") + # Standard Hermes package structure + set(HERMES_INCLUDE_DIR "${hermes_SOURCE_DIR}/include") + set(HERMES_LIB_DIR "${hermes_SOURCE_DIR}/unstripped-debug/lib") + # Set Hermes include directories - if(EXISTS "${hermes_SOURCE_DIR}/include") - target_include_directories(${PROJECT_NAME} PUBLIC "${hermes_SOURCE_DIR}/include") - message(STATUS "Added hermes include directory: ${hermes_SOURCE_DIR}/include") + if(EXISTS "${HERMES_INCLUDE_DIR}/hermes") + target_include_directories(${PROJECT_NAME} PUBLIC "${HERMES_INCLUDE_DIR}") + message(STATUS "Added hermes include directory: ${HERMES_INCLUDE_DIR}") else() - message(FATAL_ERROR "Hermes include directory not found: ${hermes_SOURCE_DIR}/include") + message(FATAL_ERROR "Hermes include directory not found: ${HERMES_INCLUDE_DIR}") endif() - # Set Hermes library directories and link libraries - if(ANDROID_ABI STREQUAL "arm64-v8a") - if(EXISTS "${hermes_SOURCE_DIR}/lib/arm64-v8a") - target_link_directories(${PROJECT_NAME} PUBLIC "${hermes_SOURCE_DIR}/lib/arm64-v8a") - message(STATUS "Added hermes library directory: ${hermes_SOURCE_DIR}/lib/arm64-v8a") - endif() - elseif(ANDROID_ABI STREQUAL "armeabi-v7a") - if(EXISTS "${hermes_SOURCE_DIR}/lib/armeabi-v7a") - target_link_directories(${PROJECT_NAME} PUBLIC "${hermes_SOURCE_DIR}/lib/armeabi-v7a") - message(STATUS "Added hermes library directory: ${hermes_SOURCE_DIR}/lib/armeabi-v7a") - endif() - elseif(ANDROID_ABI STREQUAL "x86") - if(EXISTS "${hermes_SOURCE_DIR}/lib/x86") - target_link_directories(${PROJECT_NAME} PUBLIC "${hermes_SOURCE_DIR}/lib/x86") - message(STATUS "Added hermes library directory: ${hermes_SOURCE_DIR}/lib/x86") - endif() - elseif(ANDROID_ABI STREQUAL "x86_64") - if(EXISTS "${hermes_SOURCE_DIR}/lib/x86_64") - target_link_directories(${PROJECT_NAME} PUBLIC "${hermes_SOURCE_DIR}/lib/x86_64") - message(STATUS "Added hermes library directory: ${hermes_SOURCE_DIR}/lib/x86_64") - endif() + # Set Hermes library directory for current ABI + set(HERMES_ABI_LIB_DIR "${HERMES_LIB_DIR}/${ANDROID_ABI}") + + # Debug: Print the hermes configuration + message(STATUS "Hermes configuration:") + message(STATUS " Source dir: ${hermes_SOURCE_DIR}") + message(STATUS " Include dir: ${HERMES_INCLUDE_DIR}") + message(STATUS " Library dir: ${HERMES_ABI_LIB_DIR}") + + # Check if ABI library directory exists + if(NOT EXISTS "${HERMES_ABI_LIB_DIR}") + message(FATAL_ERROR "Hermes library directory not found: ${HERMES_ABI_LIB_DIR}") endif() - # Debug: Print the hermes_SOURCE_DIR value - message(STATUS "hermes_SOURCE_DIR: ${hermes_SOURCE_DIR}") - message(STATUS "hermes include path: ${hermes_SOURCE_DIR}/include") - - target_link_libraries(${PROJECT_NAME} PUBLIC hermes jsi) + # Link all Hermes libraries (using full paths for static linking effect) + set(HERMES_LIBRARIES + "${HERMES_ABI_LIB_DIR}/libhermes.so" + "${HERMES_ABI_LIB_DIR}/libjsi.so" + "${HERMES_ABI_LIB_DIR}/libfbjni.so" + "${HERMES_ABI_LIB_DIR}/libc++_shared.so" + ) + + # Verify all libraries exist before linking + foreach(LIB ${HERMES_LIBRARIES}) + if(NOT EXISTS "${LIB}") + message(FATAL_ERROR "Hermes library not found: ${LIB}") + endif() + endforeach() + + # Link all libraries at once + target_link_libraries(${PROJECT_NAME} PUBLIC ${HERMES_LIBRARIES}) + message(STATUS "Linked Hermes libraries: libhermes.so, libjsi.so, libfbjni.so, libc++_shared.so") + target_compile_definitions(${PROJECT_NAME} PUBLIC "JS_HERMES") # Enable RTTI and exceptions for Hermes-related source files From 40e02d03b62e2caa79bb236ddeb1312476a9455c Mon Sep 17 00:00:00 2001 From: wwwcg Date: Thu, 16 Oct 2025 22:55:10 +0800 Subject: [PATCH 05/11] feat(android): hermes engine support --- devtools/devtools-integration/native/CMakeLists.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/devtools/devtools-integration/native/CMakeLists.txt b/devtools/devtools-integration/native/CMakeLists.txt index f65d78baffe..f110d5785ee 100644 --- a/devtools/devtools-integration/native/CMakeLists.txt +++ b/devtools/devtools-integration/native/CMakeLists.txt @@ -35,7 +35,7 @@ target_include_directories(${PROJECT_NAME} PUBLIC include) target_compile_options(${PROJECT_NAME} PRIVATE ${COMPILE_OPTIONS}) # region vm -if ("${JS_ENGINE}" STREQUAL "V8") +if ("${JS_ENGINE}" STREQUAL "V8" OR "${JS_ENGINE}" STREQUAL "HERMES_V8") if (NOT TARGET v8) message(FATAL_ERROR "The V8 target not found, peer dependency not satisfied") endif() @@ -45,9 +45,11 @@ if ("${JS_ENGINE}" STREQUAL "V8") if (V8_WITHOUT_INSPECTOR) target_compile_definitions(${PROJECT_NAME} PRIVATE "V8_WITHOUT_INSPECTOR") endif () -elseif ("${JS_ENGINE}" STREQUAL "JSC") +endif () + +if ("${JS_ENGINE}" STREQUAL "JSC") target_compile_definitions(${PROJECT_NAME} PRIVATE "JS_JSC") -elseif ("${JS_ENGINE}" STREQUAL "HERMES") +elseif ("${JS_ENGINE}" STREQUAL "HERMES_ONLY" OR "${JS_ENGINE}" STREQUAL "HERMES_V8") target_compile_definitions(${PROJECT_NAME} PRIVATE "JS_HERMES") endif () # endregion From ae3fd9ef2ed08593655c74a01ba834004da1f3cd Mon Sep 17 00:00:00 2001 From: wwwcg Date: Fri, 17 Oct 2025 22:16:04 +0800 Subject: [PATCH 06/11] feat(android): hermes engine support --- driver/js/CMakeLists.txt | 23 +- driver/js/include/driver/vm/js_vm.h | 1 + driver/js/src/js_driver_utils.cc | 422 ++++++++++-------- driver/js/src/scope.cc | 4 +- driver/js/src/vm/js_vm.cc | 11 + .../connector/dom/src/main/cpp/src/dom_jni.cc | 4 + 6 files changed, 258 insertions(+), 207 deletions(-) diff --git a/driver/js/CMakeLists.txt b/driver/js/CMakeLists.txt index e6e739a2633..041593b7437 100644 --- a/driver/js/CMakeLists.txt +++ b/driver/js/CMakeLists.txt @@ -104,7 +104,7 @@ if ("${JS_ENGINE}" STREQUAL "HERMES_ONLY" OR "${JS_ENGINE}" STREQUAL "HERMES_V8" # Standard Hermes package structure set(HERMES_INCLUDE_DIR "${hermes_SOURCE_DIR}/include") set(HERMES_LIB_DIR "${hermes_SOURCE_DIR}/unstripped-debug/lib") - + # Set Hermes include directories if(EXISTS "${HERMES_INCLUDE_DIR}/hermes") target_include_directories(${PROJECT_NAME} PUBLIC "${HERMES_INCLUDE_DIR}") @@ -112,21 +112,21 @@ if ("${JS_ENGINE}" STREQUAL "HERMES_ONLY" OR "${JS_ENGINE}" STREQUAL "HERMES_V8" else() message(FATAL_ERROR "Hermes include directory not found: ${HERMES_INCLUDE_DIR}") endif() - + # Set Hermes library directory for current ABI set(HERMES_ABI_LIB_DIR "${HERMES_LIB_DIR}/${ANDROID_ABI}") - + # Debug: Print the hermes configuration message(STATUS "Hermes configuration:") message(STATUS " Source dir: ${hermes_SOURCE_DIR}") message(STATUS " Include dir: ${HERMES_INCLUDE_DIR}") message(STATUS " Library dir: ${HERMES_ABI_LIB_DIR}") - + # Check if ABI library directory exists if(NOT EXISTS "${HERMES_ABI_LIB_DIR}") message(FATAL_ERROR "Hermes library directory not found: ${HERMES_ABI_LIB_DIR}") endif() - + # Link all Hermes libraries (using full paths for static linking effect) set(HERMES_LIBRARIES "${HERMES_ABI_LIB_DIR}/libhermes.so" @@ -134,27 +134,28 @@ if ("${JS_ENGINE}" STREQUAL "HERMES_ONLY" OR "${JS_ENGINE}" STREQUAL "HERMES_V8" "${HERMES_ABI_LIB_DIR}/libfbjni.so" "${HERMES_ABI_LIB_DIR}/libc++_shared.so" ) - + # Verify all libraries exist before linking foreach(LIB ${HERMES_LIBRARIES}) if(NOT EXISTS "${LIB}") message(FATAL_ERROR "Hermes library not found: ${LIB}") endif() endforeach() - + # Link all libraries at once target_link_libraries(${PROJECT_NAME} PUBLIC ${HERMES_LIBRARIES}) message(STATUS "Linked Hermes libraries: libhermes.so, libjsi.so, libfbjni.so, libc++_shared.so") - + target_compile_definitions(${PROJECT_NAME} PUBLIC "JS_HERMES") - - # Enable RTTI and exceptions for Hermes-related source files + + # Enable RTTI and exceptions for specific source files to ensure ABI compatibility + # This is necessary for HERMES_V8 mode to work correctly set_source_files_properties( + # Hermes-specific files src/napi/hermes/hermes_ctx.cc src/napi/hermes/hermes_ctx_value.cc src/napi/hermes/hermes_try_catch.cc src/vm/hermes/hermes_vm.cc - src/js_driver_utils.cc PROPERTIES COMPILE_FLAGS "-frtti -fexceptions" ) elseif ("${JS_ENGINE}" STREQUAL "JSH") diff --git a/driver/js/include/driver/vm/js_vm.h b/driver/js/include/driver/vm/js_vm.h index 3b757a6d1ec..c9a6fb64bd3 100644 --- a/driver/js/include/driver/vm/js_vm.h +++ b/driver/js/include/driver/vm/js_vm.h @@ -43,6 +43,7 @@ class VM { static inline const std::string kJSEngineV8 = "V8"; static inline const std::string kJSEngineJSC = "JSC"; static inline const std::string kJSEngineHermes = "Hermes"; + static inline const std::string kJSEngineJSH = "JSH"; using string_view = footstone::string_view; using Ctx = hippy::napi::Ctx; diff --git a/driver/js/src/js_driver_utils.cc b/driver/js/src/js_driver_utils.cc index f95d054755a..b1a06ba7a00 100644 --- a/driver/js/src/js_driver_utils.cc +++ b/driver/js/src/js_driver_utils.cc @@ -116,66 +116,75 @@ void AsyncInitializeEngine(const std::shared_ptr& engine, const std::shared_ptr& task_runner, const std::shared_ptr& param) { auto engine_initialized_callback = [](const std::shared_ptr& engine) { + auto vm = engine->GetVM(); + auto vm_type = vm->GetVMType(); + + if (vm_type == hippy::VM::kJSEngineV8) { #ifdef JS_V8 - auto v8_vm = std::static_pointer_cast(engine->GetVM()); - auto wrapper = std::make_unique([](CallbackInfo& info, void* data) { - auto scope_wrapper = reinterpret_cast(std::any_cast(info.GetSlot())); - auto scope = scope_wrapper->scope.lock(); - FOOTSTONE_CHECK(scope); - auto exception = info[0]; - V8VM::HandleException(scope->GetContext(), "uncaughtException", exception); - auto engine = scope->GetEngine().lock(); - FOOTSTONE_CHECK(engine); - auto callback = engine->GetVM()->GetUncaughtExceptionCallback(); - auto context = scope->GetContext(); - string_view description; - auto flag = context->GetValueString(info[1], &description); - FOOTSTONE_CHECK(flag); - string_view stack; - flag = context->GetValueString(info[2], &stack); - FOOTSTONE_CHECK(flag); - callback(scope->GetBridge(), description, stack); - }, nullptr); - v8_vm->AddUncaughtExceptionMessageListener(wrapper); - v8_vm->SaveUncaughtExceptionCallback(std::move(wrapper)); + auto v8_vm = std::static_pointer_cast(vm); + auto wrapper = std::make_unique([](CallbackInfo& info, void* data) { + auto scope_wrapper = reinterpret_cast(std::any_cast(info.GetSlot())); + auto scope = scope_wrapper->scope.lock(); + FOOTSTONE_CHECK(scope); + auto exception = info[0]; + V8VM::HandleException(scope->GetContext(), "uncaughtException", exception); + auto engine = scope->GetEngine().lock(); + FOOTSTONE_CHECK(engine); + auto callback = engine->GetVM()->GetUncaughtExceptionCallback(); + auto context = scope->GetContext(); + string_view description; + auto flag = context->GetValueString(info[1], &description); + FOOTSTONE_CHECK(flag); + string_view stack; + flag = context->GetValueString(info[2], &stack); + FOOTSTONE_CHECK(flag); + callback(scope->GetBridge(), description, stack); + }, nullptr); + v8_vm->AddUncaughtExceptionMessageListener(wrapper); + v8_vm->SaveUncaughtExceptionCallback(std::move(wrapper)); #if defined(ENABLE_INSPECTOR) && !defined(V8_WITHOUT_INSPECTOR) - if (v8_vm->IsDebug()) { - if (!v8_vm->GetInspectorClient()) { - v8_vm->SetInspectorClient(std::make_shared()); + if (v8_vm->IsDebug()) { + if (!v8_vm->GetInspectorClient()) { + v8_vm->SetInspectorClient(std::make_shared()); + } + v8_vm->GetInspectorClient()->SetJsRunner(engine->GetJsTaskRunner()); } - v8_vm->GetInspectorClient()->SetJsRunner(engine->GetJsTaskRunner()); - } -#endif -#elif JS_JSH - auto jsh_vm = std::static_pointer_cast(engine->GetVM()); - auto wrapper = std::make_unique([](CallbackInfo& info, void* data) { - auto scope_wrapper = reinterpret_cast(std::any_cast(info.GetSlot())); - auto scope = scope_wrapper->scope.lock(); - FOOTSTONE_CHECK(scope); - auto exception = info[0]; - JSHVM::HandleException(scope->GetContext(), "uncaughtException", exception); - auto engine = scope->GetEngine().lock(); - FOOTSTONE_CHECK(engine); - auto callback = engine->GetVM()->GetUncaughtExceptionCallback(); - auto context = scope->GetContext(); - string_view description; - auto flag = context->GetValueString(info[1], &description); - FOOTSTONE_CHECK(flag); - string_view stack; - flag = context->GetValueString(info[2], &stack); - FOOTSTONE_CHECK(flag); - callback(scope->GetBridge(), description, stack); - }, nullptr); - jsh_vm->AddUncaughtExceptionMessageListener(wrapper); - jsh_vm->SaveUncaughtExceptionCallback(std::move(wrapper)); +#endif /* ENABLE_INSPECTOR && !defined(V8_WITHOUT_INSPECTOR) */ +#endif /* JS_V8 */ + } else if (vm_type == hippy::VM::kJSEngineJSH) { +#ifdef JS_JSH + auto jsh_vm = std::static_pointer_cast(vm); + auto wrapper = std::make_unique([](CallbackInfo& info, void* data) { + auto scope_wrapper = reinterpret_cast(std::any_cast(info.GetSlot())); + auto scope = scope_wrapper->scope.lock(); + FOOTSTONE_CHECK(scope); + auto exception = info[0]; + JSHVM::HandleException(scope->GetContext(), "uncaughtException", exception); + auto engine = scope->GetEngine().lock(); + FOOTSTONE_CHECK(engine); + auto callback = engine->GetVM()->GetUncaughtExceptionCallback(); + auto context = scope->GetContext(); + string_view description; + auto flag = context->GetValueString(info[1], &description); + FOOTSTONE_CHECK(flag); + string_view stack; + flag = context->GetValueString(info[2], &stack); + FOOTSTONE_CHECK(flag); + callback(scope->GetBridge(), description, stack); + }, nullptr); + jsh_vm->AddUncaughtExceptionMessageListener(wrapper); + jsh_vm->SaveUncaughtExceptionCallback(std::move(wrapper)); #if defined(ENABLE_INSPECTOR) && !defined(JSH_WITHOUT_INSPECTOR) - if (jsh_vm->IsDebug()) { - if (!jsh_vm->GetInspectorClient()) { - jsh_vm->SetInspectorClient(std::make_shared()); + if (jsh_vm->IsDebug()) { + if (!jsh_vm->GetInspectorClient()) { + jsh_vm->SetInspectorClient(std::make_shared()); + } } +#endif /* ENABLE_INSPECTOR && !defined(JSH_WITHOUT_INSPECTOR) */ +#endif /* JS_JSH */ + } else if (vm_type == hippy::VM::kJSEngineHermes) { + // do nothing } -#endif -#endif }; engine->AsyncInitialize(task_runner, param, engine_initialized_callback); } @@ -247,65 +256,75 @@ void JsDriverUtils::InitDevTools(const std::shared_ptr& scope, const std::shared_ptr& source) { if (vm->IsDebug()) { scope->SetDevtoolsDataSource(source); + + if (vm->GetVMType() == hippy::VM::kJSEngineV8) { #if defined(JS_V8) && !defined(V8_WITHOUT_INSPECTOR) - auto v8_vm = std::static_pointer_cast(vm); - std::weak_ptr weak_v8_vm = v8_vm; - std::weak_ptr weak_scope = scope; - scope->GetDevtoolsDataSource()->SetVmRequestHandler([weak_v8_vm, weak_scope](const std::string& data) { - auto v8_vm = weak_v8_vm.lock(); - if (!v8_vm) { - FOOTSTONE_DLOG(FATAL) << "RunApp send_v8_func_ vm invalid or not debugger"; - return; - } - auto scope = weak_scope.lock(); - if (!scope) { - return; - } - auto inspector_client = v8_vm->GetInspectorClient(); - if (inspector_client) { - auto u16str = StringViewUtils::ConvertEncoding( - string_view(data), string_view::Encoding::Utf16); - inspector_client->SendMessageToV8(scope->GetInspectorContext(), std::move(u16str)); - } - }); -#elif defined(JS_HERMES) - auto hermes_vm = std::static_pointer_cast(vm); - std::weak_ptr weak_hermes_vm = hermes_vm; - std::weak_ptr weak_scope = scope; - scope->GetDevtoolsDataSource()->SetVmRequestHandler([weak_hermes_vm, weak_scope](const std::string& data) { - auto hermes_vm = weak_hermes_vm.lock(); - if (!hermes_vm) { - return; - } - auto scope = weak_scope.lock(); - if (!scope) { - return; - } - // FOOTSTONE_DLOG(INFO) << "From Debugger:" << data.c_str(); - auto hermesCtx = std::static_pointer_cast(scope->GetContext()); - hermesCtx->GetCDPAgent()->handleCommand(data); - }); -#endif + auto v8_vm = std::static_pointer_cast(vm); + std::weak_ptr weak_v8_vm = v8_vm; + std::weak_ptr weak_scope = scope; + scope->GetDevtoolsDataSource()->SetVmRequestHandler([weak_v8_vm, weak_scope](const std::string& data) { + auto v8_vm = weak_v8_vm.lock(); + if (!v8_vm) { + FOOTSTONE_DLOG(FATAL) << "RunApp send_v8_func_ vm invalid or not debugger"; + return; + } + auto scope = weak_scope.lock(); + if (!scope) { + return; + } + auto inspector_client = v8_vm->GetInspectorClient(); + if (inspector_client) { + auto u16str = StringViewUtils::ConvertEncoding( + string_view(data), string_view::Encoding::Utf16); + inspector_client->SendMessageToV8(scope->GetInspectorContext(), std::move(u16str)); + } + }); +#endif /* ENABLE_INSPECTOR && !defined(V8_WITHOUT_INSPECTOR) */ + } + +#ifdef JS_HERMES + else if (vm->GetVMType() == hippy::VM::kJSEngineHermes) { + auto hermes_vm = std::static_pointer_cast(vm); + std::weak_ptr weak_hermes_vm = hermes_vm; + std::weak_ptr weak_scope = scope; + scope->GetDevtoolsDataSource()->SetVmRequestHandler([weak_hermes_vm, weak_scope](const std::string& data) { + auto hermes_vm = weak_hermes_vm.lock(); + if (!hermes_vm) { + return; + } + auto scope = weak_scope.lock(); + if (!scope) { + return; + } + // FOOTSTONE_DLOG(INFO) << "From Debugger:" << data.c_str(); + auto hermesCtx = std::static_pointer_cast(scope->GetContext()); + hermesCtx->GetCDPAgent()->handleCommand(data); + }); + } +#endif /* JS_HERMES */ + #if defined(JS_JSH) && !defined(JSH_WITHOUT_INSPECTOR) - auto jsh_vm = std::static_pointer_cast(vm); - std::weak_ptr weak_jsh_vm = jsh_vm; - std::weak_ptr weak_scope = scope; - scope->GetDevtoolsDataSource()->SetVmRequestHandler([weak_jsh_vm, weak_scope](const std::string& data) { - auto jsh_vm = weak_jsh_vm.lock(); - if (!jsh_vm) { - FOOTSTONE_DLOG(FATAL) << "RunApp send_jsh_func_ vm invalid or not debugger"; - return; - } - auto scope = weak_scope.lock(); - if (!scope) { - return; - } - auto inspector_client = jsh_vm->GetInspectorClient(); - if (inspector_client) { - inspector_client->SendMessageToJSH(std::move(data)); - } - }); -#endif + else if (vm->GetVMType() == hippy::VM::kJSEngineJSH) { + auto jsh_vm = std::static_pointer_cast(vm); + std::weak_ptr weak_jsh_vm = jsh_vm; + std::weak_ptr weak_scope = scope; + scope->GetDevtoolsDataSource()->SetVmRequestHandler([weak_jsh_vm, weak_scope](const std::string& data) { + auto jsh_vm = weak_jsh_vm.lock(); + if (!jsh_vm) { + FOOTSTONE_DLOG(FATAL) << "RunApp send_jsh_func_ vm invalid or not debugger"; + return; + } + auto scope = weak_scope.lock(); + if (!scope) { + return; + } + auto inspector_client = jsh_vm->GetInspectorClient(); + if (inspector_client) { + inspector_client->SendMessageToJSH(std::move(data)); + } + }); + } +#endif /* ENABLE_INSPECTOR && !defined(JSH_WITHOUT_INSPECTOR) */ } } #endif /* ENABLE_INSPECTOR */ @@ -461,14 +480,8 @@ bool JsDriverUtils::RunScript(const std::shared_ptr& scope, code_cache_content = read_file_future.get(); } -#ifdef JS_HERMES FOOTSTONE_DLOG(INFO) << "uri = " << uri << "read_script_flag = " << read_script_flag; -#else - FOOTSTONE_DLOG(INFO) << "uri = " << uri - << ", read_script_flag = " << read_script_flag - << ", script content = " << script_content; -#endif if (!read_script_flag || StringViewUtils::IsEmpty(script_content)) { FOOTSTONE_LOG(WARNING) << "read_script_flag = " << read_script_flag @@ -483,7 +496,7 @@ bool JsDriverUtils::RunScript(const std::shared_ptr& scope, // perfromance start time auto entry = scope->GetPerformance()->PerformanceNavigation(kPerfNavigationHippyInit); entry->BundleInfoOfUrl(uri).execute_source_start_ = footstone::TimePoint::SystemNow(); - + #if (defined JS_V8) || (defined JS_JSH) #if (defined JS_V8) auto ret = std::static_pointer_cast(scope->GetContext())->RunScript( @@ -523,17 +536,6 @@ bool JsDriverUtils::RunScript(const std::shared_ptr& scope, worker_task_runner->PostTask(std::move(func)); } } -#elif defined(JS_HERMES) - std::shared_ptr ret = nullptr; - try { - ret = scope->GetContext()->RunScript(script_content, file_name); - } catch (facebook::jsi::JSIException& err) { - auto engine = scope->GetEngine().lock(); - FOOTSTONE_CHECK(engine); - auto callback = engine->GetVM()->GetUncaughtExceptionCallback(); - auto context = scope->GetContext(); - callback(scope->GetBridge(), "Hermes Exception", err.what()); - } #else auto ret = scope->GetContext()->RunScript(script_content, file_name); #endif @@ -659,37 +661,52 @@ void JsDriverUtils::CallJs(const string_view& action, std::shared_ptr action_value = context->CreateString(action); std::shared_ptr params; auto vm = engine->GetVM(); + + if (vm->GetVMType() == hippy::VM::kJSEngineV8) { #ifdef JS_V8 - auto v8_vm = std::static_pointer_cast(vm); - if (v8_vm->IsEnableV8Serialization()) { - auto result = v8_vm->Deserializer(scope->GetContext(), buffer_data_); - if (result.flag) { - params = result.result; - } else { - auto msg = u"deserializer error"; - if (!StringViewUtils::IsEmpty(result.message)) { - msg = StringViewUtils::ConvertEncoding(result.message, - string_view::Encoding::Utf16).utf16_value().c_str(); + auto v8_vm = std::static_pointer_cast(vm); + if (v8_vm->IsEnableV8Serialization()) { + auto result = v8_vm->Deserializer(scope->GetContext(), buffer_data_); + if (result.flag) { + params = result.result; + } else { + auto msg = u"deserializer error"; + if (!StringViewUtils::IsEmpty(result.message)) { + msg = StringViewUtils::ConvertEncoding(result.message, + string_view::Encoding::Utf16).utf16_value().c_str(); + } + cb(CALL_FUNCTION_CB_STATE::DESERIALIZER_FAILED, msg); + return; } - cb(CALL_FUNCTION_CB_STATE::DESERIALIZER_FAILED, msg); - return; - } - } else { + } else { #endif - -#ifdef __OHOS__ - string_view::u8string str(reinterpret_cast(&buffer_data_[0]), - buffer_data_.length()); -#else - std::u16string str(reinterpret_cast(&buffer_data_[0]), - buffer_data_.length() / sizeof(char16_t)); + // 使用通用解析逻辑 + #ifdef __OHOS__ + string_view::u8string str(reinterpret_cast(&buffer_data_[0]), + buffer_data_.length()); + #else + std::u16string str(reinterpret_cast(&buffer_data_[0]), + buffer_data_.length() / sizeof(char16_t)); + #endif + string_view buf_str(std::move(str)); + FOOTSTONE_DLOG(INFO) << "action = " << action << ", buf_str = " << buf_str; + params = vm->ParseJson(context, buf_str); +#ifdef JS_V8 + } #endif + } else { + // 其他引擎使用通用解析逻辑 + #ifdef __OHOS__ + string_view::u8string str(reinterpret_cast(&buffer_data_[0]), + buffer_data_.length()); + #else + std::u16string str(reinterpret_cast(&buffer_data_[0]), + buffer_data_.length() / sizeof(char16_t)); + #endif string_view buf_str(std::move(str)); FOOTSTONE_DLOG(INFO) << "action = " << action << ", buf_str = " << buf_str; params = vm->ParseJson(context, buf_str); -#ifdef JS_V8 } -#endif if (!params) { params = context->CreateNull(); } @@ -753,39 +770,51 @@ void JsDriverUtils::CallNative(hippy::napi::CallbackInfo& info, const std::funct std::string buffer_data; if (info[3] && context->IsObject(info[3])) { -#ifdef JS_V8 - auto engine = scope->GetEngine().lock(); - FOOTSTONE_DCHECK(engine); - if (!engine) { - return; - } - auto vm = engine->GetVM(); - auto v8_vm = std::static_pointer_cast(vm); - if (v8_vm->IsEnableV8Serialization()) { - auto v8_ctx = std::static_pointer_cast(context); - buffer_data = v8_ctx->GetSerializationBuffer(info[3], v8_vm->GetBuffer()); -#elif JS_JSH auto engine = scope->GetEngine().lock(); FOOTSTONE_DCHECK(engine); if (!engine) { return; } auto vm = engine->GetVM(); - auto jsh_vm = std::static_pointer_cast(vm); - if (jsh_vm->enable_v8_serialization_) { + + if (vm->GetVMType() == hippy::VM::kJSEngineV8) { +#ifdef JS_V8 + auto v8_vm = std::static_pointer_cast(vm); + if (v8_vm->IsEnableV8Serialization()) { + auto v8_ctx = std::static_pointer_cast(context); + buffer_data = v8_ctx->GetSerializationBuffer(info[3], v8_vm->GetBuffer()); + } else { + string_view json; + auto flag = context->GetValueJson(info[3], &json); + FOOTSTONE_DCHECK(flag); + FOOTSTONE_DLOG(INFO) << "CallJava json = " << json; + buffer_data = StringViewUtils::ToStdString( + StringViewUtils::ConvertEncoding(json, string_view::Encoding::Utf8).utf8_value()); + } +#endif + } else if (vm->GetVMType() == hippy::VM::kJSEngineJSH) { +#ifdef JS_JSH + auto jsh_vm = std::static_pointer_cast(vm); + if (jsh_vm->enable_v8_serialization_) { + // JSH序列化逻辑 + } else { + string_view json; + auto flag = context->GetValueJson(info[3], &json); + FOOTSTONE_DCHECK(flag); + FOOTSTONE_DLOG(INFO) << "CallJava json = " << json; + buffer_data = StringViewUtils::ToStdString( + StringViewUtils::ConvertEncoding(json, string_view::Encoding::Utf8).utf8_value()); + } #endif } else { + // 其他引擎使用通用JSON序列化 string_view json; auto flag = context->GetValueJson(info[3], &json); FOOTSTONE_DCHECK(flag); FOOTSTONE_DLOG(INFO) << "CallJava json = " << json; buffer_data = StringViewUtils::ToStdString( StringViewUtils::ConvertEncoding(json, string_view::Encoding::Utf8).utf8_value()); -#ifdef JS_V8 - } -#elif JS_JSH } -#endif } int32_t transfer_type = 0; @@ -803,36 +832,41 @@ void JsDriverUtils::LoadInstance(const std::shared_ptr& scope, byte_strin if (!scope) { return; } -#if defined(JS_V8) || defined(JS_JSH) - Deserializer deserializer( - reinterpret_cast(buffer_data_.c_str()), - buffer_data_.length()); - HippyValue value; - deserializer.ReadHeader(); - auto ret = deserializer.ReadValue(value); - if (ret) { - scope->LoadInstance(std::make_shared(std::move(value))); - } else { - scope->GetContext()->ThrowException("LoadInstance param error"); - } - #elif defined(JS_HERMES) - std::u16string str(reinterpret_cast(&buffer_data_[0]), buffer_data_.length() / sizeof(char16_t)); - string_view buf_str(std::move(str)); auto engine = scope->GetEngine().lock(); if (!engine) { return; } auto vm = engine->GetVM(); - auto hermes_vm = std::static_pointer_cast(vm); - auto context = scope->GetContext(); - HippyValue hippy_value; - auto ret = hermes_vm->ParseHippyValue(context, buf_str, hippy_value); - if (ret) { - scope->LoadInstance(std::make_shared(std::move(hippy_value))); - } else { - scope->GetContext()->ThrowException("LoadInstance param error"); - } + + if (vm->GetVMType() == hippy::VM::kJSEngineV8 || vm->GetVMType() == hippy::VM::kJSEngineJSH) { +#if defined(JS_V8) || defined(JS_JSH) + Deserializer deserializer( + reinterpret_cast(buffer_data_.c_str()), + buffer_data_.length()); + HippyValue value; + deserializer.ReadHeader(); + auto ret = deserializer.ReadValue(value); + if (ret) { + scope->LoadInstance(std::make_shared(std::move(value))); + } else { + scope->GetContext()->ThrowException("LoadInstance param error"); + } #endif + } else if (vm->GetVMType() == hippy::VM::kJSEngineHermes) { +#ifdef JS_HERMES + std::u16string str(reinterpret_cast(&buffer_data_[0]), buffer_data_.length() / sizeof(char16_t)); + string_view buf_str(std::move(str)); + auto hermes_vm = std::static_pointer_cast(vm); + auto context = scope->GetContext(); + HippyValue hippy_value; + auto ret = hermes_vm->ParseHippyValue(context, buf_str, hippy_value); + if (ret) { + scope->LoadInstance(std::make_shared(std::move(hippy_value))); + } else { + scope->GetContext()->ThrowException("LoadInstance param error"); + } +#endif + } }; runner->PostTask(std::move(callback)); } diff --git a/driver/js/src/scope.cc b/driver/js/src/scope.cc index 498b3b3e2b4..44b7d5e7fe6 100644 --- a/driver/js/src/scope.cc +++ b/driver/js/src/scope.cc @@ -515,7 +515,7 @@ void Scope::RunJS(const string_view& data, // perfromance start time auto entry = self->GetPerformance()->PerformanceNavigation(kPerfNavigationHippyInit); entry->BundleInfoOfUrl(uri).execute_source_start_ = footstone::TimePoint::SystemNow(); - + auto context = std::static_pointer_cast(weak_context.lock()); if (context) { context->RunScript(data, name, false, nullptr, is_copy); @@ -526,7 +526,7 @@ void Scope::RunJS(const string_view& data, // perfromance start time auto entry = self->GetPerformance()->PerformanceNavigation(kPerfNavigationHippyInit); entry->BundleInfoOfUrl(uri).execute_source_start_ = footstone::TimePoint::SystemNow(); - + auto context = std::static_pointer_cast(weak_context.lock()); if (context) { context->RunScript(data, name, false, nullptr, is_copy); diff --git a/driver/js/src/vm/js_vm.cc b/driver/js/src/vm/js_vm.cc index c81384fc5ee..23075de97b7 100644 --- a/driver/js/src/vm/js_vm.cc +++ b/driver/js/src/vm/js_vm.cc @@ -90,26 +90,37 @@ void VM::HandleException(const std::shared_ptr& ctx, const string_view& eve std::shared_ptr VM::CreateVM(const std::shared_ptr& param) { std::shared_ptr vm = nullptr; + FOOTSTONE_DLOG(INFO) << "VM::CreateVM called with vm_type: " << (param ? param->vm_type : "null"); + if (!param || param->vm_type.empty()) { // Using jsc on iOS and v8 on Android by default. + FOOTSTONE_DLOG(INFO) << "Using default engine"; #ifdef JS_JSC + param->vm_type = kJSEngineJSC; vm = JSCVM::CreateVM(param); #elif defined(JS_V8) + param->vm_type = kJSEngineV8; vm = V8VM::CreateVM(param); #elif defined (JS_JSH) + param->vm_type = kJSEngineJSH; vm = JSHVM::CreateVM(param); #endif /* JS_JSC/JS_V8 */ } else if (param->vm_type == kJSEngineJSC) { + FOOTSTONE_DLOG(INFO) << "Creating JSC engine"; #ifdef JS_JSC vm = JSCVM::CreateVM(param); #endif /* JS_JSC */ } else if (param->vm_type == kJSEngineHermes) { + FOOTSTONE_DLOG(INFO) << "Creating Hermes engine"; #ifdef JS_HERMES vm = HermesVM::CreateVM(param); + FOOTSTONE_DLOG(INFO) << "HermesVM::CreateVM returned: " << (vm ? "success" : "null"); #endif /* JS_HERMES */ } else if (param->vm_type == kJSEngineV8) { + FOOTSTONE_DLOG(INFO) << "Creating V8 engine"; #ifdef JS_V8 vm = V8VM::CreateVM(param); + FOOTSTONE_DLOG(INFO) << "V8VM::CreateVM returned: " << (vm ? "success" : "null"); #endif /* JS_V8 */ } diff --git a/framework/android/connector/dom/src/main/cpp/src/dom_jni.cc b/framework/android/connector/dom/src/main/cpp/src/dom_jni.cc index 750eeeaf0f2..024b5a8d1e5 100644 --- a/framework/android/connector/dom/src/main/cpp/src/dom_jni.cc +++ b/framework/android/connector/dom/src/main/cpp/src/dom_jni.cc @@ -173,17 +173,21 @@ jint CreateDomManager(JNIEnv* j_env, jobject j_obj, jint j_group_id, jint j_shar auto count = worker->FetchAndAddReuseCount(); FOOTSTONE_DLOG(INFO) << "CreateDomManager worker reuse count " << count << ", dom manager id " << dom_id; } else { + FOOTSTONE_DLOG(INFO) << "CreateDomManager: Creating new DOM worker"; auto worker = std::make_shared(kDomWorkerName, false); auto callback = std::make_shared(j_env, j_obj); worker->BeforeStart([callback]() { + FOOTSTONE_DLOG(INFO) << "DOM worker starting, setting thread priority"; if (callback->GetObj()) SetThreadPriority(callback->GetObj()); }); worker->Start(); + FOOTSTONE_DLOG(INFO) << "DOM worker started successfully"; auto runner = std::make_shared(kDomRunnerName); runner->SetWorker(worker); worker->Bind({runner}); dom_manager->SetTaskRunner(runner); dom_manager->SetWorker(worker); + FOOTSTONE_DLOG(INFO) << "DOM manager setup completed"; } return footstone::checked_numeric_cast(dom_id); } From 6c76219a2a46eddc44b644352405448bdb29a436 Mon Sep 17 00:00:00 2001 From: wwwcg Date: Tue, 21 Oct 2025 10:52:09 +0800 Subject: [PATCH 07/11] feat(android): hermes engine support --- driver/js/CMakeLists.txt | 10 +++++ driver/js/include/driver/js_driver_utils.h | 4 ++ driver/js/src/js_driver_utils.cc | 11 ++++-- driver/js/src/js_driver_utils_any_cast.cc | 38 +++++++++++++++++++ driver/js/src/modules/contextify_module.cc | 26 +++++++++---- driver/js/src/napi/hermes/hermes_ctx.cc | 17 +++++---- .../connector/driver/js/gradle.properties | 8 +++- 7 files changed, 93 insertions(+), 21 deletions(-) create mode 100644 driver/js/src/js_driver_utils_any_cast.cc diff --git a/driver/js/CMakeLists.txt b/driver/js/CMakeLists.txt index 041593b7437..e2a974a859e 100644 --- a/driver/js/CMakeLists.txt +++ b/driver/js/CMakeLists.txt @@ -214,6 +214,7 @@ set(SOURCE_SET src/base/js_value_wrapper.cc src/engine.cc src/js_driver_utils.cc + src/js_driver_utils_any_cast.cc src/modules/animation_frame_module.cc src/modules/animation_module.cc src/modules/event_module.cc @@ -289,6 +290,15 @@ if("${JS_ENGINE}" STREQUAL "HERMES_ONLY" OR "${JS_ENGINE}" STREQUAL "HERMES_V8") else() message(FATAL_ERROR "Unsupported system ${CMAKE_SYSTEM_NAME} by hermes engine") endif() + + # 为Hermes专用文件添加RTTI和异常支持 + set_source_files_properties( + src/napi/hermes/hermes_ctx.cc + src/napi/hermes/hermes_ctx_value.cc + src/napi/hermes/hermes_try_catch.cc + src/vm/hermes/hermes_vm.cc + PROPERTIES COMPILE_FLAGS "-frtti -fexceptions" + ) endif () set(SOURCE_SET_STANDALONE diff --git a/driver/js/include/driver/js_driver_utils.h b/driver/js/include/driver/js_driver_utils.h index 5b56a18022b..e7c7706b451 100644 --- a/driver/js/include/driver/js_driver_utils.h +++ b/driver/js/include/driver/js_driver_utils.h @@ -80,6 +80,10 @@ class JsDriverUtils { string_view, bool, byte_string)>& callback); + + // 引擎专用的工具函数,用于从slot中获取ScopeWrapper + static ScopeWrapper* GetScopeWrapperFromSlot(const std::any& slot); + static void LoadInstance(const std::shared_ptr& scope, byte_string&& buffer_data); static void UnloadInstance(const std::shared_ptr& scope, byte_string&& buffer_data); diff --git a/driver/js/src/js_driver_utils.cc b/driver/js/src/js_driver_utils.cc index b1a06ba7a00..2e47b919e2c 100644 --- a/driver/js/src/js_driver_utils.cc +++ b/driver/js/src/js_driver_utils.cc @@ -726,9 +726,14 @@ void JsDriverUtils::CallNative(hippy::napi::CallbackInfo& info, const std::funct bool, byte_string)>& callback) { FOOTSTONE_DLOG(INFO) << "CallHost"; - std::any slot_any = info.GetSlot(); - auto any_pointer = std::any_cast(&slot_any); - auto scope_wrapper = reinterpret_cast(static_cast(*any_pointer)); + + // 使用引擎专用的工具函数获取ScopeWrapper + ScopeWrapper* scope_wrapper = GetScopeWrapperFromSlot(info.GetSlot()); + if (!scope_wrapper) { + FOOTSTONE_DLOG(ERROR) << "Failed to get ScopeWrapper from slot"; + return; + } + auto scope = scope_wrapper->scope.lock(); FOOTSTONE_CHECK(scope); auto context = scope->GetContext(); diff --git a/driver/js/src/js_driver_utils_any_cast.cc b/driver/js/src/js_driver_utils_any_cast.cc new file mode 100644 index 00000000000..05c86407269 --- /dev/null +++ b/driver/js/src/js_driver_utils_any_cast.cc @@ -0,0 +1,38 @@ +/* + * Tencent is pleased to support the open source community by making + * Hippy available. + * + * Copyright (C) 2022 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * 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 "driver/js_driver_utils.h" +#include + +namespace hippy { +inline namespace driver { + +// 公共的any_cast工具方法 +ScopeWrapper* JsDriverUtils::GetScopeWrapperFromSlot(const std::any& slot) { + auto slot_ptr = std::any_cast(&slot); + if (slot_ptr) { + return static_cast(*slot_ptr); + } + + return nullptr; +} + +} // namespace driver +} // namespace hippy diff --git a/driver/js/src/modules/contextify_module.cc b/driver/js/src/modules/contextify_module.cc index e7ae25812c5..77d08c68a2e 100644 --- a/driver/js/src/modules/contextify_module.cc +++ b/driver/js/src/modules/contextify_module.cc @@ -85,12 +85,22 @@ void ContextifyModule::RunInThisContext(hippy::napi::CallbackInfo& info, void* d const auto &source_code = source_code_provider->GetNativeSourceCode(file_name); std::shared_ptr try_catch = hippy::TryCatch::CreateTryCatchScope(true, context); string_view str_view(reinterpret_cast(source_code.data_), source_code.length_); -#ifdef JS_V8 - auto v8_context = std::static_pointer_cast(context); - auto ret = v8_context->RunScript(str_view, key, false, nullptr, false); -#else - auto ret = context->RunScript(str_view, key); + std::shared_ptr ret; + auto engine = scope->GetEngine().lock(); + if (engine) { + auto vm = engine->GetVM(); +#if defined(JS_V8) + if (vm && vm->GetVMType() == VM::kJSEngineV8) { + auto v8_context = std::static_pointer_cast(context); + ret = v8_context->RunScript(str_view, key, false, nullptr, false); + } else #endif + { + ret = context->RunScript(str_view, key); + } + } else { + ret = context->RunScript(str_view, key); + } if (try_catch->HasCaught()) { FOOTSTONE_DLOG(ERROR) << "GetNativeSourceCode error = " << try_catch->GetExceptionMessage(); info.GetExceptionValue()->Set(try_catch->Exception()); @@ -131,16 +141,16 @@ void ContextifyModule::LoadUntrustedContent(CallbackInfo& info, void* data) { auto engine = scope->GetEngine().lock(); if (engine && engine->GetVM()->GetVMType() == VM::kJSEngineHermes) { std::string oriUri = StringViewUtils::ToStdString(StringViewUtils::CovertToUtf8(uri, uri.encoding()).utf8_value()); - + // Check that the oriUri begins with "file://" and ends with ".js" const std::string filePrefix = "file://"; const std::string jsSuffix = ".js"; - + if (oriUri.rfind(filePrefix, 0) == 0 && oriUri.size() >= jsSuffix.size() && oriUri.rfind(jsSuffix) == oriUri.size() - jsSuffix.size()) { // modify the ext to .hbc oriUri = oriUri.substr(0, oriUri.size() - jsSuffix.size()) + ".hbc"; - + // replace the uri object uri = string_view(oriUri); FOOTSTONE_DLOG(INFO) << "change file ext from .js to .hbc for hermes"; diff --git a/driver/js/src/napi/hermes/hermes_ctx.cc b/driver/js/src/napi/hermes/hermes_ctx.cc index 837480f7897..eacb604c18c 100644 --- a/driver/js/src/napi/hermes/hermes_ctx.cc +++ b/driver/js/src/napi/hermes/hermes_ctx.cc @@ -23,6 +23,7 @@ #include "driver/napi/hermes/hermes_ctx.h" #include "driver/napi/hermes/hermes_try_catch.h" #include "driver/scope.h" +#include "driver/js_driver_utils.h" #include "footstone/string_view.h" #include "footstone/string_view_utils.h" #include "driver/vm/hermes/native_source_code_hermes.h" @@ -107,8 +108,8 @@ static Value InvokePropertyCallback(Runtime& runtime, if (!global_native_state->Get(kScopeWrapperIndex, scope_any)) { return facebook::jsi::Value::undefined(); } - auto any_pointer = std::any_cast(&scope_any); - auto scope_wrapper = reinterpret_cast(static_cast(*any_pointer)); + auto scope_wrapper = JsDriverUtils::GetScopeWrapperFromSlot(scope_any); + FOOTSTONE_DCHECK(scope_wrapper != nullptr); auto scope = scope_wrapper->scope.lock(); if (scope == nullptr) return facebook::jsi::Value::undefined(); auto hermes_ctx = std::static_pointer_cast(scope->GetContext()); @@ -147,8 +148,8 @@ static Value InvokeConstructorJsCallback(Runtime& runtime, return Value::undefined(); } // 2. Get Scope - auto any_pointer = std::any_cast(&scope_any); - auto scope_wrapper = reinterpret_cast(static_cast(*any_pointer)); + auto scope_wrapper = JsDriverUtils::GetScopeWrapperFromSlot(scope_any); + FOOTSTONE_DCHECK(scope_wrapper != nullptr); auto scope = scope_wrapper->scope.lock(); if (scope == nullptr) return facebook::jsi::Value::undefined(); auto hermes_ctx = std::static_pointer_cast(scope->GetContext()); @@ -259,8 +260,8 @@ static Value InvokeJsCallback(Runtime& runtime, const Value& this_value, const V if (!global_native_state->Get(kScopeWrapperIndex, scope_any)) { return Value::undefined(); } - auto any_pointer = std::any_cast(&scope_any); - auto scope_wrapper = reinterpret_cast(static_cast(*any_pointer)); + auto scope_wrapper = JsDriverUtils::GetScopeWrapperFromSlot(scope_any); + FOOTSTONE_DCHECK(scope_wrapper != nullptr); auto scope = scope_wrapper->scope.lock(); if (scope == nullptr) return facebook::jsi::Value::undefined(); auto hermes_ctx = std::static_pointer_cast(scope->GetContext()); @@ -1209,8 +1210,8 @@ void HermesCtx::SetWeak(std::shared_ptr value, std::unique_ptrGet(kScopeWrapperIndex, scope_any)) { return; } - auto any_pointer = std::any_cast(&scope_any); - auto scope_wrapper = reinterpret_cast(static_cast(*any_pointer)); + auto scope_wrapper = JsDriverUtils::GetScopeWrapperFromSlot(scope_any); + FOOTSTONE_DCHECK(scope_wrapper != nullptr); wrapper->scope = scope_wrapper->scope; local_state->SetWeakCallbackWrapper(std::move(wrapper)); } diff --git a/framework/android/connector/driver/js/gradle.properties b/framework/android/connector/driver/js/gradle.properties index a5be0dff130..941693ca0a6 100644 --- a/framework/android/connector/driver/js/gradle.properties +++ b/framework/android/connector/driver/js/gradle.properties @@ -20,9 +20,13 @@ # Indicates the javascript engine to use: # * V8 - Use V8 engine only # * HERMES_ONLY - Use Hermes engine only -# * HERMES_V8 - Support both Hermes and V8 engines (parallel build) # -CPP_JS_ENGINE=HERMES_ONLY +# Note: +# The HERMES_V8 mode is currently not supported, +# because the relevant `#ifdef JS_V8` macros have not been fully handled. +# Support for building with both Hermes and V8 engines in parallel will be provided in the future. +# +CPP_JS_ENGINE=HERMES_V8 # # V8 Component From c38e56d15cfa908aee0d20aeba93cd496e05147a Mon Sep 17 00:00:00 2001 From: wwwcg Date: Tue, 21 Oct 2025 10:52:39 +0800 Subject: [PATCH 08/11] chore: hermes ctx optimization --- driver/js/src/napi/hermes/hermes_ctx.cc | 29 +++++++++++++++---------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/driver/js/src/napi/hermes/hermes_ctx.cc b/driver/js/src/napi/hermes/hermes_ctx.cc index eacb604c18c..0113595d424 100644 --- a/driver/js/src/napi/hermes/hermes_ctx.cc +++ b/driver/js/src/napi/hermes/hermes_ctx.cc @@ -86,7 +86,7 @@ HippyJsiBuffer::HippyJsiBuffer(const uint8_t* data, size_t len) { HippyJsiBuffer::~HippyJsiBuffer() { if (data_) free(data_); } -static void HandleJsException(std::shared_ptr scope, std::shared_ptr exception) { +static void HandleJsException(const std::shared_ptr& scope, const std::shared_ptr& exception) { VM::HandleException(scope->GetContext(), "uncaughtException", exception); auto engine = scope->GetEngine().lock(); FOOTSTONE_DCHECK(engine); @@ -230,7 +230,7 @@ static Value InvokeConstructorJsCallback(Runtime& runtime, proto_object.setNativeState(runtime, proto_state); } proto_state->Set(uniqueID, internal_data); - + // Set `LocalNativeState` for instance object (for cleanup purposes) std::shared_ptr instance_state; if (this_obj.hasNativeState(runtime)) { @@ -269,16 +269,16 @@ static Value InvokeJsCallback(Runtime& runtime, const Value& this_value, const V CallbackInfo cb_info; cb_info.SetSlot(scope_any); - + if (this_value.isObject()) { auto instance_object = this_value.asObject(runtime); - + if (instance_object.hasNativeState(runtime)) { auto local_native_state = instance_object.getNativeState(runtime); auto data = local_native_state->Get(); cb_info.SetData(data); } - + if (instance_object.hasProperty(runtime, kProtoKey)) { auto proto_object = instance_object.getProperty(runtime, kProtoKey).asObject(runtime); if (proto_object.hasNativeState(runtime)) { @@ -341,7 +341,7 @@ HermesCtx::HermesCtx() { #ifdef ENABLE_INSPECTOR shouldEnableSampleProfiling = true; #endif - + auto runtimeConfigBuilder = ::hermes::vm::RuntimeConfig::Builder() .withGCConfig(::hermes::vm::GCConfig::Builder() // Default to 3GB @@ -386,8 +386,8 @@ void HermesCtx::SetupDebugAgent(facebook::hermes::debugger::EnqueueRuntimeTaskFu cdpDebugAPI_ = CDPDebugAPI::create(*runtime_); cdpAgent_ = CDPAgent::create(0, *cdpDebugAPI_, - enqueueRuntimeTask, - messageCallback); + std::move(enqueueRuntimeTask), + std::move(messageCallback)); } #endif /* ENABLE_INSPECTOR */ @@ -527,7 +527,7 @@ std::shared_ptr HermesCtx::NewInstance(const std::shared_ptr function.setNativeState(*runtime_, local_native_state); std::vector arguments; - size_t len = static_cast(argc); + auto len = static_cast(argc); arguments.reserve(len); for (size_t i = 0; i < len; ++i) { auto arg = std::static_pointer_cast(argv[i]); @@ -746,7 +746,7 @@ std::shared_ptr HermesCtx::CreateException(const string_view& msg) { auto u8_msg = StringViewUtils::CovertToUtf8(msg, msg.encoding()); auto str = StringViewUtils::ToStdString(u8_msg.utf8_value()); auto exptr = std::make_exception_ptr(facebook::jsi::JSINativeException(str)); - + auto msg_key = CreateString("message"); auto msg_value = CreateString(msg); auto exception = std::static_pointer_cast(CreateObject({{ msg_key, msg_value }})); @@ -757,7 +757,7 @@ std::shared_ptr HermesCtx::CreateException(const string FOOTSTONE_DLOG(INFO) << "HermesCtx::CreateException msg = " << msg; auto u8_msg = StringViewUtils::CovertToUtf8(msg, msg.encoding()); auto str = StringViewUtils::ToStdString(u8_msg.utf8_value()); - + auto msg_key = CreateString("message"); auto msg_value = CreateString(msg); auto exception = std::static_pointer_cast(CreateObject({{ msg_key, msg_value }})); @@ -800,7 +800,12 @@ std::shared_ptr HermesCtx::CallFunction(const std::shared_ptrGetValue(runtime_) : facebook::jsi::Value::null(); } - auto value = jsi_func.callWithThis(*runtime_, this_object.asObject(*runtime_), (const Value *)(arg_vec.data()), argument_count); + const facebook::jsi::Value* args_ptr = arg_vec.empty() ? nullptr : static_cast(arg_vec.data()); + auto value = jsi_func.callWithThis( + *runtime_, + this_object.asObject(*runtime_), + args_ptr, + argument_count); return std::make_shared(*runtime_, value); } } catch (const facebook::jsi::JSIException& err) { From 619812aa5ce011c8e5409d81c731cee390c989a8 Mon Sep 17 00:00:00 2001 From: wwwcg Date: Tue, 21 Oct 2025 11:49:12 +0800 Subject: [PATCH 09/11] feat(android): hermes engine support --- docs/development/use-hermes-engine.md | 43 ++++++++- .../drawable/page_config_dialog_item_bg.xml | 16 ++++ .../layout/activity_page_configuration.xml | 2 +- .../src/main/res/layout/js_engine_setting.xml | 87 +++++++++++++++++++ 4 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 framework/examples/android-demo/src/main/res/drawable/page_config_dialog_item_bg.xml create mode 100644 framework/examples/android-demo/src/main/res/layout/js_engine_setting.xml diff --git a/docs/development/use-hermes-engine.md b/docs/development/use-hermes-engine.md index c488fe01ff6..afe60835250 100644 --- a/docs/development/use-hermes-engine.md +++ b/docs/development/use-hermes-engine.md @@ -134,9 +134,48 @@ Hippy 从 `3.4.0` 版本开始支持 Hermes 引擎。本文档将指导你如何 > > Tips 2: `HippyLaunchOptions` 中 `useHermesEngine` 的默认值为 `NO`,因此如果需要启用 Hermes 引擎,则必须显式地将其设置为 `YES`,否则项目将继续使用 JSC 引擎。 -### Android 平台 (Alpha版本) +### Android 平台 -由于 Android 平台的 Hermes 引擎切换目前处于 Alpha 阶段,我们将在未来版本中提供详细指引,请关注后续更新。 +Android 平台支持 Hermes 引擎,支持与 V8 引擎并行使用,并支持运行时动态切换。 + +#### 1. 编译时选择引擎 + +**修改 `gradle.properties`**: + +在项目根目录的 `gradle.properties` 文件中配置 JavaScript 引擎: + +```properties +# 可选值:V8, HERMES_ONLY +CPP_JS_ENGINE=HERMES_ONLY + +# 是否启用调试功能 +CPP_ENABLE_INSPECTOR=true +``` + +**引擎配置说明**: +- `V8`:仅使用 V8 引擎(默认) +- `HERMES_ONLY`:仅使用 Hermes 引擎 +- `HERMES_V8`:同时支持 V8 和 Hermes 引擎(并行构建,该模式目前暂不支持) + +#### 2. 运行时动态切换 + +在创建 HippyEngine 实例时,可以通过 `EngineInitParams` 动态选择引擎: + +```java +EngineInitParams params = new EngineInitParams(); +params.context = context; +params.debugMode = false; + +// 选择使用 Hermes 引擎 +params.useHermesEngine = true; +params.jsEngineType = "hermes"; + +// 或者选择使用 V8 引擎 +// params.useHermesEngine = false; +// params.jsEngineType = "v8"; + +HippyEngine engine = HippyEngine.create(params); +``` ## 前端切换步骤 diff --git a/framework/examples/android-demo/src/main/res/drawable/page_config_dialog_item_bg.xml b/framework/examples/android-demo/src/main/res/drawable/page_config_dialog_item_bg.xml new file mode 100644 index 00000000000..d4b3d686c45 --- /dev/null +++ b/framework/examples/android-demo/src/main/res/drawable/page_config_dialog_item_bg.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/framework/examples/android-demo/src/main/res/layout/activity_page_configuration.xml b/framework/examples/android-demo/src/main/res/layout/activity_page_configuration.xml index 995bc568bc6..a595fe8edb4 100644 --- a/framework/examples/android-demo/src/main/res/layout/activity_page_configuration.xml +++ b/framework/examples/android-demo/src/main/res/layout/activity_page_configuration.xml @@ -312,7 +312,7 @@ android:layout_gravity="center" android:layout_marginLeft="16dp" android:clickable="false" - android:src="@drawable/page_config_debug_2x" + android:src="@drawable/page_config_driver_2x" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" /> diff --git a/framework/examples/android-demo/src/main/res/layout/js_engine_setting.xml b/framework/examples/android-demo/src/main/res/layout/js_engine_setting.xml new file mode 100644 index 00000000000..fc6ca6763dc --- /dev/null +++ b/framework/examples/android-demo/src/main/res/layout/js_engine_setting.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + From 2a43179d5b45dd5979e897b739bddc8bfbf0954c Mon Sep 17 00:00:00 2001 From: wwwcg Date: Tue, 21 Oct 2025 11:54:05 +0800 Subject: [PATCH 10/11] feat(android): hermes engine support --- driver/js/CMakeLists.txt | 1 - driver/js/src/js_driver_utils.cc | 10 ++++++ driver/js/src/js_driver_utils_any_cast.cc | 38 ----------------------- 3 files changed, 10 insertions(+), 39 deletions(-) delete mode 100644 driver/js/src/js_driver_utils_any_cast.cc diff --git a/driver/js/CMakeLists.txt b/driver/js/CMakeLists.txt index e2a974a859e..c40ab66dbda 100644 --- a/driver/js/CMakeLists.txt +++ b/driver/js/CMakeLists.txt @@ -214,7 +214,6 @@ set(SOURCE_SET src/base/js_value_wrapper.cc src/engine.cc src/js_driver_utils.cc - src/js_driver_utils_any_cast.cc src/modules/animation_frame_module.cc src/modules/animation_module.cc src/modules/event_module.cc diff --git a/driver/js/src/js_driver_utils.cc b/driver/js/src/js_driver_utils.cc index 2e47b919e2c..41321356ba6 100644 --- a/driver/js/src/js_driver_utils.cc +++ b/driver/js/src/js_driver_utils.cc @@ -899,5 +899,15 @@ void JsDriverUtils::UnloadInstance(const std::shared_ptr& scope, byte_str runner->PostTask(std::move(callback)); } +// A public tool function for getting the ScopeWrapper from the slot. +ScopeWrapper* JsDriverUtils::GetScopeWrapperFromSlot(const std::any& slot) { + auto slot_ptr = std::any_cast(&slot); + if (slot_ptr) { + return static_cast(*slot_ptr); + } + + return nullptr; +} + } // namespace driver } // namespace hippy diff --git a/driver/js/src/js_driver_utils_any_cast.cc b/driver/js/src/js_driver_utils_any_cast.cc deleted file mode 100644 index 05c86407269..00000000000 --- a/driver/js/src/js_driver_utils_any_cast.cc +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making - * Hippy available. - * - * Copyright (C) 2022 THL A29 Limited, a Tencent company. - * All rights reserved. - * - * 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 "driver/js_driver_utils.h" -#include - -namespace hippy { -inline namespace driver { - -// 公共的any_cast工具方法 -ScopeWrapper* JsDriverUtils::GetScopeWrapperFromSlot(const std::any& slot) { - auto slot_ptr = std::any_cast(&slot); - if (slot_ptr) { - return static_cast(*slot_ptr); - } - - return nullptr; -} - -} // namespace driver -} // namespace hippy From 821c69f96048e6d925eade5786fc525b890cc79b Mon Sep 17 00:00:00 2001 From: wwwcg Date: Tue, 21 Oct 2025 12:03:50 +0800 Subject: [PATCH 11/11] feat(android): hermes engine support --- docs/development/use-hermes-engine.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/development/use-hermes-engine.md b/docs/development/use-hermes-engine.md index afe60835250..eddb0fd1352 100644 --- a/docs/development/use-hermes-engine.md +++ b/docs/development/use-hermes-engine.md @@ -136,8 +136,6 @@ Hippy 从 `3.4.0` 版本开始支持 Hermes 引擎。本文档将指导你如何 ### Android 平台 -Android 平台支持 Hermes 引擎,支持与 V8 引擎并行使用,并支持运行时动态切换。 - #### 1. 编译时选择引擎 **修改 `gradle.properties`**: