Remove additional JSON encoding for native->JS communication
Reviewed By: mhorowitz Differential Revision: D3857323 fbshipit-source-id: 4386cc107b8a1425ecb7297b0f659f6c47f01a78
This commit is contained in:
Родитель
bd4cd6ea5d
Коммит
145109fc6d
|
@ -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,9 +13,7 @@ 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 {};
|
||||||
}
|
}
|
||||||
|
@ -30,25 +28,25 @@ std::vector<MethodCall> parseMethodCalls(const std::string& json) throw(std::inv
|
||||||
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,3 +1,13 @@
|
||||||
|
TEST_SRCS = [
|
||||||
|
'CxxMessageQueueTest.cpp',
|
||||||
|
'jsarg_helpers.cpp',
|
||||||
|
'jscexecutor.cpp',
|
||||||
|
'jsclogging.cpp',
|
||||||
|
'methodcall.cpp',
|
||||||
|
'value.cpp',
|
||||||
|
]
|
||||||
|
|
||||||
|
if THIS_IS_FBANDROID:
|
||||||
include_defs('//ReactAndroid/DEFS')
|
include_defs('//ReactAndroid/DEFS')
|
||||||
include_defs('//ReactAndroid/TEST_DEFS')
|
include_defs('//ReactAndroid/TEST_DEFS')
|
||||||
|
|
||||||
|
@ -5,13 +15,7 @@ jni_instrumentation_test_lib(
|
||||||
name = 'tests',
|
name = 'tests',
|
||||||
class_under_test = 'com/facebook/react/XplatBridgeTest',
|
class_under_test = 'com/facebook/react/XplatBridgeTest',
|
||||||
soname = 'libxplat-bridge.so',
|
soname = 'libxplat-bridge.so',
|
||||||
srcs = [
|
srcs = TEST_SRCS,
|
||||||
'CxxMessageQueueTest.cpp',
|
|
||||||
'value.cpp',
|
|
||||||
'methodcall.cpp',
|
|
||||||
'jsclogging.cpp',
|
|
||||||
'jscexecutor.cpp',
|
|
||||||
],
|
|
||||||
compiler_flags = [
|
compiler_flags = [
|
||||||
'-fexceptions',
|
'-fexceptions',
|
||||||
],
|
],
|
||||||
|
@ -22,3 +26,17 @@ jni_instrumentation_test_lib(
|
||||||
],
|
],
|
||||||
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
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче