3
3
#include " node_internals.h"
4
4
5
5
namespace node {
6
+
7
+ namespace per_process {
8
+ native_module::NativeModuleLoader native_module_loader;
9
+ } // namespace per_process
10
+
6
11
namespace native_module {
7
12
8
13
using v8::Array;
@@ -78,13 +83,14 @@ void NativeModuleLoader::GetCacheUsage(
78
83
void NativeModuleLoader::SourceObjectGetter (
79
84
Local<Name> property, const PropertyCallbackInfo<Value>& info) {
80
85
Local<Context> context = info.GetIsolate ()->GetCurrentContext ();
81
- info.GetReturnValue ().Set (per_process_loader.GetSourceObject (context));
86
+ info.GetReturnValue ().Set (
87
+ per_process::native_module_loader.GetSourceObject (context));
82
88
}
83
89
84
90
void NativeModuleLoader::ConfigStringGetter (
85
91
Local<Name> property, const PropertyCallbackInfo<Value>& info) {
86
92
info.GetReturnValue ().Set (
87
- per_process_loader .GetConfigString (info.GetIsolate ()));
93
+ per_process::native_module_loader .GetConfigString (info.GetIsolate ()));
88
94
}
89
95
90
96
Local<Object> NativeModuleLoader::GetSourceObject (
@@ -96,41 +102,62 @@ Local<String> NativeModuleLoader::GetConfigString(Isolate* isolate) const {
96
102
return config_.ToStringChecked (isolate);
97
103
}
98
104
99
- Local<String> NativeModuleLoader::GetSource (Isolate* isolate,
100
- const char * id) const {
101
- const auto it = source_.find (id);
102
- CHECK_NE (it, source_.end ());
103
- return it->second .ToStringChecked (isolate);
104
- }
105
-
106
105
NativeModuleLoader::NativeModuleLoader () : config_(GetConfig()) {
107
106
LoadJavaScriptSource ();
108
107
LoadCodeCache ();
109
108
}
110
109
111
- void NativeModuleLoader::CompileCodeCache (
112
- const FunctionCallbackInfo<Value>& args) {
110
+ // This is supposed to be run only by the main thread in
111
+ // tools/generate_code_cache.js
112
+ void NativeModuleLoader::GetCodeCache (const FunctionCallbackInfo<Value>& args) {
113
113
Environment* env = Environment::GetCurrent (args);
114
+ Isolate* isolate = env->isolate ();
115
+ CHECK (env->is_main_thread ());
116
+
114
117
CHECK (args[0 ]->IsString ());
115
- node::Utf8Value id (env->isolate (), args[0 ].As <String>());
118
+ node::Utf8Value id_v (isolate, args[0 ].As <String>());
119
+ const char * id = *id_v;
116
120
117
- // TODO(joyeecheung): allow compiling cache for bootstrapper by
118
- // switching on id
119
- MaybeLocal<Value> result =
120
- CompileAsModule (env, *id, CompilationResultType::kCodeCache );
121
- if (!result.IsEmpty ()) {
122
- args.GetReturnValue ().Set (result.ToLocalChecked ());
121
+ const NativeModuleLoader& loader = per_process::native_module_loader;
122
+ MaybeLocal<Uint8Array> ret = loader.GetCodeCache (isolate, id);
123
+ if (!ret.IsEmpty ()) {
124
+ args.GetReturnValue ().Set (ret.ToLocalChecked ());
123
125
}
124
126
}
125
127
128
+ // This is supposed to be run only by the main thread in
129
+ // tools/generate_code_cache.js
130
+ MaybeLocal<Uint8Array> NativeModuleLoader::GetCodeCache (Isolate* isolate,
131
+ const char * id) const {
132
+ EscapableHandleScope scope (isolate);
133
+ Mutex::ScopedLock lock (code_cache_mutex_);
134
+
135
+ ScriptCompiler::CachedData* cached_data = nullptr ;
136
+ const auto it = code_cache_.find (id);
137
+ if (it == code_cache_.end ()) {
138
+ // The module has not been compiled before.
139
+ return MaybeLocal<Uint8Array>();
140
+ }
141
+
142
+ cached_data = it->second .get ();
143
+
144
+ MallocedBuffer<uint8_t > copied (cached_data->length );
145
+ memcpy (copied.data , cached_data->data , cached_data->length );
146
+ Local<ArrayBuffer> buf =
147
+ ArrayBuffer::New (isolate,
148
+ copied.release (),
149
+ cached_data->length ,
150
+ ArrayBufferCreationMode::kInternalized );
151
+ return scope.Escape (Uint8Array::New (buf, 0 , cached_data->length ));
152
+ }
153
+
126
154
void NativeModuleLoader::CompileFunction (
127
155
const FunctionCallbackInfo<Value>& args) {
128
156
Environment* env = Environment::GetCurrent (args);
129
157
CHECK (args[0 ]->IsString ());
130
158
node::Utf8Value id (env->isolate (), args[0 ].As <String>());
131
159
132
- MaybeLocal<Value> result =
133
- CompileAsModule (env, *id, CompilationResultType::kFunction );
160
+ MaybeLocal<Function> result = CompileAsModule (env, *id);
134
161
if (!result.IsEmpty ()) {
135
162
args.GetReturnValue ().Set (result.ToLocalChecked ());
136
163
}
@@ -145,57 +172,43 @@ MaybeLocal<Value> NativeModuleLoader::CompileAndCall(
145
172
std::vector<Local<Value>>* arguments,
146
173
Environment* optional_env) {
147
174
Isolate* isolate = context->GetIsolate ();
148
- MaybeLocal<Value> compiled = per_process_loader.LookupAndCompile (
149
- context, id, parameters, CompilationResultType::kFunction , nullptr );
175
+ MaybeLocal<Function> compiled =
176
+ per_process::native_module_loader.LookupAndCompile (
177
+ context, id, parameters, nullptr );
150
178
if (compiled.IsEmpty ()) {
151
- return compiled ;
179
+ return MaybeLocal<Value>() ;
152
180
}
153
181
Local<Function> fn = compiled.ToLocalChecked ().As <Function>();
154
182
return fn->Call (
155
183
context, v8::Null (isolate), arguments->size (), arguments->data ());
156
184
}
157
185
158
- MaybeLocal<Value > NativeModuleLoader::CompileAsModule (
159
- Environment* env, const char * id, CompilationResultType result ) {
186
+ MaybeLocal<Function > NativeModuleLoader::CompileAsModule (Environment* env,
187
+ const char * id) {
160
188
std::vector<Local<String>> parameters = {env->exports_string (),
161
189
env->require_string (),
162
190
env->module_string (),
163
191
env->process_string (),
164
192
env->internal_binding_string ()};
165
- return per_process_loader.LookupAndCompile (
166
- env->context (), id, ¶meters, result, env);
167
- }
168
-
169
- // Returns nullptr if there is no code cache corresponding to the id
170
- ScriptCompiler::CachedData* NativeModuleLoader::GetCachedData (
171
- const char * id) const {
172
- const auto it = per_process_loader.code_cache_ .find (id);
173
- // This could be false if the module cannot be cached somehow.
174
- // See lib/internal/bootstrap/cache.js on the modules that cannot be cached
175
- if (it == per_process_loader.code_cache_ .end ()) {
176
- return nullptr ;
177
- }
178
-
179
- const uint8_t * code_cache_value = it->second .one_bytes_data ();
180
- size_t code_cache_length = it->second .length ();
181
-
182
- return new ScriptCompiler::CachedData (code_cache_value, code_cache_length);
193
+ return per_process::native_module_loader.LookupAndCompile (
194
+ env->context (), id, ¶meters, env);
183
195
}
184
196
185
197
// Returns Local<Function> of the compiled module if return_code_cache
186
198
// is false (we are only compiling the function).
187
199
// Otherwise return a Local<Object> containing the cache.
188
- MaybeLocal<Value > NativeModuleLoader::LookupAndCompile (
200
+ MaybeLocal<Function > NativeModuleLoader::LookupAndCompile (
189
201
Local<Context> context,
190
202
const char * id,
191
203
std::vector<Local<String>>* parameters,
192
- CompilationResultType result_type,
193
204
Environment* optional_env) {
194
205
Isolate* isolate = context->GetIsolate ();
195
206
EscapableHandleScope scope (isolate);
196
207
Local<Value> ret; // Used to convert to MaybeLocal before return
197
208
198
- Local<String> source = GetSource (isolate, id);
209
+ const auto source_it = source_.find (id);
210
+ CHECK_NE (source_it, source_.end ());
211
+ Local<String> source = source_it->second .ToStringChecked (isolate);
199
212
200
213
std::string filename_s = id + std::string (" .js" );
201
214
Local<String> filename =
@@ -204,31 +217,24 @@ MaybeLocal<Value> NativeModuleLoader::LookupAndCompile(
204
217
Local<Integer> column_offset = Integer::New (isolate, 0 );
205
218
ScriptOrigin origin (filename, line_offset, column_offset);
206
219
207
- bool use_cache = false ;
208
- ScriptCompiler::CachedData* cached_data = nullptr ;
220
+ Mutex::ScopedLock lock (code_cache_mutex_);
209
221
210
- // 1. We won't even check the existence of the cache if the binary is not
211
- // built with them.
212
- // 2. If we are generating code cache for tools/general_code_cache.js, we
213
- // are not going to use any cache ourselves.
214
- if (has_code_cache_ && result_type == CompilationResultType::kFunction ) {
215
- cached_data = GetCachedData (id);
216
- if (cached_data != nullptr ) {
217
- use_cache = true ;
222
+ ScriptCompiler::CachedData* cached_data = nullptr ;
223
+ {
224
+ auto cache_it = code_cache_.find (id);
225
+ if (cache_it != code_cache_.end ()) {
226
+ // Transfer ownership to ScriptCompiler::Source later.
227
+ cached_data = cache_it->second .release ();
228
+ code_cache_.erase (cache_it);
218
229
}
219
230
}
220
231
232
+ const bool use_cache = cached_data != nullptr ;
233
+ ScriptCompiler::CompileOptions options =
234
+ use_cache ? ScriptCompiler::kConsumeCodeCache
235
+ : ScriptCompiler::kEagerCompile ;
221
236
ScriptCompiler::Source script_source (source, origin, cached_data);
222
237
223
- ScriptCompiler::CompileOptions options;
224
- if (result_type == CompilationResultType::kCodeCache ) {
225
- options = ScriptCompiler::kEagerCompile ;
226
- } else if (use_cache) {
227
- options = ScriptCompiler::kConsumeCodeCache ;
228
- } else {
229
- options = ScriptCompiler::kNoCompileOptions ;
230
- }
231
-
232
238
MaybeLocal<Function> maybe_fun =
233
239
ScriptCompiler::CompileFunctionInContext (context,
234
240
&script_source,
@@ -244,10 +250,14 @@ MaybeLocal<Value> NativeModuleLoader::LookupAndCompile(
244
250
// In the case of early errors, v8 is already capable of
245
251
// decorating the stack for us - note that we use CompileFunctionInContext
246
252
// so there is no need to worry about wrappers.
247
- return MaybeLocal<Value >();
253
+ return MaybeLocal<Function >();
248
254
}
249
255
250
256
Local<Function> fun = maybe_fun.ToLocalChecked ();
257
+ // XXX(joyeecheung): this bookkeeping is not exactly accurate because
258
+ // it only starts after the Environment is created, so the per_context.js
259
+ // will never be in any of these two sets, but the two sets are only for
260
+ // testing anyway.
251
261
if (use_cache) {
252
262
if (optional_env != nullptr ) {
253
263
// This could happen when Node is run with any v8 flag, but
@@ -264,29 +274,15 @@ MaybeLocal<Value> NativeModuleLoader::LookupAndCompile(
264
274
}
265
275
}
266
276
267
- if (result_type == CompilationResultType::kCodeCache ) {
268
- std::unique_ptr<ScriptCompiler::CachedData> cached_data (
269
- ScriptCompiler::CreateCodeCacheForFunction (fun));
270
- CHECK_NE (cached_data, nullptr );
271
- size_t cached_data_length = cached_data->length ;
272
- // Since we have no special allocator to create an ArrayBuffer
273
- // from a new'ed pointer, we will need to copy it - but this
274
- // code path is only run by the tooling that generates the code
275
- // cache to be bundled in the binary
276
- // so it should be fine.
277
- MallocedBuffer<uint8_t > copied (cached_data->length );
278
- memcpy (copied.data , cached_data->data , cached_data_length);
279
- Local<ArrayBuffer> buf =
280
- ArrayBuffer::New (isolate,
281
- copied.release (),
282
- cached_data_length,
283
- ArrayBufferCreationMode::kInternalized );
284
- ret = Uint8Array::New (buf, 0 , cached_data_length);
285
- } else {
286
- ret = fun;
287
- }
277
+ // Generate new cache for next compilation
278
+ std::unique_ptr<ScriptCompiler::CachedData> new_cached_data (
279
+ ScriptCompiler::CreateCodeCacheForFunction (fun));
280
+ CHECK_NE (new_cached_data, nullptr );
288
281
289
- return scope.Escape (ret);
282
+ // The old entry should've been erased by now so we can just emplace
283
+ code_cache_.emplace (id, std::move (new_cached_data));
284
+
285
+ return scope.Escape (fun);
290
286
}
291
287
292
288
void NativeModuleLoader::Initialize (Local<Object> target,
@@ -320,8 +316,7 @@ void NativeModuleLoader::Initialize(Local<Object> target,
320
316
target, " getCacheUsage" , NativeModuleLoader::GetCacheUsage);
321
317
env->SetMethod (
322
318
target, " compileFunction" , NativeModuleLoader::CompileFunction);
323
- env->SetMethod (
324
- target, " compileCodeCache" , NativeModuleLoader::CompileCodeCache);
319
+ env->SetMethod (target, " getCodeCache" , NativeModuleLoader::GetCodeCache);
325
320
// internalBinding('native_module') should be frozen
326
321
target->SetIntegrityLevel (context, IntegrityLevel::kFrozen ).FromJust ();
327
322
}
0 commit comments