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: 475 522 91.0 %
Date: 2017-11-19 Branches: 258 328 78.7 %

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
#include "v8-debug.h"
26
27
namespace node {
28
29
using v8::Array;
30
using v8::ArrayBuffer;
31
using v8::Boolean;
32
using v8::Context;
33
using v8::Debug;
34
using v8::EscapableHandleScope;
35
using v8::External;
36
using v8::Function;
37
using v8::FunctionCallbackInfo;
38
using v8::FunctionTemplate;
39
using v8::HandleScope;
40
using v8::IndexedPropertyHandlerConfiguration;
41
using v8::Integer;
42
using v8::Just;
43
using v8::Local;
44
using v8::Maybe;
45
using v8::MaybeLocal;
46
using v8::Name;
47
using v8::NamedPropertyHandlerConfiguration;
48
using v8::Nothing;
49
using v8::Object;
50
using v8::ObjectTemplate;
51
using v8::Persistent;
52
using v8::PropertyAttribute;
53
using v8::PropertyCallbackInfo;
54
using v8::PropertyDescriptor;
55
using v8::Script;
56
using v8::ScriptCompiler;
57
using v8::ScriptOrigin;
58
using v8::String;
59
using v8::Symbol;
60
using v8::TryCatch;
61
using v8::Uint32;
62
using v8::Uint8Array;
63
using v8::UnboundScript;
64
using v8::Value;
65
using v8::WeakCallbackInfo;
66
67
// The vm module executes code in a sandboxed environment with a different
68
// global object than the rest of the code. This is achieved by applying
69
// every call that changes or queries a property on the global `this` in the
70
// sandboxed code, to the sandbox object.
71
//
72
// The implementation uses V8's interceptors for methods like `set`, `get`,
73
// `delete`, `defineProperty`, and for any query of the property attributes.
74
// Property handlers with interceptors are set on the object template for
75
// the sandboxed code. Handlers for both named properties and for indexed
76
// properties are used. Their functionality is almost identical, the indexed
77
// interceptors mostly just call the named interceptors.
78
//
79
// For every `get` of a global property in the sandboxed context, the
80
// interceptor callback checks the sandbox object for the property.
81
// If the property is defined on the sandbox, that result is returned to
82
// the original call instead of finishing the query on the global object.
83
//
84
// For every `set` of a global property, the interceptor callback defines or
85
// changes the property both on the sandbox and the global proxy.
86
87
namespace {
88
89
// Convert an int to a V8 Name (String or Symbol).
90
4
Local<Name> Uint32ToName(Local<Context> context, uint32_t index) {
91
16
  return Uint32::New(context->GetIsolate(), index)->ToString(context)
92
12
      .ToLocalChecked();
93
}
94
95
class ContextifyContext {
96
 protected:
97
  // V8 reserves the first field in context objects for the debugger. We use the
98
  // second field to hold a reference to the sandbox object.
99
  enum { kSandboxObjectIndex = 1 };
100
101
  Environment* const env_;
102
  Persistent<Context> context_;
103
104
 public:
105
281
  ContextifyContext(Environment* env, Local<Object> sandbox_obj) : env_(env) {
106
281
    Local<Context> v8_context = CreateV8Context(env, sandbox_obj);
107
281
    context_.Reset(env->isolate(), v8_context);
108
109
    // Allocation failure or maximum call stack size reached
110
562
    if (context_.IsEmpty())
111
281
      return;
112
281
    context_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter);
113
281
    context_.MarkIndependent();
114
  }
115
116
117
84
  ~ContextifyContext() {
118
42
    context_.Reset();
119
42
  }
120
121
122
2003485
  inline Environment* env() const {
123
2003485
    return env_;
124
  }
125
126
127
2003106
  inline Local<Context> context() const {
128
2003106
    return PersistentToLocal(env()->isolate(), context_);
129
  }
130
131
132
440
  inline Local<Object> global_proxy() const {
133
880
    return context()->Global();
134
  }
135
136
137
1000725
  inline Local<Object> sandbox() const {
138
3002175
    return Local<Object>::Cast(context()->GetEmbedderData(kSandboxObjectIndex));
139
  }
140
141
  // This is an object that just keeps an internal pointer to this
142
  // ContextifyContext.  It's passed to the NamedPropertyHandler.  If we
143
  // pass the main JavaScript context object we're embedded in, then the
144
  // NamedPropertyHandler will store a reference to it forever and keep it
145
  // from getting gc'd.
146
562
  Local<Value> CreateDataWrapper(Environment* env) {
147
562
    EscapableHandleScope scope(env->isolate());
148
    Local<Object> wrapper =
149
        env->script_data_constructor_function()
150
1686
            ->NewInstance(env->context()).FromMaybe(Local<Object>());
151
562
    if (wrapper.IsEmpty())
152
      return scope.Escape(Local<Value>::New(env->isolate(), Local<Value>()));
153
154
562
    Wrap(wrapper, this);
155
562
    return scope.Escape(wrapper);
156
  }
157
158
159
281
  Local<Context> CreateV8Context(Environment* env, Local<Object> sandbox_obj) {
160
281
    EscapableHandleScope scope(env->isolate());
161
    Local<FunctionTemplate> function_template =
162
281
        FunctionTemplate::New(env->isolate());
163
164
562
    function_template->SetClassName(sandbox_obj->GetConstructorName());
165
166
    Local<ObjectTemplate> object_template =
167
281
        function_template->InstanceTemplate();
168
169
    NamedPropertyHandlerConfiguration config(PropertyGetterCallback,
170
                                             PropertySetterCallback,
171
                                             PropertyDescriptorCallback,
172
                                             PropertyDeleterCallback,
173
                                             PropertyEnumeratorCallback,
174
                                             PropertyDefinerCallback,
175
281
                                             CreateDataWrapper(env));
176
177
    IndexedPropertyHandlerConfiguration indexed_config(
178
        IndexedPropertyGetterCallback,
179
        IndexedPropertySetterCallback,
180
        IndexedPropertyDescriptorCallback,
181
        IndexedPropertyDeleterCallback,
182
        PropertyEnumeratorCallback,
183
        IndexedPropertyDefinerCallback,
184
281
        CreateDataWrapper(env));
185
186
281
    object_template->SetHandler(config);
187
281
    object_template->SetHandler(indexed_config);
188
189
281
    Local<Context> ctx = NewContext(env->isolate(), object_template);
190
191
281
    if (ctx.IsEmpty()) {
192
      env->ThrowError("Could not instantiate context");
193
      return Local<Context>();
194
    }
195
196
843
    ctx->SetSecurityToken(env->context()->GetSecurityToken());
197
198
    // We need to tie the lifetime of the sandbox object with the lifetime of
199
    // newly created context. We do this by making them hold references to each
200
    // other. The context can directly hold a reference to the sandbox as an
201
    // embedder data field. However, we cannot hold a reference to a v8::Context
202
    // directly in an Object, we instead hold onto the new context's global
203
    // object instead (which then has a reference to the context).
204
281
    ctx->SetEmbedderData(kSandboxObjectIndex, sandbox_obj);
205
    sandbox_obj->SetPrivate(env->context(),
206
                            env->contextify_global_private_symbol(),
207
843
                            ctx->Global());
208
209
281
    env->AssignToContext(ctx);
210
211
281
    return scope.Escape(ctx);
212
  }
213
214
215
3287
  static void Init(Environment* env, Local<Object> target) {
216
    Local<FunctionTemplate> function_template =
217
3287
        FunctionTemplate::New(env->isolate());
218
6574
    function_template->InstanceTemplate()->SetInternalFieldCount(1);
219
3287
    env->set_script_data_constructor_function(function_template->GetFunction());
220
221
3287
    env->SetMethod(target, "runInDebugContext", RunInDebugContext);
222
3287
    env->SetMethod(target, "makeContext", MakeContext);
223
3287
    env->SetMethod(target, "isContext", IsContext);
224
3287
  }
225
226
227
17
  static void RunInDebugContext(const FunctionCallbackInfo<Value>& args) {
228
51
    Local<String> script_source(args[0]->ToString(args.GetIsolate()));
229
17
    if (script_source.IsEmpty())
230
5
      return;  // Exception pending.
231
16
    Local<Context> debug_context = Debug::GetDebugContext(args.GetIsolate());
232
16
    Environment* env = Environment::GetCurrent(args);
233
16
    if (debug_context.IsEmpty()) {
234
      // Force-load the debug context.
235
68
      auto dummy_event_listener = [] (const Debug::EventDetails&) {};
236
8
      Debug::SetDebugEventListener(args.GetIsolate(), dummy_event_listener);
237
4
      debug_context = Debug::GetDebugContext(args.GetIsolate());
238
4
      CHECK(!debug_context.IsEmpty());
239
      // Ensure that the debug context has an Environment assigned in case
240
      // a fatal error is raised.  The fatal exception handler in node.cc
241
      // is not equipped to deal with contexts that don't have one and
242
      // can't easily be taught that due to a deficiency in the V8 API:
243
      // there is no way for the embedder to tell if the data index is
244
      // in use.
245
4
      const int index = Environment::kContextEmbedderDataIndex;
246
4
      debug_context->SetAlignedPointerInEmbedderData(index, env);
247
    }
248
249
    Context::Scope context_scope(debug_context);
250
16
    MaybeLocal<Script> script = Script::Compile(debug_context, script_source);
251
16
    if (script.IsEmpty())
252
3
      return;  // Exception pending.
253
65
    args.GetReturnValue().Set(script.ToLocalChecked()->Run());
254
  }
255
256
257
281
  static void MakeContext(const FunctionCallbackInfo<Value>& args) {
258
281
    Environment* env = Environment::GetCurrent(args);
259
260
562
    if (!args[0]->IsObject()) {
261
      return env->ThrowTypeError("sandbox argument must be an object.");
262
    }
263
562
    Local<Object> sandbox = args[0].As<Object>();
264
265
    // Don't allow contextifying a sandbox multiple times.
266
843
    CHECK(
267
        !sandbox->HasPrivate(
268
            env->context(),
269
            env->contextify_context_private_symbol()).FromJust());
270
271
281
    TryCatch try_catch(env->isolate());
272
281
    ContextifyContext* context = new ContextifyContext(env, sandbox);
273
274
281
    if (try_catch.HasCaught()) {
275
      try_catch.ReThrow();
276
      return;
277
    }
278
279
562
    if (context->context().IsEmpty())
280
      return;
281
282
    sandbox->SetPrivate(
283
        env->context(),
284
        env->contextify_context_private_symbol(),
285
843
        External::New(env->isolate(), context));
286
  }
287
288
289
176
  static void IsContext(const FunctionCallbackInfo<Value>& args) {
290
176
    Environment* env = Environment::GetCurrent(args);
291
292
352
    if (!args[0]->IsObject()) {
293
3
      env->ThrowTypeError("sandbox must be an object");
294
179
      return;
295
    }
296
346
    Local<Object> sandbox = args[0].As<Object>();
297
298
    Maybe<bool> result =
299
        sandbox->HasPrivate(env->context(),
300
346
                            env->contextify_context_private_symbol());
301
519
    args.GetReturnValue().Set(result.FromJust());
302
  }
303
304
305
42
  static void WeakCallback(const WeakCallbackInfo<ContextifyContext>& data) {
306
42
    ContextifyContext* context = data.GetParameter();
307
42
    delete context;
308
42
  }
309
310
311
547
  static ContextifyContext* ContextFromContextifiedSandbox(
312
      Environment* env,
313
      const Local<Object>& sandbox) {
314
    MaybeLocal<Value> maybe_value =
315
        sandbox->GetPrivate(env->context(),
316
1094
                            env->contextify_context_private_symbol());
317
    Local<Value> context_external_v;
318

1094
    if (maybe_value.ToLocal(&context_external_v) &&
319
547
        context_external_v->IsExternal()) {
320
543
      Local<External> context_external = context_external_v.As<External>();
321
543
      return static_cast<ContextifyContext*>(context_external->Value());
322
    }
323
4
    return nullptr;
324
  }
325
326
327
1000691
  static void PropertyGetterCallback(
328
      Local<Name> property,
329
      const PropertyCallbackInfo<Value>& args) {
330
    ContextifyContext* ctx;
331
2001663
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
332
333
    // Still initializing
334
2001382
    if (ctx->context_.IsEmpty())
335
281
      return;
336
337
1000410
    Local<Context> context = ctx->context();
338
1000410
    Local<Object> sandbox = ctx->sandbox();
339
    MaybeLocal<Value> maybe_rv =
340
1000410
        sandbox->GetRealNamedProperty(context, property);
341
1000410
    if (maybe_rv.IsEmpty()) {
342
      maybe_rv =
343
340
          ctx->global_proxy()->GetRealNamedProperty(context, property);
344
    }
345
346
    Local<Value> rv;
347
1000410
    if (maybe_rv.ToLocal(&rv)) {
348
1000345
      if (rv == sandbox)
349
8
        rv = ctx->global_proxy();
350
351
2000690
      args.GetReturnValue().Set(rv);
352
    }
353
  }
354
355
356
132
  static void PropertySetterCallback(
357
      Local<Name> property,
358
      Local<Value> value,
359
      const PropertyCallbackInfo<Value>& args) {
360
    ContextifyContext* ctx;
361
278
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
362
363
    // Still initializing
364
264
    if (ctx->context_.IsEmpty())
365
      return;
366
367
132
    auto attributes = PropertyAttribute::None;
368
132
    bool is_declared_on_global_proxy = ctx->global_proxy()
369
528
        ->GetRealNamedPropertyAttributes(ctx->context(), property)
370
264
        .To(&attributes);
371
    bool read_only =
372
132
        static_cast<int>(attributes) &
373
132
        static_cast<int>(PropertyAttribute::ReadOnly);
374
375
132
    bool is_declared_on_sandbox = ctx->sandbox()
376
528
        ->GetRealNamedPropertyAttributes(ctx->context(), property)
377
264
        .To(&attributes);
378

260
    read_only = read_only ||
379
128
        (static_cast<int>(attributes) &
380
132
        static_cast<int>(PropertyAttribute::ReadOnly));
381
382
132
    if (read_only)
383
13
      return;
384
385
    // true for x = 5
386
    // false for this.x = 5
387
    // false for Object.defineProperty(this, 'foo', ...)
388
    // false for vmResult.x = 5 where vmResult = vm.runInContext();
389
238
    bool is_contextual_store = ctx->global_proxy() != args.This();
390
391
    // Indicator to not return before setting (undeclared) function declarations
392
    // on the sandbox in strict mode, i.e. args.ShouldThrowOnError() = true.
393
    // True for 'function f() {}', 'this.f = function() {}',
394
    // 'var f = function()'.
395
    // In effect only for 'function f() {}' because
396
    // var f = function(), is_declared = true
397
    // this.f = function() {}, is_contextual_store = false.
398
119
    bool is_function = value->IsFunction();
399
400

119
    bool is_declared = is_declared_on_global_proxy || is_declared_on_sandbox;
401


191
    if (!is_declared && args.ShouldThrowOnError() && is_contextual_store &&
402
19
        !is_function)
403
1
      return;
404
405

314
    if (!is_declared_on_global_proxy && is_declared_on_sandbox  &&
406

133
        args.ShouldThrowOnError() && is_contextual_store && !is_function) {
407
      // The property exists on the sandbox but not on the global
408
      // proxy. Setting it would throw because we are in strict mode.
409
      // Don't attempt to set it by signaling that the call was
410
      // intercepted. Only change the value on the sandbox.
411
2
      args.GetReturnValue().Set(false);
412
    }
413
414
236
    ctx->sandbox()->Set(property, value);
415
  }
416
417
418
28
  static void PropertyDescriptorCallback(
419
      Local<Name> property,
420
      const PropertyCallbackInfo<Value>& args) {
421
    ContextifyContext* ctx;
422
56
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
423
424
    // Still initializing
425
56
    if (ctx->context_.IsEmpty())
426
      return;
427
428
28
    Local<Context> context = ctx->context();
429
430
28
    Local<Object> sandbox = ctx->sandbox();
431
432
84
    if (sandbox->HasOwnProperty(context, property).FromMaybe(false)) {
433
      args.GetReturnValue().Set(
434
13
          sandbox->GetOwnPropertyDescriptor(context, property)
435
39
              .ToLocalChecked());
436
    }
437
  }
438
439
440
15
  static void PropertyDefinerCallback(
441
      Local<Name> property,
442
      const PropertyDescriptor& desc,
443
      const PropertyCallbackInfo<Value>& args) {
444
    ContextifyContext* ctx;
445
30
    ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
446
447
    // Still initializing
448
30
    if (ctx->context_.IsEmpty())
449
      return;
450
451
15
    Local<Context> context = ctx->context();
452
15
    v8::Isolate* isolate = context->GetIsolate();
453
454
15
    auto attributes = PropertyAttribute::None;
455
    bool is_declared =
456
15
        ctx->global_proxy()->GetRealNamedPropertyAttributes(ctx->context(),
457
60
                                                            property)
458
30
            .To(&attributes);
459
    bool read_only =
460
15
        static_cast<int>(attributes) &
461
15
            static_cast<int>(PropertyAttribute::ReadOnly);
462
463
    // If the property is set on the global as read_only, don't change it on
464
    // the global or sandbox.
465

15
    if (is_declared && read_only)
466
      return;
467
468
15
    Local<Object> sandbox = ctx->sandbox();
469
470
    auto define_prop_on_sandbox =
471
15
        [&] (PropertyDescriptor* desc_for_sandbox) {
472
15
          if (desc.has_enumerable()) {
473
2
            desc_for_sandbox->set_enumerable(desc.enumerable());
474
          }
475
15
          if (desc.has_configurable()) {
476
1
            desc_for_sandbox->set_configurable(desc.configurable());
477
          }
478
          // Set the property on the sandbox.
479
30
          sandbox->DefineProperty(context, property, *desc_for_sandbox)
480
30
              .FromJust();
481
30
        };
482
483

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

23
          desc.has_set() ? desc.set() : v8::Undefined(isolate).As<Value>());
487
488
5
      define_prop_on_sandbox(&desc_for_sandbox);
489
    } else {
490
      Local<Value> value =
491

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

943644
    return !value.IsEmpty() &&
735
707733
           env->script_context_constructor_template()->HasInstance(value);
736
  }
737
738
739
  // args: [options]
740
235536
  static void RunInThisContext(const FunctionCallbackInfo<Value>& args) {
741
235536
    Environment* env = Environment::GetCurrent(args);
742
743
    // Assemble arguments
744
235536
    TryCatch try_catch(args.GetIsolate());
745
235536
    Maybe<int64_t> maybe_timeout = GetTimeoutArg(env, args[0]);
746
235536
    Maybe<bool> maybe_display_errors = GetDisplayErrorsArg(env, args[0]);
747
235536
    Maybe<bool> maybe_break_on_sigint = GetBreakOnSigintArg(env, args[0]);
748
235536
    if (try_catch.HasCaught()) {
749
4
      try_catch.ReThrow();
750
235529
      return;
751
    }
752
753
235532
    int64_t timeout = maybe_timeout.ToChecked();
754
235532
    bool display_errors = maybe_display_errors.ToChecked();
755
235532
    bool break_on_sigint = maybe_break_on_sigint.ToChecked();
756
757
    // Do the eval within this context
758
    EvalMachine(env, timeout, display_errors, break_on_sigint, args,
759
235532
                &try_catch);
760
  }
761
762
  // args: sandbox, [options]
763
388
  static void RunInContext(const FunctionCallbackInfo<Value>& args) {
764
388
    Environment* env = Environment::GetCurrent(args);
765
766
    int64_t timeout;
767
    bool display_errors;
768
    bool break_on_sigint;
769
770
    // Assemble arguments
771
776
    if (!args[0]->IsObject()) {
772
      return env->ThrowTypeError(
773
55
          "contextifiedSandbox argument must be an object.");
774
    }
775
776
762
    Local<Object> sandbox = args[0].As<Object>();
777
    {
778
381
      TryCatch try_catch(env->isolate());
779
381
      Maybe<int64_t> maybe_timeout = GetTimeoutArg(env, args[1]);
780
381
      Maybe<bool> maybe_display_errors = GetDisplayErrorsArg(env, args[1]);
781
381
      Maybe<bool> maybe_break_on_sigint = GetBreakOnSigintArg(env, args[1]);
782
381
      if (try_catch.HasCaught()) {
783
        try_catch.ReThrow();
784
        return;
785
      }
786
787
381
      timeout = maybe_timeout.ToChecked();
788
381
      display_errors = maybe_display_errors.ToChecked();
789
381
      break_on_sigint = maybe_break_on_sigint.ToChecked();
790
    }
791
792
    // Get the context from the sandbox
793
    ContextifyContext* contextify_context =
794
381
        ContextifyContext::ContextFromContextifiedSandbox(env, sandbox);
795
381
    if (contextify_context == nullptr) {
796
      return env->ThrowTypeError(
797
2
          "sandbox argument must have been converted to a context.");
798
    }
799
800
758
    if (contextify_context->context().IsEmpty())
801
      return;
802
803
    {
804
379
      TryCatch try_catch(env->isolate());
805
      // Do the eval within the context
806
719
      Context::Scope context_scope(contextify_context->context());
807
      EvalMachine(contextify_context->env(),
808
                  timeout,
809
                  display_errors,
810
                  break_on_sigint,
811
                  args,
812
379
                  &try_catch);
813
814
379
      if (try_catch.HasCaught()) {
815
39
        try_catch.ReThrow();
816
39
        return;
817
340
      }
818
    }
819
  }
820
821
318
  static void DecorateErrorStack(Environment* env, const TryCatch& try_catch) {
822
318
    Local<Value> exception = try_catch.Exception();
823
824
318
    if (!exception->IsObject())
825
14
      return;
826
827
313
    Local<Object> err_obj = exception.As<Object>();
828
829
313
    if (IsExceptionDecorated(env, err_obj))
830
3
      return;
831
832
310
    AppendExceptionLine(env, exception, try_catch.Message(), CONTEXTIFY_ERROR);
833
620
    Local<Value> stack = err_obj->Get(env->stack_string());
834
    MaybeLocal<Value> maybe_value =
835
        err_obj->GetPrivate(
836
            env->context(),
837
620
            env->arrow_message_private_symbol());
838
839
    Local<Value> arrow;
840

930
    if (!(maybe_value.ToLocal(&arrow) && arrow->IsString())) {
841
      return;
842
    }
843
844

930
    if (stack.IsEmpty() || !stack->IsString()) {
845
1
      return;
846
    }
847
848
    Local<String> decorated_stack = String::Concat(
849
        String::Concat(arrow.As<String>(),
850
          FIXED_ONE_BYTE_STRING(env->isolate(), "\n")),
851
618
        stack.As<String>());
852
618
    err_obj->Set(env->stack_string(), decorated_stack);
853
    err_obj->SetPrivate(
854
        env->context(),
855
        env->decorated_private_symbol(),
856
927
        True(env->isolate()));
857
  }
858
859
235917
  static Maybe<bool> GetBreakOnSigintArg(Environment* env,
860
                                         Local<Value> options) {
861

520646
    if (options->IsUndefined() || options->IsString()) {
862
211511
      return Just(false);
863
    }
864
24406
    if (!options->IsObject()) {
865
      env->ThrowTypeError("options must be an object");
866
      return Nothing<bool>();
867
    }
868
869
24406
    Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "breakOnSigint");
870
    MaybeLocal<Value> maybe_value =
871
73218
        options.As<Object>()->Get(env->context(), key);
872
24406
    if (maybe_value.IsEmpty())
873
      return Nothing<bool>();
874
875
24406
    Local<Value> value = maybe_value.ToLocalChecked();
876
24406
    return Just(value->IsTrue());
877
  }
878
879
235917
  static Maybe<int64_t> GetTimeoutArg(Environment* env, Local<Value> options) {
880

520646
    if (options->IsUndefined() || options->IsString()) {
881
211511
      return Just<int64_t>(-1);
882
    }
883
24406
    if (!options->IsObject()) {
884
      env->ThrowTypeError("options must be an object");
885
      return Nothing<int64_t>();
886
    }
887
888
    MaybeLocal<Value> maybe_value =
889
97624
        options.As<Object>()->Get(env->context(), env->timeout_string());
890
24406
    if (maybe_value.IsEmpty())
891
1
      return Nothing<int64_t>();
892
893
24405
    Local<Value> value = maybe_value.ToLocalChecked();
894
48810
    if (value->IsUndefined()) {
895
24394
      return Just<int64_t>(-1);
896
    }
897
898
22
    Maybe<int64_t> timeout = value->IntegerValue(env->context());
899
900

22
    if (timeout.IsJust() && timeout.ToChecked() <= 0) {
901
2
      env->ThrowRangeError("timeout must be a positive number");
902
2
      return Nothing<int64_t>();
903
    }
904
905
9
    return timeout;
906
  }
907
908
909
472135
  static Maybe<bool> GetDisplayErrorsArg(Environment* env,
910
                                         Local<Value> options) {
911

1464674
    if (options->IsUndefined() || options->IsString()) {
912
211934
      return Just(true);
913
    }
914
260201
    if (!options->IsObject()) {
915
      env->ThrowTypeError("options must be an object");
916
      return Nothing<bool>();
917
    }
918
919
260201
    Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "displayErrors");
920
    MaybeLocal<Value> maybe_value =
921
780603
        options.As<Object>()->Get(env->context(), key);
922
260201
    if (maybe_value.IsEmpty())
923
1
      return Nothing<bool>();
924
925
260200
    Local<Value> value = maybe_value.ToLocalChecked();
926
520400
    if (value->IsUndefined())
927
379
      return Just(true);
928
929
519642
    return value->BooleanValue(env->context());
930
  }
931
932
933
236218
  static MaybeLocal<String> GetFilenameArg(Environment* env,
934
                                           Local<Value> options) {
935
    Local<String> defaultFilename =
936
236218
        FIXED_ONE_BYTE_STRING(env->isolate(), "evalmachine.<anonymous>");
937
938
472436
    if (options->IsUndefined()) {
939
422
      return defaultFilename;
940
    }
941
471592
    if (options->IsString()) {
942
1
      return options.As<String>();
943
    }
944
235795
    if (!options->IsObject()) {
945
      env->ThrowTypeError("options must be an object");
946
      return Local<String>();
947
    }
948
949
235795
    Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "filename");
950
    MaybeLocal<Value> maybe_value =
951
707385
        options.As<Object>()->Get(env->context(), key);
952
235795
    if (maybe_value.IsEmpty())
953
1
      return MaybeLocal<String>();
954
955
235794
    Local<Value> value = maybe_value.ToLocalChecked();
956
471588
    if (value->IsUndefined())
957
170
      return defaultFilename;
958
471248
    return value->ToString(env->context());
959
  }
960
961
962
236218
  static MaybeLocal<Uint8Array> GetCachedData(Environment* env,
963
                                              Local<Value> options) {
964
236218
    if (!options->IsObject()) {
965
423
      return MaybeLocal<Uint8Array>();
966
    }
967
968
    MaybeLocal<Value> maybe_value =
969
943180
        options.As<Object>()->Get(env->context(), env->cached_data_string());
970
235795
    if (maybe_value.IsEmpty())
971
1
      return MaybeLocal<Uint8Array>();
972
973
235794
    Local<Value> value = maybe_value.ToLocalChecked();
974
471588
    if (value->IsUndefined()) {
975
235790
      return MaybeLocal<Uint8Array>();
976
    }
977
978
4
    if (!value->IsUint8Array()) {
979
1
      env->ThrowTypeError("options.cachedData must be a Buffer instance");
980
1
      return MaybeLocal<Uint8Array>();
981
    }
982
983
3
    return value.As<Uint8Array>();
984
  }
985
986
987
236218
  static Maybe<bool> GetProduceCachedData(Environment* env,
988
                                          Local<Value> options) {
989
236218
    if (!options->IsObject()) {
990
423
      return Just(false);
991
    }
992
993
    MaybeLocal<Value> maybe_value =
994
        options.As<Object>()->Get(env->context(),
995
943180
                                  env->produce_cached_data_string());
996
235795
    if (maybe_value.IsEmpty())
997
1
      return Nothing<bool>();
998
999
235794
    Local<Value> value = maybe_value.ToLocalChecked();
1000
235794
    return Just(value->IsTrue());
1001
  }
1002
1003
1004
236218
  static MaybeLocal<Integer> GetLineOffsetArg(Environment* env,
1005
                                              Local<Value> options) {
1006
236218
    Local<Integer> defaultLineOffset = Integer::New(env->isolate(), 0);
1007
1008
236218
    if (!options->IsObject()) {
1009
423
      return defaultLineOffset;
1010
    }
1011
1012
235795
    Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "lineOffset");
1013
    MaybeLocal<Value> maybe_value =
1014
707385
        options.As<Object>()->Get(env->context(), key);
1015
235795
    if (maybe_value.IsEmpty())
1016
1
      return MaybeLocal<Integer>();
1017
1018
235794
    Local<Value> value = maybe_value.ToLocalChecked();
1019
471588
    if (value->IsUndefined())
1020
860
      return defaultLineOffset;
1021
1022
469868
    return value->ToInteger(env->context());
1023
  }
1024
1025
1026
236218
  static MaybeLocal<Integer> GetColumnOffsetArg(Environment* env,
1027
                                                Local<Value> options) {
1028
236218
    Local<Integer> defaultColumnOffset = Integer::New(env->isolate(), 0);
1029
1030
236218
    if (!options->IsObject()) {
1031
423
      return defaultColumnOffset;
1032
    }
1033
1034
235795
    Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "columnOffset");
1035
    MaybeLocal<Value> maybe_value =
1036
707385
      options.As<Object>()->Get(env->context(), key);
1037
235795
    if (maybe_value.IsEmpty())
1038
1
      return MaybeLocal<Integer>();
1039
1040
235794
    Local<Value> value = maybe_value.ToLocalChecked();
1041
471588
    if (value->IsUndefined())
1042
235793
      return defaultColumnOffset;
1043
1044
2
    return value->ToInteger(env->context());
1045
  }
1046
1047
236218
  static MaybeLocal<Context> GetContext(Environment* env,
1048
                                        Local<Value> options) {
1049
236218
    if (!options->IsObject())
1050
423
      return MaybeLocal<Context>();
1051
1052
    MaybeLocal<Value> maybe_value =
1053
        options.As<Object>()->Get(env->context(),
1054
943180
                                  env->vm_parsing_context_symbol());
1055
    Local<Value> value;
1056
235795
    if (!maybe_value.ToLocal(&value))
1057
      return MaybeLocal<Context>();
1058
1059
235795
    if (!value->IsObject()) {
1060
471258
      if (!value->IsNullOrUndefined()) {
1061
        env->ThrowTypeError(
1062
3
            "contextifiedSandbox argument must be an object.");
1063
      }
1064
235629
      return MaybeLocal<Context>();
1065
    }
1066
1067
    ContextifyContext* sandbox =
1068
        ContextifyContext::ContextFromContextifiedSandbox(
1069
166
            env, value.As<Object>());
1070
166
    if (!sandbox) {
1071
      env->ThrowTypeError(
1072
2
          "sandbox argument must have been converted to a context.");
1073
2
      return MaybeLocal<Context>();
1074
    }
1075
1076
164
    Local<Context> context = sandbox->context();
1077
164
    if (context.IsEmpty())
1078
      return MaybeLocal<Context>();
1079
164
    return context;
1080
  }
1081
1082
1083
235911
  static bool EvalMachine(Environment* env,
1084
                          const int64_t timeout,
1085
                          const bool display_errors,
1086
                          const bool break_on_sigint,
1087
                          const FunctionCallbackInfo<Value>& args,
1088
                          TryCatch* try_catch) {
1089
235911
    if (!ContextifyScript::InstanceOf(env, args.Holder())) {
1090
      env->ThrowTypeError(
1091
          "Script methods can only be called on script instances.");
1092
      return false;
1093
    }
1094
1095
    ContextifyScript* wrapped_script;
1096
235911
    ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder(), false);
1097
    Local<UnboundScript> unbound_script =
1098
235911
        PersistentToLocal(env->isolate(), wrapped_script->script_);
1099
235911
    Local<Script> script = unbound_script->BindToCurrentContext();
1100
1101
    Local<Value> result;
1102
235911
    bool timed_out = false;
1103
235911
    bool received_signal = false;
1104

235911
    if (break_on_sigint && timeout != -1) {
1105
      Watchdog wd(env->isolate(), timeout, &timed_out);
1106
      SigintWatchdog swd(env->isolate(), &received_signal);
1107
      result = script->Run();
1108
235911
    } else if (break_on_sigint) {
1109
82
      SigintWatchdog swd(env->isolate(), &received_signal);
1110
82
      result = script->Run();
1111
235829
    } else if (timeout != -1) {
1112
9
      Watchdog wd(env->isolate(), timeout, &timed_out);
1113
9
      result = script->Run();
1114
    } else {
1115
235820
      result = script->Run();
1116
    }
1117
1118

235900
    if (timed_out || received_signal) {
1119
      // It is possible that execution was terminated by another timeout in
1120
      // which this timeout is nested, so check whether one of the watchdogs
1121
      // from this invocation is responsible for termination.
1122
15
      if (timed_out) {
1123
4
        env->ThrowError("Script execution timed out.");
1124
11
      } else if (received_signal) {
1125
11
        env->ThrowError("Script execution interrupted.");
1126
      }
1127
15
      env->isolate()->CancelTerminateExecution();
1128
    }
1129
1130
235900
    if (try_catch->HasCaught()) {
1131

92
      if (!timed_out && !received_signal && display_errors) {
1132
        // We should decorate non-termination exceptions
1133
49
        DecorateErrorStack(env, *try_catch);
1134
      }
1135
1136
      // If there was an exception thrown during script execution, re-throw it.
1137
      // If one of the above checks threw, re-throw the exception instead of
1138
      // letting try_catch catch it.
1139
      // If execution has been terminated, but not by one of the watchdogs from
1140
      // this invocation, this will re-throw a `null` value.
1141
92
      try_catch->ReThrow();
1142
1143
92
      return false;
1144
    }
1145
1146
471616
    args.GetReturnValue().Set(result);
1147
235808
    return true;
1148
  }
1149
1150
1151
236218
  ContextifyScript(Environment* env, Local<Object> object)
1152
236218
      : BaseObject(env, object) {
1153
236218
    MakeWeak<ContextifyScript>(this);
1154
236218
  }
1155
1156
1157
586923
  ~ContextifyScript() override {
1158
195641
    script_.Reset();
1159
391282
  }
1160
};
1161
1162
1163
3287
void InitContextify(Local<Object> target,
1164
                    Local<Value> unused,
1165
                    Local<Context> context) {
1166
3287
  Environment* env = Environment::GetCurrent(context);
1167
3287
  ContextifyContext::Init(env, target);
1168
3287
  ContextifyScript::Init(env, target);
1169
3287
}
1170
1171
}  // anonymous namespace
1172
}  // namespace node
1173
1174
3332
NODE_BUILTIN_MODULE_CONTEXT_AWARE(contextify, node::InitContextify)