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: 443 493 89.9 %
Date: 2019-03-02 22:23:06 Branches: 261 380 68.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_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
4567
ContextifyContext::ContextifyContext(
105
    Environment* env,
106
4567
    Local<Object> sandbox_obj, const ContextOptions& options) : env_(env) {
107
4567
  MaybeLocal<Context> v8_context = CreateV8Context(env, sandbox_obj, options);
108
109
  // Allocation failure, maximum call stack size reached, termination, etc.
110
9134
  if (v8_context.IsEmpty()) return;
111
112
9132
  context_.Reset(env->isolate(), v8_context.ToLocalChecked());
113
4566
  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
4567
MaybeLocal<Object> ContextifyContext::CreateDataWrapper(Environment* env) {
123
  Local<Object> wrapper;
124
9134
  if (!env->script_data_constructor_function()
125
13701
           ->NewInstance(env->context())
126
13701
           .ToLocal(&wrapper)) {
127
    return MaybeLocal<Object>();
128
  }
129
130
4567
  wrapper->SetAlignedPointerInInternalField(0, this);
131
4567
  return wrapper;
132
}
133
134
4567
MaybeLocal<Context> ContextifyContext::CreateV8Context(
135
    Environment* env,
136
    Local<Object> sandbox_obj,
137
    const ContextOptions& options) {
138
4567
  EscapableHandleScope scope(env->isolate());
139
  Local<FunctionTemplate> function_template =
140
4567
      FunctionTemplate::New(env->isolate());
141
142
9134
  function_template->SetClassName(sandbox_obj->GetConstructorName());
143
144
  Local<ObjectTemplate> object_template =
145
4567
      function_template->InstanceTemplate();
146
147
  Local<Object> data_wrapper;
148
9134
  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
4567
                                           data_wrapper);
158
159
  IndexedPropertyHandlerConfiguration indexed_config(
160
      IndexedPropertyGetterCallback,
161
      IndexedPropertySetterCallback,
162
      IndexedPropertyDescriptorCallback,
163
      IndexedPropertyDeleterCallback,
164
      PropertyEnumeratorCallback,
165
      IndexedPropertyDefinerCallback,
166
4567
      data_wrapper);
167
168
4567
  object_template->SetHandler(config);
169
4567
  object_template->SetHandler(indexed_config);
170
171
4567
  Local<Context> ctx = NewContext(env->isolate(), object_template);
172
173
4567
  if (ctx.IsEmpty()) {
174
1
    return MaybeLocal<Context>();
175
  }
176
177
13698
  ctx->SetSecurityToken(env->context()->GetSecurityToken());
178
179
  // We need to tie the lifetime of the sandbox object with the lifetime of
180
  // newly created context. We do this by making them hold references to each
181
  // other. The context can directly hold a reference to the sandbox as an
182
  // embedder data field. However, we cannot hold a reference to a v8::Context
183
  // directly in an Object, we instead hold onto the new context's global
184
  // object instead (which then has a reference to the context).
185
4566
  ctx->SetEmbedderData(ContextEmbedderIndex::kSandboxObject, sandbox_obj);
186
  sandbox_obj->SetPrivate(env->context(),
187
                          env->contextify_global_private_symbol(),
188
13698
                          ctx->Global());
189
190
9132
  Utf8Value name_val(env->isolate(), options.name);
191
13698
  ctx->AllowCodeGenerationFromStrings(options.allow_code_gen_strings->IsTrue());
192
  ctx->SetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration,
193
4566
                       options.allow_code_gen_wasm);
194
195
9132
  ContextInfo info(*name_val);
196
197
9132
  if (!options.origin.IsEmpty()) {
198
2
    Utf8Value origin_val(env->isolate(), options.origin);
199
2
    info.origin = *origin_val;
200
  }
201
202
4566
  env->AssignToContext(ctx, info);
203
204
4566
  return scope.Escape(ctx);
205
}
206
207
208
4403
void ContextifyContext::Init(Environment* env, Local<Object> target) {
209
  Local<FunctionTemplate> function_template =
210
4403
      FunctionTemplate::New(env->isolate());
211
8806
  function_template->InstanceTemplate()->SetInternalFieldCount(1);
212
  env->set_script_data_constructor_function(
213
13209
      function_template->GetFunction(env->context()).ToLocalChecked());
214
215
4403
  env->SetMethod(target, "makeContext", MakeContext);
216
4403
  env->SetMethod(target, "isContext", IsContext);
217
4403
  env->SetMethod(target, "compileFunction", CompileFunction);
218
4403
}
219
220
221
// makeContext(sandbox, name, origin, strings, wasm);
222
4567
void ContextifyContext::MakeContext(const FunctionCallbackInfo<Value>& args) {
223
4567
  Environment* env = Environment::GetCurrent(args);
224
225
4567
  CHECK_EQ(args.Length(), 5);
226
9134
  CHECK(args[0]->IsObject());
227
9134
  Local<Object> sandbox = args[0].As<Object>();
228
229
  // Don't allow contextifying a sandbox multiple times.
230
13701
  CHECK(
231
      !sandbox->HasPrivate(
232
          env->context(),
233
          env->contextify_context_private_symbol()).FromJust());
234
235
4567
  ContextOptions options;
236
237
13701
  CHECK(args[1]->IsString());
238
9134
  options.name = args[1].As<String>();
239
240


31963
  CHECK(args[2]->IsString() || args[2]->IsUndefined());
241
13701
  if (args[2]->IsString()) {
242
4
    options.origin = args[2].As<String>();
243
  }
244
245
9134
  CHECK(args[3]->IsBoolean());
246
9134
  options.allow_code_gen_strings = args[3].As<Boolean>();
247
248
9134
  CHECK(args[4]->IsBoolean());
249
9134
  options.allow_code_gen_wasm = args[4].As<Boolean>();
250
251
4567
  TryCatchScope try_catch(env);
252
4567
  ContextifyContext* context = new ContextifyContext(env, sandbox, options);
253
254
4567
  if (try_catch.HasCaught()) {
255
1
    if (!try_catch.HasTerminated())
256
      try_catch.ReThrow();
257
1
    return;
258
  }
259
260
9132
  if (context->context().IsEmpty())
261
    return;
262
263
  sandbox->SetPrivate(
264
      env->context(),
265
      env->contextify_context_private_symbol(),
266
13698
      External::New(env->isolate(), context));
267
}
268
269
270
12778
void ContextifyContext::IsContext(const FunctionCallbackInfo<Value>& args) {
271
12778
  Environment* env = Environment::GetCurrent(args);
272
273
25556
  CHECK(args[0]->IsObject());
274
25556
  Local<Object> sandbox = args[0].As<Object>();
275
276
  Maybe<bool> result =
277
      sandbox->HasPrivate(env->context(),
278
25556
                          env->contextify_context_private_symbol());
279
38334
  args.GetReturnValue().Set(result.FromJust());
280
12778
}
281
282
283
4133
void ContextifyContext::WeakCallback(
284
    const WeakCallbackInfo<ContextifyContext>& data) {
285
4133
  ContextifyContext* context = data.GetParameter();
286
4133
  delete context;
287
4133
}
288
289
21482
void ContextifyContext::WeakCallbackCompileFn(
290
    const WeakCallbackInfo<CompileFnEntry>& data) {
291
21482
  CompileFnEntry* entry = data.GetParameter();
292
21482
  if (entry->env->compile_fn_entries.erase(entry) != 0) {
293
21482
    entry->env->id_to_function_map.erase(entry->id);
294
21482
    delete entry;
295
  }
296
21482
}
297
298
// static
299
8138
ContextifyContext* ContextifyContext::ContextFromContextifiedSandbox(
300
    Environment* env,
301
    const Local<Object>& sandbox) {
302
  MaybeLocal<Value> maybe_value =
303
      sandbox->GetPrivate(env->context(),
304
16276
                          env->contextify_context_private_symbol());
305
  Local<Value> context_external_v;
306

16276
  if (maybe_value.ToLocal(&context_external_v) &&
307
8138
      context_external_v->IsExternal()) {
308
8138
    Local<External> context_external = context_external_v.As<External>();
309
8138
    return static_cast<ContextifyContext*>(context_external->Value());
310
  }
311
  return nullptr;
312
}
313
314
// static
315
template <typename T>
316
12686394
ContextifyContext* ContextifyContext::Get(const PropertyCallbackInfo<T>& args) {
317
12686394
  Local<Value> data = args.Data();
318
  return static_cast<ContextifyContext*>(
319
38059182
      data.As<Object>()->GetAlignedPointerFromInternalField(0));
320
}
321
322
// static
323
12683557
void ContextifyContext::PropertyGetterCallback(
324
    Local<Name> property,
325
    const PropertyCallbackInfo<Value>& args) {
326
12683557
  ContextifyContext* ctx = ContextifyContext::Get(args);
327
328
  // Still initializing
329
25367114
  if (ctx->context_.IsEmpty())
330
12710953
    return;
331
332
12656161
  Local<Context> context = ctx->context();
333
12656161
  Local<Object> sandbox = ctx->sandbox();
334
  MaybeLocal<Value> maybe_rv =
335
12656161
      sandbox->GetRealNamedProperty(context, property);
336
12656161
  if (maybe_rv.IsEmpty()) {
337
    maybe_rv =
338
11884416
        ctx->global_proxy()->GetRealNamedProperty(context, property);
339
  }
340
341
  Local<Value> rv;
342
12656161
  if (maybe_rv.ToLocal(&rv)) {
343
12652117
    if (rv == sandbox)
344
152
      rv = ctx->global_proxy();
345
346
25304234
    args.GetReturnValue().Set(rv);
347
  }
348
}
349
350
// static
351
2778
void ContextifyContext::PropertySetterCallback(
352
    Local<Name> property,
353
    Local<Value> value,
354
    const PropertyCallbackInfo<Value>& args) {
355
2778
  ContextifyContext* ctx = ContextifyContext::Get(args);
356
357
  // Still initializing
358
5556
  if (ctx->context_.IsEmpty())
359
14
    return;
360
361
2778
  auto attributes = PropertyAttribute::None;
362
  bool is_declared_on_global_proxy = ctx->global_proxy()
363
8334
      ->GetRealNamedPropertyAttributes(ctx->context(), property)
364
5556
      .To(&attributes);
365
  bool read_only =
366
2778
      static_cast<int>(attributes) &
367
2778
      static_cast<int>(PropertyAttribute::ReadOnly);
368
369
  bool is_declared_on_sandbox = ctx->sandbox()
370
8334
      ->GetRealNamedPropertyAttributes(ctx->context(), property)
371
5556
      .To(&attributes);
372

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

2765
  bool is_declared = is_declared_on_global_proxy || is_declared_on_sandbox;
395


4462
  if (!is_declared && args.ShouldThrowOnError() && is_contextual_store &&
396
31
      !is_function)
397
1
    return;
398
399

7225
  if (!is_declared_on_global_proxy && is_declared_on_sandbox  &&
400

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

15
  if (is_declared && read_only)
458
    return;
459
460
15
  Local<Object> sandbox = ctx->sandbox();
461
462
  auto define_prop_on_sandbox =
463
15
      [&] (PropertyDescriptor* desc_for_sandbox) {
464
15
        if (desc.has_enumerable()) {
465
2
          desc_for_sandbox->set_enumerable(desc.enumerable());
466
        }
467
15
        if (desc.has_configurable()) {
468
1
          desc_for_sandbox->set_configurable(desc.configurable());
469
        }
470
        // Set the property on the sandbox.
471
30
        sandbox->DefineProperty(context, property, *desc_for_sandbox)
472
30
            .FromJust();
473
30
      };
474
475

15
  if (desc.has_get() || desc.has_set()) {
476
    PropertyDescriptor desc_for_sandbox(
477
10
        desc.has_get() ? desc.get() : Undefined(isolate).As<Value>(),
478

23
        desc.has_set() ? desc.set() : Undefined(isolate).As<Value>());
479
480
5
    define_prop_on_sandbox(&desc_for_sandbox);
481
  } else {
482
    Local<Value> value =
483

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

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

400
    TRACE_EVENT_NESTABLE_ASYNC_END0(
736
        TRACING_CATEGORY_NODE2(vm, script),
737
        "ContextifyScript::New",
738
        contextify_script);
739
4752
    return;
740
  }
741
8704
  contextify_script->script_.Reset(isolate, v8_script.ToLocalChecked());
742
743
4352
  if (compile_options == ScriptCompiler::kConsumeCodeCache) {
744
    args.This()->Set(
745
        env->context(),
746
        env->cached_data_rejected_string(),
747
138
        Boolean::New(isolate, source.GetCachedData()->rejected)).FromJust();
748
4329
  } else if (produce_cached_data) {
749
    const ScriptCompiler::CachedData* cached_data =
750
6
      ScriptCompiler::CreateCodeCache(v8_script.ToLocalChecked());
751
6
    bool cached_data_produced = cached_data != nullptr;
752
6
    if (cached_data_produced) {
753
      MaybeLocal<Object> buf = Buffer::Copy(
754
          env,
755
          reinterpret_cast<const char*>(cached_data->data),
756
6
          cached_data->length);
757
      args.This()->Set(env->context(),
758
                       env->cached_data_string(),
759
30
                       buf.ToLocalChecked()).FromJust();
760
    }
761
    args.This()->Set(
762
        env->context(),
763
        env->cached_data_produced_string(),
764
36
        Boolean::New(isolate, cached_data_produced)).FromJust();
765
  }
766

8704
  TRACE_EVENT_NESTABLE_ASYNC_END0(
767
      TRACING_CATEGORY_NODE2(vm, script),
768
      "ContextifyScript::New",
769
4352
      contextify_script);
770
}
771
772
5318
bool ContextifyScript::InstanceOf(Environment* env,
773
                                  const Local<Value>& value) {
774

21272
  return !value.IsEmpty() &&
775
15954
         env->script_context_constructor_template()->HasInstance(value);
776
}
777
778
11
void ContextifyScript::CreateCachedData(
779
    const FunctionCallbackInfo<Value>& args) {
780
11
  Environment* env = Environment::GetCurrent(args);
781
  ContextifyScript* wrapped_script;
782
22
  ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder());
783
  Local<UnboundScript> unbound_script =
784
11
      PersistentToLocal::Default(env->isolate(), wrapped_script->script_);
785
  std::unique_ptr<ScriptCompiler::CachedData> cached_data(
786
11
      ScriptCompiler::CreateCodeCache(unbound_script));
787
11
  if (!cached_data) {
788
    args.GetReturnValue().Set(Buffer::New(env, 0).ToLocalChecked());
789
  } else {
790
    MaybeLocal<Object> buf = Buffer::Copy(
791
        env,
792
11
        reinterpret_cast<const char*>(cached_data->data),
793
22
        cached_data->length);
794
22
    args.GetReturnValue().Set(buf.ToLocalChecked());
795
11
  }
796
}
797
798
626
void ContextifyScript::RunInThisContext(
799
    const FunctionCallbackInfo<Value>& args) {
800
626
  Environment* env = Environment::GetCurrent(args);
801
802
  ContextifyScript* wrapped_script;
803
1239
  ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder());
804
805

1252
  TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
806
      TRACING_CATEGORY_NODE2(vm, script), "RunInThisContext", wrapped_script);
807
808
  // TODO(addaleax): Use an options object or otherwise merge this with
809
  // RunInContext().
810
626
  CHECK_EQ(args.Length(), 4);
811
812
1252
  CHECK(args[0]->IsNumber());
813
2504
  int64_t timeout = args[0]->IntegerValue(env->context()).FromJust();
814
815
1252
  CHECK(args[1]->IsBoolean());
816
1252
  bool display_errors = args[1]->IsTrue();
817
818
1252
  CHECK(args[2]->IsBoolean());
819
1252
  bool break_on_sigint = args[2]->IsTrue();
820
821
1252
  CHECK(args[3]->IsBoolean());
822
1252
  bool break_on_first_line = args[3]->IsTrue();
823
824
  // Do the eval within this context
825
  EvalMachine(env,
826
              timeout,
827
              display_errors,
828
              break_on_sigint,
829
              break_on_first_line,
830
626
              args);
831
832

1226
  TRACE_EVENT_NESTABLE_ASYNC_END0(
833
      TRACING_CATEGORY_NODE2(vm, script), "RunInThisContext", wrapped_script);
834
}
835
836
4692
void ContextifyScript::RunInContext(const FunctionCallbackInfo<Value>& args) {
837
4692
  Environment* env = Environment::GetCurrent(args);
838
839
  ContextifyScript* wrapped_script;
840
4692
  ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder());
841
842
4692
  CHECK_EQ(args.Length(), 5);
843
844
9384
  CHECK(args[0]->IsObject());
845
9384
  Local<Object> sandbox = args[0].As<Object>();
846
  // Get the context from the sandbox
847
  ContextifyContext* contextify_context =
848
4692
      ContextifyContext::ContextFromContextifiedSandbox(env, sandbox);
849
4692
  CHECK_NOT_NULL(contextify_context);
850
851
9384
  if (contextify_context->context().IsEmpty())
852
    return;
853
854

9384
  TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
855
      TRACING_CATEGORY_NODE2(vm, script), "RunInContext", wrapped_script);
856
857
9384
  CHECK(args[1]->IsNumber());
858
18768
  int64_t timeout = args[1]->IntegerValue(env->context()).FromJust();
859
860
9384
  CHECK(args[2]->IsBoolean());
861
9384
  bool display_errors = args[2]->IsTrue();
862
863
9384
  CHECK(args[3]->IsBoolean());
864
9384
  bool break_on_sigint = args[3]->IsTrue();
865
866
9384
  CHECK(args[4]->IsBoolean());
867
9384
  bool break_on_first_line = args[4]->IsTrue();
868
869
  // Do the eval within the context
870
4692
  Context::Scope context_scope(contextify_context->context());
871
  EvalMachine(contextify_context->env(),
872
              timeout,
873
              display_errors,
874
              break_on_sigint,
875
              break_on_first_line,
876
4692
              args);
877
878

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

5318
  if (break_on_sigint && timeout != -1) {
912
    Watchdog wd(env->isolate(), timeout, &timed_out);
913
    SigintWatchdog swd(env->isolate(), &received_signal);
914
    result = script->Run(env->context());
915
5318
  } else if (break_on_sigint) {
916
86
    SigintWatchdog swd(env->isolate(), &received_signal);
917
172
    result = script->Run(env->context());
918
5232
  } else if (timeout != -1) {
919
1012
    Watchdog wd(env->isolate(), timeout, &timed_out);
920
2024
    result = script->Run(env->context());
921
  } else {
922
8440
    result = script->Run(env->context());
923
  }
924
925
  // Convert the termination exception into a regular exception.
926

5305
  if (timed_out || received_signal) {
927

518
    if (!env->is_main_thread() && env->is_stopping_worker())
928
      return false;
929
518
    env->isolate()->CancelTerminateExecution();
930
    // It is possible that execution was terminated by another timeout in
931
    // which this timeout is nested, so check whether one of the watchdogs
932
    // from this invocation is responsible for termination.
933
518
    if (timed_out) {
934
507
      node::THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(env, timeout);
935
11
    } else if (received_signal) {
936
11
      node::THROW_ERR_SCRIPT_EXECUTION_INTERRUPTED(env);
937
    }
938
  }
939
940
5305
  if (try_catch.HasCaught()) {
941

3713
    if (!timed_out && !received_signal && display_errors) {
942
      // We should decorate non-termination exceptions
943
3163
      DecorateErrorStack(env, try_catch);
944
    }
945
946
    // If there was an exception thrown during script execution, re-throw it.
947
    // If one of the above checks threw, re-throw the exception instead of
948
    // letting try_catch catch it.
949
    // If execution has been terminated, but not by one of the watchdogs from
950
    // this invocation, this will re-throw a `null` value.
951
3713
    if (!try_catch.HasTerminated())
952
3703
      try_catch.ReThrow();
953
954
3713
    return false;
955
  }
956
957
3184
  args.GetReturnValue().Set(result.ToLocalChecked());
958
1592
  return true;
959
}
960
961
962
4552
ContextifyScript::ContextifyScript(Environment* env, Local<Object> object)
963
    : BaseObject(env, object),
964
9104
      id_(env->get_next_script_id()) {
965
4552
  MakeWeak();
966
4551
  env->id_to_script_map.emplace(id_, this);
967
4552
}
968
969
970
17660
ContextifyScript::~ContextifyScript() {
971
4415
  env()->id_to_script_map.erase(id_);
972
8830
}
973
974
975
41632
void ContextifyContext::CompileFunction(
976
    const FunctionCallbackInfo<Value>& args) {
977
41632
  Environment* env = Environment::GetCurrent(args);
978
41632
  Isolate* isolate = env->isolate();
979
41632
  Local<Context> context = env->context();
980
981
  // Argument 1: source code
982
124896
  CHECK(args[0]->IsString());
983
83264
  Local<String> code = args[0].As<String>();
984
985
  // Argument 2: filename
986
124896
  CHECK(args[1]->IsString());
987
83264
  Local<String> filename = args[1].As<String>();
988
989
  // Argument 3: line offset
990
83264
  CHECK(args[2]->IsNumber());
991
83264
  Local<Integer> line_offset = args[2].As<Integer>();
992
993
  // Argument 4: column offset
994
83264
  CHECK(args[3]->IsNumber());
995
83264
  Local<Integer> column_offset = args[3].As<Integer>();
996
997
  // Argument 5: cached data (optional)
998
  Local<ArrayBufferView> cached_data_buf;
999
124896
  if (!args[4]->IsUndefined()) {
1000
    CHECK(args[4]->IsArrayBufferView());
1001
    cached_data_buf = args[4].As<ArrayBufferView>();
1002
  }
1003
1004
  // Argument 6: produce cache data
1005
83264
  CHECK(args[5]->IsBoolean());
1006
83264
  bool produce_cached_data = args[5]->IsTrue();
1007
1008
  // Argument 7: parsing context (optional)
1009
  Local<Context> parsing_context;
1010
124896
  if (!args[6]->IsUndefined()) {
1011
4
    CHECK(args[6]->IsObject());
1012
    ContextifyContext* sandbox =
1013
        ContextifyContext::ContextFromContextifiedSandbox(
1014
4
            env, args[6].As<Object>());
1015
2
    CHECK_NOT_NULL(sandbox);
1016
2
    parsing_context = sandbox->context();
1017
  } else {
1018
41630
    parsing_context = context;
1019
  }
1020
1021
  // Argument 8: context extensions (optional)
1022
  Local<Array> context_extensions_buf;
1023
124896
  if (!args[7]->IsUndefined()) {
1024
83264
    CHECK(args[7]->IsArray());
1025
83264
    context_extensions_buf = args[7].As<Array>();
1026
  }
1027
1028
  // Argument 9: params for the function (optional)
1029
  Local<Array> params_buf;
1030
124896
  if (!args[8]->IsUndefined()) {
1031
83252
    CHECK(args[8]->IsArray());
1032
83252
    params_buf = args[8].As<Array>();
1033
  }
1034
1035
  // Read cache from cached data buffer
1036
41632
  ScriptCompiler::CachedData* cached_data = nullptr;
1037
41632
  if (!cached_data_buf.IsEmpty()) {
1038
    ArrayBuffer::Contents contents = cached_data_buf->Buffer()->GetContents();
1039
    uint8_t* data = static_cast<uint8_t*>(contents.Data());
1040
    cached_data = new ScriptCompiler::CachedData(
1041
      data + cached_data_buf->ByteOffset(), cached_data_buf->ByteLength());
1042
  }
1043
1044
  // Get the function id
1045
41632
  uint32_t id = env->get_next_function_id();
1046
1047
  // Set host_defined_options
1048
  Local<PrimitiveArray> host_defined_options =
1049
41632
      PrimitiveArray::New(isolate, loader::HostDefinedOptions::kLength);
1050
  host_defined_options->Set(
1051
      isolate,
1052
      loader::HostDefinedOptions::kType,
1053
124896
      Number::New(isolate, loader::ScriptType::kFunction));
1054
  host_defined_options->Set(
1055
124896
      isolate, loader::HostDefinedOptions::kID, Number::New(isolate, id));
1056
1057
  ScriptOrigin origin(filename,
1058
                      line_offset,       // line offset
1059
                      column_offset,     // column offset
1060
                      True(isolate),     // is cross origin
1061
                      Local<Integer>(),  // script id
1062
                      Local<Value>(),    // source map URL
1063
                      False(isolate),    // is opaque (?)
1064
                      False(isolate),    // is WASM
1065
                      False(isolate),    // is ES Module
1066
208160
                      host_defined_options);
1067
1068
  ScriptCompiler::Source source(code, origin, cached_data);
1069
  ScriptCompiler::CompileOptions options;
1070
41632
  if (source.GetCachedData() == nullptr) {
1071
41632
    options = ScriptCompiler::kNoCompileOptions;
1072
  } else {
1073
    options = ScriptCompiler::kConsumeCodeCache;
1074
  }
1075
1076
83252
  TryCatchScope try_catch(env);
1077
41620
  Context::Scope scope(parsing_context);
1078
1079
  // Read context extensions from buffer
1080
83252
  std::vector<Local<Object>> context_extensions;
1081
41632
  if (!context_extensions_buf.IsEmpty()) {
1082
83266
    for (uint32_t n = 0; n < context_extensions_buf->Length(); n++) {
1083
      Local<Value> val;
1084
2
      if (!context_extensions_buf->Get(context, n).ToLocal(&val)) return;
1085
1
      CHECK(val->IsObject());
1086
1
      context_extensions.push_back(val.As<Object>());
1087
    }
1088
  }
1089
1090
  // Read params from params buffer
1091
83252
  std::vector<Local<String>> params;
1092
41632
  if (!params_buf.IsEmpty()) {
1093
499452
    for (uint32_t n = 0; n < params_buf->Length(); n++) {
1094
      Local<Value> val;
1095
416200
      if (!params_buf->Get(context, n).ToLocal(&val)) return;
1096
416200
      CHECK(val->IsString());
1097
208100
      params.push_back(val.As<String>());
1098
    }
1099
  }
1100
1101
  MaybeLocal<Function> maybe_fn = ScriptCompiler::CompileFunctionInContext(
1102
      parsing_context, &source, params.size(), params.data(),
1103
41632
      context_extensions.size(), context_extensions.data(), options);
1104
1105
41632
  if (maybe_fn.IsEmpty()) {
1106

12
    if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
1107
12
      DecorateErrorStack(env, try_catch);
1108
12
      try_catch.ReThrow();
1109
    }
1110
12
    return;
1111
  }
1112
41620
  Local<Function> fn = maybe_fn.ToLocalChecked();
1113
  env->id_to_function_map.emplace(std::piecewise_construct,
1114
                                  std::make_tuple(id),
1115
41620
                                  std::make_tuple(isolate, fn));
1116
41620
  CompileFnEntry* gc_entry = new CompileFnEntry(env, id);
1117
41620
  env->id_to_function_map[id].SetWeak(gc_entry,
1118
      WeakCallbackCompileFn,
1119
41620
      v8::WeakCallbackType::kParameter);
1120
1121
41620
  if (produce_cached_data) {
1122
    const std::unique_ptr<ScriptCompiler::CachedData> cached_data(
1123
        ScriptCompiler::CreateCodeCacheForFunction(fn));
1124
    bool cached_data_produced = cached_data != nullptr;
1125
    if (cached_data_produced) {
1126
      MaybeLocal<Object> buf = Buffer::Copy(
1127
          env,
1128
          reinterpret_cast<const char*>(cached_data->data),
1129
          cached_data->length);
1130
      if (fn->Set(
1131
          parsing_context,
1132
          env->cached_data_string(),
1133
          buf.ToLocalChecked()).IsNothing()) return;
1134
    }
1135
    if (fn->Set(
1136
        parsing_context,
1137
        env->cached_data_produced_string(),
1138
        Boolean::New(isolate, cached_data_produced)).IsNothing()) return;
1139
  }
1140
1141
124860
  args.GetReturnValue().Set(fn);
1142
}
1143
1144
1145
4403
void Initialize(Local<Object> target,
1146
                Local<Value> unused,
1147
                Local<Context> context,
1148
                void* priv) {
1149
4403
  Environment* env = Environment::GetCurrent(context);
1150
4403
  ContextifyContext::Init(env, target);
1151
4403
  ContextifyScript::Init(env, target);
1152
4403
}
1153
1154
}  // namespace contextify
1155
}  // namespace node
1156
1157
4292
NODE_MODULE_CONTEXT_AWARE_INTERNAL(contextify, node::contextify::Initialize)