GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/node-core-coverage/node-core-coverage/workdir/node/out/../src/node_contextify.cc Lines: 366 392 93.4 %
Date: 2016-09-05 Branches: 169 208 81.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
896
  ContextifyContext(Environment* env, Local<Object> sandbox_obj) : env_(env) {
58
448
    Local<Context> v8_context = CreateV8Context(env, sandbox_obj);
59
896
    context_.Reset(env->isolate(), v8_context);
60
61
    // Allocation failure or maximum call stack size reached
62
896
    if (context_.IsEmpty())
63
244
      return;
64
408
    context_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter);
65
408
    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
8112
    return PersistentToLocal(env()->isolate(), context_);
81
  }
82
83
84
1504
  inline Local<Object> global_proxy() const {
85
1504
    return context()->Global();
86
  }
87
88
89
3182
  inline Local<Object> sandbox() const {
90
9546
    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
197
  void CopyProperties() {
115
394
    HandleScope scope(env()->isolate());
116
117
197
    Local<Context> context = PersistentToLocal(env()->isolate(), context_);
118
    Local<Object> global =
119
788
        context->Global()->GetPrototype()->ToObject(env()->isolate());
120
197
    Local<Object> sandbox_obj = sandbox();
121
122
197
    Local<Function> clone_property_method;
123
124
197
    Local<Array> names = global->GetOwnPropertyNames();
125
197
    int length = names->Length();
126
2503
    for (int i = 0; i < length; i++) {
127
4612
      Local<String> key = names->Get(i)->ToString(env()->isolate());
128
6918
      bool has = sandbox_obj->HasOwnProperty(context, key).FromJust();
129
2306
      if (!has) {
130
        // Could also do this like so:
131
        //
132
        // PropertyAttribute att = global->GetPropertyAttributes(key_v);
133
        // Local<Value> val = global->Get(key_v);
134
        // sandbox->ForceSet(key_v, val, att);
135
        //
136
        // However, this doesn't handle ES6-style properties configured with
137
        // Object.defineProperty, and that's exactly what we're up against at
138
        // this point.  ForceSet(key,val,att) only supports value properties
139
        // with the ES3-style attribute flags (DontDelete/DontEnum/ReadOnly),
140
        // which doesn't faithfully capture the full range of configurations
141
        // that can be done using Object.defineProperty.
142
13
        if (clone_property_method.IsEmpty()) {
143
13
          Local<String> code = FIXED_ONE_BYTE_STRING(env()->isolate(),
144
              "(function cloneProperty(source, key, target) {\n"
145
              "  if (key === 'Proxy') return;\n"
146
              "  try {\n"
147
              "    var desc = Object.getOwnPropertyDescriptor(source, key);\n"
148
              "    if (desc.value === source) desc.value = target;\n"
149
              "    Object.defineProperty(target, key, desc);\n"
150
              "  } catch (e) {\n"
151
              "   // Catch sealed properties errors\n"
152
              "  }\n"
153
              "})");
154
155
          Local<Script> script =
156
26
              Script::Compile(context, code).ToLocalChecked();
157
26
          clone_property_method = Local<Function>::Cast(script->Run());
158
13
          CHECK(clone_property_method->IsFunction());
159
        }
160
52
        Local<Value> args[] = { global, key, sandbox_obj };
161
26
        clone_property_method->Call(global, arraysize(args), args);
162
      }
163
    }
164
197
  }
165
166
167
  // This is an object that just keeps an internal pointer to this
168
  // ContextifyContext.  It's passed to the NamedPropertyHandler.  If we
169
  // pass the main JavaScript context object we're embedded in, then the
170
  // NamedPropertyHandler will store a reference to it forever and keep it
171
  // from getting gc'd.
172
448
  Local<Value> CreateDataWrapper(Environment* env) {
173
896
    EscapableHandleScope scope(env->isolate());
174
    Local<Object> wrapper =
175
448
        env->script_data_constructor_function()
176
2240
            ->NewInstance(env->context()).FromMaybe(Local<Object>());
177
448
    if (wrapper.IsEmpty())
178
      return scope.Escape(Local<Value>::New(env->isolate(), Local<Value>()));
179
180
448
    Wrap(wrapper, this);
181
896
    return scope.Escape(wrapper);
182
  }
183
184
185
448
  Local<Context> CreateV8Context(Environment* env, Local<Object> sandbox_obj) {
186
896
    EscapableHandleScope scope(env->isolate());
187
    Local<FunctionTemplate> function_template =
188
896
        FunctionTemplate::New(env->isolate());
189
448
    function_template->SetHiddenPrototype(true);
190
191
448
    function_template->SetClassName(sandbox_obj->GetConstructorName());
192
193
    Local<ObjectTemplate> object_template =
194
448
        function_template->InstanceTemplate();
195
196
    NamedPropertyHandlerConfiguration config(GlobalPropertyGetterCallback,
197
                                             GlobalPropertySetterCallback,
198
                                             GlobalPropertyQueryCallback,
199
                                             GlobalPropertyDeleterCallback,
200
                                             GlobalPropertyEnumeratorCallback,
201
896
                                             CreateDataWrapper(env));
202
448
    object_template->SetHandler(config);
203
204
448
    Local<Context> ctx = Context::New(env->isolate(), nullptr, object_template);
205
206
448
    if (ctx.IsEmpty()) {
207
244
      env->ThrowError("Could not instantiate context");
208
244
      return Local<Context>();
209
    }
210
211
204
    ctx->SetSecurityToken(env->context()->GetSecurityToken());
212
213
    // We need to tie the lifetime of the sandbox object with the lifetime of
214
    // newly created context. We do this by making them hold references to each
215
    // other. The context can directly hold a reference to the sandbox as an
216
    // embedder data field. However, we cannot hold a reference to a v8::Context
217
    // directly in an Object, we instead hold onto the new context's global
218
    // object instead (which then has a reference to the context).
219
204
    ctx->SetEmbedderData(kSandboxObjectIndex, sandbox_obj);
220
    sandbox_obj->SetPrivate(env->context(),
221
                            env->contextify_global_private_symbol(),
222
816
                            ctx->Global());
223
224
204
    env->AssignToContext(ctx);
225
226
    return scope.Escape(ctx);
227
  }
228
229
230
1626
  static void Init(Environment* env, Local<Object> target) {
231
    Local<FunctionTemplate> function_template =
232
3252
        FunctionTemplate::New(env->isolate());
233
3252
    function_template->InstanceTemplate()->SetInternalFieldCount(1);
234
1626
    env->set_script_data_constructor_function(function_template->GetFunction());
235
236
1626
    env->SetMethod(target, "runInDebugContext", RunInDebugContext);
237
1626
    env->SetMethod(target, "makeContext", MakeContext);
238
1626
    env->SetMethod(target, "isContext", IsContext);
239
1626
  }
240
241
242
184
  static void RunInDebugContext(const FunctionCallbackInfo<Value>& args) {
243
368
    Local<String> script_source(args[0]->ToString(args.GetIsolate()));
244
184
    if (script_source.IsEmpty())
245
4
      return;  // Exception pending.
246
183
    Local<Context> debug_context = Debug::GetDebugContext(args.GetIsolate());
247
183
    Environment* env = Environment::GetCurrent(args);
248
183
    if (debug_context.IsEmpty()) {
249
      // Force-load the debug context.
250
362
      Debug::GetMirror(args.GetIsolate()->GetCurrentContext(), args[0]);
251
181
      debug_context = Debug::GetDebugContext(args.GetIsolate());
252
181
      CHECK(!debug_context.IsEmpty());
253
      // Ensure that the debug context has an Environment assigned in case
254
      // a fatal error is raised.  The fatal exception handler in node.cc
255
      // is not equipped to deal with contexts that don't have one and
256
      // can't easily be taught that due to a deficiency in the V8 API:
257
      // there is no way for the embedder to tell if the data index is
258
      // in use.
259
181
      const int index = Environment::kContextEmbedderDataIndex;
260
181
      debug_context->SetAlignedPointerInEmbedderData(index, env);
261
    }
262
263
363
    Context::Scope context_scope(debug_context);
264
183
    MaybeLocal<Script> script = Script::Compile(debug_context, script_source);
265
183
    if (script.IsEmpty())
266
      return;  // Exception pending.
267
540
    args.GetReturnValue().Set(script.ToLocalChecked()->Run());
268
  }
269
270
271
448
  static void MakeContext(const FunctionCallbackInfo<Value>& args) {
272
448
    Environment* env = Environment::GetCurrent(args);
273
274
448
    if (!args[0]->IsObject()) {
275
244
      return env->ThrowTypeError("sandbox argument must be an object.");
276
    }
277
896
    Local<Object> sandbox = args[0].As<Object>();
278
279
    // Don't allow contextifying a sandbox multiple times.
280
1344
    CHECK(
281
        !sandbox->HasPrivate(
282
            env->context(),
283
            env->contextify_context_private_symbol()).FromJust());
284
285
652
    TryCatch try_catch(env->isolate());
286
448
    ContextifyContext* context = new ContextifyContext(env, sandbox);
287
288
448
    if (try_catch.HasCaught()) {
289
244
      try_catch.ReThrow();
290
244
      return;
291
    }
292
293
204
    if (context->context().IsEmpty())
294
      return;
295
296
    sandbox->SetPrivate(
297
        env->context(),
298
        env->contextify_context_private_symbol(),
299
816
        External::New(env->isolate(), context));
300
  }
301
302
303
74
  static void IsContext(const FunctionCallbackInfo<Value>& args) {
304
74
    Environment* env = Environment::GetCurrent(args);
305
306
74
    if (!args[0]->IsObject()) {
307
3
      env->ThrowTypeError("sandbox must be an object");
308
3
      return;
309
    }
310
142
    Local<Object> sandbox = args[0].As<Object>();
311
312
    auto result =
313
        sandbox->HasPrivate(env->context(),
314
142
                            env->contextify_context_private_symbol());
315
213
    args.GetReturnValue().Set(result.FromJust());
316
  }
317
318
319
6
  static void WeakCallback(const WeakCallbackInfo<ContextifyContext>& data) {
320
6
    ContextifyContext* context = data.GetParameter();
321
12
    delete context;
322
6
  }
323
324
325
220
  static ContextifyContext* ContextFromContextifiedSandbox(
326
      Environment* env,
327
      const Local<Object>& sandbox) {
328
    auto maybe_value =
329
        sandbox->GetPrivate(env->context(),
330
660
                            env->contextify_context_private_symbol());
331
220
    Local<Value> context_external_v;
332

440
    if (maybe_value.ToLocal(&context_external_v) &&
333
220
        context_external_v->IsExternal()) {
334
216
      Local<External> context_external = context_external_v.As<External>();
335
216
      return static_cast<ContextifyContext*>(context_external->Value());
336
    }
337
    return nullptr;
338
  }
339
340
341
7680
  static void GlobalPropertyGetterCallback(
342
      Local<Name> property,
343
      const PropertyCallbackInfo<Value>& args) {
344
    ContextifyContext* ctx;
345
20960
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
346
347
    // Stil initializing
348
15360
    if (ctx->context_.IsEmpty())
349
      return;
350
351
4160
    Local<Context> context = ctx->context();
352
2080
    Local<Object> sandbox = ctx->sandbox();
353
    MaybeLocal<Value> maybe_rv =
354
2080
        sandbox->GetRealNamedProperty(context, property);
355
2080
    if (maybe_rv.IsEmpty()) {
356
      maybe_rv =
357
204
          ctx->global_proxy()->GetRealNamedProperty(context, property);
358
    }
359
360
2080
    Local<Value> rv;
361
2080
    if (maybe_rv.ToLocal(&rv)) {
362
2062
      if (rv == sandbox)
363
6
        rv = ctx->global_proxy();
364
365
2062
      args.GetReturnValue().Set(rv);
366
    }
367
  }
368
369
370
703
  static void GlobalPropertySetterCallback(
371
      Local<Name> property,
372
      Local<Value> value,
373
      const PropertyCallbackInfo<Value>& args) {
374
    ContextifyContext* ctx;
375
1406
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
376
377
    // Stil initializing
378
1406
    if (ctx->context_.IsEmpty())
379
      return;
380
381
    bool is_declared =
382
2079
        ctx->global_proxy()->HasRealNamedProperty(ctx->context(),
383
2772
                                                  property).FromJust();
384
1386
    bool is_contextual_store = ctx->global_proxy() != args.This();
385
386
    bool set_property_will_throw =
387
693
        args.ShouldThrowOnError() &&
388

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

208497
    return !value.IsEmpty() &&
556
138998
           env->script_context_constructor_template()->HasInstance(value);
557
  }
558
559
560
  // args: [options]
561
69285
  static void RunInThisContext(const FunctionCallbackInfo<Value>& args) {
562
    // Assemble arguments
563
138564
    TryCatch try_catch(args.GetIsolate());
564
69285
    uint64_t timeout = GetTimeoutArg(args, 0);
565
69285
    bool display_errors = GetDisplayErrorsArg(args, 0);
566
69285
    bool break_on_sigint = GetBreakOnSigintArg(args, 0);
567
69285
    if (try_catch.HasCaught()) {
568
2
      try_catch.ReThrow();
569
2
      return;
570
    }
571
572
    // Do the eval within this context
573
69283
    Environment* env = Environment::GetCurrent(args);
574
69283
    EvalMachine(env, timeout, display_errors, break_on_sigint, args,
575
69283
                &try_catch);
576
  }
577
578
  // args: sandbox, [options]
579
230
  static void RunInContext(const FunctionCallbackInfo<Value>& args) {
580
230
    Environment* env = Environment::GetCurrent(args);
581
582
    int64_t timeout;
583
    bool display_errors;
584
    bool break_on_sigint;
585
586
    // Assemble arguments
587
230
    if (!args[0]->IsObject()) {
588
      return env->ThrowTypeError(
589
33
          "contextifiedSandbox argument must be an object.");
590
    }
591
592
440
    Local<Object> sandbox = args[0].As<Object>();
593
    {
594
440
      TryCatch try_catch(env->isolate());
595
220
      timeout = GetTimeoutArg(args, 1);
596
220
      display_errors = GetDisplayErrorsArg(args, 1);
597
220
      break_on_sigint = GetBreakOnSigintArg(args, 1);
598
220
      if (try_catch.HasCaught()) {
599
        try_catch.ReThrow();
600
        return;
601
      }
602
    }
603
604
    // Get the context from the sandbox
605
    ContextifyContext* contextify_context =
606
220
        ContextifyContext::ContextFromContextifiedSandbox(env, sandbox);
607
220
    if (contextify_context == nullptr) {
608
      return env->ThrowTypeError(
609
          "sandbox argument must have been converted to a context.");
610
    }
611
612
216
    if (contextify_context->context().IsEmpty())
613
      return;
614
615
    {
616
413
      TryCatch try_catch(env->isolate());
617
      // Do the eval within the context
618
629
      Context::Scope context_scope(contextify_context->context());
619
216
      if (EvalMachine(contextify_context->env(),
620
                      timeout,
621
                      display_errors,
622
                      break_on_sigint,
623
                      args,
624
                      &try_catch)) {
625
197
        contextify_context->CopyProperties();
626
      }
627
628
216
      if (try_catch.HasCaught()) {
629
19
        try_catch.ReThrow();
630
19
        return;
631
      }
632
    }
633
  }
634
635
334
  static void DecorateErrorStack(Environment* env, const TryCatch& try_catch) {
636
334
    Local<Value> exception = try_catch.Exception();
637
638
334
    if (!exception->IsObject())
639
168
      return;
640
641
334
    Local<Object> err_obj = exception.As<Object>();
642
643
334
    if (IsExceptionDecorated(env, err_obj))
644
      return;
645
646
334
    AppendExceptionLine(env, exception, try_catch.Message(), CONTEXTIFY_ERROR);
647
668
    Local<Value> stack = err_obj->Get(env->stack_string());
648
    auto maybe_value =
649
        err_obj->GetPrivate(
650
            env->context(),
651
668
            env->arrow_message_private_symbol());
652
653
334
    Local<Value> arrow;
654

668
    if (!(maybe_value.ToLocal(&arrow) && arrow->IsString())) {
655
      return;
656
    }
657
658

500
    if (stack.IsEmpty() || !stack->IsString()) {
659
      return;
660
    }
661
662
    Local<String> decorated_stack = String::Concat(arrow.As<String>(),
663
332
                                                   stack.As<String>());
664
498
    err_obj->Set(env->stack_string(), decorated_stack);
665
    err_obj->SetPrivate(
666
        env->context(),
667
        env->decorated_private_symbol(),
668
830
        True(env->isolate()));
669
  }
670
671
69505
  static bool GetBreakOnSigintArg(const FunctionCallbackInfo<Value>& args,
672
                                  const int i) {
673

220833
    if (args[i]->IsUndefined() || args[i]->IsString()) {
674
      return false;
675
    }
676
6144
    if (!args[i]->IsObject()) {
677
      Environment::ThrowTypeError(args.GetIsolate(),
678
                                  "options must be an object");
679
      return false;
680
    }
681
682
12288
    Local<String> key = FIXED_ONE_BYTE_STRING(args.GetIsolate(),
683
                                              "breakOnSigint");
684
18432
    Local<Value> value = args[i].As<Object>()->Get(key);
685
6144
    return value->IsTrue();
686
  }
687
688
69505
  static int64_t GetTimeoutArg(const FunctionCallbackInfo<Value>& args,
689
                               const int i) {
690

220833
    if (args[i]->IsUndefined() || args[i]->IsString()) {
691
      return -1;
692
    }
693
6144
    if (!args[i]->IsObject()) {
694
      Environment::ThrowTypeError(args.GetIsolate(),
695
                                  "options must be an object");
696
      return -1;
697
    }
698
699
12288
    Local<String> key = FIXED_ONE_BYTE_STRING(args.GetIsolate(), "timeout");
700
18432
    Local<Value> value = args[i].As<Object>()->Get(key);
701
12288
    if (value->IsUndefined()) {
702
      return -1;
703
    }
704
11
    int64_t timeout = value->IntegerValue();
705
706
11
    if (timeout <= 0) {
707
2
      Environment::ThrowRangeError(args.GetIsolate(),
708
2
                                   "timeout must be a positive number");
709
2
      return -1;
710
    }
711
    return timeout;
712
  }
713
714
715
139610
  static bool GetDisplayErrorsArg(const FunctionCallbackInfo<Value>& args,
716
                                  const int i) {
717

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