GCC Code Coverage Report
Directory: ../src/ Exec Total Coverage
File: /home/node-core-coverage/node-core-coverage/workdir/node/src/node_contextify.cc Lines: 359 385 93.2 %
Date: 2016-07-23 Branches: 163 202 80.7 %

Line 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::Isolate;
27
using v8::Local;
28
using v8::Maybe;
29
using v8::MaybeLocal;
30
using v8::Name;
31
using v8::NamedPropertyHandlerConfiguration;
32
using v8::Object;
33
using v8::ObjectTemplate;
34
using v8::Persistent;
35
using v8::PropertyAttribute;
36
using v8::PropertyCallbackInfo;
37
using v8::Script;
38
using v8::ScriptCompiler;
39
using v8::ScriptOrigin;
40
using v8::String;
41
using v8::TryCatch;
42
using v8::Uint8Array;
43
using v8::UnboundScript;
44
using v8::V8;
45
using v8::Value;
46
using v8::WeakCallbackInfo;
47
48
49
class ContextifyContext {
50
 protected:
51
  // V8 reserves the first field in context objects for the debugger. We use the
52
  // second field to hold a reference to the sandbox object.
53
  enum { kSandboxObjectIndex = 1 };
54
55
  Environment* const env_;
56
  Persistent<Context> context_;
57
58
 public:
59
940
  ContextifyContext(Environment* env, Local<Object> sandbox_obj) : env_(env) {
60
470
    Local<Context> v8_context = CreateV8Context(env, sandbox_obj);
61
940
    context_.Reset(env->isolate(), v8_context);
62
63
    // Allocation failure or maximum call stack size reached
64
940
    if (context_.IsEmpty())
65
259
      return;
66
422
    context_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter);
67
422
    context_.MarkIndependent();
68
  }
69
70
71
12
  ~ContextifyContext() {
72
12
    context_.Reset();
73
  }
74
75
76
  inline Environment* env() const {
77
    return env_;
78
  }
79
80
81
  inline Local<Context> context() const {
82
6906
    return PersistentToLocal(env()->isolate(), context_);
83
  }
84
85
86
115
  inline Local<Object> global_proxy() const {
87
115
    return context()->Global();
88
  }
89
90
91
3680
  inline Local<Object> sandbox() const {
92
11040
    return Local<Object>::Cast(context()->GetEmbedderData(kSandboxObjectIndex));
93
  }
94
95
  // XXX(isaacs): This function only exists because of a shortcoming of
96
  // the V8 SetNamedPropertyHandler function.
97
  //
98
  // It does not provide a way to intercept Object.defineProperty(..)
99
  // calls.  As a result, these properties are not copied onto the
100
  // contextified sandbox when a new global property is added via either
101
  // a function declaration or a Object.defineProperty(global, ...) call.
102
  //
103
  // Note that any function declarations or Object.defineProperty()
104
  // globals that are created asynchronously (in a setTimeout, callback,
105
  // etc.) will happen AFTER the call to copy properties, and thus not be
106
  // caught.
107
  //
108
  // The way to properly fix this is to add some sort of a
109
  // Object::SetNamedDefinePropertyHandler() function that takes a callback,
110
  // which receives the property name and property descriptor as arguments.
111
  //
112
  // Luckily, such situations are rare, and asynchronously-added globals
113
  // weren't supported by Node's VM module until 0.12 anyway.  But, this
114
  // should be fixed properly in V8, and this copy function should be
115
  // removed once there is a better way.
116
186
  void CopyProperties() {
117
372
    HandleScope scope(env()->isolate());
118
119
186
    Local<Context> context = PersistentToLocal(env()->isolate(), context_);
120
    Local<Object> global =
121
744
        context->Global()->GetPrototype()->ToObject(env()->isolate());
122
186
    Local<Object> sandbox_obj = sandbox();
123
124
186
    Local<Function> clone_property_method;
125
126
186
    Local<Array> names = global->GetOwnPropertyNames();
127
186
    int length = names->Length();
128
2378
    for (int i = 0; i < length; i++) {
129
4384
      Local<String> key = names->Get(i)->ToString(env()->isolate());
130
6576
      bool has = sandbox_obj->HasOwnProperty(context, key).FromJust();
131
2192
      if (!has) {
132
        // Could also do this like so:
133
        //
134
        // PropertyAttribute att = global->GetPropertyAttributes(key_v);
135
        // Local<Value> val = global->Get(key_v);
136
        // sandbox->ForceSet(key_v, val, att);
137
        //
138
        // However, this doesn't handle ES6-style properties configured with
139
        // Object.defineProperty, and that's exactly what we're up against at
140
        // this point.  ForceSet(key,val,att) only supports value properties
141
        // with the ES3-style attribute flags (DontDelete/DontEnum/ReadOnly),
142
        // which doesn't faithfully capture the full range of configurations
143
        // that can be done using Object.defineProperty.
144
13
        if (clone_property_method.IsEmpty()) {
145
13
          Local<String> code = FIXED_ONE_BYTE_STRING(env()->isolate(),
146
              "(function cloneProperty(source, key, target) {\n"
147
              "  if (key === 'Proxy') return;\n"
148
              "  try {\n"
149
              "    var desc = Object.getOwnPropertyDescriptor(source, key);\n"
150
              "    if (desc.value === source) desc.value = target;\n"
151
              "    Object.defineProperty(target, key, desc);\n"
152
              "  } catch (e) {\n"
153
              "   // Catch sealed properties errors\n"
154
              "  }\n"
155
              "})");
156
157
          Local<Script> script =
158
26
              Script::Compile(context, code).ToLocalChecked();
159
26
          clone_property_method = Local<Function>::Cast(script->Run());
160
13
          CHECK(clone_property_method->IsFunction());
161
        }
162
52
        Local<Value> args[] = { global, key, sandbox_obj };
163
26
        clone_property_method->Call(global, arraysize(args), args);
164
      }
165
    }
166
186
  }
167
168
169
  // This is an object that just keeps an internal pointer to this
170
  // ContextifyContext.  It's passed to the NamedPropertyHandler.  If we
171
  // pass the main JavaScript context object we're embedded in, then the
172
  // NamedPropertyHandler will store a reference to it forever and keep it
173
  // from getting gc'd.
174
470
  Local<Value> CreateDataWrapper(Environment* env) {
175
940
    EscapableHandleScope scope(env->isolate());
176
    Local<Object> wrapper =
177
470
        env->script_data_constructor_function()
178
2350
            ->NewInstance(env->context()).FromMaybe(Local<Object>());
179
470
    if (wrapper.IsEmpty())
180
      return scope.Escape(Local<Value>::New(env->isolate(), Local<Value>()));
181
182
470
    Wrap(wrapper, this);
183
940
    return scope.Escape(wrapper);
184
  }
185
186
187
470
  Local<Context> CreateV8Context(Environment* env, Local<Object> sandbox_obj) {
188
940
    EscapableHandleScope scope(env->isolate());
189
    Local<FunctionTemplate> function_template =
190
940
        FunctionTemplate::New(env->isolate());
191
470
    function_template->SetHiddenPrototype(true);
192
193
470
    function_template->SetClassName(sandbox_obj->GetConstructorName());
194
195
    Local<ObjectTemplate> object_template =
196
470
        function_template->InstanceTemplate();
197
198
    NamedPropertyHandlerConfiguration config(GlobalPropertyGetterCallback,
199
                                             GlobalPropertySetterCallback,
200
                                             GlobalPropertyQueryCallback,
201
                                             GlobalPropertyDeleterCallback,
202
                                             GlobalPropertyEnumeratorCallback,
203
940
                                             CreateDataWrapper(env));
204
470
    object_template->SetHandler(config);
205
206
470
    Local<Context> ctx = Context::New(env->isolate(), nullptr, object_template);
207
208
470
    if (ctx.IsEmpty()) {
209
259
      env->ThrowError("Could not instantiate context");
210
259
      return Local<Context>();
211
    }
212
213
211
    ctx->SetSecurityToken(env->context()->GetSecurityToken());
214
215
    // We need to tie the lifetime of the sandbox object with the lifetime of
216
    // newly created context. We do this by making them hold references to each
217
    // other. The context can directly hold a reference to the sandbox as an
218
    // embedder data field. However, we cannot hold a reference to a v8::Context
219
    // directly in an Object, we instead hold onto the new context's global
220
    // object instead (which then has a reference to the context).
221
211
    ctx->SetEmbedderData(kSandboxObjectIndex, sandbox_obj);
222
    sandbox_obj->SetPrivate(env->context(),
223
                            env->contextify_global_private_symbol(),
224
844
                            ctx->Global());
225
226
211
    env->AssignToContext(ctx);
227
228
    return scope.Escape(ctx);
229
  }
230
231
232
1563
  static void Init(Environment* env, Local<Object> target) {
233
    Local<FunctionTemplate> function_template =
234
3126
        FunctionTemplate::New(env->isolate());
235
3126
    function_template->InstanceTemplate()->SetInternalFieldCount(1);
236
1563
    env->set_script_data_constructor_function(function_template->GetFunction());
237
238
1563
    env->SetMethod(target, "runInDebugContext", RunInDebugContext);
239
1563
    env->SetMethod(target, "makeContext", MakeContext);
240
1563
    env->SetMethod(target, "isContext", IsContext);
241
1563
  }
242
243
244
174
  static void RunInDebugContext(const FunctionCallbackInfo<Value>& args) {
245
348
    Local<String> script_source(args[0]->ToString(args.GetIsolate()));
246
174
    if (script_source.IsEmpty())
247
4
      return;  // Exception pending.
248
173
    Local<Context> debug_context = Debug::GetDebugContext(args.GetIsolate());
249
173
    Environment* env = Environment::GetCurrent(args);
250
173
    if (debug_context.IsEmpty()) {
251
      // Force-load the debug context.
252
342
      Debug::GetMirror(args.GetIsolate()->GetCurrentContext(), args[0]);
253
171
      debug_context = Debug::GetDebugContext(args.GetIsolate());
254
171
      CHECK(!debug_context.IsEmpty());
255
      // Ensure that the debug context has an Environment assigned in case
256
      // a fatal error is raised.  The fatal exception handler in node.cc
257
      // is not equipped to deal with contexts that don't have one and
258
      // can't easily be taught that due to a deficiency in the V8 API:
259
      // there is no way for the embedder to tell if the data index is
260
      // in use.
261
171
      const int index = Environment::kContextEmbedderDataIndex;
262
171
      debug_context->SetAlignedPointerInEmbedderData(index, env);
263
    }
264
265
343
    Context::Scope context_scope(debug_context);
266
173
    MaybeLocal<Script> script = Script::Compile(debug_context, script_source);
267
173
    if (script.IsEmpty())
268
      return;  // Exception pending.
269
510
    args.GetReturnValue().Set(script.ToLocalChecked()->Run());
270
  }
271
272
273
470
  static void MakeContext(const FunctionCallbackInfo<Value>& args) {
274
470
    Environment* env = Environment::GetCurrent(args);
275
276
470
    if (!args[0]->IsObject()) {
277
259
      return env->ThrowTypeError("sandbox argument must be an object.");
278
    }
279
940
    Local<Object> sandbox = args[0].As<Object>();
280
281
    // Don't allow contextifying a sandbox multiple times.
282
1410
    CHECK(
283
        !sandbox->HasPrivate(
284
            env->context(),
285
            env->contextify_context_private_symbol()).FromJust());
286
287
681
    TryCatch try_catch(env->isolate());
288
470
    ContextifyContext* context = new ContextifyContext(env, sandbox);
289
290
470
    if (try_catch.HasCaught()) {
291
259
      try_catch.ReThrow();
292
259
      return;
293
    }
294
295
211
    if (context->context().IsEmpty())
296
      return;
297
298
    sandbox->SetPrivate(
299
        env->context(),
300
        env->contextify_context_private_symbol(),
301
844
        External::New(env->isolate(), context));
302
  }
303
304
305
73
  static void IsContext(const FunctionCallbackInfo<Value>& args) {
306
73
    Environment* env = Environment::GetCurrent(args);
307
308
73
    if (!args[0]->IsObject()) {
309
3
      env->ThrowTypeError("sandbox must be an object");
310
3
      return;
311
    }
312
140
    Local<Object> sandbox = args[0].As<Object>();
313
314
    auto result =
315
        sandbox->HasPrivate(env->context(),
316
140
                            env->contextify_context_private_symbol());
317
210
    args.GetReturnValue().Set(result.FromJust());
318
  }
319
320
321
6
  static void WeakCallback(const WeakCallbackInfo<ContextifyContext>& data) {
322
6
    ContextifyContext* context = data.GetParameter();
323
12
    delete context;
324
6
  }
325
326
327
207
  static ContextifyContext* ContextFromContextifiedSandbox(
328
      Environment* env,
329
      const Local<Object>& sandbox) {
330
    auto maybe_value =
331
        sandbox->GetPrivate(env->context(),
332
621
                            env->contextify_context_private_symbol());
333
207
    Local<Value> context_external_v;
334
414
    if (maybe_value.ToLocal(&context_external_v) &&
335
207
        context_external_v->IsExternal()) {
336
203
      Local<External> context_external = context_external_v.As<External>();
337
203
      return static_cast<ContextifyContext*>(context_external->Value());
338
    }
339
    return nullptr;
340
  }
341
342
343
8257
  static void GlobalPropertyGetterCallback(
344
      Local<Name> property,
345
      const PropertyCallbackInfo<Value>& args) {
346
    ContextifyContext* ctx;
347
22294
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
348
349
    // Stil initializing
350
16514
    if (ctx->context_.IsEmpty())
351
      return;
352
353
4954
    Local<Context> context = ctx->context();
354
2477
    Local<Object> sandbox = ctx->sandbox();
355
    MaybeLocal<Value> maybe_rv =
356
2477
        sandbox->GetRealNamedProperty(context, property);
357
2477
    if (maybe_rv.IsEmpty()) {
358
      maybe_rv =
359
198
          ctx->global_proxy()->GetRealNamedProperty(context, property);
360
    }
361
362
2477
    Local<Value> rv;
363
2477
    if (maybe_rv.ToLocal(&rv)) {
364
2462
      if (rv == sandbox)
365
6
        rv = ctx->global_proxy();
366
367
2462
      args.GetReturnValue().Set(rv);
368
    }
369
  }
370
371
372
824
  static void GlobalPropertySetterCallback(
373
      Local<Name> property,
374
      Local<Value> value,
375
      const PropertyCallbackInfo<Value>& args) {
376
    ContextifyContext* ctx;
377
1648
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
378
379
    // Stil initializing
380
1648
    if (ctx->context_.IsEmpty())
381
      return;
382
383
1628
    ctx->sandbox()->Set(property, value);
384
  }
385
386
387
16
  static void GlobalPropertyQueryCallback(
388
      Local<Name> property,
389
      const PropertyCallbackInfo<Integer>& args) {
390
    ContextifyContext* ctx;
391
32
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
392
393
    // Stil initializing
394
32
    if (ctx->context_.IsEmpty())
395
      return;
396
397
32
    Local<Context> context = ctx->context();
398
    Maybe<PropertyAttribute> maybe_prop_attr =
399
32
        ctx->sandbox()->GetRealNamedPropertyAttributes(context, property);
400
401
16
    if (maybe_prop_attr.IsNothing()) {
402
      maybe_prop_attr =
403
26
          ctx->global_proxy()->GetRealNamedPropertyAttributes(context,
404
13
                                                              property);
405
    }
406
407
16
    if (maybe_prop_attr.IsJust()) {
408
15
      PropertyAttribute prop_attr = maybe_prop_attr.FromJust();
409
45
      args.GetReturnValue().Set(prop_attr);
410
    }
411
  }
412
413
414
1
  static void GlobalPropertyDeleterCallback(
415
      Local<Name> property,
416
      const PropertyCallbackInfo<Boolean>& args) {
417
    ContextifyContext* ctx;
418
2
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
419
420
    // Stil initializing
421
2
    if (ctx->context_.IsEmpty())
422
      return;
423
424
3
    Maybe<bool> success = ctx->sandbox()->Delete(ctx->context(), property);
425
426
1
    if (success.IsJust())
427
3
      args.GetReturnValue().Set(success.FromJust());
428
  }
429
430
431
186
  static void GlobalPropertyEnumeratorCallback(
432
      const PropertyCallbackInfo<Array>& args) {
433
    ContextifyContext* ctx;
434
372
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
435
436
    // Stil initializing
437
372
    if (ctx->context_.IsEmpty())
438
      return;
439
440
744
    args.GetReturnValue().Set(ctx->sandbox()->GetPropertyNames());
441
  }
442
};
443
444
class ContextifyScript : public BaseObject {
445
 private:
446
  Persistent<UnboundScript> script_;
447
448
 public:
449
1563
  static void Init(Environment* env, Local<Object> target) {
450
3126
    HandleScope scope(env->isolate());
451
    Local<String> class_name =
452
1563
        FIXED_ONE_BYTE_STRING(env->isolate(), "ContextifyScript");
453
454
3126
    Local<FunctionTemplate> script_tmpl = env->NewFunctionTemplate(New);
455
3126
    script_tmpl->InstanceTemplate()->SetInternalFieldCount(1);
456
1563
    script_tmpl->SetClassName(class_name);
457
1563
    env->SetProtoMethod(script_tmpl, "runInContext", RunInContext);
458
1563
    env->SetProtoMethod(script_tmpl, "runInThisContext", RunInThisContext);
459
460
6252
    target->Set(class_name, script_tmpl->GetFunction());
461
1563
    env->set_script_context_constructor_template(script_tmpl);
462
1563
  }
463
464
465
  // args: code, [options]
466
64112
  static void New(const FunctionCallbackInfo<Value>& args) {
467
64112
    Environment* env = Environment::GetCurrent(args);
468
469
64112
    if (!args.IsConstructCall()) {
470
330
      return env->ThrowError("Must call vm.Script as a constructor.");
471
    }
472
473
    ContextifyScript* contextify_script =
474
128224
        new ContextifyScript(env, args.This());
475
476
127894
    TryCatch try_catch(env->isolate());
477
128224
    Local<String> code = args[0]->ToString(env->isolate());
478
64112
    Local<String> filename = GetFilenameArg(args, 1);
479
64112
    Local<Integer> lineOffset = GetLineOffsetArg(args, 1);
480
64112
    Local<Integer> columnOffset = GetColumnOffsetArg(args, 1);
481
64112
    bool display_errors = GetDisplayErrorsArg(args, 1);
482
64112
    MaybeLocal<Uint8Array> cached_data_buf = GetCachedData(env, args, 1);
483
64112
    bool produce_cached_data = GetProduceCachedData(env, args, 1);
484
64112
    if (try_catch.HasCaught()) {
485
1
      try_catch.ReThrow();
486
1
      return;
487
    }
488
489
64111
    ScriptCompiler::CachedData* cached_data = nullptr;
490
64111
    if (!cached_data_buf.IsEmpty()) {
491
3
      Local<Uint8Array> ui8 = cached_data_buf.ToLocalChecked();
492
6
      ArrayBuffer::Contents contents = ui8->Buffer()->GetContents();
493
      cached_data = new ScriptCompiler::CachedData(
494
3
          static_cast<uint8_t*>(contents.Data()) + ui8->ByteOffset(),
495
3
          ui8->ByteLength());
496
    }
497
498
192333
    ScriptOrigin origin(filename, lineOffset, columnOffset);
499
127893
    ScriptCompiler::Source source(code, origin, cached_data);
500
    ScriptCompiler::CompileOptions compile_options =
501
64111
        ScriptCompiler::kNoCompileOptions;
502
503
64111
    if (source.GetCachedData() != nullptr)
504
      compile_options = ScriptCompiler::kConsumeCodeCache;
505
64108
    else if (produce_cached_data)
506
6
      compile_options = ScriptCompiler::kProduceCodeCache;
507
508
    MaybeLocal<UnboundScript> v8_script = ScriptCompiler::CompileUnboundScript(
509
        env->isolate(),
510
        &source,
511
64111
        compile_options);
512
513
64111
    if (v8_script.IsEmpty()) {
514
329
      if (display_errors) {
515
187
        DecorateErrorStack(env, try_catch);
516
      }
517
329
      try_catch.ReThrow();
518
      return;
519
    }
520
63782
    contextify_script->script_.Reset(env->isolate(),
521
127564
                                     v8_script.ToLocalChecked());
522
523
63782
    if (compile_options == ScriptCompiler::kConsumeCodeCache) {
524
21
      args.This()->Set(
525
          env->cached_data_rejected_string(),
526
6
          Boolean::New(env->isolate(), source.GetCachedData()->rejected));
527
63779
    } else if (compile_options == ScriptCompiler::kProduceCodeCache) {
528
6
      const ScriptCompiler::CachedData* cached_data = source.GetCachedData();
529
6
      bool cached_data_produced = cached_data != nullptr;
530
6
      if (cached_data_produced) {
531
        MaybeLocal<Object> buf = Buffer::Copy(
532
            env,
533
5
            reinterpret_cast<const char*>(cached_data->data),
534
10
            cached_data->length);
535
25
        args.This()->Set(env->cached_data_string(), buf.ToLocalChecked());
536
      }
537
42
      args.This()->Set(
538
          env->cached_data_produced_string(),
539
6
          Boolean::New(env->isolate(), cached_data_produced));
540
    }
541
  }
542
543
544
  static bool InstanceOf(Environment* env, const Local<Value>& value) {
545
190425
    return !value.IsEmpty() &&
546
126950
           env->script_context_constructor_template()->HasInstance(value);
547
  }
548
549
550
  // args: [options]
551
63274
  static void RunInThisContext(const FunctionCallbackInfo<Value>& args) {
552
    // Assemble arguments
553
126542
    TryCatch try_catch(args.GetIsolate());
554
63274
    uint64_t timeout = GetTimeoutArg(args, 0);
555
63274
    bool display_errors = GetDisplayErrorsArg(args, 0);
556
63274
    bool break_on_sigint = GetBreakOnSigintArg(args, 0);
557
63274
    if (try_catch.HasCaught()) {
558
2
      try_catch.ReThrow();
559
2
      return;
560
    }
561
562
    // Do the eval within this context
563
63272
    Environment* env = Environment::GetCurrent(args);
564
63272
    EvalMachine(env, timeout, display_errors, break_on_sigint, args,
565
63272
                &try_catch);
566
  }
567
568
  // args: sandbox, [options]
569
217
  static void RunInContext(const FunctionCallbackInfo<Value>& args) {
570
217
    Environment* env = Environment::GetCurrent(args);
571
572
    int64_t timeout;
573
    bool display_errors;
574
    bool break_on_sigint;
575
576
    // Assemble arguments
577
217
    if (!args[0]->IsObject()) {
578
      return env->ThrowTypeError(
579
31
          "contextifiedSandbox argument must be an object.");
580
    }
581
582
414
    Local<Object> sandbox = args[0].As<Object>();
583
    {
584
414
      TryCatch try_catch(env->isolate());
585
207
      timeout = GetTimeoutArg(args, 1);
586
207
      display_errors = GetDisplayErrorsArg(args, 1);
587
207
      break_on_sigint = GetBreakOnSigintArg(args, 1);
588
207
      if (try_catch.HasCaught()) {
589
        try_catch.ReThrow();
590
        return;
591
      }
592
    }
593
594
    // Get the context from the sandbox
595
    ContextifyContext* contextify_context =
596
207
        ContextifyContext::ContextFromContextifiedSandbox(env, sandbox);
597
207
    if (contextify_context == nullptr) {
598
      return env->ThrowTypeError(
599
          "sandbox argument must have been converted to a context.");
600
    }
601
602
203
    if (contextify_context->context().IsEmpty())
603
      return;
604
605
    {
606
389
      TryCatch try_catch(env->isolate());
607
      // Do the eval within the context
608
592
      Context::Scope context_scope(contextify_context->context());
609
203
      if (EvalMachine(contextify_context->env(),
610
                      timeout,
611
                      display_errors,
612
                      break_on_sigint,
613
                      args,
614
                      &try_catch)) {
615
186
        contextify_context->CopyProperties();
616
      }
617
618
203
      if (try_catch.HasCaught()) {
619
17
        try_catch.ReThrow();
620
17
        return;
621
      }
622
    }
623
  }
624
625
187
  static void DecorateErrorStack(Environment* env, const TryCatch& try_catch) {
626
187
    Local<Value> exception = try_catch.Exception();
627
628
187
    if (!exception->IsObject())
629
168
      return;
630
631
187
    Local<Object> err_obj = exception.As<Object>();
632
633
187
    if (IsExceptionDecorated(env, err_obj))
634
      return;
635
636
187
    AppendExceptionLine(env, exception, try_catch.Message(), CONTEXTIFY_ERROR);
637
374
    Local<Value> stack = err_obj->Get(env->stack_string());
638
    auto maybe_value =
639
        err_obj->GetPrivate(
640
            env->context(),
641
374
            env->arrow_message_private_symbol());
642
643
187
    Local<Value> arrow;
644
374
    if (!(maybe_value.ToLocal(&arrow) && arrow->IsString())) {
645
      return;
646
    }
647
648
206
    if (stack.IsEmpty() || !stack->IsString()) {
649
      return;
650
    }
651
652
    Local<String> decorated_stack = String::Concat(arrow.As<String>(),
653
38
                                                   stack.As<String>());
654
57
    err_obj->Set(env->stack_string(), decorated_stack);
655
    err_obj->SetPrivate(
656
        env->context(),
657
        env->decorated_private_symbol(),
658
95
        True(env->isolate()));
659
  }
660
661
63481
  static bool GetBreakOnSigintArg(const FunctionCallbackInfo<Value>& args,
662
                                  const int i) {
663
198529
    if (args[i]->IsUndefined() || args[i]->IsString()) {
664
      return false;
665
    }
666
4028
    if (!args[i]->IsObject()) {
667
      Environment::ThrowTypeError(args.GetIsolate(),
668
                                  "options must be an object");
669
      return false;
670
    }
671
672
8056
    Local<String> key = FIXED_ONE_BYTE_STRING(args.GetIsolate(),
673
                                              "breakOnSigint");
674
12084
    Local<Value> value = args[i].As<Object>()->Get(key);
675
4028
    return value->IsTrue();
676
  }
677
678
63481
  static int64_t GetTimeoutArg(const FunctionCallbackInfo<Value>& args,
679
                               const int i) {
680
198529
    if (args[i]->IsUndefined() || args[i]->IsString()) {
681
      return -1;
682
    }
683
4028
    if (!args[i]->IsObject()) {
684
      Environment::ThrowTypeError(args.GetIsolate(),
685
                                  "options must be an object");
686
      return -1;
687
    }
688
689
8056
    Local<String> key = FIXED_ONE_BYTE_STRING(args.GetIsolate(), "timeout");
690
12084
    Local<Value> value = args[i].As<Object>()->Get(key);
691
8056
    if (value->IsUndefined()) {
692
      return -1;
693
    }
694
11
    int64_t timeout = value->IntegerValue();
695
696
11
    if (timeout <= 0) {
697
2
      Environment::ThrowRangeError(args.GetIsolate(),
698
2
                                   "timeout must be a positive number");
699
2
      return -1;
700
    }
701
    return timeout;
702
  }
703
704
705
127593
  static bool GetDisplayErrorsArg(const FunctionCallbackInfo<Value>& args,
706
                                  const int i) {
707
518005
    if (args[i]->IsUndefined() || args[i]->IsString()) {
708
      return true;
709
    }
710
67581
    if (!args[i]->IsObject()) {
711
      Environment::ThrowTypeError(args.GetIsolate(),
712
                                  "options must be an object");
713
      return false;
714
    }
715
716
135162
    Local<String> key = FIXED_ONE_BYTE_STRING(args.GetIsolate(),
717
                                              "displayErrors");
718
202743
    Local<Value> value = args[i].As<Object>()->Get(key);
719
720
135162
    return value->IsUndefined() ? true : value->BooleanValue();
721
  }
722
723
724
64112
  static Local<String> GetFilenameArg(const FunctionCallbackInfo<Value>& args,
725
                                      const int i) {
726
    Local<String> defaultFilename =
727
128224
        FIXED_ONE_BYTE_STRING(args.GetIsolate(), "evalmachine.<anonymous>");
728
729
128224
    if (args[i]->IsUndefined()) {
730
542
      return defaultFilename;
731
    }
732
127140
    if (args[i]->IsString()) {
733
34
      return args[i].As<String>();
734
    }
735
63553
    if (!args[i]->IsObject()) {
736
      Environment::ThrowTypeError(args.GetIsolate(),
737
                                  "options must be an object");
738
      return Local<String>();
739
    }
740
741
127106
    Local<String> key = FIXED_ONE_BYTE_STRING(args.GetIsolate(), "filename");
742
190659
    Local<Value> value = args[i].As<Object>()->Get(key);
743
744
127106
    if (value->IsUndefined())
745
26
      return defaultFilename;
746
63527
    return value->ToString(args.GetIsolate());
747
  }
748
749
750
64112
  static MaybeLocal<Uint8Array> GetCachedData(
751
      Environment* env,
752
      const FunctionCallbackInfo<Value>& args,
753
      const int i) {
754
64112
    if (!args[i]->IsObject()) {
755
559
      return MaybeLocal<Uint8Array>();
756
    }
757
254212
    Local<Value> value = args[i].As<Object>()->Get(env->cached_data_string());
758
127106
    if (value->IsUndefined()) {
759
63549
      return MaybeLocal<Uint8Array>();
760
    }
761
762
4
    if (!value->IsUint8Array()) {
763
1
      Environment::ThrowTypeError(
764
          args.GetIsolate(),
765
1
          "options.cachedData must be a Buffer instance");
766
1
      return MaybeLocal<Uint8Array>();
767
    }
768
769
6
    return value.As<Uint8Array>();
770
  }
771
772
773
64112
  static bool GetProduceCachedData(
774
      Environment* env,
775
      const FunctionCallbackInfo<Value>& args,
776
      const int i) {
777
64112
    if (!args[i]->IsObject()) {
778
      return false;
779
    }
780
    Local<Value> value =
781
254212
        args[i].As<Object>()->Get(env->produce_cached_data_string());
782
783
63553
    return value->IsTrue();
784
  }
785
786
787
64112
  static Local<Integer> GetLineOffsetArg(
788
                                      const FunctionCallbackInfo<Value>& args,
789
                                      const int i) {
790
64112
    Local<Integer> defaultLineOffset = Integer::New(args.GetIsolate(), 0);
791
792
64112
    if (!args[i]->IsObject()) {
793
559
      return defaultLineOffset;
794
    }
795
796
127106
    Local<String> key = FIXED_ONE_BYTE_STRING(args.GetIsolate(), "lineOffset");
797
190659
    Local<Value> value = args[i].As<Object>()->Get(key);
798
799
127106
    return value->IsUndefined() ? defaultLineOffset : value->ToInteger();
800
  }
801
802
803
64112
  static Local<Integer> GetColumnOffsetArg(
804
                                      const FunctionCallbackInfo<Value>& args,
805
                                      const int i) {
806
64112
    Local<Integer> defaultColumnOffset = Integer::New(args.GetIsolate(), 0);
807
808
64112
    if (!args[i]->IsObject()) {
809
559
      return defaultColumnOffset;
810
    }
811
812
127106
    Local<String> key = FIXED_ONE_BYTE_STRING(args.GetIsolate(),
813
                                              "columnOffset");
814
190659
    Local<Value> value = args[i].As<Object>()->Get(key);
815
816
127106
    return value->IsUndefined() ? defaultColumnOffset : value->ToInteger();
817
  }
818
819
820
63475
  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
190425
    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
63475
    ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder(), false);
834
    Local<UnboundScript> unbound_script =
835
63475
        PersistentToLocal(env->isolate(), wrapped_script->script_);
836
63475
    Local<Script> script = unbound_script->BindToCurrentContext();
837
838
63475
    Local<Value> result;
839
63475
    bool timed_out = false;
840
63475
    bool received_signal = false;
841
63475
    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
63475
    } else if (break_on_sigint) {
848
40
      SigintWatchdog swd(env->isolate());
849
21
      result = script->Run();
850
19
      received_signal = swd.HasReceivedSignal();
851
63454
    } 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
63445
      result = script->Run();
857
    }
858
859
63471
    if (try_catch->HasCaught()) {
860
41
      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
41
      if (timed_out) {
867
        env->ThrowError("Script execution timed out.");
868
37
      } 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
41
      try_catch->ReThrow();
878
879
41
      return false;
880
    }
881
882
63430
    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
63430
    args.GetReturnValue().Set(result);
892
    return true;
893
  }
894
895
896
  ContextifyScript(Environment* env, Local<Object> object)
897
128224
      : BaseObject(env, object) {
898
64112
    MakeWeak<ContextifyScript>(this);
899
  }
900
901
902
260590
  ~ContextifyScript() override {
903
104236
    script_.Reset();
904
104236
  }
905
};
906
907
908
1563
void InitContextify(Local<Object> target,
909
                    Local<Value> unused,
910
                    Local<Context> context) {
911
1563
  Environment* env = Environment::GetCurrent(context);
912
1563
  ContextifyContext::Init(env, target);
913
1563
  ContextifyScript::Init(env, target);
914
1563
}
915
916
}  // namespace node
917
918
1568
NODE_MODULE_CONTEXT_AWARE_BUILTIN(contextify, node::InitContextify);