Remove additional JSON encoding for native->JS communication

Reviewed By: mhorowitz

Differential Revision: D3857323

fbshipit-source-id: 4386cc107b8a1425ecb7297b0f659f6c47f01a78
This commit is contained in:
Pieter De Baets 2016-09-19 04:43:09 -07:00 коммит произвёл Facebook Github Bot 2
Родитель bd4cd6ea5d
Коммит 145109fc6d
16 изменённых файлов: 224 добавлений и 100 удалений

Просмотреть файл

@ -12,12 +12,7 @@
const MessageQueue = require('MessageQueue'); const MessageQueue = require('MessageQueue');
const serializeNativeParams = typeof global.__fbBatchedBridgeSerializeNativeParams !== 'undefined'; const BatchedBridge = new MessageQueue(() => global.__fbBatchedBridgeConfig);
const BatchedBridge = new MessageQueue(
() => global.__fbBatchedBridgeConfig,
serializeNativeParams
);
// TODO: Move these around to solve the cycle in a cleaner way. // TODO: Move these around to solve the cycle in a cleaner way.

Просмотреть файл

@ -19,6 +19,7 @@ const JSTimersExecution = require('JSTimersExecution');
const invariant = require('fbjs/lib/invariant'); const invariant = require('fbjs/lib/invariant');
const keyMirror = require('fbjs/lib/keyMirror'); const keyMirror = require('fbjs/lib/keyMirror');
const deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev');
const stringifySafe = require('stringifySafe'); const stringifySafe = require('stringifySafe');
const MODULE_IDS = 0; const MODULE_IDS = 0;
@ -52,7 +53,7 @@ type Config = {
}; };
class MessageQueue { class MessageQueue {
constructor(configProvider: () => Config, serializeNativeParams: boolean) { constructor(configProvider: () => Config) {
this._callableModules = {}; this._callableModules = {};
this._queue = [[], [], [], 0]; this._queue = [[], [], [], 0];
this._callbacks = []; this._callbacks = [];
@ -60,7 +61,6 @@ class MessageQueue {
this._callID = 0; this._callID = 0;
this._lastFlush = 0; this._lastFlush = 0;
this._eventLoopStartTime = new Date().getTime(); this._eventLoopStartTime = new Date().getTime();
this._serializeNativeParams = serializeNativeParams;
if (__DEV__) { if (__DEV__) {
this._debugInfo = {}; this._debugInfo = {};
@ -165,7 +165,7 @@ class MessageQueue {
__nativeCall(module, method, params, onFail, onSucc) { __nativeCall(module, method, params, onFail, onSucc) {
if (onFail || onSucc) { if (onFail || onSucc) {
if (__DEV__) { if (__DEV__) {
let callId = this._callbackID >> 1; const callId = this._callbackID >> 1;
this._debugInfo[callId] = [module, method]; this._debugInfo[callId] = [module, method];
if (callId > DEBUG_INFO_LIMIT) { if (callId > DEBUG_INFO_LIMIT) {
delete this._debugInfo[callId - DEBUG_INFO_LIMIT]; delete this._debugInfo[callId - DEBUG_INFO_LIMIT];
@ -186,8 +186,14 @@ class MessageQueue {
this._queue[MODULE_IDS].push(module); this._queue[MODULE_IDS].push(module);
this._queue[METHOD_IDS].push(method); this._queue[METHOD_IDS].push(method);
const preparedParams = this._serializeNativeParams ? JSON.stringify(params) : params; if (__DEV__) {
this._queue[PARAMS].push(preparedParams); // Any params sent over the bridge should be encodable as JSON
JSON.stringify(params);
// The params object should not be mutated after being queued
deepFreezeAndThrowOnMutationInDev(params);
}
this._queue[PARAMS].push(params);
const now = new Date().getTime(); const now = new Date().getTime();
if (global.nativeFlushQueueImmediate && if (global.nativeFlushQueueImmediate &&

Просмотреть файл

@ -11,9 +11,7 @@
'use strict'; 'use strict';
const BatchedBridge = require('BatchedBridge'); const BatchedBridge = require('BatchedBridge');
const fbjsPerformanceNow = require('fbjs/lib/performanceNow'); const performanceNow = global.nativePerformanceNow || require('fbjs/lib/performanceNow');
const performanceNow = global.nativePerformanceNow || fbjsPerformanceNow;
var timespans = {}; var timespans = {};
var extras = {}; var extras = {};

Просмотреть файл

@ -58,9 +58,6 @@ ProxyExecutor::ProxyExecutor(jni::global_ref<jobject>&& executorInstance,
setGlobalVariable( setGlobalVariable(
"__fbBatchedBridgeConfig", "__fbBatchedBridgeConfig",
folly::make_unique<JSBigStdString>(detail::toStdString(folly::toJson(config)))); folly::make_unique<JSBigStdString>(detail::toStdString(folly::toJson(config))));
setGlobalVariable(
"__fbBatchedBridgeSerializeNativeParams",
folly::make_unique<JSBigStdString>("1"));
} }
ProxyExecutor::~ProxyExecutor() { ProxyExecutor::~ProxyExecutor() {
@ -95,7 +92,7 @@ void ProxyExecutor::callFunction(const std::string& moduleId, const std::string&
std::move(arguments), std::move(arguments),
}; };
std::string result = executeJSCallWithProxy(m_executor.get(), "callFunctionReturnFlushedQueue", std::move(call)); std::string result = executeJSCallWithProxy(m_executor.get(), "callFunctionReturnFlushedQueue", std::move(call));
m_delegate->callNativeModules(*this, result, true); m_delegate->callNativeModules(*this, folly::parseJson(result), true);
} }
void ProxyExecutor::invokeCallback(const double callbackId, const folly::dynamic& arguments) { void ProxyExecutor::invokeCallback(const double callbackId, const folly::dynamic& arguments) {
@ -104,7 +101,7 @@ void ProxyExecutor::invokeCallback(const double callbackId, const folly::dynamic
std::move(arguments) std::move(arguments)
}; };
std::string result = executeJSCallWithProxy(m_executor.get(), "invokeCallbackAndReturnFlushedQueue", std::move(call)); std::string result = executeJSCallWithProxy(m_executor.get(), "invokeCallbackAndReturnFlushedQueue", std::move(call));
m_delegate->callNativeModules(*this, result, true); m_delegate->callNativeModules(*this, folly::parseJson(result), true);
} }
void ProxyExecutor::setGlobalVariable(std::string propName, void ProxyExecutor::setGlobalVariable(std::string propName,

Просмотреть файл

@ -53,6 +53,9 @@ elif THIS_IS_FBOBJC:
frameworks = [ frameworks = [
'$SDKROOT/System/Library/Frameworks/JavaScriptCore.framework', '$SDKROOT/System/Library/Frameworks/JavaScriptCore.framework',
], ],
tests = [
react_native_xplat_target('cxxreact/tests:tests')
],
**kwargs_add( **kwargs_add(
kwargs, kwargs,
preprocessor_flags = DEBUG_PREPROCESSOR_FLAGS, preprocessor_flags = DEBUG_PREPROCESSOR_FLAGS,

Просмотреть файл

@ -45,7 +45,7 @@ class ExecutorDelegate {
virtual std::vector<std::string> moduleNames() = 0; virtual std::vector<std::string> moduleNames() = 0;
virtual folly::dynamic getModuleConfig(const std::string& name) = 0; virtual folly::dynamic getModuleConfig(const std::string& name) = 0;
virtual void callNativeModules( virtual void callNativeModules(
JSExecutor& executor, std::string callJSON, bool isEndOfBatch) = 0; JSExecutor& executor, folly::dynamic&& calls, bool isEndOfBatch) = 0;
virtual MethodCallResult callSerializableNativeHook( virtual MethodCallResult callSerializableNativeHook(
JSExecutor& executor, unsigned int moduleId, unsigned int methodId, folly::dynamic&& args) = 0; JSExecutor& executor, unsigned int moduleId, unsigned int methodId, folly::dynamic&& args) = 0;
}; };

Просмотреть файл

@ -56,7 +56,7 @@ class Instance {
void handleMemoryPressureCritical(); void handleMemoryPressureCritical();
private: private:
void callNativeModules(ExecutorToken token, const std::string& calls, bool isEndOfBatch); void callNativeModules(ExecutorToken token, folly::dynamic&& calls, bool isEndOfBatch);
std::shared_ptr<InstanceCallback> callback_; std::shared_ptr<InstanceCallback> callback_;
std::unique_ptr<NativeToJsBridge> nativeToJsBridge_; std::unique_ptr<NativeToJsBridge> nativeToJsBridge_;

Просмотреть файл

@ -124,9 +124,6 @@ JSCExecutor::JSCExecutor(std::shared_ptr<ExecutorDelegate> delegate,
setGlobalVariable( setGlobalVariable(
"__fbBatchedBridgeConfig", "__fbBatchedBridgeConfig",
folly::make_unique<JSBigStdString>(detail::toStdString(folly::toJson(config)))); folly::make_unique<JSBigStdString>(detail::toStdString(folly::toJson(config))));
setGlobalVariable(
"__fbBatchedBridgeSerializeNativeParams",
folly::make_unique<JSBigStdString>(""));
} }
JSCExecutor::JSCExecutor( JSCExecutor::JSCExecutor(
@ -349,7 +346,7 @@ void JSCExecutor::callNativeModules(Value&& value) {
SystraceSection s("JSCExecutor::callNativeModules"); SystraceSection s("JSCExecutor::callNativeModules");
try { try {
auto calls = value.toJSONString(); auto calls = value.toJSONString();
m_delegate->callNativeModules(*this, std::move(calls), true); m_delegate->callNativeModules(*this, folly::parseJson(calls), true);
} catch (...) { } catch (...) {
std::string message = "Error in callNativeModules()"; std::string message = "Error in callNativeModules()";
try { try {
@ -471,8 +468,9 @@ void JSCExecutor::handleMemoryPressureCritical() {
#endif #endif
} }
void JSCExecutor::flushQueueImmediate(std::string queueJSON) { void JSCExecutor::flushQueueImmediate(Value&& queue) {
m_delegate->callNativeModules(*this, std::move(queueJSON), false); auto queueStr = queue.toJSONString();
m_delegate->callNativeModules(*this, folly::parseJson(queueStr), false);
} }
void JSCExecutor::loadModule(uint32_t moduleId) { void JSCExecutor::loadModule(uint32_t moduleId) {
@ -649,8 +647,7 @@ JSValueRef JSCExecutor::nativeFlushQueueImmediate(
throw std::invalid_argument("Got wrong number of args"); throw std::invalid_argument("Got wrong number of args");
} }
std::string resStr = Value(m_context, arguments[0]).toJSONString(); flushQueueImmediate(Value(m_context, arguments[0]));
flushQueueImmediate(std::move(resStr));
return JSValueMakeUndefined(m_context); return JSValueMakeUndefined(m_context);
} }

Просмотреть файл

@ -119,7 +119,7 @@ private:
void bindBridge() throw(JSException); void bindBridge() throw(JSException);
void callNativeModules(Value&&); void callNativeModules(Value&&);
void flush(); void flush();
void flushQueueImmediate(std::string queueJSON); void flushQueueImmediate(Value&&);
void loadModule(uint32_t moduleId); void loadModule(uint32_t moduleId);
int addWebWorker(std::string scriptURL, JSValueRef workerRef, JSValueRef globalObjRef); int addWebWorker(std::string scriptURL, JSValueRef workerRef, JSValueRef globalObjRef);

Просмотреть файл

@ -13,42 +13,40 @@ namespace react {
#define REQUEST_PARAMSS 2 #define REQUEST_PARAMSS 2
#define REQUEST_CALLID 3 #define REQUEST_CALLID 3
std::vector<MethodCall> parseMethodCalls(const std::string& json) throw(std::invalid_argument) { std::vector<MethodCall> parseMethodCalls(folly::dynamic&& jsonData) throw(std::invalid_argument) {
folly::dynamic jsonData = folly::parseJson(json);
if (jsonData.isNull()) { if (jsonData.isNull()) {
return {}; return {};
} }
if (!jsonData.isArray()) { if (!jsonData.isArray()) {
throw std::invalid_argument( throw std::invalid_argument(
folly::to<std::string>("Did not get valid calls back from JS: ", jsonData.typeName())); folly::to<std::string>("Did not get valid calls back from JS: ", jsonData.typeName()));
} }
if (jsonData.size() < REQUEST_PARAMSS + 1) { if (jsonData.size() < REQUEST_PARAMSS + 1) {
throw std::invalid_argument( throw std::invalid_argument(
folly::to<std::string>("Did not get valid calls back from JS: size == ", jsonData.size())); folly::to<std::string>("Did not get valid calls back from JS: size == ", jsonData.size()));
} }
auto moduleIds = jsonData[REQUEST_MODULE_IDS]; auto& moduleIds = jsonData[REQUEST_MODULE_IDS];
auto methodIds = jsonData[REQUEST_METHOD_IDS]; auto& methodIds = jsonData[REQUEST_METHOD_IDS];
auto params = jsonData[REQUEST_PARAMSS]; auto& params = jsonData[REQUEST_PARAMSS];
int callId = -1; int callId = -1;
if (!moduleIds.isArray() || !methodIds.isArray() || !params.isArray()) { if (!moduleIds.isArray() || !methodIds.isArray() || !params.isArray()) {
throw std::invalid_argument( throw std::invalid_argument(
folly::to<std::string>("Did not get valid calls back from JS: ", json.c_str())); folly::to<std::string>("Did not get valid calls back from JS: ", folly::toJson(jsonData)));
} }
if (moduleIds.size() != methodIds.size() || moduleIds.size() != params.size()) { if (moduleIds.size() != methodIds.size() || moduleIds.size() != params.size()) {
throw std::invalid_argument( throw std::invalid_argument(
folly::to<std::string>("Did not get valid calls back from JS: ", json.c_str())); folly::to<std::string>("Did not get valid calls back from JS: ", folly::toJson(jsonData)));
} }
if (jsonData.size() > REQUEST_CALLID) { if (jsonData.size() > REQUEST_CALLID) {
if (!jsonData[REQUEST_CALLID].isInt()) { if (!jsonData[REQUEST_CALLID].isInt()) {
throw std::invalid_argument( throw std::invalid_argument(
folly::to<std::string>("Did not get valid calls back from JS: %s", json.c_str())); folly::to<std::string>("Did not get valid calls back from JS: %s", folly::toJson(jsonData)));
} else { } else {
callId = jsonData[REQUEST_CALLID].getInt(); callId = jsonData[REQUEST_CALLID].getInt();
} }
@ -56,20 +54,15 @@ std::vector<MethodCall> parseMethodCalls(const std::string& json) throw(std::inv
std::vector<MethodCall> methodCalls; std::vector<MethodCall> methodCalls;
for (size_t i = 0; i < moduleIds.size(); i++) { for (size_t i = 0; i < moduleIds.size(); i++) {
if (!params[i].isString()) { if (!params[i].isArray()) {
throw std::invalid_argument( throw std::invalid_argument(
folly::to<std::string>("Call argument isn't a string")); folly::to<std::string>("Call argument isn't an array"));
}
auto paramsValue = folly::parseJson(params[i].asString());
if (!paramsValue.isArray()) {
throw std::invalid_argument(
folly::to<std::string>("Parsed params isn't an array"));
} }
methodCalls.emplace_back( methodCalls.emplace_back(
moduleIds[i].getInt(), moduleIds[i].getInt(),
methodIds[i].getInt(), methodIds[i].getInt(),
std::move(paramsValue), std::move(params[i]),
callId); callId);
// only incremement callid if contains valid callid as callid is optional // only incremement callid if contains valid callid as callid is optional

Просмотреть файл

@ -24,6 +24,6 @@ struct MethodCall {
, callId(cid) {} , callId(cid) {}
}; };
std::vector<MethodCall> parseMethodCalls(const std::string& json) throw(std::invalid_argument); std::vector<MethodCall> parseMethodCalls(folly::dynamic&& calls) throw(std::invalid_argument);
} } } }

Просмотреть файл

@ -53,13 +53,13 @@ public:
} }
void callNativeModules( void callNativeModules(
JSExecutor& executor, std::string callJSON, bool isEndOfBatch) override { JSExecutor& executor, folly::dynamic&& calls, bool isEndOfBatch) override {
ExecutorToken token = m_nativeToJs->getTokenForExecutor(executor); ExecutorToken token = m_nativeToJs->getTokenForExecutor(executor);
m_nativeQueue->runOnQueue([this, token, callJSON=std::move(callJSON), isEndOfBatch] { m_nativeQueue->runOnQueue([this, token, calls=std::move(calls), isEndOfBatch] () mutable {
// An exception anywhere in here stops processing of the batch. This // An exception anywhere in here stops processing of the batch. This
// was the behavior of the Android bridge, and since exception handling // was the behavior of the Android bridge, and since exception handling
// terminates the whole bridge, there's not much point in continuing. // terminates the whole bridge, there's not much point in continuing.
for (auto& call : react::parseMethodCalls(callJSON)) { for (auto& call : react::parseMethodCalls(std::move(calls))) {
m_registry->callNativeMethod( m_registry->callNativeMethod(
token, call.moduleId, call.methodId, std::move(call.arguments), call.callId); token, call.moduleId, call.methodId, std::move(call.arguments), call.callId);
} }

Просмотреть файл

@ -1,24 +1,42 @@
include_defs('//ReactAndroid/DEFS') TEST_SRCS = [
include_defs('//ReactAndroid/TEST_DEFS') 'CxxMessageQueueTest.cpp',
'jsarg_helpers.cpp',
'jscexecutor.cpp',
'jsclogging.cpp',
'methodcall.cpp',
'value.cpp',
]
jni_instrumentation_test_lib( if THIS_IS_FBANDROID:
name = 'tests', include_defs('//ReactAndroid/DEFS')
class_under_test = 'com/facebook/react/XplatBridgeTest', include_defs('//ReactAndroid/TEST_DEFS')
soname = 'libxplat-bridge.so',
srcs = [ jni_instrumentation_test_lib(
'CxxMessageQueueTest.cpp', name = 'tests',
'value.cpp', class_under_test = 'com/facebook/react/XplatBridgeTest',
'methodcall.cpp', soname = 'libxplat-bridge.so',
'jsclogging.cpp', srcs = TEST_SRCS,
'jscexecutor.cpp', compiler_flags = [
], '-fexceptions',
compiler_flags = [ ],
'-fexceptions', deps = [
], '//native/third-party/android-ndk:android',
deps = [ '//xplat/third-party/gmock:gtest',
'//native/third-party/android-ndk:android', react_native_xplat_target('cxxreact:bridge'),
'//xplat/third-party/gmock:gtest', ],
react_native_xplat_target('cxxreact:bridge'), visibility = ['//instrumentation_tests/...'],
], )
visibility = ['//instrumentation_tests/...'],
) if THIS_IS_FBOBJC:
fb_xplat_cxx_test(
name = 'tests',
srcs = TEST_SRCS,
compiler_flags = [
'-fexceptions',
],
deps = [
'//xplat/third-party/gmock:gtest',
react_native_xplat_target('cxxreact:bridge'),
],
visibility = [react_native_xplat_target('cxxreact/...')],
)

Просмотреть файл

@ -0,0 +1,101 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include <cxxreact/JsArgumentHelpers.h>
#include <folly/dynamic.h>
#include <gtest/gtest.h>
#include <algorithm>
#include <vector>
using namespace std;
using namespace folly;
using namespace facebook::xplat;
#define EXPECT_JSAE(statement, exstr) do { \
try { \
statement; \
FAIL() << "Expected JsArgumentException(" << (exstr) << ") not thrown"; \
} catch (const JsArgumentException& ex) { \
EXPECT_EQ(ex.what(), std::string(exstr)); \
} \
} while(0) // let any other exception escape, gtest will deal.
TEST(JsArgumentHelpersTest, args) {
const bool aBool = true;
const int64_t anInt = 17;
const double aDouble = 3.14;
const string aString = "word";
const dynamic anArray = dynamic::array("a", "b", "c");
const dynamic anObject = dynamic::object("k1", "v1")("k2", "v2");
const string aNumericString = to<string>(anInt);
folly::dynamic args = dynamic::array(aBool, anInt, aDouble, aString, anArray, anObject, aNumericString);
EXPECT_EQ(jsArgAsBool(args, 0), aBool);
EXPECT_EQ(jsArgAsInt(args, 1), anInt);
EXPECT_EQ(jsArgAsDouble(args, 2), aDouble);
EXPECT_EQ(jsArgAsString(args, 3), aString);
EXPECT_EQ(jsArgAsArray(args, 4), anArray);
EXPECT_EQ(jsArgAsObject(args, 5), anObject);
// const args
const folly::dynamic& cargs = args;
const folly::dynamic& a4 = jsArgAsArray(cargs, 4);
EXPECT_EQ(a4, anArray);
EXPECT_EQ(jsArgAsObject(cargs, 5), anObject);
// helpers returning dynamic should return same object without copying
EXPECT_EQ(&jsArgAsArray(args, 4), &(args[4]));
EXPECT_EQ(&jsArgAsArray(cargs, 4), &(args[4]));
// dynamics returned for mutable args should be mutable. The test is that
// this compiles.
jsArgAsArray(args, 4)[2] = "d";
jsArgAsArray(args, 4)[2] = "c";
// These fail to compile due to constness.
// jsArgAsArray(cargs, 4)[2] = "d";
// jsArgAsArray(cargs, 4)[2] = "c";
// ref-qualified member function tests
EXPECT_EQ(jsArgN(args, 3, &folly::dynamic::getString), aString);
EXPECT_EQ(jsArg(args[3], &folly::dynamic::getString), aString);
// conversions
EXPECT_EQ(jsArgAsDouble(args, 1), anInt * 1.0);
EXPECT_EQ(jsArgAsString(args, 1), aNumericString);
EXPECT_EQ(jsArgAsInt(args, 6), anInt);
// Test exception messages.
// out_of_range
EXPECT_JSAE(jsArgAsBool(args, 7),
"JavaScript provided 7 arguments for C++ method which references at least "
"8 arguments: out of range in dynamic array");
// Conv range_error (invalid value conversion)
const std::string exhead = "Could not convert argument 3 to required type: ";
const std::string extail = ": Invalid leading character: \"word\"";
try {
jsArgAsInt(args, 3);
FAIL() << "Expected JsArgumentException(" << exhead << "..." << extail << ") not thrown";
} catch (const JsArgumentException& ex) {
const std::string exwhat = ex.what();
EXPECT_GT(exwhat.size(), exhead.size());
EXPECT_GT(exwhat.size(), extail.size());
EXPECT_TRUE(std::equal(exhead.cbegin(), exhead.cend(), exwhat.cbegin()))
<< "JsArgumentException('" << exwhat << "') does not begin with '" << exhead << "'";
EXPECT_TRUE(std::equal(extail.crbegin(), extail.crend(), exwhat.crbegin()))
<< "JsArgumentException('" << exwhat << "') does not end with '" << extail << "'";
}
// inconvertible types
EXPECT_JSAE(jsArgAsArray(args, 2),
"Argument 3 of type double is not required type Array");
EXPECT_JSAE(jsArgAsInt(args, 4),
"Error converting javascript arg 4 to C++: "
"TypeError: expected dynamic type `int/double/bool/string', but had type `array'");
// type predicate failure
EXPECT_JSAE(jsArgAsObject(args, 4),
"Argument 5 of type array is not required type Object");
}

Просмотреть файл

@ -2,13 +2,15 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <cxxreact/MethodCall.h> #include <cxxreact/MethodCall.h>
#include <folly/json.h>
using namespace facebook; using namespace facebook;
using namespace facebook::react; using namespace facebook::react;
using namespace folly;
TEST(parseMethodCalls, SingleReturnCallNoArgs) { TEST(parseMethodCalls, SingleReturnCallNoArgs) {
auto jsText = "[[7],[3],[\"[]\"]]"; auto jsText = "[[7],[3],[[]]]";
auto returnedCalls = parseMethodCalls(jsText); auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
ASSERT_EQ(1, returnedCalls.size()); ASSERT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0]; auto returnedCall = returnedCalls[0];
ASSERT_EQ(0, returnedCall.arguments.size()); ASSERT_EQ(0, returnedCall.arguments.size());
@ -18,31 +20,40 @@ TEST(parseMethodCalls, SingleReturnCallNoArgs) {
TEST(parseMethodCalls, InvalidReturnFormat) { TEST(parseMethodCalls, InvalidReturnFormat) {
try { try {
parseMethodCalls("{\"foo\":1}"); auto input = dynamic::object("foo", 1);
parseMethodCalls(std::move(input));
ADD_FAILURE(); ADD_FAILURE();
} catch (const std::invalid_argument&) { } catch (const std::invalid_argument&) {
// ignored // ignored
} }
try { try {
parseMethodCalls("[{\"foo\":1}]"); auto input = dynamic::array(dynamic::object("foo", 1));
parseMethodCalls(std::move(input));
ADD_FAILURE(); ADD_FAILURE();
} catch (const std::invalid_argument&) { } catch (const std::invalid_argument&) {
// ignored // ignored
} }
try { try {
parseMethodCalls("[1,4,{\"foo\":2}]"); auto input = dynamic::array(1, 4, dynamic::object("foo", 2));
parseMethodCalls(std::move(input));
ADD_FAILURE(); ADD_FAILURE();
} catch (const std::invalid_argument&) { } catch (const std::invalid_argument&) {
// ignored // ignored
} }
try { try {
parseMethodCalls("[[1],[4],{\"foo\":2}]"); auto input = dynamic::array(dynamic::array(1),
dynamic::array(4),
dynamic::object("foo", 2));
parseMethodCalls(std::move(input));
ADD_FAILURE(); ADD_FAILURE();
} catch (const std::invalid_argument&) { } catch (const std::invalid_argument&) {
// ignored // ignored
} }
try { try {
parseMethodCalls("[[1],[4],[]]"); auto input = dynamic::array(dynamic::array(1),
dynamic::array(4),
dynamic::array());
parseMethodCalls(std::move(input));
ADD_FAILURE(); ADD_FAILURE();
} catch (const std::invalid_argument&) { } catch (const std::invalid_argument&) {
// ignored // ignored
@ -50,8 +61,8 @@ TEST(parseMethodCalls, InvalidReturnFormat) {
} }
TEST(parseMethodCalls, NumberReturn) { TEST(parseMethodCalls, NumberReturn) {
auto jsText = "[[0],[0],[\"[\\\"foobar\\\"]\"]]"; auto jsText = "[[0],[0],[[\"foobar\"]]]";
auto returnedCalls = parseMethodCalls(jsText); auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
ASSERT_EQ(1, returnedCalls.size()); ASSERT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0]; auto returnedCall = returnedCalls[0];
ASSERT_EQ(1, returnedCall.arguments.size()); ASSERT_EQ(1, returnedCall.arguments.size());
@ -60,8 +71,8 @@ TEST(parseMethodCalls, NumberReturn) {
} }
TEST(parseMethodCalls, StringReturn) { TEST(parseMethodCalls, StringReturn) {
auto jsText = "[[0],[0],[\"[42.16]\"]]"; auto jsText = "[[0],[0],[[42.16]]]";
auto returnedCalls = parseMethodCalls(jsText); auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
ASSERT_EQ(1, returnedCalls.size()); ASSERT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0]; auto returnedCall = returnedCalls[0];
ASSERT_EQ(1, returnedCall.arguments.size()); ASSERT_EQ(1, returnedCall.arguments.size());
@ -70,8 +81,8 @@ TEST(parseMethodCalls, StringReturn) {
} }
TEST(parseMethodCalls, BooleanReturn) { TEST(parseMethodCalls, BooleanReturn) {
auto jsText = "[[0],[0],[\"[false]\"]]"; auto jsText = "[[0],[0],[[false]]]";
auto returnedCalls = parseMethodCalls(jsText); auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
ASSERT_EQ(1, returnedCalls.size()); ASSERT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0]; auto returnedCall = returnedCalls[0];
ASSERT_EQ(1, returnedCall.arguments.size()); ASSERT_EQ(1, returnedCall.arguments.size());
@ -80,8 +91,8 @@ TEST(parseMethodCalls, BooleanReturn) {
} }
TEST(parseMethodCalls, NullReturn) { TEST(parseMethodCalls, NullReturn) {
auto jsText = "[[0],[0],[\"[null]\"]]"; auto jsText = "[[0],[0],[[null]]]";
auto returnedCalls = parseMethodCalls(jsText); auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
ASSERT_EQ(1, returnedCalls.size()); ASSERT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0]; auto returnedCall = returnedCalls[0];
ASSERT_EQ(1, returnedCall.arguments.size()); ASSERT_EQ(1, returnedCall.arguments.size());
@ -89,8 +100,8 @@ TEST(parseMethodCalls, NullReturn) {
} }
TEST(parseMethodCalls, MapReturn) { TEST(parseMethodCalls, MapReturn) {
auto jsText = "[[0],[0],[\"[{\\\"foo\\\": \\\"hello\\\", \\\"bar\\\": 4.0, \\\"baz\\\": true}]\"]]"; auto jsText = "[[0],[0],[[{\"foo\": \"hello\", \"bar\": 4.0, \"baz\": true}]]]";
auto returnedCalls = parseMethodCalls(jsText); auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
ASSERT_EQ(1, returnedCalls.size()); ASSERT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0]; auto returnedCall = returnedCalls[0];
ASSERT_EQ(1, returnedCall.arguments.size()); ASSERT_EQ(1, returnedCall.arguments.size());
@ -105,8 +116,8 @@ TEST(parseMethodCalls, MapReturn) {
} }
TEST(parseMethodCalls, ArrayReturn) { TEST(parseMethodCalls, ArrayReturn) {
auto jsText = "[[0],[0],[\"[[\\\"foo\\\", 42.0, false]]\"]]"; auto jsText = "[[0],[0],[[[\"foo\", 42.0, false]]]]";
auto returnedCalls = parseMethodCalls(jsText); auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
ASSERT_EQ(1, returnedCalls.size()); ASSERT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0]; auto returnedCall = returnedCalls[0];
ASSERT_EQ(1, returnedCall.arguments.size()); ASSERT_EQ(1, returnedCall.arguments.size());
@ -119,8 +130,8 @@ TEST(parseMethodCalls, ArrayReturn) {
} }
TEST(parseMethodCalls, ReturnMultipleParams) { TEST(parseMethodCalls, ReturnMultipleParams) {
auto jsText = "[[0],[0],[\"[\\\"foo\\\", 14, null, false]\"]]"; auto jsText = "[[0],[0],[[\"foo\", 14, null, false]]]";
auto returnedCalls = parseMethodCalls(jsText); auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
ASSERT_EQ(1, returnedCalls.size()); ASSERT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0]; auto returnedCall = returnedCalls[0];
ASSERT_EQ(4, returnedCall.arguments.size()); ASSERT_EQ(4, returnedCall.arguments.size());
@ -131,7 +142,7 @@ TEST(parseMethodCalls, ReturnMultipleParams) {
} }
TEST(parseMethodCalls, ParseTwoCalls) { TEST(parseMethodCalls, ParseTwoCalls) {
auto jsText = "[[0,0],[1,1],[\"[]\",\"[]\"]]"; auto jsText = "[[0,0],[1,1],[[],[]]]";
auto returnedCalls = parseMethodCalls(jsText); auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
ASSERT_EQ(2, returnedCalls.size()); ASSERT_EQ(2, returnedCalls.size());
} }

Просмотреть файл

@ -2,11 +2,13 @@
#include <string> #include <string>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <folly/json.h> #include <folly/json.h>
#include <cxxreact/Value.h>
#ifdef WITH_FBJSCEXTENSION
#undef ASSERT #undef ASSERT
#include <JavaScriptCore/config.h> #include <JavaScriptCore/config.h>
#include <cxxreact/Value.h>
#include "OpaqueJSString.h" #include "OpaqueJSString.h"
#endif
#include <stdexcept> #include <stdexcept>
@ -58,6 +60,7 @@ TEST(Value, ToJSONString) {
JSGlobalContextRelease(ctx); JSGlobalContextRelease(ctx);
} }
#ifdef WITH_FBJSCEXTENSION
// Just test that handling invalid data doesn't crash. // Just test that handling invalid data doesn't crash.
TEST(Value, FromBadUtf8) { TEST(Value, FromBadUtf8) {
prepare(); prepare();
@ -101,3 +104,5 @@ TEST(Value, BadUtf16) {
v.toJSONString(0); v.toJSONString(0);
JSGlobalContextRelease(ctx); JSGlobalContextRelease(ctx);
} }
#endif