GCC Code Coverage Report
Directory: ../src/ Exec Total Coverage
File: /home/node-core-coverage/node-core-coverage/workdir/node/out/../src/node_contextify.cc Lines: 357 387 92.2 %
Date: 2016-12-18 Branches: 166 212 78.3 %

Line Branch Exec Source
1
#include "node.h"
2
#include "node_internals.h"
3
#include "node_watchdog.h"
4
#include "base-object.h"
5
#include "base-object-inl.h"
6
#include "env.h"
7
#include "env-inl.h"
8
#include "util.h"
9
#include "util-inl.h"
10
#include "v8-debug.h"
11
12
namespace node {
13
14
using v8::Array;
15
using v8::ArrayBuffer;
16
using v8::Boolean;
17
using v8::Context;
18
using v8::Debug;
19
using v8::EscapableHandleScope;
20
using v8::External;
21
using v8::Function;
22
using v8::FunctionCallbackInfo;
23
using v8::FunctionTemplate;
24
using v8::HandleScope;
25
using v8::Integer;
26
using v8::Local;
27
using v8::Maybe;
28
using v8::MaybeLocal;
29
using v8::Name;
30
using v8::NamedPropertyHandlerConfiguration;
31
using v8::Object;
32
using v8::ObjectTemplate;
33
using v8::Persistent;
34
using v8::PropertyAttribute;
35
using v8::PropertyCallbackInfo;
36
using v8::Script;
37
using v8::ScriptCompiler;
38
using v8::ScriptOrigin;
39
using v8::String;
40
using v8::TryCatch;
41
using v8::Uint8Array;
42
using v8::UnboundScript;
43
using v8::Value;
44
using v8::WeakCallbackInfo;
45
46
47
class ContextifyContext {
48
 protected:
49
  // V8 reserves the first field in context objects for the debugger. We use the
50
  // second field to hold a reference to the sandbox object.
51
  enum { kSandboxObjectIndex = 1 };
52
53
  Environment* const env_;
54
  Persistent<Context> context_;
55
56
 public:
57
516
  ContextifyContext(Environment* env, Local<Object> sandbox_obj) : env_(env) {
58
258
    Local<Context> v8_context = CreateV8Context(env, sandbox_obj);
59
516
    context_.Reset(env->isolate(), v8_context);
60
61
    // Allocation failure or maximum call stack size reached
62
516
    if (context_.IsEmpty())
63
      return;
64
516
    context_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter);
65
516
    context_.MarkIndependent();
66
  }
67
68
69
12
  ~ContextifyContext() {
70
12
    context_.Reset();
71
  }
72
73
74
  inline Environment* env() const {
75
    return env_;
76
  }
77
78
79
  inline Local<Context> context() const {
80
7849
    return PersistentToLocal(env()->isolate(), context_);
81
  }
82
83
84
863
  inline Local<Object> global_proxy() const {
85
863
    return context()->Global();
86
  }
87
88
89
3341
  inline Local<Object> sandbox() const {
90
10023
    return Local<Object>::Cast(context()->GetEmbedderData(kSandboxObjectIndex));
91
  }
92
93
  // XXX(isaacs): This function only exists because of a shortcoming of
94
  // the V8 SetNamedPropertyHandler function.
95
  //
96
  // It does not provide a way to intercept Object.defineProperty(..)
97
  // calls.  As a result, these properties are not copied onto the
98
  // contextified sandbox when a new global property is added via either
99
  // a function declaration or a Object.defineProperty(global, ...) call.
100
  //
101
  // Note that any function declarations or Object.defineProperty()
102
  // globals that are created asynchronously (in a setTimeout, callback,
103
  // etc.) will happen AFTER the call to copy properties, and thus not be
104
  // caught.
105
  //
106
  // The way to properly fix this is to add some sort of a
107
  // Object::SetNamedDefinePropertyHandler() function that takes a callback,
108
  // which receives the property name and property descriptor as arguments.
109
  //
110
  // Luckily, such situations are rare, and asynchronously-added globals
111
  // weren't supported by Node's VM module until 0.12 anyway.  But, this
112
  // should be fixed properly in V8, and this copy function should be
113
  // removed once there is a better way.
114
207
  void CopyProperties() {
115
414
    HandleScope scope(env()->isolate());
116
117
207
    Local<Context> context = PersistentToLocal(env()->isolate(), context_);
118
    Local<Object> global =
119
828
        context->Global()->GetPrototype()->ToObject(env()->isolate());
120
207
    Local<Object> sandbox_obj = sandbox();
121
122
207
    Local<Function> clone_property_method;
123
124
207
    Local<Array> names = global->GetOwnPropertyNames();
125
207
    int length = names->Length();
126
2517
    for (int i = 0; i < length; i++) {
127
4620
      Local<String> key = names->Get(i)->ToString(env()->isolate());
128
4620
      Maybe<bool> has = sandbox_obj->HasOwnProperty(context, key);
129
130
      // Check for pending exceptions
131
2310
      if (has.IsNothing())
132
        return;
133
134
2310
      if (!has.FromJust()) {
135
        // Could also do this like so:
136
        //
137
        // PropertyAttribute att = global->GetPropertyAttributes(key_v);
138
        // Local<Value> val = global->Get(key_v);
139
        // sandbox->ForceSet(key_v, val, att);
140
        //
141
        // However, this doesn't handle ES6-style properties configured with
142
        // Object.defineProperty, and that's exactly what we're up against at
143
        // this point.  ForceSet(key,val,att) only supports value properties
144
        // with the ES3-style attribute flags (DontDelete/DontEnum/ReadOnly),
145
        // which doesn't faithfully capture the full range of configurations
146
        // that can be done using Object.defineProperty.
147
15
        if (clone_property_method.IsEmpty()) {
148
15
          Local<String> code = FIXED_ONE_BYTE_STRING(env()->isolate(),
149
              "(function cloneProperty(source, key, target) {\n"
150
              "  if (key === 'Proxy') return;\n"
151
              "  try {\n"
152
              "    var desc = Object.getOwnPropertyDescriptor(source, key);\n"
153
              "    if (desc.value === source) desc.value = target;\n"
154
              "    Object.defineProperty(target, key, desc);\n"
155
              "  } catch (e) {\n"
156
              "   // Catch sealed properties errors\n"
157
              "  }\n"
158
              "})");
159
160
          Local<Script> script =
161
30
              Script::Compile(context, code).ToLocalChecked();
162
30
          clone_property_method = Local<Function>::Cast(script->Run());
163
15
          CHECK(clone_property_method->IsFunction());
164
        }
165
60
        Local<Value> args[] = { global, key, sandbox_obj };
166
30
        clone_property_method->Call(global, arraysize(args), args);
167
      }
168
    }
169
  }
170
171
172
  // This is an object that just keeps an internal pointer to this
173
  // ContextifyContext.  It's passed to the NamedPropertyHandler.  If we
174
  // pass the main JavaScript context object we're embedded in, then the
175
  // NamedPropertyHandler will store a reference to it forever and keep it
176
  // from getting gc'd.
177
258
  Local<Value> CreateDataWrapper(Environment* env) {
178
516
    EscapableHandleScope scope(env->isolate());
179
    Local<Object> wrapper =
180
258
        env->script_data_constructor_function()
181
1290
            ->NewInstance(env->context()).FromMaybe(Local<Object>());
182
258
    if (wrapper.IsEmpty())
183
      return scope.Escape(Local<Value>::New(env->isolate(), Local<Value>()));
184
185
258
    Wrap(wrapper, this);
186
516
    return scope.Escape(wrapper);
187
  }
188
189
190
258
  Local<Context> CreateV8Context(Environment* env, Local<Object> sandbox_obj) {
191
516
    EscapableHandleScope scope(env->isolate());
192
    Local<FunctionTemplate> function_template =
193
516
        FunctionTemplate::New(env->isolate());
194
258
    function_template->SetHiddenPrototype(true);
195
196
258
    function_template->SetClassName(sandbox_obj->GetConstructorName());
197
198
    Local<ObjectTemplate> object_template =
199
258
        function_template->InstanceTemplate();
200
201
    NamedPropertyHandlerConfiguration config(GlobalPropertyGetterCallback,
202
                                             GlobalPropertySetterCallback,
203
                                             GlobalPropertyQueryCallback,
204
                                             GlobalPropertyDeleterCallback,
205
                                             GlobalPropertyEnumeratorCallback,
206
516
                                             CreateDataWrapper(env));
207
258
    object_template->SetHandler(config);
208
209
516
    Local<Context> ctx = Context::New(env->isolate(), nullptr, object_template);
210
211
258
    if (ctx.IsEmpty()) {
212
      env->ThrowError("Could not instantiate context");
213
      return Local<Context>();
214
    }
215
216
258
    ctx->SetSecurityToken(env->context()->GetSecurityToken());
217
218
    // We need to tie the lifetime of the sandbox object with the lifetime of
219
    // newly created context. We do this by making them hold references to each
220
    // other. The context can directly hold a reference to the sandbox as an
221
    // embedder data field. However, we cannot hold a reference to a v8::Context
222
    // directly in an Object, we instead hold onto the new context's global
223
    // object instead (which then has a reference to the context).
224
    ctx->SetEmbedderData(kSandboxObjectIndex, sandbox_obj);
225
    sandbox_obj->SetPrivate(env->context(),
226
                            env->contextify_global_private_symbol(),
227
1032
                            ctx->Global());
228
229
258
    env->AssignToContext(ctx);
230
231
    return scope.Escape(ctx);
232
  }
233
234
235
1727
  static void Init(Environment* env, Local<Object> target) {
236
    Local<FunctionTemplate> function_template =
237
3454
        FunctionTemplate::New(env->isolate());
238
3454
    function_template->InstanceTemplate()->SetInternalFieldCount(1);
239
1727
    env->set_script_data_constructor_function(function_template->GetFunction());
240
241
1727
    env->SetMethod(target, "runInDebugContext", RunInDebugContext);
242
1727
    env->SetMethod(target, "makeContext", MakeContext);
243
1727
    env->SetMethod(target, "isContext", IsContext);
244
1727
  }
245
246
247
17
  static void RunInDebugContext(const FunctionCallbackInfo<Value>& args) {
248
34
    Local<String> script_source(args[0]->ToString(args.GetIsolate()));
249
17
    if (script_source.IsEmpty())
250
4
      return;  // Exception pending.
251
16
    Local<Context> debug_context = Debug::GetDebugContext(args.GetIsolate());
252
16
    Environment* env = Environment::GetCurrent(args);
253
16
    if (debug_context.IsEmpty()) {
254
      // Force-load the debug context.
255
28
      Debug::GetMirror(args.GetIsolate()->GetCurrentContext(), args[0]);
256
14
      debug_context = Debug::GetDebugContext(args.GetIsolate());
257
14
      CHECK(!debug_context.IsEmpty());
258
      // Ensure that the debug context has an Environment assigned in case
259
      // a fatal error is raised.  The fatal exception handler in node.cc
260
      // is not equipped to deal with contexts that don't have one and
261
      // can't easily be taught that due to a deficiency in the V8 API:
262
      // there is no way for the embedder to tell if the data index is
263
      // in use.
264
14
      const int index = Environment::kContextEmbedderDataIndex;
265
14
      debug_context->SetAlignedPointerInEmbedderData(index, env);
266
    }
267
268
29
    Context::Scope context_scope(debug_context);
269
16
    MaybeLocal<Script> script = Script::Compile(debug_context, script_source);
270
16
    if (script.IsEmpty())
271
      return;  // Exception pending.
272
39
    args.GetReturnValue().Set(script.ToLocalChecked()->Run());
273
  }
274
275
276
258
  static void MakeContext(const FunctionCallbackInfo<Value>& args) {
277
258
    Environment* env = Environment::GetCurrent(args);
278
279
258
    if (!args[0]->IsObject()) {
280
      return env->ThrowTypeError("sandbox argument must be an object.");
281
    }
282
516
    Local<Object> sandbox = args[0].As<Object>();
283
284
    // Don't allow contextifying a sandbox multiple times.
285
774
    CHECK(
286
        !sandbox->HasPrivate(
287
            env->context(),
288
            env->contextify_context_private_symbol()).FromJust());
289
290
516
    TryCatch try_catch(env->isolate());
291
258
    ContextifyContext* context = new ContextifyContext(env, sandbox);
292
293
258
    if (try_catch.HasCaught()) {
294
      try_catch.ReThrow();
295
      return;
296
    }
297
298
258
    if (context->context().IsEmpty())
299
      return;
300
301
    sandbox->SetPrivate(
302
        env->context(),
303
        env->contextify_context_private_symbol(),
304
1032
        External::New(env->isolate(), context));
305
  }
306
307
308
75
  static void IsContext(const FunctionCallbackInfo<Value>& args) {
309
75
    Environment* env = Environment::GetCurrent(args);
310
311
75
    if (!args[0]->IsObject()) {
312
3
      env->ThrowTypeError("sandbox must be an object");
313
3
      return;
314
    }
315
144
    Local<Object> sandbox = args[0].As<Object>();
316
317
    Maybe<bool> result =
318
        sandbox->HasPrivate(env->context(),
319
144
                            env->contextify_context_private_symbol());
320
216
    args.GetReturnValue().Set(result.FromJust());
321
  }
322
323
324
6
  static void WeakCallback(const WeakCallbackInfo<ContextifyContext>& data) {
325
6
    ContextifyContext* context = data.GetParameter();
326
12
    delete context;
327
6
  }
328
329
330
231
  static ContextifyContext* ContextFromContextifiedSandbox(
331
      Environment* env,
332
      const Local<Object>& sandbox) {
333
    MaybeLocal<Value> maybe_value =
334
        sandbox->GetPrivate(env->context(),
335
693
                            env->contextify_context_private_symbol());
336
231
    Local<Value> context_external_v;
337

462
    if (maybe_value.ToLocal(&context_external_v) &&
338
231
        context_external_v->IsExternal()) {
339
227
      Local<External> context_external = context_external_v.As<External>();
340
227
      return static_cast<ContextifyContext*>(context_external->Value());
341
    }
342
    return nullptr;
343
  }
344
345
346
2442
  static void GlobalPropertyGetterCallback(
347
      Local<Name> property,
348
      const PropertyCallbackInfo<Value>& args) {
349
    ContextifyContext* ctx;
350
5142
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
351
352
    // Still initializing
353
4884
    if (ctx->context_.IsEmpty())
354
      return;
355
356
4368
    Local<Context> context = ctx->context();
357
2184
    Local<Object> sandbox = ctx->sandbox();
358
    MaybeLocal<Value> maybe_rv =
359
2184
        sandbox->GetRealNamedProperty(context, property);
360
2184
    if (maybe_rv.IsEmpty()) {
361
      maybe_rv =
362
232
          ctx->global_proxy()->GetRealNamedProperty(context, property);
363
    }
364
365
2184
    Local<Value> rv;
366
2184
    if (maybe_rv.ToLocal(&rv)) {
367
2166
      if (rv == sandbox)
368
6
        rv = ctx->global_proxy();
369
370
2166
      args.GetReturnValue().Set(rv);
371
    }
372
  }
373
374
375
737
  static void GlobalPropertySetterCallback(
376
      Local<Name> property,
377
      Local<Value> value,
378
      const PropertyCallbackInfo<Value>& args) {
379
    ContextifyContext* ctx;
380
1474
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
381
382
    // Still initializing
383
1474
    if (ctx->context_.IsEmpty())
384
      return;
385
386
727
    auto attributes = PropertyAttribute::None;
387
    bool is_declared =
388
2181
        ctx->global_proxy()->GetRealNamedPropertyAttributes(ctx->context(),
389
2181
                                                            property)
390
727
        .To(&attributes);
391
    bool read_only =
392
727
        static_cast<int>(attributes) &
393
727
        static_cast<int>(PropertyAttribute::ReadOnly);
394
395
727
    if (is_declared && read_only)
396
      return;
397
398

757
    if (!is_declared && args.ShouldThrowOnError())
399
      return;
400
401
1442
    ctx->sandbox()->Set(property, value);
402
  }
403
404
405
20
  static void GlobalPropertyQueryCallback(
406
      Local<Name> property,
407
      const PropertyCallbackInfo<Integer>& args) {
408
    ContextifyContext* ctx;
409
40
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
410
411
    // Still initializing
412
40
    if (ctx->context_.IsEmpty())
413
      return;
414
415
40
    Local<Context> context = ctx->context();
416
    Maybe<PropertyAttribute> maybe_prop_attr =
417
40
        ctx->sandbox()->GetRealNamedPropertyAttributes(context, property);
418
419
20
    if (maybe_prop_attr.IsNothing()) {
420
      maybe_prop_attr =
421
34
          ctx->global_proxy()->GetRealNamedPropertyAttributes(context,
422
17
                                                              property);
423
    }
424
425
20
    if (maybe_prop_attr.IsJust()) {
426
18
      PropertyAttribute prop_attr = maybe_prop_attr.FromJust();
427
54
      args.GetReturnValue().Set(prop_attr);
428
    }
429
  }
430
431
432
2
  static void GlobalPropertyDeleterCallback(
433
      Local<Name> property,
434
      const PropertyCallbackInfo<Boolean>& args) {
435
    ContextifyContext* ctx;
436
4
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
437
438
    // Still initializing
439
4
    if (ctx->context_.IsEmpty())
440
      return;
441
442
6
    Maybe<bool> success = ctx->sandbox()->Delete(ctx->context(), property);
443
444
2
    if (success.IsJust())
445
6
      args.GetReturnValue().Set(success.FromJust());
446
  }
447
448
449
207
  static void GlobalPropertyEnumeratorCallback(
450
      const PropertyCallbackInfo<Array>& args) {
451
    ContextifyContext* ctx;
452
414
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
453
454
    // Still initializing
455
414
    if (ctx->context_.IsEmpty())
456
      return;
457
458
828
    args.GetReturnValue().Set(ctx->sandbox()->GetPropertyNames());
459
  }
460
};
461
462
class ContextifyScript : public BaseObject {
463
 private:
464
  Persistent<UnboundScript> script_;
465
466
 public:
467
1727
  static void Init(Environment* env, Local<Object> target) {
468
3454
    HandleScope scope(env->isolate());
469
    Local<String> class_name =
470
1727
        FIXED_ONE_BYTE_STRING(env->isolate(), "ContextifyScript");
471
472
3454
    Local<FunctionTemplate> script_tmpl = env->NewFunctionTemplate(New);
473
3454
    script_tmpl->InstanceTemplate()->SetInternalFieldCount(1);
474
1727
    script_tmpl->SetClassName(class_name);
475
1727
    env->SetProtoMethod(script_tmpl, "runInContext", RunInContext);
476
1727
    env->SetProtoMethod(script_tmpl, "runInThisContext", RunInThisContext);
477
478
6908
    target->Set(class_name, script_tmpl->GetFunction());
479
1727
    env->set_script_context_constructor_template(script_tmpl);
480
1727
  }
481
482
483
  // args: code, [options]
484
76552
  static void New(const FunctionCallbackInfo<Value>& args) {
485
76552
    Environment* env = Environment::GetCurrent(args);
486
487
76552
    if (!args.IsConstructCall()) {
488
303
      return env->ThrowError("Must call vm.Script as a constructor.");
489
    }
490
491
    ContextifyScript* contextify_script =
492
153104
        new ContextifyScript(env, args.This());
493
494
152801
    TryCatch try_catch(env->isolate());
495
153104
    Local<String> code = args[0]->ToString(env->isolate());
496
497
76552
    Local<Value> options = args[1];
498
76552
    Local<String> filename = GetFilenameArg(env, options);
499
76552
    Local<Integer> lineOffset = GetLineOffsetArg(env, options);
500
76552
    Local<Integer> columnOffset = GetColumnOffsetArg(env, options);
501
76552
    bool display_errors = GetDisplayErrorsArg(env, options);
502
76552
    MaybeLocal<Uint8Array> cached_data_buf = GetCachedData(env, options);
503
76552
    bool produce_cached_data = GetProduceCachedData(env, options);
504
76552
    if (try_catch.HasCaught()) {
505
1
      try_catch.ReThrow();
506
1
      return;
507
    }
508
509
76551
    ScriptCompiler::CachedData* cached_data = nullptr;
510
76551
    Local<Uint8Array> ui8;
511
76551
    if (cached_data_buf.ToLocal(&ui8)) {
512
6
      ArrayBuffer::Contents contents = ui8->Buffer()->GetContents();
513
      cached_data = new ScriptCompiler::CachedData(
514
3
          static_cast<uint8_t*>(contents.Data()) + ui8->ByteOffset(),
515
3
          ui8->ByteLength());
516
    }
517
518
229653
    ScriptOrigin origin(filename, lineOffset, columnOffset);
519
152800
    ScriptCompiler::Source source(code, origin, cached_data);
520
    ScriptCompiler::CompileOptions compile_options =
521
76551
        ScriptCompiler::kNoCompileOptions;
522
523
76551
    if (source.GetCachedData() != nullptr)
524
      compile_options = ScriptCompiler::kConsumeCodeCache;
525
76548
    else if (produce_cached_data)
526
6
      compile_options = ScriptCompiler::kProduceCodeCache;
527
528
    MaybeLocal<UnboundScript> v8_script = ScriptCompiler::CompileUnboundScript(
529
        env->isolate(),
530
        &source,
531
76551
        compile_options);
532
533
76551
    if (v8_script.IsEmpty()) {
534
302
      if (display_errors) {
535
301
        DecorateErrorStack(env, try_catch);
536
      }
537
302
      try_catch.ReThrow();
538
      return;
539
    }
540
76249
    contextify_script->script_.Reset(env->isolate(),
541
152498
                                     v8_script.ToLocalChecked());
542
543
76249
    if (compile_options == ScriptCompiler::kConsumeCodeCache) {
544
21
      args.This()->Set(
545
          env->cached_data_rejected_string(),
546
6
          Boolean::New(env->isolate(), source.GetCachedData()->rejected));
547
76246
    } else if (compile_options == ScriptCompiler::kProduceCodeCache) {
548
6
      const ScriptCompiler::CachedData* cached_data = source.GetCachedData();
549
6
      bool cached_data_produced = cached_data != nullptr;
550
6
      if (cached_data_produced) {
551
        MaybeLocal<Object> buf = Buffer::Copy(
552
            env,
553
6
            reinterpret_cast<const char*>(cached_data->data),
554
12
            cached_data->length);
555
30
        args.This()->Set(env->cached_data_string(), buf.ToLocalChecked());
556
      }
557
42
      args.This()->Set(
558
          env->cached_data_produced_string(),
559
6
          Boolean::New(env->isolate(), cached_data_produced));
560
    }
561
  }
562
563
564
  static bool InstanceOf(Environment* env, const Local<Value>& value) {
565

228537
    return !value.IsEmpty() &&
566
152358
           env->script_context_constructor_template()->HasInstance(value);
567
  }
568
569
570
  // args: [options]
571
75954
  static void RunInThisContext(const FunctionCallbackInfo<Value>& args) {
572
75954
    Environment* env = Environment::GetCurrent(args);
573
574
    // Assemble arguments
575
151901
    TryCatch try_catch(args.GetIsolate());
576
75954
    uint64_t timeout = GetTimeoutArg(env, args[0]);
577
75954
    bool display_errors = GetDisplayErrorsArg(env, args[0]);
578
75954
    bool break_on_sigint = GetBreakOnSigintArg(env, args[0]);
579
75954
    if (try_catch.HasCaught()) {
580
2
      try_catch.ReThrow();
581
2
      return;
582
    }
583
584
    // Do the eval within this context
585
75952
    EvalMachine(env, timeout, display_errors, break_on_sigint, args,
586
75952
                &try_catch);
587
  }
588
589
  // args: sandbox, [options]
590
241
  static void RunInContext(const FunctionCallbackInfo<Value>& args) {
591
241
    Environment* env = Environment::GetCurrent(args);
592
593
    int64_t timeout;
594
    bool display_errors;
595
    bool break_on_sigint;
596
597
    // Assemble arguments
598
241
    if (!args[0]->IsObject()) {
599
      return env->ThrowTypeError(
600
35
          "contextifiedSandbox argument must be an object.");
601
    }
602
603
462
    Local<Object> sandbox = args[0].As<Object>();
604
    {
605
462
      TryCatch try_catch(env->isolate());
606
231
      timeout = GetTimeoutArg(env, args[1]);
607
231
      display_errors = GetDisplayErrorsArg(env, args[1]);
608
231
      break_on_sigint = GetBreakOnSigintArg(env, args[1]);
609
231
      if (try_catch.HasCaught()) {
610
        try_catch.ReThrow();
611
        return;
612
      }
613
    }
614
615
    // Get the context from the sandbox
616
    ContextifyContext* contextify_context =
617
231
        ContextifyContext::ContextFromContextifiedSandbox(env, sandbox);
618
231
    if (contextify_context == nullptr) {
619
      return env->ThrowTypeError(
620
          "sandbox argument must have been converted to a context.");
621
    }
622
623
227
    if (contextify_context->context().IsEmpty())
624
      return;
625
626
    {
627
433
      TryCatch try_catch(env->isolate());
628
      // Do the eval within the context
629
660
      Context::Scope context_scope(contextify_context->context());
630
227
      if (EvalMachine(contextify_context->env(),
631
                      timeout,
632
                      display_errors,
633
                      break_on_sigint,
634
                      args,
635
                      &try_catch)) {
636
207
        contextify_context->CopyProperties();
637
      }
638
639
227
      if (try_catch.HasCaught()) {
640
21
        try_catch.ReThrow();
641
21
        return;
642
      }
643
    }
644
  }
645
646
301
  static void DecorateErrorStack(Environment* env, const TryCatch& try_catch) {
647
301
    Local<Value> exception = try_catch.Exception();
648
649
301
    if (!exception->IsObject())
650
      return;
651
652
301
    Local<Object> err_obj = exception.As<Object>();
653
654
301
    if (IsExceptionDecorated(env, err_obj))
655
      return;
656
657
301
    AppendExceptionLine(env, exception, try_catch.Message(), CONTEXTIFY_ERROR);
658
602
    Local<Value> stack = err_obj->Get(env->stack_string());
659
    MaybeLocal<Value> maybe_value =
660
        err_obj->GetPrivate(
661
            env->context(),
662
602
            env->arrow_message_private_symbol());
663
664
301
    Local<Value> arrow;
665

602
    if (!(maybe_value.ToLocal(&arrow) && arrow->IsString())) {
666
      return;
667
    }
668
669

602
    if (stack.IsEmpty() || !stack->IsString()) {
670
      return;
671
    }
672
673
    Local<String> decorated_stack = String::Concat(arrow.As<String>(),
674
602
                                                   stack.As<String>());
675
903
    err_obj->Set(env->stack_string(), decorated_stack);
676
    err_obj->SetPrivate(
677
        env->context(),
678
        env->decorated_private_symbol(),
679
1505
        True(env->isolate()));
680
  }
681
682
76185
  static bool GetBreakOnSigintArg(Environment* env, Local<Value> options) {
683

159157
    if (options->IsUndefined() || options->IsString()) {
684
      return false;
685
    }
686
6772
    if (!options->IsObject()) {
687
      env->ThrowTypeError("options must be an object");
688
      return false;
689
    }
690
691
6772
    Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "breakOnSigint");
692
13544
    Local<Value> value = options.As<Object>()->Get(key);
693
6772
    return value->IsTrue();
694
  }
695
696
76185
  static int64_t GetTimeoutArg(Environment* env, Local<Value> options) {
697

159157
    if (options->IsUndefined() || options->IsString()) {
698
      return -1;
699
    }
700
6772
    if (!options->IsObject()) {
701
      env->ThrowTypeError("options must be an object");
702
      return -1;
703
    }
704
705
6772
    Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "timeout");
706
13544
    Local<Value> value = options.As<Object>()->Get(key);
707
13544
    if (value->IsUndefined()) {
708
      return -1;
709
    }
710
11
    int64_t timeout = value->IntegerValue();
711
712
11
    if (timeout <= 0) {
713
2
      env->ThrowRangeError("timeout must be a positive number");
714
2
      return -1;
715
    }
716
    return timeout;
717
  }
718
719
720
152737
  static bool GetDisplayErrorsArg(Environment* env, Local<Value> options) {
721

388531
    if (options->IsUndefined() || options->IsString()) {
722
      return true;
723
    }
724
83025
    if (!options->IsObject()) {
725
      env->ThrowTypeError("options must be an object");
726
      return false;
727
    }
728
729
83025
    Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "displayErrors");
730
166050
    Local<Value> value = options.As<Object>()->Get(key);
731
732
166050
    return value->IsUndefined() ? true : value->BooleanValue();
733
  }
734
735
736
76552
  static Local<String> GetFilenameArg(Environment* env, Local<Value> options) {
737
    Local<String> defaultFilename =
738
76552
        FIXED_ONE_BYTE_STRING(env->isolate(), "evalmachine.<anonymous>");
739
740
153104
    if (options->IsUndefined()) {
741
282
      return defaultFilename;
742
    }
743
152540
    if (options->IsString()) {
744
      return options.As<String>();
745
    }
746
76253
    if (!options->IsObject()) {
747
      env->ThrowTypeError("options must be an object");
748
      return Local<String>();
749
    }
750
751
76253
    Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "filename");
752
152506
    Local<Value> value = options.As<Object>()->Get(key);
753
754
152506
    if (value->IsUndefined())
755
26
      return defaultFilename;
756
76227
    return value->ToString(env->isolate());
757
  }
758
759
760
76552
  static MaybeLocal<Uint8Array> GetCachedData(Environment* env,
761
                                              Local<Value> options) {
762
76552
    if (!options->IsObject()) {
763
299
      return MaybeLocal<Uint8Array>();
764
    }
765
228759
    Local<Value> value = options.As<Object>()->Get(env->cached_data_string());
766
152506
    if (value->IsUndefined()) {
767
76249
      return MaybeLocal<Uint8Array>();
768
    }
769
770
4
    if (!value->IsUint8Array()) {
771
1
      env->ThrowTypeError("options.cachedData must be a Buffer instance");
772
1
      return MaybeLocal<Uint8Array>();
773
    }
774
775
6
    return value.As<Uint8Array>();
776
  }
777
778
779
76552
  static bool GetProduceCachedData(Environment* env, Local<Value> options) {
780
76552
    if (!options->IsObject()) {
781
      return false;
782
    }
783
    Local<Value> value =
784
228759
        options.As<Object>()->Get(env->produce_cached_data_string());
785
786
76253
    return value->IsTrue();
787
  }
788
789
790
76552
  static Local<Integer> GetLineOffsetArg(Environment* env,
791
                                         Local<Value> options) {
792
76552
    Local<Integer> defaultLineOffset = Integer::New(env->isolate(), 0);
793
794
76552
    if (!options->IsObject()) {
795
299
      return defaultLineOffset;
796
    }
797
798
76253
    Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "lineOffset");
799
152506
    Local<Value> value = options.As<Object>()->Get(key);
800
801
152506
    return value->IsUndefined() ? defaultLineOffset : value->ToInteger();
802
  }
803
804
805
76552
  static Local<Integer> GetColumnOffsetArg(Environment* env,
806
                                           Local<Value> options) {
807
76552
    Local<Integer> defaultColumnOffset = Integer::New(env->isolate(), 0);
808
809
76552
    if (!options->IsObject()) {
810
299
      return defaultColumnOffset;
811
    }
812
813
76253
    Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "columnOffset");
814
152506
    Local<Value> value = options.As<Object>()->Get(key);
815
816
152506
    return value->IsUndefined() ? defaultColumnOffset : value->ToInteger();
817
  }
818
819
820
76179
  static bool EvalMachine(Environment* env,
821
                          const int64_t timeout,
822
                          const bool display_errors,
823
                          const bool break_on_sigint,
824
                          const FunctionCallbackInfo<Value>& args,
825
                          TryCatch* try_catch) {
826
228537
    if (!ContextifyScript::InstanceOf(env, args.Holder())) {
827
      env->ThrowTypeError(
828
          "Script methods can only be called on script instances.");
829
      return false;
830
    }
831
832
    ContextifyScript* wrapped_script;
833
76179
    ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder(), false);
834
    Local<UnboundScript> unbound_script =
835
76179
        PersistentToLocal(env->isolate(), wrapped_script->script_);
836
76179
    Local<Script> script = unbound_script->BindToCurrentContext();
837
838
76179
    Local<Value> result;
839
76179
    bool timed_out = false;
840
76179
    bool received_signal = false;
841
76179
    if (break_on_sigint && timeout != -1) {
842
      Watchdog wd(env->isolate(), timeout);
843
      SigintWatchdog swd(env->isolate());
844
      result = script->Run();
845
      timed_out = wd.HasTimedOut();
846
      received_signal = swd.HasReceivedSignal();
847
76179
    } else if (break_on_sigint) {
848
58
      SigintWatchdog swd(env->isolate());
849
30
      result = script->Run();
850
28
      received_signal = swd.HasReceivedSignal();
851
76149
    } else if (timeout != -1) {
852
18
      Watchdog wd(env->isolate(), timeout);
853
9
      result = script->Run();
854
9
      timed_out = wd.HasTimedOut();
855
    } else {
856
76140
      result = script->Run();
857
    }
858
859
76174
    if (try_catch->HasCaught()) {
860
44
      if (try_catch->HasTerminated())
861
8
        env->isolate()->CancelTerminateExecution();
862
863
      // It is possible that execution was terminated by another timeout in
864
      // which this timeout is nested, so check whether one of the watchdogs
865
      // from this invocation is responsible for termination.
866
44
      if (timed_out) {
867
        env->ThrowError("Script execution timed out.");
868
40
      } else if (received_signal) {
869
        env->ThrowError("Script execution interrupted.");
870
      }
871
872
      // If there was an exception thrown during script execution, re-throw it.
873
      // If one of the above checks threw, re-throw the exception instead of
874
      // letting try_catch catch it.
875
      // If execution has been terminated, but not by one of the watchdogs from
876
      // this invocation, this will re-throw a `null` value.
877
44
      try_catch->ReThrow();
878
879
44
      return false;
880
    }
881
882
76130
    if (result.IsEmpty()) {
883
      // Error occurred during execution of the script.
884
      if (display_errors) {
885
        DecorateErrorStack(env, *try_catch);
886
      }
887
      try_catch->ReThrow();
888
      return false;
889
    }
890
891
76130
    args.GetReturnValue().Set(result);
892
    return true;
893
  }
894
895
896
  ContextifyScript(Environment* env, Local<Object> object)
897
153104
      : BaseObject(env, object) {
898
76552
    MakeWeak<ContextifyScript>(this);
899
  }
900
901
902
358375
  ~ContextifyScript() override {
903
143350
    script_.Reset();
904
143350
  }
905
};
906
907
908
1727
void InitContextify(Local<Object> target,
909
                    Local<Value> unused,
910
                    Local<Context> context) {
911
1727
  Environment* env = Environment::GetCurrent(context);
912
1727
  ContextifyContext::Init(env, target);
913
1727
  ContextifyScript::Init(env, target);
914
1727
}
915
916
}  // namespace node
917
918
1734
NODE_MODULE_CONTEXT_AWARE_BUILTIN(contextify, node::InitContextify);