@@ -42,7 +42,6 @@ using v8::ArrayBufferView; |
42 | 42 | using v8::Boolean; |
43 | 43 | using v8::Context; |
44 | 44 | using v8::EscapableHandleScope; |
45 | | -using v8::External; |
46 | 45 | using v8::Function; |
47 | 46 | using v8::FunctionCallbackInfo; |
48 | 47 | using v8::FunctionTemplate; |
@@ -108,62 +107,77 @@ Local<Name> Uint32ToName(Local<Context> context, uint32_t index) { |
108 | 107 |
|
109 | 108 | } // anonymous namespace |
110 | 109 |
|
111 | | -ContextifyContext::ContextifyContext( |
| 110 | +BaseObjectPtr<ContextifyContext> ContextifyContext::New( |
112 | 111 | Environment* env, |
113 | 112 | Local<Object> sandbox_obj, |
114 | | - const ContextOptions& options) |
115 | | - : env_(env), |
116 | | - microtask_queue_wrap_(options.microtask_queue_wrap) { |
| 113 | + const ContextOptions& options) { |
| 114 | + HandleScope scope(env->isolate()); |
| 115 | + InitializeGlobalTemplates(env->isolate_data()); |
117 | 116 | Local<ObjectTemplate> object_template = env->contextify_global_template(); |
118 | | - if (object_template.IsEmpty()) { |
119 | | - object_template = CreateGlobalTemplate(env->isolate()); |
120 | | - env->set_contextify_global_template(object_template); |
121 | | - } |
| 117 | + DCHECK(!object_template.IsEmpty()); |
122 | 118 | bool use_node_snapshot = per_process::cli_options->node_snapshot; |
123 | 119 | const SnapshotData* snapshot_data = |
124 | 120 | use_node_snapshot ? SnapshotBuilder::GetEmbeddedSnapshotData() : nullptr; |
125 | 121 |
|
126 | 122 | MicrotaskQueue* queue = |
127 | | - microtask_queue() |
128 | | - ? microtask_queue().get() |
| 123 | + options.microtask_queue_wrap |
| 124 | + ? options.microtask_queue_wrap->microtask_queue().get() |
129 | 125 | : env->isolate()->GetCurrentContext()->GetMicrotaskQueue(); |
130 | 126 |
|
131 | 127 | Local<Context> v8_context; |
132 | 128 | if (!(CreateV8Context(env->isolate(), object_template, snapshot_data, queue) |
133 | | - .ToLocal(&v8_context)) || |
134 | | - !InitializeContext(v8_context, env, sandbox_obj, options)) { |
| 129 | + .ToLocal(&v8_context))) { |
135 | 130 | // Allocation failure, maximum call stack size reached, termination, etc. |
136 | | - return; |
| 131 | + return BaseObjectPtr<ContextifyContext>(); |
137 | 132 | } |
| 133 | + return New(v8_context, env, sandbox_obj, options); |
| 134 | +} |
138 | 135 |
|
139 | | - context_.Reset(env->isolate(), v8_context); |
140 | | - context_.SetWeak(this, WeakCallback, WeakCallbackType::kParameter); |
141 | | - env->AddCleanupHook(CleanupHook, this); |
| 136 | +void ContextifyContext::MemoryInfo(MemoryTracker* tracker) const { |
| 137 | + if (microtask_queue_wrap_) { |
| 138 | + tracker->TrackField("microtask_queue_wrap", |
| 139 | + microtask_queue_wrap_->object()); |
| 140 | + } |
142 | 141 | } |
143 | 142 |
|
| 143 | +ContextifyContext::ContextifyContext(Environment* env, |
| 144 | + Local<Object> wrapper, |
| 145 | + Local<Context> v8_context, |
| 146 | + const ContextOptions& options) |
| 147 | + : BaseObject(env, wrapper), |
| 148 | + microtask_queue_wrap_(options.microtask_queue_wrap) { |
| 149 | + context_.Reset(env->isolate(), v8_context); |
| 150 | + // This should only be done after the initial initializations of the context |
| 151 | + // global object is finished. |
| 152 | + DCHECK_NULL(v8_context->GetAlignedPointerFromEmbedderData( |
| 153 | + ContextEmbedderIndex::kContextifyContext)); |
| 154 | + v8_context->SetAlignedPointerInEmbedderData( |
| 155 | + ContextEmbedderIndex::kContextifyContext, this); |
| 156 | + // It's okay to make this reference weak - V8 would create an internal |
| 157 | + // reference to this context via the constructor of the wrapper. |
| 158 | + // As long as the wrapper is alive, it's constructor is alive, and so |
| 159 | + // is the context. |
| 160 | + context_.SetWeak(); |
| 161 | +} |
144 | 162 |
|
145 | 163 | ContextifyContext::~ContextifyContext() { |
146 | | - env()->RemoveCleanupHook(CleanupHook, this); |
147 | 164 | Isolate* isolate = env()->isolate(); |
148 | 165 | HandleScope scope(isolate); |
149 | 166 |
|
150 | 167 | env()->async_hooks() |
151 | 168 | ->RemoveContext(PersistentToLocal::Weak(isolate, context_)); |
| 169 | + context_.Reset(); |
152 | 170 | } |
153 | 171 |
|
154 | | - |
155 | | -void ContextifyContext::CleanupHook(void* arg) { |
156 | | - ContextifyContext* self = static_cast<ContextifyContext*>(arg); |
157 | | - self->context_.Reset(); |
158 | | - delete self; |
159 | | -} |
160 | | - |
161 | | -Local<ObjectTemplate> ContextifyContext::CreateGlobalTemplate( |
162 | | - Isolate* isolate) { |
163 | | - Local<FunctionTemplate> function_template = FunctionTemplate::New(isolate); |
164 | | - |
165 | | - Local<ObjectTemplate> object_template = |
166 | | - function_template->InstanceTemplate(); |
| 172 | +void ContextifyContext::InitializeGlobalTemplates(IsolateData* isolate_data) { |
| 173 | + if (!isolate_data->contextify_global_template().IsEmpty()) { |
| 174 | + return; |
| 175 | + } |
| 176 | + DCHECK(isolate_data->contextify_wrapper_template().IsEmpty()); |
| 177 | + Local<FunctionTemplate> global_func_template = |
| 178 | + FunctionTemplate::New(isolate_data->isolate()); |
| 179 | + Local<ObjectTemplate> global_object_template = |
| 180 | + global_func_template->InstanceTemplate(); |
167 | 181 |
|
168 | 182 | NamedPropertyHandlerConfiguration config( |
169 | 183 | PropertyGetterCallback, |
@@ -185,10 +199,15 @@ Local<ObjectTemplate> ContextifyContext::CreateGlobalTemplate( |
185 | 199 | {}, |
186 | 200 | PropertyHandlerFlags::kHasNoSideEffect); |
187 | 201 |
|
188 | | - object_template->SetHandler(config); |
189 | | - object_template->SetHandler(indexed_config); |
| 202 | + global_object_template->SetHandler(config); |
| 203 | + global_object_template->SetHandler(indexed_config); |
| 204 | + isolate_data->set_contextify_global_template(global_object_template); |
190 | 205 |
|
191 | | - return object_template; |
| 206 | + Local<FunctionTemplate> wrapper_func_template = |
| 207 | + BaseObject::MakeLazilyInitializedJSTemplate(isolate_data); |
| 208 | + Local<ObjectTemplate> wrapper_object_template = |
| 209 | + wrapper_func_template->InstanceTemplate(); |
| 210 | + isolate_data->set_contextify_wrapper_template(wrapper_object_template); |
192 | 211 | } |
193 | 212 |
|
194 | 213 | MaybeLocal<Context> ContextifyContext::CreateV8Context( |
@@ -218,43 +237,45 @@ MaybeLocal<Context> ContextifyContext::CreateV8Context( |
218 | 237 | .ToLocal(&ctx)) { |
219 | 238 | return MaybeLocal<Context>(); |
220 | 239 | } |
| 240 | + |
221 | 241 | return scope.Escape(ctx); |
222 | 242 | } |
223 | 243 |
|
224 | | -bool ContextifyContext::InitializeContext(Local<Context> ctx, |
225 | | - Environment* env, |
226 | | - Local<Object> sandbox_obj, |
227 | | - const ContextOptions& options) { |
| 244 | +BaseObjectPtr<ContextifyContext> ContextifyContext::New( |
| 245 | + Local<Context> v8_context, |
| 246 | + Environment* env, |
| 247 | + Local<Object> sandbox_obj, |
| 248 | + const ContextOptions& options) { |
228 | 249 | HandleScope scope(env->isolate()); |
229 | | - |
230 | 250 | // This only initializes part of the context. The primordials are |
231 | 251 | // only initilaized when needed because even deserializing them slows |
232 | 252 | // things down significantly and they are only needed in rare occasions |
233 | 253 | // in the vm contexts. |
234 | | - if (InitializeContextRuntime(ctx).IsNothing()) { |
235 | | - return false; |
| 254 | + if (InitializeContextRuntime(v8_context).IsNothing()) { |
| 255 | + return BaseObjectPtr<ContextifyContext>(); |
236 | 256 | } |
237 | 257 |
|
238 | 258 | Local<Context> main_context = env->context(); |
239 | | - ctx->SetSecurityToken(main_context->GetSecurityToken()); |
| 259 | + Local<Object> new_context_global = v8_context->Global(); |
| 260 | + v8_context->SetSecurityToken(main_context->GetSecurityToken()); |
240 | 261 |
|
241 | 262 | // We need to tie the lifetime of the sandbox object with the lifetime of |
242 | 263 | // newly created context. We do this by making them hold references to each |
243 | 264 | // other. The context can directly hold a reference to the sandbox as an |
244 | | - // embedder data field. However, we cannot hold a reference to a v8::Context |
245 | | - // directly in an Object, we instead hold onto the new context's global |
246 | | - // object instead (which then has a reference to the context). |
247 | | - ctx->SetEmbedderData(ContextEmbedderIndex::kSandboxObject, sandbox_obj); |
248 | | - sandbox_obj->SetPrivate( |
249 | | - main_context, env->contextify_global_private_symbol(), ctx->Global()); |
| 265 | + // embedder data field. The sandbox uses a private symbol to hold a reference |
| 266 | + // to the ContextifyContext wrapper which in turn internally references |
| 267 | + // the context from its constructor. |
| 268 | + v8_context->SetEmbedderData(ContextEmbedderIndex::kSandboxObject, |
| 269 | + sandbox_obj); |
250 | 270 |
|
251 | 271 | // Delegate the code generation validation to |
252 | 272 | // node::ModifyCodeGenerationFromStrings. |
253 | | - ctx->AllowCodeGenerationFromStrings(false); |
254 | | - ctx->SetEmbedderData(ContextEmbedderIndex::kAllowCodeGenerationFromStrings, |
255 | | - options.allow_code_gen_strings); |
256 | | - ctx->SetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration, |
257 | | - options.allow_code_gen_wasm); |
| 273 | + v8_context->AllowCodeGenerationFromStrings(false); |
| 274 | + v8_context->SetEmbedderData( |
| 275 | + ContextEmbedderIndex::kAllowCodeGenerationFromStrings, |
| 276 | + options.allow_code_gen_strings); |
| 277 | + v8_context->SetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration, |
| 278 | + options.allow_code_gen_wasm); |
258 | 279 |
|
259 | 280 | Utf8Value name_val(env->isolate(), options.name); |
260 | 281 | ContextInfo info(*name_val); |
@@ -263,28 +284,43 @@ bool ContextifyContext::InitializeContext(Local<Context> ctx, |
263 | 284 | info.origin = *origin_val; |
264 | 285 | } |
265 | 286 |
|
| 287 | + BaseObjectPtr<ContextifyContext> result; |
| 288 | + Local<Object> wrapper; |
266 | 289 | { |
267 | | - Context::Scope context_scope(ctx); |
| 290 | + Context::Scope context_scope(v8_context); |
268 | 291 | Local<String> ctor_name = sandbox_obj->GetConstructorName(); |
269 | | - if (!ctor_name->Equals(ctx, env->object_string()).FromMaybe(false) && |
270 | | - ctx->Global() |
| 292 | + if (!ctor_name->Equals(v8_context, env->object_string()).FromMaybe(false) && |
| 293 | + new_context_global |
271 | 294 | ->DefineOwnProperty( |
272 | | - ctx, |
| 295 | + v8_context, |
273 | 296 | v8::Symbol::GetToStringTag(env->isolate()), |
274 | 297 | ctor_name, |
275 | 298 | static_cast<v8::PropertyAttribute>(v8::DontEnum)) |
276 | 299 | .IsNothing()) { |
277 | | - return false; |
| 300 | + return BaseObjectPtr<ContextifyContext>(); |
278 | 301 | } |
| 302 | + env->AssignToContext(v8_context, nullptr, info); |
| 303 | + |
| 304 | + if (!env->contextify_wrapper_template() |
| 305 | + ->NewInstance(v8_context) |
| 306 | + .ToLocal(&wrapper)) { |
| 307 | + return BaseObjectPtr<ContextifyContext>(); |
| 308 | + } |
| 309 | + |
| 310 | + result = |
| 311 | + MakeBaseObject<ContextifyContext>(env, wrapper, v8_context, options); |
| 312 | + // The only strong reference to the wrapper will come from the sandbox. |
| 313 | + result->MakeWeak(); |
279 | 314 | } |
280 | 315 |
|
281 | | - env->AssignToContext(ctx, nullptr, info); |
| 316 | + if (sandbox_obj |
| 317 | + ->SetPrivate( |
| 318 | + v8_context, env->contextify_context_private_symbol(), wrapper) |
| 319 | + .IsNothing()) { |
| 320 | + return BaseObjectPtr<ContextifyContext>(); |
| 321 | + } |
282 | 322 |
|
283 | | - // This should only be done after the initial initializations of the context |
284 | | - // global object is finished. |
285 | | - ctx->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kContextifyContext, |
286 | | - this); |
287 | | - return true; |
| 323 | + return result; |
288 | 324 | } |
289 | 325 |
|
290 | 326 | void ContextifyContext::Init(Environment* env, Local<Object> target) { |
@@ -350,22 +386,14 @@ void ContextifyContext::MakeContext(const FunctionCallbackInfo<Value>& args) { |
350 | 386 | } |
351 | 387 |
|
352 | 388 | TryCatchScope try_catch(env); |
353 | | - std::unique_ptr<ContextifyContext> context_ptr = |
354 | | - std::make_unique<ContextifyContext>(env, sandbox, options); |
| 389 | + BaseObjectPtr<ContextifyContext> context_ptr = |
| 390 | + ContextifyContext::New(env, sandbox, options); |
355 | 391 |
|
356 | 392 | if (try_catch.HasCaught()) { |
357 | 393 | if (!try_catch.HasTerminated()) |
358 | 394 | try_catch.ReThrow(); |
359 | 395 | return; |
360 | 396 | } |
361 | | - |
362 | | - Local<Context> new_context = context_ptr->context(); |
363 | | - if (new_context.IsEmpty()) return; |
364 | | - |
365 | | - sandbox->SetPrivate( |
366 | | - env->context(), |
367 | | - env->contextify_context_private_symbol(), |
368 | | - External::New(env->isolate(), context_ptr.release())); |
369 | 397 | } |
370 | 398 |
|
371 | 399 |
|
@@ -392,23 +420,24 @@ void ContextifyContext::WeakCallback( |
392 | 420 | ContextifyContext* ContextifyContext::ContextFromContextifiedSandbox( |
393 | 421 | Environment* env, |
394 | 422 | const Local<Object>& sandbox) { |
395 | | - MaybeLocal<Value> maybe_value = |
396 | | - sandbox->GetPrivate(env->context(), |
397 | | - env->contextify_context_private_symbol()); |
398 | | - Local<Value> context_external_v; |
399 | | - if (maybe_value.ToLocal(&context_external_v) && |
400 | | - context_external_v->IsExternal()) { |
401 | | - Local<External> context_external = context_external_v.As<External>(); |
402 | | - return static_cast<ContextifyContext*>(context_external->Value()); |
| 423 | + Local<Value> context_global; |
| 424 | + if (sandbox |
| 425 | + ->GetPrivate(env->context(), env->contextify_context_private_symbol()) |
| 426 | + .ToLocal(&context_global) && |
| 427 | + context_global->IsObject()) { |
| 428 | + return Unwrap<ContextifyContext>(context_global.As<Object>()); |
403 | 429 | } |
404 | 430 | return nullptr; |
405 | 431 | } |
406 | 432 |
|
407 | | -// static |
408 | 433 | template <typename T> |
409 | 434 | ContextifyContext* ContextifyContext::Get(const PropertyCallbackInfo<T>& args) { |
| 435 | + return Get(args.This()); |
| 436 | +} |
| 437 | + |
| 438 | +ContextifyContext* ContextifyContext::Get(Local<Object> object) { |
410 | 439 | Local<Context> context; |
411 | | - if (!args.This()->GetCreationContext().ToLocal(&context)) { |
| 440 | + if (!object->GetCreationContext().ToLocal(&context)) { |
412 | 441 | return nullptr; |
413 | 442 | } |
414 | 443 | if (!ContextEmbedderTag::IsNodeContext(context)) { |
|