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