1 /*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 #include "ABI49_0_0JSIndexedRAMBundle.h"
9
10 #include <glog/logging.h>
11 #include <fstream>
12 #include <memory>
13 #include <sstream>
14
15 namespace ABI49_0_0facebook {
16 namespace ABI49_0_0React {
17
18 std::function<std::unique_ptr<JSModulesUnbundle>(std::string)>
buildFactory()19 JSIndexedRAMBundle::buildFactory() {
20 return [](const std::string &bundlePath) {
21 return std::make_unique<JSIndexedRAMBundle>(bundlePath.c_str());
22 };
23 }
24
JSIndexedRAMBundle(const char * sourcePath)25 JSIndexedRAMBundle::JSIndexedRAMBundle(const char *sourcePath) {
26 m_bundle = std::make_unique<std::ifstream>(sourcePath, std::ifstream::binary);
27 if (!m_bundle) {
28 throw std::ios_base::failure(folly::to<std::string>(
29 "Bundle ", sourcePath, "cannot be opened: ", m_bundle->rdstate()));
30 }
31 init();
32 }
33
JSIndexedRAMBundle(std::unique_ptr<const JSBigString> script)34 JSIndexedRAMBundle::JSIndexedRAMBundle(
35 std::unique_ptr<const JSBigString> script) {
36 // tmpStream is needed because m_bundle is std::istream type
37 // which has no member 'write'
38 std::unique_ptr<std::stringstream> tmpStream =
39 std::make_unique<std::stringstream>();
40 tmpStream->write(script->c_str(), script->size());
41 m_bundle = std::move(tmpStream);
42 if (!m_bundle) {
43 throw std::ios_base::failure(folly::to<std::string>(
44 "Bundle from string cannot be opened: ", m_bundle->rdstate()));
45 }
46 init();
47 }
48
init()49 void JSIndexedRAMBundle::init() {
50 // read in magic header, number of entries, and length of the startup section
51 uint32_t header[3];
52 static_assert(
53 sizeof(header) == 12,
54 "header size must exactly match the input file format");
55
56 readBundle(reinterpret_cast<char *>(header), sizeof(header));
57 const size_t numTableEntries = folly::Endian::little(header[1]);
58 const size_t startupCodeSize = folly::Endian::little(header[2]);
59
60 // allocate memory for meta data and lookup table.
61 m_table = ModuleTable(numTableEntries);
62 m_baseOffset = sizeof(header) + m_table.byteLength();
63
64 // read the lookup table from the file
65 readBundle(
66 reinterpret_cast<char *>(m_table.data.get()), m_table.byteLength());
67
68 // read the startup code
69 m_startupCode = std::unique_ptr<JSBigBufferString>(
70 new JSBigBufferString{startupCodeSize - 1});
71
72 readBundle(m_startupCode->data(), startupCodeSize - 1);
73 }
74
getModule(uint32_t moduleId) const75 JSIndexedRAMBundle::Module JSIndexedRAMBundle::getModule(
76 uint32_t moduleId) const {
77 Module ret;
78 ret.name = folly::to<std::string>(moduleId, ".js");
79 ret.code = getModuleCode(moduleId);
80 return ret;
81 }
82
getStartupCode()83 std::unique_ptr<const JSBigString> JSIndexedRAMBundle::getStartupCode() {
84 CHECK(m_startupCode)
85 << "startup code for a RAM Bundle can only be retrieved once";
86 return std::move(m_startupCode);
87 }
88
getModuleCode(const uint32_t id) const89 std::string JSIndexedRAMBundle::getModuleCode(const uint32_t id) const {
90 const auto moduleData = id < m_table.numEntries ? &m_table.data[id] : nullptr;
91
92 // entries without associated code have offset = 0 and length = 0
93 const uint32_t length =
94 moduleData ? folly::Endian::little(moduleData->length) : 0;
95 if (length == 0) {
96 throw std::ios_base::failure(
97 folly::to<std::string>("Error loading module", id, "from RAM Bundle"));
98 }
99
100 std::string ret(length - 1, '\0');
101 readBundle(
102 &ret.front(),
103 length - 1,
104 m_baseOffset + folly::Endian::little(moduleData->offset));
105 return ret;
106 }
107
readBundle(char * buffer,const std::streamsize bytes) const108 void JSIndexedRAMBundle::readBundle(char *buffer, const std::streamsize bytes)
109 const {
110 if (!m_bundle->read(buffer, bytes)) {
111 if (m_bundle->rdstate() & std::ios::eofbit) {
112 throw std::ios_base::failure("Unexpected end of RAM Bundle file");
113 }
114 throw std::ios_base::failure(folly::to<std::string>(
115 "Error reading RAM Bundle: ", m_bundle->rdstate()));
116 }
117 }
118
readBundle(char * buffer,const std::streamsize bytes,const std::ifstream::pos_type position) const119 void JSIndexedRAMBundle::readBundle(
120 char *buffer,
121 const std::streamsize bytes,
122 const std::ifstream::pos_type position) const {
123 if (!m_bundle->seekg(position)) {
124 throw std::ios_base::failure(folly::to<std::string>(
125 "Error reading RAM Bundle: ", m_bundle->rdstate()));
126 }
127 readBundle(buffer, bytes);
128 }
129
130 } // namespace ABI49_0_0React
131 } // namespace ABI49_0_0facebook
132