// // 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. // import 'dart:convert'; import 'dart:core'; import 'dart:ffi'; import 'dart:io'; import 'dart:typed_data'; import 'package:ffi/ffi.dart'; import 'package:flutter/services.dart' show rootBundle; import 'package:voltron_ffi/voltron_ffi.dart'; import 'package:voltron_renderer/voltron_renderer.dart'; import 'bridge_define.dart'; import 'voltron_bridge.dart'; /// 管理dart to c++方法调用以及c++ to dart方法注册逻辑 class _BridgeFFIManager { static const String _kVoltronCoreRegisterHeader = 'voltron_core'; static _BridgeFFIManager? _instance; factory _BridgeFFIManager() => _getInstance(); static _BridgeFFIManager get instance => _getInstance(); final DynamicLibrary _library = FfiManager().library; // 初始化js framework late InitJsFrameworkFfiDartType initJsFramework; // 执行js bundle late RunScriptFromUriFfiDartType runScriptFromUri; // 调用js方法 late CallFunctionFfiDartType callFunction; late GetCrashMessageFfiType getCrashMessage; // 初始化native dom late LoadInstanceFfiDartType loadInstance; // 销毁native dom late UnloadInstanceFfiDartType unloadInstance; // 绑定dom和render manager late BindDomAndRenderDartType bindDomAndRender; // 链接rootView和jsRuntime late ConnectRootViewAndRuntimeDartType connectRootViewAndRuntime; // devtools 请求拦截 late OnNetworkRequestInvokeDartType onNetworkRequestInvoke; late OnNetworkResponseInvokeDartType onNetworkResponseInvoke; // 销毁 late DestroyFfiDartType destroy; late CreateDevtoolsDartType createDevtools; late DestroyDevtoolsDartType destroyDevtools; late NotifyNetworkEventFfiDartType notifyNetworkEvent; // 执行回调任务 late ExecuteCallbackDartType executeCallback; // 获取引擎自增id late GetVoltronEngineIndexFfiDartType getVoltronEngineIndex; static _BridgeFFIManager _getInstance() { // 只能有一个实例 _instance ??= _BridgeFFIManager._internal(); return _instance!; } _BridgeFFIManager._internal() { initJsFramework = _library.lookupFunction( "InitJSFrameworkFFI", ); runScriptFromUri = _library.lookupFunction( "RunScriptFromUriFFI", ); notifyNetworkEvent = _library.lookupFunction( "NotifyNetworkEvent", ); loadInstance = _library.lookupFunction( 'LoadInstanceFFI', ); bindDomAndRender = _library.lookupFunction( 'DoBindDomAndRender', ); connectRootViewAndRuntime = _library .lookupFunction( 'DoConnectRootViewAndRuntime', ); unloadInstance = _library.lookupFunction( 'UnloadInstanceFFI', ); callFunction = _library.lookupFunction( "CallFunctionFFI", ); getCrashMessage = _library.lookupFunction( "GetCrashMessageFFI", ); destroy = _library.lookupFunction( "DestroyFFI", ); onNetworkRequestInvoke = _library.lookupFunction( 'OnNetworkRequestInvoke', ); onNetworkResponseInvoke = _library.lookupFunction( 'OnNetworkResponseInvoke', ); createDevtools = _library.lookupFunction( "CreateDevtoolsFFI", ); destroyDevtools = _library.lookupFunction( "DestroyDevtoolsFFI", ); getVoltronEngineIndex = _library .lookupFunction( 'GetVoltronEngineIndexFFI', ); } } /// 封装dart to c++的api调用,处理各种中间数据 class VoltronApi { // jsc暂时不支持通过bson通信 static bool get enableVoltronBuffer => (Platform.isIOS || Platform.isMacOS) ? false : true; static int getVoltronEngineIndex() { return _BridgeFFIManager.instance.getVoltronEngineIndex(); } // ------------------ dart call native方法 start --------------------- static Future initJsFrameWork({ String globalConfig = '', bool singleThreadMode = false, bool isDevModule = false, required int groupId, required int engineId, required int vfsId, required int domId, required CommonCallback callback, required int devtoolsId, }) async { var globalConfigPtr = globalConfig.toNativeUtf16(); globalConfig.toNativeUtf16(); var result = _BridgeFFIManager.instance.initJsFramework( globalConfigPtr, singleThreadMode ? 1 : 0, enableVoltronBuffer ? 0 : 1, isDevModule ? 1 : 0, groupId, vfsId, FfiManager().id, domId, engineId, generateCallback( (value) { callback(value); }, ), devtoolsId, ); free(globalConfigPtr); return result; } static void bindDomAndRender( int domInstanceId, int engineId, int renderManagerId, ) { _BridgeFFIManager.instance.bindDomAndRender( domInstanceId, engineId, renderManagerId, ); } static void connectRootViewAndRuntime( int engineId, int rootId, ) { _BridgeFFIManager.instance.connectRootViewAndRuntime( engineId, rootId, ); } static VoltronPair, Uint8List> _parseParams(Object params) { if (enableVoltronBuffer) { var paramsBuffer = params.encode(); assert(paramsBuffer.isNotEmpty); final paramsPointer = malloc(paramsBuffer.length); final nativeParams = paramsPointer.asTypedList(paramsBuffer.length); nativeParams.setRange(0, paramsBuffer.length, paramsBuffer); return VoltronPair(paramsPointer, nativeParams); } else { var paramsJson = objectToJson(params); assert(paramsJson.isNotEmpty); final units = utf8.encode(paramsJson); final Pointer result = malloc(units.length + 1); final Uint8List nativeString = result.asTypedList(units.length + 1); nativeString.setAll(0, units); nativeString[units.length] = 0; return VoltronPair(result, nativeString); } } static Future runScriptFromUri( int engineId, int vfsId, String uri, String codeCacheDir, bool canUseCodeCache, bool isLocalFile, CommonCallback callback, ) async { var uriPtr = uri.toNativeUtf16(); var codeCacheDirPtr = codeCacheDir.toNativeUtf16(); var result = _BridgeFFIManager.instance.runScriptFromUri( engineId, vfsId, uriPtr, codeCacheDirPtr, canUseCodeCache ? 1 : 0, isLocalFile ? 1 : 0, generateCallback( (value) { callback(value); }, ), ); free(uriPtr); free(codeCacheDirPtr); return result == 1; } /// network notification, when network a request will be sent to server static void notifyRequestWillBeSent( int engineId, String requestId, String requestContent, ) { var contentPtr = requestContent.toNativeUtf16(); var requestIdPtr = requestId.toNativeUtf16(); _BridgeFFIManager.instance.notifyNetworkEvent( engineId, requestIdPtr, NetworkEventType.requestWillBeSent.index, contentPtr, nullptr, ); free(contentPtr); free(requestIdPtr); } /// network notification, when a network request response received static void notifyResponseReceived( int engineId, String requestId, String responseContent, String bodyData, ) { var contentPtr = responseContent.toNativeUtf16(); var requestIdPtr = requestId.toNativeUtf16(); var extraPtr = bodyData.toNativeUtf16(); _BridgeFFIManager.instance.notifyNetworkEvent( engineId, requestIdPtr, NetworkEventType.responseReceived.index, contentPtr, extraPtr, ); free(requestIdPtr); free(contentPtr); free(extraPtr); } /// network notification, when a network request will response Finished static void notifyLoadingFinished( int engineId, String requestId, String loadingFinishContent, ) { var contentPtr = loadingFinishContent.toNativeUtf16(); var requestIdPtr = requestId.toNativeUtf16(); _BridgeFFIManager.instance.notifyNetworkEvent( engineId, requestIdPtr, NetworkEventType.loadingFinished.index, contentPtr, nullptr, ); free(contentPtr); free(requestIdPtr); } static Pointer strByteDataToPointer(ByteData data) { var units = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); var result = utf8.decode(units); return result.toNativeUtf16(); } static Future loadInstance( int engineId, VoltronMap params, ) async { var stopwatch = Stopwatch(); stopwatch.start(); var paramsPair = _parseParams(params); stopwatch.stop(); LogUtils.profile("loadInstance parse params", stopwatch.elapsedMilliseconds); _BridgeFFIManager.instance.loadInstance( engineId, paramsPair.left, paramsPair.right.length, ); stopwatch.stop(); LogUtils.profile("loadInstance", stopwatch.elapsedMilliseconds); free(paramsPair.left); } static Future unloadInstance( int engineId, VoltronMap params, ) async { var stopwatch = Stopwatch(); stopwatch.start(); var paramsPair = _parseParams(params); stopwatch.stop(); LogUtils.profile("unloadInstance parse params", stopwatch.elapsedMilliseconds); _BridgeFFIManager.instance.unloadInstance( engineId, paramsPair.left, paramsPair.right.length, ); stopwatch.stop(); LogUtils.profile("unloadInstance", stopwatch.elapsedMilliseconds); } static Future callFunction( int engineId, String action, Object params, CommonCallback callback, ) async { var stopwatch = Stopwatch(); stopwatch.start(); var actionPtr = action.toNativeUtf16(); var paramsPair = _parseParams(params); stopwatch.stop(); LogUtils.profile("callFunction", stopwatch.elapsedMilliseconds); _BridgeFFIManager.instance.callFunction( engineId, actionPtr, paramsPair.left, paramsPair.right.length, generateCallback((value) { stopwatch.stop(); LogUtils.profile("callFunction", stopwatch.elapsedMilliseconds); callback(value); }), ); free(actionPtr); free(paramsPair.left); } static Future getCrashMessage() async { final crashMessage = _BridgeFFIManager.instance.getCrashMessage(); return crashMessage.toDartString(); } static Future destroy( int engineId, CommonCallback callback, bool isReload, ) async { _BridgeFFIManager.instance.destroy( engineId, generateCallback((value) { callback(value); }), isReload ? 1 : 0, ); } static Future createDevtools({ String dataDir = '', String wsUrl = '', }) async { return _BridgeFFIManager.instance.createDevtools( dataDir.toNativeUtf16(), wsUrl.toNativeUtf16(), ); } static Future destroyDevtools( int devtoolsId, bool isReload, ) async { _BridgeFFIManager.instance.destroyDevtools( devtoolsId, isReload ? 1 : 0, ); } // ------------------ dart call native方法 end --------------------- // 初始化bridge层 static void initBridge() { // 注册callNative回调 var callNativeRegisterFunc = FfiManager() .library .lookupFunction, AddCallFuncDartType>( FfiManager().registerFuncName, ); var callNativeFunc = Pointer.fromFunction(callNative); FfiManager().addRegisterFunc( _BridgeFFIManager._kVoltronCoreRegisterHeader, LoaderFuncType.callNative.index, callNativeFunc, callNativeRegisterFunc, ); // 注册reportJsonException回调 var reportJsonExceptionRegisterFunc = FfiManager().library.lookupFunction< AddCallFuncNativeType, AddCallFuncDartType>( FfiManager().registerFuncName, ); var reportJsonExceptionFunc = Pointer.fromFunction(reportJsonException); FfiManager().addRegisterFunc( _BridgeFFIManager._kVoltronCoreRegisterHeader, LoaderFuncType.reportJsonException.index, reportJsonExceptionFunc, reportJsonExceptionRegisterFunc, ); // 注册reportJSException回调 var reportJsExceptionRegisterFunc = FfiManager().library.lookupFunction< AddCallFuncNativeType, AddCallFuncDartType>( FfiManager().registerFuncName, ); var reportJSExceptionFunc = Pointer.fromFunction(reportJSException); FfiManager().addRegisterFunc( _BridgeFFIManager._kVoltronCoreRegisterHeader, LoaderFuncType.reportJsException.index, reportJSExceptionFunc, reportJsExceptionRegisterFunc, ); } } // ------------------ native call dart方法 start --------------------- void callNative( int engineId, Pointer moduleNamePtr, Pointer moduleFuncPtr, Pointer callIdPtr, Pointer paramsDataPtr, int paramsLen, int bridgeParamJsonInt, ) { var bridgeParamJson = bridgeParamJsonInt == 1; var moduleName = moduleNamePtr.toDartString(); var moduleFunc = moduleFuncPtr.toDartString(); var callId = callIdPtr.toDartString(); var dataList = paramsDataPtr.cast().asTypedList(paramsLen); final bridge = VoltronBridgeManager.bridgeMap[engineId]; if (bridge != null) { bridge.callNatives( moduleName, moduleFunc, callId, dataList, bridgeParamJson, ); } } void reportJsonException( int engineId, Pointer jsonValue, ) { var exception = jsonValue.toDartString(); LogUtils.e("Voltron_bridge", "reportJsonException\n !!!!!!!!!!!!!!!!!!! \n Error($exception)"); final bridge = VoltronBridgeManager.bridgeMap[engineId]; if (bridge != null) { bridge.reportException(exception, ""); } } void reportJSException( int engineId, Pointer descriptionStream, Pointer stackStream, ) { var exception = descriptionStream.toDartString(); var stackTrace = stackStream.toDartString(); LogUtils.e( "Voltron_bridge", "reportJsException\n !!!!!!!!!!!!!!!!!!! \n Error($exception)\n StackTrace($stackTrace)", ); final bridge = VoltronBridgeManager.bridgeMap[engineId]; if (bridge != null) { bridge.reportException(exception, stackTrace); } } VoltronBridgeManager? getCurrentBridge( int engineId, ) { var bridge = VoltronBridgeManager.bridgeMap[engineId]; bridge ??= VoltronBridgeManager.bridgeMap[0]; return bridge; } // ------------------ native call dart方法 end ---------------------