• 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 "base_object-inl.h"
25 #include "memory_tracker-inl.h"
26 #include "module_wrap.h"
27 #include "node_context_data.h"
28 #include "node_errors.h"
29 #include "node_external_reference.h"
30 #include "node_internals.h"
31 #include "node_snapshot_builder.h"
32 #include "node_watchdog.h"
33 #include "util-inl.h"
34 
35 namespace node {
36 namespace contextify {
37 
38 using errors::TryCatchScope;
39 
40 using v8::Array;
41 using v8::ArrayBufferView;
42 using v8::Boolean;
43 using v8::Context;
44 using v8::EscapableHandleScope;
45 using v8::Function;
46 using v8::FunctionCallbackInfo;
47 using v8::FunctionTemplate;
48 using v8::HandleScope;
49 using v8::IndexedPropertyHandlerConfiguration;
50 using v8::Int32;
51 using v8::Isolate;
52 using v8::Just;
53 using v8::Local;
54 using v8::Maybe;
55 using v8::MaybeLocal;
56 using v8::MeasureMemoryExecution;
57 using v8::MeasureMemoryMode;
58 using v8::MicrotaskQueue;
59 using v8::MicrotasksPolicy;
60 using v8::Name;
61 using v8::NamedPropertyHandlerConfiguration;
62 using v8::Nothing;
63 using v8::Object;
64 using v8::ObjectTemplate;
65 using v8::PrimitiveArray;
66 using v8::Promise;
67 using v8::PropertyAttribute;
68 using v8::PropertyCallbackInfo;
69 using v8::PropertyDescriptor;
70 using v8::PropertyHandlerFlags;
71 using v8::Script;
72 using v8::ScriptCompiler;
73 using v8::ScriptOrigin;
74 using v8::String;
75 using v8::Symbol;
76 using v8::Uint32;
77 using v8::UnboundScript;
78 using v8::Value;
79 using v8::WeakCallbackInfo;
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 
New(Environment * env,Local<Object> sandbox_obj,const ContextOptions & options)111 BaseObjectPtr<ContextifyContext> ContextifyContext::New(
112     Environment* env,
113     Local<Object> sandbox_obj,
114     const ContextOptions& options) {
115   HandleScope scope(env->isolate());
116   InitializeGlobalTemplates(env->isolate_data());
117   Local<ObjectTemplate> object_template = env->contextify_global_template();
118   DCHECK(!object_template.IsEmpty());
119   bool use_node_snapshot = per_process::cli_options->node_snapshot;
120   const SnapshotData* snapshot_data =
121       use_node_snapshot ? SnapshotBuilder::GetEmbeddedSnapshotData() : nullptr;
122 
123   MicrotaskQueue* queue =
124       options.microtask_queue_wrap
125           ? options.microtask_queue_wrap->microtask_queue().get()
126           : env->isolate()->GetCurrentContext()->GetMicrotaskQueue();
127 
128   Local<Context> v8_context;
129   if (!(CreateV8Context(env->isolate(), object_template, snapshot_data, queue)
130             .ToLocal(&v8_context))) {
131     // Allocation failure, maximum call stack size reached, termination, etc.
132     return BaseObjectPtr<ContextifyContext>();
133   }
134   return New(v8_context, env, sandbox_obj, options);
135 }
136 
MemoryInfo(MemoryTracker * tracker) const137 void ContextifyContext::MemoryInfo(MemoryTracker* tracker) const {
138   if (microtask_queue_wrap_) {
139     tracker->TrackField("microtask_queue_wrap",
140                         microtask_queue_wrap_->object());
141   }
142 }
143 
ContextifyContext(Environment * env,Local<Object> wrapper,Local<Context> v8_context,const ContextOptions & options)144 ContextifyContext::ContextifyContext(Environment* env,
145                                      Local<Object> wrapper,
146                                      Local<Context> v8_context,
147                                      const ContextOptions& options)
148     : BaseObject(env, wrapper),
149       microtask_queue_wrap_(options.microtask_queue_wrap) {
150   context_.Reset(env->isolate(), v8_context);
151   // This should only be done after the initial initializations of the context
152   // global object is finished.
153   DCHECK_NULL(v8_context->GetAlignedPointerFromEmbedderData(
154       ContextEmbedderIndex::kContextifyContext));
155   v8_context->SetAlignedPointerInEmbedderData(
156       ContextEmbedderIndex::kContextifyContext, this);
157   // It's okay to make this reference weak - V8 would create an internal
158   // reference to this context via the constructor of the wrapper.
159   // As long as the wrapper is alive, it's constructor is alive, and so
160   // is the context.
161   context_.SetWeak();
162 }
163 
~ContextifyContext()164 ContextifyContext::~ContextifyContext() {
165   Isolate* isolate = env()->isolate();
166   HandleScope scope(isolate);
167 
168   env()->UntrackContext(PersistentToLocal::Weak(isolate, context_));
169   context_.Reset();
170 }
171 
InitializeGlobalTemplates(IsolateData * isolate_data)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();
181 
182   NamedPropertyHandlerConfiguration config(
183       PropertyGetterCallback,
184       PropertySetterCallback,
185       PropertyDescriptorCallback,
186       PropertyDeleterCallback,
187       PropertyEnumeratorCallback,
188       PropertyDefinerCallback,
189       {},
190       PropertyHandlerFlags::kHasNoSideEffect);
191 
192   IndexedPropertyHandlerConfiguration indexed_config(
193       IndexedPropertyGetterCallback,
194       IndexedPropertySetterCallback,
195       IndexedPropertyDescriptorCallback,
196       IndexedPropertyDeleterCallback,
197       PropertyEnumeratorCallback,
198       IndexedPropertyDefinerCallback,
199       {},
200       PropertyHandlerFlags::kHasNoSideEffect);
201 
202   global_object_template->SetHandler(config);
203   global_object_template->SetHandler(indexed_config);
204   isolate_data->set_contextify_global_template(global_object_template);
205 
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);
211 }
212 
CreateV8Context(Isolate * isolate,Local<ObjectTemplate> object_template,const SnapshotData * snapshot_data,MicrotaskQueue * queue)213 MaybeLocal<Context> ContextifyContext::CreateV8Context(
214     Isolate* isolate,
215     Local<ObjectTemplate> object_template,
216     const SnapshotData* snapshot_data,
217     MicrotaskQueue* queue) {
218   EscapableHandleScope scope(isolate);
219 
220   Local<Context> ctx;
221   if (snapshot_data == nullptr) {
222     ctx = Context::New(isolate,
223                        nullptr,  // extensions
224                        object_template,
225                        {},  // global object
226                        {},  // deserialization callback
227                        queue);
228     if (ctx.IsEmpty() || InitializeBaseContextForSnapshot(ctx).IsNothing()) {
229       return MaybeLocal<Context>();
230     }
231   } else if (!Context::FromSnapshot(isolate,
232                                     SnapshotData::kNodeVMContextIndex,
233                                     {},       // deserialization callback
234                                     nullptr,  // extensions
235                                     {},       // global object
236                                     queue)
237                   .ToLocal(&ctx)) {
238     return MaybeLocal<Context>();
239   }
240 
241   return scope.Escape(ctx);
242 }
243 
New(Local<Context> v8_context,Environment * env,Local<Object> sandbox_obj,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) {
249   HandleScope scope(env->isolate());
250   // This only initializes part of the context. The primordials are
251   // only initialized when needed because even deserializing them slows
252   // things down significantly and they are only needed in rare occasions
253   // in the vm contexts.
254   if (InitializeContextRuntime(v8_context).IsNothing()) {
255     return BaseObjectPtr<ContextifyContext>();
256   }
257 
258   Local<Context> main_context = env->context();
259   Local<Object> new_context_global = v8_context->Global();
260   v8_context->SetSecurityToken(main_context->GetSecurityToken());
261 
262   // We need to tie the lifetime of the sandbox object with the lifetime of
263   // newly created context. We do this by making them hold references to each
264   // other. The context can directly hold a reference to the sandbox as an
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);
270 
271   // Delegate the code generation validation to
272   // node::ModifyCodeGenerationFromStrings.
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);
279 
280   Utf8Value name_val(env->isolate(), options.name);
281   ContextInfo info(*name_val);
282   if (!options.origin.IsEmpty()) {
283     Utf8Value origin_val(env->isolate(), options.origin);
284     info.origin = *origin_val;
285   }
286 
287   BaseObjectPtr<ContextifyContext> result;
288   Local<Object> wrapper;
289   {
290     Context::Scope context_scope(v8_context);
291     Local<String> ctor_name = sandbox_obj->GetConstructorName();
292     if (!ctor_name->Equals(v8_context, env->object_string()).FromMaybe(false) &&
293         new_context_global
294             ->DefineOwnProperty(
295                 v8_context,
296                 v8::Symbol::GetToStringTag(env->isolate()),
297                 ctor_name,
298                 static_cast<v8::PropertyAttribute>(v8::DontEnum))
299             .IsNothing()) {
300       return BaseObjectPtr<ContextifyContext>();
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();
314   }
315 
316   if (sandbox_obj
317           ->SetPrivate(
318               v8_context, env->contextify_context_private_symbol(), wrapper)
319           .IsNothing()) {
320     return BaseObjectPtr<ContextifyContext>();
321   }
322 
323   return result;
324 }
325 
Init(Environment * env,Local<Object> target)326 void ContextifyContext::Init(Environment* env, Local<Object> target) {
327   Local<Context> context = env->context();
328   SetMethod(context, target, "makeContext", MakeContext);
329   SetMethod(context, target, "isContext", IsContext);
330   SetMethod(context, target, "compileFunction", CompileFunction);
331 }
332 
RegisterExternalReferences(ExternalReferenceRegistry * registry)333 void ContextifyContext::RegisterExternalReferences(
334     ExternalReferenceRegistry* registry) {
335   registry->Register(MakeContext);
336   registry->Register(IsContext);
337   registry->Register(CompileFunction);
338   registry->Register(PropertyGetterCallback);
339   registry->Register(PropertySetterCallback);
340   registry->Register(PropertyDescriptorCallback);
341   registry->Register(PropertyDeleterCallback);
342   registry->Register(PropertyEnumeratorCallback);
343   registry->Register(PropertyDefinerCallback);
344   registry->Register(IndexedPropertyGetterCallback);
345   registry->Register(IndexedPropertySetterCallback);
346   registry->Register(IndexedPropertyDescriptorCallback);
347   registry->Register(IndexedPropertyDeleterCallback);
348   registry->Register(IndexedPropertyDefinerCallback);
349 }
350 
351 // makeContext(sandbox, name, origin, strings, wasm);
MakeContext(const FunctionCallbackInfo<Value> & args)352 void ContextifyContext::MakeContext(const FunctionCallbackInfo<Value>& args) {
353   Environment* env = Environment::GetCurrent(args);
354 
355   CHECK_EQ(args.Length(), 6);
356   CHECK(args[0]->IsObject());
357   Local<Object> sandbox = args[0].As<Object>();
358 
359   // Don't allow contextifying a sandbox multiple times.
360   CHECK(
361       !sandbox->HasPrivate(
362           env->context(),
363           env->contextify_context_private_symbol()).FromJust());
364 
365   ContextOptions options;
366 
367   CHECK(args[1]->IsString());
368   options.name = args[1].As<String>();
369 
370   CHECK(args[2]->IsString() || args[2]->IsUndefined());
371   if (args[2]->IsString()) {
372     options.origin = args[2].As<String>();
373   }
374 
375   CHECK(args[3]->IsBoolean());
376   options.allow_code_gen_strings = args[3].As<Boolean>();
377 
378   CHECK(args[4]->IsBoolean());
379   options.allow_code_gen_wasm = args[4].As<Boolean>();
380 
381   if (args[5]->IsObject() &&
382       !env->microtask_queue_ctor_template().IsEmpty() &&
383       env->microtask_queue_ctor_template()->HasInstance(args[5])) {
384     options.microtask_queue_wrap.reset(
385         Unwrap<MicrotaskQueueWrap>(args[5].As<Object>()));
386   }
387 
388   TryCatchScope try_catch(env);
389   BaseObjectPtr<ContextifyContext> context_ptr =
390       ContextifyContext::New(env, sandbox, options);
391 
392   if (try_catch.HasCaught()) {
393     if (!try_catch.HasTerminated())
394       try_catch.ReThrow();
395     return;
396   }
397 }
398 
399 
IsContext(const FunctionCallbackInfo<Value> & args)400 void ContextifyContext::IsContext(const FunctionCallbackInfo<Value>& args) {
401   Environment* env = Environment::GetCurrent(args);
402 
403   CHECK(args[0]->IsObject());
404   Local<Object> sandbox = args[0].As<Object>();
405 
406   Maybe<bool> result =
407       sandbox->HasPrivate(env->context(),
408                           env->contextify_context_private_symbol());
409   args.GetReturnValue().Set(result.FromJust());
410 }
411 
412 
WeakCallback(const WeakCallbackInfo<ContextifyContext> & data)413 void ContextifyContext::WeakCallback(
414     const WeakCallbackInfo<ContextifyContext>& data) {
415   ContextifyContext* context = data.GetParameter();
416   delete context;
417 }
418 
419 // static
ContextFromContextifiedSandbox(Environment * env,const Local<Object> & sandbox)420 ContextifyContext* ContextifyContext::ContextFromContextifiedSandbox(
421     Environment* env,
422     const Local<Object>& sandbox) {
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>());
429   }
430   return nullptr;
431 }
432 
433 template <typename T>
Get(const PropertyCallbackInfo<T> & args)434 ContextifyContext* ContextifyContext::Get(const PropertyCallbackInfo<T>& args) {
435   return Get(args.This());
436 }
437 
Get(Local<Object> object)438 ContextifyContext* ContextifyContext::Get(Local<Object> object) {
439   Local<Context> context;
440   if (!object->GetCreationContext().ToLocal(&context)) {
441     return nullptr;
442   }
443   if (!ContextEmbedderTag::IsNodeContext(context)) {
444     return nullptr;
445   }
446   return static_cast<ContextifyContext*>(
447       context->GetAlignedPointerFromEmbedderData(
448           ContextEmbedderIndex::kContextifyContext));
449 }
450 
IsStillInitializing(const ContextifyContext * ctx)451 bool ContextifyContext::IsStillInitializing(const ContextifyContext* ctx) {
452   return ctx == nullptr || ctx->context_.IsEmpty();
453 }
454 
455 // static
PropertyGetterCallback(Local<Name> property,const PropertyCallbackInfo<Value> & args)456 void ContextifyContext::PropertyGetterCallback(
457     Local<Name> property,
458     const PropertyCallbackInfo<Value>& args) {
459   ContextifyContext* ctx = ContextifyContext::Get(args);
460 
461   // Still initializing
462   if (IsStillInitializing(ctx)) return;
463 
464   Local<Context> context = ctx->context();
465   Local<Object> sandbox = ctx->sandbox();
466   MaybeLocal<Value> maybe_rv =
467       sandbox->GetRealNamedProperty(context, property);
468   if (maybe_rv.IsEmpty()) {
469     maybe_rv =
470         ctx->global_proxy()->GetRealNamedProperty(context, property);
471   }
472 
473   Local<Value> rv;
474   if (maybe_rv.ToLocal(&rv)) {
475     if (rv == sandbox)
476       rv = ctx->global_proxy();
477 
478     args.GetReturnValue().Set(rv);
479   }
480 }
481 
482 // static
PropertySetterCallback(Local<Name> property,Local<Value> value,const PropertyCallbackInfo<Value> & args)483 void ContextifyContext::PropertySetterCallback(
484     Local<Name> property,
485     Local<Value> value,
486     const PropertyCallbackInfo<Value>& args) {
487   ContextifyContext* ctx = ContextifyContext::Get(args);
488 
489   // Still initializing
490   if (IsStillInitializing(ctx)) return;
491 
492   Local<Context> context = ctx->context();
493   PropertyAttribute attributes = PropertyAttribute::None;
494   bool is_declared_on_global_proxy = ctx->global_proxy()
495       ->GetRealNamedPropertyAttributes(context, property)
496       .To(&attributes);
497   bool read_only =
498       static_cast<int>(attributes) &
499       static_cast<int>(PropertyAttribute::ReadOnly);
500 
501   bool is_declared_on_sandbox = ctx->sandbox()
502       ->GetRealNamedPropertyAttributes(context, property)
503       .To(&attributes);
504   read_only = read_only ||
505       (static_cast<int>(attributes) &
506       static_cast<int>(PropertyAttribute::ReadOnly));
507 
508   if (read_only)
509     return;
510 
511   // true for x = 5
512   // false for this.x = 5
513   // false for Object.defineProperty(this, 'foo', ...)
514   // false for vmResult.x = 5 where vmResult = vm.runInContext();
515   bool is_contextual_store = ctx->global_proxy() != args.This();
516 
517   // Indicator to not return before setting (undeclared) function declarations
518   // on the sandbox in strict mode, i.e. args.ShouldThrowOnError() = true.
519   // True for 'function f() {}', 'this.f = function() {}',
520   // 'var f = function()'.
521   // In effect only for 'function f() {}' because
522   // var f = function(), is_declared = true
523   // this.f = function() {}, is_contextual_store = false.
524   bool is_function = value->IsFunction();
525 
526   bool is_declared = is_declared_on_global_proxy || is_declared_on_sandbox;
527   if (!is_declared && args.ShouldThrowOnError() && is_contextual_store &&
528       !is_function)
529     return;
530 
531   if (!is_declared && property->IsSymbol()) return;
532   if (ctx->sandbox()->Set(context, property, value).IsNothing()) return;
533 
534   Local<Value> desc;
535   if (is_declared_on_sandbox &&
536       ctx->sandbox()
537           ->GetOwnPropertyDescriptor(context, property)
538           .ToLocal(&desc) &&
539       !desc->IsUndefined()) {
540     Environment* env = Environment::GetCurrent(context);
541     Local<Object> desc_obj = desc.As<Object>();
542 
543     // We have to specify the return value for any contextual or get/set
544     // property
545     if (desc_obj->HasOwnProperty(context, env->get_string()).FromMaybe(false) ||
546         desc_obj->HasOwnProperty(context, env->set_string()).FromMaybe(false))
547       args.GetReturnValue().Set(value);
548   }
549 }
550 
551 // static
PropertyDescriptorCallback(Local<Name> property,const PropertyCallbackInfo<Value> & args)552 void ContextifyContext::PropertyDescriptorCallback(
553     Local<Name> property,
554     const PropertyCallbackInfo<Value>& args) {
555   ContextifyContext* ctx = ContextifyContext::Get(args);
556 
557   // Still initializing
558   if (IsStillInitializing(ctx)) return;
559 
560   Local<Context> context = ctx->context();
561 
562   Local<Object> sandbox = ctx->sandbox();
563 
564   if (sandbox->HasOwnProperty(context, property).FromMaybe(false)) {
565     Local<Value> desc;
566     if (sandbox->GetOwnPropertyDescriptor(context, property).ToLocal(&desc)) {
567       args.GetReturnValue().Set(desc);
568     }
569   }
570 }
571 
572 // static
PropertyDefinerCallback(Local<Name> property,const PropertyDescriptor & desc,const PropertyCallbackInfo<Value> & args)573 void ContextifyContext::PropertyDefinerCallback(
574     Local<Name> property,
575     const PropertyDescriptor& desc,
576     const PropertyCallbackInfo<Value>& args) {
577   ContextifyContext* ctx = ContextifyContext::Get(args);
578 
579   // Still initializing
580   if (IsStillInitializing(ctx)) return;
581 
582   Local<Context> context = ctx->context();
583   Isolate* isolate = context->GetIsolate();
584 
585   PropertyAttribute attributes = PropertyAttribute::None;
586   bool is_declared =
587       ctx->global_proxy()->GetRealNamedPropertyAttributes(context,
588                                                           property)
589           .To(&attributes);
590   bool read_only =
591       static_cast<int>(attributes) &
592           static_cast<int>(PropertyAttribute::ReadOnly);
593 
594   // If the property is set on the global as read_only, don't change it on
595   // the global or sandbox.
596   if (is_declared && read_only)
597     return;
598 
599   Local<Object> sandbox = ctx->sandbox();
600 
601   auto define_prop_on_sandbox =
602       [&] (PropertyDescriptor* desc_for_sandbox) {
603         if (desc.has_enumerable()) {
604           desc_for_sandbox->set_enumerable(desc.enumerable());
605         }
606         if (desc.has_configurable()) {
607           desc_for_sandbox->set_configurable(desc.configurable());
608         }
609         // Set the property on the sandbox.
610         USE(sandbox->DefineProperty(context, property, *desc_for_sandbox));
611       };
612 
613   if (desc.has_get() || desc.has_set()) {
614     PropertyDescriptor desc_for_sandbox(
615         desc.has_get() ? desc.get() : Undefined(isolate).As<Value>(),
616         desc.has_set() ? desc.set() : Undefined(isolate).As<Value>());
617 
618     define_prop_on_sandbox(&desc_for_sandbox);
619   } else {
620     Local<Value> value =
621         desc.has_value() ? desc.value() : Undefined(isolate).As<Value>();
622 
623     if (desc.has_writable()) {
624       PropertyDescriptor desc_for_sandbox(value, desc.writable());
625       define_prop_on_sandbox(&desc_for_sandbox);
626     } else {
627       PropertyDescriptor desc_for_sandbox(value);
628       define_prop_on_sandbox(&desc_for_sandbox);
629     }
630   }
631 }
632 
633 // static
PropertyDeleterCallback(Local<Name> property,const PropertyCallbackInfo<Boolean> & args)634 void ContextifyContext::PropertyDeleterCallback(
635     Local<Name> property,
636     const PropertyCallbackInfo<Boolean>& args) {
637   ContextifyContext* ctx = ContextifyContext::Get(args);
638 
639   // Still initializing
640   if (IsStillInitializing(ctx)) return;
641 
642   Maybe<bool> success = ctx->sandbox()->Delete(ctx->context(), property);
643 
644   if (success.FromMaybe(false))
645     return;
646 
647   // Delete failed on the sandbox, intercept and do not delete on
648   // the global object.
649   args.GetReturnValue().Set(false);
650 }
651 
652 // static
PropertyEnumeratorCallback(const PropertyCallbackInfo<Array> & args)653 void ContextifyContext::PropertyEnumeratorCallback(
654     const PropertyCallbackInfo<Array>& args) {
655   ContextifyContext* ctx = ContextifyContext::Get(args);
656 
657   // Still initializing
658   if (IsStillInitializing(ctx)) return;
659 
660   Local<Array> properties;
661 
662   if (!ctx->sandbox()->GetPropertyNames(ctx->context()).ToLocal(&properties))
663     return;
664 
665   args.GetReturnValue().Set(properties);
666 }
667 
668 // static
IndexedPropertyGetterCallback(uint32_t index,const PropertyCallbackInfo<Value> & args)669 void ContextifyContext::IndexedPropertyGetterCallback(
670     uint32_t index,
671     const PropertyCallbackInfo<Value>& args) {
672   ContextifyContext* ctx = ContextifyContext::Get(args);
673 
674   // Still initializing
675   if (IsStillInitializing(ctx)) return;
676 
677   ContextifyContext::PropertyGetterCallback(
678       Uint32ToName(ctx->context(), index), args);
679 }
680 
681 
IndexedPropertySetterCallback(uint32_t index,Local<Value> value,const PropertyCallbackInfo<Value> & args)682 void ContextifyContext::IndexedPropertySetterCallback(
683     uint32_t index,
684     Local<Value> value,
685     const PropertyCallbackInfo<Value>& args) {
686   ContextifyContext* ctx = ContextifyContext::Get(args);
687 
688   // Still initializing
689   if (IsStillInitializing(ctx)) return;
690 
691   ContextifyContext::PropertySetterCallback(
692       Uint32ToName(ctx->context(), index), value, args);
693 }
694 
695 // static
IndexedPropertyDescriptorCallback(uint32_t index,const PropertyCallbackInfo<Value> & args)696 void ContextifyContext::IndexedPropertyDescriptorCallback(
697     uint32_t index,
698     const PropertyCallbackInfo<Value>& args) {
699   ContextifyContext* ctx = ContextifyContext::Get(args);
700 
701   // Still initializing
702   if (IsStillInitializing(ctx)) return;
703 
704   ContextifyContext::PropertyDescriptorCallback(
705       Uint32ToName(ctx->context(), index), args);
706 }
707 
708 
IndexedPropertyDefinerCallback(uint32_t index,const PropertyDescriptor & desc,const PropertyCallbackInfo<Value> & args)709 void ContextifyContext::IndexedPropertyDefinerCallback(
710     uint32_t index,
711     const PropertyDescriptor& desc,
712     const PropertyCallbackInfo<Value>& args) {
713   ContextifyContext* ctx = ContextifyContext::Get(args);
714 
715   // Still initializing
716   if (IsStillInitializing(ctx)) return;
717 
718   ContextifyContext::PropertyDefinerCallback(
719       Uint32ToName(ctx->context(), index), desc, args);
720 }
721 
722 // static
IndexedPropertyDeleterCallback(uint32_t index,const PropertyCallbackInfo<Boolean> & args)723 void ContextifyContext::IndexedPropertyDeleterCallback(
724     uint32_t index,
725     const PropertyCallbackInfo<Boolean>& args) {
726   ContextifyContext* ctx = ContextifyContext::Get(args);
727 
728   // Still initializing
729   if (IsStillInitializing(ctx)) return;
730 
731   Maybe<bool> success = ctx->sandbox()->Delete(ctx->context(), index);
732 
733   if (success.FromMaybe(false))
734     return;
735 
736   // Delete failed on the sandbox, intercept and do not delete on
737   // the global object.
738   args.GetReturnValue().Set(false);
739 }
740 
Init(Environment * env,Local<Object> target)741 void ContextifyScript::Init(Environment* env, Local<Object> target) {
742   Isolate* isolate = env->isolate();
743   HandleScope scope(env->isolate());
744   Local<String> class_name =
745       FIXED_ONE_BYTE_STRING(env->isolate(), "ContextifyScript");
746 
747   Local<FunctionTemplate> script_tmpl = NewFunctionTemplate(isolate, New);
748   script_tmpl->InstanceTemplate()->SetInternalFieldCount(
749       ContextifyScript::kInternalFieldCount);
750   script_tmpl->SetClassName(class_name);
751   SetProtoMethod(isolate, script_tmpl, "createCachedData", CreateCachedData);
752   SetProtoMethod(isolate, script_tmpl, "runInContext", RunInContext);
753 
754   Local<Context> context = env->context();
755 
756   target->Set(context, class_name,
757       script_tmpl->GetFunction(context).ToLocalChecked()).Check();
758   env->set_script_context_constructor_template(script_tmpl);
759 }
760 
RegisterExternalReferences(ExternalReferenceRegistry * registry)761 void ContextifyScript::RegisterExternalReferences(
762     ExternalReferenceRegistry* registry) {
763   registry->Register(New);
764   registry->Register(CreateCachedData);
765   registry->Register(RunInContext);
766 }
767 
New(const FunctionCallbackInfo<Value> & args)768 void ContextifyScript::New(const FunctionCallbackInfo<Value>& args) {
769   Environment* env = Environment::GetCurrent(args);
770   Isolate* isolate = env->isolate();
771   Local<Context> context = env->context();
772 
773   CHECK(args.IsConstructCall());
774 
775   const int argc = args.Length();
776   CHECK_GE(argc, 2);
777 
778   CHECK(args[0]->IsString());
779   Local<String> code = args[0].As<String>();
780 
781   CHECK(args[1]->IsString());
782   Local<String> filename = args[1].As<String>();
783 
784   int line_offset = 0;
785   int column_offset = 0;
786   Local<ArrayBufferView> cached_data_buf;
787   bool produce_cached_data = false;
788   Local<Context> parsing_context = context;
789 
790   Local<Symbol> id_symbol;
791   if (argc > 2) {
792     // new ContextifyScript(code, filename, lineOffset, columnOffset,
793     //                      cachedData, produceCachedData, parsingContext,
794     //                      hostDefinedOptionId)
795     CHECK_EQ(argc, 8);
796     CHECK(args[2]->IsNumber());
797     line_offset = args[2].As<Int32>()->Value();
798     CHECK(args[3]->IsNumber());
799     column_offset = args[3].As<Int32>()->Value();
800     if (!args[4]->IsUndefined()) {
801       CHECK(args[4]->IsArrayBufferView());
802       cached_data_buf = args[4].As<ArrayBufferView>();
803     }
804     CHECK(args[5]->IsBoolean());
805     produce_cached_data = args[5]->IsTrue();
806     if (!args[6]->IsUndefined()) {
807       CHECK(args[6]->IsObject());
808       ContextifyContext* sandbox =
809           ContextifyContext::ContextFromContextifiedSandbox(
810               env, args[6].As<Object>());
811       CHECK_NOT_NULL(sandbox);
812       parsing_context = sandbox->context();
813     }
814     CHECK(args[7]->IsSymbol());
815     id_symbol = args[7].As<Symbol>();
816   }
817 
818   ContextifyScript* contextify_script =
819       new ContextifyScript(env, args.This());
820 
821   if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
822           TRACING_CATEGORY_NODE2(vm, script)) != 0) {
823     Utf8Value fn(isolate, filename);
824     TRACE_EVENT_BEGIN1(TRACING_CATEGORY_NODE2(vm, script),
825                        "ContextifyScript::New",
826                        "filename",
827                        TRACE_STR_COPY(*fn));
828   }
829 
830   ScriptCompiler::CachedData* cached_data = nullptr;
831   if (!cached_data_buf.IsEmpty()) {
832     uint8_t* data = static_cast<uint8_t*>(cached_data_buf->Buffer()->Data());
833     cached_data = new ScriptCompiler::CachedData(
834         data + cached_data_buf->ByteOffset(), cached_data_buf->ByteLength());
835   }
836 
837   Local<PrimitiveArray> host_defined_options =
838       PrimitiveArray::New(isolate, loader::HostDefinedOptions::kLength);
839   host_defined_options->Set(
840       isolate, loader::HostDefinedOptions::kID, id_symbol);
841 
842   ScriptOrigin origin(isolate,
843                       filename,
844                       line_offset,                          // line offset
845                       column_offset,                        // column offset
846                       true,                                 // is cross origin
847                       -1,                                   // script id
848                       Local<Value>(),                       // source map URL
849                       false,                                // is opaque (?)
850                       false,                                // is WASM
851                       false,                                // is ES Module
852                       host_defined_options);
853   ScriptCompiler::Source source(code, origin, cached_data);
854   ScriptCompiler::CompileOptions compile_options =
855       ScriptCompiler::kNoCompileOptions;
856 
857   if (source.GetCachedData() != nullptr)
858     compile_options = ScriptCompiler::kConsumeCodeCache;
859 
860   TryCatchScope try_catch(env);
861   ShouldNotAbortOnUncaughtScope no_abort_scope(env);
862   Context::Scope scope(parsing_context);
863 
864   MaybeLocal<UnboundScript> maybe_v8_script =
865       ScriptCompiler::CompileUnboundScript(isolate, &source, compile_options);
866 
867   Local<UnboundScript> v8_script;
868   if (!maybe_v8_script.ToLocal(&v8_script)) {
869     errors::DecorateErrorStack(env, try_catch);
870     no_abort_scope.Close();
871     if (!try_catch.HasTerminated())
872       try_catch.ReThrow();
873     TRACE_EVENT_END0(TRACING_CATEGORY_NODE2(vm, script),
874                      "ContextifyScript::New");
875     return;
876   }
877 
878   contextify_script->script_.Reset(isolate, v8_script);
879   contextify_script->script_.SetWeak();
880   contextify_script->object()->SetInternalFieldForNodeCore(kUnboundScriptSlot,
881                                                            v8_script);
882 
883   std::unique_ptr<ScriptCompiler::CachedData> new_cached_data;
884   if (produce_cached_data) {
885     new_cached_data.reset(ScriptCompiler::CreateCodeCache(v8_script));
886   }
887 
888   if (contextify_script->object()
889           ->SetPrivate(context, env->host_defined_option_symbol(), id_symbol)
890           .IsNothing()) {
891     return;
892   }
893 
894   if (StoreCodeCacheResult(env,
895                            args.This(),
896                            compile_options,
897                            source,
898                            produce_cached_data,
899                            std::move(new_cached_data))
900           .IsNothing()) {
901     return;
902   }
903 
904   if (args.This()
905           ->Set(env->context(),
906                 env->source_map_url_string(),
907                 v8_script->GetSourceMappingURL())
908           .IsNothing())
909     return;
910 
911   TRACE_EVENT_END0(TRACING_CATEGORY_NODE2(vm, script), "ContextifyScript::New");
912 }
913 
StoreCodeCacheResult(Environment * env,Local<Object> target,ScriptCompiler::CompileOptions compile_options,const v8::ScriptCompiler::Source & source,bool produce_cached_data,std::unique_ptr<ScriptCompiler::CachedData> new_cached_data)914 Maybe<bool> StoreCodeCacheResult(
915     Environment* env,
916     Local<Object> target,
917     ScriptCompiler::CompileOptions compile_options,
918     const v8::ScriptCompiler::Source& source,
919     bool produce_cached_data,
920     std::unique_ptr<ScriptCompiler::CachedData> new_cached_data) {
921   Local<Context> context;
922   if (!target->GetCreationContext().ToLocal(&context)) {
923     return Nothing<bool>();
924   }
925   if (compile_options == ScriptCompiler::kConsumeCodeCache) {
926     if (target
927             ->Set(
928                 context,
929                 env->cached_data_rejected_string(),
930                 Boolean::New(env->isolate(), source.GetCachedData()->rejected))
931             .IsNothing()) {
932       return Nothing<bool>();
933     }
934   }
935   if (produce_cached_data) {
936     bool cached_data_produced = new_cached_data != nullptr;
937     if (cached_data_produced) {
938       MaybeLocal<Object> buf =
939           Buffer::Copy(env,
940                        reinterpret_cast<const char*>(new_cached_data->data),
941                        new_cached_data->length);
942       if (target->Set(context, env->cached_data_string(), buf.ToLocalChecked())
943               .IsNothing()) {
944         return Nothing<bool>();
945       }
946     }
947     if (target
948             ->Set(context,
949                   env->cached_data_produced_string(),
950                   Boolean::New(env->isolate(), cached_data_produced))
951             .IsNothing()) {
952       return Nothing<bool>();
953     }
954   }
955   return Just(true);
956 }
957 
InstanceOf(Environment * env,const Local<Value> & value)958 bool ContextifyScript::InstanceOf(Environment* env,
959                                   const Local<Value>& value) {
960   return !value.IsEmpty() &&
961          env->script_context_constructor_template()->HasInstance(value);
962 }
963 
CreateCachedData(const FunctionCallbackInfo<Value> & args)964 void ContextifyScript::CreateCachedData(
965     const FunctionCallbackInfo<Value>& args) {
966   Environment* env = Environment::GetCurrent(args);
967   ContextifyScript* wrapped_script;
968   ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder());
969   Local<UnboundScript> unbound_script =
970       PersistentToLocal::Default(env->isolate(), wrapped_script->script_);
971   std::unique_ptr<ScriptCompiler::CachedData> cached_data(
972       ScriptCompiler::CreateCodeCache(unbound_script));
973   if (!cached_data) {
974     args.GetReturnValue().Set(Buffer::New(env, 0).ToLocalChecked());
975   } else {
976     MaybeLocal<Object> buf = Buffer::Copy(
977         env,
978         reinterpret_cast<const char*>(cached_data->data),
979         cached_data->length);
980     args.GetReturnValue().Set(buf.ToLocalChecked());
981   }
982 }
983 
RunInContext(const FunctionCallbackInfo<Value> & args)984 void ContextifyScript::RunInContext(const FunctionCallbackInfo<Value>& args) {
985   Environment* env = Environment::GetCurrent(args);
986 
987   ContextifyScript* wrapped_script;
988   ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder());
989 
990   CHECK_EQ(args.Length(), 5);
991   CHECK(args[0]->IsObject() || args[0]->IsNull());
992 
993   Local<Context> context;
994   std::shared_ptr<MicrotaskQueue> microtask_queue;
995 
996   if (args[0]->IsObject()) {
997     Local<Object> sandbox = args[0].As<Object>();
998     // Get the context from the sandbox
999     ContextifyContext* contextify_context =
1000         ContextifyContext::ContextFromContextifiedSandbox(env, sandbox);
1001     CHECK_NOT_NULL(contextify_context);
1002     CHECK_EQ(contextify_context->env(), env);
1003 
1004     context = contextify_context->context();
1005     if (context.IsEmpty()) return;
1006 
1007     microtask_queue = contextify_context->microtask_queue();
1008   } else {
1009     context = env->context();
1010   }
1011 
1012   TRACE_EVENT0(TRACING_CATEGORY_NODE2(vm, script), "RunInContext");
1013 
1014   CHECK(args[1]->IsNumber());
1015   int64_t timeout = args[1]->IntegerValue(env->context()).FromJust();
1016 
1017   CHECK(args[2]->IsBoolean());
1018   bool display_errors = args[2]->IsTrue();
1019 
1020   CHECK(args[3]->IsBoolean());
1021   bool break_on_sigint = args[3]->IsTrue();
1022 
1023   CHECK(args[4]->IsBoolean());
1024   bool break_on_first_line = args[4]->IsTrue();
1025 
1026   // Do the eval within the context
1027   EvalMachine(context,
1028               env,
1029               timeout,
1030               display_errors,
1031               break_on_sigint,
1032               break_on_first_line,
1033               microtask_queue,
1034               args);
1035 }
1036 
EvalMachine(Local<Context> context,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)1037 bool ContextifyScript::EvalMachine(Local<Context> context,
1038                                    Environment* env,
1039                                    const int64_t timeout,
1040                                    const bool display_errors,
1041                                    const bool break_on_sigint,
1042                                    const bool break_on_first_line,
1043                                    std::shared_ptr<MicrotaskQueue> mtask_queue,
1044                                    const FunctionCallbackInfo<Value>& args) {
1045   Context::Scope context_scope(context);
1046 
1047   if (!env->can_call_into_js())
1048     return false;
1049   if (!ContextifyScript::InstanceOf(env, args.Holder())) {
1050     THROW_ERR_INVALID_THIS(
1051         env,
1052         "Script methods can only be called on script instances.");
1053     return false;
1054   }
1055 
1056   TryCatchScope try_catch(env);
1057   Isolate::SafeForTerminationScope safe_for_termination(env->isolate());
1058   ContextifyScript* wrapped_script;
1059   ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder(), false);
1060   Local<UnboundScript> unbound_script =
1061       PersistentToLocal::Default(env->isolate(), wrapped_script->script_);
1062   Local<Script> script = unbound_script->BindToCurrentContext();
1063 
1064 #if HAVE_INSPECTOR
1065   if (break_on_first_line) {
1066     env->inspector_agent()->PauseOnNextJavascriptStatement("Break on start");
1067   }
1068 #endif
1069 
1070   MaybeLocal<Value> result;
1071   bool timed_out = false;
1072   bool received_signal = false;
1073   auto run = [&]() {
1074     MaybeLocal<Value> result = script->Run(context);
1075     if (!result.IsEmpty() && mtask_queue)
1076       mtask_queue->PerformCheckpoint(env->isolate());
1077     return result;
1078   };
1079   if (break_on_sigint && timeout != -1) {
1080     Watchdog wd(env->isolate(), timeout, &timed_out);
1081     SigintWatchdog swd(env->isolate(), &received_signal);
1082     result = run();
1083   } else if (break_on_sigint) {
1084     SigintWatchdog swd(env->isolate(), &received_signal);
1085     result = run();
1086   } else if (timeout != -1) {
1087     Watchdog wd(env->isolate(), timeout, &timed_out);
1088     result = run();
1089   } else {
1090     result = run();
1091   }
1092 
1093   // Convert the termination exception into a regular exception.
1094   if (timed_out || received_signal) {
1095     if (!env->is_main_thread() && env->is_stopping())
1096       return false;
1097     env->isolate()->CancelTerminateExecution();
1098     // It is possible that execution was terminated by another timeout in
1099     // which this timeout is nested, so check whether one of the watchdogs
1100     // from this invocation is responsible for termination.
1101     if (timed_out) {
1102       node::THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(env, timeout);
1103     } else if (received_signal) {
1104       node::THROW_ERR_SCRIPT_EXECUTION_INTERRUPTED(env);
1105     }
1106   }
1107 
1108   if (try_catch.HasCaught()) {
1109     if (!timed_out && !received_signal && display_errors) {
1110       // We should decorate non-termination exceptions
1111       errors::DecorateErrorStack(env, try_catch);
1112     }
1113 
1114     // If there was an exception thrown during script execution, re-throw it.
1115     // If one of the above checks threw, re-throw the exception instead of
1116     // letting try_catch catch it.
1117     // If execution has been terminated, but not by one of the watchdogs from
1118     // this invocation, this will re-throw a `null` value.
1119     if (!try_catch.HasTerminated())
1120       try_catch.ReThrow();
1121 
1122     return false;
1123   }
1124 
1125   args.GetReturnValue().Set(result.ToLocalChecked());
1126   return true;
1127 }
1128 
1129 
ContextifyScript(Environment * env,Local<Object> object)1130 ContextifyScript::ContextifyScript(Environment* env, Local<Object> object)
1131     : BaseObject(env, object) {
1132   MakeWeak();
1133 }
1134 
~ContextifyScript()1135 ContextifyScript::~ContextifyScript() {}
1136 
CompileFunction(const FunctionCallbackInfo<Value> & args)1137 void ContextifyContext::CompileFunction(
1138     const FunctionCallbackInfo<Value>& args) {
1139   Environment* env = Environment::GetCurrent(args);
1140   Isolate* isolate = env->isolate();
1141   Local<Context> context = env->context();
1142 
1143   // Argument 1: source code
1144   CHECK(args[0]->IsString());
1145   Local<String> code = args[0].As<String>();
1146 
1147   // Argument 2: filename
1148   CHECK(args[1]->IsString());
1149   Local<String> filename = args[1].As<String>();
1150 
1151   // Argument 3: line offset
1152   CHECK(args[2]->IsNumber());
1153   int line_offset = args[2].As<Int32>()->Value();
1154 
1155   // Argument 4: column offset
1156   CHECK(args[3]->IsNumber());
1157   int column_offset = args[3].As<Int32>()->Value();
1158 
1159   // Argument 5: cached data (optional)
1160   Local<ArrayBufferView> cached_data_buf;
1161   if (!args[4]->IsUndefined()) {
1162     CHECK(args[4]->IsArrayBufferView());
1163     cached_data_buf = args[4].As<ArrayBufferView>();
1164   }
1165 
1166   // Argument 6: produce cache data
1167   CHECK(args[5]->IsBoolean());
1168   bool produce_cached_data = args[5]->IsTrue();
1169 
1170   // Argument 7: parsing context (optional)
1171   Local<Context> parsing_context;
1172   if (!args[6]->IsUndefined()) {
1173     CHECK(args[6]->IsObject());
1174     ContextifyContext* sandbox =
1175         ContextifyContext::ContextFromContextifiedSandbox(
1176             env, args[6].As<Object>());
1177     CHECK_NOT_NULL(sandbox);
1178     parsing_context = sandbox->context();
1179   } else {
1180     parsing_context = context;
1181   }
1182 
1183   // Argument 8: context extensions (optional)
1184   Local<Array> context_extensions_buf;
1185   if (!args[7]->IsUndefined()) {
1186     CHECK(args[7]->IsArray());
1187     context_extensions_buf = args[7].As<Array>();
1188   }
1189 
1190   // Argument 9: params for the function (optional)
1191   Local<Array> params_buf;
1192   if (!args[8]->IsUndefined()) {
1193     CHECK(args[8]->IsArray());
1194     params_buf = args[8].As<Array>();
1195   }
1196 
1197   // Argument 10: host-defined option symbol
1198   CHECK(args[9]->IsSymbol());
1199   Local<Symbol> id_symbol = args[9].As<Symbol>();
1200 
1201   // Read cache from cached data buffer
1202   ScriptCompiler::CachedData* cached_data = nullptr;
1203   if (!cached_data_buf.IsEmpty()) {
1204     uint8_t* data = static_cast<uint8_t*>(cached_data_buf->Buffer()->Data());
1205     cached_data = new ScriptCompiler::CachedData(
1206       data + cached_data_buf->ByteOffset(), cached_data_buf->ByteLength());
1207   }
1208 
1209   // Set host_defined_options
1210   Local<PrimitiveArray> host_defined_options =
1211       PrimitiveArray::New(isolate, loader::HostDefinedOptions::kLength);
1212   host_defined_options->Set(
1213       isolate, loader::HostDefinedOptions::kID, id_symbol);
1214 
1215   ScriptOrigin origin(isolate,
1216                       filename,
1217                       line_offset,       // line offset
1218                       column_offset,     // column offset
1219                       true,              // is cross origin
1220                       -1,                // script id
1221                       Local<Value>(),    // source map URL
1222                       false,             // is opaque (?)
1223                       false,             // is WASM
1224                       false,             // is ES Module
1225                       host_defined_options);
1226 
1227   ScriptCompiler::Source source(code, origin, cached_data);
1228   ScriptCompiler::CompileOptions options;
1229   if (source.GetCachedData() == nullptr) {
1230     options = ScriptCompiler::kNoCompileOptions;
1231   } else {
1232     options = ScriptCompiler::kConsumeCodeCache;
1233   }
1234 
1235   TryCatchScope try_catch(env);
1236   Context::Scope scope(parsing_context);
1237 
1238   // Read context extensions from buffer
1239   std::vector<Local<Object>> context_extensions;
1240   if (!context_extensions_buf.IsEmpty()) {
1241     for (uint32_t n = 0; n < context_extensions_buf->Length(); n++) {
1242       Local<Value> val;
1243       if (!context_extensions_buf->Get(context, n).ToLocal(&val)) return;
1244       CHECK(val->IsObject());
1245       context_extensions.push_back(val.As<Object>());
1246     }
1247   }
1248 
1249   // Read params from params buffer
1250   std::vector<Local<String>> params;
1251   if (!params_buf.IsEmpty()) {
1252     for (uint32_t n = 0; n < params_buf->Length(); n++) {
1253       Local<Value> val;
1254       if (!params_buf->Get(context, n).ToLocal(&val)) return;
1255       CHECK(val->IsString());
1256       params.push_back(val.As<String>());
1257     }
1258   }
1259 
1260   MaybeLocal<Function> maybe_fn = ScriptCompiler::CompileFunction(
1261       parsing_context,
1262       &source,
1263       params.size(),
1264       params.data(),
1265       context_extensions.size(),
1266       context_extensions.data(),
1267       options,
1268       v8::ScriptCompiler::NoCacheReason::kNoCacheNoReason);
1269 
1270   Local<Function> fn;
1271   if (!maybe_fn.ToLocal(&fn)) {
1272     if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
1273       errors::DecorateErrorStack(env, try_catch);
1274       try_catch.ReThrow();
1275     }
1276     return;
1277   }
1278   if (fn->SetPrivate(context, env->host_defined_option_symbol(), id_symbol)
1279           .IsNothing()) {
1280     return;
1281   }
1282 
1283   Local<Object> result = Object::New(isolate);
1284   if (result->Set(parsing_context, env->function_string(), fn).IsNothing())
1285     return;
1286   if (result
1287           ->Set(parsing_context,
1288                 env->source_map_url_string(),
1289                 fn->GetScriptOrigin().SourceMapUrl())
1290           .IsNothing())
1291     return;
1292 
1293   std::unique_ptr<ScriptCompiler::CachedData> new_cached_data;
1294   if (produce_cached_data) {
1295     new_cached_data.reset(ScriptCompiler::CreateCodeCacheForFunction(fn));
1296   }
1297   if (StoreCodeCacheResult(env,
1298                            result,
1299                            options,
1300                            source,
1301                            produce_cached_data,
1302                            std::move(new_cached_data))
1303           .IsNothing()) {
1304     return;
1305   }
1306 
1307   args.GetReturnValue().Set(result);
1308 }
1309 
StartSigintWatchdog(const FunctionCallbackInfo<Value> & args)1310 static void StartSigintWatchdog(const FunctionCallbackInfo<Value>& args) {
1311   int ret = SigintWatchdogHelper::GetInstance()->Start();
1312   args.GetReturnValue().Set(ret == 0);
1313 }
1314 
StopSigintWatchdog(const FunctionCallbackInfo<Value> & args)1315 static void StopSigintWatchdog(const FunctionCallbackInfo<Value>& args) {
1316   bool had_pending_signals = SigintWatchdogHelper::GetInstance()->Stop();
1317   args.GetReturnValue().Set(had_pending_signals);
1318 }
1319 
WatchdogHasPendingSigint(const FunctionCallbackInfo<Value> & args)1320 static void WatchdogHasPendingSigint(const FunctionCallbackInfo<Value>& args) {
1321   bool ret = SigintWatchdogHelper::GetInstance()->HasPendingSignal();
1322   args.GetReturnValue().Set(ret);
1323 }
1324 
MeasureMemory(const FunctionCallbackInfo<Value> & args)1325 static void MeasureMemory(const FunctionCallbackInfo<Value>& args) {
1326   CHECK(args[0]->IsInt32());
1327   CHECK(args[1]->IsInt32());
1328   int32_t mode = args[0].As<v8::Int32>()->Value();
1329   int32_t execution = args[1].As<v8::Int32>()->Value();
1330   Isolate* isolate = args.GetIsolate();
1331 
1332   Local<Context> current_context = isolate->GetCurrentContext();
1333   Local<Promise::Resolver> resolver;
1334   if (!Promise::Resolver::New(current_context).ToLocal(&resolver)) return;
1335   std::unique_ptr<v8::MeasureMemoryDelegate> delegate =
1336       v8::MeasureMemoryDelegate::Default(
1337           isolate,
1338           current_context,
1339           resolver,
1340           static_cast<v8::MeasureMemoryMode>(mode));
1341   isolate->MeasureMemory(std::move(delegate),
1342                          static_cast<v8::MeasureMemoryExecution>(execution));
1343   Local<Promise> promise = resolver->GetPromise();
1344 
1345   args.GetReturnValue().Set(promise);
1346 }
1347 
MicrotaskQueueWrap(Environment * env,Local<Object> obj)1348 MicrotaskQueueWrap::MicrotaskQueueWrap(Environment* env, Local<Object> obj)
1349   : BaseObject(env, obj),
1350     microtask_queue_(
1351         MicrotaskQueue::New(env->isolate(), MicrotasksPolicy::kExplicit)) {
1352   MakeWeak();
1353 }
1354 
1355 const std::shared_ptr<MicrotaskQueue>&
microtask_queue() const1356 MicrotaskQueueWrap::microtask_queue() const {
1357   return microtask_queue_;
1358 }
1359 
New(const FunctionCallbackInfo<Value> & args)1360 void MicrotaskQueueWrap::New(const FunctionCallbackInfo<Value>& args) {
1361   CHECK(args.IsConstructCall());
1362   new MicrotaskQueueWrap(Environment::GetCurrent(args), args.This());
1363 }
1364 
Init(Environment * env,Local<Object> target)1365 void MicrotaskQueueWrap::Init(Environment* env, Local<Object> target) {
1366   Isolate* isolate = env->isolate();
1367   HandleScope scope(isolate);
1368   Local<Context> context = env->context();
1369   Local<FunctionTemplate> tmpl = NewFunctionTemplate(isolate, New);
1370   tmpl->InstanceTemplate()->SetInternalFieldCount(
1371       ContextifyScript::kInternalFieldCount);
1372   env->set_microtask_queue_ctor_template(tmpl);
1373   SetConstructorFunction(context, target, "MicrotaskQueue", tmpl);
1374 }
1375 
RegisterExternalReferences(ExternalReferenceRegistry * registry)1376 void MicrotaskQueueWrap::RegisterExternalReferences(
1377     ExternalReferenceRegistry* registry) {
1378   registry->Register(New);
1379 }
1380 
Initialize(Local<Object> target,Local<Value> unused,Local<Context> context,void * priv)1381 void Initialize(Local<Object> target,
1382                 Local<Value> unused,
1383                 Local<Context> context,
1384                 void* priv) {
1385   Environment* env = Environment::GetCurrent(context);
1386   Isolate* isolate = env->isolate();
1387   ContextifyContext::Init(env, target);
1388   ContextifyScript::Init(env, target);
1389   MicrotaskQueueWrap::Init(env, target);
1390 
1391   SetMethod(context, target, "startSigintWatchdog", StartSigintWatchdog);
1392   SetMethod(context, target, "stopSigintWatchdog", StopSigintWatchdog);
1393   // Used in tests.
1394   SetMethodNoSideEffect(
1395       context, target, "watchdogHasPendingSigint", WatchdogHasPendingSigint);
1396 
1397   Local<Object> constants = Object::New(env->isolate());
1398   Local<Object> measure_memory = Object::New(env->isolate());
1399   Local<Object> memory_execution = Object::New(env->isolate());
1400 
1401   {
1402     Local<Object> memory_mode = Object::New(env->isolate());
1403     MeasureMemoryMode SUMMARY = MeasureMemoryMode::kSummary;
1404     MeasureMemoryMode DETAILED = MeasureMemoryMode::kDetailed;
1405     NODE_DEFINE_CONSTANT(memory_mode, SUMMARY);
1406     NODE_DEFINE_CONSTANT(memory_mode, DETAILED);
1407     READONLY_PROPERTY(measure_memory, "mode", memory_mode);
1408   }
1409 
1410   {
1411     MeasureMemoryExecution DEFAULT = MeasureMemoryExecution::kDefault;
1412     MeasureMemoryExecution EAGER = MeasureMemoryExecution::kEager;
1413     NODE_DEFINE_CONSTANT(memory_execution, DEFAULT);
1414     NODE_DEFINE_CONSTANT(memory_execution, EAGER);
1415     READONLY_PROPERTY(measure_memory, "execution", memory_execution);
1416   }
1417 
1418   READONLY_PROPERTY(constants, "measureMemory", measure_memory);
1419 
1420   target->Set(context, env->constants_string(), constants).Check();
1421 
1422   SetMethod(context, target, "measureMemory", MeasureMemory);
1423 }
1424 
RegisterExternalReferences(ExternalReferenceRegistry * registry)1425 void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
1426   ContextifyContext::RegisterExternalReferences(registry);
1427   ContextifyScript::RegisterExternalReferences(registry);
1428   MicrotaskQueueWrap::RegisterExternalReferences(registry);
1429 
1430   registry->Register(StartSigintWatchdog);
1431   registry->Register(StopSigintWatchdog);
1432   registry->Register(WatchdogHasPendingSigint);
1433   registry->Register(MeasureMemory);
1434 }
1435 }  // namespace contextify
1436 }  // namespace node
1437 
1438 NODE_BINDING_CONTEXT_AWARE_INTERNAL(contextify, node::contextify::Initialize)
1439 NODE_BINDING_EXTERNAL_REFERENCE(contextify,
1440                                 node::contextify::RegisterExternalReferences)
1441