@@ -108,62 +108,151 @@ Local<Name> Uint32ToName(Local<Context> context, uint32_t index) {
108108
109109} // anonymous namespace
110110
111- ContextifyContext::ContextifyContext (
111+ BaseObjectPtr< ContextifyContext> ContextifyContext::New (
112112 Environment* env,
113113 Local<Object> sandbox_obj,
114- const ContextOptions& options)
115- : env_ (env),
116- microtask_queue_wrap_ (options.microtask_queue_wrap) {
114+ const ContextOptions& options) {
115+ HandleScope scope (env-> isolate ());
116+ InitializeGlobalTemplates (env-> isolate_data ());
117117 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- }
118+ DCHECK (!object_template.IsEmpty ());
122119 bool use_node_snapshot = per_process::cli_options->node_snapshot ;
123120 const SnapshotData* snapshot_data =
124121 use_node_snapshot ? SnapshotBuilder::GetEmbeddedSnapshotData () : nullptr ;
125122
126123 MicrotaskQueue* queue =
127- microtask_queue ()
128- ? microtask_queue ().get ()
124+ options. microtask_queue_wrap
125+ ? options. microtask_queue_wrap -> microtask_queue ().get ()
129126 : env->isolate ()->GetCurrentContext ()->GetMicrotaskQueue ();
130127
131128 Local<Context> v8_context;
132129 if (!(CreateV8Context (env->isolate (), object_template, snapshot_data, queue)
133- .ToLocal (&v8_context)) ||
134- !InitializeContext (v8_context, env, sandbox_obj, options)) {
130+ .ToLocal (&v8_context))) {
135131 // Allocation failure, maximum call stack size reached, termination, etc.
136- return ;
132+ return BaseObjectPtr<ContextifyContext>() ;
137133 }
138134
139- context_.Reset (env->isolate (), v8_context);
140- context_.SetWeak (this , WeakCallback, WeakCallbackType::kParameter );
141- env->AddCleanupHook (CleanupHook, this );
135+ // This only initializes part of the context. The primordials are
136+ // only initilaized when needed because even deserializing them slows
137+ // things down significantly and they are only needed in rare occasions
138+ // in the vm contexts.
139+ if (InitializeContextRuntime (v8_context).IsNothing ()) {
140+ return BaseObjectPtr<ContextifyContext>();
141+ }
142+
143+ Local<Context> main_context = env->context ();
144+ Local<Object> new_context_global = v8_context->Global ();
145+ v8_context->SetSecurityToken (main_context->GetSecurityToken ());
146+
147+ // We need to tie the lifetime of the sandbox object with the lifetime of
148+ // newly created context. We do this by making them hold references to each
149+ // other. The context can directly hold a reference to the sandbox as an
150+ // embedder data field. The sandbox uses a private symbol to hold a reference
151+ // to the ContextifyContext wrapper which in turn internally references
152+ // the context from its constructor.
153+ v8_context->SetEmbedderData (ContextEmbedderIndex::kSandboxObject ,
154+ sandbox_obj);
155+
156+ // Delegate the code generation validation to
157+ // node::ModifyCodeGenerationFromStrings.
158+ v8_context->AllowCodeGenerationFromStrings (false );
159+ v8_context->SetEmbedderData (
160+ ContextEmbedderIndex::kAllowCodeGenerationFromStrings ,
161+ options.allow_code_gen_strings );
162+ v8_context->SetEmbedderData (ContextEmbedderIndex::kAllowWasmCodeGeneration ,
163+ options.allow_code_gen_wasm );
164+
165+ Utf8Value name_val (env->isolate (), options.name );
166+ ContextInfo info (*name_val);
167+ if (!options.origin .IsEmpty ()) {
168+ Utf8Value origin_val (env->isolate (), options.origin );
169+ info.origin = *origin_val;
170+ }
171+
172+ BaseObjectPtr<ContextifyContext> result;
173+ Local<Object> wrapper;
174+ {
175+ Context::Scope context_scope (v8_context);
176+ Local<String> ctor_name = sandbox_obj->GetConstructorName ();
177+ if (!ctor_name->Equals (v8_context, env->object_string ()).FromMaybe (false ) &&
178+ new_context_global
179+ ->DefineOwnProperty (
180+ v8_context,
181+ v8::Symbol::GetToStringTag (env->isolate ()),
182+ ctor_name,
183+ static_cast <v8::PropertyAttribute>(v8::DontEnum))
184+ .IsNothing ()) {
185+ return BaseObjectPtr<ContextifyContext>();
186+ }
187+ env->AssignToContext (v8_context, nullptr , info);
188+
189+ if (!env->contextify_wrapper_template ()
190+ ->NewInstance (v8_context)
191+ .ToLocal (&wrapper)) {
192+ return BaseObjectPtr<ContextifyContext>();
193+ }
194+
195+ result =
196+ MakeBaseObject<ContextifyContext>(env, wrapper, v8_context, options);
197+ // The only strong reference to the wrapper will come from the sandbox.
198+ result->MakeWeak ();
199+ }
200+
201+ if (sandbox_obj
202+ ->SetPrivate (
203+ v8_context, env->contextify_context_private_symbol (), wrapper)
204+ .IsNothing ()) {
205+ return BaseObjectPtr<ContextifyContext>();
206+ }
207+
208+ return result;
142209}
143210
211+ void ContextifyContext::MemoryInfo (MemoryTracker* tracker) const {
212+ if (microtask_queue_wrap_) {
213+ tracker->TrackField (" microtask_queue_wrap" ,
214+ microtask_queue_wrap_->object ());
215+ }
216+ }
217+
218+ ContextifyContext::ContextifyContext (Environment* env,
219+ Local<Object> wrapper,
220+ Local<Context> v8_context,
221+ const ContextOptions& options)
222+ : BaseObject(env, wrapper),
223+ microtask_queue_wrap_ (options.microtask_queue_wrap) {
224+ context_.Reset (env->isolate (), v8_context);
225+ // This should only be done after the initial initializations of the context
226+ // global object is finished.
227+ DCHECK_NULL (v8_context->GetAlignedPointerFromEmbedderData (
228+ ContextEmbedderIndex::kContextifyContext ));
229+ v8_context->SetAlignedPointerInEmbedderData (
230+ ContextEmbedderIndex::kContextifyContext , this );
231+ // It's okay to make this reference weak - V8 would create an internal
232+ // reference to this context via the constructor of the wrapper.
233+ // As long as the wrapper is alive, it's constructor is alive, and so
234+ // is the context.
235+ context_.SetWeak ();
236+ }
144237
145238ContextifyContext::~ContextifyContext () {
146- env ()->RemoveCleanupHook (CleanupHook, this );
147239 Isolate* isolate = env ()->isolate ();
148240 HandleScope scope (isolate);
149241
150242 env ()->async_hooks ()
151243 ->RemoveContext (PersistentToLocal::Weak (isolate, context_));
244+ context_.Reset ();
152245}
153246
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 ();
247+ void ContextifyContext::InitializeGlobalTemplates (IsolateData* isolate_data) {
248+ if (!isolate_data->contextify_global_template ().IsEmpty ()) {
249+ return ;
250+ }
251+ DCHECK (isolate_data->contextify_wrapper_template ().IsEmpty ());
252+ Local<FunctionTemplate> global_func_template =
253+ FunctionTemplate::New (isolate_data->isolate ());
254+ Local<ObjectTemplate> global_object_template =
255+ global_func_template->InstanceTemplate ();
167256
168257 NamedPropertyHandlerConfiguration config (
169258 PropertyGetterCallback,
@@ -185,10 +274,15 @@ Local<ObjectTemplate> ContextifyContext::CreateGlobalTemplate(
185274 {},
186275 PropertyHandlerFlags::kHasNoSideEffect );
187276
188- object_template->SetHandler (config);
189- object_template->SetHandler (indexed_config);
277+ global_object_template->SetHandler (config);
278+ global_object_template->SetHandler (indexed_config);
279+ isolate_data->set_contextify_global_template (global_object_template);
190280
191- return object_template;
281+ Local<FunctionTemplate> wrapper_func_template =
282+ BaseObject::MakeLazilyInitializedJSTemplate (isolate_data);
283+ Local<ObjectTemplate> wrapper_object_template =
284+ wrapper_func_template->InstanceTemplate ();
285+ isolate_data->set_contextify_wrapper_template (wrapper_object_template);
192286}
193287
194288MaybeLocal<Context> ContextifyContext::CreateV8Context (
@@ -218,73 +312,8 @@ MaybeLocal<Context> ContextifyContext::CreateV8Context(
218312 .ToLocal (&ctx)) {
219313 return MaybeLocal<Context>();
220314 }
221- return scope.Escape (ctx);
222- }
223-
224- bool ContextifyContext::InitializeContext (Local<Context> ctx,
225- Environment* env,
226- Local<Object> sandbox_obj,
227- const ContextOptions& options) {
228- HandleScope scope (env->isolate ());
229-
230- // This only initializes part of the context. The primordials are
231- // only initilaized when needed because even deserializing them slows
232- // things down significantly and they are only needed in rare occasions
233- // in the vm contexts.
234- if (InitializeContextRuntime (ctx).IsNothing ()) {
235- return false ;
236- }
237-
238- Local<Context> main_context = env->context ();
239- ctx->SetSecurityToken (main_context->GetSecurityToken ());
240-
241- // We need to tie the lifetime of the sandbox object with the lifetime of
242- // newly created context. We do this by making them hold references to each
243- // 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 ());
250-
251- // Delegate the code generation validation to
252- // 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 );
258-
259- Utf8Value name_val (env->isolate (), options.name );
260- ContextInfo info (*name_val);
261- if (!options.origin .IsEmpty ()) {
262- Utf8Value origin_val (env->isolate (), options.origin );
263- info.origin = *origin_val;
264- }
265-
266- {
267- Context::Scope context_scope (ctx);
268- Local<String> ctor_name = sandbox_obj->GetConstructorName ();
269- if (!ctor_name->Equals (ctx, env->object_string ()).FromMaybe (false ) &&
270- ctx->Global ()
271- ->DefineOwnProperty (
272- ctx,
273- v8::Symbol::GetToStringTag (env->isolate ()),
274- ctor_name,
275- static_cast <v8::PropertyAttribute>(v8::DontEnum))
276- .IsNothing ()) {
277- return false ;
278- }
279- }
280-
281- env->AssignToContext (ctx, nullptr , info);
282315
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 ;
316+ return scope.Escape (ctx);
288317}
289318
290319void ContextifyContext::Init (Environment* env, Local<Object> target) {
@@ -350,22 +379,20 @@ void ContextifyContext::MakeContext(const FunctionCallbackInfo<Value>& args) {
350379 }
351380
352381 TryCatchScope try_catch (env);
353- std::unique_ptr <ContextifyContext> context_ptr =
354- std::make_unique<ContextifyContext> (env, sandbox, options);
382+ BaseObjectPtr <ContextifyContext> context_ptr =
383+ ContextifyContext::New (env, sandbox, options);
355384
356385 if (try_catch.HasCaught ()) {
357386 if (!try_catch.HasTerminated ())
358387 try_catch.ReThrow ();
359388 return ;
360389 }
361390
391+ if (context_ptr.get () == nullptr ) {
392+ return ;
393+ }
362394 Local<Context> new_context = context_ptr->context ();
363395 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 ()));
369396}
370397
371398
@@ -392,23 +419,24 @@ void ContextifyContext::WeakCallback(
392419ContextifyContext* ContextifyContext::ContextFromContextifiedSandbox (
393420 Environment* env,
394421 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 ());
422+ Local<Value> context_global;
423+ if (sandbox
424+ ->GetPrivate (env->context (), env->contextify_context_private_symbol ())
425+ .ToLocal (&context_global) &&
426+ context_global->IsObject ()) {
427+ return Unwrap<ContextifyContext>(context_global.As <Object>());
403428 }
404429 return nullptr ;
405430}
406431
407- // static
408432template <typename T>
409433ContextifyContext* ContextifyContext::Get (const PropertyCallbackInfo<T>& args) {
434+ return Get (args.This ());
435+ }
436+
437+ ContextifyContext* ContextifyContext::Get (Local<Object> object) {
410438 Local<Context> context;
411- if (!args. This () ->GetCreationContext ().ToLocal (&context)) {
439+ if (!object ->GetCreationContext ().ToLocal (&context)) {
412440 return nullptr ;
413441 }
414442 if (!ContextEmbedderTag::IsNodeContext (context)) {
0 commit comments