• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright Joyent, Inc. and other Node contributors.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
21 
22 #include "node_contextify.h"
23 
24 #include "memory_tracker-inl.h"
25 #include "node_internals.h"
26 #include "node_watchdog.h"
27 #include "base_object-inl.h"
28 #include "node_context_data.h"
29 #include "node_errors.h"
30 #include "module_wrap.h"
31 #include "util-inl.h"
32 
33 namespace node {
34 namespace contextify {
35 
36 using errors::TryCatchScope;
37 
38 using v8::Array;
39 using v8::ArrayBufferView;
40 using v8::Boolean;
41 using v8::Context;
42 using v8::EscapableHandleScope;
43 using v8::External;
44 using v8::Function;
45 using v8::FunctionCallbackInfo;
46 using v8::FunctionTemplate;
47 using v8::HandleScope;
48 using v8::IndexedPropertyHandlerConfiguration;
49 using v8::Int32;
50 using v8::Integer;
51 using v8::Isolate;
52 using v8::Local;
53 using v8::Maybe;
54 using v8::MaybeLocal;
55 using v8::MeasureMemoryExecution;
56 using v8::MeasureMemoryMode;
57 using v8::MicrotaskQueue;
58 using v8::MicrotasksPolicy;
59 using v8::Name;
60 using v8::NamedPropertyHandlerConfiguration;
61 using v8::Number;
62 using v8::Object;
63 using v8::ObjectTemplate;
64 using v8::PrimitiveArray;
65 using v8::Promise;
66 using v8::PropertyAttribute;
67 using v8::PropertyCallbackInfo;
68 using v8::PropertyDescriptor;
69 using v8::PropertyHandlerFlags;
70 using v8::Script;
71 using v8::ScriptCompiler;
72 using v8::ScriptOrigin;
73 using v8::ScriptOrModule;
74 using v8::String;
75 using v8::Uint32;
76 using v8::UnboundScript;
77 using v8::Value;
78 using v8::WeakCallbackInfo;
79 using v8::WeakCallbackType;
80 
81 // The vm module executes code in a sandboxed environment with a different
82 // global object than the rest of the code. This is achieved by applying
83 // every call that changes or queries a property on the global `this` in the
84 // sandboxed code, to the sandbox object.
85 //
86 // The implementation uses V8's interceptors for methods like `set`, `get`,
87 // `delete`, `defineProperty`, and for any query of the property attributes.
88 // Property handlers with interceptors are set on the object template for
89 // the sandboxed code. Handlers for both named properties and for indexed
90 // properties are used. Their functionality is almost identical, the indexed
91 // interceptors mostly just call the named interceptors.
92 //
93 // For every `get` of a global property in the sandboxed context, the
94 // interceptor callback checks the sandbox object for the property.
95 // If the property is defined on the sandbox, that result is returned to
96 // the original call instead of finishing the query on the global object.
97 //
98 // For every `set` of a global property, the interceptor callback defines or
99 // changes the property both on the sandbox and the global proxy.
100 
101 namespace {
102 
103 // Convert an int to a V8 Name (String or Symbol).
Uint32ToName(Local<Context> context,uint32_t index)104 Local<Name> Uint32ToName(Local<Context> context, uint32_t index) {
105   return Uint32::New(context->GetIsolate(), index)->ToString(context)
106       .ToLocalChecked();
107 }
108 
109 }  // anonymous namespace
110 
ContextifyContext(Environment * env,Local<Object> sandbox_obj,const ContextOptions & options)111 ContextifyContext::ContextifyContext(
112     Environment* env,
113     Local<Object> sandbox_obj,
114     const ContextOptions& options)
115   : env_(env),
116     microtask_queue_wrap_(options.microtask_queue_wrap) {
117   MaybeLocal<Context> v8_context = CreateV8Context(env, sandbox_obj, options);
118 
119   // Allocation failure, maximum call stack size reached, termination, etc.
120   if (v8_context.IsEmpty()) return;
121 
122   context_.Reset(env->isolate(), v8_context.ToLocalChecked());
123   context_.SetWeak(this, WeakCallback, WeakCallbackType::kParameter);
124   env->AddCleanupHook(CleanupHook, this);
125 }
126 
127 
~ContextifyContext()128 ContextifyContext::~ContextifyContext() {
129   env()->RemoveCleanupHook(CleanupHook, this);
130   Isolate* isolate = env()->isolate();
131   HandleScope scope(isolate);
132 
133   env()->async_hooks()
134     ->RemoveContext(PersistentToLocal::Weak(isolate, context_));
135 }
136 
137 
CleanupHook(void * arg)138 void ContextifyContext::CleanupHook(void* arg) {
139   ContextifyContext* self = static_cast<ContextifyContext*>(arg);
140   self->context_.Reset();
141   delete self;
142 }
143 
144 
145 // This is an object that just keeps an internal pointer to this
146 // ContextifyContext.  It's passed to the NamedPropertyHandler.  If we
147 // pass the main JavaScript context object we're embedded in, then the
148 // NamedPropertyHandler will store a reference to it forever and keep it
149 // from getting gc'd.
CreateDataWrapper(Environment * env)150 MaybeLocal<Object> ContextifyContext::CreateDataWrapper(Environment* env) {
151   Local<Object> wrapper;
152   if (!env->script_data_constructor_function()
153            ->NewInstance(env->context())
154            .ToLocal(&wrapper)) {
155     return MaybeLocal<Object>();
156   }
157 
158   wrapper->SetAlignedPointerInInternalField(ContextifyContext::kSlot, this);
159   return wrapper;
160 }
161 
CreateV8Context(Environment * env,Local<Object> sandbox_obj,const ContextOptions & options)162 MaybeLocal<Context> ContextifyContext::CreateV8Context(
163     Environment* env,
164     Local<Object> sandbox_obj,
165     const ContextOptions& options) {
166   EscapableHandleScope scope(env->isolate());
167   Local<FunctionTemplate> function_template =
168       FunctionTemplate::New(env->isolate());
169 
170   function_template->SetClassName(sandbox_obj->GetConstructorName());
171 
172   Local<ObjectTemplate> object_template =
173       function_template->InstanceTemplate();
174 
175   Local<Object> data_wrapper;
176   if (!CreateDataWrapper(env).ToLocal(&data_wrapper))
177     return MaybeLocal<Context>();
178 
179   NamedPropertyHandlerConfiguration config(
180       PropertyGetterCallback,
181       PropertySetterCallback,
182       PropertyDescriptorCallback,
183       PropertyDeleterCallback,
184       PropertyEnumeratorCallback,
185       PropertyDefinerCallback,
186       data_wrapper,
187       PropertyHandlerFlags::kHasNoSideEffect);
188 
189   IndexedPropertyHandlerConfiguration indexed_config(
190       IndexedPropertyGetterCallback,
191       IndexedPropertySetterCallback,
192       IndexedPropertyDescriptorCallback,
193       IndexedPropertyDeleterCallback,
194       PropertyEnumeratorCallback,
195       IndexedPropertyDefinerCallback,
196       data_wrapper,
197       PropertyHandlerFlags::kHasNoSideEffect);
198 
199   object_template->SetHandler(config);
200   object_template->SetHandler(indexed_config);
201   Local<Context> ctx = Context::New(
202       env->isolate(),
203       nullptr,  // extensions
204       object_template,
205       {},       // global object
206       {},       // deserialization callback
207       microtask_queue() ? microtask_queue().get() : nullptr);
208   if (ctx.IsEmpty()) return MaybeLocal<Context>();
209   // Only partially initialize the context - the primordials are left out
210   // and only initialized when necessary.
211   InitializeContextRuntime(ctx);
212 
213   if (ctx.IsEmpty()) {
214     return MaybeLocal<Context>();
215   }
216 
217   Local<Context> context = env->context();
218   ctx->SetSecurityToken(context->GetSecurityToken());
219 
220   // We need to tie the lifetime of the sandbox object with the lifetime of
221   // newly created context. We do this by making them hold references to each
222   // other. The context can directly hold a reference to the sandbox as an
223   // embedder data field. However, we cannot hold a reference to a v8::Context
224   // directly in an Object, we instead hold onto the new context's global
225   // object instead (which then has a reference to the context).
226   ctx->SetEmbedderData(ContextEmbedderIndex::kSandboxObject, sandbox_obj);
227   sandbox_obj->SetPrivate(context,
228                           env->contextify_global_private_symbol(),
229                           ctx->Global());
230 
231   Utf8Value name_val(env->isolate(), options.name);
232   ctx->AllowCodeGenerationFromStrings(options.allow_code_gen_strings->IsTrue());
233   ctx->SetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration,
234                        options.allow_code_gen_wasm);
235 
236   ContextInfo info(*name_val);
237 
238   if (!options.origin.IsEmpty()) {
239     Utf8Value origin_val(env->isolate(), options.origin);
240     info.origin = *origin_val;
241   }
242 
243   env->AssignToContext(ctx, info);
244 
245   return scope.Escape(ctx);
246 }
247 
248 
Init(Environment * env,Local<Object> target)249 void ContextifyContext::Init(Environment* env, Local<Object> target) {
250   Local<FunctionTemplate> function_template =
251       FunctionTemplate::New(env->isolate());
252   function_template->InstanceTemplate()->SetInternalFieldCount(
253       ContextifyContext::kInternalFieldCount);
254   env->set_script_data_constructor_function(
255       function_template->GetFunction(env->context()).ToLocalChecked());
256 
257   env->SetMethod(target, "makeContext", MakeContext);
258   env->SetMethod(target, "isContext", IsContext);
259   env->SetMethod(target, "compileFunction", CompileFunction);
260 }
261 
262 
263 // makeContext(sandbox, name, origin, strings, wasm);
MakeContext(const FunctionCallbackInfo<Value> & args)264 void ContextifyContext::MakeContext(const FunctionCallbackInfo<Value>& args) {
265   Environment* env = Environment::GetCurrent(args);
266 
267   CHECK_EQ(args.Length(), 6);
268   CHECK(args[0]->IsObject());
269   Local<Object> sandbox = args[0].As<Object>();
270 
271   // Don't allow contextifying a sandbox multiple times.
272   CHECK(
273       !sandbox->HasPrivate(
274           env->context(),
275           env->contextify_context_private_symbol()).FromJust());
276 
277   ContextOptions options;
278 
279   CHECK(args[1]->IsString());
280   options.name = args[1].As<String>();
281 
282   CHECK(args[2]->IsString() || args[2]->IsUndefined());
283   if (args[2]->IsString()) {
284     options.origin = args[2].As<String>();
285   }
286 
287   CHECK(args[3]->IsBoolean());
288   options.allow_code_gen_strings = args[3].As<Boolean>();
289 
290   CHECK(args[4]->IsBoolean());
291   options.allow_code_gen_wasm = args[4].As<Boolean>();
292 
293   if (args[5]->IsObject() &&
294       !env->microtask_queue_ctor_template().IsEmpty() &&
295       env->microtask_queue_ctor_template()->HasInstance(args[5])) {
296     options.microtask_queue_wrap.reset(
297         Unwrap<MicrotaskQueueWrap>(args[5].As<Object>()));
298   }
299 
300   TryCatchScope try_catch(env);
301   std::unique_ptr<ContextifyContext> context_ptr =
302       std::make_unique<ContextifyContext>(env, sandbox, options);
303 
304   if (try_catch.HasCaught()) {
305     if (!try_catch.HasTerminated())
306       try_catch.ReThrow();
307     return;
308   }
309 
310   if (context_ptr->context().IsEmpty())
311     return;
312 
313   sandbox->SetPrivate(
314       env->context(),
315       env->contextify_context_private_symbol(),
316       External::New(env->isolate(), context_ptr.release()));
317 }
318 
319 
IsContext(const FunctionCallbackInfo<Value> & args)320 void ContextifyContext::IsContext(const FunctionCallbackInfo<Value>& args) {
321   Environment* env = Environment::GetCurrent(args);
322 
323   CHECK(args[0]->IsObject());
324   Local<Object> sandbox = args[0].As<Object>();
325 
326   Maybe<bool> result =
327       sandbox->HasPrivate(env->context(),
328                           env->contextify_context_private_symbol());
329   args.GetReturnValue().Set(result.FromJust());
330 }
331 
332 
WeakCallback(const WeakCallbackInfo<ContextifyContext> & data)333 void ContextifyContext::WeakCallback(
334     const WeakCallbackInfo<ContextifyContext>& data) {
335   ContextifyContext* context = data.GetParameter();
336   delete context;
337 }
338 
339 // static
ContextFromContextifiedSandbox(Environment * env,const Local<Object> & sandbox)340 ContextifyContext* ContextifyContext::ContextFromContextifiedSandbox(
341     Environment* env,
342     const Local<Object>& sandbox) {
343   MaybeLocal<Value> maybe_value =
344       sandbox->GetPrivate(env->context(),
345                           env->contextify_context_private_symbol());
346   Local<Value> context_external_v;
347   if (maybe_value.ToLocal(&context_external_v) &&
348       context_external_v->IsExternal()) {
349     Local<External> context_external = context_external_v.As<External>();
350     return static_cast<ContextifyContext*>(context_external->Value());
351   }
352   return nullptr;
353 }
354 
355 // static
356 template <typename T>
Get(const PropertyCallbackInfo<T> & args)357 ContextifyContext* ContextifyContext::Get(const PropertyCallbackInfo<T>& args) {
358   Local<Value> data = args.Data();
359   return static_cast<ContextifyContext*>(
360       data.As<Object>()->GetAlignedPointerFromInternalField(
361           ContextifyContext::kSlot));
362 }
363 
364 // static
PropertyGetterCallback(Local<Name> property,const PropertyCallbackInfo<Value> & args)365 void ContextifyContext::PropertyGetterCallback(
366     Local<Name> property,
367     const PropertyCallbackInfo<Value>& args) {
368   ContextifyContext* ctx = ContextifyContext::Get(args);
369 
370   // Still initializing
371   if (ctx->context_.IsEmpty())
372     return;
373 
374   Local<Context> context = ctx->context();
375   Local<Object> sandbox = ctx->sandbox();
376   MaybeLocal<Value> maybe_rv =
377       sandbox->GetRealNamedProperty(context, property);
378   if (maybe_rv.IsEmpty()) {
379     maybe_rv =
380         ctx->global_proxy()->GetRealNamedProperty(context, property);
381   }
382 
383   Local<Value> rv;
384   if (maybe_rv.ToLocal(&rv)) {
385     if (rv == sandbox)
386       rv = ctx->global_proxy();
387 
388     args.GetReturnValue().Set(rv);
389   }
390 }
391 
392 // static
PropertySetterCallback(Local<Name> property,Local<Value> value,const PropertyCallbackInfo<Value> & args)393 void ContextifyContext::PropertySetterCallback(
394     Local<Name> property,
395     Local<Value> value,
396     const PropertyCallbackInfo<Value>& args) {
397   ContextifyContext* ctx = ContextifyContext::Get(args);
398 
399   // Still initializing
400   if (ctx->context_.IsEmpty())
401     return;
402 
403   Local<Context> context = ctx->context();
404   PropertyAttribute attributes = PropertyAttribute::None;
405   bool is_declared_on_global_proxy = ctx->global_proxy()
406       ->GetRealNamedPropertyAttributes(context, property)
407       .To(&attributes);
408   bool read_only =
409       static_cast<int>(attributes) &
410       static_cast<int>(PropertyAttribute::ReadOnly);
411 
412   bool is_declared_on_sandbox = ctx->sandbox()
413       ->GetRealNamedPropertyAttributes(context, property)
414       .To(&attributes);
415   read_only = read_only ||
416       (static_cast<int>(attributes) &
417       static_cast<int>(PropertyAttribute::ReadOnly));
418 
419   if (read_only)
420     return;
421 
422   // true for x = 5
423   // false for this.x = 5
424   // false for Object.defineProperty(this, 'foo', ...)
425   // false for vmResult.x = 5 where vmResult = vm.runInContext();
426   bool is_contextual_store = ctx->global_proxy() != args.This();
427 
428   // Indicator to not return before setting (undeclared) function declarations
429   // on the sandbox in strict mode, i.e. args.ShouldThrowOnError() = true.
430   // True for 'function f() {}', 'this.f = function() {}',
431   // 'var f = function()'.
432   // In effect only for 'function f() {}' because
433   // var f = function(), is_declared = true
434   // this.f = function() {}, is_contextual_store = false.
435   bool is_function = value->IsFunction();
436 
437   bool is_declared = is_declared_on_global_proxy || is_declared_on_sandbox;
438   if (!is_declared && args.ShouldThrowOnError() && is_contextual_store &&
439       !is_function)
440     return;
441 
442   if (!is_declared_on_global_proxy && is_declared_on_sandbox  &&
443       args.ShouldThrowOnError() && is_contextual_store && !is_function) {
444     // The property exists on the sandbox but not on the global
445     // proxy. Setting it would throw because we are in strict mode.
446     // Don't attempt to set it by signaling that the call was
447     // intercepted. Only change the value on the sandbox.
448     args.GetReturnValue().Set(false);
449   }
450 
451   USE(ctx->sandbox()->Set(context, property, value));
452 }
453 
454 // static
PropertyDescriptorCallback(Local<Name> property,const PropertyCallbackInfo<Value> & args)455 void ContextifyContext::PropertyDescriptorCallback(
456     Local<Name> property,
457     const PropertyCallbackInfo<Value>& args) {
458   ContextifyContext* ctx = ContextifyContext::Get(args);
459 
460   // Still initializing
461   if (ctx->context_.IsEmpty())
462     return;
463 
464   Local<Context> context = ctx->context();
465 
466   Local<Object> sandbox = ctx->sandbox();
467 
468   if (sandbox->HasOwnProperty(context, property).FromMaybe(false)) {
469     Local<Value> desc;
470     if (sandbox->GetOwnPropertyDescriptor(context, property).ToLocal(&desc)) {
471       args.GetReturnValue().Set(desc);
472     }
473   }
474 }
475 
476 // static
PropertyDefinerCallback(Local<Name> property,const PropertyDescriptor & desc,const PropertyCallbackInfo<Value> & args)477 void ContextifyContext::PropertyDefinerCallback(
478     Local<Name> property,
479     const PropertyDescriptor& desc,
480     const PropertyCallbackInfo<Value>& args) {
481   ContextifyContext* ctx = ContextifyContext::Get(args);
482 
483   // Still initializing
484   if (ctx->context_.IsEmpty())
485     return;
486 
487   Local<Context> context = ctx->context();
488   Isolate* isolate = context->GetIsolate();
489 
490   PropertyAttribute attributes = PropertyAttribute::None;
491   bool is_declared =
492       ctx->global_proxy()->GetRealNamedPropertyAttributes(context,
493                                                           property)
494           .To(&attributes);
495   bool read_only =
496       static_cast<int>(attributes) &
497           static_cast<int>(PropertyAttribute::ReadOnly);
498 
499   // If the property is set on the global as read_only, don't change it on
500   // the global or sandbox.
501   if (is_declared && read_only)
502     return;
503 
504   Local<Object> sandbox = ctx->sandbox();
505 
506   auto define_prop_on_sandbox =
507       [&] (PropertyDescriptor* desc_for_sandbox) {
508         if (desc.has_enumerable()) {
509           desc_for_sandbox->set_enumerable(desc.enumerable());
510         }
511         if (desc.has_configurable()) {
512           desc_for_sandbox->set_configurable(desc.configurable());
513         }
514         // Set the property on the sandbox.
515         USE(sandbox->DefineProperty(context, property, *desc_for_sandbox));
516       };
517 
518   if (desc.has_get() || desc.has_set()) {
519     PropertyDescriptor desc_for_sandbox(
520         desc.has_get() ? desc.get() : Undefined(isolate).As<Value>(),
521         desc.has_set() ? desc.set() : Undefined(isolate).As<Value>());
522 
523     define_prop_on_sandbox(&desc_for_sandbox);
524   } else {
525     Local<Value> value =
526         desc.has_value() ? desc.value() : Undefined(isolate).As<Value>();
527 
528     if (desc.has_writable()) {
529       PropertyDescriptor desc_for_sandbox(value, desc.writable());
530       define_prop_on_sandbox(&desc_for_sandbox);
531     } else {
532       PropertyDescriptor desc_for_sandbox(value);
533       define_prop_on_sandbox(&desc_for_sandbox);
534     }
535   }
536 }
537 
538 // static
PropertyDeleterCallback(Local<Name> property,const PropertyCallbackInfo<Boolean> & args)539 void ContextifyContext::PropertyDeleterCallback(
540     Local<Name> property,
541     const PropertyCallbackInfo<Boolean>& args) {
542   ContextifyContext* ctx = ContextifyContext::Get(args);
543 
544   // Still initializing
545   if (ctx->context_.IsEmpty())
546     return;
547 
548   Maybe<bool> success = ctx->sandbox()->Delete(ctx->context(), property);
549 
550   if (success.FromMaybe(false))
551     return;
552 
553   // Delete failed on the sandbox, intercept and do not delete on
554   // the global object.
555   args.GetReturnValue().Set(false);
556 }
557 
558 // static
PropertyEnumeratorCallback(const PropertyCallbackInfo<Array> & args)559 void ContextifyContext::PropertyEnumeratorCallback(
560     const PropertyCallbackInfo<Array>& args) {
561   ContextifyContext* ctx = ContextifyContext::Get(args);
562 
563   // Still initializing
564   if (ctx->context_.IsEmpty())
565     return;
566 
567   Local<Array> properties;
568 
569   if (!ctx->sandbox()->GetPropertyNames(ctx->context()).ToLocal(&properties))
570     return;
571 
572   args.GetReturnValue().Set(properties);
573 }
574 
575 // static
IndexedPropertyGetterCallback(uint32_t index,const PropertyCallbackInfo<Value> & args)576 void ContextifyContext::IndexedPropertyGetterCallback(
577     uint32_t index,
578     const PropertyCallbackInfo<Value>& args) {
579   ContextifyContext* ctx = ContextifyContext::Get(args);
580 
581   // Still initializing
582   if (ctx->context_.IsEmpty())
583     return;
584 
585   ContextifyContext::PropertyGetterCallback(
586       Uint32ToName(ctx->context(), index), args);
587 }
588 
589 
IndexedPropertySetterCallback(uint32_t index,Local<Value> value,const PropertyCallbackInfo<Value> & args)590 void ContextifyContext::IndexedPropertySetterCallback(
591     uint32_t index,
592     Local<Value> value,
593     const PropertyCallbackInfo<Value>& args) {
594   ContextifyContext* ctx = ContextifyContext::Get(args);
595 
596   // Still initializing
597   if (ctx->context_.IsEmpty())
598     return;
599 
600   ContextifyContext::PropertySetterCallback(
601       Uint32ToName(ctx->context(), index), value, args);
602 }
603 
604 // static
IndexedPropertyDescriptorCallback(uint32_t index,const PropertyCallbackInfo<Value> & args)605 void ContextifyContext::IndexedPropertyDescriptorCallback(
606     uint32_t index,
607     const PropertyCallbackInfo<Value>& args) {
608   ContextifyContext* ctx = ContextifyContext::Get(args);
609 
610   // Still initializing
611   if (ctx->context_.IsEmpty())
612     return;
613 
614   ContextifyContext::PropertyDescriptorCallback(
615       Uint32ToName(ctx->context(), index), args);
616 }
617 
618 
IndexedPropertyDefinerCallback(uint32_t index,const PropertyDescriptor & desc,const PropertyCallbackInfo<Value> & args)619 void ContextifyContext::IndexedPropertyDefinerCallback(
620     uint32_t index,
621     const PropertyDescriptor& desc,
622     const PropertyCallbackInfo<Value>& args) {
623   ContextifyContext* ctx = ContextifyContext::Get(args);
624 
625   // Still initializing
626   if (ctx->context_.IsEmpty())
627     return;
628 
629   ContextifyContext::PropertyDefinerCallback(
630       Uint32ToName(ctx->context(), index), desc, args);
631 }
632 
633 // static
IndexedPropertyDeleterCallback(uint32_t index,const PropertyCallbackInfo<Boolean> & args)634 void ContextifyContext::IndexedPropertyDeleterCallback(
635     uint32_t index,
636     const PropertyCallbackInfo<Boolean>& args) {
637   ContextifyContext* ctx = ContextifyContext::Get(args);
638 
639   // Still initializing
640   if (ctx->context_.IsEmpty())
641     return;
642 
643   Maybe<bool> success = ctx->sandbox()->Delete(ctx->context(), index);
644 
645   if (success.FromMaybe(false))
646     return;
647 
648   // Delete failed on the sandbox, intercept and do not delete on
649   // the global object.
650   args.GetReturnValue().Set(false);
651 }
652 
Init(Environment * env,Local<Object> target)653 void ContextifyScript::Init(Environment* env, Local<Object> target) {
654   HandleScope scope(env->isolate());
655   Local<String> class_name =
656       FIXED_ONE_BYTE_STRING(env->isolate(), "ContextifyScript");
657 
658   Local<FunctionTemplate> script_tmpl = env->NewFunctionTemplate(New);
659   script_tmpl->InstanceTemplate()->SetInternalFieldCount(
660       ContextifyScript::kInternalFieldCount);
661   script_tmpl->SetClassName(class_name);
662   env->SetProtoMethod(script_tmpl, "createCachedData", CreateCachedData);
663   env->SetProtoMethod(script_tmpl, "runInContext", RunInContext);
664   env->SetProtoMethod(script_tmpl, "runInThisContext", RunInThisContext);
665 
666   Local<Context> context = env->context();
667 
668   target->Set(context, class_name,
669       script_tmpl->GetFunction(context).ToLocalChecked()).Check();
670   env->set_script_context_constructor_template(script_tmpl);
671 }
672 
New(const FunctionCallbackInfo<Value> & args)673 void ContextifyScript::New(const FunctionCallbackInfo<Value>& args) {
674   Environment* env = Environment::GetCurrent(args);
675   Isolate* isolate = env->isolate();
676   Local<Context> context = env->context();
677 
678   CHECK(args.IsConstructCall());
679 
680   const int argc = args.Length();
681   CHECK_GE(argc, 2);
682 
683   CHECK(args[0]->IsString());
684   Local<String> code = args[0].As<String>();
685 
686   CHECK(args[1]->IsString());
687   Local<String> filename = args[1].As<String>();
688 
689   Local<Integer> line_offset;
690   Local<Integer> column_offset;
691   Local<ArrayBufferView> cached_data_buf;
692   bool produce_cached_data = false;
693   Local<Context> parsing_context = context;
694 
695   if (argc > 2) {
696     // new ContextifyScript(code, filename, lineOffset, columnOffset,
697     //                      cachedData, produceCachedData, parsingContext)
698     CHECK_EQ(argc, 7);
699     CHECK(args[2]->IsNumber());
700     line_offset = args[2].As<Integer>();
701     CHECK(args[3]->IsNumber());
702     column_offset = args[3].As<Integer>();
703     if (!args[4]->IsUndefined()) {
704       CHECK(args[4]->IsArrayBufferView());
705       cached_data_buf = args[4].As<ArrayBufferView>();
706     }
707     CHECK(args[5]->IsBoolean());
708     produce_cached_data = args[5]->IsTrue();
709     if (!args[6]->IsUndefined()) {
710       CHECK(args[6]->IsObject());
711       ContextifyContext* sandbox =
712           ContextifyContext::ContextFromContextifiedSandbox(
713               env, args[6].As<Object>());
714       CHECK_NOT_NULL(sandbox);
715       parsing_context = sandbox->context();
716     }
717   } else {
718     line_offset = Integer::New(isolate, 0);
719     column_offset = Integer::New(isolate, 0);
720   }
721 
722   ContextifyScript* contextify_script =
723       new ContextifyScript(env, args.This());
724 
725   if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
726           TRACING_CATEGORY_NODE2(vm, script)) != 0) {
727     Utf8Value fn(isolate, filename);
728     TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(
729         TRACING_CATEGORY_NODE2(vm, script),
730         "ContextifyScript::New",
731         contextify_script,
732         "filename", TRACE_STR_COPY(*fn));
733   }
734 
735   ScriptCompiler::CachedData* cached_data = nullptr;
736   if (!cached_data_buf.IsEmpty()) {
737     uint8_t* data = static_cast<uint8_t*>(
738         cached_data_buf->Buffer()->GetBackingStore()->Data());
739     cached_data = new ScriptCompiler::CachedData(
740         data + cached_data_buf->ByteOffset(), cached_data_buf->ByteLength());
741   }
742 
743   Local<PrimitiveArray> host_defined_options =
744       PrimitiveArray::New(isolate, loader::HostDefinedOptions::kLength);
745   host_defined_options->Set(isolate, loader::HostDefinedOptions::kType,
746                             Number::New(isolate, loader::ScriptType::kScript));
747   host_defined_options->Set(isolate, loader::HostDefinedOptions::kID,
748                             Number::New(isolate, contextify_script->id()));
749 
750   ScriptOrigin origin(filename,
751                       line_offset,                          // line offset
752                       column_offset,                        // column offset
753                       True(isolate),                        // is cross origin
754                       Local<Integer>(),                     // script id
755                       Local<Value>(),                       // source map URL
756                       False(isolate),                       // is opaque (?)
757                       False(isolate),                       // is WASM
758                       False(isolate),                       // is ES Module
759                       host_defined_options);
760   ScriptCompiler::Source source(code, origin, cached_data);
761   ScriptCompiler::CompileOptions compile_options =
762       ScriptCompiler::kNoCompileOptions;
763 
764   if (source.GetCachedData() != nullptr)
765     compile_options = ScriptCompiler::kConsumeCodeCache;
766 
767   TryCatchScope try_catch(env);
768   ShouldNotAbortOnUncaughtScope no_abort_scope(env);
769   Context::Scope scope(parsing_context);
770 
771   MaybeLocal<UnboundScript> v8_script = ScriptCompiler::CompileUnboundScript(
772       isolate,
773       &source,
774       compile_options);
775 
776   if (v8_script.IsEmpty()) {
777     errors::DecorateErrorStack(env, try_catch);
778     no_abort_scope.Close();
779     if (!try_catch.HasTerminated())
780       try_catch.ReThrow();
781     TRACE_EVENT_NESTABLE_ASYNC_END0(
782         TRACING_CATEGORY_NODE2(vm, script),
783         "ContextifyScript::New",
784         contextify_script);
785     return;
786   }
787   contextify_script->script_.Reset(isolate, v8_script.ToLocalChecked());
788 
789   Local<Context> env_context = env->context();
790   if (compile_options == ScriptCompiler::kConsumeCodeCache) {
791     args.This()->Set(
792         env_context,
793         env->cached_data_rejected_string(),
794         Boolean::New(isolate, source.GetCachedData()->rejected)).Check();
795   } else if (produce_cached_data) {
796     std::unique_ptr<ScriptCompiler::CachedData> cached_data {
797       ScriptCompiler::CreateCodeCache(v8_script.ToLocalChecked()) };
798     bool cached_data_produced = cached_data != nullptr;
799     if (cached_data_produced) {
800       MaybeLocal<Object> buf = Buffer::Copy(
801           env,
802           reinterpret_cast<const char*>(cached_data->data),
803           cached_data->length);
804       args.This()->Set(env_context,
805                        env->cached_data_string(),
806                        buf.ToLocalChecked()).Check();
807     }
808     args.This()->Set(
809         env_context,
810         env->cached_data_produced_string(),
811         Boolean::New(isolate, cached_data_produced)).Check();
812   }
813   TRACE_EVENT_NESTABLE_ASYNC_END0(
814       TRACING_CATEGORY_NODE2(vm, script),
815       "ContextifyScript::New",
816       contextify_script);
817 }
818 
InstanceOf(Environment * env,const Local<Value> & value)819 bool ContextifyScript::InstanceOf(Environment* env,
820                                   const Local<Value>& value) {
821   return !value.IsEmpty() &&
822          env->script_context_constructor_template()->HasInstance(value);
823 }
824 
CreateCachedData(const FunctionCallbackInfo<Value> & args)825 void ContextifyScript::CreateCachedData(
826     const FunctionCallbackInfo<Value>& args) {
827   Environment* env = Environment::GetCurrent(args);
828   ContextifyScript* wrapped_script;
829   ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder());
830   Local<UnboundScript> unbound_script =
831       PersistentToLocal::Default(env->isolate(), wrapped_script->script_);
832   std::unique_ptr<ScriptCompiler::CachedData> cached_data(
833       ScriptCompiler::CreateCodeCache(unbound_script));
834   if (!cached_data) {
835     args.GetReturnValue().Set(Buffer::New(env, 0).ToLocalChecked());
836   } else {
837     MaybeLocal<Object> buf = Buffer::Copy(
838         env,
839         reinterpret_cast<const char*>(cached_data->data),
840         cached_data->length);
841     args.GetReturnValue().Set(buf.ToLocalChecked());
842   }
843 }
844 
RunInThisContext(const FunctionCallbackInfo<Value> & args)845 void ContextifyScript::RunInThisContext(
846     const FunctionCallbackInfo<Value>& args) {
847   Environment* env = Environment::GetCurrent(args);
848 
849   ContextifyScript* wrapped_script;
850   ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder());
851 
852   TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
853       TRACING_CATEGORY_NODE2(vm, script), "RunInThisContext", wrapped_script);
854 
855   // TODO(addaleax): Use an options object or otherwise merge this with
856   // RunInContext().
857   CHECK_EQ(args.Length(), 4);
858 
859   CHECK(args[0]->IsNumber());
860   int64_t timeout = args[0]->IntegerValue(env->context()).FromJust();
861 
862   CHECK(args[1]->IsBoolean());
863   bool display_errors = args[1]->IsTrue();
864 
865   CHECK(args[2]->IsBoolean());
866   bool break_on_sigint = args[2]->IsTrue();
867 
868   CHECK(args[3]->IsBoolean());
869   bool break_on_first_line = args[3]->IsTrue();
870 
871   // Do the eval within this context
872   EvalMachine(env,
873               timeout,
874               display_errors,
875               break_on_sigint,
876               break_on_first_line,
877               nullptr,  // microtask_queue
878               args);
879 
880   TRACE_EVENT_NESTABLE_ASYNC_END0(
881       TRACING_CATEGORY_NODE2(vm, script), "RunInThisContext", wrapped_script);
882 }
883 
RunInContext(const FunctionCallbackInfo<Value> & args)884 void ContextifyScript::RunInContext(const FunctionCallbackInfo<Value>& args) {
885   Environment* env = Environment::GetCurrent(args);
886 
887   ContextifyScript* wrapped_script;
888   ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder());
889 
890   CHECK_EQ(args.Length(), 5);
891 
892   CHECK(args[0]->IsObject());
893   Local<Object> sandbox = args[0].As<Object>();
894   // Get the context from the sandbox
895   ContextifyContext* contextify_context =
896       ContextifyContext::ContextFromContextifiedSandbox(env, sandbox);
897   CHECK_NOT_NULL(contextify_context);
898 
899   Local<Context> context = contextify_context->context();
900   if (context.IsEmpty())
901     return;
902 
903   TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
904       TRACING_CATEGORY_NODE2(vm, script), "RunInContext", wrapped_script);
905 
906   CHECK(args[1]->IsNumber());
907   int64_t timeout = args[1]->IntegerValue(env->context()).FromJust();
908 
909   CHECK(args[2]->IsBoolean());
910   bool display_errors = args[2]->IsTrue();
911 
912   CHECK(args[3]->IsBoolean());
913   bool break_on_sigint = args[3]->IsTrue();
914 
915   CHECK(args[4]->IsBoolean());
916   bool break_on_first_line = args[4]->IsTrue();
917 
918   // Do the eval within the context
919   Context::Scope context_scope(context);
920   EvalMachine(contextify_context->env(),
921               timeout,
922               display_errors,
923               break_on_sigint,
924               break_on_first_line,
925               contextify_context->microtask_queue(),
926               args);
927 
928   TRACE_EVENT_NESTABLE_ASYNC_END0(
929       TRACING_CATEGORY_NODE2(vm, script), "RunInContext", wrapped_script);
930 }
931 
EvalMachine(Environment * env,const int64_t timeout,const bool display_errors,const bool break_on_sigint,const bool break_on_first_line,std::shared_ptr<MicrotaskQueue> mtask_queue,const FunctionCallbackInfo<Value> & args)932 bool ContextifyScript::EvalMachine(Environment* env,
933                                    const int64_t timeout,
934                                    const bool display_errors,
935                                    const bool break_on_sigint,
936                                    const bool break_on_first_line,
937                                    std::shared_ptr<MicrotaskQueue> mtask_queue,
938                                    const FunctionCallbackInfo<Value>& args) {
939   if (!env->can_call_into_js())
940     return false;
941   if (!ContextifyScript::InstanceOf(env, args.Holder())) {
942     THROW_ERR_INVALID_THIS(
943         env,
944         "Script methods can only be called on script instances.");
945     return false;
946   }
947   TryCatchScope try_catch(env);
948   Isolate::SafeForTerminationScope safe_for_termination(env->isolate());
949   ContextifyScript* wrapped_script;
950   ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder(), false);
951   Local<UnboundScript> unbound_script =
952       PersistentToLocal::Default(env->isolate(), wrapped_script->script_);
953   Local<Script> script = unbound_script->BindToCurrentContext();
954 
955 #if HAVE_INSPECTOR
956   if (break_on_first_line) {
957     env->inspector_agent()->PauseOnNextJavascriptStatement("Break on start");
958   }
959 #endif
960 
961   MaybeLocal<Value> result;
962   bool timed_out = false;
963   bool received_signal = false;
964   auto run = [&]() {
965     MaybeLocal<Value> result = script->Run(env->context());
966     if (!result.IsEmpty() && mtask_queue)
967       mtask_queue->PerformCheckpoint(env->isolate());
968     return result;
969   };
970   if (break_on_sigint && timeout != -1) {
971     Watchdog wd(env->isolate(), timeout, &timed_out);
972     SigintWatchdog swd(env->isolate(), &received_signal);
973     result = run();
974   } else if (break_on_sigint) {
975     SigintWatchdog swd(env->isolate(), &received_signal);
976     result = run();
977   } else if (timeout != -1) {
978     Watchdog wd(env->isolate(), timeout, &timed_out);
979     result = run();
980   } else {
981     result = run();
982   }
983 
984   // Convert the termination exception into a regular exception.
985   if (timed_out || received_signal) {
986     if (!env->is_main_thread() && env->is_stopping())
987       return false;
988     env->isolate()->CancelTerminateExecution();
989     // It is possible that execution was terminated by another timeout in
990     // which this timeout is nested, so check whether one of the watchdogs
991     // from this invocation is responsible for termination.
992     if (timed_out) {
993       node::THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(env, timeout);
994     } else if (received_signal) {
995       node::THROW_ERR_SCRIPT_EXECUTION_INTERRUPTED(env);
996     }
997   }
998 
999   if (try_catch.HasCaught()) {
1000     if (!timed_out && !received_signal && display_errors) {
1001       // We should decorate non-termination exceptions
1002       errors::DecorateErrorStack(env, try_catch);
1003     }
1004 
1005     // If there was an exception thrown during script execution, re-throw it.
1006     // If one of the above checks threw, re-throw the exception instead of
1007     // letting try_catch catch it.
1008     // If execution has been terminated, but not by one of the watchdogs from
1009     // this invocation, this will re-throw a `null` value.
1010     if (!try_catch.HasTerminated())
1011       try_catch.ReThrow();
1012 
1013     return false;
1014   }
1015 
1016   args.GetReturnValue().Set(result.ToLocalChecked());
1017   return true;
1018 }
1019 
1020 
ContextifyScript(Environment * env,Local<Object> object)1021 ContextifyScript::ContextifyScript(Environment* env, Local<Object> object)
1022     : BaseObject(env, object),
1023       id_(env->get_next_script_id()) {
1024   MakeWeak();
1025   env->id_to_script_map.emplace(id_, this);
1026 }
1027 
1028 
~ContextifyScript()1029 ContextifyScript::~ContextifyScript() {
1030   env()->id_to_script_map.erase(id_);
1031 }
1032 
1033 
CompileFunction(const FunctionCallbackInfo<Value> & args)1034 void ContextifyContext::CompileFunction(
1035     const FunctionCallbackInfo<Value>& args) {
1036   Environment* env = Environment::GetCurrent(args);
1037   Isolate* isolate = env->isolate();
1038   Local<Context> context = env->context();
1039 
1040   // Argument 1: source code
1041   CHECK(args[0]->IsString());
1042   Local<String> code = args[0].As<String>();
1043 
1044   // Argument 2: filename
1045   CHECK(args[1]->IsString());
1046   Local<String> filename = args[1].As<String>();
1047 
1048   // Argument 3: line offset
1049   CHECK(args[2]->IsNumber());
1050   Local<Integer> line_offset = args[2].As<Integer>();
1051 
1052   // Argument 4: column offset
1053   CHECK(args[3]->IsNumber());
1054   Local<Integer> column_offset = args[3].As<Integer>();
1055 
1056   // Argument 5: cached data (optional)
1057   Local<ArrayBufferView> cached_data_buf;
1058   if (!args[4]->IsUndefined()) {
1059     CHECK(args[4]->IsArrayBufferView());
1060     cached_data_buf = args[4].As<ArrayBufferView>();
1061   }
1062 
1063   // Argument 6: produce cache data
1064   CHECK(args[5]->IsBoolean());
1065   bool produce_cached_data = args[5]->IsTrue();
1066 
1067   // Argument 7: parsing context (optional)
1068   Local<Context> parsing_context;
1069   if (!args[6]->IsUndefined()) {
1070     CHECK(args[6]->IsObject());
1071     ContextifyContext* sandbox =
1072         ContextifyContext::ContextFromContextifiedSandbox(
1073             env, args[6].As<Object>());
1074     CHECK_NOT_NULL(sandbox);
1075     parsing_context = sandbox->context();
1076   } else {
1077     parsing_context = context;
1078   }
1079 
1080   // Argument 8: context extensions (optional)
1081   Local<Array> context_extensions_buf;
1082   if (!args[7]->IsUndefined()) {
1083     CHECK(args[7]->IsArray());
1084     context_extensions_buf = args[7].As<Array>();
1085   }
1086 
1087   // Argument 9: params for the function (optional)
1088   Local<Array> params_buf;
1089   if (!args[8]->IsUndefined()) {
1090     CHECK(args[8]->IsArray());
1091     params_buf = args[8].As<Array>();
1092   }
1093 
1094   // Read cache from cached data buffer
1095   ScriptCompiler::CachedData* cached_data = nullptr;
1096   if (!cached_data_buf.IsEmpty()) {
1097     uint8_t* data = static_cast<uint8_t*>(
1098         cached_data_buf->Buffer()->GetBackingStore()->Data());
1099     cached_data = new ScriptCompiler::CachedData(
1100       data + cached_data_buf->ByteOffset(), cached_data_buf->ByteLength());
1101   }
1102 
1103   // Get the function id
1104   uint32_t id = env->get_next_function_id();
1105 
1106   // Set host_defined_options
1107   Local<PrimitiveArray> host_defined_options =
1108       PrimitiveArray::New(isolate, loader::HostDefinedOptions::kLength);
1109   host_defined_options->Set(
1110       isolate,
1111       loader::HostDefinedOptions::kType,
1112       Number::New(isolate, loader::ScriptType::kFunction));
1113   host_defined_options->Set(
1114       isolate, loader::HostDefinedOptions::kID, Number::New(isolate, id));
1115 
1116   ScriptOrigin origin(filename,
1117                       line_offset,       // line offset
1118                       column_offset,     // column offset
1119                       True(isolate),     // is cross origin
1120                       Local<Integer>(),  // script id
1121                       Local<Value>(),    // source map URL
1122                       False(isolate),    // is opaque (?)
1123                       False(isolate),    // is WASM
1124                       False(isolate),    // is ES Module
1125                       host_defined_options);
1126 
1127   ScriptCompiler::Source source(code, origin, cached_data);
1128   ScriptCompiler::CompileOptions options;
1129   if (source.GetCachedData() == nullptr) {
1130     options = ScriptCompiler::kNoCompileOptions;
1131   } else {
1132     options = ScriptCompiler::kConsumeCodeCache;
1133   }
1134 
1135   TryCatchScope try_catch(env);
1136   Context::Scope scope(parsing_context);
1137 
1138   // Read context extensions from buffer
1139   std::vector<Local<Object>> context_extensions;
1140   if (!context_extensions_buf.IsEmpty()) {
1141     for (uint32_t n = 0; n < context_extensions_buf->Length(); n++) {
1142       Local<Value> val;
1143       if (!context_extensions_buf->Get(context, n).ToLocal(&val)) return;
1144       CHECK(val->IsObject());
1145       context_extensions.push_back(val.As<Object>());
1146     }
1147   }
1148 
1149   // Read params from params buffer
1150   std::vector<Local<String>> params;
1151   if (!params_buf.IsEmpty()) {
1152     for (uint32_t n = 0; n < params_buf->Length(); n++) {
1153       Local<Value> val;
1154       if (!params_buf->Get(context, n).ToLocal(&val)) return;
1155       CHECK(val->IsString());
1156       params.push_back(val.As<String>());
1157     }
1158   }
1159 
1160   Local<ScriptOrModule> script;
1161   MaybeLocal<Function> maybe_fn = ScriptCompiler::CompileFunctionInContext(
1162       parsing_context, &source, params.size(), params.data(),
1163       context_extensions.size(), context_extensions.data(), options,
1164       v8::ScriptCompiler::NoCacheReason::kNoCacheNoReason, &script);
1165 
1166   Local<Function> fn;
1167   if (!maybe_fn.ToLocal(&fn)) {
1168     if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
1169       errors::DecorateErrorStack(env, try_catch);
1170       try_catch.ReThrow();
1171     }
1172     return;
1173   }
1174 
1175   Local<Object> cache_key;
1176   if (!env->compiled_fn_entry_template()->NewInstance(
1177            context).ToLocal(&cache_key)) {
1178     return;
1179   }
1180   CompiledFnEntry* entry = new CompiledFnEntry(env, cache_key, id, script);
1181   env->id_to_function_map.emplace(id, entry);
1182 
1183   Local<Object> result = Object::New(isolate);
1184   if (result->Set(parsing_context, env->function_string(), fn).IsNothing())
1185     return;
1186   if (result->Set(parsing_context, env->cache_key_string(), cache_key)
1187           .IsNothing())
1188     return;
1189 
1190   if (produce_cached_data) {
1191     const std::unique_ptr<ScriptCompiler::CachedData> cached_data(
1192         ScriptCompiler::CreateCodeCacheForFunction(fn));
1193     bool cached_data_produced = cached_data != nullptr;
1194     if (cached_data_produced) {
1195       MaybeLocal<Object> buf = Buffer::Copy(
1196           env,
1197           reinterpret_cast<const char*>(cached_data->data),
1198           cached_data->length);
1199       if (result
1200               ->Set(parsing_context,
1201                     env->cached_data_string(),
1202                     buf.ToLocalChecked())
1203               .IsNothing())
1204         return;
1205     }
1206     if (result
1207             ->Set(parsing_context,
1208                   env->cached_data_produced_string(),
1209                   Boolean::New(isolate, cached_data_produced))
1210             .IsNothing())
1211       return;
1212   }
1213 
1214   args.GetReturnValue().Set(result);
1215 }
1216 
WeakCallback(const WeakCallbackInfo<CompiledFnEntry> & data)1217 void CompiledFnEntry::WeakCallback(
1218     const WeakCallbackInfo<CompiledFnEntry>& data) {
1219   CompiledFnEntry* entry = data.GetParameter();
1220   delete entry;
1221 }
1222 
CompiledFnEntry(Environment * env,Local<Object> object,uint32_t id,Local<ScriptOrModule> script)1223 CompiledFnEntry::CompiledFnEntry(Environment* env,
1224                                  Local<Object> object,
1225                                  uint32_t id,
1226                                  Local<ScriptOrModule> script)
1227     : BaseObject(env, object),
1228       id_(id),
1229       script_(env->isolate(), script) {
1230   script_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter);
1231 }
1232 
~CompiledFnEntry()1233 CompiledFnEntry::~CompiledFnEntry() {
1234   env()->id_to_function_map.erase(id_);
1235   script_.ClearWeak();
1236 }
1237 
StartSigintWatchdog(const FunctionCallbackInfo<Value> & args)1238 static void StartSigintWatchdog(const FunctionCallbackInfo<Value>& args) {
1239   int ret = SigintWatchdogHelper::GetInstance()->Start();
1240   args.GetReturnValue().Set(ret == 0);
1241 }
1242 
StopSigintWatchdog(const FunctionCallbackInfo<Value> & args)1243 static void StopSigintWatchdog(const FunctionCallbackInfo<Value>& args) {
1244   bool had_pending_signals = SigintWatchdogHelper::GetInstance()->Stop();
1245   args.GetReturnValue().Set(had_pending_signals);
1246 }
1247 
WatchdogHasPendingSigint(const FunctionCallbackInfo<Value> & args)1248 static void WatchdogHasPendingSigint(const FunctionCallbackInfo<Value>& args) {
1249   bool ret = SigintWatchdogHelper::GetInstance()->HasPendingSignal();
1250   args.GetReturnValue().Set(ret);
1251 }
1252 
MeasureMemory(const FunctionCallbackInfo<Value> & args)1253 static void MeasureMemory(const FunctionCallbackInfo<Value>& args) {
1254   CHECK(args[0]->IsInt32());
1255   CHECK(args[1]->IsInt32());
1256   int32_t mode = args[0].As<v8::Int32>()->Value();
1257   int32_t execution = args[1].As<v8::Int32>()->Value();
1258   Isolate* isolate = args.GetIsolate();
1259 
1260   Local<Context> current_context = isolate->GetCurrentContext();
1261   Local<Promise::Resolver> resolver;
1262   if (!Promise::Resolver::New(current_context).ToLocal(&resolver)) return;
1263   std::unique_ptr<v8::MeasureMemoryDelegate> delegate =
1264       v8::MeasureMemoryDelegate::Default(
1265           isolate,
1266           current_context,
1267           resolver,
1268           static_cast<v8::MeasureMemoryMode>(mode));
1269   isolate->MeasureMemory(std::move(delegate),
1270                          static_cast<v8::MeasureMemoryExecution>(execution));
1271   v8::Local<v8::Promise> promise = resolver->GetPromise();
1272 
1273   args.GetReturnValue().Set(promise);
1274 }
1275 
MicrotaskQueueWrap(Environment * env,Local<Object> obj)1276 MicrotaskQueueWrap::MicrotaskQueueWrap(Environment* env, Local<Object> obj)
1277   : BaseObject(env, obj),
1278     microtask_queue_(
1279         MicrotaskQueue::New(env->isolate(), MicrotasksPolicy::kExplicit)) {
1280   MakeWeak();
1281 }
1282 
1283 const std::shared_ptr<MicrotaskQueue>&
microtask_queue() const1284 MicrotaskQueueWrap::microtask_queue() const {
1285   return microtask_queue_;
1286 }
1287 
New(const FunctionCallbackInfo<Value> & args)1288 void MicrotaskQueueWrap::New(const FunctionCallbackInfo<Value>& args) {
1289   CHECK(args.IsConstructCall());
1290   new MicrotaskQueueWrap(Environment::GetCurrent(args), args.This());
1291 }
1292 
Init(Environment * env,Local<Object> target)1293 void MicrotaskQueueWrap::Init(Environment* env, Local<Object> target) {
1294   HandleScope scope(env->isolate());
1295   Local<FunctionTemplate> tmpl = env->NewFunctionTemplate(New);
1296   tmpl->InstanceTemplate()->SetInternalFieldCount(
1297       ContextifyScript::kInternalFieldCount);
1298   env->set_microtask_queue_ctor_template(tmpl);
1299   env->SetConstructorFunction(target, "MicrotaskQueue", tmpl);
1300 }
1301 
1302 
Initialize(Local<Object> target,Local<Value> unused,Local<Context> context,void * priv)1303 void Initialize(Local<Object> target,
1304                 Local<Value> unused,
1305                 Local<Context> context,
1306                 void* priv) {
1307   Environment* env = Environment::GetCurrent(context);
1308   Isolate* isolate = env->isolate();
1309   ContextifyContext::Init(env, target);
1310   ContextifyScript::Init(env, target);
1311   MicrotaskQueueWrap::Init(env, target);
1312 
1313   env->SetMethod(target, "startSigintWatchdog", StartSigintWatchdog);
1314   env->SetMethod(target, "stopSigintWatchdog", StopSigintWatchdog);
1315   // Used in tests.
1316   env->SetMethodNoSideEffect(
1317       target, "watchdogHasPendingSigint", WatchdogHasPendingSigint);
1318 
1319   {
1320     Local<FunctionTemplate> tpl = FunctionTemplate::New(env->isolate());
1321     tpl->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "CompiledFnEntry"));
1322     tpl->InstanceTemplate()->SetInternalFieldCount(
1323         CompiledFnEntry::kInternalFieldCount);
1324 
1325     env->set_compiled_fn_entry_template(tpl->InstanceTemplate());
1326   }
1327 
1328   Local<Object> constants = Object::New(env->isolate());
1329   Local<Object> measure_memory = Object::New(env->isolate());
1330   Local<Object> memory_execution = Object::New(env->isolate());
1331 
1332   {
1333     Local<Object> memory_mode = Object::New(env->isolate());
1334     MeasureMemoryMode SUMMARY = MeasureMemoryMode::kSummary;
1335     MeasureMemoryMode DETAILED = MeasureMemoryMode::kDetailed;
1336     NODE_DEFINE_CONSTANT(memory_mode, SUMMARY);
1337     NODE_DEFINE_CONSTANT(memory_mode, DETAILED);
1338     READONLY_PROPERTY(measure_memory, "mode", memory_mode);
1339   }
1340 
1341   {
1342     MeasureMemoryExecution DEFAULT = MeasureMemoryExecution::kDefault;
1343     MeasureMemoryExecution EAGER = MeasureMemoryExecution::kEager;
1344     NODE_DEFINE_CONSTANT(memory_execution, DEFAULT);
1345     NODE_DEFINE_CONSTANT(memory_execution, EAGER);
1346     READONLY_PROPERTY(measure_memory, "execution", memory_execution);
1347   }
1348 
1349   READONLY_PROPERTY(constants, "measureMemory", measure_memory);
1350 
1351   target->Set(context, env->constants_string(), constants).Check();
1352 
1353   env->SetMethod(target, "measureMemory", MeasureMemory);
1354 }
1355 
1356 }  // namespace contextify
1357 }  // namespace node
1358 
1359 NODE_MODULE_CONTEXT_AWARE_INTERNAL(contextify, node::contextify::Initialize)
1360