src: deduplicate CHECK_EQ/CHECK_NE macros
[platform/upstream/nodejs.git] / src / node_contextify.cc
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.h"
23 #include "node_internals.h"
24 #include "node_watchdog.h"
25 #include "base-object.h"
26 #include "base-object-inl.h"
27 #include "env.h"
28 #include "env-inl.h"
29 #include "util.h"
30 #include "util-inl.h"
31
32 namespace node {
33
34 using v8::AccessType;
35 using v8::Array;
36 using v8::Boolean;
37 using v8::Context;
38 using v8::EscapableHandleScope;
39 using v8::External;
40 using v8::Function;
41 using v8::FunctionCallbackInfo;
42 using v8::FunctionTemplate;
43 using v8::Handle;
44 using v8::HandleScope;
45 using v8::Integer;
46 using v8::Isolate;
47 using v8::Local;
48 using v8::None;
49 using v8::Object;
50 using v8::ObjectTemplate;
51 using v8::Persistent;
52 using v8::PropertyCallbackInfo;
53 using v8::Script;
54 using v8::String;
55 using v8::TryCatch;
56 using v8::V8;
57 using v8::Value;
58 using v8::WeakCallbackData;
59
60
61 class ContextifyContext {
62  protected:
63   enum Kind {
64     kSandbox,
65     kContext,
66     kProxyGlobal
67   };
68
69   Environment* const env_;
70   Persistent<Object> sandbox_;
71   Persistent<Context> context_;
72   Persistent<Object> proxy_global_;
73   int references_;
74
75  public:
76   explicit ContextifyContext(Environment* env, Local<Object> sandbox)
77       : env_(env),
78         sandbox_(env->isolate(), sandbox),
79         context_(env->isolate(), CreateV8Context(env)),
80         // Wait for sandbox_, proxy_global_, and context_ to die
81         references_(0) {
82     sandbox_.SetWeak(this, WeakCallback<Object, kSandbox>);
83     sandbox_.MarkIndependent();
84     references_++;
85
86     // Allocation failure or maximum call stack size reached
87     if (context_.IsEmpty())
88       return;
89     context_.SetWeak(this, WeakCallback<Context, kContext>);
90     context_.MarkIndependent();
91     references_++;
92
93     proxy_global_.Reset(env->isolate(), context()->Global());
94     proxy_global_.SetWeak(this, WeakCallback<Object, kProxyGlobal>);
95     proxy_global_.MarkIndependent();
96     references_++;
97   }
98
99
100   ~ContextifyContext() {
101     context_.Reset();
102     proxy_global_.Reset();
103     sandbox_.Reset();
104   }
105
106
107   inline Environment* env() const {
108     return env_;
109   }
110
111
112   inline Local<Context> context() const {
113     return PersistentToLocal(env()->isolate(), context_);
114   }
115
116
117   // XXX(isaacs): This function only exists because of a shortcoming of
118   // the V8 SetNamedPropertyHandler function.
119   //
120   // It does not provide a way to intercept Object.defineProperty(..)
121   // calls.  As a result, these properties are not copied onto the
122   // contextified sandbox when a new global property is added via either
123   // a function declaration or a Object.defineProperty(global, ...) call.
124   //
125   // Note that any function declarations or Object.defineProperty()
126   // globals that are created asynchronously (in a setTimeout, callback,
127   // etc.) will happen AFTER the call to copy properties, and thus not be
128   // caught.
129   //
130   // The way to properly fix this is to add some sort of a
131   // Object::SetNamedDefinePropertyHandler() function that takes a callback,
132   // which receives the property name and property descriptor as arguments.
133   //
134   // Luckily, such situations are rare, and asynchronously-added globals
135   // weren't supported by Node's VM module until 0.12 anyway.  But, this
136   // should be fixed properly in V8, and this copy function should be
137   // removed once there is a better way.
138   void CopyProperties() {
139     HandleScope scope(env()->isolate());
140
141     Local<Context> context = PersistentToLocal(env()->isolate(), context_);
142     Local<Object> global = context->Global()->GetPrototype()->ToObject();
143     Local<Object> sandbox = PersistentToLocal(env()->isolate(), sandbox_);
144
145     Local<Function> clone_property_method;
146
147     Local<Array> names = global->GetOwnPropertyNames();
148     int length = names->Length();
149     for (int i = 0; i < length; i++) {
150       Local<String> key = names->Get(i)->ToString();
151       bool has = sandbox->HasOwnProperty(key);
152       if (!has) {
153         // Could also do this like so:
154         //
155         // PropertyAttribute att = global->GetPropertyAttributes(key_v);
156         // Local<Value> val = global->Get(key_v);
157         // sandbox->ForceSet(key_v, val, att);
158         //
159         // However, this doesn't handle ES6-style properties configured with
160         // Object.defineProperty, and that's exactly what we're up against at
161         // this point.  ForceSet(key,val,att) only supports value properties
162         // with the ES3-style attribute flags (DontDelete/DontEnum/ReadOnly),
163         // which doesn't faithfully capture the full range of configurations
164         // that can be done using Object.defineProperty.
165         if (clone_property_method.IsEmpty()) {
166           Local<String> code = FIXED_ONE_BYTE_STRING(env()->isolate(),
167               "(function cloneProperty(source, key, target) {\n"
168               "  if (key === 'Proxy') return;\n"
169               "  try {\n"
170               "    var desc = Object.getOwnPropertyDescriptor(source, key);\n"
171               "    if (desc.value === source) desc.value = target;\n"
172               "    Object.defineProperty(target, key, desc);\n"
173               "  } catch (e) {\n"
174               "   // Catch sealed properties errors\n"
175               "  }\n"
176               "})");
177
178           Local<String> fname = FIXED_ONE_BYTE_STRING(env()->isolate(),
179               "binding:script");
180           Local<Script> script = Script::Compile(code, fname);
181           clone_property_method = Local<Function>::Cast(script->Run());
182           assert(clone_property_method->IsFunction());
183         }
184         Local<Value> args[] = { global, key, sandbox };
185         clone_property_method->Call(global, ARRAY_SIZE(args), args);
186       }
187     }
188   }
189
190
191   // This is an object that just keeps an internal pointer to this
192   // ContextifyContext.  It's passed to the NamedPropertyHandler.  If we
193   // pass the main JavaScript context object we're embedded in, then the
194   // NamedPropertyHandler will store a reference to it forever and keep it
195   // from getting gc'd.
196   Local<Value> CreateDataWrapper(Environment* env) {
197     EscapableHandleScope scope(env->isolate());
198     Local<Object> wrapper =
199         env->script_data_constructor_function()->NewInstance();
200     if (wrapper.IsEmpty())
201       return scope.Escape(Local<Value>::New(env->isolate(), Handle<Value>()));
202
203     Wrap<ContextifyContext>(wrapper, this);
204     return scope.Escape(wrapper);
205   }
206
207
208   Local<Context> CreateV8Context(Environment* env) {
209     EscapableHandleScope scope(env->isolate());
210     Local<FunctionTemplate> function_template =
211         FunctionTemplate::New(env->isolate());
212     function_template->SetHiddenPrototype(true);
213
214     Local<Object> sandbox = PersistentToLocal(env->isolate(), sandbox_);
215     function_template->SetClassName(sandbox->GetConstructorName());
216
217     Local<ObjectTemplate> object_template =
218         function_template->InstanceTemplate();
219     object_template->SetNamedPropertyHandler(GlobalPropertyGetterCallback,
220                                              GlobalPropertySetterCallback,
221                                              GlobalPropertyQueryCallback,
222                                              GlobalPropertyDeleterCallback,
223                                              GlobalPropertyEnumeratorCallback,
224                                              CreateDataWrapper(env));
225     object_template->SetAccessCheckCallbacks(GlobalPropertyNamedAccessCheck,
226                                              GlobalPropertyIndexedAccessCheck);
227     return scope.Escape(Context::New(env->isolate(), NULL, object_template));
228   }
229
230
231   static void Init(Environment* env, Local<Object> target) {
232     Local<FunctionTemplate> function_template =
233         FunctionTemplate::New(env->isolate());
234     function_template->InstanceTemplate()->SetInternalFieldCount(1);
235     env->set_script_data_constructor_function(function_template->GetFunction());
236
237     NODE_SET_METHOD(target, "makeContext", MakeContext);
238     NODE_SET_METHOD(target, "isContext", IsContext);
239   }
240
241
242   static void MakeContext(const FunctionCallbackInfo<Value>& args) {
243     Environment* env = Environment::GetCurrent(args.GetIsolate());
244     HandleScope scope(env->isolate());
245
246     if (!args[0]->IsObject()) {
247       return env->ThrowTypeError("sandbox argument must be an object.");
248     }
249     Local<Object> sandbox = args[0].As<Object>();
250
251     Local<String> hidden_name =
252         FIXED_ONE_BYTE_STRING(env->isolate(), "_contextifyHidden");
253
254     // Don't allow contextifying a sandbox multiple times.
255     assert(sandbox->GetHiddenValue(hidden_name).IsEmpty());
256
257     TryCatch try_catch;
258     ContextifyContext* context = new ContextifyContext(env, sandbox);
259
260     if (try_catch.HasCaught()) {
261       try_catch.ReThrow();
262       return;
263     }
264
265     if (context->context().IsEmpty())
266       return;
267
268     Local<External> hidden_context = External::New(env->isolate(), context);
269     sandbox->SetHiddenValue(hidden_name, hidden_context);
270   }
271
272
273   static void IsContext(const FunctionCallbackInfo<Value>& args) {
274     Environment* env = Environment::GetCurrent(args.GetIsolate());
275     HandleScope scope(env->isolate());
276
277     if (!args[0]->IsObject()) {
278       env->ThrowTypeError("sandbox must be an object");
279       return;
280     }
281     Local<Object> sandbox = args[0].As<Object>();
282
283     Local<String> hidden_name =
284         FIXED_ONE_BYTE_STRING(env->isolate(), "_contextifyHidden");
285
286     args.GetReturnValue().Set(!sandbox->GetHiddenValue(hidden_name).IsEmpty());
287   }
288
289
290   template <class T, Kind kind>
291   static void WeakCallback(const WeakCallbackData<T, ContextifyContext>& data) {
292     ContextifyContext* context = data.GetParameter();
293     if (kind == kSandbox)
294       context->sandbox_.ClearWeak();
295     else if (kind == kContext)
296       context->context_.ClearWeak();
297     else
298       context->proxy_global_.ClearWeak();
299
300     if (--context->references_ == 0)
301       delete context;
302   }
303
304
305   static ContextifyContext* ContextFromContextifiedSandbox(
306       Isolate* isolate,
307       const Local<Object>& sandbox) {
308     Local<String> hidden_name =
309         FIXED_ONE_BYTE_STRING(isolate, "_contextifyHidden");
310     Local<Value> context_external_v = sandbox->GetHiddenValue(hidden_name);
311     if (context_external_v.IsEmpty() || !context_external_v->IsExternal()) {
312       return NULL;
313     }
314     Local<External> context_external = context_external_v.As<External>();
315
316     return static_cast<ContextifyContext*>(context_external->Value());
317   }
318
319
320   static bool GlobalPropertyNamedAccessCheck(Local<Object> host,
321                                              Local<Value> key,
322                                              AccessType type,
323                                              Local<Value> data) {
324     return true;
325   }
326
327
328   static bool GlobalPropertyIndexedAccessCheck(Local<Object> host,
329                                                uint32_t key,
330                                                AccessType type,
331                                                Local<Value> data) {
332     return true;
333   }
334
335
336   static void GlobalPropertyGetterCallback(
337       Local<String> property,
338       const PropertyCallbackInfo<Value>& args) {
339     Isolate* isolate = args.GetIsolate();
340     HandleScope scope(isolate);
341
342     ContextifyContext* ctx =
343         Unwrap<ContextifyContext>(args.Data().As<Object>());
344
345     Local<Object> sandbox = PersistentToLocal(isolate, ctx->sandbox_);
346     Local<Value> rv = sandbox->GetRealNamedProperty(property);
347     if (rv.IsEmpty()) {
348       Local<Object> proxy_global = PersistentToLocal(isolate,
349                                                      ctx->proxy_global_);
350       rv = proxy_global->GetRealNamedProperty(property);
351     }
352     if (!rv.IsEmpty() && rv == ctx->sandbox_) {
353       rv = PersistentToLocal(isolate, ctx->proxy_global_);
354     }
355
356     args.GetReturnValue().Set(rv);
357   }
358
359
360   static void GlobalPropertySetterCallback(
361       Local<String> property,
362       Local<Value> value,
363       const PropertyCallbackInfo<Value>& args) {
364     Isolate* isolate = args.GetIsolate();
365     HandleScope scope(isolate);
366
367     ContextifyContext* ctx =
368         Unwrap<ContextifyContext>(args.Data().As<Object>());
369
370     PersistentToLocal(isolate, ctx->sandbox_)->Set(property, value);
371   }
372
373
374   static void GlobalPropertyQueryCallback(
375       Local<String> property,
376       const PropertyCallbackInfo<Integer>& args) {
377     Isolate* isolate = args.GetIsolate();
378     HandleScope scope(isolate);
379
380     ContextifyContext* ctx =
381         Unwrap<ContextifyContext>(args.Data().As<Object>());
382
383     Local<Object> sandbox = PersistentToLocal(isolate, ctx->sandbox_);
384     Local<Object> proxy_global = PersistentToLocal(isolate,
385                                                    ctx->proxy_global_);
386
387     bool in_sandbox = sandbox->GetRealNamedProperty(property).IsEmpty();
388     bool in_proxy_global =
389         proxy_global->GetRealNamedProperty(property).IsEmpty();
390     if (!in_sandbox || !in_proxy_global) {
391       args.GetReturnValue().Set(None);
392     }
393   }
394
395
396   static void GlobalPropertyDeleterCallback(
397       Local<String> property,
398       const PropertyCallbackInfo<Boolean>& args) {
399     Isolate* isolate = args.GetIsolate();
400     HandleScope scope(isolate);
401
402     ContextifyContext* ctx =
403         Unwrap<ContextifyContext>(args.Data().As<Object>());
404
405     bool success = PersistentToLocal(isolate,
406                                      ctx->sandbox_)->Delete(property);
407     if (!success) {
408       success = PersistentToLocal(isolate,
409                                   ctx->proxy_global_)->Delete(property);
410     }
411     args.GetReturnValue().Set(success);
412   }
413
414
415   static void GlobalPropertyEnumeratorCallback(
416       const PropertyCallbackInfo<Array>& args) {
417     HandleScope scope(args.GetIsolate());
418
419     ContextifyContext* ctx =
420         Unwrap<ContextifyContext>(args.Data().As<Object>());
421
422     Local<Object> sandbox = PersistentToLocal(args.GetIsolate(), ctx->sandbox_);
423     args.GetReturnValue().Set(sandbox->GetPropertyNames());
424   }
425 };
426
427 class ContextifyScript : public BaseObject {
428  private:
429   Persistent<Script> script_;
430
431  public:
432   static void Init(Environment* env, Local<Object> target) {
433     HandleScope scope(env->isolate());
434     Local<String> class_name =
435         FIXED_ONE_BYTE_STRING(env->isolate(), "ContextifyScript");
436
437     Local<FunctionTemplate> script_tmpl = FunctionTemplate::New(env->isolate(),
438                                                                 New);
439     script_tmpl->InstanceTemplate()->SetInternalFieldCount(1);
440     script_tmpl->SetClassName(class_name);
441     NODE_SET_PROTOTYPE_METHOD(script_tmpl, "runInContext", RunInContext);
442     NODE_SET_PROTOTYPE_METHOD(script_tmpl,
443                               "runInThisContext",
444                               RunInThisContext);
445
446     target->Set(class_name, script_tmpl->GetFunction());
447     env->set_script_context_constructor_template(script_tmpl);
448   }
449
450
451   // args: code, [options]
452   static void New(const FunctionCallbackInfo<Value>& args) {
453     Environment* env = Environment::GetCurrent(args.GetIsolate());
454     HandleScope scope(env->isolate());
455
456     if (!args.IsConstructCall()) {
457       return env->ThrowError("Must call vm.Script as a constructor.");
458     }
459
460     ContextifyScript* contextify_script =
461         new ContextifyScript(env, args.This());
462
463     TryCatch try_catch;
464     Local<String> code = args[0]->ToString();
465     Local<String> filename = GetFilenameArg(args, 1);
466     bool display_errors = GetDisplayErrorsArg(args, 1);
467     if (try_catch.HasCaught()) {
468       try_catch.ReThrow();
469       return;
470     }
471
472     Local<Context> context = env->context();
473     Context::Scope context_scope(context);
474
475     Local<Script> v8_script = Script::New(code, filename);
476
477     if (v8_script.IsEmpty()) {
478       if (display_errors) {
479         AppendExceptionLine(env, try_catch.Exception(), try_catch.Message());
480       }
481       try_catch.ReThrow();
482       return;
483     }
484     contextify_script->script_.Reset(env->isolate(), v8_script);
485   }
486
487
488   static bool InstanceOf(Environment* env, const Local<Value>& value) {
489     return !value.IsEmpty() &&
490            env->script_context_constructor_template()->HasInstance(value);
491   }
492
493
494   // args: [options]
495   static void RunInThisContext(const FunctionCallbackInfo<Value>& args) {
496     Isolate* isolate = args.GetIsolate();
497     HandleScope handle_scope(isolate);
498
499     // Assemble arguments
500     TryCatch try_catch;
501     uint64_t timeout = GetTimeoutArg(args, 0);
502     bool display_errors = GetDisplayErrorsArg(args, 0);
503     if (try_catch.HasCaught()) {
504       try_catch.ReThrow();
505       return;
506     }
507
508     // Do the eval within this context
509     Environment* env = Environment::GetCurrent(isolate);
510     EvalMachine(env, timeout, display_errors, args, try_catch);
511   }
512
513   // args: sandbox, [options]
514   static void RunInContext(const FunctionCallbackInfo<Value>& args) {
515     Environment* env = Environment::GetCurrent(args.GetIsolate());
516     HandleScope scope(env->isolate());
517
518     // Assemble arguments
519     TryCatch try_catch;
520     if (!args[0]->IsObject()) {
521       return env->ThrowTypeError(
522           "contextifiedSandbox argument must be an object.");
523     }
524     Local<Object> sandbox = args[0].As<Object>();
525     int64_t timeout = GetTimeoutArg(args, 1);
526     bool display_errors = GetDisplayErrorsArg(args, 1);
527     if (try_catch.HasCaught()) {
528       try_catch.ReThrow();
529       return;
530     }
531
532     // Get the context from the sandbox
533     ContextifyContext* contextify_context =
534         ContextifyContext::ContextFromContextifiedSandbox(env->isolate(),
535                                                           sandbox);
536     if (contextify_context == NULL) {
537       return env->ThrowTypeError(
538           "sandbox argument must have been converted to a context.");
539     }
540
541     if (contextify_context->context().IsEmpty())
542       return;
543
544     // Do the eval within the context
545     Context::Scope context_scope(contextify_context->context());
546     if (EvalMachine(contextify_context->env(),
547                     timeout,
548                     display_errors,
549                     args,
550                     try_catch)) {
551       contextify_context->CopyProperties();
552     }
553   }
554
555   static int64_t GetTimeoutArg(const FunctionCallbackInfo<Value>& args,
556                                const int i) {
557     if (args[i]->IsUndefined() || args[i]->IsString()) {
558       return -1;
559     }
560     if (!args[i]->IsObject()) {
561       Environment::ThrowTypeError(args.GetIsolate(),
562                                   "options must be an object");
563       return -1;
564     }
565
566     Local<String> key = FIXED_ONE_BYTE_STRING(args.GetIsolate(), "timeout");
567     Local<Value> value = args[i].As<Object>()->Get(key);
568     if (value->IsUndefined()) {
569       return -1;
570     }
571     int64_t timeout = value->IntegerValue();
572
573     if (timeout <= 0) {
574       Environment::ThrowRangeError(args.GetIsolate(),
575                                    "timeout must be a positive number");
576       return -1;
577     }
578     return timeout;
579   }
580
581
582   static bool GetDisplayErrorsArg(const FunctionCallbackInfo<Value>& args,
583                                   const int i) {
584     if (args[i]->IsUndefined() || args[i]->IsString()) {
585       return true;
586     }
587     if (!args[i]->IsObject()) {
588       Environment::ThrowTypeError(args.GetIsolate(),
589                                   "options must be an object");
590       return false;
591     }
592
593     Local<String> key = FIXED_ONE_BYTE_STRING(args.GetIsolate(),
594                                               "displayErrors");
595     Local<Value> value = args[i].As<Object>()->Get(key);
596
597     return value->IsUndefined() ? true : value->BooleanValue();
598   }
599
600
601   static Local<String> GetFilenameArg(const FunctionCallbackInfo<Value>& args,
602                                       const int i) {
603     Local<String> defaultFilename =
604         FIXED_ONE_BYTE_STRING(args.GetIsolate(), "evalmachine.<anonymous>");
605
606     if (args[i]->IsUndefined()) {
607       return defaultFilename;
608     }
609     if (args[i]->IsString()) {
610       return args[i].As<String>();
611     }
612     if (!args[i]->IsObject()) {
613       Environment::ThrowTypeError(args.GetIsolate(),
614                                   "options must be an object");
615       return Local<String>();
616     }
617
618     Local<String> key = FIXED_ONE_BYTE_STRING(args.GetIsolate(), "filename");
619     Local<Value> value = args[i].As<Object>()->Get(key);
620
621     return value->IsUndefined() ? defaultFilename : value->ToString();
622   }
623
624
625   static bool EvalMachine(Environment* env,
626                           const int64_t timeout,
627                           const bool display_errors,
628                           const FunctionCallbackInfo<Value>& args,
629                           TryCatch& try_catch) {
630     if (!ContextifyScript::InstanceOf(env, args.This())) {
631       env->ThrowTypeError(
632           "Script methods can only be called on script instances.");
633       return false;
634     }
635
636     ContextifyScript* wrapped_script =
637         Unwrap<ContextifyScript>(args.This());
638     Local<Script> script = PersistentToLocal(env->isolate(),
639                                              wrapped_script->script_);
640
641     Local<Value> result;
642     if (timeout != -1) {
643       Watchdog wd(timeout);
644       result = script->Run();
645     } else {
646       result = script->Run();
647     }
648
649     if (try_catch.HasCaught() && try_catch.HasTerminated()) {
650       V8::CancelTerminateExecution(args.GetIsolate());
651       env->ThrowError("Script execution timed out.");
652       return false;
653     }
654
655     if (result.IsEmpty()) {
656       // Error occurred during execution of the script.
657       if (display_errors) {
658         AppendExceptionLine(env, try_catch.Exception(), try_catch.Message());
659       }
660       try_catch.ReThrow();
661       return false;
662     }
663
664     args.GetReturnValue().Set(result);
665     return true;
666   }
667
668
669   ContextifyScript(Environment* env, Local<Object> object)
670       : BaseObject(env, object) {
671     MakeWeak<ContextifyScript>(this);
672   }
673
674
675   ~ContextifyScript() {
676     script_.Reset();
677   }
678 };
679
680
681 void InitContextify(Handle<Object> target,
682                     Handle<Value> unused,
683                     Handle<Context> context) {
684   Environment* env = Environment::GetCurrent(context);
685   ContextifyContext::Init(env, target);
686   ContextifyScript::Init(env, target);
687 }
688
689 }  // namespace node
690
691 NODE_MODULE_CONTEXT_AWARE_BUILTIN(contextify, node::InitContextify);