GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/node_contextify.cc Lines: 421 473 89.0 %
Date: 2019-02-13 22:28:58 Branches: 251 364 69.0 %

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_errors.h"
23
#include "node_internals.h"
24
#include "node_watchdog.h"
25
#include "base_object-inl.h"
26
#include "node_contextify.h"
27
#include "node_context_data.h"
28
#include "node_errors.h"
29
#include "module_wrap.h"
30
31
namespace node {
32
namespace contextify {
33
34
using errors::TryCatchScope;
35
36
using v8::Array;
37
using v8::ArrayBuffer;
38
using v8::ArrayBufferView;
39
using v8::Boolean;
40
using v8::Context;
41
using v8::EscapableHandleScope;
42
using v8::External;
43
using v8::Function;
44
using v8::FunctionCallbackInfo;
45
using v8::FunctionTemplate;
46
using v8::HandleScope;
47
using v8::IndexedPropertyHandlerConfiguration;
48
using v8::Integer;
49
using v8::Isolate;
50
using v8::Just;
51
using v8::Local;
52
using v8::Maybe;
53
using v8::MaybeLocal;
54
using v8::Name;
55
using v8::NamedPropertyHandlerConfiguration;
56
using v8::Number;
57
using v8::Object;
58
using v8::ObjectTemplate;
59
using v8::PrimitiveArray;
60
using v8::PropertyAttribute;
61
using v8::PropertyCallbackInfo;
62
using v8::PropertyDescriptor;
63
using v8::Script;
64
using v8::ScriptCompiler;
65
using v8::ScriptOrigin;
66
using v8::String;
67
using v8::Symbol;
68
using v8::Uint32;
69
using v8::UnboundScript;
70
using v8::Value;
71
using v8::WeakCallbackInfo;
72
using v8::WeakCallbackType;
73
74
// The vm module executes code in a sandboxed environment with a different
75
// global object than the rest of the code. This is achieved by applying
76
// every call that changes or queries a property on the global `this` in the
77
// sandboxed code, to the sandbox object.
78
//
79
// The implementation uses V8's interceptors for methods like `set`, `get`,
80
// `delete`, `defineProperty`, and for any query of the property attributes.
81
// Property handlers with interceptors are set on the object template for
82
// the sandboxed code. Handlers for both named properties and for indexed
83
// properties are used. Their functionality is almost identical, the indexed
84
// interceptors mostly just call the named interceptors.
85
//
86
// For every `get` of a global property in the sandboxed context, the
87
// interceptor callback checks the sandbox object for the property.
88
// If the property is defined on the sandbox, that result is returned to
89
// the original call instead of finishing the query on the global object.
90
//
91
// For every `set` of a global property, the interceptor callback defines or
92
// changes the property both on the sandbox and the global proxy.
93
94
namespace {
95
96
// Convert an int to a V8 Name (String or Symbol).
97
3
Local<Name> Uint32ToName(Local<Context> context, uint32_t index) {
98
12
  return Uint32::New(context->GetIsolate(), index)->ToString(context)
99
9
      .ToLocalChecked();
100
}
101
102
}  // anonymous namespace
103
104
4523
ContextifyContext::ContextifyContext(
105
    Environment* env,
106
4523
    Local<Object> sandbox_obj, const ContextOptions& options) : env_(env) {
107
4523
  MaybeLocal<Context> v8_context = CreateV8Context(env, sandbox_obj, options);
108
109
  // Allocation failure, maximum call stack size reached, termination, etc.
110
9046
  if (v8_context.IsEmpty()) return;
111
112
9046
  context_.Reset(env->isolate(), v8_context.ToLocalChecked());
113
4523
  context_.SetWeak(this, WeakCallback, WeakCallbackType::kParameter);
114
}
115
116
117
// This is an object that just keeps an internal pointer to this
118
// ContextifyContext.  It's passed to the NamedPropertyHandler.  If we
119
// pass the main JavaScript context object we're embedded in, then the
120
// NamedPropertyHandler will store a reference to it forever and keep it
121
// from getting gc'd.
122
4523
MaybeLocal<Object> ContextifyContext::CreateDataWrapper(Environment* env) {
123
  Local<Object> wrapper;
124
9046
  if (!env->script_data_constructor_function()
125
13569
           ->NewInstance(env->context())
126
13569
           .ToLocal(&wrapper)) {
127
    return MaybeLocal<Object>();
128
  }
129
130
4523
  wrapper->SetAlignedPointerInInternalField(0, this);
131
4523
  return wrapper;
132
}
133
134
4523
MaybeLocal<Context> ContextifyContext::CreateV8Context(
135
    Environment* env,
136
    Local<Object> sandbox_obj,
137
    const ContextOptions& options) {
138
4523
  EscapableHandleScope scope(env->isolate());
139
  Local<FunctionTemplate> function_template =
140
4523
      FunctionTemplate::New(env->isolate());
141
142
9046
  function_template->SetClassName(sandbox_obj->GetConstructorName());
143
144
  Local<ObjectTemplate> object_template =
145
4523
      function_template->InstanceTemplate();
146
147
  Local<Object> data_wrapper;
148
9046
  if (!CreateDataWrapper(env).ToLocal(&data_wrapper))
149
    return MaybeLocal<Context>();
150
151
  NamedPropertyHandlerConfiguration config(PropertyGetterCallback,
152
                                           PropertySetterCallback,
153
                                           PropertyDescriptorCallback,
154
                                           PropertyDeleterCallback,
155
                                           PropertyEnumeratorCallback,
156
                                           PropertyDefinerCallback,
157
4523
                                           data_wrapper);
158
159
  IndexedPropertyHandlerConfiguration indexed_config(
160
      IndexedPropertyGetterCallback,
161
      IndexedPropertySetterCallback,
162
      IndexedPropertyDescriptorCallback,
163
      IndexedPropertyDeleterCallback,
164
      PropertyEnumeratorCallback,
165
      IndexedPropertyDefinerCallback,
166
4523
      data_wrapper);
167
168
4523
  object_template->SetHandler(config);
169
4523
  object_template->SetHandler(indexed_config);
170
171
4523
  Local<Context> ctx = NewContext(env->isolate(), object_template);
172
173
4523
  if (ctx.IsEmpty()) {
174
    env->ThrowError("Could not instantiate context");
175
    return MaybeLocal<Context>();
176
  }
177
178
13569
  ctx->SetSecurityToken(env->context()->GetSecurityToken());
179
180
  // We need to tie the lifetime of the sandbox object with the lifetime of
181
  // newly created context. We do this by making them hold references to each
182
  // other. The context can directly hold a reference to the sandbox as an
183
  // embedder data field. However, we cannot hold a reference to a v8::Context
184
  // directly in an Object, we instead hold onto the new context's global
185
  // object instead (which then has a reference to the context).
186
4523
  ctx->SetEmbedderData(ContextEmbedderIndex::kSandboxObject, sandbox_obj);
187
  sandbox_obj->SetPrivate(env->context(),
188
                          env->contextify_global_private_symbol(),
189
13569
                          ctx->Global());
190
191
9046
  Utf8Value name_val(env->isolate(), options.name);
192
13569
  ctx->AllowCodeGenerationFromStrings(options.allow_code_gen_strings->IsTrue());
193
  ctx->SetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration,
194
4523
                       options.allow_code_gen_wasm);
195
196
9046
  ContextInfo info(*name_val);
197
198
9046
  if (!options.origin.IsEmpty()) {
199
2
    Utf8Value origin_val(env->isolate(), options.origin);
200
2
    info.origin = *origin_val;
201
  }
202
203
4523
  env->AssignToContext(ctx, info);
204
205
4523
  return scope.Escape(ctx);
206
}
207
208
209
4399
void ContextifyContext::Init(Environment* env, Local<Object> target) {
210
  Local<FunctionTemplate> function_template =
211
4399
      FunctionTemplate::New(env->isolate());
212
8798
  function_template->InstanceTemplate()->SetInternalFieldCount(1);
213
  env->set_script_data_constructor_function(
214
13197
      function_template->GetFunction(env->context()).ToLocalChecked());
215
216
4399
  env->SetMethod(target, "makeContext", MakeContext);
217
4399
  env->SetMethod(target, "isContext", IsContext);
218
4399
  env->SetMethod(target, "compileFunction", CompileFunction);
219
4399
}
220
221
222
// makeContext(sandbox, name, origin, strings, wasm);
223
4523
void ContextifyContext::MakeContext(const FunctionCallbackInfo<Value>& args) {
224
4523
  Environment* env = Environment::GetCurrent(args);
225
226
4523
  CHECK_EQ(args.Length(), 5);
227
9046
  CHECK(args[0]->IsObject());
228
9046
  Local<Object> sandbox = args[0].As<Object>();
229
230
  // Don't allow contextifying a sandbox multiple times.
231
13569
  CHECK(
232
      !sandbox->HasPrivate(
233
          env->context(),
234
          env->contextify_context_private_symbol()).FromJust());
235
236
4523
  ContextOptions options;
237
238
13569
  CHECK(args[1]->IsString());
239
9046
  options.name = args[1].As<String>();
240
241


31655
  CHECK(args[2]->IsString() || args[2]->IsUndefined());
242
13569
  if (args[2]->IsString()) {
243
4
    options.origin = args[2].As<String>();
244
  }
245
246
9046
  CHECK(args[3]->IsBoolean());
247
9046
  options.allow_code_gen_strings = args[3].As<Boolean>();
248
249
9046
  CHECK(args[4]->IsBoolean());
250
9046
  options.allow_code_gen_wasm = args[4].As<Boolean>();
251
252
4523
  TryCatchScope try_catch(env);
253
4523
  ContextifyContext* context = new ContextifyContext(env, sandbox, options);
254
255
4523
  if (try_catch.HasCaught()) {
256
    try_catch.ReThrow();
257
    return;
258
  }
259
260
9046
  if (context->context().IsEmpty())
261
    return;
262
263
  sandbox->SetPrivate(
264
      env->context(),
265
      env->contextify_context_private_symbol(),
266
13569
      External::New(env->isolate(), context));
267
}
268
269
270
12647
void ContextifyContext::IsContext(const FunctionCallbackInfo<Value>& args) {
271
12647
  Environment* env = Environment::GetCurrent(args);
272
273
25294
  CHECK(args[0]->IsObject());
274
25294
  Local<Object> sandbox = args[0].As<Object>();
275
276
  Maybe<bool> result =
277
      sandbox->HasPrivate(env->context(),
278
25294
                          env->contextify_context_private_symbol());
279
37941
  args.GetReturnValue().Set(result.FromJust());
280
12647
}
281
282
283
4145
void ContextifyContext::WeakCallback(
284
    const WeakCallbackInfo<ContextifyContext>& data) {
285
4145
  ContextifyContext* context = data.GetParameter();
286
4145
  delete context;
287
4145
}
288
289
// static
290
8051
ContextifyContext* ContextifyContext::ContextFromContextifiedSandbox(
291
    Environment* env,
292
    const Local<Object>& sandbox) {
293
  MaybeLocal<Value> maybe_value =
294
      sandbox->GetPrivate(env->context(),
295
16102
                          env->contextify_context_private_symbol());
296
  Local<Value> context_external_v;
297

16102
  if (maybe_value.ToLocal(&context_external_v) &&
298
8051
      context_external_v->IsExternal()) {
299
8051
    Local<External> context_external = context_external_v.As<External>();
300
8051
    return static_cast<ContextifyContext*>(context_external->Value());
301
  }
302
  return nullptr;
303
}
304
305
// static
306
template <typename T>
307
14269096
ContextifyContext* ContextifyContext::Get(const PropertyCallbackInfo<T>& args) {
308
14269096
  Local<Value> data = args.Data();
309
  return static_cast<ContextifyContext*>(
310
42807288
      data.As<Object>()->GetAlignedPointerFromInternalField(0));
311
}
312
313
// static
314
14266258
void ContextifyContext::PropertyGetterCallback(
315
    Local<Name> property,
316
    const PropertyCallbackInfo<Value>& args) {
317
14266258
  ContextifyContext* ctx = ContextifyContext::Get(args);
318
319
  // Still initializing
320
28532516
  if (ctx->context_.IsEmpty())
321
14293396
    return;
322
323
14239120
  Local<Context> context = ctx->context();
324
14239120
  Local<Object> sandbox = ctx->sandbox();
325
  MaybeLocal<Value> maybe_rv =
326
14239120
      sandbox->GetRealNamedProperty(context, property);
327
14239120
  if (maybe_rv.IsEmpty()) {
328
    maybe_rv =
329
13467376
        ctx->global_proxy()->GetRealNamedProperty(context, property);
330
  }
331
332
  Local<Value> rv;
333
14239120
  if (maybe_rv.ToLocal(&rv)) {
334
14235076
    if (rv == sandbox)
335
152
      rv = ctx->global_proxy();
336
337
28470152
    args.GetReturnValue().Set(rv);
338
  }
339
}
340
341
// static
342
2779
void ContextifyContext::PropertySetterCallback(
343
    Local<Name> property,
344
    Local<Value> value,
345
    const PropertyCallbackInfo<Value>& args) {
346
2779
  ContextifyContext* ctx = ContextifyContext::Get(args);
347
348
  // Still initializing
349
5558
  if (ctx->context_.IsEmpty())
350
14
    return;
351
352
2779
  auto attributes = PropertyAttribute::None;
353
  bool is_declared_on_global_proxy = ctx->global_proxy()
354
8337
      ->GetRealNamedPropertyAttributes(ctx->context(), property)
355
5558
      .To(&attributes);
356
  bool read_only =
357
2779
      static_cast<int>(attributes) &
358
2779
      static_cast<int>(PropertyAttribute::ReadOnly);
359
360
  bool is_declared_on_sandbox = ctx->sandbox()
361
8337
      ->GetRealNamedPropertyAttributes(ctx->context(), property)
362
5558
      .To(&attributes);
363

5554
  read_only = read_only ||
364
2775
      (static_cast<int>(attributes) &
365
2779
      static_cast<int>(PropertyAttribute::ReadOnly));
366
367
2779
  if (read_only)
368
13
    return;
369
370
  // true for x = 5
371
  // false for this.x = 5
372
  // false for Object.defineProperty(this, 'foo', ...)
373
  // false for vmResult.x = 5 where vmResult = vm.runInContext();
374
5532
  bool is_contextual_store = ctx->global_proxy() != args.This();
375
376
  // Indicator to not return before setting (undeclared) function declarations
377
  // on the sandbox in strict mode, i.e. args.ShouldThrowOnError() = true.
378
  // True for 'function f() {}', 'this.f = function() {}',
379
  // 'var f = function()'.
380
  // In effect only for 'function f() {}' because
381
  // var f = function(), is_declared = true
382
  // this.f = function() {}, is_contextual_store = false.
383
2766
  bool is_function = value->IsFunction();
384
385

2766
  bool is_declared = is_declared_on_global_proxy || is_declared_on_sandbox;
386


4463
  if (!is_declared && args.ShouldThrowOnError() && is_contextual_store &&
387
31
      !is_function)
388
1
    return;
389
390

7227
  if (!is_declared_on_global_proxy && is_declared_on_sandbox  &&
391

2786
      args.ShouldThrowOnError() && is_contextual_store && !is_function) {
392
    // The property exists on the sandbox but not on the global
393
    // proxy. Setting it would throw because we are in strict mode.
394
    // Don't attempt to set it by signaling that the call was
395
    // intercepted. Only change the value on the sandbox.
396
8
    args.GetReturnValue().Set(false);
397
  }
398
399
8295
  ctx->sandbox()->Set(ctx->context(), property, value).FromJust();
400
}
401
402
// static
403
29
void ContextifyContext::PropertyDescriptorCallback(
404
    Local<Name> property,
405
    const PropertyCallbackInfo<Value>& args) {
406
29
  ContextifyContext* ctx = ContextifyContext::Get(args);
407
408
  // Still initializing
409
58
  if (ctx->context_.IsEmpty())
410
29
    return;
411
412
29
  Local<Context> context = ctx->context();
413
414
29
  Local<Object> sandbox = ctx->sandbox();
415
416
87
  if (sandbox->HasOwnProperty(context, property).FromMaybe(false)) {
417
    args.GetReturnValue().Set(
418
14
        sandbox->GetOwnPropertyDescriptor(context, property)
419
42
            .ToLocalChecked());
420
  }
421
}
422
423
// static
424
15
void ContextifyContext::PropertyDefinerCallback(
425
    Local<Name> property,
426
    const PropertyDescriptor& desc,
427
    const PropertyCallbackInfo<Value>& args) {
428
15
  ContextifyContext* ctx = ContextifyContext::Get(args);
429
430
  // Still initializing
431
30
  if (ctx->context_.IsEmpty())
432
    return;
433
434
15
  Local<Context> context = ctx->context();
435
15
  Isolate* isolate = context->GetIsolate();
436
437
15
  auto attributes = PropertyAttribute::None;
438
  bool is_declared =
439
      ctx->global_proxy()->GetRealNamedPropertyAttributes(ctx->context(),
440
45
                                                          property)
441
30
          .To(&attributes);
442
  bool read_only =
443
15
      static_cast<int>(attributes) &
444
15
          static_cast<int>(PropertyAttribute::ReadOnly);
445
446
  // If the property is set on the global as read_only, don't change it on
447
  // the global or sandbox.
448

15
  if (is_declared && read_only)
449
    return;
450
451
15
  Local<Object> sandbox = ctx->sandbox();
452
453
  auto define_prop_on_sandbox =
454
15
      [&] (PropertyDescriptor* desc_for_sandbox) {
455
15
        if (desc.has_enumerable()) {
456
2
          desc_for_sandbox->set_enumerable(desc.enumerable());
457
        }
458
15
        if (desc.has_configurable()) {
459
1
          desc_for_sandbox->set_configurable(desc.configurable());
460
        }
461
        // Set the property on the sandbox.
462
30
        sandbox->DefineProperty(context, property, *desc_for_sandbox)
463
30
            .FromJust();
464
30
      };
465
466

15
  if (desc.has_get() || desc.has_set()) {
467
    PropertyDescriptor desc_for_sandbox(
468
10
        desc.has_get() ? desc.get() : Undefined(isolate).As<Value>(),
469

23
        desc.has_set() ? desc.set() : Undefined(isolate).As<Value>());
470
471
5
    define_prop_on_sandbox(&desc_for_sandbox);
472
  } else {
473
    Local<Value> value =
474

12
        desc.has_value() ? desc.value() : Undefined(isolate).As<Value>();
475
476
10
    if (desc.has_writable()) {
477
      PropertyDescriptor desc_for_sandbox(value, desc.writable());
478
      define_prop_on_sandbox(&desc_for_sandbox);
479
    } else {
480
10
      PropertyDescriptor desc_for_sandbox(value);
481
10
      define_prop_on_sandbox(&desc_for_sandbox);
482
    }
483
  }
484
}
485
486
// static
487
2
void ContextifyContext::PropertyDeleterCallback(
488
    Local<Name> property,
489
    const PropertyCallbackInfo<Boolean>& args) {
490
2
  ContextifyContext* ctx = ContextifyContext::Get(args);
491
492
  // Still initializing
493
4
  if (ctx->context_.IsEmpty())
494
1
    return;
495
496
4
  Maybe<bool> success = ctx->sandbox()->Delete(ctx->context(), property);
497
498
4
  if (success.FromMaybe(false))
499
1
    return;
500
501
  // Delete failed on the sandbox, intercept and do not delete on
502
  // the global object.
503
2
  args.GetReturnValue().Set(false);
504
}
505
506
// static
507
10
void ContextifyContext::PropertyEnumeratorCallback(
508
    const PropertyCallbackInfo<Array>& args) {
509
10
  ContextifyContext* ctx = ContextifyContext::Get(args);
510
511
  // Still initializing
512
20
  if (ctx->context_.IsEmpty())
513
    return;
514
515
  Local<Array> properties;
516
517
30
  if (!ctx->sandbox()->GetPropertyNames(ctx->context()).ToLocal(&properties))
518
    return;
519
520
20
  args.GetReturnValue().Set(properties);
521
}
522
523
// static
524
void ContextifyContext::IndexedPropertyGetterCallback(
525
    uint32_t index,
526
    const PropertyCallbackInfo<Value>& args) {
527
  ContextifyContext* ctx = ContextifyContext::Get(args);
528
529
  // Still initializing
530
  if (ctx->context_.IsEmpty())
531
    return;
532
533
  ContextifyContext::PropertyGetterCallback(
534
      Uint32ToName(ctx->context(), index), args);
535
}
536
537
538
1
void ContextifyContext::IndexedPropertySetterCallback(
539
    uint32_t index,
540
    Local<Value> value,
541
    const PropertyCallbackInfo<Value>& args) {
542
1
  ContextifyContext* ctx = ContextifyContext::Get(args);
543
544
  // Still initializing
545
2
  if (ctx->context_.IsEmpty())
546
1
    return;
547
548
  ContextifyContext::PropertySetterCallback(
549
1
      Uint32ToName(ctx->context(), index), value, args);
550
}
551
552
// static
553
1
void ContextifyContext::IndexedPropertyDescriptorCallback(
554
    uint32_t index,
555
    const PropertyCallbackInfo<Value>& args) {
556
1
  ContextifyContext* ctx = ContextifyContext::Get(args);
557
558
  // Still initializing
559
2
  if (ctx->context_.IsEmpty())
560
1
    return;
561
562
  ContextifyContext::PropertyDescriptorCallback(
563
1
      Uint32ToName(ctx->context(), index), args);
564
}
565
566
567
1
void ContextifyContext::IndexedPropertyDefinerCallback(
568
    uint32_t index,
569
    const PropertyDescriptor& desc,
570
    const PropertyCallbackInfo<Value>& args) {
571
1
  ContextifyContext* ctx = ContextifyContext::Get(args);
572
573
  // Still initializing
574
2
  if (ctx->context_.IsEmpty())
575
1
    return;
576
577
  ContextifyContext::PropertyDefinerCallback(
578
1
      Uint32ToName(ctx->context(), index), desc, args);
579
}
580
581
// static
582
void ContextifyContext::IndexedPropertyDeleterCallback(
583
    uint32_t index,
584
    const PropertyCallbackInfo<Boolean>& args) {
585
  ContextifyContext* ctx = ContextifyContext::Get(args);
586
587
  // Still initializing
588
  if (ctx->context_.IsEmpty())
589
    return;
590
591
  Maybe<bool> success = ctx->sandbox()->Delete(ctx->context(), index);
592
593
  if (success.FromMaybe(false))
594
    return;
595
596
  // Delete failed on the sandbox, intercept and do not delete on
597
  // the global object.
598
  args.GetReturnValue().Set(false);
599
}
600
601
4399
void ContextifyScript::Init(Environment* env, Local<Object> target) {
602
4399
  HandleScope scope(env->isolate());
603
  Local<String> class_name =
604
4399
      FIXED_ONE_BYTE_STRING(env->isolate(), "ContextifyScript");
605
606
4399
  Local<FunctionTemplate> script_tmpl = env->NewFunctionTemplate(New);
607
8798
  script_tmpl->InstanceTemplate()->SetInternalFieldCount(1);
608
4399
  script_tmpl->SetClassName(class_name);
609
4399
  env->SetProtoMethod(script_tmpl, "createCachedData", CreateCachedData);
610
4399
  env->SetProtoMethod(script_tmpl, "runInContext", RunInContext);
611
4399
  env->SetProtoMethod(script_tmpl, "runInThisContext", RunInThisContext);
612
613
  target->Set(env->context(), class_name,
614
21995
      script_tmpl->GetFunction(env->context()).ToLocalChecked()).FromJust();
615
4399
  env->set_script_context_constructor_template(script_tmpl);
616
4399
}
617
618
446216
void ContextifyScript::New(const FunctionCallbackInfo<Value>& args) {
619
446216
  Environment* env = Environment::GetCurrent(args);
620
446216
  Isolate* isolate = env->isolate();
621
446216
  Local<Context> context = env->context();
622
623
446216
  CHECK(args.IsConstructCall());
624
625
446216
  const int argc = args.Length();
626
446216
  CHECK_GE(argc, 2);
627
628
1338648
  CHECK(args[0]->IsString());
629
892432
  Local<String> code = args[0].As<String>();
630
631
1338648
  CHECK(args[1]->IsString());
632
892432
  Local<String> filename = args[1].As<String>();
633
634
  Local<Integer> line_offset;
635
  Local<Integer> column_offset;
636
  Local<ArrayBufferView> cached_data_buf;
637
446216
  bool produce_cached_data = false;
638
446216
  Local<Context> parsing_context = context;
639
640
446216
  if (argc > 2) {
641
    // new ContextifyScript(code, filename, lineOffset, columnOffset,
642
    //                      cachedData, produceCachedData, parsingContext)
643
446216
    CHECK_EQ(argc, 7);
644
892432
    CHECK(args[2]->IsNumber());
645
892432
    line_offset = args[2].As<Integer>();
646
892432
    CHECK(args[3]->IsNumber());
647
892432
    column_offset = args[3].As<Integer>();
648
1338648
    if (!args[4]->IsUndefined()) {
649
46
      CHECK(args[4]->IsArrayBufferView());
650
46
      cached_data_buf = args[4].As<ArrayBufferView>();
651
    }
652
892432
    CHECK(args[5]->IsBoolean());
653
892432
    produce_cached_data = args[5]->IsTrue();
654
1338648
    if (!args[6]->IsUndefined()) {
655
6788
      CHECK(args[6]->IsObject());
656
      ContextifyContext* sandbox =
657
          ContextifyContext::ContextFromContextifiedSandbox(
658
6788
              env, args[6].As<Object>());
659
3394
      CHECK_NOT_NULL(sandbox);
660
3394
      parsing_context = sandbox->context();
661
    }
662
  } else {
663
    line_offset = Integer::New(isolate, 0);
664
    column_offset = Integer::New(isolate, 0);
665
  }
666
667
  ContextifyScript* contextify_script =
668
446216
      new ContextifyScript(env, args.This());
669
670
892432
  if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
671
446216
          TRACING_CATEGORY_NODE2(vm, script)) != 0) {
672
19
    Utf8Value fn(isolate, filename);
673

19
    TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(
674
        TRACING_CATEGORY_NODE2(vm, script),
675
        "ContextifyScript::New",
676
        contextify_script,
677
19
        "filename", TRACE_STR_COPY(*fn));
678
  }
679
680
446216
  ScriptCompiler::CachedData* cached_data = nullptr;
681
446216
  if (!cached_data_buf.IsEmpty()) {
682
46
    ArrayBuffer::Contents contents = cached_data_buf->Buffer()->GetContents();
683
23
    uint8_t* data = static_cast<uint8_t*>(contents.Data());
684
    cached_data = new ScriptCompiler::CachedData(
685
46
        data + cached_data_buf->ByteOffset(), cached_data_buf->ByteLength());
686
  }
687
688
  Local<PrimitiveArray> host_defined_options =
689
446216
      PrimitiveArray::New(isolate, loader::HostDefinedOptions::kLength);
690
  host_defined_options->Set(isolate, loader::HostDefinedOptions::kType,
691
892432
                            Number::New(isolate, loader::ScriptType::kScript));
692
  host_defined_options->Set(isolate, loader::HostDefinedOptions::kID,
693
892432
                            Number::New(isolate, contextify_script->id()));
694
695
  ScriptOrigin origin(filename,
696
                      line_offset,                          // line offset
697
                      column_offset,                        // column offset
698
                      True(isolate),                        // is cross origin
699
                      Local<Integer>(),                     // script id
700
                      Local<Value>(),                       // source map URL
701
                      False(isolate),                       // is opaque (?)
702
                      False(isolate),                       // is WASM
703
                      False(isolate),                       // is ES Module
704
446216
                      host_defined_options);
705
  ScriptCompiler::Source source(code, origin, cached_data);
706
  ScriptCompiler::CompileOptions compile_options =
707
446216
      ScriptCompiler::kNoCompileOptions;
708
709
446216
  if (source.GetCachedData() != nullptr)
710
23
    compile_options = ScriptCompiler::kConsumeCodeCache;
711
712
892110
  TryCatchScope try_catch(env);
713
892110
  Environment::ShouldNotAbortOnUncaughtScope no_abort_scope(env);
714
445894
  Context::Scope scope(parsing_context);
715
716
  MaybeLocal<UnboundScript> v8_script = ScriptCompiler::CompileUnboundScript(
717
      isolate,
718
      &source,
719
446216
      compile_options);
720
721
446216
  if (v8_script.IsEmpty()) {
722
322
    DecorateErrorStack(env, try_catch);
723
322
    no_abort_scope.Close();
724
322
    try_catch.ReThrow();
725

322
    TRACE_EVENT_NESTABLE_ASYNC_END0(
726
        TRACING_CATEGORY_NODE2(vm, script),
727
        "ContextifyScript::New",
728
        contextify_script);
729
446538
    return;
730
  }
731
891788
  contextify_script->script_.Reset(isolate, v8_script.ToLocalChecked());
732
733
445894
  if (compile_options == ScriptCompiler::kConsumeCodeCache) {
734
    args.This()->Set(
735
        env->context(),
736
        env->cached_data_rejected_string(),
737
138
        Boolean::New(isolate, source.GetCachedData()->rejected)).FromJust();
738
445871
  } else if (produce_cached_data) {
739
    const ScriptCompiler::CachedData* cached_data =
740
6
      ScriptCompiler::CreateCodeCache(v8_script.ToLocalChecked());
741
6
    bool cached_data_produced = cached_data != nullptr;
742
6
    if (cached_data_produced) {
743
      MaybeLocal<Object> buf = Buffer::Copy(
744
          env,
745
          reinterpret_cast<const char*>(cached_data->data),
746
6
          cached_data->length);
747
      args.This()->Set(env->context(),
748
                       env->cached_data_string(),
749
30
                       buf.ToLocalChecked()).FromJust();
750
    }
751
    args.This()->Set(
752
        env->context(),
753
        env->cached_data_produced_string(),
754
36
        Boolean::New(isolate, cached_data_produced)).FromJust();
755
  }
756

445894
  TRACE_EVENT_NESTABLE_ASYNC_END0(
757
      TRACING_CATEGORY_NODE2(vm, script),
758
      "ContextifyScript::New",
759
445894
      contextify_script);
760
}
761
762
446861
bool ContextifyScript::InstanceOf(Environment* env,
763
                                  const Local<Value>& value) {
764

1787444
  return !value.IsEmpty() &&
765
1340583
         env->script_context_constructor_template()->HasInstance(value);
766
}
767
768
11
void ContextifyScript::CreateCachedData(
769
    const FunctionCallbackInfo<Value>& args) {
770
11
  Environment* env = Environment::GetCurrent(args);
771
  ContextifyScript* wrapped_script;
772
22
  ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder());
773
  Local<UnboundScript> unbound_script =
774
11
      PersistentToLocal::Default(env->isolate(), wrapped_script->script_);
775
  std::unique_ptr<ScriptCompiler::CachedData> cached_data(
776
11
      ScriptCompiler::CreateCodeCache(unbound_script));
777
11
  if (!cached_data) {
778
    args.GetReturnValue().Set(Buffer::New(env, 0).ToLocalChecked());
779
  } else {
780
    MaybeLocal<Object> buf = Buffer::Copy(
781
        env,
782
11
        reinterpret_cast<const char*>(cached_data->data),
783
22
        cached_data->length);
784
22
    args.GetReturnValue().Set(buf.ToLocalChecked());
785
11
  }
786
}
787
788
442212
void ContextifyScript::RunInThisContext(
789
    const FunctionCallbackInfo<Value>& args) {
790
442212
  Environment* env = Environment::GetCurrent(args);
791
792
  ContextifyScript* wrapped_script;
793
884411
  ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder());
794
795

442212
  TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
796
      TRACING_CATEGORY_NODE2(vm, script), "RunInThisContext", wrapped_script);
797
798
  // TODO(addaleax): Use an options object or otherwise merge this with
799
  // RunInContext().
800
442212
  CHECK_EQ(args.Length(), 4);
801
802
884424
  CHECK(args[0]->IsNumber());
803
1768848
  int64_t timeout = args[0]->IntegerValue(env->context()).FromJust();
804
805
884424
  CHECK(args[1]->IsBoolean());
806
884424
  bool display_errors = args[1]->IsTrue();
807
808
884424
  CHECK(args[2]->IsBoolean());
809
884424
  bool break_on_sigint = args[2]->IsTrue();
810
811
884424
  CHECK(args[3]->IsBoolean());
812
884424
  bool break_on_first_line = args[3]->IsTrue();
813
814
  // Do the eval within this context
815
  EvalMachine(env,
816
              timeout,
817
              display_errors,
818
              break_on_sigint,
819
              break_on_first_line,
820
442212
              args);
821
822

442199
  TRACE_EVENT_NESTABLE_ASYNC_END0(
823
      TRACING_CATEGORY_NODE2(vm, script), "RunInThisContext", wrapped_script);
824
}
825
826
4649
void ContextifyScript::RunInContext(const FunctionCallbackInfo<Value>& args) {
827
4649
  Environment* env = Environment::GetCurrent(args);
828
829
  ContextifyScript* wrapped_script;
830
4649
  ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder());
831
832
4649
  CHECK_EQ(args.Length(), 5);
833
834
9298
  CHECK(args[0]->IsObject());
835
9298
  Local<Object> sandbox = args[0].As<Object>();
836
  // Get the context from the sandbox
837
  ContextifyContext* contextify_context =
838
4649
      ContextifyContext::ContextFromContextifiedSandbox(env, sandbox);
839
4649
  CHECK_NOT_NULL(contextify_context);
840
841
9298
  if (contextify_context->context().IsEmpty())
842
    return;
843
844

4649
  TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
845
      TRACING_CATEGORY_NODE2(vm, script), "RunInContext", wrapped_script);
846
847
9298
  CHECK(args[1]->IsNumber());
848
18596
  int64_t timeout = args[1]->IntegerValue(env->context()).FromJust();
849
850
9298
  CHECK(args[2]->IsBoolean());
851
9298
  bool display_errors = args[2]->IsTrue();
852
853
9298
  CHECK(args[3]->IsBoolean());
854
9298
  bool break_on_sigint = args[3]->IsTrue();
855
856
9298
  CHECK(args[4]->IsBoolean());
857
9298
  bool break_on_first_line = args[4]->IsTrue();
858
859
  // Do the eval within the context
860
4649
  Context::Scope context_scope(contextify_context->context());
861
  EvalMachine(contextify_context->env(),
862
              timeout,
863
              display_errors,
864
              break_on_sigint,
865
              break_on_first_line,
866
4649
              args);
867
868

4649
  TRACE_EVENT_NESTABLE_ASYNC_END0(
869
      TRACING_CATEGORY_NODE2(vm, script), "RunInContext", wrapped_script);
870
}
871
872
446861
bool ContextifyScript::EvalMachine(Environment* env,
873
                                   const int64_t timeout,
874
                                   const bool display_errors,
875
                                   const bool break_on_sigint,
876
                                   const bool break_on_first_line,
877
                                   const FunctionCallbackInfo<Value>& args) {
878
446861
  if (!env->can_call_into_js())
879
    return false;
880
446861
  if (!ContextifyScript::InstanceOf(env, args.Holder())) {
881
    env->ThrowTypeError(
882
        "Script methods can only be called on script instances.");
883
    return false;
884
  }
885
446861
  TryCatchScope try_catch(env);
886
  ContextifyScript* wrapped_script;
887
446861
  ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder(), false);
888
  Local<UnboundScript> unbound_script =
889
446861
      PersistentToLocal::Default(env->isolate(), wrapped_script->script_);
890
446861
  Local<Script> script = unbound_script->BindToCurrentContext();
891
892
#if HAVE_INSPECTOR
893
446861
  if (break_on_first_line) {
894
8
    env->inspector_agent()->PauseOnNextJavascriptStatement("Break on start");
895
  }
896
#endif
897
898
  MaybeLocal<Value> result;
899
446861
  bool timed_out = false;
900
446861
  bool received_signal = false;
901

446861
  if (break_on_sigint && timeout != -1) {
902
    Watchdog wd(env->isolate(), timeout, &timed_out);
903
    SigintWatchdog swd(env->isolate(), &received_signal);
904
    result = script->Run(env->context());
905
446861
  } else if (break_on_sigint) {
906
86
    SigintWatchdog swd(env->isolate(), &received_signal);
907
172
    result = script->Run(env->context());
908
446775
  } else if (timeout != -1) {
909
1013
    Watchdog wd(env->isolate(), timeout, &timed_out);
910
2026
    result = script->Run(env->context());
911
  } else {
912
891524
    result = script->Run(env->context());
913
  }
914
915
  // Convert the termination exception into a regular exception.
916

446848
  if (timed_out || received_signal) {
917
518
    env->isolate()->CancelTerminateExecution();
918
    // It is possible that execution was terminated by another timeout in
919
    // which this timeout is nested, so check whether one of the watchdogs
920
    // from this invocation is responsible for termination.
921
518
    if (timed_out) {
922
507
      node::THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(env, timeout);
923
11
    } else if (received_signal) {
924
11
      node::THROW_ERR_SCRIPT_EXECUTION_INTERRUPTED(env);
925
    }
926
  }
927
928
446848
  if (try_catch.HasCaught()) {
929

3714
    if (!timed_out && !received_signal && display_errors) {
930
      // We should decorate non-termination exceptions
931
3164
      DecorateErrorStack(env, try_catch);
932
    }
933
934
    // If there was an exception thrown during script execution, re-throw it.
935
    // If one of the above checks threw, re-throw the exception instead of
936
    // letting try_catch catch it.
937
    // If execution has been terminated, but not by one of the watchdogs from
938
    // this invocation, this will re-throw a `null` value.
939
3714
    try_catch.ReThrow();
940
941
3714
    return false;
942
  }
943
944
886268
  args.GetReturnValue().Set(result.ToLocalChecked());
945
443134
  return true;
946
}
947
948
949
446216
ContextifyScript::ContextifyScript(Environment* env, Local<Object> object)
950
    : BaseObject(env, object),
951
892432
      id_(env->get_next_script_id()) {
952
446216
  MakeWeak();
953
446216
  env->id_to_script_map.emplace(id_, this);
954
446216
}
955
956
957
1779432
ContextifyScript::~ContextifyScript() {
958
444858
  env()->id_to_script_map.erase(id_);
959
889716
}
960
961
962
13
void ContextifyContext::CompileFunction(
963
    const FunctionCallbackInfo<Value>& args) {
964
13
  Environment* env = Environment::GetCurrent(args);
965
13
  Isolate* isolate = env->isolate();
966
13
  Local<Context> context = env->context();
967
968
  // Argument 1: source code
969
39
  CHECK(args[0]->IsString());
970
26
  Local<String> code = args[0].As<String>();
971
972
  // Argument 2: filename
973
39
  CHECK(args[1]->IsString());
974
26
  Local<String> filename = args[1].As<String>();
975
976
  // Argument 3: line offset
977
26
  CHECK(args[2]->IsNumber());
978
26
  Local<Integer> line_offset = args[2].As<Integer>();
979
980
  // Argument 4: column offset
981
26
  CHECK(args[3]->IsNumber());
982
26
  Local<Integer> column_offset = args[3].As<Integer>();
983
984
  // Argument 5: cached data (optional)
985
  Local<ArrayBufferView> cached_data_buf;
986
39
  if (!args[4]->IsUndefined()) {
987
    CHECK(args[4]->IsArrayBufferView());
988
    cached_data_buf = args[4].As<ArrayBufferView>();
989
  }
990
991
  // Argument 6: produce cache data
992
26
  CHECK(args[5]->IsBoolean());
993
26
  bool produce_cached_data = args[5]->IsTrue();
994
995
  // Argument 7: parsing context (optional)
996
  Local<Context> parsing_context;
997
39
  if (!args[6]->IsUndefined()) {
998
4
    CHECK(args[6]->IsObject());
999
    ContextifyContext* sandbox =
1000
        ContextifyContext::ContextFromContextifiedSandbox(
1001
4
            env, args[6].As<Object>());
1002
2
    CHECK_NOT_NULL(sandbox);
1003
2
    parsing_context = sandbox->context();
1004
  } else {
1005
11
    parsing_context = context;
1006
  }
1007
1008
  // Argument 8: context extensions (optional)
1009
  Local<Array> context_extensions_buf;
1010
39
  if (!args[7]->IsUndefined()) {
1011
26
    CHECK(args[7]->IsArray());
1012
26
    context_extensions_buf = args[7].As<Array>();
1013
  }
1014
1015
  // Argument 9: params for the function (optional)
1016
  Local<Array> params_buf;
1017
39
  if (!args[8]->IsUndefined()) {
1018
14
    CHECK(args[8]->IsArray());
1019
14
    params_buf = args[8].As<Array>();
1020
  }
1021
1022
  // Read cache from cached data buffer
1023
13
  ScriptCompiler::CachedData* cached_data = nullptr;
1024
13
  if (!cached_data_buf.IsEmpty()) {
1025
    ArrayBuffer::Contents contents = cached_data_buf->Buffer()->GetContents();
1026
    uint8_t* data = static_cast<uint8_t*>(contents.Data());
1027
    cached_data = new ScriptCompiler::CachedData(
1028
      data + cached_data_buf->ByteOffset(), cached_data_buf->ByteLength());
1029
  }
1030
1031
13
  ScriptOrigin origin(filename, line_offset, column_offset, True(isolate));
1032
  ScriptCompiler::Source source(code, origin, cached_data);
1033
  ScriptCompiler::CompileOptions options;
1034
13
  if (source.GetCachedData() == nullptr) {
1035
13
    options = ScriptCompiler::kNoCompileOptions;
1036
  } else {
1037
    options = ScriptCompiler::kConsumeCodeCache;
1038
  }
1039
1040
25
  TryCatchScope try_catch(env);
1041
12
  Context::Scope scope(parsing_context);
1042
1043
  // Read context extensions from buffer
1044
25
  std::vector<Local<Object>> context_extensions;
1045
13
  if (!context_extensions_buf.IsEmpty()) {
1046
28
    for (uint32_t n = 0; n < context_extensions_buf->Length(); n++) {
1047
      Local<Value> val;
1048
2
      if (!context_extensions_buf->Get(context, n).ToLocal(&val)) return;
1049
1
      CHECK(val->IsObject());
1050
1
      context_extensions.push_back(val.As<Object>());
1051
    }
1052
  }
1053
1054
  // Read params from params buffer
1055
25
  std::vector<Local<String>> params;
1056
13
  if (!params_buf.IsEmpty()) {
1057
24
    for (uint32_t n = 0; n < params_buf->Length(); n++) {
1058
      Local<Value> val;
1059
10
      if (!params_buf->Get(context, n).ToLocal(&val)) return;
1060
10
      CHECK(val->IsString());
1061
5
      params.push_back(val.As<String>());
1062
    }
1063
  }
1064
1065
  MaybeLocal<Function> maybe_fun = ScriptCompiler::CompileFunctionInContext(
1066
      parsing_context, &source, params.size(), params.data(),
1067
13
      context_extensions.size(), context_extensions.data(), options);
1068
1069
  Local<Function> fun;
1070

25
  if (maybe_fun.IsEmpty() || !maybe_fun.ToLocal(&fun)) {
1071
1
    DecorateErrorStack(env, try_catch);
1072
1
    try_catch.ReThrow();
1073
1
    return;
1074
  }
1075
1076
12
  if (produce_cached_data) {
1077
    const std::unique_ptr<ScriptCompiler::CachedData> cached_data(
1078
        ScriptCompiler::CreateCodeCacheForFunction(fun));
1079
    bool cached_data_produced = cached_data != nullptr;
1080
    if (cached_data_produced) {
1081
      MaybeLocal<Object> buf = Buffer::Copy(
1082
          env,
1083
          reinterpret_cast<const char*>(cached_data->data),
1084
          cached_data->length);
1085
      if (fun->Set(
1086
          parsing_context,
1087
          env->cached_data_string(),
1088
          buf.ToLocalChecked()).IsNothing()) return;
1089
    }
1090
    if (fun->Set(
1091
        parsing_context,
1092
        env->cached_data_produced_string(),
1093
        Boolean::New(isolate, cached_data_produced)).IsNothing()) return;
1094
  }
1095
1096
36
  args.GetReturnValue().Set(fun);
1097
}
1098
1099
1100
4399
void Initialize(Local<Object> target,
1101
                Local<Value> unused,
1102
                Local<Context> context,
1103
                void* priv) {
1104
4399
  Environment* env = Environment::GetCurrent(context);
1105
4399
  ContextifyContext::Init(env, target);
1106
4399
  ContextifyScript::Init(env, target);
1107
4399
}
1108
1109
}  // namespace contextify
1110
}  // namespace node
1111
1112
4314
NODE_MODULE_CONTEXT_AWARE_INTERNAL(contextify, node::contextify::Initialize)