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: 217 465 46.7 %
Date: 2019-02-01 22:03:38 Branches: 92 358 25.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
Local<Name> Uint32ToName(Local<Context> context, uint32_t index) {
98
  return Uint32::New(context->GetIsolate(), index)->ToString(context)
99
      .ToLocalChecked();
100
}
101
102
}  // anonymous namespace
103
104
2
ContextifyContext::ContextifyContext(
105
    Environment* env,
106
2
    Local<Object> sandbox_obj, const ContextOptions& options) : env_(env) {
107
2
  MaybeLocal<Context> v8_context = CreateV8Context(env, sandbox_obj, options);
108
109
  // Allocation failure, maximum call stack size reached, termination, etc.
110
4
  if (v8_context.IsEmpty()) return;
111
112
4
  context_.Reset(env->isolate(), v8_context.ToLocalChecked());
113
2
  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
2
MaybeLocal<Object> ContextifyContext::CreateDataWrapper(Environment* env) {
123
  Local<Object> wrapper;
124
4
  if (!env->script_data_constructor_function()
125
6
           ->NewInstance(env->context())
126
6
           .ToLocal(&wrapper)) {
127
    return MaybeLocal<Object>();
128
  }
129
130
2
  wrapper->SetAlignedPointerInInternalField(0, this);
131
2
  return wrapper;
132
}
133
134
2
MaybeLocal<Context> ContextifyContext::CreateV8Context(
135
    Environment* env,
136
    Local<Object> sandbox_obj,
137
    const ContextOptions& options) {
138
2
  EscapableHandleScope scope(env->isolate());
139
  Local<FunctionTemplate> function_template =
140
2
      FunctionTemplate::New(env->isolate());
141
142
4
  function_template->SetClassName(sandbox_obj->GetConstructorName());
143
144
  Local<ObjectTemplate> object_template =
145
2
      function_template->InstanceTemplate();
146
147
  Local<Object> data_wrapper;
148
4
  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
2
                                           data_wrapper);
158
159
  IndexedPropertyHandlerConfiguration indexed_config(
160
      IndexedPropertyGetterCallback,
161
      IndexedPropertySetterCallback,
162
      IndexedPropertyDescriptorCallback,
163
      IndexedPropertyDeleterCallback,
164
      PropertyEnumeratorCallback,
165
      IndexedPropertyDefinerCallback,
166
2
      data_wrapper);
167
168
2
  object_template->SetHandler(config);
169
2
  object_template->SetHandler(indexed_config);
170
171
2
  Local<Context> ctx = NewContext(env->isolate(), object_template);
172
173
2
  if (ctx.IsEmpty()) {
174
    env->ThrowError("Could not instantiate context");
175
    return MaybeLocal<Context>();
176
  }
177
178
6
  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
2
  ctx->SetEmbedderData(ContextEmbedderIndex::kSandboxObject, sandbox_obj);
187
  sandbox_obj->SetPrivate(env->context(),
188
                          env->contextify_global_private_symbol(),
189
6
                          ctx->Global());
190
191
4
  Utf8Value name_val(env->isolate(), options.name);
192
6
  ctx->AllowCodeGenerationFromStrings(options.allow_code_gen_strings->IsTrue());
193
  ctx->SetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration,
194
2
                       options.allow_code_gen_wasm);
195
196
4
  ContextInfo info(*name_val);
197
198
4
  if (!options.origin.IsEmpty()) {
199
    Utf8Value origin_val(env->isolate(), options.origin);
200
    info.origin = *origin_val;
201
  }
202
203
2
  env->AssignToContext(ctx, info);
204
205
2
  return scope.Escape(ctx);
206
}
207
208
209
164
void ContextifyContext::Init(Environment* env, Local<Object> target) {
210
  Local<FunctionTemplate> function_template =
211
164
      FunctionTemplate::New(env->isolate());
212
328
  function_template->InstanceTemplate()->SetInternalFieldCount(1);
213
  env->set_script_data_constructor_function(
214
492
      function_template->GetFunction(env->context()).ToLocalChecked());
215
216
164
  env->SetMethod(target, "makeContext", MakeContext);
217
164
  env->SetMethod(target, "isContext", IsContext);
218
164
  env->SetMethod(target, "compileFunction", CompileFunction);
219
164
}
220
221
222
// makeContext(sandbox, name, origin, strings, wasm);
223
2
void ContextifyContext::MakeContext(const FunctionCallbackInfo<Value>& args) {
224
2
  Environment* env = Environment::GetCurrent(args);
225
226
2
  CHECK_EQ(args.Length(), 5);
227
4
  CHECK(args[0]->IsObject());
228
4
  Local<Object> sandbox = args[0].As<Object>();
229
230
  // Don't allow contextifying a sandbox multiple times.
231
6
  CHECK(
232
      !sandbox->HasPrivate(
233
          env->context(),
234
          env->contextify_context_private_symbol()).FromJust());
235
236
2
  ContextOptions options;
237
238
6
  CHECK(args[1]->IsString());
239
4
  options.name = args[1].As<String>();
240
241


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

8
  if (maybe_value.ToLocal(&context_external_v) &&
298
4
      context_external_v->IsExternal()) {
299
4
    Local<External> context_external = context_external_v.As<External>();
300
4
    return static_cast<ContextifyContext*>(context_external->Value());
301
  }
302
  return nullptr;
303
}
304
305
// static
306
template <typename T>
307
15
ContextifyContext* ContextifyContext::Get(const PropertyCallbackInfo<T>& args) {
308
15
  Local<Value> data = args.Data();
309
  return static_cast<ContextifyContext*>(
310
45
      data.As<Object>()->GetAlignedPointerFromInternalField(0));
311
}
312
313
// static
314
15
void ContextifyContext::PropertyGetterCallback(
315
    Local<Name> property,
316
    const PropertyCallbackInfo<Value>& args) {
317
15
  ContextifyContext* ctx = ContextifyContext::Get(args);
318
319
  // Still initializing
320
30
  if (ctx->context_.IsEmpty())
321
27
    return;
322
323
3
  Local<Context> context = ctx->context();
324
3
  Local<Object> sandbox = ctx->sandbox();
325
  MaybeLocal<Value> maybe_rv =
326
3
      sandbox->GetRealNamedProperty(context, property);
327
3
  if (maybe_rv.IsEmpty()) {
328
    maybe_rv =
329
6
        ctx->global_proxy()->GetRealNamedProperty(context, property);
330
  }
331
332
  Local<Value> rv;
333
3
  if (maybe_rv.ToLocal(&rv)) {
334
3
    if (rv == sandbox)
335
      rv = ctx->global_proxy();
336
337
6
    args.GetReturnValue().Set(rv);
338
  }
339
}
340
341
// static
342
void ContextifyContext::PropertySetterCallback(
343
    Local<Name> property,
344
    Local<Value> value,
345
    const PropertyCallbackInfo<Value>& args) {
346
  ContextifyContext* ctx = ContextifyContext::Get(args);
347
348
  // Still initializing
349
  if (ctx->context_.IsEmpty())
350
    return;
351
352
  auto attributes = PropertyAttribute::None;
353
  bool is_declared_on_global_proxy = ctx->global_proxy()
354
      ->GetRealNamedPropertyAttributes(ctx->context(), property)
355
      .To(&attributes);
356
  bool read_only =
357
      static_cast<int>(attributes) &
358
      static_cast<int>(PropertyAttribute::ReadOnly);
359
360
  bool is_declared_on_sandbox = ctx->sandbox()
361
      ->GetRealNamedPropertyAttributes(ctx->context(), property)
362
      .To(&attributes);
363
  read_only = read_only ||
364
      (static_cast<int>(attributes) &
365
      static_cast<int>(PropertyAttribute::ReadOnly));
366
367
  if (read_only)
368
    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
  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
  bool is_function = value->IsFunction();
384
385
  bool is_declared = is_declared_on_global_proxy || is_declared_on_sandbox;
386
  if (!is_declared && args.ShouldThrowOnError() && is_contextual_store &&
387
      !is_function)
388
    return;
389
390
  if (!is_declared_on_global_proxy && is_declared_on_sandbox  &&
391
      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
    args.GetReturnValue().Set(false);
397
  }
398
399
  ctx->sandbox()->Set(ctx->context(), property, value).FromJust();
400
}
401
402
// static
403
void ContextifyContext::PropertyDescriptorCallback(
404
    Local<Name> property,
405
    const PropertyCallbackInfo<Value>& args) {
406
  ContextifyContext* ctx = ContextifyContext::Get(args);
407
408
  // Still initializing
409
  if (ctx->context_.IsEmpty())
410
    return;
411
412
  Local<Context> context = ctx->context();
413
414
  Local<Object> sandbox = ctx->sandbox();
415
416
  if (sandbox->HasOwnProperty(context, property).FromMaybe(false)) {
417
    args.GetReturnValue().Set(
418
        sandbox->GetOwnPropertyDescriptor(context, property)
419
            .ToLocalChecked());
420
  }
421
}
422
423
// static
424
void ContextifyContext::PropertyDefinerCallback(
425
    Local<Name> property,
426
    const PropertyDescriptor& desc,
427
    const PropertyCallbackInfo<Value>& args) {
428
  ContextifyContext* ctx = ContextifyContext::Get(args);
429
430
  // Still initializing
431
  if (ctx->context_.IsEmpty())
432
    return;
433
434
  Local<Context> context = ctx->context();
435
  Isolate* isolate = context->GetIsolate();
436
437
  auto attributes = PropertyAttribute::None;
438
  bool is_declared =
439
      ctx->global_proxy()->GetRealNamedPropertyAttributes(ctx->context(),
440
                                                          property)
441
          .To(&attributes);
442
  bool read_only =
443
      static_cast<int>(attributes) &
444
          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
  if (is_declared && read_only)
449
    return;
450
451
  Local<Object> sandbox = ctx->sandbox();
452
453
  auto define_prop_on_sandbox =
454
      [&] (PropertyDescriptor* desc_for_sandbox) {
455
        if (desc.has_enumerable()) {
456
          desc_for_sandbox->set_enumerable(desc.enumerable());
457
        }
458
        if (desc.has_configurable()) {
459
          desc_for_sandbox->set_configurable(desc.configurable());
460
        }
461
        // Set the property on the sandbox.
462
        sandbox->DefineProperty(context, property, *desc_for_sandbox)
463
            .FromJust();
464
      };
465
466
  if (desc.has_get() || desc.has_set()) {
467
    PropertyDescriptor desc_for_sandbox(
468
        desc.has_get() ? desc.get() : Undefined(isolate).As<Value>(),
469
        desc.has_set() ? desc.set() : Undefined(isolate).As<Value>());
470
471
    define_prop_on_sandbox(&desc_for_sandbox);
472
  } else {
473
    Local<Value> value =
474
        desc.has_value() ? desc.value() : Undefined(isolate).As<Value>();
475
476
    if (desc.has_writable()) {
477
      PropertyDescriptor desc_for_sandbox(value, desc.writable());
478
      define_prop_on_sandbox(&desc_for_sandbox);
479
    } else {
480
      PropertyDescriptor desc_for_sandbox(value);
481
      define_prop_on_sandbox(&desc_for_sandbox);
482
    }
483
  }
484
}
485
486
// static
487
void ContextifyContext::PropertyDeleterCallback(
488
    Local<Name> property,
489
    const PropertyCallbackInfo<Boolean>& args) {
490
  ContextifyContext* ctx = ContextifyContext::Get(args);
491
492
  // Still initializing
493
  if (ctx->context_.IsEmpty())
494
    return;
495
496
  Maybe<bool> success = ctx->sandbox()->Delete(ctx->context(), property);
497
498
  if (success.FromMaybe(false))
499
    return;
500
501
  // Delete failed on the sandbox, intercept and do not delete on
502
  // the global object.
503
  args.GetReturnValue().Set(false);
504
}
505
506
// static
507
void ContextifyContext::PropertyEnumeratorCallback(
508
    const PropertyCallbackInfo<Array>& args) {
509
  ContextifyContext* ctx = ContextifyContext::Get(args);
510
511
  // Still initializing
512
  if (ctx->context_.IsEmpty())
513
    return;
514
515
  Local<Array> properties;
516
517
  if (!ctx->sandbox()->GetPropertyNames(ctx->context()).ToLocal(&properties))
518
    return;
519
520
  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
void ContextifyContext::IndexedPropertySetterCallback(
539
    uint32_t index,
540
    Local<Value> value,
541
    const PropertyCallbackInfo<Value>& args) {
542
  ContextifyContext* ctx = ContextifyContext::Get(args);
543
544
  // Still initializing
545
  if (ctx->context_.IsEmpty())
546
    return;
547
548
  ContextifyContext::PropertySetterCallback(
549
      Uint32ToName(ctx->context(), index), value, args);
550
}
551
552
// static
553
void ContextifyContext::IndexedPropertyDescriptorCallback(
554
    uint32_t index,
555
    const PropertyCallbackInfo<Value>& args) {
556
  ContextifyContext* ctx = ContextifyContext::Get(args);
557
558
  // Still initializing
559
  if (ctx->context_.IsEmpty())
560
    return;
561
562
  ContextifyContext::PropertyDescriptorCallback(
563
      Uint32ToName(ctx->context(), index), args);
564
}
565
566
567
void ContextifyContext::IndexedPropertyDefinerCallback(
568
    uint32_t index,
569
    const PropertyDescriptor& desc,
570
    const PropertyCallbackInfo<Value>& args) {
571
  ContextifyContext* ctx = ContextifyContext::Get(args);
572
573
  // Still initializing
574
  if (ctx->context_.IsEmpty())
575
    return;
576
577
  ContextifyContext::PropertyDefinerCallback(
578
      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
164
void ContextifyScript::Init(Environment* env, Local<Object> target) {
602
164
  HandleScope scope(env->isolate());
603
  Local<String> class_name =
604
164
      FIXED_ONE_BYTE_STRING(env->isolate(), "ContextifyScript");
605
606
164
  Local<FunctionTemplate> script_tmpl = env->NewFunctionTemplate(New);
607
328
  script_tmpl->InstanceTemplate()->SetInternalFieldCount(1);
608
164
  script_tmpl->SetClassName(class_name);
609
164
  env->SetProtoMethod(script_tmpl, "createCachedData", CreateCachedData);
610
164
  env->SetProtoMethod(script_tmpl, "runInContext", RunInContext);
611
164
  env->SetProtoMethod(script_tmpl, "runInThisContext", RunInThisContext);
612
613
  target->Set(env->context(), class_name,
614
820
      script_tmpl->GetFunction(env->context()).ToLocalChecked()).FromJust();
615
164
  env->set_script_context_constructor_template(script_tmpl);
616
164
}
617
618
10138
void ContextifyScript::New(const FunctionCallbackInfo<Value>& args) {
619
10138
  Environment* env = Environment::GetCurrent(args);
620
10138
  Isolate* isolate = env->isolate();
621
10138
  Local<Context> context = env->context();
622
623
10138
  CHECK(args.IsConstructCall());
624
625
10138
  const int argc = args.Length();
626
10138
  CHECK_GE(argc, 2);
627
628
30414
  CHECK(args[0]->IsString());
629
20276
  Local<String> code = args[0].As<String>();
630
631
30414
  CHECK(args[1]->IsString());
632
20276
  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
10138
  bool produce_cached_data = false;
638
10138
  Local<Context> parsing_context = context;
639
640
10138
  if (argc > 2) {
641
    // new ContextifyScript(code, filename, lineOffset, columnOffset,
642
    //                      cachedData, produceCachedData, parsingContext)
643
10138
    CHECK_EQ(argc, 7);
644
20276
    CHECK(args[2]->IsNumber());
645
20276
    line_offset = args[2].As<Integer>();
646
20276
    CHECK(args[3]->IsNumber());
647
20276
    column_offset = args[3].As<Integer>();
648
30414
    if (!args[4]->IsUndefined()) {
649
      CHECK(args[4]->IsArrayBufferView());
650
      cached_data_buf = args[4].As<ArrayBufferView>();
651
    }
652
20276
    CHECK(args[5]->IsBoolean());
653
20276
    produce_cached_data = args[5]->IsTrue();
654
30414
    if (!args[6]->IsUndefined()) {
655
4
      CHECK(args[6]->IsObject());
656
      ContextifyContext* sandbox =
657
          ContextifyContext::ContextFromContextifiedSandbox(
658
4
              env, args[6].As<Object>());
659
2
      CHECK_NOT_NULL(sandbox);
660
2
      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
10138
      new ContextifyScript(env, args.This());
669
670
20276
  if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
671
10138
          TRACING_CATEGORY_NODE2(vm, script)) != 0) {
672
    Utf8Value fn(isolate, filename);
673
    TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(
674
        TRACING_CATEGORY_NODE2(vm, script),
675
        "ContextifyScript::New",
676
        contextify_script,
677
        "filename", TRACE_STR_COPY(*fn));
678
  }
679
680
10138
  ScriptCompiler::CachedData* cached_data = nullptr;
681
10138
  if (!cached_data_buf.IsEmpty()) {
682
    ArrayBuffer::Contents contents = cached_data_buf->Buffer()->GetContents();
683
    uint8_t* data = static_cast<uint8_t*>(contents.Data());
684
    cached_data = new ScriptCompiler::CachedData(
685
        data + cached_data_buf->ByteOffset(), cached_data_buf->ByteLength());
686
  }
687
688
  Local<PrimitiveArray> host_defined_options =
689
10138
      PrimitiveArray::New(isolate, loader::HostDefinedOptions::kLength);
690
  host_defined_options->Set(isolate, loader::HostDefinedOptions::kType,
691
20276
                            Number::New(isolate, loader::ScriptType::kScript));
692
  host_defined_options->Set(isolate, loader::HostDefinedOptions::kID,
693
20276
                            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
10138
                      host_defined_options);
705
  ScriptCompiler::Source source(code, origin, cached_data);
706
  ScriptCompiler::CompileOptions compile_options =
707
10138
      ScriptCompiler::kNoCompileOptions;
708
709
10138
  if (source.GetCachedData() != nullptr)
710
    compile_options = ScriptCompiler::kConsumeCodeCache;
711
712
20276
  TryCatchScope try_catch(env);
713
20276
  Environment::ShouldNotAbortOnUncaughtScope no_abort_scope(env);
714
10138
  Context::Scope scope(parsing_context);
715
716
  MaybeLocal<UnboundScript> v8_script = ScriptCompiler::CompileUnboundScript(
717
      isolate,
718
      &source,
719
10138
      compile_options);
720
721
10138
  if (v8_script.IsEmpty()) {
722
    DecorateErrorStack(env, try_catch);
723
    no_abort_scope.Close();
724
    try_catch.ReThrow();
725
    TRACE_EVENT_NESTABLE_ASYNC_END0(
726
        TRACING_CATEGORY_NODE2(vm, script),
727
        "ContextifyScript::New",
728
        contextify_script);
729
10138
    return;
730
  }
731
20276
  contextify_script->script_.Reset(isolate, v8_script.ToLocalChecked());
732
733
10138
  if (compile_options == ScriptCompiler::kConsumeCodeCache) {
734
    args.This()->Set(
735
        env->context(),
736
        env->cached_data_rejected_string(),
737
        Boolean::New(isolate, source.GetCachedData()->rejected)).FromJust();
738
10138
  } else if (produce_cached_data) {
739
    const ScriptCompiler::CachedData* cached_data =
740
      ScriptCompiler::CreateCodeCache(v8_script.ToLocalChecked());
741
    bool cached_data_produced = cached_data != nullptr;
742
    if (cached_data_produced) {
743
      MaybeLocal<Object> buf = Buffer::Copy(
744
          env,
745
          reinterpret_cast<const char*>(cached_data->data),
746
          cached_data->length);
747
      args.This()->Set(env->context(),
748
                       env->cached_data_string(),
749
                       buf.ToLocalChecked()).FromJust();
750
    }
751
    args.This()->Set(
752
        env->context(),
753
        env->cached_data_produced_string(),
754
        Boolean::New(isolate, cached_data_produced)).FromJust();
755
  }
756

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

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

10136
  TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
796
      TRACING_CATEGORY_NODE2(vm, script), "RunInThisContext", wrapped_script);
797
798
10136
  CHECK_EQ(args.Length(), 3);
799
800
20272
  CHECK(args[0]->IsNumber());
801
40544
  int64_t timeout = args[0]->IntegerValue(env->context()).FromJust();
802
803
20272
  CHECK(args[1]->IsBoolean());
804
20272
  bool display_errors = args[1]->IsTrue();
805
806
20272
  CHECK(args[2]->IsBoolean());
807
20272
  bool break_on_sigint = args[2]->IsTrue();
808
809
  // Do the eval within this context
810
10136
  EvalMachine(env, timeout, display_errors, break_on_sigint, args);
811
812

10136
  TRACE_EVENT_NESTABLE_ASYNC_END0(
813
      TRACING_CATEGORY_NODE2(vm, script), "RunInThisContext", wrapped_script);
814
}
815
816
2
void ContextifyScript::RunInContext(const FunctionCallbackInfo<Value>& args) {
817
2
  Environment* env = Environment::GetCurrent(args);
818
819
  ContextifyScript* wrapped_script;
820
2
  ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder());
821
822
2
  CHECK_EQ(args.Length(), 4);
823
824
4
  CHECK(args[0]->IsObject());
825
4
  Local<Object> sandbox = args[0].As<Object>();
826
  // Get the context from the sandbox
827
  ContextifyContext* contextify_context =
828
2
      ContextifyContext::ContextFromContextifiedSandbox(env, sandbox);
829
2
  CHECK_NOT_NULL(contextify_context);
830
831
4
  if (contextify_context->context().IsEmpty())
832
    return;
833
834

2
  TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
835
      TRACING_CATEGORY_NODE2(vm, script), "RunInContext", wrapped_script);
836
837
4
  CHECK(args[1]->IsNumber());
838
8
  int64_t timeout = args[1]->IntegerValue(env->context()).FromJust();
839
840
4
  CHECK(args[2]->IsBoolean());
841
4
  bool display_errors = args[2]->IsTrue();
842
843
4
  CHECK(args[3]->IsBoolean());
844
4
  bool break_on_sigint = args[3]->IsTrue();
845
846
  // Do the eval within the context
847
2
  Context::Scope context_scope(contextify_context->context());
848
  EvalMachine(contextify_context->env(),
849
              timeout,
850
              display_errors,
851
              break_on_sigint,
852
2
              args);
853
854

2
  TRACE_EVENT_NESTABLE_ASYNC_END0(
855
      TRACING_CATEGORY_NODE2(vm, script), "RunInContext", wrapped_script);
856
}
857
858
10138
bool ContextifyScript::EvalMachine(Environment* env,
859
                                   const int64_t timeout,
860
                                   const bool display_errors,
861
                                   const bool break_on_sigint,
862
                                   const FunctionCallbackInfo<Value>& args) {
863
10138
  if (!env->can_call_into_js())
864
    return false;
865
10138
  if (!ContextifyScript::InstanceOf(env, args.Holder())) {
866
    env->ThrowTypeError(
867
        "Script methods can only be called on script instances.");
868
    return false;
869
  }
870
10138
  TryCatchScope try_catch(env);
871
  ContextifyScript* wrapped_script;
872
10138
  ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder(), false);
873
  Local<UnboundScript> unbound_script =
874
10138
      PersistentToLocal::Default(env->isolate(), wrapped_script->script_);
875
10138
  Local<Script> script = unbound_script->BindToCurrentContext();
876
877
  MaybeLocal<Value> result;
878
10138
  bool timed_out = false;
879
10138
  bool received_signal = false;
880

10138
  if (break_on_sigint && timeout != -1) {
881
    Watchdog wd(env->isolate(), timeout, &timed_out);
882
    SigintWatchdog swd(env->isolate(), &received_signal);
883
    result = script->Run(env->context());
884
10138
  } else if (break_on_sigint) {
885
    SigintWatchdog swd(env->isolate(), &received_signal);
886
    result = script->Run(env->context());
887
10138
  } else if (timeout != -1) {
888
    Watchdog wd(env->isolate(), timeout, &timed_out);
889
    result = script->Run(env->context());
890
  } else {
891
20276
    result = script->Run(env->context());
892
  }
893
894
  // Convert the termination exception into a regular exception.
895

10138
  if (timed_out || received_signal) {
896
    env->isolate()->CancelTerminateExecution();
897
    // It is possible that execution was terminated by another timeout in
898
    // which this timeout is nested, so check whether one of the watchdogs
899
    // from this invocation is responsible for termination.
900
    if (timed_out) {
901
      node::THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(env, timeout);
902
    } else if (received_signal) {
903
      node::THROW_ERR_SCRIPT_EXECUTION_INTERRUPTED(env);
904
    }
905
  }
906
907
10138
  if (try_catch.HasCaught()) {
908
    if (!timed_out && !received_signal && display_errors) {
909
      // We should decorate non-termination exceptions
910
      DecorateErrorStack(env, try_catch);
911
    }
912
913
    // If there was an exception thrown during script execution, re-throw it.
914
    // If one of the above checks threw, re-throw the exception instead of
915
    // letting try_catch catch it.
916
    // If execution has been terminated, but not by one of the watchdogs from
917
    // this invocation, this will re-throw a `null` value.
918
    try_catch.ReThrow();
919
920
    return false;
921
  }
922
923
20276
  args.GetReturnValue().Set(result.ToLocalChecked());
924
10138
  return true;
925
}
926
927
928
10138
ContextifyScript::ContextifyScript(Environment* env, Local<Object> object)
929
    : BaseObject(env, object),
930
20276
      id_(env->get_next_script_id()) {
931
10138
  MakeWeak();
932
10138
  env->id_to_script_map.emplace(id_, this);
933
10138
}
934
935
936
39260
ContextifyScript::~ContextifyScript() {
937
9815
  env()->id_to_script_map.erase(id_);
938
19630
}
939
940
941
void ContextifyContext::CompileFunction(
942
    const FunctionCallbackInfo<Value>& args) {
943
  Environment* env = Environment::GetCurrent(args);
944
  Isolate* isolate = env->isolate();
945
  Local<Context> context = env->context();
946
947
  // Argument 1: source code
948
  CHECK(args[0]->IsString());
949
  Local<String> code = args[0].As<String>();
950
951
  // Argument 2: filename
952
  CHECK(args[1]->IsString());
953
  Local<String> filename = args[1].As<String>();
954
955
  // Argument 3: line offset
956
  CHECK(args[2]->IsNumber());
957
  Local<Integer> line_offset = args[2].As<Integer>();
958
959
  // Argument 4: column offset
960
  CHECK(args[3]->IsNumber());
961
  Local<Integer> column_offset = args[3].As<Integer>();
962
963
  // Argument 5: cached data (optional)
964
  Local<ArrayBufferView> cached_data_buf;
965
  if (!args[4]->IsUndefined()) {
966
    CHECK(args[4]->IsArrayBufferView());
967
    cached_data_buf = args[4].As<ArrayBufferView>();
968
  }
969
970
  // Argument 6: produce cache data
971
  CHECK(args[5]->IsBoolean());
972
  bool produce_cached_data = args[5]->IsTrue();
973
974
  // Argument 7: parsing context (optional)
975
  Local<Context> parsing_context;
976
  if (!args[6]->IsUndefined()) {
977
    CHECK(args[6]->IsObject());
978
    ContextifyContext* sandbox =
979
        ContextifyContext::ContextFromContextifiedSandbox(
980
            env, args[6].As<Object>());
981
    CHECK_NOT_NULL(sandbox);
982
    parsing_context = sandbox->context();
983
  } else {
984
    parsing_context = context;
985
  }
986
987
  // Argument 8: context extensions (optional)
988
  Local<Array> context_extensions_buf;
989
  if (!args[7]->IsUndefined()) {
990
    CHECK(args[7]->IsArray());
991
    context_extensions_buf = args[7].As<Array>();
992
  }
993
994
  // Argument 9: params for the function (optional)
995
  Local<Array> params_buf;
996
  if (!args[8]->IsUndefined()) {
997
    CHECK(args[8]->IsArray());
998
    params_buf = args[8].As<Array>();
999
  }
1000
1001
  // Read cache from cached data buffer
1002
  ScriptCompiler::CachedData* cached_data = nullptr;
1003
  if (!cached_data_buf.IsEmpty()) {
1004
    ArrayBuffer::Contents contents = cached_data_buf->Buffer()->GetContents();
1005
    uint8_t* data = static_cast<uint8_t*>(contents.Data());
1006
    cached_data = new ScriptCompiler::CachedData(
1007
      data + cached_data_buf->ByteOffset(), cached_data_buf->ByteLength());
1008
  }
1009
1010
  ScriptOrigin origin(filename, line_offset, column_offset, True(isolate));
1011
  ScriptCompiler::Source source(code, origin, cached_data);
1012
  ScriptCompiler::CompileOptions options;
1013
  if (source.GetCachedData() == nullptr) {
1014
    options = ScriptCompiler::kNoCompileOptions;
1015
  } else {
1016
    options = ScriptCompiler::kConsumeCodeCache;
1017
  }
1018
1019
  TryCatchScope try_catch(env);
1020
  Context::Scope scope(parsing_context);
1021
1022
  // Read context extensions from buffer
1023
  std::vector<Local<Object>> context_extensions;
1024
  if (!context_extensions_buf.IsEmpty()) {
1025
    for (uint32_t n = 0; n < context_extensions_buf->Length(); n++) {
1026
      Local<Value> val;
1027
      if (!context_extensions_buf->Get(context, n).ToLocal(&val)) return;
1028
      CHECK(val->IsObject());
1029
      context_extensions.push_back(val.As<Object>());
1030
    }
1031
  }
1032
1033
  // Read params from params buffer
1034
  std::vector<Local<String>> params;
1035
  if (!params_buf.IsEmpty()) {
1036
    for (uint32_t n = 0; n < params_buf->Length(); n++) {
1037
      Local<Value> val;
1038
      if (!params_buf->Get(context, n).ToLocal(&val)) return;
1039
      CHECK(val->IsString());
1040
      params.push_back(val.As<String>());
1041
    }
1042
  }
1043
1044
  MaybeLocal<Function> maybe_fun = ScriptCompiler::CompileFunctionInContext(
1045
      parsing_context, &source, params.size(), params.data(),
1046
      context_extensions.size(), context_extensions.data(), options);
1047
1048
  Local<Function> fun;
1049
  if (maybe_fun.IsEmpty() || !maybe_fun.ToLocal(&fun)) {
1050
    DecorateErrorStack(env, try_catch);
1051
    try_catch.ReThrow();
1052
    return;
1053
  }
1054
1055
  if (produce_cached_data) {
1056
    const std::unique_ptr<ScriptCompiler::CachedData> cached_data(
1057
        ScriptCompiler::CreateCodeCacheForFunction(fun));
1058
    bool cached_data_produced = cached_data != nullptr;
1059
    if (cached_data_produced) {
1060
      MaybeLocal<Object> buf = Buffer::Copy(
1061
          env,
1062
          reinterpret_cast<const char*>(cached_data->data),
1063
          cached_data->length);
1064
      if (fun->Set(
1065
          parsing_context,
1066
          env->cached_data_string(),
1067
          buf.ToLocalChecked()).IsNothing()) return;
1068
    }
1069
    if (fun->Set(
1070
        parsing_context,
1071
        env->cached_data_produced_string(),
1072
        Boolean::New(isolate, cached_data_produced)).IsNothing()) return;
1073
  }
1074
1075
  args.GetReturnValue().Set(fun);
1076
}
1077
1078
1079
164
void Initialize(Local<Object> target,
1080
                Local<Value> unused,
1081
                Local<Context> context,
1082
                void* priv) {
1083
164
  Environment* env = Environment::GetCurrent(context);
1084
164
  ContextifyContext::Init(env, target);
1085
164
  ContextifyScript::Init(env, target);
1086
164
}
1087
1088
}  // namespace contextify
1089
}  // namespace node
1090
1091
164
NODE_MODULE_CONTEXT_AWARE_INTERNAL(contextify, node::contextify::Initialize)