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 std::unique_ptr<ScriptCompiler::CachedData> new_cached_data;
883 if (produce_cached_data) {
884 new_cached_data.reset(ScriptCompiler::CreateCodeCache(v8_script));
885 }
886
887 if (contextify_script->object()
888 ->SetPrivate(context, env->host_defined_option_symbol(), id_symbol)
889 .IsNothing()) {
890 return;
891 }
892
893 if (StoreCodeCacheResult(env,
894 args.This(),
895 compile_options,
896 source,
897 produce_cached_data,
898 std::move(new_cached_data))
899 .IsNothing()) {
900 return;
901 }
902
903 if (args.This()
904 ->Set(env->context(),
905 env->source_map_url_string(),
906 v8_script->GetSourceMappingURL())
907 .IsNothing())
908 return;
909
910 TRACE_EVENT_END0(TRACING_CATEGORY_NODE2(vm, script), "ContextifyScript::New");
911 }
912
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)913 Maybe<bool> StoreCodeCacheResult(
914 Environment* env,
915 Local<Object> target,
916 ScriptCompiler::CompileOptions compile_options,
917 const v8::ScriptCompiler::Source& source,
918 bool produce_cached_data,
919 std::unique_ptr<ScriptCompiler::CachedData> new_cached_data) {
920 Local<Context> context;
921 if (!target->GetCreationContext().ToLocal(&context)) {
922 return Nothing<bool>();
923 }
924 if (compile_options == ScriptCompiler::kConsumeCodeCache) {
925 if (target
926 ->Set(
927 context,
928 env->cached_data_rejected_string(),
929 Boolean::New(env->isolate(), source.GetCachedData()->rejected))
930 .IsNothing()) {
931 return Nothing<bool>();
932 }
933 }
934 if (produce_cached_data) {
935 bool cached_data_produced = new_cached_data != nullptr;
936 if (cached_data_produced) {
937 MaybeLocal<Object> buf =
938 Buffer::Copy(env,
939 reinterpret_cast<const char*>(new_cached_data->data),
940 new_cached_data->length);
941 if (target->Set(context, env->cached_data_string(), buf.ToLocalChecked())
942 .IsNothing()) {
943 return Nothing<bool>();
944 }
945 }
946 if (target
947 ->Set(context,
948 env->cached_data_produced_string(),
949 Boolean::New(env->isolate(), cached_data_produced))
950 .IsNothing()) {
951 return Nothing<bool>();
952 }
953 }
954 return Just(true);
955 }
956
InstanceOf(Environment * env,const Local<Value> & value)957 bool ContextifyScript::InstanceOf(Environment* env,
958 const Local<Value>& value) {
959 return !value.IsEmpty() &&
960 env->script_context_constructor_template()->HasInstance(value);
961 }
962
CreateCachedData(const FunctionCallbackInfo<Value> & args)963 void ContextifyScript::CreateCachedData(
964 const FunctionCallbackInfo<Value>& args) {
965 Environment* env = Environment::GetCurrent(args);
966 ContextifyScript* wrapped_script;
967 ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder());
968 Local<UnboundScript> unbound_script =
969 PersistentToLocal::Default(env->isolate(), wrapped_script->script_);
970 std::unique_ptr<ScriptCompiler::CachedData> cached_data(
971 ScriptCompiler::CreateCodeCache(unbound_script));
972 if (!cached_data) {
973 args.GetReturnValue().Set(Buffer::New(env, 0).ToLocalChecked());
974 } else {
975 MaybeLocal<Object> buf = Buffer::Copy(
976 env,
977 reinterpret_cast<const char*>(cached_data->data),
978 cached_data->length);
979 args.GetReturnValue().Set(buf.ToLocalChecked());
980 }
981 }
982
RunInContext(const FunctionCallbackInfo<Value> & args)983 void ContextifyScript::RunInContext(const FunctionCallbackInfo<Value>& args) {
984 Environment* env = Environment::GetCurrent(args);
985
986 ContextifyScript* wrapped_script;
987 ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder());
988
989 CHECK_EQ(args.Length(), 5);
990 CHECK(args[0]->IsObject() || args[0]->IsNull());
991
992 Local<Context> context;
993 std::shared_ptr<MicrotaskQueue> microtask_queue;
994
995 if (args[0]->IsObject()) {
996 Local<Object> sandbox = args[0].As<Object>();
997 // Get the context from the sandbox
998 ContextifyContext* contextify_context =
999 ContextifyContext::ContextFromContextifiedSandbox(env, sandbox);
1000 CHECK_NOT_NULL(contextify_context);
1001 CHECK_EQ(contextify_context->env(), env);
1002
1003 context = contextify_context->context();
1004 if (context.IsEmpty()) return;
1005
1006 microtask_queue = contextify_context->microtask_queue();
1007 } else {
1008 context = env->context();
1009 }
1010
1011 TRACE_EVENT0(TRACING_CATEGORY_NODE2(vm, script), "RunInContext");
1012
1013 CHECK(args[1]->IsNumber());
1014 int64_t timeout = args[1]->IntegerValue(env->context()).FromJust();
1015
1016 CHECK(args[2]->IsBoolean());
1017 bool display_errors = args[2]->IsTrue();
1018
1019 CHECK(args[3]->IsBoolean());
1020 bool break_on_sigint = args[3]->IsTrue();
1021
1022 CHECK(args[4]->IsBoolean());
1023 bool break_on_first_line = args[4]->IsTrue();
1024
1025 // Do the eval within the context
1026 EvalMachine(context,
1027 env,
1028 timeout,
1029 display_errors,
1030 break_on_sigint,
1031 break_on_first_line,
1032 microtask_queue,
1033 args);
1034 }
1035
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)1036 bool ContextifyScript::EvalMachine(Local<Context> context,
1037 Environment* env,
1038 const int64_t timeout,
1039 const bool display_errors,
1040 const bool break_on_sigint,
1041 const bool break_on_first_line,
1042 std::shared_ptr<MicrotaskQueue> mtask_queue,
1043 const FunctionCallbackInfo<Value>& args) {
1044 Context::Scope context_scope(context);
1045
1046 if (!env->can_call_into_js())
1047 return false;
1048 if (!ContextifyScript::InstanceOf(env, args.Holder())) {
1049 THROW_ERR_INVALID_THIS(
1050 env,
1051 "Script methods can only be called on script instances.");
1052 return false;
1053 }
1054
1055 TryCatchScope try_catch(env);
1056 Isolate::SafeForTerminationScope safe_for_termination(env->isolate());
1057 ContextifyScript* wrapped_script;
1058 ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder(), false);
1059 Local<UnboundScript> unbound_script =
1060 PersistentToLocal::Default(env->isolate(), wrapped_script->script_);
1061 Local<Script> script = unbound_script->BindToCurrentContext();
1062
1063 #if HAVE_INSPECTOR
1064 if (break_on_first_line) {
1065 env->inspector_agent()->PauseOnNextJavascriptStatement("Break on start");
1066 }
1067 #endif
1068
1069 MaybeLocal<Value> result;
1070 bool timed_out = false;
1071 bool received_signal = false;
1072 auto run = [&]() {
1073 MaybeLocal<Value> result = script->Run(context);
1074 if (!result.IsEmpty() && mtask_queue)
1075 mtask_queue->PerformCheckpoint(env->isolate());
1076 return result;
1077 };
1078 if (break_on_sigint && timeout != -1) {
1079 Watchdog wd(env->isolate(), timeout, &timed_out);
1080 SigintWatchdog swd(env->isolate(), &received_signal);
1081 result = run();
1082 } else if (break_on_sigint) {
1083 SigintWatchdog swd(env->isolate(), &received_signal);
1084 result = run();
1085 } else if (timeout != -1) {
1086 Watchdog wd(env->isolate(), timeout, &timed_out);
1087 result = run();
1088 } else {
1089 result = run();
1090 }
1091
1092 // Convert the termination exception into a regular exception.
1093 if (timed_out || received_signal) {
1094 if (!env->is_main_thread() && env->is_stopping())
1095 return false;
1096 env->isolate()->CancelTerminateExecution();
1097 // It is possible that execution was terminated by another timeout in
1098 // which this timeout is nested, so check whether one of the watchdogs
1099 // from this invocation is responsible for termination.
1100 if (timed_out) {
1101 node::THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(env, timeout);
1102 } else if (received_signal) {
1103 node::THROW_ERR_SCRIPT_EXECUTION_INTERRUPTED(env);
1104 }
1105 }
1106
1107 if (try_catch.HasCaught()) {
1108 if (!timed_out && !received_signal && display_errors) {
1109 // We should decorate non-termination exceptions
1110 errors::DecorateErrorStack(env, try_catch);
1111 }
1112
1113 // If there was an exception thrown during script execution, re-throw it.
1114 // If one of the above checks threw, re-throw the exception instead of
1115 // letting try_catch catch it.
1116 // If execution has been terminated, but not by one of the watchdogs from
1117 // this invocation, this will re-throw a `null` value.
1118 if (!try_catch.HasTerminated())
1119 try_catch.ReThrow();
1120
1121 return false;
1122 }
1123
1124 args.GetReturnValue().Set(result.ToLocalChecked());
1125 return true;
1126 }
1127
1128
ContextifyScript(Environment * env,Local<Object> object)1129 ContextifyScript::ContextifyScript(Environment* env, Local<Object> object)
1130 : BaseObject(env, object) {
1131 MakeWeak();
1132 }
1133
~ContextifyScript()1134 ContextifyScript::~ContextifyScript() {}
1135
CompileFunction(const FunctionCallbackInfo<Value> & args)1136 void ContextifyContext::CompileFunction(
1137 const FunctionCallbackInfo<Value>& args) {
1138 Environment* env = Environment::GetCurrent(args);
1139 Isolate* isolate = env->isolate();
1140 Local<Context> context = env->context();
1141
1142 // Argument 1: source code
1143 CHECK(args[0]->IsString());
1144 Local<String> code = args[0].As<String>();
1145
1146 // Argument 2: filename
1147 CHECK(args[1]->IsString());
1148 Local<String> filename = args[1].As<String>();
1149
1150 // Argument 3: line offset
1151 CHECK(args[2]->IsNumber());
1152 int line_offset = args[2].As<Int32>()->Value();
1153
1154 // Argument 4: column offset
1155 CHECK(args[3]->IsNumber());
1156 int column_offset = args[3].As<Int32>()->Value();
1157
1158 // Argument 5: cached data (optional)
1159 Local<ArrayBufferView> cached_data_buf;
1160 if (!args[4]->IsUndefined()) {
1161 CHECK(args[4]->IsArrayBufferView());
1162 cached_data_buf = args[4].As<ArrayBufferView>();
1163 }
1164
1165 // Argument 6: produce cache data
1166 CHECK(args[5]->IsBoolean());
1167 bool produce_cached_data = args[5]->IsTrue();
1168
1169 // Argument 7: parsing context (optional)
1170 Local<Context> parsing_context;
1171 if (!args[6]->IsUndefined()) {
1172 CHECK(args[6]->IsObject());
1173 ContextifyContext* sandbox =
1174 ContextifyContext::ContextFromContextifiedSandbox(
1175 env, args[6].As<Object>());
1176 CHECK_NOT_NULL(sandbox);
1177 parsing_context = sandbox->context();
1178 } else {
1179 parsing_context = context;
1180 }
1181
1182 // Argument 8: context extensions (optional)
1183 Local<Array> context_extensions_buf;
1184 if (!args[7]->IsUndefined()) {
1185 CHECK(args[7]->IsArray());
1186 context_extensions_buf = args[7].As<Array>();
1187 }
1188
1189 // Argument 9: params for the function (optional)
1190 Local<Array> params_buf;
1191 if (!args[8]->IsUndefined()) {
1192 CHECK(args[8]->IsArray());
1193 params_buf = args[8].As<Array>();
1194 }
1195
1196 // Argument 10: host-defined option symbol
1197 CHECK(args[9]->IsSymbol());
1198 Local<Symbol> id_symbol = args[9].As<Symbol>();
1199
1200 // Read cache from cached data buffer
1201 ScriptCompiler::CachedData* cached_data = nullptr;
1202 if (!cached_data_buf.IsEmpty()) {
1203 uint8_t* data = static_cast<uint8_t*>(cached_data_buf->Buffer()->Data());
1204 cached_data = new ScriptCompiler::CachedData(
1205 data + cached_data_buf->ByteOffset(), cached_data_buf->ByteLength());
1206 }
1207
1208 // Set host_defined_options
1209 Local<PrimitiveArray> host_defined_options =
1210 PrimitiveArray::New(isolate, loader::HostDefinedOptions::kLength);
1211 host_defined_options->Set(
1212 isolate, loader::HostDefinedOptions::kID, id_symbol);
1213
1214 ScriptOrigin origin(isolate,
1215 filename,
1216 line_offset, // line offset
1217 column_offset, // column offset
1218 true, // is cross origin
1219 -1, // script id
1220 Local<Value>(), // source map URL
1221 false, // is opaque (?)
1222 false, // is WASM
1223 false, // is ES Module
1224 host_defined_options);
1225
1226 ScriptCompiler::Source source(code, origin, cached_data);
1227 ScriptCompiler::CompileOptions options;
1228 if (source.GetCachedData() == nullptr) {
1229 options = ScriptCompiler::kNoCompileOptions;
1230 } else {
1231 options = ScriptCompiler::kConsumeCodeCache;
1232 }
1233
1234 TryCatchScope try_catch(env);
1235 Context::Scope scope(parsing_context);
1236
1237 // Read context extensions from buffer
1238 std::vector<Local<Object>> context_extensions;
1239 if (!context_extensions_buf.IsEmpty()) {
1240 for (uint32_t n = 0; n < context_extensions_buf->Length(); n++) {
1241 Local<Value> val;
1242 if (!context_extensions_buf->Get(context, n).ToLocal(&val)) return;
1243 CHECK(val->IsObject());
1244 context_extensions.push_back(val.As<Object>());
1245 }
1246 }
1247
1248 // Read params from params buffer
1249 std::vector<Local<String>> params;
1250 if (!params_buf.IsEmpty()) {
1251 for (uint32_t n = 0; n < params_buf->Length(); n++) {
1252 Local<Value> val;
1253 if (!params_buf->Get(context, n).ToLocal(&val)) return;
1254 CHECK(val->IsString());
1255 params.push_back(val.As<String>());
1256 }
1257 }
1258
1259 MaybeLocal<Function> maybe_fn = ScriptCompiler::CompileFunction(
1260 parsing_context,
1261 &source,
1262 params.size(),
1263 params.data(),
1264 context_extensions.size(),
1265 context_extensions.data(),
1266 options,
1267 v8::ScriptCompiler::NoCacheReason::kNoCacheNoReason);
1268
1269 Local<Function> fn;
1270 if (!maybe_fn.ToLocal(&fn)) {
1271 if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
1272 errors::DecorateErrorStack(env, try_catch);
1273 try_catch.ReThrow();
1274 }
1275 return;
1276 }
1277 if (fn->SetPrivate(context, env->host_defined_option_symbol(), id_symbol)
1278 .IsNothing()) {
1279 return;
1280 }
1281
1282 Local<Object> result = Object::New(isolate);
1283 if (result->Set(parsing_context, env->function_string(), fn).IsNothing())
1284 return;
1285 if (result
1286 ->Set(parsing_context,
1287 env->source_map_url_string(),
1288 fn->GetScriptOrigin().SourceMapUrl())
1289 .IsNothing())
1290 return;
1291
1292 std::unique_ptr<ScriptCompiler::CachedData> new_cached_data;
1293 if (produce_cached_data) {
1294 new_cached_data.reset(ScriptCompiler::CreateCodeCacheForFunction(fn));
1295 }
1296 if (StoreCodeCacheResult(env,
1297 result,
1298 options,
1299 source,
1300 produce_cached_data,
1301 std::move(new_cached_data))
1302 .IsNothing()) {
1303 return;
1304 }
1305
1306 args.GetReturnValue().Set(result);
1307 }
1308
StartSigintWatchdog(const FunctionCallbackInfo<Value> & args)1309 static void StartSigintWatchdog(const FunctionCallbackInfo<Value>& args) {
1310 int ret = SigintWatchdogHelper::GetInstance()->Start();
1311 args.GetReturnValue().Set(ret == 0);
1312 }
1313
StopSigintWatchdog(const FunctionCallbackInfo<Value> & args)1314 static void StopSigintWatchdog(const FunctionCallbackInfo<Value>& args) {
1315 bool had_pending_signals = SigintWatchdogHelper::GetInstance()->Stop();
1316 args.GetReturnValue().Set(had_pending_signals);
1317 }
1318
WatchdogHasPendingSigint(const FunctionCallbackInfo<Value> & args)1319 static void WatchdogHasPendingSigint(const FunctionCallbackInfo<Value>& args) {
1320 bool ret = SigintWatchdogHelper::GetInstance()->HasPendingSignal();
1321 args.GetReturnValue().Set(ret);
1322 }
1323
MeasureMemory(const FunctionCallbackInfo<Value> & args)1324 static void MeasureMemory(const FunctionCallbackInfo<Value>& args) {
1325 CHECK(args[0]->IsInt32());
1326 CHECK(args[1]->IsInt32());
1327 int32_t mode = args[0].As<v8::Int32>()->Value();
1328 int32_t execution = args[1].As<v8::Int32>()->Value();
1329 Isolate* isolate = args.GetIsolate();
1330
1331 Local<Context> current_context = isolate->GetCurrentContext();
1332 Local<Promise::Resolver> resolver;
1333 if (!Promise::Resolver::New(current_context).ToLocal(&resolver)) return;
1334 std::unique_ptr<v8::MeasureMemoryDelegate> delegate =
1335 v8::MeasureMemoryDelegate::Default(
1336 isolate,
1337 current_context,
1338 resolver,
1339 static_cast<v8::MeasureMemoryMode>(mode));
1340 isolate->MeasureMemory(std::move(delegate),
1341 static_cast<v8::MeasureMemoryExecution>(execution));
1342 Local<Promise> promise = resolver->GetPromise();
1343
1344 args.GetReturnValue().Set(promise);
1345 }
1346
MicrotaskQueueWrap(Environment * env,Local<Object> obj)1347 MicrotaskQueueWrap::MicrotaskQueueWrap(Environment* env, Local<Object> obj)
1348 : BaseObject(env, obj),
1349 microtask_queue_(
1350 MicrotaskQueue::New(env->isolate(), MicrotasksPolicy::kExplicit)) {
1351 MakeWeak();
1352 }
1353
1354 const std::shared_ptr<MicrotaskQueue>&
microtask_queue() const1355 MicrotaskQueueWrap::microtask_queue() const {
1356 return microtask_queue_;
1357 }
1358
New(const FunctionCallbackInfo<Value> & args)1359 void MicrotaskQueueWrap::New(const FunctionCallbackInfo<Value>& args) {
1360 CHECK(args.IsConstructCall());
1361 new MicrotaskQueueWrap(Environment::GetCurrent(args), args.This());
1362 }
1363
Init(Environment * env,Local<Object> target)1364 void MicrotaskQueueWrap::Init(Environment* env, Local<Object> target) {
1365 Isolate* isolate = env->isolate();
1366 HandleScope scope(isolate);
1367 Local<Context> context = env->context();
1368 Local<FunctionTemplate> tmpl = NewFunctionTemplate(isolate, New);
1369 tmpl->InstanceTemplate()->SetInternalFieldCount(
1370 ContextifyScript::kInternalFieldCount);
1371 env->set_microtask_queue_ctor_template(tmpl);
1372 SetConstructorFunction(context, target, "MicrotaskQueue", tmpl);
1373 }
1374
RegisterExternalReferences(ExternalReferenceRegistry * registry)1375 void MicrotaskQueueWrap::RegisterExternalReferences(
1376 ExternalReferenceRegistry* registry) {
1377 registry->Register(New);
1378 }
1379
Initialize(Local<Object> target,Local<Value> unused,Local<Context> context,void * priv)1380 void Initialize(Local<Object> target,
1381 Local<Value> unused,
1382 Local<Context> context,
1383 void* priv) {
1384 Environment* env = Environment::GetCurrent(context);
1385 Isolate* isolate = env->isolate();
1386 ContextifyContext::Init(env, target);
1387 ContextifyScript::Init(env, target);
1388 MicrotaskQueueWrap::Init(env, target);
1389
1390 SetMethod(context, target, "startSigintWatchdog", StartSigintWatchdog);
1391 SetMethod(context, target, "stopSigintWatchdog", StopSigintWatchdog);
1392 // Used in tests.
1393 SetMethodNoSideEffect(
1394 context, target, "watchdogHasPendingSigint", WatchdogHasPendingSigint);
1395
1396 Local<Object> constants = Object::New(env->isolate());
1397 Local<Object> measure_memory = Object::New(env->isolate());
1398 Local<Object> memory_execution = Object::New(env->isolate());
1399
1400 {
1401 Local<Object> memory_mode = Object::New(env->isolate());
1402 MeasureMemoryMode SUMMARY = MeasureMemoryMode::kSummary;
1403 MeasureMemoryMode DETAILED = MeasureMemoryMode::kDetailed;
1404 NODE_DEFINE_CONSTANT(memory_mode, SUMMARY);
1405 NODE_DEFINE_CONSTANT(memory_mode, DETAILED);
1406 READONLY_PROPERTY(measure_memory, "mode", memory_mode);
1407 }
1408
1409 {
1410 MeasureMemoryExecution DEFAULT = MeasureMemoryExecution::kDefault;
1411 MeasureMemoryExecution EAGER = MeasureMemoryExecution::kEager;
1412 NODE_DEFINE_CONSTANT(memory_execution, DEFAULT);
1413 NODE_DEFINE_CONSTANT(memory_execution, EAGER);
1414 READONLY_PROPERTY(measure_memory, "execution", memory_execution);
1415 }
1416
1417 READONLY_PROPERTY(constants, "measureMemory", measure_memory);
1418
1419 target->Set(context, env->constants_string(), constants).Check();
1420
1421 SetMethod(context, target, "measureMemory", MeasureMemory);
1422 }
1423
RegisterExternalReferences(ExternalReferenceRegistry * registry)1424 void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
1425 ContextifyContext::RegisterExternalReferences(registry);
1426 ContextifyScript::RegisterExternalReferences(registry);
1427 MicrotaskQueueWrap::RegisterExternalReferences(registry);
1428
1429 registry->Register(StartSigintWatchdog);
1430 registry->Register(StopSigintWatchdog);
1431 registry->Register(WatchdogHasPendingSigint);
1432 registry->Register(MeasureMemory);
1433 }
1434 } // namespace contextify
1435 } // namespace node
1436
1437 NODE_BINDING_CONTEXT_AWARE_INTERNAL(contextify, node::contextify::Initialize)
1438 NODE_BINDING_EXTERNAL_REFERENCE(contextify,
1439 node::contextify::RegisterExternalReferences)
1440