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
This commit is contained in:
Michel Weststrate
2020-01-16 04:45:03 -08:00
committed by Facebook Github Bot
parent db9c41303d
commit 28fd95589f
3 changed files with 40 additions and 12 deletions

View File

@@ -72,10 +72,14 @@ public class FlipperReactNativeJavaScriptPluginManager {
} }
public void send(String pluginId, String method, String data) { 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 // Optimization: throwing raw strings around to the desktop would probably avoid some double
// parsing... // parsing...
Object parsedData = parseJSON(data); Object parsedData = parseJSON(data);
FlipperReactNativeJavaScriptPlugin plugin = getPlugin(pluginId);
if (parsedData instanceof FlipperArray) { if (parsedData instanceof FlipperArray) {
plugin.getConnection().send(method, (FlipperArray) parsedData); plugin.getConnection().send(method, (FlipperArray) parsedData);
} else { } else {
@@ -134,14 +138,17 @@ public class FlipperReactNativeJavaScriptPluginManager {
} }
private static Object /* FlipperArray | FlipperObject */ parseJSON(String json) { private static Object /* FlipperArray | FlipperObject */ parseJSON(String json) {
if (json == null) {
return null;
}
// returns either a FlipperObject or Flipper array, pending the data // returns either a FlipperObject or Flipper array, pending the data
try { try {
JSONTokener tokener = new JSONTokener(json); JSONTokener tokener = new JSONTokener(json);
if (tokener.nextClean() == '[') { char firstChar = tokener.nextClean();
tokener.back(); tokener.back();
if (firstChar == '[') {
return new FlipperArray(new JSONArray(tokener)); return new FlipperArray(new JSONArray(tokener));
} else { } else {
tokener.back();
return new FlipperObject(new JSONObject(tokener)); return new FlipperObject(new JSONObject(tokener));
} }
} catch (JSONException e) { } catch (JSONException e) {

View File

@@ -32,12 +32,14 @@ public class FlipperReactNativeJavaScriptReceiver implements FlipperReceiver {
@Override @Override
public void onReceive(FlipperObject params, FlipperResponder responder) throws Exception { public void onReceive(FlipperObject params, FlipperResponder responder) throws Exception {
String responderId = manager.createResponderId(responder);
WritableMap eventData = Arguments.createMap(); WritableMap eventData = Arguments.createMap();
eventData.putString("plugin", plugin); eventData.putString("plugin", plugin);
eventData.putString("method", method); eventData.putString("method", method);
eventData.putString("params", params.toJsonString()); 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); module.sendJSEvent("react-native-flipper-receive-event", eventData);
} }
} }

View File

@@ -17,6 +17,20 @@ export default Flipper;
const listeners = {}; // plugin#method -> callback const listeners = {}; // plugin#method -> callback
const plugins = {}; // plugin -> Plugin 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 { class Connection {
connected; connected;
pluginId; pluginId;
@@ -30,15 +44,20 @@ class Connection {
if (!this.connected) { if (!this.connected) {
throw new Error('Cannot send data, not connected'); throw new Error('Cannot send data, not connected');
} }
assertSerializable(data);
Flipper.send(this.pluginId, method, JSON.stringify(data)); Flipper.send(this.pluginId, method, JSON.stringify(data));
} }
reportErrorWithMetadata(reason, stackTrace) { reportErrorWithMetadata(reason, stackTrace) {
Flipper.reportErrorWithMetadata(this.pluginId, reason, stackTrace); Flipper.reportErrorWithMetadata(
this.pluginId,
'' + reason,
'' + stackTrace,
);
} }
reportError(error) { reportError(error) {
Flipper.reportError(this.pluginId, error); Flipper.reportError(this.pluginId, '' + error);
} }
receive(method, listener) { receive(method, listener) {
@@ -59,6 +78,7 @@ class Responder {
} }
success(response) { success(response) {
assertSerializable(response);
Flipper.respondSuccess( Flipper.respondSuccess(
this.responderId, this.responderId,
response == null ? null : JSON.stringify(response), response == null ? null : JSON.stringify(response),
@@ -66,15 +86,13 @@ class Responder {
} }
error(response) { error(response) {
assertSerializable(response);
Flipper.respondError(this.responderId, JSON.stringify(response)); Flipper.respondError(this.responderId, JSON.stringify(response));
} }
} }
function startEventListeners() { function startEventListeners() {
const emitter = new NativeEventEmitter(Flipper); 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 => { emitter.addListener('react-native-flipper-plugin-connect', event => {
const {plugin} = event; const {plugin} = event;
@@ -98,7 +116,8 @@ function startEventListeners() {
const {plugin, method, params, responderId} = event; const {plugin, method, params, responderId} = event;
const key = plugin + '#' + method; const key = plugin + '#' + method;
if (listeners[key]) { if (listeners[key]) {
const responder = new Responder(responderId); const responder =
responderId != null ? new Responder(responderId) : undefined;
listeners[key](JSON.parse(params), responder); listeners[key](JSON.parse(params), responder);
} }
}); });