Reverted commit D3545345

Reviewed By: tadeuzagallo

Differential Revision: D3545345

fbshipit-source-id: d655918be7dcadaf8143800497e85f3de44bd48a
This commit is contained in:
David Aurelio 2016-07-14 10:43:23 -07:00 committed by Facebook Github Bot 6
parent 7d2b5714af
commit df01215006
18 changed files with 158 additions and 199 deletions

View File

@ -161,9 +161,8 @@ public class CatalystInstanceImpl implements CatalystInstance {
MessageQueueThread moduleQueue,
ModuleRegistryHolder registryHolder);
/* package */ native void loadScriptFromAssets(AssetManager assetManager, String assetURL);
/* package */ native void loadScriptFromAssets(AssetManager assetManager, String assetURL, boolean useLazyBundle);
/* package */ native void loadScriptFromFile(String fileName, String sourceURL);
/* package */ native void loadScriptFromOptimizedBundle(String path, String sourceURL, int flags);
@Override
public void runJSBundle() {

View File

@ -30,11 +30,18 @@ public abstract class JSBundleLoader {
public static JSBundleLoader createFileLoader(
final Context context,
final String fileName) {
return createFileLoader(context, fileName, false);
}
public static JSBundleLoader createFileLoader(
final Context context,
final String fileName,
final boolean useLazyBundle) {
return new JSBundleLoader() {
@Override
public void loadScript(CatalystInstanceImpl instance) {
if (fileName.startsWith("assets://")) {
instance.loadScriptFromAssets(context.getAssets(), fileName);
instance.loadScriptFromAssets(context.getAssets(), fileName, useLazyBundle);
} else {
instance.loadScriptFromFile(fileName, fileName);
}

View File

@ -38,12 +38,6 @@ import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JAVA_BRIDGE;
*/
public class UnpackingJSBundleLoader extends JSBundleLoader {
/**
* Flag passed to loadScriptFromOptimizedBundle to let the bridge know that
* the unpacked unpacked js source file.
*/
static final int UNPACKED_JS_SOURCE = (1 << 0);
/**
* Name of the lock files. Multiple processes can be spawned off the same app
* and we need to guarantee that at most one unpacks files at any time. To
@ -147,10 +141,10 @@ public class UnpackingJSBundleLoader extends JSBundleLoader {
@Override
public void loadScript(CatalystInstanceImpl instance) {
prepare();
instance.loadScriptFromOptimizedBundle(
mDirectoryPath.getPath(),
mSourceURL,
UNPACKED_JS_SOURCE);
// TODO(12128379): add instance method that would take bundle directory
instance.loadScriptFromFile(
new File(mDirectoryPath, "bundle.js").getPath(),
mSourceURL);
}
@Override

View File

@ -13,6 +13,8 @@
#include <jni/Countable.h>
#include <jni/LocalReference.h>
#include <sys/stat.h>
#include <cxxreact/Instance.h>
#include <cxxreact/MethodCall.h>
#include <cxxreact/ModuleRegistry.h>
@ -23,6 +25,7 @@
#include "ModuleRegistryHolder.h"
#include "NativeArray.h"
#include "JNativeRunnable.h"
#include "OnLoad.h"
using namespace facebook::jni;
@ -98,11 +101,9 @@ void CatalystInstanceImpl::registerNatives() {
makeNativeMethod("initHybrid", CatalystInstanceImpl::initHybrid),
makeNativeMethod("initializeBridge", CatalystInstanceImpl::initializeBridge),
makeNativeMethod("loadScriptFromAssets",
"(Landroid/content/res/AssetManager;Ljava/lang/String;)V",
"(Landroid/content/res/AssetManager;Ljava/lang/String;Z)V",
CatalystInstanceImpl::loadScriptFromAssets),
makeNativeMethod("loadScriptFromFile", CatalystInstanceImpl::loadScriptFromFile),
makeNativeMethod("loadScriptFromOptimizedBundle",
CatalystInstanceImpl::loadScriptFromOptimizedBundle),
makeNativeMethod("callJSFunction", CatalystInstanceImpl::callJSFunction),
makeNativeMethod("callJSCallback", CatalystInstanceImpl::callJSCallback),
makeNativeMethod("getMainExecutorToken", CatalystInstanceImpl::getMainExecutorToken),
@ -151,20 +152,131 @@ void CatalystInstanceImpl::initializeBridge(
mrh->getModuleRegistry());
}
#ifdef WITH_FBJSCEXTENSIONS
static std::unique_ptr<const JSBigString> loadScriptFromCache(
AAssetManager* manager,
std::string& sourceURL) {
// 20-byte sha1 as hex
static const size_t HASH_STR_SIZE = 40;
// load bundle hash from the metadata file in the APK
auto hash = react::loadScriptFromAssets(manager, sourceURL + ".meta");
auto cacheDir = getApplicationCacheDir() + "/rn-bundle";
auto encoding = static_cast<JSBigMmapString::Encoding>(hash->c_str()[20]);
if (mkdir(cacheDir.c_str(), 0755) == -1 && errno != EEXIST) {
throw std::runtime_error("Can't create cache directory");
}
if (encoding != JSBigMmapString::Encoding::Ascii) {
throw std::runtime_error("Can't use mmap fastpath for non-ascii bundles");
}
// convert hash to string
char hashStr[HASH_STR_SIZE + 1];
for (size_t i = 0; i < HASH_STR_SIZE; i += 2) {
snprintf(hashStr + i, 3, "%02hhx", hash->c_str()[i / 2] & 0xFF);
}
// the name of the cached bundle file should be the hash
std::string cachePath = cacheDir + "/" + hashStr;
FILE *cache = fopen(cachePath.c_str(), "r");
SCOPE_EXIT { if (cache) fclose(cache); };
size_t size = 0;
if (cache == NULL) {
// delete old bundle, if there was one.
std::string metaPath = cacheDir + "/meta";
if (auto meta = fopen(metaPath.c_str(), "r")) {
char oldBundleHash[HASH_STR_SIZE + 1];
if (fread(oldBundleHash, HASH_STR_SIZE, 1, meta) == HASH_STR_SIZE) {
remove((cacheDir + "/" + oldBundleHash).c_str());
remove(metaPath.c_str());
}
fclose(meta);
}
// load script from the APK and write to temporary file
auto script = react::loadScriptFromAssets(manager, sourceURL);
auto tmpPath = cachePath + "_";
cache = fopen(tmpPath.c_str(), "w");
if (!cache) {
throw std::runtime_error("Can't open cache, errno: " + errno);
}
if (fwrite(script->c_str(), 1, script->size(), cache) != size) {
remove(tmpPath.c_str());
throw std::runtime_error("Failed to unpack bundle");
}
// force data to be written to disk
fsync(fileno(cache));
fclose(cache);
// move script to final path - atomic operation
if (rename(tmpPath.c_str(), cachePath.c_str())) {
throw std::runtime_error("Failed to update cache, errno: " + errno);
}
// store the bundle hash in a metadata file
auto meta = fopen(metaPath.c_str(), "w");
if (!meta) {
throw std::runtime_error("Failed to open metadata file to store bundle hash");
}
if (fwrite(hashStr, HASH_STR_SIZE, 1, meta) != HASH_STR_SIZE) {
throw std::runtime_error("Failed to write bundle hash to metadata file");
}
fsync(fileno(meta));
fclose(meta);
// return the final written cache
cache = fopen(cachePath.c_str(), "r");
if (!cache) {
throw std::runtime_error("Cache has been cleared");
}
} else {
struct stat fileInfo = {0};
if (fstat(fileno(cache), &fileInfo)) {
throw std::runtime_error("Failed to get cache stats, errno: " + errno);
}
size = fileInfo.st_size;
}
return folly::make_unique<const JSBigMmapString>(
dup(fileno(cache)),
size,
reinterpret_cast<const uint8_t*>(hash->c_str()),
encoding);
}
#endif
void CatalystInstanceImpl::loadScriptFromAssets(jobject assetManager,
const std::string& assetURL) {
const std::string& assetURL,
bool useLazyBundle) {
const int kAssetsLength = 9; // strlen("assets://");
auto sourceURL = assetURL.substr(kAssetsLength);
auto manager = react::extractAssetManager(assetManager);
auto script = react::loadScriptFromAssets(manager, sourceURL);
if (JniJSModulesUnbundle::isUnbundle(manager, sourceURL)) {
auto script = react::loadScriptFromAssets(manager, sourceURL);
instance_->loadUnbundle(
folly::make_unique<JniJSModulesUnbundle>(manager, sourceURL),
std::move(script),
sourceURL);
return;
} else {
#ifdef WITH_FBJSCEXTENSIONS
if (useLazyBundle) {
try {
auto script = loadScriptFromCache(manager, sourceURL);
instance_->loadScriptFromString(std::move(script), sourceURL);
return;
} catch (...) {
LOG(WARNING) << "Failed to load bundle as Source Code";
}
}
#endif
auto script = react::loadScriptFromAssets(manager, sourceURL);
instance_->loadScriptFromString(std::move(script), sourceURL);
}
}
@ -175,14 +287,6 @@ void CatalystInstanceImpl::loadScriptFromFile(jni::alias_ref<jstring> fileName,
sourceURL);
}
void CatalystInstanceImpl::loadScriptFromOptimizedBundle(const std::string& bundlePath,
const std::string& sourceURL,
jint flags) {
return instance_->loadScriptFromOptimizedBundle(std::move(bundlePath),
std::move(sourceURL),
flags);
}
void CatalystInstanceImpl::callJSFunction(
JExecutorToken* token, std::string module, std::string method, NativeArray* arguments) {
// We want to share the C++ code, and on iOS, modules pass module/method

View File

@ -47,9 +47,8 @@ class CatalystInstanceImpl : public jni::HybridClass<CatalystInstanceImpl> {
jni::alias_ref<JavaMessageQueueThread::javaobject> jsQueue,
jni::alias_ref<JavaMessageQueueThread::javaobject> moduleQueue,
ModuleRegistryHolder* mrh);
void loadScriptFromAssets(jobject assetManager, const std::string& assetURL);
void loadScriptFromAssets(jobject assetManager, const std::string& assetURL, bool useLazyBundle);
void loadScriptFromFile(jni::alias_ref<jstring> fileName, const std::string& sourceURL);
void loadScriptFromOptimizedBundle(const std::string& bundlePath, const std::string& sourceURL, jint flags);
void callJSFunction(JExecutorToken* token, std::string module, std::string method, NativeArray* arguments);
void callJSCallback(JExecutorToken* token, jint callbackId, NativeArray* arguments);
local_ref<JExecutorToken::JavaPart> getMainExecutorToken();

View File

@ -51,10 +51,6 @@ static std::string getApplicationDir(const char* methodName) {
return getAbsolutePathMethod(dirObj)->toStdString();
}
static std::string getApplicationCacheDir() {
return getApplicationDir("getCacheDir");
}
static std::string getApplicationPersistentDir() {
return getApplicationDir("getFilesDir");
}
@ -162,6 +158,10 @@ class JReactMarker : public JavaClass<JReactMarker> {
}
std::string getApplicationCacheDir() {
return getApplicationDir("getCacheDir");
}
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
return initialize(vm, [] {
// Inject some behavior into react/

View File

@ -10,5 +10,6 @@ namespace facebook {
namespace react {
jmethodID getLogMarkerMethod();
std::string getApplicationCacheDir();
} // namespace react
} // namespace facebook

View File

@ -102,10 +102,9 @@ public class UnpackingJSBundleLoaderTest {
@Test
public void testCallsAppropriateInstanceMethod() throws IOException {
mBuilder.build().loadScript(mCatalystInstanceImpl);
verify(mCatalystInstanceImpl).loadScriptFromOptimizedBundle(
eq(mDestinationPath.getPath()),
eq(URL),
eq(UnpackingJSBundleLoader.UNPACKED_JS_SOURCE));
verify(mCatalystInstanceImpl).loadScriptFromFile(
eq(new File(mDestinationPath, "bundle.js").getPath()),
eq(URL));
verifyNoMoreInteractions(mCatalystInstanceImpl);
}

View File

@ -5,7 +5,6 @@ include $(CLEAR_VARS)
LOCAL_MODULE := libreactnativefb
LOCAL_SRC_FILES := \
Executor.cpp \
Instance.cpp \
JSCExecutor.cpp \
JSCHelpers.cpp \

View File

@ -109,7 +109,6 @@ react_library(
force_static = True,
srcs = [
'CxxMessageQueue.cpp',
'Executor.cpp',
'Instance.cpp',
'JSCExecutor.cpp',
'JSCHelpers.cpp',

View File

@ -1,77 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "Executor.h"
#include <errno.h>
#include <fcntl.h>
#include <fstream>
#include <stdio.h>
#include <sys/stat.h>
#include <folly/Memory.h>
namespace facebook {
namespace react {
void JSExecutor::loadApplicationScript(std::string bundlePath, std::string sourceURL, int flags) {
if ((flags & UNPACKED_JS_SOURCE) == 0) {
throw std::runtime_error("No unpacked js source file");
}
return loadApplicationScript(
JSBigMmapString::fromOptimizedBundle(bundlePath),
std::move(sourceURL));
}
static JSBigMmapString::Encoding encodingFromByte(uint8_t byte) {
switch (byte) {
case 0:
return JSBigMmapString::Encoding::Unknown;
case 1:
return JSBigMmapString::Encoding::Ascii;
case 2:
return JSBigMmapString::Encoding::Utf8;
case 3:
return JSBigMmapString::Encoding::Utf16;
default:
throw std::invalid_argument("Unknown bundle encoding");
}
}
std::unique_ptr<const JSBigMmapString> JSBigMmapString::fromOptimizedBundle(
const std::string& bundlePath) {
uint8_t sha1[20];
uint8_t encoding;
struct stat fileInfo;
int fd = -1;
SCOPE_FAIL { CHECK(fd == -1 || ::close(fd) == 0); };
{
auto metaPath = bundlePath + UNPACKED_META_PATH_SUFFIX;
std::ifstream metaFile;
metaFile.exceptions(std::ifstream::eofbit | std::ifstream::failbit | std::ifstream::badbit);
metaFile.open(metaPath, std::ifstream::in | std::ifstream::binary);
metaFile.read(reinterpret_cast<char*>(sha1), sizeof(sha1));
metaFile.read(reinterpret_cast<char*>(&encoding), sizeof(encoding));
}
{
auto sourcePath = bundlePath + UNPACKED_JS_SOURCE_PATH_SUFFIX;
fd = ::open(sourcePath.c_str(), O_RDONLY);
if (fd == -1) {
throw std::runtime_error(std::string("could not open js bundle file: ") + ::strerror(errno));
}
}
if (::fstat(fd, &fileInfo)) {
throw std::runtime_error(std::string("fstat on js bundle failed: ") + strerror(errno));
}
return folly::make_unique<const JSBigMmapString>(
fd,
fileInfo.st_size,
sha1,
encodingFromByte(encoding));
}
} // namespace react
} // namespace facebook

View File

@ -16,13 +16,6 @@
namespace facebook {
namespace react {
#define UNPACKED_JS_SOURCE_PATH_SUFFIX "/bundle.js"
#define UNPACKED_META_PATH_SUFFIX "/bundle.meta"
enum {
UNPACKED_JS_SOURCE = (1 << 0),
};
class JSExecutor;
class MessageQueueThread;
@ -197,8 +190,6 @@ public:
return m_encoding;
}
static std::unique_ptr<const JSBigMmapString> fromOptimizedBundle(const std::string& bundlePath);
private:
int m_fd;
size_t m_size;
@ -215,11 +206,6 @@ public:
virtual void loadApplicationScript(std::unique_ptr<const JSBigString> script,
std::string sourceURL) = 0;
/**
* Execute an application script optimized bundle in the JS context.
*/
virtual void loadApplicationScript(std::string bundlePath, std::string source, int flags);
/**
* Add an application "unbundle" file
*/
@ -259,6 +245,4 @@ public:
virtual ~JSExecutor() {}
};
std::unique_ptr<const JSBigMmapString> readJSBundle(const std::string& path);
} }

View File

@ -75,16 +75,6 @@ void Instance::loadScriptFromFile(const std::string& filename,
loadScriptFromString(std::move(buf), sourceURL);
}
void Instance::loadScriptFromOptimizedBundle(std::string bundlePath,
std::string sourceURL,
int flags) {
SystraceSection s("reactbridge_xplat_loadScriptFromOptimizedBundle",
"bundlePath", bundlePath);
nativeToJsBridge_->loadOptimizedApplicationScript(std::move(bundlePath),
std::move(sourceURL),
flags);
}
void Instance::loadUnbundle(std::unique_ptr<JSModulesUnbundle> unbundle,
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL) {

View File

@ -36,7 +36,6 @@ class Instance {
std::shared_ptr<ModuleRegistry> moduleRegistry);
void loadScriptFromString(std::unique_ptr<const JSBigString> string, std::string sourceURL);
void loadScriptFromFile(const std::string& filename, const std::string& sourceURL);
void loadScriptFromOptimizedBundle(std::string bundlePath, std::string sourceURL, int flags);
void loadUnbundle(
std::unique_ptr<JSModulesUnbundle> unbundle,
std::unique_ptr<const JSBigString> startupScript,

View File

@ -254,37 +254,15 @@ void JSCExecutor::terminateOnJSVMThread() {
}
#ifdef WITH_FBJSCEXTENSIONS
void JSCExecutor::loadApplicationScript(
std::string bundlePath,
std::string sourceURL,
int flags) {
SystraceSection s("JSCExecutor::loadApplicationScript",
"sourceURL", sourceURL);
if ((flags & UNPACKED_JS_SOURCE) == 0) {
throw std::runtime_error("Optimized bundle with no unpacked js source");
}
auto jsScriptBigString = JSBigMmapString::fromOptimizedBundle(bundlePath);
if (jsScriptBigString->encoding() != JSBigMmapString::Encoding::Ascii) {
throw std::runtime_error("Optimized bundle needs to be ascii encoded");
}
static void loadApplicationSource(
const JSGlobalContextRef context,
const JSBigMmapString* script,
const std::string& sourceURL) {
String jsSourceURL(sourceURL.c_str());
JSSourceCodeRef sourceCode = JSCreateSourceCode(
jsScriptBigString->fd(),
jsScriptBigString->size(),
jsSourceURL,
jsScriptBigString->hash(),
true);
SCOPE_EXIT { JSReleaseSourceCode(sourceCode); };
evaluateSourceCode(m_context, sourceCode, jsSourceURL);
bindBridge();
flush();
ReactMarker::logMarker("CREATE_REACT_CONTEXT_END");
bool is8bit = script->encoding() == JSBigMmapString::Encoding::Ascii || script->encoding() == JSBigMmapString::Encoding::Utf8;
JSSourceCodeRef sourceCode = JSCreateSourceCode(script->fd(), script->size(), jsSourceURL, script->hash(), is8bit);
evaluateSourceCode(context, sourceCode, jsSourceURL);
JSReleaseSourceCode(sourceCode);
}
#endif
@ -292,6 +270,16 @@ void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> scrip
SystraceSection s("JSCExecutor::loadApplicationScript",
"sourceURL", sourceURL);
#ifdef WITH_FBJSCEXTENSIONS
if (auto source = dynamic_cast<const JSBigMmapString *>(script.get())) {
loadApplicationSource(m_context, source, sourceURL);
bindBridge();
flush();
ReactMarker::logMarker("CREATE_REACT_CONTEXT_END");
return;
}
#endif
#ifdef WITH_FBSYSTRACE
fbsystrace_begin_section(
TRACE_TAG_REACT_CXX_BRIDGE,

View File

@ -59,12 +59,6 @@ public:
virtual void loadApplicationScript(
std::unique_ptr<const JSBigString> script,
std::string sourceURL) throw(JSException) override;
#ifdef WITH_FBJSCEXTENSIONS
virtual void loadApplicationScript(
std::string bundlePath,
std::string sourceURL,
int flags) override;
#endif
virtual void setJSModulesUnbundle(
std::unique_ptr<JSModulesUnbundle> unbundle) override;
virtual void callFunction(

View File

@ -124,20 +124,6 @@ void NativeToJsBridge::loadApplicationScript(std::unique_ptr<const JSBigString>
m_mainExecutor->loadApplicationScript(std::move(script), std::move(sourceURL));
}
void NativeToJsBridge::loadOptimizedApplicationScript(
std::string bundlePath,
std::string sourceURL,
int flags) {
runOnExecutorQueue(
m_mainExecutorToken,
[bundlePath=std::move(bundlePath),
sourceURL=std::move(sourceURL),
flags=flags]
(JSExecutor* executor) {
executor->loadApplicationScript(std::move(bundlePath), std::move(sourceURL), flags);
});
}
void NativeToJsBridge::loadApplicationUnbundle(
std::unique_ptr<JSModulesUnbundle> unbundle,
std::unique_ptr<const JSBigString> startupScript,

View File

@ -84,12 +84,6 @@ public:
*/
void loadApplicationScript(std::unique_ptr<const JSBigString> script, std::string sourceURL);
/**
* Similar to loading a "bundle", but instead of passing js source this method accepts
* path to a directory containing files prepared for particular JSExecutor.
*/
void loadOptimizedApplicationScript(std::string bundlePath, std::string sourceURL, int flags);
/**
* An "unbundle" is a backend that stores and injects JavaScript modules as
* individual scripts, rather than bundling all of them into a single scrupt.