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