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 "ABI47_0_0JSIndexedRAMBundle.h"
9 
10 #include <glog/logging.h>
11 #include <fstream>
12 #include <memory>
13 #include <sstream>
14 
15 namespace ABI47_0_0facebook {
16 namespace ABI47_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 ABI47_0_0React
131 } // namespace ABI47_0_0facebook
132