Skip to content

Commit 2b0e11c

Browse files
dratwaskelset
authored andcommitted
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 #21282 Pull Request resolved: #24967 Differential Revision: D15575924 Pulled By: cpojer fbshipit-source-id: 5ea428e0b793edd8242243f39f933d1092b35260 # Conflicts: # ReactCommon/cxxreact/JSIndexedRAMBundle.cpp
1 parent ebe2827 commit 2b0e11c

File tree

5 files changed

+82
-30
lines changed

5 files changed

+82
-30
lines changed

ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ void CatalystInstanceImpl::jniLoadScriptFromAssets(
197197
sourceURL,
198198
loadSynchronously);
199199
return;
200+
} else if (Instance::isIndexedRAMBundle(&script)) {
201+
instance_->loadRAMBundleFromString(std::move(script), sourceURL);
200202
} else {
201203
instance_->loadScriptFromString(std::move(script), sourceURL, loadSynchronously);
202204
}

ReactCommon/cxxreact/Instance.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,24 @@ bool Instance::isIndexedRAMBundle(const char *sourcePath) {
108108
return parseTypeFromHeader(header) == ScriptTag::RAMBundle;
109109
}
110110

111+
bool Instance::isIndexedRAMBundle(std::unique_ptr<const JSBigString>* script) {
112+
BundleHeader header;
113+
strncpy(reinterpret_cast<char *>(&header), script->get()->c_str(), sizeof(header));
114+
115+
return parseTypeFromHeader(header) == ScriptTag::RAMBundle;
116+
}
117+
118+
void Instance::loadRAMBundleFromString(std::unique_ptr<const JSBigString> script, const std::string& sourceURL) {
119+
auto bundle = folly::make_unique<JSIndexedRAMBundle>(std::move(script));
120+
auto startupScript = bundle->getStartupCode();
121+
auto registry = RAMBundleRegistry::singleBundleRegistry(std::move(bundle));
122+
loadRAMBundle(
123+
std::move(registry),
124+
std::move(startupScript),
125+
sourceURL,
126+
true);
127+
}
128+
111129
void Instance::loadRAMBundleFromFile(const std::string& sourcePath,
112130
const std::string& sourceURL,
113131
bool loadSynchronously) {

ReactCommon/cxxreact/Instance.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ class RN_EXPORT Instance {
4747
void loadScriptFromString(std::unique_ptr<const JSBigString> string,
4848
std::string sourceURL, bool loadSynchronously);
4949
static bool isIndexedRAMBundle(const char *sourcePath);
50+
static bool isIndexedRAMBundle(std::unique_ptr<const JSBigString>* string);
51+
void loadRAMBundleFromString(std::unique_ptr<const JSBigString> script, const std::string& sourceURL);
5052
void loadRAMBundleFromFile(const std::string& sourcePath,
5153
const std::string& sourceURL,
5254
bool loadSynchronously);

ReactCommon/cxxreact/JSIndexedRAMBundle.cpp

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,50 @@
66
#include "JSIndexedRAMBundle.h"
77

88
#include <folly/Memory.h>
9+
#include <glog/logging.h>
10+
#include <fstream>
11+
#include <sstream>
912

1013
namespace facebook {
1114
namespace react {
1215

13-
std::function<std::unique_ptr<JSModulesUnbundle>(std::string)> JSIndexedRAMBundle::buildFactory() {
14-
return [](const std::string& bundlePath){
16+
std::function<std::unique_ptr<JSModulesUnbundle>(std::string)>
17+
JSIndexedRAMBundle::buildFactory() {
18+
return [](const std::string &bundlePath) {
1519
return folly::make_unique<JSIndexedRAMBundle>(bundlePath.c_str());
1620
};
1721
}
1822

19-
JSIndexedRAMBundle::JSIndexedRAMBundle(const char *sourcePath) :
20-
m_bundle (sourcePath, std::ios_base::in) {
23+
JSIndexedRAMBundle::JSIndexedRAMBundle(const char *sourcePath) {
24+
m_bundle = std::make_unique<std::ifstream>(sourcePath, std::ifstream::binary);
2125
if (!m_bundle) {
22-
throw std::ios_base::failure(
23-
folly::to<std::string>("Bundle ", sourcePath,
24-
"cannot be opened: ", m_bundle.rdstate()));
26+
throw std::ios_base::failure(folly::to<std::string>(
27+
"Bundle ", sourcePath, "cannot be opened: ", m_bundle->rdstate()));
28+
}
29+
init();
30+
}
31+
32+
JSIndexedRAMBundle::JSIndexedRAMBundle(
33+
std::unique_ptr<const JSBigString> script) {
34+
// tmpStream is needed because m_bundle is std::istream type
35+
// which has no member 'write'
36+
std::unique_ptr<std::stringstream> tmpStream =
37+
std::make_unique<std::stringstream>();
38+
tmpStream->write(script->c_str(), script->size());
39+
m_bundle = std::move(tmpStream);
40+
if (!m_bundle) {
41+
throw std::ios_base::failure(folly::to<std::string>(
42+
"Bundle from string cannot be opened: ", m_bundle->rdstate()));
2543
}
44+
init();
45+
}
2646

47+
void JSIndexedRAMBundle::init() {
2748
// read in magic header, number of entries, and length of the startup section
2849
uint32_t header[3];
2950
static_assert(
30-
sizeof(header) == 12,
31-
"header size must exactly match the input file format");
51+
sizeof(header) == 12,
52+
"header size must exactly match the input file format");
3253

3354
readBundle(reinterpret_cast<char *>(header), sizeof(header));
3455
const size_t numTableEntries = folly::Endian::little(header[1]);
@@ -40,62 +61,69 @@ JSIndexedRAMBundle::JSIndexedRAMBundle(const char *sourcePath) :
4061

4162
// read the lookup table from the file
4263
readBundle(
43-
reinterpret_cast<char *>(m_table.data.get()), m_table.byteLength());
64+
reinterpret_cast<char *>(m_table.data.get()), m_table.byteLength());
4465

4566
// read the startup code
46-
m_startupCode = std::unique_ptr<JSBigBufferString>(new JSBigBufferString{startupCodeSize - 1});
67+
m_startupCode = std::unique_ptr<JSBigBufferString>(
68+
new JSBigBufferString{startupCodeSize - 1});
4769

4870
readBundle(m_startupCode->data(), startupCodeSize - 1);
4971
}
5072

51-
JSIndexedRAMBundle::Module JSIndexedRAMBundle::getModule(uint32_t moduleId) const {
73+
JSIndexedRAMBundle::Module JSIndexedRAMBundle::getModule(
74+
uint32_t moduleId) const {
5275
Module ret;
5376
ret.name = folly::to<std::string>(moduleId, ".js");
5477
ret.code = getModuleCode(moduleId);
5578
return ret;
5679
}
5780

5881
std::unique_ptr<const JSBigString> JSIndexedRAMBundle::getStartupCode() {
59-
CHECK(m_startupCode) << "startup code for a RAM Bundle can only be retrieved once";
82+
CHECK(m_startupCode)
83+
<< "startup code for a RAM Bundle can only be retrieved once";
6084
return std::move(m_startupCode);
6185
}
6286

6387
std::string JSIndexedRAMBundle::getModuleCode(const uint32_t id) const {
6488
const auto moduleData = id < m_table.numEntries ? &m_table.data[id] : nullptr;
6589

6690
// entries without associated code have offset = 0 and length = 0
67-
const uint32_t length = moduleData ? folly::Endian::little(moduleData->length) : 0;
91+
const uint32_t length =
92+
moduleData ? folly::Endian::little(moduleData->length) : 0;
6893
if (length == 0) {
6994
throw std::ios_base::failure(
70-
folly::to<std::string>("Error loading module", id, "from RAM Bundle"));
95+
folly::to<std::string>("Error loading module", id, "from RAM Bundle"));
7196
}
7297

7398
std::string ret(length - 1, '\0');
74-
readBundle(&ret.front(), length - 1, m_baseOffset + folly::Endian::little(moduleData->offset));
99+
readBundle(
100+
&ret.front(),
101+
length - 1,
102+
m_baseOffset + folly::Endian::little(moduleData->offset));
75103
return ret;
76104
}
77105

78-
void JSIndexedRAMBundle::readBundle(char *buffer, const std::streamsize bytes) const {
79-
if (!m_bundle.read(buffer, bytes)) {
80-
if (m_bundle.rdstate() & std::ios::eofbit) {
106+
void JSIndexedRAMBundle::readBundle(char *buffer, const std::streamsize bytes)
107+
const {
108+
if (!m_bundle->read(buffer, bytes)) {
109+
if (m_bundle->rdstate() & std::ios::eofbit) {
81110
throw std::ios_base::failure("Unexpected end of RAM Bundle file");
82111
}
83-
throw std::ios_base::failure(
84-
folly::to<std::string>("Error reading RAM Bundle: ", m_bundle.rdstate()));
112+
throw std::ios_base::failure(folly::to<std::string>(
113+
"Error reading RAM Bundle: ", m_bundle->rdstate()));
85114
}
86115
}
87116

88117
void JSIndexedRAMBundle::readBundle(
89118
char *buffer,
90119
const std::streamsize bytes,
91120
const std::ifstream::pos_type position) const {
92-
93-
if (!m_bundle.seekg(position)) {
94-
throw std::ios_base::failure(
95-
folly::to<std::string>("Error reading RAM Bundle: ", m_bundle.rdstate()));
121+
if (!m_bundle->seekg(position)) {
122+
throw std::ios_base::failure(folly::to<std::string>(
123+
"Error reading RAM Bundle: ", m_bundle->rdstate()));
96124
}
97125
readBundle(buffer, bytes);
98126
}
99127

100-
} // namespace react
101-
} // namespace facebook
128+
} // namespace react
129+
} // namespace facebook

ReactCommon/cxxreact/JSIndexedRAMBundle.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
#pragma once
77

8-
#include <fstream>
8+
#include <istream>
99
#include <memory>
1010

1111
#include <cxxreact/JSBigString.h>
@@ -24,6 +24,7 @@ class RN_EXPORT JSIndexedRAMBundle : public JSModulesUnbundle {
2424

2525
// Throws std::runtime_error on failure.
2626
JSIndexedRAMBundle(const char *sourceURL);
27+
JSIndexedRAMBundle(std::unique_ptr<const JSBigString> script);
2728

2829
// Throws std::runtime_error on failure.
2930
std::unique_ptr<const JSBigString> getStartupCode();
@@ -51,14 +52,15 @@ class RN_EXPORT JSIndexedRAMBundle : public JSModulesUnbundle {
5152
}
5253
};
5354

55+
void init();
5456
std::string getModuleCode(const uint32_t id) const;
5557
void readBundle(char *buffer, const std::streamsize bytes) const;
5658
void readBundle(
5759
char *buffer, const
5860
std::streamsize bytes,
59-
const std::ifstream::pos_type position) const;
61+
const std::istream::pos_type position) const;
6062

61-
mutable std::ifstream m_bundle;
63+
mutable std::unique_ptr<std::istream> m_bundle;
6264
ModuleTable m_table;
6365
size_t m_baseOffset;
6466
std::unique_ptr<JSBigBufferString> m_startupCode;

0 commit comments

Comments
 (0)