From 4d931d529ef15f3fd4eeafa577c537e16670c4c2 Mon Sep 17 00:00:00 2001 From: David Aurelio Date: Thu, 3 May 2018 08:38:01 -0700 Subject: [PATCH] Add native delta client for Metro Summary: Adds C++ delta client that keeps modules in memory, and can be used as a RAM bundle. For now, this client expects a `folly::dynamic` object as payload for patches, i.e. the JSON response retrieved from Metro needs to be parsed with `folly::parseJson` by the caller. In the future, we will replace JSON with a streaming friendly binary format. Reviewed By: fromcelticpark Differential Revision: D7845136 fbshipit-source-id: f003f98a2607c8354c427a7e60e01e19e20295b1 --- .../jni/react/jni/JniJSModulesUnbundle.cpp | 2 +- ReactCommon/cxxreact/Android.mk | 1 + ReactCommon/cxxreact/BUCK | 1 + ReactCommon/cxxreact/JSDeltaBundleClient.cpp | 72 +++++++++++++++++++ ReactCommon/cxxreact/JSDeltaBundleClient.h | 42 +++++++++++ ReactCommon/cxxreact/JSModulesUnbundle.h | 4 ++ 6 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 ReactCommon/cxxreact/JSDeltaBundleClient.cpp create mode 100644 ReactCommon/cxxreact/JSDeltaBundleClient.h diff --git a/ReactAndroid/src/main/jni/react/jni/JniJSModulesUnbundle.cpp b/ReactAndroid/src/main/jni/react/jni/JniJSModulesUnbundle.cpp index be00bfdcb..504f937c0 100644 --- a/ReactAndroid/src/main/jni/react/jni/JniJSModulesUnbundle.cpp +++ b/ReactAndroid/src/main/jni/react/jni/JniJSModulesUnbundle.cpp @@ -82,7 +82,7 @@ JSModulesUnbundle::Module JniJSModulesUnbundle::getModule(uint32_t moduleId) con buffer = static_cast(AAsset_getBuffer(asset.get())); } if (buffer == nullptr) { - throw ModuleNotFound("Module not found: " + sourceUrl); + throw ModuleNotFound(moduleId); } return {sourceUrl, std::string(buffer, AAsset_getLength(asset.get()))}; } diff --git a/ReactCommon/cxxreact/Android.mk b/ReactCommon/cxxreact/Android.mk index 1c22c761e..8efb42a3a 100644 --- a/ReactCommon/cxxreact/Android.mk +++ b/ReactCommon/cxxreact/Android.mk @@ -17,6 +17,7 @@ LOCAL_SRC_FILES := \ JSCSamplingProfiler.cpp \ JSCTracing.cpp \ JSCUtils.cpp \ + JSDeltaBundleClient.cpp \ JSExecutor.cpp \ JSIndexedRAMBundle.cpp \ MethodCall.cpp \ diff --git a/ReactCommon/cxxreact/BUCK b/ReactCommon/cxxreact/BUCK index 515ff8cb3..c5b958918 100644 --- a/ReactCommon/cxxreact/BUCK +++ b/ReactCommon/cxxreact/BUCK @@ -92,6 +92,7 @@ CXXREACT_PUBLIC_HEADERS = [ "JSCExecutor.h", "JSCNativeModules.h", "JSCUtils.h", + "JSDeltaBundleClient.h", "JSIndexedRAMBundle.h", "JSModulesUnbundle.h", "MessageQueueThread.h", diff --git a/ReactCommon/cxxreact/JSDeltaBundleClient.cpp b/ReactCommon/cxxreact/JSDeltaBundleClient.cpp new file mode 100644 index 000000000..8c082d04c --- /dev/null +++ b/ReactCommon/cxxreact/JSDeltaBundleClient.cpp @@ -0,0 +1,72 @@ +#include "JSDeltaBundleClient.h" + +#include + +#include + +namespace facebook { +namespace react { + +namespace { + std::string startupCode(const folly::dynamic *pre, const folly::dynamic *post) { + std::ostringstream startupCode; + + for (auto section : {pre, post}) { + if (section != nullptr) { + for (folly::dynamic pair : *section) { + startupCode << pair[1].getString() << '\n'; + } + } + } + + return startupCode.str(); + } +} // namespace + +void JSDeltaBundleClient::patch(const folly::dynamic& delta) { + auto const reset = delta.get_ptr("reset"); + if (reset != nullptr && reset->asBool()) { + clear(); + } + + auto const pre = delta.get_ptr("pre"); + auto const post = delta.get_ptr("post"); + + if ((pre != nullptr && pre->size() > 0) || (post != nullptr && post->size() > 0)) { + startupCode_ = startupCode(pre, post); + } + + const folly::dynamic *modules = delta.get_ptr("delta"); + if (modules != nullptr) { + for (const folly::dynamic pair : *modules) { + auto id = pair[0].getInt(); + auto module = pair[1]; + if (module.isNull()) { + modules_.erase(id); + } else { + modules_.emplace(id, module.getString()); + } + } + } +} + +JSModulesUnbundle::Module JSDeltaBundleClient::getModule(uint32_t moduleId) const { + auto search = modules_.find(moduleId); + if (search != modules_.end()) { + return {folly::to(search->first, ".js"), search->second}; + } + + throw JSModulesUnbundle::ModuleNotFound(moduleId); +} + +std::unique_ptr JSDeltaBundleClient::getStartupCode() const { + return folly::make_unique(startupCode_); +} + +void JSDeltaBundleClient::clear() { + modules_.clear(); + startupCode_.clear(); +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/cxxreact/JSDeltaBundleClient.h b/ReactCommon/cxxreact/JSDeltaBundleClient.h new file mode 100644 index 000000000..107bd3988 --- /dev/null +++ b/ReactCommon/cxxreact/JSDeltaBundleClient.h @@ -0,0 +1,42 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +namespace facebook { +namespace react { + +class JSDeltaBundleClient { +public: + void patch(const folly::dynamic& delta); + JSModulesUnbundle::Module getModule(uint32_t moduleId) const; + std::unique_ptr getStartupCode() const; + void clear(); + +private: + std::unordered_map modules_; + std::string startupCode_; +}; + +class JSDeltaBundleClientRAMBundle : public JSModulesUnbundle { +public: + JSDeltaBundleClientRAMBundle( + std::shared_ptr client) : client_(client) {} + + Module getModule(uint32_t moduleId) const override { + return client_->getModule(moduleId); + } +private: + const std::shared_ptr client_; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/cxxreact/JSModulesUnbundle.h b/ReactCommon/cxxreact/JSModulesUnbundle.h index a04d5808c..bbf3a6c8d 100644 --- a/ReactCommon/cxxreact/JSModulesUnbundle.h +++ b/ReactCommon/cxxreact/JSModulesUnbundle.h @@ -6,6 +6,7 @@ #include #include +#include #include namespace facebook { @@ -21,7 +22,10 @@ class JSModulesUnbundle : noncopyable { */ public: class ModuleNotFound : public std::out_of_range { + public: using std::out_of_range::out_of_range; + ModuleNotFound(uint32_t moduleId) : std::out_of_range::out_of_range( + folly::to("Module not found: ", moduleId)) {} }; struct Module { std::string name;