Add ability to load “unbundles” to android
Summary: public This adds the ability to load “unbundles” in RN android apps. Unbundles are created by invoking the packager with the `unbundle` command rather than `bundle`. The code detects usage of an “unbundle” by checking for the existence of a specific asset. Reviewed By: astreet Differential Revision: D2739596 fb-gh-sync-id: d0813c003fe0fa7b47798b970f56707079bfa5d7
This commit is contained in:
parent
0963192b18
commit
17e1ceb543
|
@ -9,6 +9,7 @@ LOCAL_SRC_FILES := \
|
||||||
JSCExecutor.cpp \
|
JSCExecutor.cpp \
|
||||||
JSCHelpers.cpp \
|
JSCHelpers.cpp \
|
||||||
JSCWebWorker.cpp \
|
JSCWebWorker.cpp \
|
||||||
|
JSModulesUnbundle.cpp \
|
||||||
MethodCall.cpp \
|
MethodCall.cpp \
|
||||||
Value.cpp \
|
Value.cpp \
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,13 @@ public:
|
||||||
m_jsExecutor->executeApplicationScript(script, sourceURL);
|
m_jsExecutor->executeApplicationScript(script, sourceURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void loadApplicationUnbundle(
|
||||||
|
JSModulesUnbundle&& unbundle,
|
||||||
|
const std::string& startupCode,
|
||||||
|
const std::string& sourceURL) {
|
||||||
|
m_jsExecutor->loadApplicationUnbundle(std::move(unbundle), startupCode, sourceURL);
|
||||||
|
}
|
||||||
|
|
||||||
void flush() {
|
void flush() {
|
||||||
auto returnedJSON = m_jsExecutor->flush();
|
auto returnedJSON = m_jsExecutor->flush();
|
||||||
m_callback(parseMethodCalls(returnedJSON), true /* = isEndOfBatch */);
|
m_callback(parseMethodCalls(returnedJSON), true /* = isEndOfBatch */);
|
||||||
|
@ -95,6 +102,13 @@ void Bridge::executeApplicationScript(const std::string& script, const std::stri
|
||||||
m_threadState->executeApplicationScript(script, sourceURL);
|
m_threadState->executeApplicationScript(script, sourceURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Bridge::loadApplicationUnbundle(
|
||||||
|
JSModulesUnbundle&& unbundle,
|
||||||
|
const std::string& startupCode,
|
||||||
|
const std::string& sourceURL) {
|
||||||
|
m_threadState->loadApplicationUnbundle(std::move(unbundle), startupCode, sourceURL);
|
||||||
|
}
|
||||||
|
|
||||||
void Bridge::flush() {
|
void Bridge::flush() {
|
||||||
if (*m_destroyed) {
|
if (*m_destroyed) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "Value.h"
|
#include "Value.h"
|
||||||
#include "Executor.h"
|
#include "Executor.h"
|
||||||
#include "MethodCall.h"
|
#include "MethodCall.h"
|
||||||
|
#include "JSModulesUnbundle.h"
|
||||||
|
|
||||||
namespace folly {
|
namespace folly {
|
||||||
|
|
||||||
|
@ -45,7 +46,20 @@ public:
|
||||||
*/
|
*/
|
||||||
void invokeCallback(const double callbackId, const folly::dynamic& args);
|
void invokeCallback(const double callbackId, const folly::dynamic& args);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the JS application from an "bundle", i.e. a JavaScript file that
|
||||||
|
* contains code for all modules and a runtime that resolves and
|
||||||
|
* executes modules.
|
||||||
|
*/
|
||||||
void executeApplicationScript(const std::string& script, const std::string& sourceURL);
|
void executeApplicationScript(const std::string& script, const std::string& sourceURL);
|
||||||
|
/**
|
||||||
|
* Starts the JS application from an "unbundle", i.e. a backend that stores
|
||||||
|
* and injects each module as individual file.
|
||||||
|
*/
|
||||||
|
void loadApplicationUnbundle(
|
||||||
|
JSModulesUnbundle&& unbundle,
|
||||||
|
const std::string& startupCode,
|
||||||
|
const std::string& sourceURL);
|
||||||
void setGlobalVariable(const std::string& propName, const std::string& jsonValue);
|
void setGlobalVariable(const std::string& propName, const std::string& jsonValue);
|
||||||
bool supportsProfiling();
|
bool supportsProfiling();
|
||||||
void startProfiler(const std::string& title);
|
void startProfiler(const std::string& title);
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <jni/Countable.h>
|
#include <jni/Countable.h>
|
||||||
|
#include "JSModulesUnbundle.h"
|
||||||
|
|
||||||
namespace folly {
|
namespace folly {
|
||||||
|
|
||||||
|
@ -35,6 +36,14 @@ public:
|
||||||
const std::string& script,
|
const std::string& script,
|
||||||
const std::string& sourceURL) = 0;
|
const std::string& sourceURL) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an application "unbundle" file
|
||||||
|
*/
|
||||||
|
virtual void loadApplicationUnbundle(
|
||||||
|
JSModulesUnbundle&& bundle,
|
||||||
|
const std::string& startupCode,
|
||||||
|
const std::string& sourceURL) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes BatchedBridge.flushedQueue in JS to get the next queue of changes.
|
* Executes BatchedBridge.flushedQueue in JS to get the next queue of changes.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "Value.h"
|
#include "Value.h"
|
||||||
#include "jni/JMessageQueueThread.h"
|
#include "jni/JMessageQueueThread.h"
|
||||||
#include "jni/OnLoad.h"
|
#include "jni/OnLoad.h"
|
||||||
|
#include <react/JSCHelpers.h>
|
||||||
|
|
||||||
#ifdef WITH_JSC_EXTRA_TRACING
|
#ifdef WITH_JSC_EXTRA_TRACING
|
||||||
#include <react/JSCTracing.h>
|
#include <react/JSCTracing.h>
|
||||||
|
@ -152,12 +153,24 @@ void JSCExecutor::executeApplicationScript(
|
||||||
env->DeleteLocalRef(startStringMarker);
|
env->DeleteLocalRef(startStringMarker);
|
||||||
env->DeleteLocalRef(endStringMarker);
|
env->DeleteLocalRef(endStringMarker);
|
||||||
|
|
||||||
String jsSourceURL(sourceURL.c_str());
|
|
||||||
#ifdef WITH_FBSYSTRACE
|
#ifdef WITH_FBSYSTRACE
|
||||||
FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, "JSCExecutor::executeApplicationScript",
|
FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, "JSCExecutor::executeApplicationScript",
|
||||||
"sourceURL", sourceURL);
|
"sourceURL", sourceURL);
|
||||||
#endif
|
#endif
|
||||||
evaluateScript(m_context, jsScript, jsSourceURL);
|
evaluateScript(m_context, jsScript, String(sourceURL.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void JSCExecutor::loadApplicationUnbundle(
|
||||||
|
JSModulesUnbundle&& unbundle,
|
||||||
|
const std::string& startupCode,
|
||||||
|
const std::string& sourceURL) {
|
||||||
|
|
||||||
|
m_unbundle = std::move(unbundle);
|
||||||
|
if (!m_isUnbundleInitialized) {
|
||||||
|
m_isUnbundleInitialized = true;
|
||||||
|
installGlobalFunction(m_context, "nativeRequire", nativeRequire);
|
||||||
|
}
|
||||||
|
executeApplicationScript(startupCode, sourceURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string JSCExecutor::flush() {
|
std::string JSCExecutor::flush() {
|
||||||
|
@ -238,6 +251,13 @@ void JSCExecutor::flushQueueImmediate(std::string queueJSON) {
|
||||||
m_flushImmediateCallback(queueJSON, false);
|
m_flushImmediateCallback(queueJSON, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JSCExecutor::loadModule(uint32_t moduleId) {
|
||||||
|
auto module = m_unbundle.getModule(moduleId);
|
||||||
|
auto sourceUrl = String::createExpectingAscii(module.name);
|
||||||
|
auto source = String::createExpectingAscii(module.code);
|
||||||
|
evaluateScript(m_context, source, sourceUrl);
|
||||||
|
}
|
||||||
|
|
||||||
// WebWorker impl
|
// WebWorker impl
|
||||||
|
|
||||||
JSGlobalContextRef JSCExecutor::getContext() {
|
JSGlobalContextRef JSCExecutor::getContext() {
|
||||||
|
@ -287,12 +307,55 @@ void JSCExecutor::terminateWebWorker(int workerId) {
|
||||||
m_webWorkerJSObjs.erase(workerId);
|
m_webWorkerJSObjs.erase(workerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Native JS hooks
|
||||||
|
|
||||||
|
static JSValueRef makeInvalidModuleIdJSCException(
|
||||||
|
JSContextRef ctx,
|
||||||
|
const JSValueRef id,
|
||||||
|
JSValueRef *exception) {
|
||||||
|
std::string message = "Received invalid module ID: ";
|
||||||
|
message += String::adopt(JSValueToStringCopy(ctx, id, exception)).str();
|
||||||
|
return makeJSCException(ctx, message.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValueRef JSCExecutor::nativeRequire(
|
||||||
|
JSContextRef ctx,
|
||||||
|
JSObjectRef function,
|
||||||
|
JSObjectRef thisObject,
|
||||||
|
size_t argumentCount,
|
||||||
|
const JSValueRef arguments[],
|
||||||
|
JSValueRef *exception) {
|
||||||
|
|
||||||
|
if (argumentCount != 1) {
|
||||||
|
*exception = makeJSCException(ctx, "Got wrong number of args");
|
||||||
|
return JSValueMakeUndefined(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSCExecutor *executor;
|
||||||
|
try {
|
||||||
|
executor = s_globalContextRefToJSCExecutor.at(JSContextGetGlobalContext(ctx));
|
||||||
|
} catch (std::out_of_range& e) {
|
||||||
|
*exception = makeJSCException(ctx, "Global JS context didn't map to a valid executor");
|
||||||
|
return JSValueMakeUndefined(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
double moduleId = JSValueToNumber(ctx, arguments[0], exception);
|
||||||
|
if (moduleId <= (double) UINT32_MAX && moduleId >= 0.0) {
|
||||||
|
try {
|
||||||
|
executor->loadModule(moduleId);
|
||||||
|
} catch (JSModulesUnbundle::ModuleNotFound&) {
|
||||||
|
*exception = makeInvalidModuleIdJSCException(ctx, arguments[0], exception);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*exception = makeInvalidModuleIdJSCException(ctx, arguments[0], exception);
|
||||||
|
}
|
||||||
|
return JSValueMakeUndefined(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
static JSValueRef createErrorString(JSContextRef ctx, const char *msg) {
|
static JSValueRef createErrorString(JSContextRef ctx, const char *msg) {
|
||||||
return JSValueMakeString(ctx, String(msg));
|
return JSValueMakeString(ctx, String(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Native JS hooks
|
|
||||||
|
|
||||||
static JSValueRef nativeFlushQueueImmediate(
|
static JSValueRef nativeFlushQueueImmediate(
|
||||||
JSContextRef ctx,
|
JSContextRef ctx,
|
||||||
JSObjectRef function,
|
JSObjectRef function,
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <JavaScriptCore/JSContextRef.h>
|
#include <JavaScriptCore/JSContextRef.h>
|
||||||
|
@ -30,6 +31,10 @@ public:
|
||||||
virtual void executeApplicationScript(
|
virtual void executeApplicationScript(
|
||||||
const std::string& script,
|
const std::string& script,
|
||||||
const std::string& sourceURL) override;
|
const std::string& sourceURL) override;
|
||||||
|
virtual void loadApplicationUnbundle(
|
||||||
|
JSModulesUnbundle&& unbundle,
|
||||||
|
const std::string& startupCode,
|
||||||
|
const std::string& sourceURL) override;
|
||||||
virtual std::string flush() override;
|
virtual std::string flush() override;
|
||||||
virtual std::string callFunction(
|
virtual std::string callFunction(
|
||||||
const double moduleId,
|
const double moduleId,
|
||||||
|
@ -59,10 +64,13 @@ private:
|
||||||
std::unordered_map<int, JSCWebWorker> m_webWorkers;
|
std::unordered_map<int, JSCWebWorker> m_webWorkers;
|
||||||
std::unordered_map<int, Object> m_webWorkerJSObjs;
|
std::unordered_map<int, Object> m_webWorkerJSObjs;
|
||||||
std::shared_ptr<JMessageQueueThread> m_messageQueueThread;
|
std::shared_ptr<JMessageQueueThread> m_messageQueueThread;
|
||||||
|
JSModulesUnbundle m_unbundle;
|
||||||
|
bool m_isUnbundleInitialized = false;
|
||||||
|
|
||||||
int addWebWorker(const std::string& script, JSValueRef workerRef);
|
int addWebWorker(const std::string& script, JSValueRef workerRef);
|
||||||
void postMessageToWebWorker(int worker, JSValueRef message, JSValueRef *exn);
|
void postMessageToWebWorker(int worker, JSValueRef message, JSValueRef *exn);
|
||||||
void terminateWebWorker(int worker);
|
void terminateWebWorker(int worker);
|
||||||
|
void loadModule(uint32_t moduleId);
|
||||||
|
|
||||||
static JSValueRef nativeStartWorker(
|
static JSValueRef nativeStartWorker(
|
||||||
JSContextRef ctx,
|
JSContextRef ctx,
|
||||||
|
@ -85,6 +93,13 @@ private:
|
||||||
size_t argumentCount,
|
size_t argumentCount,
|
||||||
const JSValueRef arguments[],
|
const JSValueRef arguments[],
|
||||||
JSValueRef *exception);
|
JSValueRef *exception);
|
||||||
|
static JSValueRef nativeRequire(
|
||||||
|
JSContextRef ctx,
|
||||||
|
JSObjectRef function,
|
||||||
|
JSObjectRef thisObject,
|
||||||
|
size_t argumentCount,
|
||||||
|
const JSValueRef arguments[],
|
||||||
|
JSValueRef *exception);
|
||||||
};
|
};
|
||||||
|
|
||||||
} }
|
} }
|
||||||
|
|
|
@ -41,13 +41,14 @@ JSValueRef evaluateScript(JSContextRef context, JSStringRef script, JSStringRef
|
||||||
FBLOGE("Got JS Exception: %s", exceptionText.c_str());
|
FBLOGE("Got JS Exception: %s", exceptionText.c_str());
|
||||||
auto line = exception.asObject().getProperty("line");
|
auto line = exception.asObject().getProperty("line");
|
||||||
|
|
||||||
std::ostringstream lineInfo;
|
std::ostringstream locationInfo;
|
||||||
|
std::string file = String::adopt(source).str();
|
||||||
|
locationInfo << "(" << (file.length() ? file : "<unknown file>");
|
||||||
if (line != nullptr && line.isNumber()) {
|
if (line != nullptr && line.isNumber()) {
|
||||||
lineInfo << " (line " << line.asInteger() << " in the generated bundle)";
|
locationInfo << ":" << line.asInteger();
|
||||||
} else {
|
|
||||||
lineInfo << " (no line info)";
|
|
||||||
}
|
}
|
||||||
throwJSExecutionException("%s%s", exceptionText.c_str(), lineInfo.str().c_str());
|
locationInfo << ")";
|
||||||
|
throwJSExecutionException("%s %s", exceptionText.c_str(), locationInfo.str().c_str());
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
|
|
||||||
#define throwJSExecutionException(...) jni::throwNewJavaException("com/facebook/react/bridge/JSExecutionException", __VA_ARGS__)
|
#define throwJSExecutionException(...) jni::throwNewJavaException("com/facebook/react/bridge/JSExecutionException", __VA_ARGS__)
|
||||||
|
|
||||||
#define throwJSExecutionException(...) jni::throwNewJavaException("com/facebook/react/bridge/JSExecutionException", __VA_ARGS__)
|
|
||||||
|
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace react {
|
namespace react {
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "JSModulesUnbundle.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <fb/assert.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <sstream>
|
||||||
|
#include <sys/endian.h>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
using magic_number_t = uint32_t;
|
||||||
|
const magic_number_t MAGIC_FILE_HEADER = 0xFB0BD1E5;
|
||||||
|
const std::string MAGIC_FILE_NAME = "UNBUNDLE";
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace react {
|
||||||
|
|
||||||
|
using asset_ptr =
|
||||||
|
std::unique_ptr<AAsset, std::function<decltype(AAsset_close)>>;
|
||||||
|
|
||||||
|
static std::string jsModulesDir(const std::string& entryFile) {
|
||||||
|
std::string dir = dirname(entryFile.c_str());
|
||||||
|
|
||||||
|
// android's asset manager does not work with paths that start with a dot
|
||||||
|
return dir == "." ? "js-modules/" : dir + "/js-modules/";
|
||||||
|
}
|
||||||
|
|
||||||
|
static asset_ptr openAsset(
|
||||||
|
AAssetManager *manager,
|
||||||
|
const std::string& fileName,
|
||||||
|
int mode = AASSET_MODE_STREAMING) {
|
||||||
|
return asset_ptr(
|
||||||
|
AAssetManager_open(manager, fileName.c_str(), mode),
|
||||||
|
AAsset_close);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSModulesUnbundle::JSModulesUnbundle(AAssetManager *assetManager, const std::string& entryFile) :
|
||||||
|
m_assetManager(assetManager),
|
||||||
|
m_moduleDirectory(jsModulesDir(entryFile)) {}
|
||||||
|
|
||||||
|
JSModulesUnbundle::JSModulesUnbundle(JSModulesUnbundle&& other) noexcept {
|
||||||
|
*this = std::move(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSModulesUnbundle& JSModulesUnbundle::operator= (JSModulesUnbundle&& other) noexcept {
|
||||||
|
std::swap(m_assetManager, other.m_assetManager);
|
||||||
|
std::swap(m_moduleDirectory, other.m_moduleDirectory);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JSModulesUnbundle::isUnbundle(
|
||||||
|
AAssetManager *assetManager,
|
||||||
|
const std::string& assetName) {
|
||||||
|
if (!assetManager) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto magicFileName = jsModulesDir(assetName) + MAGIC_FILE_NAME;
|
||||||
|
auto asset = openAsset(assetManager, magicFileName.c_str());
|
||||||
|
if (asset == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
magic_number_t fileHeader = 0;
|
||||||
|
AAsset_read(asset.get(), &fileHeader, sizeof(fileHeader));
|
||||||
|
return fileHeader == htole32(MAGIC_FILE_HEADER);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSModulesUnbundle::Module JSModulesUnbundle::getModule(uint32_t moduleId) const {
|
||||||
|
// can be nullptr for default constructor.
|
||||||
|
FBASSERTMSGF(m_assetManager != nullptr, "Unbundle has not been initialized with an asset manager");
|
||||||
|
|
||||||
|
std::ostringstream sourceUrlBuilder;
|
||||||
|
sourceUrlBuilder << moduleId << ".js";
|
||||||
|
auto sourceUrl = sourceUrlBuilder.str();
|
||||||
|
|
||||||
|
auto fileName = m_moduleDirectory + sourceUrl;
|
||||||
|
auto asset = openAsset(m_assetManager, fileName, AASSET_MODE_BUFFER);
|
||||||
|
|
||||||
|
const char *buffer = nullptr;
|
||||||
|
if (asset != nullptr) {
|
||||||
|
buffer = static_cast<const char *>(AAsset_getBuffer(asset.get()));
|
||||||
|
}
|
||||||
|
if (buffer == nullptr) {
|
||||||
|
throw ModuleNotFound("Module not found: " + sourceUrl);
|
||||||
|
}
|
||||||
|
return {sourceUrl, std::string(buffer, AAsset_getLength(asset.get()))};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <android/asset_manager.h>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <fb/noncopyable.h>
|
||||||
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace react {
|
||||||
|
|
||||||
|
class JSModulesUnbundle : noncopyable {
|
||||||
|
/**
|
||||||
|
* Represents the set of JavaScript modules that the application consists of.
|
||||||
|
* The source code of each module can be retrieved by module ID.
|
||||||
|
* This implementation reads modules as single file from the assets of an apk.
|
||||||
|
*
|
||||||
|
* The class is non-copyable because copying instances might involve copying
|
||||||
|
* several megabytes of memory.
|
||||||
|
*/
|
||||||
|
public:
|
||||||
|
class ModuleNotFound : public std::out_of_range {
|
||||||
|
using std::out_of_range::out_of_range;
|
||||||
|
};
|
||||||
|
struct Module {
|
||||||
|
std::string name;
|
||||||
|
std::string code;
|
||||||
|
};
|
||||||
|
|
||||||
|
JSModulesUnbundle() = default;
|
||||||
|
JSModulesUnbundle(AAssetManager *assetManager, const std::string& entryFile);
|
||||||
|
JSModulesUnbundle(JSModulesUnbundle&& other) noexcept;
|
||||||
|
JSModulesUnbundle& operator= (JSModulesUnbundle&& other) noexcept;
|
||||||
|
|
||||||
|
static bool isUnbundle(
|
||||||
|
AAssetManager *assetManager,
|
||||||
|
const std::string& assetName);
|
||||||
|
Module getModule(uint32_t moduleId) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
AAssetManager *m_assetManager = nullptr;
|
||||||
|
std::string m_moduleDirectory;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,6 @@
|
||||||
#include "Value.h"
|
#include "Value.h"
|
||||||
|
|
||||||
#include <jni/fbjni.h>
|
#include <jni/fbjni.h>
|
||||||
#include <fb/log.h>
|
|
||||||
|
|
||||||
#include "JSCHelpers.h"
|
#include "JSCHelpers.h"
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
#include "JSLoader.h"
|
#include "JSLoader.h"
|
||||||
|
|
||||||
#include <android/asset_manager.h>
|
|
||||||
#include <android/asset_manager_jni.h>
|
#include <android/asset_manager_jni.h>
|
||||||
#include <jni/Environment.h>
|
#include <jni/Environment.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
@ -10,7 +9,6 @@
|
||||||
#include <streambuf>
|
#include <streambuf>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <fb/log.h>
|
#include <fb/log.h>
|
||||||
|
|
||||||
#ifdef WITH_FBSYSTRACE
|
#ifdef WITH_FBSYSTRACE
|
||||||
#include <fbsystrace.h>
|
#include <fbsystrace.h>
|
||||||
using fbsystrace::FbSystraceSection;
|
using fbsystrace::FbSystraceSection;
|
||||||
|
@ -29,19 +27,16 @@ std::string loadScriptFromAssets(std::string assetName) {
|
||||||
gApplicationHolderClass,
|
gApplicationHolderClass,
|
||||||
gGetApplicationMethod);
|
gGetApplicationMethod);
|
||||||
jobject assetManager = env->CallObjectMethod(application, gGetAssetManagerMethod);
|
jobject assetManager = env->CallObjectMethod(application, gGetAssetManagerMethod);
|
||||||
return loadScriptFromAssets(env, assetManager, assetName);
|
return loadScriptFromAssets(AAssetManager_fromJava(env, assetManager), assetName);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string loadScriptFromAssets(
|
std::string loadScriptFromAssets(
|
||||||
JNIEnv *env,
|
AAssetManager *manager,
|
||||||
jobject assetManager,
|
|
||||||
std::string assetName) {
|
std::string assetName) {
|
||||||
#ifdef WITH_FBSYSTRACE
|
#ifdef WITH_FBSYSTRACE
|
||||||
FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, "reactbridge_jni_loadScriptFromAssets",
|
FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, "reactbridge_jni_loadScriptFromAssets",
|
||||||
"assetName", assetName);
|
"assetName", assetName);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto manager = AAssetManager_fromJava(env, assetManager);
|
|
||||||
if (manager) {
|
if (manager) {
|
||||||
auto asset = AAssetManager_open(
|
auto asset = AAssetManager_open(
|
||||||
manager,
|
manager,
|
||||||
|
@ -90,7 +85,7 @@ void registerJSLoaderNatives() {
|
||||||
jclass applicationHolderClass = env->FindClass("com/facebook/react/common/ApplicationHolder");
|
jclass applicationHolderClass = env->FindClass("com/facebook/react/common/ApplicationHolder");
|
||||||
gApplicationHolderClass = (jclass)env->NewGlobalRef(applicationHolderClass);
|
gApplicationHolderClass = (jclass)env->NewGlobalRef(applicationHolderClass);
|
||||||
gGetApplicationMethod = env->GetStaticMethodID(applicationHolderClass, "getApplication", "()Landroid/app/Application;");
|
gGetApplicationMethod = env->GetStaticMethodID(applicationHolderClass, "getApplication", "()Landroid/app/Application;");
|
||||||
|
|
||||||
jclass appClass = env->FindClass("android/app/Application");
|
jclass appClass = env->FindClass("android/app/Application");
|
||||||
gGetAssetManagerMethod = env->GetMethodID(appClass, "getAssets", "()Landroid/content/res/AssetManager;");
|
gGetAssetManagerMethod = env->GetMethodID(appClass, "getAssets", "()Landroid/content/res/AssetManager;");
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <android/asset_manager.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
|
@ -17,7 +18,7 @@ std::string loadScriptFromAssets(std::string assetName);
|
||||||
/**
|
/**
|
||||||
* Helper method for loading JS script from android asset
|
* Helper method for loading JS script from android asset
|
||||||
*/
|
*/
|
||||||
std::string loadScriptFromAssets(JNIEnv *env, jobject assetManager, std::string assetName);
|
std::string loadScriptFromAssets(AAssetManager *assetManager, std::string assetName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method for loading JS script from a file
|
* Helper method for loading JS script from a file
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
|
#include <android/asset_manager_jni.h>
|
||||||
#include <android/input.h>
|
#include <android/input.h>
|
||||||
#include <fb/log.h>
|
#include <fb/log.h>
|
||||||
#include <folly/json.h>
|
#include <folly/json.h>
|
||||||
|
@ -13,6 +14,7 @@
|
||||||
#include <react/Bridge.h>
|
#include <react/Bridge.h>
|
||||||
#include <react/Executor.h>
|
#include <react/Executor.h>
|
||||||
#include <react/JSCExecutor.h>
|
#include <react/JSCExecutor.h>
|
||||||
|
#include <react/JSModulesUnbundle.h>
|
||||||
#include "JNativeRunnable.h"
|
#include "JNativeRunnable.h"
|
||||||
#include "JSLoader.h"
|
#include "JSLoader.h"
|
||||||
#include "ReadableNativeArray.h"
|
#include "ReadableNativeArray.h"
|
||||||
|
@ -643,15 +645,33 @@ static void executeApplicationScript(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void loadApplicationUnbundle(
|
||||||
|
const RefPtr<Bridge>& bridge,
|
||||||
|
AAssetManager *assetManager,
|
||||||
|
const std::string& startupCode,
|
||||||
|
const std::string& startupFileName) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Load the application unbundle and collect/dispatch any native calls that might have occured
|
||||||
|
bridge->loadApplicationUnbundle(
|
||||||
|
JSModulesUnbundle(assetManager, startupFileName),
|
||||||
|
startupCode,
|
||||||
|
startupFileName);
|
||||||
|
bridge->flush();
|
||||||
|
} catch (...) {
|
||||||
|
translatePendingCppExceptionToJavaException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void loadScriptFromAssets(JNIEnv* env, jobject obj, jobject assetManager,
|
static void loadScriptFromAssets(JNIEnv* env, jobject obj, jobject assetManager,
|
||||||
jstring assetName) {
|
jstring assetName) {
|
||||||
jclass markerClass = env->FindClass("com/facebook/react/bridge/ReactMarker");
|
jclass markerClass = env->FindClass("com/facebook/react/bridge/ReactMarker");
|
||||||
|
auto manager = AAssetManager_fromJava(env, assetManager);
|
||||||
auto bridge = extractRefPtr<Bridge>(env, obj);
|
auto bridge = extractRefPtr<Bridge>(env, obj);
|
||||||
auto assetNameStr = fromJString(env, assetName);
|
auto assetNameStr = fromJString(env, assetName);
|
||||||
|
|
||||||
env->CallStaticVoidMethod(markerClass, gLogMarkerMethod, env->NewStringUTF("loadScriptFromAssets_start"));
|
env->CallStaticVoidMethod(markerClass, gLogMarkerMethod, env->NewStringUTF("loadScriptFromAssets_start"));
|
||||||
auto script = react::loadScriptFromAssets(env, assetManager, assetNameStr);
|
auto script = react::loadScriptFromAssets(manager, assetNameStr);
|
||||||
#ifdef WITH_FBSYSTRACE
|
#ifdef WITH_FBSYSTRACE
|
||||||
FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, "reactbridge_jni_"
|
FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, "reactbridge_jni_"
|
||||||
"executeApplicationScript",
|
"executeApplicationScript",
|
||||||
|
@ -659,7 +679,11 @@ static void loadScriptFromAssets(JNIEnv* env, jobject obj, jobject assetManager,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
env->CallStaticVoidMethod(markerClass, gLogMarkerMethod, env->NewStringUTF("loadScriptFromAssets_read"));
|
env->CallStaticVoidMethod(markerClass, gLogMarkerMethod, env->NewStringUTF("loadScriptFromAssets_read"));
|
||||||
executeApplicationScript(bridge, script, assetNameStr);
|
if (JSModulesUnbundle::isUnbundle(manager, assetNameStr)) {
|
||||||
|
loadApplicationUnbundle(bridge, manager, script, assetNameStr);
|
||||||
|
} else {
|
||||||
|
executeApplicationScript(bridge, script, assetNameStr);
|
||||||
|
}
|
||||||
if (env->ExceptionCheck()) {
|
if (env->ExceptionCheck()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "ProxyExecutor.h"
|
#include "ProxyExecutor.h"
|
||||||
|
|
||||||
|
#include <fb/assert.h>
|
||||||
#include <jni/Environment.h>
|
#include <jni/Environment.h>
|
||||||
#include <jni/LocalReference.h>
|
#include <jni/LocalReference.h>
|
||||||
#include <jni/LocalString.h>
|
#include <jni/LocalString.h>
|
||||||
|
@ -50,6 +51,11 @@ void ProxyExecutor::executeApplicationScript(
|
||||||
jni::make_jstring(sourceURL).get());
|
jni::make_jstring(sourceURL).get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProxyExecutor::loadApplicationUnbundle(JSModulesUnbundle&&, const std::string&, const std::string&) {
|
||||||
|
jni::throwNewJavaException(
|
||||||
|
"java/lang/UnsupportedOperationException",
|
||||||
|
"Loading application unbundles is not supported for proxy executors");
|
||||||
|
}
|
||||||
|
|
||||||
std::string ProxyExecutor::flush() {
|
std::string ProxyExecutor::flush() {
|
||||||
return executeJSCallWithProxy(m_executor.get(), "flushedQueue", std::vector<folly::dynamic>());
|
return executeJSCallWithProxy(m_executor.get(), "flushedQueue", std::vector<folly::dynamic>());
|
||||||
|
|
|
@ -32,6 +32,10 @@ public:
|
||||||
virtual void executeApplicationScript(
|
virtual void executeApplicationScript(
|
||||||
const std::string& script,
|
const std::string& script,
|
||||||
const std::string& sourceURL) override;
|
const std::string& sourceURL) override;
|
||||||
|
virtual void loadApplicationUnbundle(
|
||||||
|
JSModulesUnbundle&& bundle,
|
||||||
|
const std::string& startupCode,
|
||||||
|
const std::string& sourceURL) override;
|
||||||
virtual std::string flush() override;
|
virtual std::string flush() override;
|
||||||
virtual std::string callFunction(
|
virtual std::string callFunction(
|
||||||
const double moduleId,
|
const double moduleId,
|
||||||
|
|
|
@ -76,7 +76,7 @@ function uInt32Buffer(n) {
|
||||||
|
|
||||||
function buildModuleTable(buffers) {
|
function buildModuleTable(buffers) {
|
||||||
// table format:
|
// table format:
|
||||||
// - table_length: uint_32 length of all table entries in bytes
|
// - table_length: uint_32 length of all table entries in bytes + the table length itself
|
||||||
// - entries: entry...
|
// - entries: entry...
|
||||||
//
|
//
|
||||||
// entry:
|
// entry:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const {ErrorUtils, __nativeRequire} = global;
|
const {ErrorUtils, nativeRequire} = global;
|
||||||
global.require = require;
|
global.require = require;
|
||||||
global.__d = define;
|
global.__d = define;
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ function guardedLoadModule(moduleId, module) {
|
||||||
|
|
||||||
function loadModuleImplementation(moduleId, module) {
|
function loadModuleImplementation(moduleId, module) {
|
||||||
if (!module) {
|
if (!module) {
|
||||||
__nativeRequire(moduleId);
|
nativeRequire(moduleId);
|
||||||
module = modules[moduleId];
|
module = modules[moduleId];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue