GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage/nodes/benchmark/out/../src/node_contextify.cc Lines: 458 504 90.9 %
Date: 2017-12-18 Branches: 252 320 78.8 %

Line Branch Exec Source
1
// Copyright Joyent, Inc. and other Node contributors.
2
//
3
// Permission is hereby granted, free of charge, to any person obtaining a
4
// copy of this software and associated documentation files (the
5
// "Software"), to deal in the Software without restriction, including
6
// without limitation the rights to use, copy, modify, merge, publish,
7
// distribute, sublicense, and/or sell copies of the Software, and to permit
8
// persons to whom the Software is furnished to do so, subject to the
9
// following conditions:
10
//
11
// The above copyright notice and this permission notice shall be included
12
// in all copies or substantial portions of the Software.
13
//
14
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20
// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22
#include "node_internals.h"
23
#include "node_watchdog.h"
24
#include "base_object-inl.h"
25
26
namespace node {
27
28
using v8::Array;
29
using v8::ArrayBuffer;
30
using v8::Boolean;
31
using v8::Context;
32
using v8::EscapableHandleScope;
33
using v8::External;
34
using v8::Function;
35
using v8::FunctionCallbackInfo;
36
using v8::FunctionTemplate;
37
using v8::HandleScope;
38
using v8::IndexedPropertyHandlerConfiguration;
39
using v8::Integer;
40
using v8::Just;
41
using v8::Local;
42
using v8::Maybe;
43
using v8::MaybeLocal;
44
using v8::Name;
45
using v8::NamedPropertyHandlerConfiguration;
46
using v8::Nothing;
47
using v8::Object;
48
using v8::ObjectTemplate;
49
using v8::Persistent;
50
using v8::PropertyAttribute;
51
using v8::PropertyCallbackInfo;
52
using v8::PropertyDescriptor;
53
using v8::Script;
54
using v8::ScriptCompiler;
55
using v8::ScriptOrigin;
56
using v8::String;
57
using v8::Symbol;
58
using v8::TryCatch;
59
using v8::Uint32;
60
using v8::Uint8Array;
61
using v8::UnboundScript;
62
using v8::Value;
63
using v8::WeakCallbackInfo;
64
65
// The vm module executes code in a sandboxed environment with a different
66
// global object than the rest of the code. This is achieved by applying
67
// every call that changes or queries a property on the global `this` in the
68
// sandboxed code, to the sandbox object.
69
//
70
// The implementation uses V8's interceptors for methods like `set`, `get`,
71
// `delete`, `defineProperty`, and for any query of the property attributes.
72
// Property handlers with interceptors are set on the object template for
73
// the sandboxed code. Handlers for both named properties and for indexed
74
// properties are used. Their functionality is almost identical, the indexed
75
// interceptors mostly just call the named interceptors.
76
//
77
// For every `get` of a global property in the sandboxed context, the
78
// interceptor callback checks the sandbox object for the property.
79
// If the property is defined on the sandbox, that result is returned to
80
// the original call instead of finishing the query on the global object.
81
//
82
// For every `set` of a global property, the interceptor callback defines or
83
// changes the property both on the sandbox and the global proxy.
84
85
namespace {
86
87
// Convert an int to a V8 Name (String or Symbol).
88
4
Local<Name> Uint32ToName(Local<Context> context, uint32_t index) {
89
16
  return Uint32::New(context->GetIsolate(), index)->ToString(context)
90
12
      .ToLocalChecked();
91
}
92
93
class ContextifyContext {
94
 protected:
95
  // V8 reserves the first field in context objects for the debugger. We use the
96
  // second field to hold a reference to the sandbox object.
97
  enum { kSandboxObjectIndex = 1 };
98
99
  Environment* const env_;
100
  Persistent<Context> context_;
101
102
 public:
103
284
  ContextifyContext(Environment* env, Local<Object> sandbox_obj) : env_(env) {
104
284
    Local<Context> v8_context = CreateV8Context(env, sandbox_obj);
105
284
    context_.Reset(env->isolate(), v8_context);
106
107
    // Allocation failure or maximum call stack size reached
108
568
    if (context_.IsEmpty())
109
284
      return;
110
284
    context_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter);
111
284
    context_.MarkIndependent();
112
  }
113
114
115
84
  ~ContextifyContext() {
116
42
    context_.Reset();
117
42
  }
118
119
120
2003489
  inline Environment* env() const {
121
2003489
    return env_;
122
  }
123
124
125
2003109
  inline Local<Context> context() const {
126
2003109
    return PersistentToLocal(env()->isolate(), context_);
127
  }
128
129
130
440
  inline Local<Object> global_proxy() const {
131
880
    return context()->Global();
132
  }
133
134
135
1000724
  inline Local<Object> sandbox() const {
136
3002172
    return Local<Object>::Cast(context()->GetEmbedderData(kSandboxObjectIndex));
137
  }
138
139
  // This is an object that just keeps an internal pointer to this
140
  // ContextifyContext.  It's passed to the NamedPropertyHandler.  If we
141
  // pass the main JavaScript context object we're embedded in, then the
142
  // NamedPropertyHandler will store a reference to it forever and keep it
143
  // from getting gc'd.
144
568
  Local<Value> CreateDataWrapper(Environment* env) {
145
568
    EscapableHandleScope scope(env->isolate());
146
    Local<Object> wrapper =
147
        env->script_data_constructor_function()
148
1704
            ->NewInstance(env->context()).FromMaybe(Local<Object>());
149
568
    if (wrapper.IsEmpty())
150
      return scope.Escape(Local<Value>::New(env->isolate(), Local<Value>()));
151
152
568
    Wrap(wrapper, this);
153
568
    return scope.Escape(wrapper);
154
  }
155
156
157
284
  Local<Context> CreateV8Context(Environment* env, Local<Object> sandbox_obj) {
158
284
    EscapableHandleScope scope(env->isolate());
159
    Local<FunctionTemplate> function_template =
160
284
        FunctionTemplate::New(env->isolate());
161
162
568
    function_template->SetClassName(sandbox_obj->GetConstructorName());
163
164
    Local<ObjectTemplate> object_template =
165
284
        function_template->InstanceTemplate();
166
167
    NamedPropertyHandlerConfiguration config(PropertyGetterCallback,
168
                                             PropertySetterCallback,
169
                                             PropertyDescriptorCallback,
170
                                             PropertyDeleterCallback,
171
                                             PropertyEnumeratorCallback,
172
                                             PropertyDefinerCallback,
173
284
                                             CreateDataWrapper(env));
174
175
    IndexedPropertyHandlerConfiguration indexed_config(
176
        IndexedPropertyGetterCallback,
177
        IndexedPropertySetterCallback,
178
        IndexedPropertyDescriptorCallback,
179
        IndexedPropertyDeleterCallback,
180
        PropertyEnumeratorCallback,
181
        IndexedPropertyDefinerCallback,
182
284
        CreateDataWrapper(env));
183
184
284
    object_template->SetHandler(config);
185
284
    object_template->SetHandler(indexed_config);
186
187
284
    Local<Context> ctx = NewContext(env->isolate(), object_template);
188
189
284
    if (ctx.IsEmpty()) {
190
      env->ThrowError("Could not instantiate context");
191
      return Local<Context>();
192
    }
193
194
852
    ctx->SetSecurityToken(env->context()->GetSecurityToken());
195
196
    // We need to tie the lifetime of the sandbox object with the lifetime of
197
    // newly created context. We do this by making them hold references to each
198
    // other. The context can directly hold a reference to the sandbox as an
199
    // embedder data field. However, we cannot hold a reference to a v8::Context
200
    // directly in an Object, we instead hold onto the new context's global
201
    // object instead (which then has a reference to the context).
202
284
    ctx->SetEmbedderData(kSandboxObjectIndex, sandbox_obj);
203
    sandbox_obj->SetPrivate(env->context(),
204
                            env->contextify_global_private_symbol(),
205
852
                            ctx->Global());
206
207
284
    env->AssignToContext(ctx);
208
209
284
    return scope.Escape(ctx);
210
  }
211
212
213
3348
  static void Init(Environment* env, Local<Object> target) {
214
    Local<FunctionTemplate> function_template =
215
3348
        FunctionTemplate::New(env->isolate());
216
6696
    function_template->InstanceTemplate()->SetInternalFieldCount(1);
217
3348
    env->set_script_data_constructor_function(function_template->GetFunction());
218
219
3348
    env->SetMethod(target, "makeContext", MakeContext);
220
3348
    env->SetMethod(target, "isContext", IsContext);
221
3348
  }
222
223
224
284
  static void MakeContext(const FunctionCallbackInfo<Value>& args) {
225
284
    Environment* env = Environment::GetCurrent(args);
226
227
568
    if (!args[0]->IsObject()) {
228
      return env->ThrowTypeError("sandbox argument must be an object.");
229
    }
230
568
    Local<Object> sandbox = args[0].As<Object>();
231
232
    // Don't allow contextifying a sandbox multiple times.
233
852
    CHECK(
234
        !sandbox->HasPrivate(
235
            env->context(),
236
            env->contextify_context_private_symbol()).FromJust());
237
238
284
    TryCatch try_catch(env->isolate());
239
284
    ContextifyContext* context = new ContextifyContext(env, sandbox);
240
241
284
    if (try_catch.HasCaught()) {
242
      try_catch.ReThrow();
243
      return;
244
    }
245
246
568
    if (context->context().IsEmpty())
247
      return;
248
249
    sandbox->SetPrivate(
250
        env->context(),
251
        env->contextify_context_private_symbol(),
252
852
        External::New(env->isolate(), context));
253
  }
254
255
256
178
  static void IsContext(const FunctionCallbackInfo<Value>& args) {
257
178
    Environment* env = Environment::GetCurrent(args);
258
259
356
    if (!args[0]->IsObject()) {
260
3
      env->ThrowTypeError("sandbox must be an object");
261
181
      return;
262
    }
263
350
    Local<Object> sandbox = args[0].As<Object>();
264
265
    Maybe<bool> result =
266
        sandbox->HasPrivate(env->context(),
267
350
                            env->contextify_context_private_symbol());
268
525
    args.GetReturnValue().Set(result.FromJust());
269
  }
270
271
272
42
  static void WeakCallback(const WeakCallbackInfo<ContextifyContext>& data) {
273
42
    ContextifyContext* context = data.GetParameter();
274
42
    delete context;
275
42
  }
276
277
278
548
  static ContextifyContext* ContextFromContextifiedSandbox(
279
      Environment* env,
280
      const Local<Object>& sandbox) {
281
    MaybeLocal<Value> maybe_value =
282
        sandbox->GetPrivate(env->context(),
283
1096
                            env->contextify_context_private_symbol());
284
    Local<Value> context_external_v;
285

1096
    if (maybe_value.ToLocal(&context_external_v) &&
286
548
        context_external_v->IsExternal()) {
287
544
      Local<External> context_external = context_external_v.As<External>();
288
544
      return static_cast<ContextifyContext*>(context_external->Value());
289
    }
290
4
    return nullptr;
291
  }
292
293
294
1000693
  static void PropertyGetterCallback(
295
      Local<Name> property,
296
      const PropertyCallbackInfo<Value>& args) {
297
    ContextifyContext* ctx;
298
2001670
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
299
300
    // Still initializing
301
2001386
    if (ctx->context_.IsEmpty())
302
284
      return;
303
304
1000409
    Local<Context> context = ctx->context();
305
1000409
    Local<Object> sandbox = ctx->sandbox();
306
    MaybeLocal<Value> maybe_rv =
307
1000409
        sandbox->GetRealNamedProperty(context, property);
308
1000409
    if (maybe_rv.IsEmpty()) {
309
      maybe_rv =
310
340
          ctx->global_proxy()->GetRealNamedProperty(context, property);
311
    }
312
313
    Local<Value> rv;
314
1000409
    if (maybe_rv.ToLocal(&rv)) {
315
1000344
      if (rv == sandbox)
316
8
        rv = ctx->global_proxy();
317
318
2000688
      args.GetReturnValue().Set(rv);
319
    }
320
  }
321
322
323
132
  static void PropertySetterCallback(
324
      Local<Name> property,
325
      Local<Value> value,
326
      const PropertyCallbackInfo<Value>& args) {
327
    ContextifyContext* ctx;
328
278
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
329
330
    // Still initializing
331
264
    if (ctx->context_.IsEmpty())
332
      return;
333
334
132
    auto attributes = PropertyAttribute::None;
335
132
    bool is_declared_on_global_proxy = ctx->global_proxy()
336
528
        ->GetRealNamedPropertyAttributes(ctx->context(), property)
337
264
        .To(&attributes);
338
    bool read_only =
339
132
        static_cast<int>(attributes) &
340
132
        static_cast<int>(PropertyAttribute::ReadOnly);
341
342
132
    bool is_declared_on_sandbox = ctx->sandbox()
343
528
        ->GetRealNamedPropertyAttributes(ctx->context(), property)
344
264
        .To(&attributes);
345

260
    read_only = read_only ||
346
128
        (static_cast<int>(attributes) &
347
132
        static_cast<int>(PropertyAttribute::ReadOnly));
348
349
132
    if (read_only)
350
13
      return;
351
352
    // true for x = 5
353
    // false for this.x = 5
354
    // false for Object.defineProperty(this, 'foo', ...)
355
    // false for vmResult.x = 5 where vmResult = vm.runInContext();
356
238
    bool is_contextual_store = ctx->global_proxy() != args.This();
357
358
    // Indicator to not return before setting (undeclared) function declarations
359
    // on the sandbox in strict mode, i.e. args.ShouldThrowOnError() = true.
360
    // True for 'function f() {}', 'this.f = function() {}',
361
    // 'var f = function()'.
362
    // In effect only for 'function f() {}' because
363
    // var f = function(), is_declared = true
364
    // this.f = function() {}, is_contextual_store = false.
365
119
    bool is_function = value->IsFunction();
366
367

119
    bool is_declared = is_declared_on_global_proxy || is_declared_on_sandbox;
368


191
    if (!is_declared && args.ShouldThrowOnError() && is_contextual_store &&
369
19
        !is_function)
370
1
      return;
371
372

314
    if (!is_declared_on_global_proxy && is_declared_on_sandbox  &&
373

133
        args.ShouldThrowOnError() && is_contextual_store && !is_function) {
374
      // The property exists on the sandbox but not on the global
375
      // proxy. Setting it would throw because we are in strict mode.
376
      // Don't attempt to set it by signaling that the call was
377
      // intercepted. Only change the value on the sandbox.
378
2
      args.GetReturnValue().Set(false);
379
    }
380
381
236
    ctx->sandbox()->Set(property, value);
382
  }
383
384
385
28
  static void PropertyDescriptorCallback(
386
      Local<Name> property,
387
      const PropertyCallbackInfo<Value>& args) {
388
    ContextifyContext* ctx;
389
56
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
390
391
    // Still initializing
392
56
    if (ctx->context_.IsEmpty())
393
      return;
394
395
28
    Local<Context> context = ctx->context();
396
397
28
    Local<Object> sandbox = ctx->sandbox();
398
399
84
    if (sandbox->HasOwnProperty(context, property).FromMaybe(false)) {
400
      args.GetReturnValue().Set(
401
13
          sandbox->GetOwnPropertyDescriptor(context, property)
402
39
              .ToLocalChecked());
403
    }
404
  }
405
406
407
15
  static void PropertyDefinerCallback(
408
      Local<Name> property,
409
      const PropertyDescriptor& desc,
410
      const PropertyCallbackInfo<Value>& args) {
411
    ContextifyContext* ctx;
412
30
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
413
414
    // Still initializing
415
30
    if (ctx->context_.IsEmpty())
416
      return;
417
418
15
    Local<Context> context = ctx->context();
419
15
    v8::Isolate* isolate = context->GetIsolate();
420
421
15
    auto attributes = PropertyAttribute::None;
422
    bool is_declared =
423
15
        ctx->global_proxy()->GetRealNamedPropertyAttributes(ctx->context(),
424
60
                                                            property)
425
30
            .To(&attributes);
426
    bool read_only =
427
15
        static_cast<int>(attributes) &
428
15
            static_cast<int>(PropertyAttribute::ReadOnly);
429
430
    // If the property is set on the global as read_only, don't change it on
431
    // the global or sandbox.
432

15
    if (is_declared && read_only)
433
      return;
434
435
15
    Local<Object> sandbox = ctx->sandbox();
436
437
    auto define_prop_on_sandbox =
438
15
        [&] (PropertyDescriptor* desc_for_sandbox) {
439
15
          if (desc.has_enumerable()) {
440
2
            desc_for_sandbox->set_enumerable(desc.enumerable());
441
          }
442
15
          if (desc.has_configurable()) {
443
1
            desc_for_sandbox->set_configurable(desc.configurable());
444
          }
445
          // Set the property on the sandbox.
446
30
          sandbox->DefineProperty(context, property, *desc_for_sandbox)
447
30
              .FromJust();
448
30
        };
449
450

15
    if (desc.has_get() || desc.has_set()) {
451
      PropertyDescriptor desc_for_sandbox(
452
10
          desc.has_get() ? desc.get() : v8::Undefined(isolate).As<Value>(),
453

23
          desc.has_set() ? desc.set() : v8::Undefined(isolate).As<Value>());
454
455
5
      define_prop_on_sandbox(&desc_for_sandbox);
456
    } else {
457
      Local<Value> value =
458

12
          desc.has_value() ? desc.value() : v8::Undefined(isolate).As<Value>();
459
460
10
      if (desc.has_writable()) {
461
        PropertyDescriptor desc_for_sandbox(value, desc.writable());
462
        define_prop_on_sandbox(&desc_for_sandbox);
463
      } else {
464
10
        PropertyDescriptor desc_for_sandbox(value);
465
10
        define_prop_on_sandbox(&desc_for_sandbox);
466
      }
467
    }
468
  }
469
470
2
  static void PropertyDeleterCallback(
471
      Local<Name> property,
472
      const PropertyCallbackInfo<Boolean>& args) {
473
    ContextifyContext* ctx;
474
5
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
475
476
    // Still initializing
477
4
    if (ctx->context_.IsEmpty())
478
      return;
479
480
4
    Maybe<bool> success = ctx->sandbox()->Delete(ctx->context(), property);
481
482
4
    if (success.FromMaybe(false))
483
1
      return;
484
485
    // Delete failed on the sandbox, intercept and do not delete on
486
    // the global object.
487
2
    args.GetReturnValue().Set(false);
488
  }
489
490
491
20
  static void PropertyEnumeratorCallback(
492
      const PropertyCallbackInfo<Array>& args) {
493
    ContextifyContext* ctx;
494
40
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
495
496
    // Still initializing
497
40
    if (ctx->context_.IsEmpty())
498
      return;
499
500
80
    args.GetReturnValue().Set(ctx->sandbox()->GetPropertyNames());
501
  }
502
503
1
  static void IndexedPropertyGetterCallback(
504
      uint32_t index,
505
      const PropertyCallbackInfo<Value>& args) {
506
    ContextifyContext* ctx;
507
2
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
508
509
    // Still initializing
510
2
    if (ctx->context_.IsEmpty())
511
      return;
512
513
1
    PropertyGetterCallback(Uint32ToName(ctx->context(), index), args);
514
  }
515
516
517
1
  static void IndexedPropertySetterCallback(
518
      uint32_t index,
519
      Local<Value> value,
520
      const PropertyCallbackInfo<Value>& args) {
521
    ContextifyContext* ctx;
522
2
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
523
524
    // Still initializing
525
2
    if (ctx->context_.IsEmpty())
526
      return;
527
528
1
    PropertySetterCallback(Uint32ToName(ctx->context(), index), value, args);
529
  }
530
531
532
1
  static void IndexedPropertyDescriptorCallback(
533
      uint32_t index,
534
      const PropertyCallbackInfo<Value>& args) {
535
    ContextifyContext* ctx;
536
2
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
537
538
    // Still initializing
539
2
    if (ctx->context_.IsEmpty())
540
      return;
541
542
1
    PropertyDescriptorCallback(Uint32ToName(ctx->context(), index), args);
543
  }
544
545
546
1
  static void IndexedPropertyDefinerCallback(
547
      uint32_t index,
548
      const PropertyDescriptor& desc,
549
      const PropertyCallbackInfo<Value>& args) {
550
    ContextifyContext* ctx;
551
2
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
552
553
    // Still initializing
554
2
    if (ctx->context_.IsEmpty())
555
      return;
556
557
1
    PropertyDefinerCallback(Uint32ToName(ctx->context(), index), desc, args);
558
  }
559
560
  static void IndexedPropertyDeleterCallback(
561
      uint32_t index,
562
      const PropertyCallbackInfo<Boolean>& args) {
563
    ContextifyContext* ctx;
564
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
565
566
    // Still initializing
567
    if (ctx->context_.IsEmpty())
568
      return;
569
570
    Maybe<bool> success = ctx->sandbox()->Delete(ctx->context(), index);
571
572
    if (success.FromMaybe(false))
573
      return;
574
575
    // Delete failed on the sandbox, intercept and do not delete on
576
    // the global object.
577
    args.GetReturnValue().Set(false);
578
  }
579
};
580
581
class ContextifyScript : public BaseObject {
582
 private:
583
  Persistent<UnboundScript> script_;
584
585
 public:
586
3348
  static void Init(Environment* env, Local<Object> target) {
587
3348
    HandleScope scope(env->isolate());
588
    Local<String> class_name =
589
3348
        FIXED_ONE_BYTE_STRING(env->isolate(), "ContextifyScript");
590
591
3348
    Local<FunctionTemplate> script_tmpl = env->NewFunctionTemplate(New);
592
6696
    script_tmpl->InstanceTemplate()->SetInternalFieldCount(1);
593
3348
    script_tmpl->SetClassName(class_name);
594
3348
    env->SetProtoMethod(script_tmpl, "runInContext", RunInContext);
595
3348
    env->SetProtoMethod(script_tmpl, "runInThisContext", RunInThisContext);
596
597
6696
    target->Set(class_name, script_tmpl->GetFunction());
598
3348
    env->set_script_context_constructor_template(script_tmpl);
599
600
    Local<Symbol> parsing_context_symbol =
601
        Symbol::New(env->isolate(),
602
                    FIXED_ONE_BYTE_STRING(env->isolate(),
603
3348
                                          "script parsing context"));
604
3348
    env->set_vm_parsing_context_symbol(parsing_context_symbol);
605
    target->Set(env->context(),
606
                FIXED_ONE_BYTE_STRING(env->isolate(), "kParsingContext"),
607
13392
                parsing_context_symbol)
608
10044
        .FromJust();
609
3348
  }
610
611
612
  // args: code, [options]
613
248199
  static void New(const FunctionCallbackInfo<Value>& args) {
614
248199
    Environment* env = Environment::GetCurrent(args);
615
616
248199
    if (!args.IsConstructCall()) {
617
286
      return env->ThrowError("Must call vm.Script as a constructor.");
618
    }
619
620
    ContextifyScript* contextify_script =
621
248199
        new ContextifyScript(env, args.This());
622
623
248199
    TryCatch try_catch(env->isolate());
624
496112
    Environment::ShouldNotAbortOnUncaughtScope no_abort_scope(env);
625
    Local<String> code =
626
992796
        args[0]->ToString(env->context()).FromMaybe(Local<String>());
627
628
248199
    Local<Value> options = args[1];
629
248199
    MaybeLocal<String> filename = GetFilenameArg(env, options);
630
248199
    MaybeLocal<Integer> lineOffset = GetLineOffsetArg(env, options);
631
248199
    MaybeLocal<Integer> columnOffset = GetColumnOffsetArg(env, options);
632
248199
    MaybeLocal<Uint8Array> cached_data_buf = GetCachedData(env, options);
633
248199
    Maybe<bool> maybe_produce_cached_data = GetProduceCachedData(env, options);
634
248199
    MaybeLocal<Context> maybe_context = GetContext(env, options);
635
248199
    if (try_catch.HasCaught()) {
636
13
      no_abort_scope.Close();
637
13
      try_catch.ReThrow();
638
13
      return;
639
    }
640
641
248186
    bool produce_cached_data = maybe_produce_cached_data.ToChecked();
642
643
248186
    ScriptCompiler::CachedData* cached_data = nullptr;
644
    Local<Uint8Array> ui8;
645
248186
    if (cached_data_buf.ToLocal(&ui8)) {
646
6
      ArrayBuffer::Contents contents = ui8->Buffer()->GetContents();
647
      cached_data = new ScriptCompiler::CachedData(
648
6
          static_cast<uint8_t*>(contents.Data()) + ui8->ByteOffset(),
649
6
          ui8->ByteLength());
650
    }
651
652
    ScriptOrigin origin(filename.ToLocalChecked(), lineOffset.ToLocalChecked(),
653
248186
                        columnOffset.ToLocalChecked());
654
247913
    ScriptCompiler::Source source(code, origin, cached_data);
655
    ScriptCompiler::CompileOptions compile_options =
656
248186
        ScriptCompiler::kNoCompileOptions;
657
658
248186
    if (source.GetCachedData() != nullptr)
659
3
      compile_options = ScriptCompiler::kConsumeCodeCache;
660
248183
    else if (produce_cached_data)
661
6
      compile_options = ScriptCompiler::kProduceCodeCache;
662
663
496099
    Context::Scope scope(maybe_context.FromMaybe(env->context()));
664
665
    MaybeLocal<UnboundScript> v8_script = ScriptCompiler::CompileUnboundScript(
666
        env->isolate(),
667
        &source,
668
248186
        compile_options);
669
670
248186
    if (v8_script.IsEmpty()) {
671
273
      DecorateErrorStack(env, try_catch);
672
273
      no_abort_scope.Close();
673
273
      try_catch.ReThrow();
674
273
      return;
675
    }
676
    contextify_script->script_.Reset(env->isolate(),
677
495826
                                     v8_script.ToLocalChecked());
678
679
247913
    if (compile_options == ScriptCompiler::kConsumeCodeCache) {
680
      args.This()->Set(
681
          env->cached_data_rejected_string(),
682
12
          Boolean::New(env->isolate(), source.GetCachedData()->rejected));
683
247910
    } else if (compile_options == ScriptCompiler::kProduceCodeCache) {
684
6
      const ScriptCompiler::CachedData* cached_data = source.GetCachedData();
685
6
      bool cached_data_produced = cached_data != nullptr;
686
6
      if (cached_data_produced) {
687
        MaybeLocal<Object> buf = Buffer::Copy(
688
            env,
689
            reinterpret_cast<const char*>(cached_data->data),
690
6
            cached_data->length);
691
18
        args.This()->Set(env->cached_data_string(), buf.ToLocalChecked());
692
      }
693
      args.This()->Set(
694
          env->cached_data_produced_string(),
695
24
          Boolean::New(env->isolate(), cached_data_produced));
696
247913
    }
697
  }
698
699
700
247889
  static bool InstanceOf(Environment* env, const Local<Value>& value) {
701

991556
    return !value.IsEmpty() &&
702
743667
           env->script_context_constructor_template()->HasInstance(value);
703
  }
704
705
706
  // args: [options]
707
247513
  static void RunInThisContext(const FunctionCallbackInfo<Value>& args) {
708
247513
    Environment* env = Environment::GetCurrent(args);
709
710
    // Assemble arguments
711
247513
    TryCatch try_catch(args.GetIsolate());
712
247513
    Maybe<int64_t> maybe_timeout = GetTimeoutArg(env, args[0]);
713
247513
    Maybe<bool> maybe_display_errors = GetDisplayErrorsArg(env, args[0]);
714
247513
    Maybe<bool> maybe_break_on_sigint = GetBreakOnSigintArg(env, args[0]);
715
247513
    if (try_catch.HasCaught()) {
716
4
      try_catch.ReThrow();
717
247506
      return;
718
    }
719
720
247509
    int64_t timeout = maybe_timeout.ToChecked();
721
247509
    bool display_errors = maybe_display_errors.ToChecked();
722
247509
    bool break_on_sigint = maybe_break_on_sigint.ToChecked();
723
724
    // Do the eval within this context
725
    EvalMachine(env, timeout, display_errors, break_on_sigint, args,
726
247509
                &try_catch);
727
  }
728
729
  // args: sandbox, [options]
730
389
  static void RunInContext(const FunctionCallbackInfo<Value>& args) {
731
389
    Environment* env = Environment::GetCurrent(args);
732
733
    int64_t timeout;
734
    bool display_errors;
735
    bool break_on_sigint;
736
737
    // Assemble arguments
738
778
    if (!args[0]->IsObject()) {
739
      return env->ThrowTypeError(
740
55
          "contextifiedSandbox argument must be an object.");
741
    }
742
743
764
    Local<Object> sandbox = args[0].As<Object>();
744
    {
745
382
      TryCatch try_catch(env->isolate());
746
382
      Maybe<int64_t> maybe_timeout = GetTimeoutArg(env, args[1]);
747
382
      Maybe<bool> maybe_display_errors = GetDisplayErrorsArg(env, args[1]);
748
382
      Maybe<bool> maybe_break_on_sigint = GetBreakOnSigintArg(env, args[1]);
749
382
      if (try_catch.HasCaught()) {
750
        try_catch.ReThrow();
751
        return;
752
      }
753
754
382
      timeout = maybe_timeout.ToChecked();
755
382
      display_errors = maybe_display_errors.ToChecked();
756
382
      break_on_sigint = maybe_break_on_sigint.ToChecked();
757
    }
758
759
    // Get the context from the sandbox
760
    ContextifyContext* contextify_context =
761
382
        ContextifyContext::ContextFromContextifiedSandbox(env, sandbox);
762
382
    if (contextify_context == nullptr) {
763
      return env->ThrowTypeError(
764
2
          "sandbox argument must have been converted to a context.");
765
    }
766
767
760
    if (contextify_context->context().IsEmpty())
768
      return;
769
770
    {
771
380
      TryCatch try_catch(env->isolate());
772
      // Do the eval within the context
773
721
      Context::Scope context_scope(contextify_context->context());
774
      EvalMachine(contextify_context->env(),
775
                  timeout,
776
                  display_errors,
777
                  break_on_sigint,
778
                  args,
779
380
                  &try_catch);
780
781
380
      if (try_catch.HasCaught()) {
782
39
        try_catch.ReThrow();
783
39
        return;
784
341
      }
785
    }
786
  }
787
788
322
  static void DecorateErrorStack(Environment* env, const TryCatch& try_catch) {
789
322
    Local<Value> exception = try_catch.Exception();
790
791
322
    if (!exception->IsObject())
792
105
      return;
793
794
317
    Local<Object> err_obj = exception.As<Object>();
795
796
317
    if (IsExceptionDecorated(env, err_obj))
797
3
      return;
798
799
314
    AppendExceptionLine(env, exception, try_catch.Message(), CONTEXTIFY_ERROR);
800
628
    Local<Value> stack = err_obj->Get(env->stack_string());
801
    MaybeLocal<Value> maybe_value =
802
        err_obj->GetPrivate(
803
            env->context(),
804
628
            env->arrow_message_private_symbol());
805
806
    Local<Value> arrow;
807

942
    if (!(maybe_value.ToLocal(&arrow) && arrow->IsString())) {
808
91
      return;
809
    }
810
811

669
    if (stack.IsEmpty() || !stack->IsString()) {
812
1
      return;
813
    }
814
815
    Local<String> decorated_stack = String::Concat(
816
        String::Concat(arrow.As<String>(),
817
          FIXED_ONE_BYTE_STRING(env->isolate(), "\n")),
818
444
        stack.As<String>());
819
444
    err_obj->Set(env->stack_string(), decorated_stack);
820
    err_obj->SetPrivate(
821
        env->context(),
822
        env->decorated_private_symbol(),
823
666
        True(env->isolate()));
824
  }
825
826
247895
  static Maybe<bool> GetBreakOnSigintArg(Environment* env,
827
                                         Local<Value> options) {
828

554578
    if (options->IsUndefined() || options->IsString()) {
829
218501
      return Just(false);
830
    }
831
29394
    if (!options->IsObject()) {
832
      env->ThrowTypeError("options must be an object");
833
      return Nothing<bool>();
834
    }
835
836
29394
    Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "breakOnSigint");
837
    MaybeLocal<Value> maybe_value =
838
88182
        options.As<Object>()->Get(env->context(), key);
839
29394
    if (maybe_value.IsEmpty())
840
      return Nothing<bool>();
841
842
29394
    Local<Value> value = maybe_value.ToLocalChecked();
843
29394
    return Just(value->IsTrue());
844
  }
845
846
247895
  static Maybe<int64_t> GetTimeoutArg(Environment* env, Local<Value> options) {
847

554578
    if (options->IsUndefined() || options->IsString()) {
848
218501
      return Just<int64_t>(-1);
849
    }
850
29394
    if (!options->IsObject()) {
851
      env->ThrowTypeError("options must be an object");
852
      return Nothing<int64_t>();
853
    }
854
855
    MaybeLocal<Value> maybe_value =
856
117576
        options.As<Object>()->Get(env->context(), env->timeout_string());
857
29394
    if (maybe_value.IsEmpty())
858
1
      return Nothing<int64_t>();
859
860
29393
    Local<Value> value = maybe_value.ToLocalChecked();
861
58786
    if (value->IsUndefined()) {
862
29382
      return Just<int64_t>(-1);
863
    }
864
865
22
    Maybe<int64_t> timeout = value->IntegerValue(env->context());
866
867

22
    if (timeout.IsJust() && timeout.ToChecked() <= 0) {
868
2
      env->ThrowRangeError("timeout must be a positive number");
869
2
      return Nothing<int64_t>();
870
    }
871
872
9
    return timeout;
873
  }
874
875
876
247895
  static Maybe<bool> GetDisplayErrorsArg(Environment* env,
877
                                         Local<Value> options) {
878

554578
    if (options->IsUndefined() || options->IsString()) {
879
218501
      return Just(true);
880
    }
881
29394
    if (!options->IsObject()) {
882
      env->ThrowTypeError("options must be an object");
883
      return Nothing<bool>();
884
    }
885
886
29394
    Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "displayErrors");
887
    MaybeLocal<Value> maybe_value =
888
88182
        options.As<Object>()->Get(env->context(), key);
889
29394
    if (maybe_value.IsEmpty())
890
1
      return Nothing<bool>();
891
892
29393
    Local<Value> value = maybe_value.ToLocalChecked();
893
58786
    if (value->IsUndefined())
894
182
      return Just(true);
895
896
58422
    return value->BooleanValue(env->context());
897
  }
898
899
900
248199
  static MaybeLocal<String> GetFilenameArg(Environment* env,
901
                                           Local<Value> options) {
902
    Local<String> defaultFilename =
903
248199
        FIXED_ONE_BYTE_STRING(env->isolate(), "evalmachine.<anonymous>");
904
905
496398
    if (options->IsUndefined()) {
906
422
      return defaultFilename;
907
    }
908
495554
    if (options->IsString()) {
909
1
      return options.As<String>();
910
    }
911
247776
    if (!options->IsObject()) {
912
      env->ThrowTypeError("options must be an object");
913
      return Local<String>();
914
    }
915
916
247776
    Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "filename");
917
    MaybeLocal<Value> maybe_value =
918
743328
        options.As<Object>()->Get(env->context(), key);
919
247776
    if (maybe_value.IsEmpty())
920
1
      return MaybeLocal<String>();
921
922
247775
    Local<Value> value = maybe_value.ToLocalChecked();
923
495550
    if (value->IsUndefined())
924
172
      return defaultFilename;
925
495206
    return value->ToString(env->context());
926
  }
927
928
929
248199
  static MaybeLocal<Uint8Array> GetCachedData(Environment* env,
930
                                              Local<Value> options) {
931
248199
    if (!options->IsObject()) {
932
423
      return MaybeLocal<Uint8Array>();
933
    }
934
935
    MaybeLocal<Value> maybe_value =
936
991104
        options.As<Object>()->Get(env->context(), env->cached_data_string());
937
247776
    if (maybe_value.IsEmpty())
938
1
      return MaybeLocal<Uint8Array>();
939
940
247775
    Local<Value> value = maybe_value.ToLocalChecked();
941
495550
    if (value->IsUndefined()) {
942
247771
      return MaybeLocal<Uint8Array>();
943
    }
944
945
4
    if (!value->IsUint8Array()) {
946
1
      env->ThrowTypeError("options.cachedData must be a Buffer instance");
947
1
      return MaybeLocal<Uint8Array>();
948
    }
949
950
3
    return value.As<Uint8Array>();
951
  }
952
953
954
248199
  static Maybe<bool> GetProduceCachedData(Environment* env,
955
                                          Local<Value> options) {
956
248199
    if (!options->IsObject()) {
957
423
      return Just(false);
958
    }
959
960
    MaybeLocal<Value> maybe_value =
961
        options.As<Object>()->Get(env->context(),
962
991104
                                  env->produce_cached_data_string());
963
247776
    if (maybe_value.IsEmpty())
964
1
      return Nothing<bool>();
965
966
247775
    Local<Value> value = maybe_value.ToLocalChecked();
967
247775
    return Just(value->IsTrue());
968
  }
969
970
971
248199
  static MaybeLocal<Integer> GetLineOffsetArg(Environment* env,
972
                                              Local<Value> options) {
973
248199
    Local<Integer> defaultLineOffset = Integer::New(env->isolate(), 0);
974
975
248199
    if (!options->IsObject()) {
976
423
      return defaultLineOffset;
977
    }
978
979
247776
    Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "lineOffset");
980
    MaybeLocal<Value> maybe_value =
981
743328
        options.As<Object>()->Get(env->context(), key);
982
247776
    if (maybe_value.IsEmpty())
983
1
      return MaybeLocal<Integer>();
984
985
247775
    Local<Value> value = maybe_value.ToLocalChecked();
986
495550
    if (value->IsUndefined())
987
882
      return defaultLineOffset;
988
989
493786
    return value->ToInteger(env->context());
990
  }
991
992
993
248199
  static MaybeLocal<Integer> GetColumnOffsetArg(Environment* env,
994
                                                Local<Value> options) {
995
248199
    Local<Integer> defaultColumnOffset = Integer::New(env->isolate(), 0);
996
997
248199
    if (!options->IsObject()) {
998
423
      return defaultColumnOffset;
999
    }
1000
1001
247776
    Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "columnOffset");
1002
    MaybeLocal<Value> maybe_value =
1003
743328
      options.As<Object>()->Get(env->context(), key);
1004
247776
    if (maybe_value.IsEmpty())
1005
1
      return MaybeLocal<Integer>();
1006
1007
247775
    Local<Value> value = maybe_value.ToLocalChecked();
1008
495550
    if (value->IsUndefined())
1009
247774
      return defaultColumnOffset;
1010
1011
2
    return value->ToInteger(env->context());
1012
  }
1013
1014
248199
  static MaybeLocal<Context> GetContext(Environment* env,
1015
                                        Local<Value> options) {
1016
248199
    if (!options->IsObject())
1017
423
      return MaybeLocal<Context>();
1018
1019
    MaybeLocal<Value> maybe_value =
1020
        options.As<Object>()->Get(env->context(),
1021
991104
                                  env->vm_parsing_context_symbol());
1022
    Local<Value> value;
1023
247776
    if (!maybe_value.ToLocal(&value))
1024
      return MaybeLocal<Context>();
1025
1026
247776
    if (!value->IsObject()) {
1027
495220
      if (!value->IsNullOrUndefined()) {
1028
        env->ThrowTypeError(
1029
3
            "contextifiedSandbox argument must be an object.");
1030
      }
1031
247610
      return MaybeLocal<Context>();
1032
    }
1033
1034
    ContextifyContext* sandbox =
1035
        ContextifyContext::ContextFromContextifiedSandbox(
1036
166
            env, value.As<Object>());
1037
166
    if (!sandbox) {
1038
      env->ThrowTypeError(
1039
2
          "sandbox argument must have been converted to a context.");
1040
2
      return MaybeLocal<Context>();
1041
    }
1042
1043
164
    Local<Context> context = sandbox->context();
1044
164
    if (context.IsEmpty())
1045
      return MaybeLocal<Context>();
1046
164
    return context;
1047
  }
1048
1049
1050
247889
  static bool EvalMachine(Environment* env,
1051
                          const int64_t timeout,
1052
                          const bool display_errors,
1053
                          const bool break_on_sigint,
1054
                          const FunctionCallbackInfo<Value>& args,
1055
                          TryCatch* try_catch) {
1056
247889
    if (!ContextifyScript::InstanceOf(env, args.Holder())) {
1057
      env->ThrowTypeError(
1058
          "Script methods can only be called on script instances.");
1059
      return false;
1060
    }
1061
1062
    ContextifyScript* wrapped_script;
1063
247889
    ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder(), false);
1064
    Local<UnboundScript> unbound_script =
1065
247889
        PersistentToLocal(env->isolate(), wrapped_script->script_);
1066
247889
    Local<Script> script = unbound_script->BindToCurrentContext();
1067
1068
    Local<Value> result;
1069
247889
    bool timed_out = false;
1070
247889
    bool received_signal = false;
1071

247889
    if (break_on_sigint && timeout != -1) {
1072
      Watchdog wd(env->isolate(), timeout, &timed_out);
1073
      SigintWatchdog swd(env->isolate(), &received_signal);
1074
      result = script->Run();
1075
247889
    } else if (break_on_sigint) {
1076
82
      SigintWatchdog swd(env->isolate(), &received_signal);
1077
82
      result = script->Run();
1078
247807
    } else if (timeout != -1) {
1079
9
      Watchdog wd(env->isolate(), timeout, &timed_out);
1080
9
      result = script->Run();
1081
    } else {
1082
247798
      result = script->Run();
1083
    }
1084
1085

247878
    if (timed_out || received_signal) {
1086
      // It is possible that execution was terminated by another timeout in
1087
      // which this timeout is nested, so check whether one of the watchdogs
1088
      // from this invocation is responsible for termination.
1089
15
      if (timed_out) {
1090
4
        env->ThrowError("Script execution timed out.");
1091
11
      } else if (received_signal) {
1092
11
        env->ThrowError("Script execution interrupted.");
1093
      }
1094
15
      env->isolate()->CancelTerminateExecution();
1095
    }
1096
1097
247878
    if (try_catch->HasCaught()) {
1098

92
      if (!timed_out && !received_signal && display_errors) {
1099
        // We should decorate non-termination exceptions
1100
49
        DecorateErrorStack(env, *try_catch);
1101
      }
1102
1103
      // If there was an exception thrown during script execution, re-throw it.
1104
      // If one of the above checks threw, re-throw the exception instead of
1105
      // letting try_catch catch it.
1106
      // If execution has been terminated, but not by one of the watchdogs from
1107
      // this invocation, this will re-throw a `null` value.
1108
92
      try_catch->ReThrow();
1109
1110
92
      return false;
1111
    }
1112
1113
495572
    args.GetReturnValue().Set(result);
1114
247786
    return true;
1115
  }
1116
1117
1118
248199
  ContextifyScript(Environment* env, Local<Object> object)
1119
248199
      : BaseObject(env, object) {
1120
248199
    MakeWeak<ContextifyScript>(this);
1121
248199
  }
1122
1123
1124
613005
  ~ContextifyScript() override {
1125
204335
    script_.Reset();
1126
408670
  }
1127
};
1128
1129
1130
3348
void InitContextify(Local<Object> target,
1131
                    Local<Value> unused,
1132
                    Local<Context> context) {
1133
3348
  Environment* env = Environment::GetCurrent(context);
1134
3348
  ContextifyContext::Init(env, target);
1135
3348
  ContextifyScript::Init(env, target);
1136
3348
}
1137
1138
}  // anonymous namespace
1139
}  // namespace node
1140
1141
3391
NODE_BUILTIN_MODULE_CONTEXT_AWARE(contextify, node::InitContextify)