-
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathRequire.cpp
More file actions
204 lines (164 loc) · 5.79 KB
/
Require.cpp
File metadata and controls
204 lines (164 loc) · 5.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#include "Require.h"
#include "NapiUtil.h"
#include <dlfcn.h>
#include <iostream>
#include <string>
#include <dlfcn.h>
napi_value Require::createRequire(napi_env env, std::string &path,
std::string &tilde, Require **pRequire) {
Require *require = new Require(path, tilde);
if (pRequire) {
*pRequire = require;
}
napi_value result;
napi_create_function(env, "require", NAPI_AUTO_LENGTH,
Require::requireCallback, require, &result);
napi_ref ref;
napi_wrap(env, result, require, Require::finalize, nullptr, &ref);
return result;
}
Require *Require::init(napi_env env, std::string &path, std::string &tilde) {
napi_value global;
napi_get_global(env, &global);
Require *out = nullptr;
napi_value require = createRequire(env, path, tilde, &out);
napi_set_named_property(env, global, "require", require);
return out;
}
void Require::finalize(napi_env env, void *data, void *hint) {
Require *require = (Require *)data;
delete require;
}
std::string Require::resolve(std::string &spec) {
if (spec.find("/") == 0) {
return spec;
}
size_t dotpos = spec.find(".");
size_t tildepos = spec.find("~");
std::string result;
if (tildepos == 0) {
result = tilde;
result += "/app";
} else {
result = path;
}
if (dotpos == 0 || tildepos == 0) {
std::string relativeSpec =
spec.substr(spec.find("./") == 0 || spec.find("~/") == 0 ? 2 : 1);
if (relativeSpec.empty()) {
result += "/index.js";
} else {
result += "/" + relativeSpec;
}
} else {
result += "/" + spec;
}
size_t pos = result.rfind("/");
size_t jspos = result.find(".js");
size_t dylibpos = result.rfind(".dylib");
if ((jspos < pos || jspos == std::string::npos) && !(dylibpos < pos)) {
// result += result.ends_with("/") ? "index.js" : "/index.js";
}
return result;
}
void finalize_dlobject(napi_env env, void *finalize_data, void *finalize_hint) {
void *handle = finalize_data;
dlclose(handle);
}
typedef napi_value napi_module_register_fn(napi_env env, napi_value exports);
napi_value Require::require(napi_env env, std::string &spec) {
std::string path = resolve(spec);
if (path.ends_with(".node") || path.ends_with(".dylib") ||
path.ends_with(".so")) {
void *handle = dlopen(path.c_str(), RTLD_GLOBAL | RTLD_LAZY);
if (!handle) {
std::cerr << "error in dlopen: " << dlerror() << std::endl;
return nullptr;
}
void *sym = dlsym(handle, "napi_register_module_v1");
if (!sym) {
std::cerr << "error in dlsym: " << dlerror() << std::endl;
return nullptr;
}
napi_value module, exports;
napi_create_object(env, &module);
napi_create_object(env, &exports);
napi_set_named_property(env, module, "exports", exports);
napi_module_register_fn *register_fn = (napi_module_register_fn *)sym;
exports = register_fn(env, exports);
napi_ref ref;
napi_add_finalizer(env, module, handle, finalize_dlobject, nullptr, &ref);
return module;
}
// std::cout << "================\nrequire: " << path << std::endl;
char *source = nullptr;
size_t size = 0;
FILE *file = fopen(path.c_str(), "r");
if (file) {
fseek(file, 0, SEEK_END);
size = ftell(file);
fseek(file, 0, SEEK_SET);
source = (char *)malloc(size + 1);
fread(source, 1, size, file);
source[size] = 0;
fclose(file);
} else {
std::cerr << "error in open file: " << path << std::endl;
return nullptr;
}
if (spec.ends_with(".json")) {
napi_value script, result;
napi_create_string_utf8(env, source, NAPI_AUTO_LENGTH, &script);
napi_run_script(env, script, &result);
return result;
}
std::string bootstrap;
bootstrap = "let cjsModule; try { cjsModule = function c(exports, "
"require, module, __filename, __dirname) {";
bootstrap += source;
bootstrap += "\n};\n"
"Object.defineProperty(cjsModule, \"name\", { value: `" +
path +
"` });\n} catch (e) { throw new Error(`Failed to evaluate "
"module: ${e.stack}`); }\n cjsModule";
napi_status status;
napi_value func, script, module, exports, require, __filename, __dirname,
global, result;
napi_create_string_utf8(env, bootstrap.c_str(), NAPI_AUTO_LENGTH, &script);
status = napi_run_script(env, script, &func);
if (status != napi_ok) {
const napi_extended_error_info *info;
napi_get_last_error_info(env, &info);
std::cerr << "error in run script: " << status << ", "
<< info->error_message << std::endl;
return nullptr;
}
napi_create_object(env, &module);
napi_create_object(env, &exports);
napi_set_named_property(env, module, "exports", exports);
napi_get_global(env, &global);
napi_create_string_utf8(env, path.c_str(), NAPI_AUTO_LENGTH, &__filename);
std::string dirname = path.substr(0, path.rfind("/"));
require = Require::createRequire(env, dirname, tilde);
napi_create_string_utf8(env, dirname.c_str(), NAPI_AUTO_LENGTH, &__dirname);
napi_value argv[5] = {exports, require, module, __filename, __dirname};
status = napi_call_function(env, global, func, 5, argv, &result);
if (status != napi_ok) {
const napi_extended_error_info *info;
napi_get_last_error_info(env, &info);
std::cerr << "error in evaluate module: " << status << ", "
<< ((info->error_message == nullptr) ? "" : info->error_message)
<< std::endl;
return nullptr;
}
napi_get_named_property(env, module, "exports", &exports);
return exports;
}
napi_value Require::requireCallback(napi_env env, napi_callback_info cbinfo) {
napi_value arg;
Require *require;
size_t argc = 1;
napi_get_cb_info(env, cbinfo, &argc, &arg, nullptr, (void **)&require);
std::string spec = getStringValue(env, arg);
return require->require(env, spec);
}