GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage/nodes/benchmark/out/../src/node_contextify.cc Lines: 453 490 92.4 %
Date: 2017-10-21 Branches: 227 284 79.9 %

Line Branch Exec Source
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_internals.h"
23
#include "node_watchdog.h"
24
#include "base-object.h"
25
#include "base-object-inl.h"
26
#include "v8-debug.h"
27
28
namespace node {
29
30
using v8::Array;
31
using v8::ArrayBuffer;
32
using v8::Boolean;
33
using v8::Context;
34
using v8::Debug;
35
using v8::EscapableHandleScope;
36
using v8::External;
37
using v8::Function;
38
using v8::FunctionCallbackInfo;
39
using v8::FunctionTemplate;
40
using v8::HandleScope;
41
using v8::Integer;
42
using v8::Just;
43
using v8::Local;
44
using v8::Maybe;
45
using v8::MaybeLocal;
46
using v8::Name;
47
using v8::NamedPropertyHandlerConfiguration;
48
using v8::Nothing;
49
using v8::Object;
50
using v8::ObjectTemplate;
51
using v8::Persistent;
52
using v8::PropertyAttribute;
53
using v8::PropertyCallbackInfo;
54
using v8::PropertyDescriptor;
55
using v8::Script;
56
using v8::ScriptCompiler;
57
using v8::ScriptOrigin;
58
using v8::String;
59
using v8::Symbol;
60
using v8::TryCatch;
61
using v8::Uint8Array;
62
using v8::UnboundScript;
63
using v8::Value;
64
using v8::WeakCallbackInfo;
65
66
namespace {
67
68
class ContextifyContext {
69
 protected:
70
  // V8 reserves the first field in context objects for the debugger. We use the
71
  // second field to hold a reference to the sandbox object.
72
  enum { kSandboxObjectIndex = 1 };
73
74
  Environment* const env_;
75
  Persistent<Context> context_;
76
77
 public:
78
269
  ContextifyContext(Environment* env, Local<Object> sandbox_obj) : env_(env) {
79
269
    Local<Context> v8_context = CreateV8Context(env, sandbox_obj);
80
269
    context_.Reset(env->isolate(), v8_context);
81
82
    // Allocation failure or maximum call stack size reached
83
538
    if (context_.IsEmpty())
84
269
      return;
85
269
    context_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter);
86
269
    context_.MarkIndependent();
87
  }
88
89
90
14
  ~ContextifyContext() {
91
7
    context_.Reset();
92
7
  }
93
94
95
2007360
  inline Environment* env() const {
96
2007360
    return env_;
97
  }
98
99
100
2002937
  inline Local<Context> context() const {
101
2002937
    return PersistentToLocal(env()->isolate(), context_);
102
  }
103
104
105
391
  inline Local<Object> global_proxy() const {
106
782
    return context()->Global();
107
  }
108
109
110
1001029
  inline Local<Object> sandbox() const {
111
3003087
    return Local<Object>::Cast(context()->GetEmbedderData(kSandboxObjectIndex));
112
  }
113
114
  // XXX(isaacs): This function only exists because of a shortcoming of
115
  // the V8 SetNamedPropertyHandler function.
116
  //
117
  // It does not provide a way to intercept Object.defineProperty(..)
118
  // calls.  As a result, these properties are not copied onto the
119
  // contextified sandbox when a new global property is added via either
120
  // a function declaration or a Object.defineProperty(global, ...) call.
121
  //
122
  // Note that any function declarations or Object.defineProperty()
123
  // globals that are created asynchronously (in a setTimeout, callback,
124
  // etc.) will happen AFTER the call to copy properties, and thus not be
125
  // caught.
126
  //
127
  // The way to properly fix this is to add some sort of a
128
  // Object::SetNamedDefinePropertyHandler() function that takes a callback,
129
  // which receives the property name and property descriptor as arguments.
130
  //
131
  // Luckily, such situations are rare, and asynchronously-added globals
132
  // weren't supported by Node's VM module until 0.12 anyway.  But, this
133
  // should be fixed properly in V8, and this copy function should be
134
  // removed once there is a better way.
135
272
  void CopyProperties() {
136
272
    HandleScope scope(env()->isolate());
137
138
272
    Local<Context> context = PersistentToLocal(env()->isolate(), context_);
139
    Local<Object> global =
140
1088
        context->Global()->GetPrototype()->ToObject(env()->isolate());
141
272
    Local<Object> sandbox_obj = sandbox();
142
143
    Local<Function> clone_property_method;
144
145
272
    Local<Array> names = global->GetOwnPropertyNames();
146
272
    int length = names->Length();
147

3476
    for (int i = 0; i < length; i++) {
148
9612
      Local<String> key = names->Get(i)->ToString(env()->isolate());
149
3204
      Maybe<bool> has = sandbox_obj->HasOwnProperty(context, key);
150
151
      // Check for pending exceptions
152
3204
      if (has.IsNothing())
153
272
        return;
154
155
3204
      if (!has.FromJust()) {
156
        Local<Object> desc_vm_context =
157
32
            global->GetOwnPropertyDescriptor(context, key)
158
48
            .ToLocalChecked().As<Object>();
159
160
        bool is_accessor =
161


128
            desc_vm_context->Has(context, env()->get_string()).FromJust() ||
162

80
            desc_vm_context->Has(context, env()->set_string()).FromJust();
163
164
16
        auto define_property_on_sandbox = [&] (PropertyDescriptor* desc) {
165
            desc->set_configurable(desc_vm_context
166
64
                ->Get(context, env()->configurable_string()).ToLocalChecked()
167
64
                ->BooleanValue(context).FromJust());
168
            desc->set_enumerable(desc_vm_context
169
64
                ->Get(context, env()->enumerable_string()).ToLocalChecked()
170
64
                ->BooleanValue(context).FromJust());
171
64
            CHECK(sandbox_obj->DefineProperty(context, key, *desc).FromJust());
172
32
        };
173
174
16
        if (is_accessor) {
175
          Local<Function> get =
176
              desc_vm_context->Get(context, env()->get_string())
177
              .ToLocalChecked().As<Function>();
178
          Local<Function> set =
179
              desc_vm_context->Get(context, env()->set_string())
180
              .ToLocalChecked().As<Function>();
181
182
          PropertyDescriptor desc(get, set);
183
          define_property_on_sandbox(&desc);
184
        } else {
185
          Local<Value> value =
186
48
              desc_vm_context->Get(context, env()->value_string())
187
32
              .ToLocalChecked();
188
189
          bool writable =
190
48
              desc_vm_context->Get(context, env()->writable_string())
191
64
              .ToLocalChecked()->BooleanValue(context).FromJust();
192
193
16
          PropertyDescriptor desc(value, writable);
194
16
          define_property_on_sandbox(&desc);
195
        }
196
    }
197
272
  }
198
}
199
200
201
  // This is an object that just keeps an internal pointer to this
202
  // ContextifyContext.  It's passed to the NamedPropertyHandler.  If we
203
  // pass the main JavaScript context object we're embedded in, then the
204
  // NamedPropertyHandler will store a reference to it forever and keep it
205
  // from getting gc'd.
206
269
  Local<Value> CreateDataWrapper(Environment* env) {
207
269
    EscapableHandleScope scope(env->isolate());
208
    Local<Object> wrapper =
209
        env->script_data_constructor_function()
210
807
            ->NewInstance(env->context()).FromMaybe(Local<Object>());
211
269
    if (wrapper.IsEmpty())
212
      return scope.Escape(Local<Value>::New(env->isolate(), Local<Value>()));
213
214
269
    Wrap(wrapper, this);
215
269
    return scope.Escape(wrapper);
216
  }
217
218
219
269
  Local<Context> CreateV8Context(Environment* env, Local<Object> sandbox_obj) {
220
269
    EscapableHandleScope scope(env->isolate());
221
    Local<FunctionTemplate> function_template =
222
269
        FunctionTemplate::New(env->isolate());
223
269
    function_template->SetHiddenPrototype(true);
224
225
538
    function_template->SetClassName(sandbox_obj->GetConstructorName());
226
227
    Local<ObjectTemplate> object_template =
228
269
        function_template->InstanceTemplate();
229
230
    NamedPropertyHandlerConfiguration config(GlobalPropertyGetterCallback,
231
                                             GlobalPropertySetterCallback,
232
                                             GlobalPropertyQueryCallback,
233
                                             GlobalPropertyDeleterCallback,
234
                                             GlobalPropertyEnumeratorCallback,
235
269
                                             CreateDataWrapper(env));
236
269
    object_template->SetHandler(config);
237
238
269
    Local<Context> ctx = NewContext(env->isolate(), object_template);
239
240
269
    if (ctx.IsEmpty()) {
241
      env->ThrowError("Could not instantiate context");
242
      return Local<Context>();
243
    }
244
245
807
    ctx->SetSecurityToken(env->context()->GetSecurityToken());
246
247
    // We need to tie the lifetime of the sandbox object with the lifetime of
248
    // newly created context. We do this by making them hold references to each
249
    // other. The context can directly hold a reference to the sandbox as an
250
    // embedder data field. However, we cannot hold a reference to a v8::Context
251
    // directly in an Object, we instead hold onto the new context's global
252
    // object instead (which then has a reference to the context).
253
269
    ctx->SetEmbedderData(kSandboxObjectIndex, sandbox_obj);
254
    sandbox_obj->SetPrivate(env->context(),
255
                            env->contextify_global_private_symbol(),
256
807
                            ctx->Global());
257
258
269
    env->AssignToContext(ctx);
259
260
269
    return scope.Escape(ctx);
261
  }
262
263
264
3200
  static void Init(Environment* env, Local<Object> target) {
265
    Local<FunctionTemplate> function_template =
266
3200
        FunctionTemplate::New(env->isolate());
267
6400
    function_template->InstanceTemplate()->SetInternalFieldCount(1);
268
3200
    env->set_script_data_constructor_function(function_template->GetFunction());
269
270
3200
    env->SetMethod(target, "runInDebugContext", RunInDebugContext);
271
3200
    env->SetMethod(target, "makeContext", MakeContext);
272
3200
    env->SetMethod(target, "isContext", IsContext);
273
3200
  }
274
275
276
16
  static void RunInDebugContext(const FunctionCallbackInfo<Value>& args) {
277
48
    Local<String> script_source(args[0]->ToString(args.GetIsolate()));
278
16
    if (script_source.IsEmpty())
279
5
      return;  // Exception pending.
280
15
    Local<Context> debug_context = Debug::GetDebugContext(args.GetIsolate());
281
15
    Environment* env = Environment::GetCurrent(args);
282
15
    if (debug_context.IsEmpty()) {
283
      // Force-load the debug context.
284
66
      auto dummy_event_listener = [] (const Debug::EventDetails&) {};
285
8
      Debug::SetDebugEventListener(args.GetIsolate(), dummy_event_listener);
286
4
      debug_context = Debug::GetDebugContext(args.GetIsolate());
287
4
      CHECK(!debug_context.IsEmpty());
288
      // Ensure that the debug context has an Environment assigned in case
289
      // a fatal error is raised.  The fatal exception handler in node.cc
290
      // is not equipped to deal with contexts that don't have one and
291
      // can't easily be taught that due to a deficiency in the V8 API:
292
      // there is no way for the embedder to tell if the data index is
293
      // in use.
294
4
      const int index = Environment::kContextEmbedderDataIndex;
295
4
      debug_context->SetAlignedPointerInEmbedderData(index, env);
296
    }
297
298
    Context::Scope context_scope(debug_context);
299
15
    MaybeLocal<Script> script = Script::Compile(debug_context, script_source);
300
15
    if (script.IsEmpty())
301
3
      return;  // Exception pending.
302
60
    args.GetReturnValue().Set(script.ToLocalChecked()->Run());
303
  }
304
305
306
269
  static void MakeContext(const FunctionCallbackInfo<Value>& args) {
307
269
    Environment* env = Environment::GetCurrent(args);
308
309
538
    if (!args[0]->IsObject()) {
310
      return env->ThrowTypeError("sandbox argument must be an object.");
311
    }
312
538
    Local<Object> sandbox = args[0].As<Object>();
313
314
    // Don't allow contextifying a sandbox multiple times.
315
807
    CHECK(
316
        !sandbox->HasPrivate(
317
            env->context(),
318
            env->contextify_context_private_symbol()).FromJust());
319
320
269
    TryCatch try_catch(env->isolate());
321
269
    ContextifyContext* context = new ContextifyContext(env, sandbox);
322
323
269
    if (try_catch.HasCaught()) {
324
      try_catch.ReThrow();
325
      return;
326
    }
327
328
538
    if (context->context().IsEmpty())
329
      return;
330
331
    sandbox->SetPrivate(
332
        env->context(),
333
        env->contextify_context_private_symbol(),
334
807
        External::New(env->isolate(), context));
335
  }
336
337
338
164
  static void IsContext(const FunctionCallbackInfo<Value>& args) {
339
164
    Environment* env = Environment::GetCurrent(args);
340
341
328
    if (!args[0]->IsObject()) {
342
3
      env->ThrowTypeError("sandbox must be an object");
343
167
      return;
344
    }
345
322
    Local<Object> sandbox = args[0].As<Object>();
346
347
    Maybe<bool> result =
348
        sandbox->HasPrivate(env->context(),
349
322
                            env->contextify_context_private_symbol());
350
483
    args.GetReturnValue().Set(result.FromJust());
351
  }
352
353
354
7
  static void WeakCallback(const WeakCallbackInfo<ContextifyContext>& data) {
355
7
    ContextifyContext* context = data.GetParameter();
356
7
    delete context;
357
7
  }
358
359
360
454
  static ContextifyContext* ContextFromContextifiedSandbox(
361
      Environment* env,
362
      const Local<Object>& sandbox) {
363
    MaybeLocal<Value> maybe_value =
364
        sandbox->GetPrivate(env->context(),
365
908
                            env->contextify_context_private_symbol());
366
    Local<Value> context_external_v;
367

908
    if (maybe_value.ToLocal(&context_external_v) &&
368
454
        context_external_v->IsExternal()) {
369
450
      Local<External> context_external = context_external_v.As<External>();
370
450
      return static_cast<ContextifyContext*>(context_external->Value());
371
    }
372
4
    return nullptr;
373
  }
374
375
376
1000593
  static void GlobalPropertyGetterCallback(
377
      Local<Name> property,
378
      const PropertyCallbackInfo<Value>& args) {
379
    ContextifyContext* ctx;
380
2001455
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
381
382
    // Still initializing
383
2001186
    if (ctx->context_.IsEmpty())
384
269
      return;
385
386
1000324
    Local<Context> context = ctx->context();
387
1000324
    Local<Object> sandbox = ctx->sandbox();
388
    MaybeLocal<Value> maybe_rv =
389
1000324
        sandbox->GetRealNamedProperty(context, property);
390
1000324
    if (maybe_rv.IsEmpty()) {
391
      maybe_rv =
392
246
          ctx->global_proxy()->GetRealNamedProperty(context, property);
393
    }
394
395
    Local<Value> rv;
396
1000324
    if (maybe_rv.ToLocal(&rv)) {
397
1000295
      if (rv == sandbox)
398
8
        rv = ctx->global_proxy();
399
400
2000590
      args.GetReturnValue().Set(rv);
401
    }
402
  }
403
404
405
113
  static void GlobalPropertySetterCallback(
406
      Local<Name> property,
407
      Local<Value> value,
408
      const PropertyCallbackInfo<Value>& args) {
409
    ContextifyContext* ctx;
410
232
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
411
412
    // Still initializing
413
226
    if (ctx->context_.IsEmpty())
414
      return;
415
416
113
    auto attributes = PropertyAttribute::None;
417
    bool is_declared =
418
113
        ctx->global_proxy()->GetRealNamedPropertyAttributes(ctx->context(),
419
452
                                                            property)
420
226
        .To(&attributes);
421
    bool read_only =
422
113
        static_cast<int>(attributes) &
423
113
        static_cast<int>(PropertyAttribute::ReadOnly);
424
425

113
    if (is_declared && read_only)
426
2
      return;
427
428
    // true for x = 5
429
    // false for this.x = 5
430
    // false for Object.defineProperty(this, 'foo', ...)
431
    // false for vmResult.x = 5 where vmResult = vm.runInContext();
432
222
    bool is_contextual_store = ctx->global_proxy() != args.This();
433
434
    // Indicator to not return before setting (undeclared) function declarations
435
    // on the sandbox in strict mode, i.e. args.ShouldThrowOnError() = true.
436
    // True for 'function f() {}', 'this.f = function() {}',
437
    // 'var f = function()'.
438
    // In effect only for 'function f() {}' because
439
    // var f = function(), is_declared = true
440
    // this.f = function() {}, is_contextual_store = false.
441
111
    bool is_function = value->IsFunction();
442
443


188
    if (!is_declared && args.ShouldThrowOnError() && is_contextual_store &&
444
20
    !is_function)
445
4
      return;
446
447
214
    ctx->sandbox()->Set(property, value);
448
  }
449
450
451
52
  static void GlobalPropertyQueryCallback(
452
      Local<Name> property,
453
      const PropertyCallbackInfo<Integer>& args) {
454
    ContextifyContext* ctx;
455
104
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
456
457
    // Still initializing
458
104
    if (ctx->context_.IsEmpty())
459
      return;
460
461
52
    Local<Context> context = ctx->context();
462
    Maybe<PropertyAttribute> maybe_prop_attr =
463
104
        ctx->sandbox()->GetRealNamedPropertyAttributes(context, property);
464
465
52
    if (maybe_prop_attr.IsNothing()) {
466
      maybe_prop_attr =
467
40
          ctx->global_proxy()->GetRealNamedPropertyAttributes(context,
468
80
                                                              property);
469
    }
470
471
52
    if (maybe_prop_attr.IsJust()) {
472
26
      PropertyAttribute prop_attr = maybe_prop_attr.FromJust();
473
78
      args.GetReturnValue().Set(prop_attr);
474
    }
475
  }
476
477
478
2
  static void GlobalPropertyDeleterCallback(
479
      Local<Name> property,
480
      const PropertyCallbackInfo<Boolean>& args) {
481
    ContextifyContext* ctx;
482
5
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
483
484
    // Still initializing
485
4
    if (ctx->context_.IsEmpty())
486
      return;
487
488
4
    Maybe<bool> success = ctx->sandbox()->Delete(ctx->context(), property);
489
490
4
    if (success.FromMaybe(false))
491
1
      return;
492
493
    // Delete failed on the sandbox, intercept and do not delete on
494
    // the global object.
495
2
    args.GetReturnValue().Set(false);
496
  }
497
498
499
272
  static void GlobalPropertyEnumeratorCallback(
500
      const PropertyCallbackInfo<Array>& args) {
501
    ContextifyContext* ctx;
502
544
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
503
504
    // Still initializing
505
544
    if (ctx->context_.IsEmpty())
506
      return;
507
508
1088
    args.GetReturnValue().Set(ctx->sandbox()->GetPropertyNames());
509
  }
510
};
511
512
class ContextifyScript : public BaseObject {
513
 private:
514
  Persistent<UnboundScript> script_;
515
516
 public:
517
3200
  static void Init(Environment* env, Local<Object> target) {
518
3200
    HandleScope scope(env->isolate());
519
    Local<String> class_name =
520
3200
        FIXED_ONE_BYTE_STRING(env->isolate(), "ContextifyScript");
521
522
3200
    Local<FunctionTemplate> script_tmpl = env->NewFunctionTemplate(New);
523
6400
    script_tmpl->InstanceTemplate()->SetInternalFieldCount(1);
524
3200
    script_tmpl->SetClassName(class_name);
525
3200
    env->SetProtoMethod(script_tmpl, "runInContext", RunInContext);
526
3200
    env->SetProtoMethod(script_tmpl, "runInThisContext", RunInThisContext);
527
528
6400
    target->Set(class_name, script_tmpl->GetFunction());
529
3200
    env->set_script_context_constructor_template(script_tmpl);
530
531
    Local<Symbol> parsing_context_symbol =
532
        Symbol::New(env->isolate(),
533
                    FIXED_ONE_BYTE_STRING(env->isolate(),
534
3200
                                          "script parsing context"));
535
3200
    env->set_vm_parsing_context_symbol(parsing_context_symbol);
536
    target->Set(env->context(),
537
                FIXED_ONE_BYTE_STRING(env->isolate(), "kParsingContext"),
538
12800
                parsing_context_symbol)
539
9600
        .FromJust();
540
3200
  }
541
542
543
  // args: code, [options]
544
224983
  static void New(const FunctionCallbackInfo<Value>& args) {
545
224983
    Environment* env = Environment::GetCurrent(args);
546
547
224983
    if (!args.IsConstructCall()) {
548
279
      return env->ThrowError("Must call vm.Script as a constructor.");
549
    }
550
551
    ContextifyScript* contextify_script =
552
224983
        new ContextifyScript(env, args.This());
553
554
224983
    TryCatch try_catch(env->isolate());
555
674949
    Local<String> code = args[0]->ToString(env->isolate());
556
557
224983
    Local<Value> options = args[1];
558
224983
    MaybeLocal<String> filename = GetFilenameArg(env, options);
559
224983
    MaybeLocal<Integer> lineOffset = GetLineOffsetArg(env, options);
560
224983
    MaybeLocal<Integer> columnOffset = GetColumnOffsetArg(env, options);
561
224983
    Maybe<bool> maybe_display_errors = GetDisplayErrorsArg(env, options);
562
224983
    MaybeLocal<Uint8Array> cached_data_buf = GetCachedData(env, options);
563
224983
    Maybe<bool> maybe_produce_cached_data = GetProduceCachedData(env, options);
564
224983
    MaybeLocal<Context> maybe_context = GetContext(env, options);
565
224983
    if (try_catch.HasCaught()) {
566
11
      try_catch.ReThrow();
567
11
      return;
568
    }
569
570
224972
    bool display_errors = maybe_display_errors.ToChecked();
571
224972
    bool produce_cached_data = maybe_produce_cached_data.ToChecked();
572
573
224972
    ScriptCompiler::CachedData* cached_data = nullptr;
574
    Local<Uint8Array> ui8;
575
224972
    if (cached_data_buf.ToLocal(&ui8)) {
576
6
      ArrayBuffer::Contents contents = ui8->Buffer()->GetContents();
577
      cached_data = new ScriptCompiler::CachedData(
578
6
          static_cast<uint8_t*>(contents.Data()) + ui8->ByteOffset(),
579
6
          ui8->ByteLength());
580
    }
581
582
    ScriptOrigin origin(filename.ToLocalChecked(), lineOffset.ToLocalChecked(),
583
224972
                        columnOffset.ToLocalChecked());
584
224704
    ScriptCompiler::Source source(code, origin, cached_data);
585
    ScriptCompiler::CompileOptions compile_options =
586
224972
        ScriptCompiler::kNoCompileOptions;
587
588
224972
    if (source.GetCachedData() != nullptr)
589
3
      compile_options = ScriptCompiler::kConsumeCodeCache;
590
224969
    else if (produce_cached_data)
591
6
      compile_options = ScriptCompiler::kProduceCodeCache;
592
593
449676
    Context::Scope scope(maybe_context.FromMaybe(env->context()));
594
595
    MaybeLocal<UnboundScript> v8_script = ScriptCompiler::CompileUnboundScript(
596
        env->isolate(),
597
        &source,
598
224972
        compile_options);
599
600
224972
    if (v8_script.IsEmpty()) {
601
268
      if (display_errors) {
602
265
        DecorateErrorStack(env, try_catch);
603
      }
604
268
      try_catch.ReThrow();
605
268
      return;
606
    }
607
    contextify_script->script_.Reset(env->isolate(),
608
449408
                                     v8_script.ToLocalChecked());
609
610
224704
    if (compile_options == ScriptCompiler::kConsumeCodeCache) {
611
      args.This()->Set(
612
          env->cached_data_rejected_string(),
613
12
          Boolean::New(env->isolate(), source.GetCachedData()->rejected));
614
224701
    } else if (compile_options == ScriptCompiler::kProduceCodeCache) {
615
6
      const ScriptCompiler::CachedData* cached_data = source.GetCachedData();
616
6
      bool cached_data_produced = cached_data != nullptr;
617
6
      if (cached_data_produced) {
618
        MaybeLocal<Object> buf = Buffer::Copy(
619
            env,
620
            reinterpret_cast<const char*>(cached_data->data),
621
6
            cached_data->length);
622
18
        args.This()->Set(env->cached_data_string(), buf.ToLocalChecked());
623
      }
624
      args.This()->Set(
625
          env->cached_data_produced_string(),
626
24
          Boolean::New(env->isolate(), cached_data_produced));
627
224704
    }
628
  }
629
630
631
224680
  static bool InstanceOf(Environment* env, const Local<Value>& value) {
632

898720
    return !value.IsEmpty() &&
633
674040
           env->script_context_constructor_template()->HasInstance(value);
634
  }
635
636
637
  // args: [options]
638
224377
  static void RunInThisContext(const FunctionCallbackInfo<Value>& args) {
639
224377
    Environment* env = Environment::GetCurrent(args);
640
641
    // Assemble arguments
642
224377
    TryCatch try_catch(args.GetIsolate());
643
224377
    Maybe<int64_t> maybe_timeout = GetTimeoutArg(env, args[0]);
644
224377
    Maybe<bool> maybe_display_errors = GetDisplayErrorsArg(env, args[0]);
645
224377
    Maybe<bool> maybe_break_on_sigint = GetBreakOnSigintArg(env, args[0]);
646
224377
    if (try_catch.HasCaught()) {
647
4
      try_catch.ReThrow();
648
224371
      return;
649
    }
650
651
224373
    int64_t timeout = maybe_timeout.ToChecked();
652
224373
    bool display_errors = maybe_display_errors.ToChecked();
653
224373
    bool break_on_sigint = maybe_break_on_sigint.ToChecked();
654
655
    // Do the eval within this context
656
    EvalMachine(env, timeout, display_errors, break_on_sigint, args,
657
224373
                &try_catch);
658
  }
659
660
  // args: sandbox, [options]
661
316
  static void RunInContext(const FunctionCallbackInfo<Value>& args) {
662
316
    Environment* env = Environment::GetCurrent(args);
663
664
    int64_t timeout;
665
    bool display_errors;
666
    bool break_on_sigint;
667
668
    // Assemble arguments
669
632
    if (!args[0]->IsObject()) {
670
      return env->ThrowTypeError(
671
53
          "contextifiedSandbox argument must be an object.");
672
    }
673
674
618
    Local<Object> sandbox = args[0].As<Object>();
675
    {
676
309
      TryCatch try_catch(env->isolate());
677
309
      Maybe<int64_t> maybe_timeout = GetTimeoutArg(env, args[1]);
678
309
      Maybe<bool> maybe_display_errors = GetDisplayErrorsArg(env, args[1]);
679
309
      Maybe<bool> maybe_break_on_sigint = GetBreakOnSigintArg(env, args[1]);
680
309
      if (try_catch.HasCaught()) {
681
        try_catch.ReThrow();
682
        return;
683
      }
684
685
309
      timeout = maybe_timeout.ToChecked();
686
309
      display_errors = maybe_display_errors.ToChecked();
687
309
      break_on_sigint = maybe_break_on_sigint.ToChecked();
688
    }
689
690
    // Get the context from the sandbox
691
    ContextifyContext* contextify_context =
692
309
        ContextifyContext::ContextFromContextifiedSandbox(env, sandbox);
693
309
    if (contextify_context == nullptr) {
694
      return env->ThrowTypeError(
695
2
          "sandbox argument must have been converted to a context.");
696
    }
697
698
614
    if (contextify_context->context().IsEmpty())
699
      return;
700
701
    {
702
307
      TryCatch try_catch(env->isolate());
703
      // Do the eval within the context
704
577
      Context::Scope context_scope(contextify_context->context());
705
307
      if (EvalMachine(contextify_context->env(),
706
                      timeout,
707
                      display_errors,
708
                      break_on_sigint,
709
                      args,
710
307
                      &try_catch)) {
711
272
        contextify_context->CopyProperties();
712
      }
713
714
307
      if (try_catch.HasCaught()) {
715
37
        try_catch.ReThrow();
716
37
        return;
717
270
      }
718
    }
719
  }
720
721
313
  static void DecorateErrorStack(Environment* env, const TryCatch& try_catch) {
722
313
    Local<Value> exception = try_catch.Exception();
723
724
313
    if (!exception->IsObject())
725
14
      return;
726
727
308
    Local<Object> err_obj = exception.As<Object>();
728
729
308
    if (IsExceptionDecorated(env, err_obj))
730
3
      return;
731
732
305
    AppendExceptionLine(env, exception, try_catch.Message(), CONTEXTIFY_ERROR);
733
610
    Local<Value> stack = err_obj->Get(env->stack_string());
734
    MaybeLocal<Value> maybe_value =
735
        err_obj->GetPrivate(
736
            env->context(),
737
610
            env->arrow_message_private_symbol());
738
739
    Local<Value> arrow;
740

915
    if (!(maybe_value.ToLocal(&arrow) && arrow->IsString())) {
741
      return;
742
    }
743
744

915
    if (stack.IsEmpty() || !stack->IsString()) {
745
1
      return;
746
    }
747
748
    Local<String> decorated_stack = String::Concat(
749
        String::Concat(arrow.As<String>(),
750
          FIXED_ONE_BYTE_STRING(env->isolate(), "\n")),
751
608
        stack.As<String>());
752
608
    err_obj->Set(env->stack_string(), decorated_stack);
753
    err_obj->SetPrivate(
754
        env->context(),
755
        env->decorated_private_symbol(),
756
912
        True(env->isolate()));
757
  }
758
759
224686
  static Maybe<bool> GetBreakOnSigintArg(Environment* env,
760
                                         Local<Value> options) {
761

499966
    if (options->IsUndefined() || options->IsString()) {
762
199389
      return Just(false);
763
    }
764
25297
    if (!options->IsObject()) {
765
      env->ThrowTypeError("options must be an object");
766
      return Nothing<bool>();
767
    }
768
769
25297
    Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "breakOnSigint");
770
    MaybeLocal<Value> maybe_value =
771
75891
        options.As<Object>()->Get(env->context(), key);
772
25297
    if (maybe_value.IsEmpty())
773
      return Nothing<bool>();
774
775
25297
    Local<Value> value = maybe_value.ToLocalChecked();
776
25297
    return Just(value->IsTrue());
777
  }
778
779
224686
  static Maybe<int64_t> GetTimeoutArg(Environment* env, Local<Value> options) {
780

499966
    if (options->IsUndefined() || options->IsString()) {
781
199389
      return Just<int64_t>(-1);
782
    }
783
25297
    if (!options->IsObject()) {
784
      env->ThrowTypeError("options must be an object");
785
      return Nothing<int64_t>();
786
    }
787
788
    MaybeLocal<Value> maybe_value =
789
101188
        options.As<Object>()->Get(env->context(), env->timeout_string());
790
25297
    if (maybe_value.IsEmpty())
791
1
      return Nothing<int64_t>();
792
793
25296
    Local<Value> value = maybe_value.ToLocalChecked();
794
50592
    if (value->IsUndefined()) {
795
25285
      return Just<int64_t>(-1);
796
    }
797
798
22
    Maybe<int64_t> timeout = value->IntegerValue(env->context());
799
800

22
    if (timeout.IsJust() && timeout.ToChecked() <= 0) {
801
2
      env->ThrowRangeError("timeout must be a positive number");
802
2
      return Nothing<int64_t>();
803
    }
804
805
9
    return timeout;
806
  }
807
808
809
449669
  static Maybe<bool> GetDisplayErrorsArg(Environment* env,
810
                                         Local<Value> options) {
811

1399054
    if (options->IsUndefined() || options->IsString()) {
812
199812
      return Just(true);
813
    }
814
249857
    if (!options->IsObject()) {
815
      env->ThrowTypeError("options must be an object");
816
      return Nothing<bool>();
817
    }
818
819
249857
    Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "displayErrors");
820
    MaybeLocal<Value> maybe_value =
821
749571
        options.As<Object>()->Get(env->context(), key);
822
249857
    if (maybe_value.IsEmpty())
823
1
      return Nothing<bool>();
824
825
249856
    Local<Value> value = maybe_value.ToLocalChecked();
826
499712
    if (value->IsUndefined())
827
337
      return Just(true);
828
829
499038
    return value->BooleanValue(env->context());
830
  }
831
832
833
224983
  static MaybeLocal<String> GetFilenameArg(Environment* env,
834
                                           Local<Value> options) {
835
    Local<String> defaultFilename =
836
224983
        FIXED_ONE_BYTE_STRING(env->isolate(), "evalmachine.<anonymous>");
837
838
449966
    if (options->IsUndefined()) {
839
422
      return defaultFilename;
840
    }
841
449122
    if (options->IsString()) {
842
1
      return options.As<String>();
843
    }
844
224560
    if (!options->IsObject()) {
845
      env->ThrowTypeError("options must be an object");
846
      return Local<String>();
847
    }
848
849
224560
    Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "filename");
850
    MaybeLocal<Value> maybe_value =
851
673680
        options.As<Object>()->Get(env->context(), key);
852
224560
    if (maybe_value.IsEmpty())
853
1
      return MaybeLocal<String>();
854
855
224559
    Local<Value> value = maybe_value.ToLocalChecked();
856
449118
    if (value->IsUndefined())
857
153
      return defaultFilename;
858
448812
    return value->ToString(env->context());
859
  }
860
861
862
224983
  static MaybeLocal<Uint8Array> GetCachedData(Environment* env,
863
                                              Local<Value> options) {
864
224983
    if (!options->IsObject()) {
865
423
      return MaybeLocal<Uint8Array>();
866
    }
867
868
    MaybeLocal<Value> maybe_value =
869
898240
        options.As<Object>()->Get(env->context(), env->cached_data_string());
870
224560
    if (maybe_value.IsEmpty())
871
1
      return MaybeLocal<Uint8Array>();
872
873
224559
    Local<Value> value = maybe_value.ToLocalChecked();
874
449118
    if (value->IsUndefined()) {
875
224555
      return MaybeLocal<Uint8Array>();
876
    }
877
878
4
    if (!value->IsUint8Array()) {
879
1
      env->ThrowTypeError("options.cachedData must be a Buffer instance");
880
1
      return MaybeLocal<Uint8Array>();
881
    }
882
883
3
    return value.As<Uint8Array>();
884
  }
885
886
887
224983
  static Maybe<bool> GetProduceCachedData(Environment* env,
888
                                          Local<Value> options) {
889
224983
    if (!options->IsObject()) {
890
423
      return Just(false);
891
    }
892
893
    MaybeLocal<Value> maybe_value =
894
        options.As<Object>()->Get(env->context(),
895
898240
                                  env->produce_cached_data_string());
896
224560
    if (maybe_value.IsEmpty())
897
1
      return Nothing<bool>();
898
899
224559
    Local<Value> value = maybe_value.ToLocalChecked();
900
224559
    return Just(value->IsTrue());
901
  }
902
903
904
224983
  static MaybeLocal<Integer> GetLineOffsetArg(Environment* env,
905
                                              Local<Value> options) {
906
224983
    Local<Integer> defaultLineOffset = Integer::New(env->isolate(), 0);
907
908
224983
    if (!options->IsObject()) {
909
423
      return defaultLineOffset;
910
    }
911
912
224560
    Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "lineOffset");
913
    MaybeLocal<Value> maybe_value =
914
673680
        options.As<Object>()->Get(env->context(), key);
915
224560
    if (maybe_value.IsEmpty())
916
1
      return MaybeLocal<Integer>();
917
918
224559
    Local<Value> value = maybe_value.ToLocalChecked();
919
449118
    if (value->IsUndefined())
920
776
      return defaultLineOffset;
921
922
447566
    return value->ToInteger(env->context());
923
  }
924
925
926
224983
  static MaybeLocal<Integer> GetColumnOffsetArg(Environment* env,
927
                                                Local<Value> options) {
928
224983
    Local<Integer> defaultColumnOffset = Integer::New(env->isolate(), 0);
929
930
224983
    if (!options->IsObject()) {
931
423
      return defaultColumnOffset;
932
    }
933
934
224560
    Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "columnOffset");
935
    MaybeLocal<Value> maybe_value =
936
673680
      options.As<Object>()->Get(env->context(), key);
937
224560
    if (maybe_value.IsEmpty())
938
1
      return MaybeLocal<Integer>();
939
940
224559
    Local<Value> value = maybe_value.ToLocalChecked();
941
449118
    if (value->IsUndefined())
942
224558
      return defaultColumnOffset;
943
944
2
    return value->ToInteger(env->context());
945
  }
946
947
224983
  static MaybeLocal<Context> GetContext(Environment* env,
948
                                        Local<Value> options) {
949
224983
    if (!options->IsObject())
950
423
      return MaybeLocal<Context>();
951
952
    MaybeLocal<Value> maybe_value =
953
        options.As<Object>()->Get(env->context(),
954
898240
                                  env->vm_parsing_context_symbol());
955
    Local<Value> value;
956
224560
    if (!maybe_value.ToLocal(&value))
957
      return MaybeLocal<Context>();
958
959
224560
    if (!value->IsObject()) {
960
448830
      if (!value->IsNullOrUndefined()) {
961
        env->ThrowTypeError(
962
3
            "contextifiedSandbox argument must be an object.");
963
      }
964
224415
      return MaybeLocal<Context>();
965
    }
966
967
    ContextifyContext* sandbox =
968
        ContextifyContext::ContextFromContextifiedSandbox(
969
145
            env, value.As<Object>());
970
145
    if (!sandbox) {
971
      env->ThrowTypeError(
972
2
          "sandbox argument must have been converted to a context.");
973
2
      return MaybeLocal<Context>();
974
    }
975
976
143
    Local<Context> context = sandbox->context();
977
143
    if (context.IsEmpty())
978
      return MaybeLocal<Context>();
979
143
    return context;
980
  }
981
982
983
224680
  static bool EvalMachine(Environment* env,
984
                          const int64_t timeout,
985
                          const bool display_errors,
986
                          const bool break_on_sigint,
987
                          const FunctionCallbackInfo<Value>& args,
988
                          TryCatch* try_catch) {
989
224680
    if (!ContextifyScript::InstanceOf(env, args.Holder())) {
990
      env->ThrowTypeError(
991
          "Script methods can only be called on script instances.");
992
      return false;
993
    }
994
995
    ContextifyScript* wrapped_script;
996
224680
    ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder(), false);
997
    Local<UnboundScript> unbound_script =
998
224680
        PersistentToLocal(env->isolate(), wrapped_script->script_);
999
224680
    Local<Script> script = unbound_script->BindToCurrentContext();
1000
1001
    Local<Value> result;
1002
224680
    bool timed_out = false;
1003
224680
    bool received_signal = false;
1004

224680
    if (break_on_sigint && timeout != -1) {
1005
      Watchdog wd(env->isolate(), timeout, &timed_out);
1006
      SigintWatchdog swd(env->isolate(), &received_signal);
1007
      result = script->Run();
1008
224680
    } else if (break_on_sigint) {
1009
34
      SigintWatchdog swd(env->isolate(), &received_signal);
1010
34
      result = script->Run();
1011
224646
    } else if (timeout != -1) {
1012
9
      Watchdog wd(env->isolate(), timeout, &timed_out);
1013
9
      result = script->Run();
1014
    } else {
1015
224637
      result = script->Run();
1016
    }
1017
1018

224670
    if (timed_out || received_signal) {
1019
      // It is possible that execution was terminated by another timeout in
1020
      // which this timeout is nested, so check whether one of the watchdogs
1021
      // from this invocation is responsible for termination.
1022
15
      if (timed_out) {
1023
4
        env->ThrowError("Script execution timed out.");
1024
11
      } else if (received_signal) {
1025
11
        env->ThrowError("Script execution interrupted.");
1026
      }
1027
15
      env->isolate()->CancelTerminateExecution();
1028
    }
1029
1030
224670
    if (try_catch->HasCaught()) {
1031

87
      if (!timed_out && !received_signal && display_errors) {
1032
        // We should decorate non-termination exceptions
1033
48
        DecorateErrorStack(env, *try_catch);
1034
      }
1035
1036
      // If there was an exception thrown during script execution, re-throw it.
1037
      // If one of the above checks threw, re-throw the exception instead of
1038
      // letting try_catch catch it.
1039
      // If execution has been terminated, but not by one of the watchdogs from
1040
      // this invocation, this will re-throw a `null` value.
1041
87
      try_catch->ReThrow();
1042
1043
87
      return false;
1044
    }
1045
1046
449166
    args.GetReturnValue().Set(result);
1047
224583
    return true;
1048
  }
1049
1050
1051
224983
  ContextifyScript(Environment* env, Local<Object> object)
1052
224983
      : BaseObject(env, object) {
1053
224983
    MakeWeak<ContextifyScript>(this);
1054
224983
  }
1055
1056
1057
577056
  ~ContextifyScript() override {
1058
192352
    script_.Reset();
1059
384704
  }
1060
};
1061
1062
1063
3200
void InitContextify(Local<Object> target,
1064
                    Local<Value> unused,
1065
                    Local<Context> context) {
1066
3200
  Environment* env = Environment::GetCurrent(context);
1067
3200
  ContextifyContext::Init(env, target);
1068
3200
  ContextifyScript::Init(env, target);
1069
3200
}
1070
1071
}  // anonymous namespace
1072
}  // namespace node
1073
1074
3245
NODE_MODULE_CONTEXT_AWARE_BUILTIN(contextify, node::InitContextify)