From 28fd95589f5dab2ebb3f8deeb00db8ff5f727ced Mon Sep 17 00:00:00 2001 From: Michel Weststrate Date: Thu, 16 Jan 2020 04:45:03 -0800 Subject: [PATCH] Made flipper plugins a little more robust Summary: Added some assertions and string casts to make plugins a bit more robust Reviewed By: passy Differential Revision: D19427909 fbshipit-source-id: 46a3138805db865b538f745fae25ce1897e35736 --- ...perReactNativeJavaScriptPluginManager.java | 15 ++++++--- .../FlipperReactNativeJavaScriptReceiver.java | 6 ++-- react-native/react-native-flipper/index.js | 31 +++++++++++++++---- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/react-native/react-native-flipper/android/src/main/java/com/facebook/flipper/reactnative/FlipperReactNativeJavaScriptPluginManager.java b/react-native/react-native-flipper/android/src/main/java/com/facebook/flipper/reactnative/FlipperReactNativeJavaScriptPluginManager.java index 84ef078ce..0abdd4c83 100644 --- a/react-native/react-native-flipper/android/src/main/java/com/facebook/flipper/reactnative/FlipperReactNativeJavaScriptPluginManager.java +++ b/react-native/react-native-flipper/android/src/main/java/com/facebook/flipper/reactnative/FlipperReactNativeJavaScriptPluginManager.java @@ -72,10 +72,14 @@ public class FlipperReactNativeJavaScriptPluginManager { } public void send(String pluginId, String method, String data) { + FlipperReactNativeJavaScriptPlugin plugin = getPlugin(pluginId); + if (data == null) { + plugin.getConnection().send(method, (FlipperObject) null); + return; + } // Optimization: throwing raw strings around to the desktop would probably avoid some double // parsing... Object parsedData = parseJSON(data); - FlipperReactNativeJavaScriptPlugin plugin = getPlugin(pluginId); if (parsedData instanceof FlipperArray) { plugin.getConnection().send(method, (FlipperArray) parsedData); } else { @@ -134,14 +138,17 @@ public class FlipperReactNativeJavaScriptPluginManager { } private static Object /* FlipperArray | FlipperObject */ parseJSON(String json) { + if (json == null) { + return null; + } // returns either a FlipperObject or Flipper array, pending the data try { JSONTokener tokener = new JSONTokener(json); - if (tokener.nextClean() == '[') { - tokener.back(); + char firstChar = tokener.nextClean(); + tokener.back(); + if (firstChar == '[') { return new FlipperArray(new JSONArray(tokener)); } else { - tokener.back(); return new FlipperObject(new JSONObject(tokener)); } } catch (JSONException e) { diff --git a/react-native/react-native-flipper/android/src/main/java/com/facebook/flipper/reactnative/FlipperReactNativeJavaScriptReceiver.java b/react-native/react-native-flipper/android/src/main/java/com/facebook/flipper/reactnative/FlipperReactNativeJavaScriptReceiver.java index 8c8a99d47..083d50ce1 100644 --- a/react-native/react-native-flipper/android/src/main/java/com/facebook/flipper/reactnative/FlipperReactNativeJavaScriptReceiver.java +++ b/react-native/react-native-flipper/android/src/main/java/com/facebook/flipper/reactnative/FlipperReactNativeJavaScriptReceiver.java @@ -32,12 +32,14 @@ public class FlipperReactNativeJavaScriptReceiver implements FlipperReceiver { @Override public void onReceive(FlipperObject params, FlipperResponder responder) throws Exception { - String responderId = manager.createResponderId(responder); WritableMap eventData = Arguments.createMap(); eventData.putString("plugin", plugin); eventData.putString("method", method); eventData.putString("params", params.toJsonString()); - eventData.putString("responderId", responderId); + if (responder != null) { + String responderId = manager.createResponderId(responder); + eventData.putString("responderId", responderId); + } module.sendJSEvent("react-native-flipper-receive-event", eventData); } } diff --git a/react-native/react-native-flipper/index.js b/react-native/react-native-flipper/index.js index 1393eaef5..ac406cf36 100644 --- a/react-native/react-native-flipper/index.js +++ b/react-native/react-native-flipper/index.js @@ -17,6 +17,20 @@ export default Flipper; const listeners = {}; // plugin#method -> callback const plugins = {}; // plugin -> Plugin +function assertSerializable(data) { + if ( + data === undefined || + Array.isArray(data) || + (data && typeof data === 'object') + ) { + return true; + } + throw new Error( + 'Flipper: Expected serializable (undefined, an array or an object). Got: ' + + data, + ); +} + class Connection { connected; pluginId; @@ -30,15 +44,20 @@ class Connection { if (!this.connected) { throw new Error('Cannot send data, not connected'); } + assertSerializable(data); Flipper.send(this.pluginId, method, JSON.stringify(data)); } reportErrorWithMetadata(reason, stackTrace) { - Flipper.reportErrorWithMetadata(this.pluginId, reason, stackTrace); + Flipper.reportErrorWithMetadata( + this.pluginId, + '' + reason, + '' + stackTrace, + ); } reportError(error) { - Flipper.reportError(this.pluginId, error); + Flipper.reportError(this.pluginId, '' + error); } receive(method, listener) { @@ -59,6 +78,7 @@ class Responder { } success(response) { + assertSerializable(response); Flipper.respondSuccess( this.responderId, response == null ? null : JSON.stringify(response), @@ -66,15 +86,13 @@ class Responder { } error(response) { + assertSerializable(response); Flipper.respondError(this.responderId, JSON.stringify(response)); } } function startEventListeners() { const emitter = new NativeEventEmitter(Flipper); - emitter.removeAllListeners('react-native-flipper-plugin-connect'); - emitter.removeAllListeners('react-native-flipper-plugin-disconnect'); - emitter.removeAllListeners('react-native-flipper-receive-event'); emitter.addListener('react-native-flipper-plugin-connect', event => { const {plugin} = event; @@ -98,7 +116,8 @@ function startEventListeners() { const {plugin, method, params, responderId} = event; const key = plugin + '#' + method; if (listeners[key]) { - const responder = new Responder(responderId); + const responder = + responderId != null ? new Responder(responderId) : undefined; listeners[key](JSON.parse(params), responder); } });