LWNode_Release_211109_610b330
[platform/framework/web/lwnode.git] / src / node_native_module_lwnode-inl.h
1 /*
2  * Copyright (c) 2021-present Samsung Electronics Co., Ltd
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #pragma once
18
19 #include <unzip.h>
20 #include <codecvt>
21 #include <locale>
22 #include <map>
23 #include "lwnode-loader.h"
24 #include "lwnode.h"
25 #include "node_native_module.h"
26 #include "trace.h"
27
28 using namespace LWNode;
29
30 namespace node {
31 namespace native_module {
32 using v8::Isolate;
33 using v8::Local;
34 using v8::MaybeLocal;
35 using v8::String;
36
37 class ArchiveFileScope {
38  public:
39   ArchiveFileScope() = default;
40   ArchiveFileScope(const char* path) { open(path); }
41   ~ArchiveFileScope() {
42     if (file_) {
43       unzClose(file_);
44     }
45   }
46
47   void open(const char* path) {
48     if (file_ == nullptr) {
49       file_ = unzOpen(path);
50     }
51   }
52
53   bool isFileOpened() { return (file_ != nullptr) ? true : false; }
54   unzFile file() { return file_; }
55
56  private:
57   unzFile file_{nullptr};
58 };
59
60 struct UnzFileCachedInfo {
61   unz_file_pos position{0, 0};
62   uLong uncompressedSize{0};
63 };
64
65 static ArchiveFileScope s_archiveFileScope;
66 static std::map<std::string, UnzFileCachedInfo> s_unzFileInfoDictionary;
67
68 std::string getSelfProcPath() {
69   char path[PATH_MAX + 1];
70   ssize_t length = readlink("/proc/self/exe", path, PATH_MAX);
71   if (length < 0) {
72     ERROR_AND_ABORT("readlink fails");
73   }
74   path[length] = '\0';
75   return std::string(path);
76 }
77
78 bool readCurrentFileFromArchive(const unzFile file,
79                                 uLong uncompressedSize,
80                                 char** buffer,
81                                 size_t* fileSize) {
82   if (unzOpenCurrentFile(file) < 0) {
83     return false;
84   }
85
86   *fileSize = uncompressedSize;
87   *buffer = (char*)allocateStringBuffer(uncompressedSize + 1);
88
89   bool result = true;
90
91   if (unzReadCurrentFile(file, *buffer, *fileSize) < 0) {
92     freeStringBuffer(buffer);
93     buffer = nullptr;
94     result = false;
95   } else {
96     (*buffer)[*fileSize] = '\0';
97   }
98
99   unzCloseCurrentFile(file);
100   return result;
101 }
102
103 bool readFileFromArchive(const std::string& archiveFilename,
104                          const std::string& filename,
105                          char** buffer,
106                          size_t* fileSize) {
107   DCHECK_NOT_NULL(buffer);
108   DCHECK_NOT_NULL(fileSize);
109
110   if (s_archiveFileScope.isFileOpened() == false) {
111     s_archiveFileScope.open(archiveFilename.c_str());
112   }
113
114   const unzFile file = s_archiveFileScope.file();
115   CHECK_NOT_NULL(file);
116
117   // 1.1 check if the cache on this filename exists
118   const auto& it = s_unzFileInfoDictionary.find(filename);
119   if (it != s_unzFileInfoDictionary.end()) {
120     UnzFileCachedInfo cache = it->second;
121
122     // 1.2 move the file position using the cached info and read the data
123     unzGoToFilePos(file, &cache.position);
124     return readCurrentFileFromArchive(
125         file, cache.uncompressedSize, buffer, fileSize);
126   }
127
128   // 2. read the data by searching the file position from the first one.
129   if (unzGoToFirstFile(file) < 0) {
130     return false;
131   }
132
133   bool isFileFound = false;
134   do {
135     unz_file_info fileInfo;
136     char currentFileName[PATH_MAX];
137
138     if (unzGetCurrentFileInfo(file,
139                               &fileInfo,
140                               currentFileName,
141                               sizeof(currentFileName),
142                               nullptr,
143                               0,
144                               nullptr,
145                               0) < 0) {
146       return false;
147     }
148
149     if (filename.compare(currentFileName) == 0) {
150       isFileFound = true;
151
152       // 2.1 read the data from the current file poistion
153       if (readCurrentFileFromArchive(
154               file, fileInfo.uncompressed_size, buffer, fileSize) == false) {
155         return false;
156       }
157
158       // 2.2 create the cache for this file and register it to the dictionary
159       UnzFileCachedInfo cache;
160       auto result = unzGetFilePos(file, &cache.position);
161       CHECK(result == UNZ_OK);
162       cache.uncompressedSize = fileInfo.uncompressed_size;
163
164       s_unzFileInfoDictionary[filename] = cache;
165       break;
166     }
167
168   } while (unzGoToNextFile(file) != UNZ_END_OF_LIST_OF_FILE);
169
170   return isFileFound;
171 }
172
173 FileData readFileFromArchive(std::string filename,
174                              const Encoding encodingHint) {
175   CHECK(encodingHint != Encoding::kUnknown);
176
177   size_t bufferSize = 0;
178   char* buffer = nullptr;
179
180   static std::string s_externalBuiltinsPath;
181
182   if (s_externalBuiltinsPath.empty()) {
183     std::string executablePath = getSelfProcPath();
184     executablePath = executablePath.substr(0, executablePath.rfind('/') + 1);
185     s_externalBuiltinsPath = executablePath + LWNODE_EXTERNAL_BUILTINS_FILENAME;
186   }
187
188   if (readFileFromArchive(
189           s_externalBuiltinsPath, filename, &buffer, &bufferSize) == false) {
190     return FileData();
191   }
192
193   std::unique_ptr<void, std::function<void(void*)>> bufferHolder(
194       buffer, freeStringBuffer);
195
196   return Loader::createFileDataForReloadableString(
197       filename, std::move(bufferHolder), bufferSize, encodingHint);
198 }
199
200 bool NativeModuleLoader::IsOneByte(const char* id) {
201   const auto& it = source_.find(id);
202   if (it == source_.end()) {
203     CHECK(false);
204   }
205   return it->second.is_one_byte();
206 }
207
208 static std::string getFileNameOnArchive(const char* id) {
209   std::string filename;
210   if (strncmp(id, "internal/deps", strlen("internal/deps")) == 0) {
211     id += strlen("internal/");
212   } else {
213     filename += "lib/";
214   }
215   filename += id;
216   filename += ".js";
217   return filename;
218 }
219
220 class SourceReaderOnArchive : public SourceReaderInterface {
221  public:
222   static SourceReaderOnArchive* getInstance() {
223     static SourceReaderOnArchive s_singleton;
224     return &s_singleton;
225   }
226
227   FileData read(std::string filename, const Encoding encodingHint) {
228     CHECK(encodingHint != Encoding::kUnknown);
229
230     size_t bufferSize = 0;
231     char* buffer = nullptr;
232
233     static std::string s_externalBuiltinsPath;
234
235     if (s_externalBuiltinsPath.empty()) {
236       std::string executablePath = getSelfProcPath();
237       executablePath = executablePath.substr(0, executablePath.rfind('/') + 1);
238       s_externalBuiltinsPath =
239           executablePath + LWNODE_EXTERNAL_BUILTINS_FILENAME;
240     }
241
242     if (readFileFromArchive(
243             s_externalBuiltinsPath, filename, &buffer, &bufferSize) == false) {
244       return FileData();
245     }
246
247     std::unique_ptr<void, std::function<void(void*)>> bufferHolder(
248         buffer, freeStringBuffer);
249
250     return Loader::createFileDataForReloadableString(
251         filename, std::move(bufferHolder), bufferSize, encodingHint);
252   }
253
254  private:
255   SourceReaderOnArchive() = default;
256 };
257
258 MaybeLocal<String> NativeModuleLoader::LoadExternalBuiltinSource(
259     Isolate* isolate, const char* id) {
260   std::string filename = getFileNameOnArchive(id);
261
262   auto sourceReader = SourceReaderOnArchive::getInstance();
263
264   FileData fileData = sourceReader->read(
265       filename, (IsOneByte(id) ? Encoding::kAscii : Encoding::kUtf16));
266
267   if (fileData.buffer == nullptr) {
268     ERROR_AND_ABORT("Failed to open builtins");
269     return MaybeLocal<String>();
270   }
271
272   return Loader::NewReloadableString(
273       isolate, Loader::ReloadableSourceData::create(fileData, sourceReader));
274 }
275
276 }  // namespace native_module
277 }  // namespace node