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