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