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