fix indexed RAM bundle (#24967)

Summary:
Co-Authored: zamotany
With React Native 0.59.8 the app keeps crashing with indexed RAM bundle on Android with the following error:

```
2019-05-09 11:58:06.684 2793-2856/? E/AndroidRuntime: FATAL EXCEPTION: mqt_js
    Process: com.ramtestapp, PID: 2793
    com.facebook.jni.CppException: getPropertyAsObject: property '__fbRequireBatchedBridge' is not an Object

    no stack
        at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:29)
        at android.os.Looper.loop(Looper.java:193)
        at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run(MessageQueueThreadImpl.java:232)
        at java.lang.Thread.run(Thread.java:764)
```

After investigation we found that when using any bundle, let it be non-ram, FIle RAM bundle or Index RAM bundle, the `CatalystInstanceImpl.java` is always using `loadScriptsFromAsset`, which is calling `CatalystInstanceImpl::jniLoadScriptFromAssets` in C++. This method when checking if bundle is a RAM bundle, uses `JniJSModulesUnbundle::isUnbundle` which only check for js-modules/UNBUNDLE - file generated when building File RAM bundle. There is no other logic to handle Indexed RAM bundle, so it figures that the bundle is not RAM, cause there is no js-modules/UNBUNDLE file and tries to load as regular bundle and fails.

In this PR we added check if it is indexed RAM bundle in `jniLoadScriptFromAssets` and handle it if it is.
## Changelog
[Android] [Fixed] fix indexed RAM bundle

Solves https://github.com/facebook/react-native/issues/21282
Pull Request resolved: https://github.com/facebook/react-native/pull/24967

Differential Revision: D15575924

Pulled By: cpojer

fbshipit-source-id: 5ea428e0b793edd8242243f39f933d1092b35260

# Conflicts:
#	ReactCommon/cxxreact/JSIndexedRAMBundle.cpp
This commit is contained in:
Dratwas
2019-06-05 13:53:58 +01:00
committed by Lorenzo Sciandra
parent ebe282773d
commit 2b0e11cc91
5 changed files with 82 additions and 30 deletions
+55 -27
View File
@@ -6,29 +6,50 @@
#include "JSIndexedRAMBundle.h"
#include <folly/Memory.h>
#include <glog/logging.h>
#include <fstream>
#include <sstream>
namespace facebook {
namespace react {
std::function<std::unique_ptr<JSModulesUnbundle>(std::string)> JSIndexedRAMBundle::buildFactory() {
return [](const std::string& bundlePath){
std::function<std::unique_ptr<JSModulesUnbundle>(std::string)>
JSIndexedRAMBundle::buildFactory() {
return [](const std::string &bundlePath) {
return folly::make_unique<JSIndexedRAMBundle>(bundlePath.c_str());
};
}
JSIndexedRAMBundle::JSIndexedRAMBundle(const char *sourcePath) :
m_bundle (sourcePath, std::ios_base::in) {
JSIndexedRAMBundle::JSIndexedRAMBundle(const char *sourcePath) {
m_bundle = std::make_unique<std::ifstream>(sourcePath, std::ifstream::binary);
if (!m_bundle) {
throw std::ios_base::failure(
folly::to<std::string>("Bundle ", sourcePath,
"cannot be opened: ", m_bundle.rdstate()));
throw std::ios_base::failure(folly::to<std::string>(
"Bundle ", sourcePath, "cannot be opened: ", m_bundle->rdstate()));
}
init();
}
JSIndexedRAMBundle::JSIndexedRAMBundle(
std::unique_ptr<const JSBigString> script) {
// tmpStream is needed because m_bundle is std::istream type
// which has no member 'write'
std::unique_ptr<std::stringstream> tmpStream =
std::make_unique<std::stringstream>();
tmpStream->write(script->c_str(), script->size());
m_bundle = std::move(tmpStream);
if (!m_bundle) {
throw std::ios_base::failure(folly::to<std::string>(
"Bundle from string cannot be opened: ", m_bundle->rdstate()));
}
init();
}
void JSIndexedRAMBundle::init() {
// read in magic header, number of entries, and length of the startup section
uint32_t header[3];
static_assert(
sizeof(header) == 12,
"header size must exactly match the input file format");
sizeof(header) == 12,
"header size must exactly match the input file format");
readBundle(reinterpret_cast<char *>(header), sizeof(header));
const size_t numTableEntries = folly::Endian::little(header[1]);
@@ -40,15 +61,17 @@ JSIndexedRAMBundle::JSIndexedRAMBundle(const char *sourcePath) :
// read the lookup table from the file
readBundle(
reinterpret_cast<char *>(m_table.data.get()), m_table.byteLength());
reinterpret_cast<char *>(m_table.data.get()), m_table.byteLength());
// read the startup code
m_startupCode = std::unique_ptr<JSBigBufferString>(new JSBigBufferString{startupCodeSize - 1});
m_startupCode = std::unique_ptr<JSBigBufferString>(
new JSBigBufferString{startupCodeSize - 1});
readBundle(m_startupCode->data(), startupCodeSize - 1);
}
JSIndexedRAMBundle::Module JSIndexedRAMBundle::getModule(uint32_t moduleId) const {
JSIndexedRAMBundle::Module JSIndexedRAMBundle::getModule(
uint32_t moduleId) const {
Module ret;
ret.name = folly::to<std::string>(moduleId, ".js");
ret.code = getModuleCode(moduleId);
@@ -56,7 +79,8 @@ JSIndexedRAMBundle::Module JSIndexedRAMBundle::getModule(uint32_t moduleId) cons
}
std::unique_ptr<const JSBigString> JSIndexedRAMBundle::getStartupCode() {
CHECK(m_startupCode) << "startup code for a RAM Bundle can only be retrieved once";
CHECK(m_startupCode)
<< "startup code for a RAM Bundle can only be retrieved once";
return std::move(m_startupCode);
}
@@ -64,24 +88,29 @@ std::string JSIndexedRAMBundle::getModuleCode(const uint32_t id) const {
const auto moduleData = id < m_table.numEntries ? &m_table.data[id] : nullptr;
// entries without associated code have offset = 0 and length = 0
const uint32_t length = moduleData ? folly::Endian::little(moduleData->length) : 0;
const uint32_t length =
moduleData ? folly::Endian::little(moduleData->length) : 0;
if (length == 0) {
throw std::ios_base::failure(
folly::to<std::string>("Error loading module", id, "from RAM Bundle"));
folly::to<std::string>("Error loading module", id, "from RAM Bundle"));
}
std::string ret(length - 1, '\0');
readBundle(&ret.front(), length - 1, m_baseOffset + folly::Endian::little(moduleData->offset));
readBundle(
&ret.front(),
length - 1,
m_baseOffset + folly::Endian::little(moduleData->offset));
return ret;
}
void JSIndexedRAMBundle::readBundle(char *buffer, const std::streamsize bytes) const {
if (!m_bundle.read(buffer, bytes)) {
if (m_bundle.rdstate() & std::ios::eofbit) {
void JSIndexedRAMBundle::readBundle(char *buffer, const std::streamsize bytes)
const {
if (!m_bundle->read(buffer, bytes)) {
if (m_bundle->rdstate() & std::ios::eofbit) {
throw std::ios_base::failure("Unexpected end of RAM Bundle file");
}
throw std::ios_base::failure(
folly::to<std::string>("Error reading RAM Bundle: ", m_bundle.rdstate()));
throw std::ios_base::failure(folly::to<std::string>(
"Error reading RAM Bundle: ", m_bundle->rdstate()));
}
}
@@ -89,13 +118,12 @@ void JSIndexedRAMBundle::readBundle(
char *buffer,
const std::streamsize bytes,
const std::ifstream::pos_type position) const {
if (!m_bundle.seekg(position)) {
throw std::ios_base::failure(
folly::to<std::string>("Error reading RAM Bundle: ", m_bundle.rdstate()));
if (!m_bundle->seekg(position)) {
throw std::ios_base::failure(folly::to<std::string>(
"Error reading RAM Bundle: ", m_bundle->rdstate()));
}
readBundle(buffer, bytes);
}
} // namespace react
} // namespace facebook
} // namespace react
} // namespace facebook