// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "node_contextify.h"
#include "memory_tracker-inl.h"
#include "node_internals.h"
#include "node_watchdog.h"
#include "base_object-inl.h"
#include "node_context_data.h"
#include "node_errors.h"
#include "module_wrap.h"
#include "util-inl.h"
namespace node {
namespace contextify {
using errors::TryCatchScope;
using v8::Array;
using v8::ArrayBufferView;
using v8::Boolean;
using v8::Context;
using v8::EscapableHandleScope;
using v8::External;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::HandleScope;
using v8::IndexedPropertyHandlerConfiguration;
using v8::Int32;
using v8::Integer;
using v8::Isolate;
using v8::Local;
using v8::Maybe;
using v8::MaybeLocal;
using v8::MeasureMemoryExecution;
using v8::MeasureMemoryMode;
using v8::MicrotaskQueue;
using v8::MicrotasksPolicy;
using v8::Name;
using v8::NamedPropertyHandlerConfiguration;
using v8::Number;
using v8::Object;
using v8::ObjectTemplate;
using v8::PrimitiveArray;
using v8::Promise;
using v8::PropertyAttribute;
using v8::PropertyCallbackInfo;
using v8::PropertyDescriptor;
using v8::PropertyHandlerFlags;
using v8::Script;
using v8::ScriptCompiler;
using v8::ScriptOrigin;
using v8::ScriptOrModule;
using v8::String;
using v8::Uint32;
using v8::UnboundScript;
using v8::Value;
using v8::WeakCallbackInfo;
using v8::WeakCallbackType;
// The vm module executes code in a sandboxed environment with a different
// global object than the rest of the code. This is achieved by applying
// every call that changes or queries a property on the global `this` in the
// sandboxed code, to the sandbox object.
//
// The implementation uses V8's interceptors for methods like `set`, `get`,
// `delete`, `defineProperty`, and for any query of the property attributes.
// Property handlers with interceptors are set on the object template for
// the sandboxed code. Handlers for both named properties and for indexed
// properties are used. Their functionality is almost identical, the indexed
// interceptors mostly just call the named interceptors.
//
// For every `get` of a global property in the sandboxed context, the
// interceptor callback checks the sandbox object for the property.
// If the property is defined on the sandbox, that result is returned to
// the original call instead of finishing the query on the global object.
//
// For every `set` of a global property, the interceptor callback defines or
// changes the property both on the sandbox and the global proxy.
namespace {
// Convert an int to a V8 Name (String or Symbol).
Local Uint32ToName(Local context, uint32_t index) {
return Uint32::New(context->GetIsolate(), index)->ToString(context)
.ToLocalChecked();
}
} // anonymous namespace
ContextifyContext::ContextifyContext(
Environment* env,
Local sandbox_obj,
const ContextOptions& options)
: env_(env),
microtask_queue_wrap_(options.microtask_queue_wrap) {
MaybeLocal v8_context = CreateV8Context(env, sandbox_obj, options);
// Allocation failure, maximum call stack size reached, termination, etc.
if (v8_context.IsEmpty()) return;
context_.Reset(env->isolate(), v8_context.ToLocalChecked());
context_.SetWeak(this, WeakCallback, WeakCallbackType::kParameter);
env->AddCleanupHook(CleanupHook, this);
}
ContextifyContext::~ContextifyContext() {
env()->RemoveCleanupHook(CleanupHook, this);
}
void ContextifyContext::CleanupHook(void* arg) {
ContextifyContext* self = static_cast(arg);
self->context_.Reset();
delete self;
}
// This is an object that just keeps an internal pointer to this
// ContextifyContext. It's passed to the NamedPropertyHandler. If we
// pass the main JavaScript context object we're embedded in, then the
// NamedPropertyHandler will store a reference to it forever and keep it
// from getting gc'd.
MaybeLocal ContextifyContext::CreateDataWrapper(Environment* env) {
Local wrapper;
if (!env->script_data_constructor_function()
->NewInstance(env->context())
.ToLocal(&wrapper)) {
return MaybeLocal();
}
wrapper->SetAlignedPointerInInternalField(ContextifyContext::kSlot, this);
return wrapper;
}
MaybeLocal ContextifyContext::CreateV8Context(
Environment* env,
Local sandbox_obj,
const ContextOptions& options) {
EscapableHandleScope scope(env->isolate());
Local function_template =
FunctionTemplate::New(env->isolate());
function_template->SetClassName(sandbox_obj->GetConstructorName());
Local object_template =
function_template->InstanceTemplate();
Local data_wrapper;
if (!CreateDataWrapper(env).ToLocal(&data_wrapper))
return MaybeLocal();
NamedPropertyHandlerConfiguration config(
PropertyGetterCallback,
PropertySetterCallback,
PropertyDescriptorCallback,
PropertyDeleterCallback,
PropertyEnumeratorCallback,
PropertyDefinerCallback,
data_wrapper,
PropertyHandlerFlags::kHasNoSideEffect);
IndexedPropertyHandlerConfiguration indexed_config(
IndexedPropertyGetterCallback,
IndexedPropertySetterCallback,
IndexedPropertyDescriptorCallback,
IndexedPropertyDeleterCallback,
PropertyEnumeratorCallback,
IndexedPropertyDefinerCallback,
data_wrapper,
PropertyHandlerFlags::kHasNoSideEffect);
object_template->SetHandler(config);
object_template->SetHandler(indexed_config);
Local ctx = Context::New(
env->isolate(),
nullptr, // extensions
object_template,
{}, // global object
{}, // deserialization callback
microtask_queue() ? microtask_queue().get() : nullptr);
if (ctx.IsEmpty()) return MaybeLocal();
// Only partially initialize the context - the primordials are left out
// and only initialized when necessary.
InitializeContextRuntime(ctx);
if (ctx.IsEmpty()) {
return MaybeLocal();
}
ctx->SetSecurityToken(env->context()->GetSecurityToken());
// We need to tie the lifetime of the sandbox object with the lifetime of
// newly created context. We do this by making them hold references to each
// other. The context can directly hold a reference to the sandbox as an
// embedder data field. However, we cannot hold a reference to a v8::Context
// directly in an Object, we instead hold onto the new context's global
// object instead (which then has a reference to the context).
ctx->SetEmbedderData(ContextEmbedderIndex::kSandboxObject, sandbox_obj);
sandbox_obj->SetPrivate(env->context(),
env->contextify_global_private_symbol(),
ctx->Global());
Utf8Value name_val(env->isolate(), options.name);
ctx->AllowCodeGenerationFromStrings(options.allow_code_gen_strings->IsTrue());
ctx->SetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration,
options.allow_code_gen_wasm);
ContextInfo info(*name_val);
if (!options.origin.IsEmpty()) {
Utf8Value origin_val(env->isolate(), options.origin);
info.origin = *origin_val;
}
env->AssignToContext(ctx, info);
return scope.Escape(ctx);
}
void ContextifyContext::Init(Environment* env, Local target) {
Local function_template =
FunctionTemplate::New(env->isolate());
function_template->InstanceTemplate()->SetInternalFieldCount(
ContextifyContext::kInternalFieldCount);
env->set_script_data_constructor_function(
function_template->GetFunction(env->context()).ToLocalChecked());
env->SetMethod(target, "makeContext", MakeContext);
env->SetMethod(target, "isContext", IsContext);
env->SetMethod(target, "compileFunction", CompileFunction);
}
// makeContext(sandbox, name, origin, strings, wasm);
void ContextifyContext::MakeContext(const FunctionCallbackInfo& args) {
Environment* env = Environment::GetCurrent(args);
CHECK_EQ(args.Length(), 6);
CHECK(args[0]->IsObject());
Local sandbox = args[0].As();
// Don't allow contextifying a sandbox multiple times.
CHECK(
!sandbox->HasPrivate(
env->context(),
env->contextify_context_private_symbol()).FromJust());
ContextOptions options;
CHECK(args[1]->IsString());
options.name = args[1].As();
CHECK(args[2]->IsString() || args[2]->IsUndefined());
if (args[2]->IsString()) {
options.origin = args[2].As();
}
CHECK(args[3]->IsBoolean());
options.allow_code_gen_strings = args[3].As();
CHECK(args[4]->IsBoolean());
options.allow_code_gen_wasm = args[4].As();
if (args[5]->IsObject() &&
!env->microtask_queue_ctor_template().IsEmpty() &&
env->microtask_queue_ctor_template()->HasInstance(args[5])) {
options.microtask_queue_wrap.reset(
Unwrap(args[5].As()));
}
TryCatchScope try_catch(env);
auto context_ptr = std::make_unique(env, sandbox, options);
if (try_catch.HasCaught()) {
if (!try_catch.HasTerminated())
try_catch.ReThrow();
return;
}
if (context_ptr->context().IsEmpty())
return;
sandbox->SetPrivate(
env->context(),
env->contextify_context_private_symbol(),
External::New(env->isolate(), context_ptr.release()));
}
void ContextifyContext::IsContext(const FunctionCallbackInfo& args) {
Environment* env = Environment::GetCurrent(args);
CHECK(args[0]->IsObject());
Local sandbox = args[0].As();
Maybe result =
sandbox->HasPrivate(env->context(),
env->contextify_context_private_symbol());
args.GetReturnValue().Set(result.FromJust());
}
void ContextifyContext::WeakCallback(
const WeakCallbackInfo& data) {
ContextifyContext* context = data.GetParameter();
delete context;
}
// static
ContextifyContext* ContextifyContext::ContextFromContextifiedSandbox(
Environment* env,
const Local& sandbox) {
MaybeLocal maybe_value =
sandbox->GetPrivate(env->context(),
env->contextify_context_private_symbol());
Local context_external_v;
if (maybe_value.ToLocal(&context_external_v) &&
context_external_v->IsExternal()) {
Local context_external = context_external_v.As();
return static_cast(context_external->Value());
}
return nullptr;
}
// static
template
ContextifyContext* ContextifyContext::Get(const PropertyCallbackInfo& args) {
Local data = args.Data();
return static_cast(
data.As()->GetAlignedPointerFromInternalField(
ContextifyContext::kSlot));
}
// static
void ContextifyContext::PropertyGetterCallback(
Local property,
const PropertyCallbackInfo& args) {
ContextifyContext* ctx = ContextifyContext::Get(args);
// Still initializing
if (ctx->context_.IsEmpty())
return;
Local context = ctx->context();
Local sandbox = ctx->sandbox();
MaybeLocal maybe_rv =
sandbox->GetRealNamedProperty(context, property);
if (maybe_rv.IsEmpty()) {
maybe_rv =
ctx->global_proxy()->GetRealNamedProperty(context, property);
}
Local rv;
if (maybe_rv.ToLocal(&rv)) {
if (rv == sandbox)
rv = ctx->global_proxy();
args.GetReturnValue().Set(rv);
}
}
// static
void ContextifyContext::PropertySetterCallback(
Local property,
Local value,
const PropertyCallbackInfo& args) {
ContextifyContext* ctx = ContextifyContext::Get(args);
// Still initializing
if (ctx->context_.IsEmpty())
return;
auto attributes = PropertyAttribute::None;
bool is_declared_on_global_proxy = ctx->global_proxy()
->GetRealNamedPropertyAttributes(ctx->context(), property)
.To(&attributes);
bool read_only =
static_cast(attributes) &
static_cast(PropertyAttribute::ReadOnly);
bool is_declared_on_sandbox = ctx->sandbox()
->GetRealNamedPropertyAttributes(ctx->context(), property)
.To(&attributes);
read_only = read_only ||
(static_cast(attributes) &
static_cast(PropertyAttribute::ReadOnly));
if (read_only)
return;
// true for x = 5
// false for this.x = 5
// false for Object.defineProperty(this, 'foo', ...)
// false for vmResult.x = 5 where vmResult = vm.runInContext();
bool is_contextual_store = ctx->global_proxy() != args.This();
// Indicator to not return before setting (undeclared) function declarations
// on the sandbox in strict mode, i.e. args.ShouldThrowOnError() = true.
// True for 'function f() {}', 'this.f = function() {}',
// 'var f = function()'.
// In effect only for 'function f() {}' because
// var f = function(), is_declared = true
// this.f = function() {}, is_contextual_store = false.
bool is_function = value->IsFunction();
bool is_declared = is_declared_on_global_proxy || is_declared_on_sandbox;
if (!is_declared && args.ShouldThrowOnError() && is_contextual_store &&
!is_function)
return;
if (!is_declared_on_global_proxy && is_declared_on_sandbox &&
args.ShouldThrowOnError() && is_contextual_store && !is_function) {
// The property exists on the sandbox but not on the global
// proxy. Setting it would throw because we are in strict mode.
// Don't attempt to set it by signaling that the call was
// intercepted. Only change the value on the sandbox.
args.GetReturnValue().Set(false);
}
USE(ctx->sandbox()->Set(ctx->context(), property, value));
}
// static
void ContextifyContext::PropertyDescriptorCallback(
Local property,
const PropertyCallbackInfo& args) {
ContextifyContext* ctx = ContextifyContext::Get(args);
// Still initializing
if (ctx->context_.IsEmpty())
return;
Local context = ctx->context();
Local sandbox = ctx->sandbox();
if (sandbox->HasOwnProperty(context, property).FromMaybe(false)) {
Local desc;
if (sandbox->GetOwnPropertyDescriptor(context, property).ToLocal(&desc)) {
args.GetReturnValue().Set(desc);
}
}
}
// static
void ContextifyContext::PropertyDefinerCallback(
Local property,
const PropertyDescriptor& desc,
const PropertyCallbackInfo& args) {
ContextifyContext* ctx = ContextifyContext::Get(args);
// Still initializing
if (ctx->context_.IsEmpty())
return;
Local context = ctx->context();
Isolate* isolate = context->GetIsolate();
auto attributes = PropertyAttribute::None;
bool is_declared =
ctx->global_proxy()->GetRealNamedPropertyAttributes(ctx->context(),
property)
.To(&attributes);
bool read_only =
static_cast(attributes) &
static_cast(PropertyAttribute::ReadOnly);
// If the property is set on the global as read_only, don't change it on
// the global or sandbox.
if (is_declared && read_only)
return;
Local sandbox = ctx->sandbox();
auto define_prop_on_sandbox =
[&] (PropertyDescriptor* desc_for_sandbox) {
if (desc.has_enumerable()) {
desc_for_sandbox->set_enumerable(desc.enumerable());
}
if (desc.has_configurable()) {
desc_for_sandbox->set_configurable(desc.configurable());
}
// Set the property on the sandbox.
USE(sandbox->DefineProperty(context, property, *desc_for_sandbox));
};
if (desc.has_get() || desc.has_set()) {
PropertyDescriptor desc_for_sandbox(
desc.has_get() ? desc.get() : Undefined(isolate).As(),
desc.has_set() ? desc.set() : Undefined(isolate).As());
define_prop_on_sandbox(&desc_for_sandbox);
} else {
Local value =
desc.has_value() ? desc.value() : Undefined(isolate).As();
if (desc.has_writable()) {
PropertyDescriptor desc_for_sandbox(value, desc.writable());
define_prop_on_sandbox(&desc_for_sandbox);
} else {
PropertyDescriptor desc_for_sandbox(value);
define_prop_on_sandbox(&desc_for_sandbox);
}
}
}
// static
void ContextifyContext::PropertyDeleterCallback(
Local property,
const PropertyCallbackInfo& args) {
ContextifyContext* ctx = ContextifyContext::Get(args);
// Still initializing
if (ctx->context_.IsEmpty())
return;
Maybe success = ctx->sandbox()->Delete(ctx->context(), property);
if (success.FromMaybe(false))
return;
// Delete failed on the sandbox, intercept and do not delete on
// the global object.
args.GetReturnValue().Set(false);
}
// static
void ContextifyContext::PropertyEnumeratorCallback(
const PropertyCallbackInfo& args) {
ContextifyContext* ctx = ContextifyContext::Get(args);
// Still initializing
if (ctx->context_.IsEmpty())
return;
Local properties;
if (!ctx->sandbox()->GetPropertyNames(ctx->context()).ToLocal(&properties))
return;
args.GetReturnValue().Set(properties);
}
// static
void ContextifyContext::IndexedPropertyGetterCallback(
uint32_t index,
const PropertyCallbackInfo& args) {
ContextifyContext* ctx = ContextifyContext::Get(args);
// Still initializing
if (ctx->context_.IsEmpty())
return;
ContextifyContext::PropertyGetterCallback(
Uint32ToName(ctx->context(), index), args);
}
void ContextifyContext::IndexedPropertySetterCallback(
uint32_t index,
Local value,
const PropertyCallbackInfo& args) {
ContextifyContext* ctx = ContextifyContext::Get(args);
// Still initializing
if (ctx->context_.IsEmpty())
return;
ContextifyContext::PropertySetterCallback(
Uint32ToName(ctx->context(), index), value, args);
}
// static
void ContextifyContext::IndexedPropertyDescriptorCallback(
uint32_t index,
const PropertyCallbackInfo& args) {
ContextifyContext* ctx = ContextifyContext::Get(args);
// Still initializing
if (ctx->context_.IsEmpty())
return;
ContextifyContext::PropertyDescriptorCallback(
Uint32ToName(ctx->context(), index), args);
}
void ContextifyContext::IndexedPropertyDefinerCallback(
uint32_t index,
const PropertyDescriptor& desc,
const PropertyCallbackInfo& args) {
ContextifyContext* ctx = ContextifyContext::Get(args);
// Still initializing
if (ctx->context_.IsEmpty())
return;
ContextifyContext::PropertyDefinerCallback(
Uint32ToName(ctx->context(), index), desc, args);
}
// static
void ContextifyContext::IndexedPropertyDeleterCallback(
uint32_t index,
const PropertyCallbackInfo& args) {
ContextifyContext* ctx = ContextifyContext::Get(args);
// Still initializing
if (ctx->context_.IsEmpty())
return;
Maybe success = ctx->sandbox()->Delete(ctx->context(), index);
if (success.FromMaybe(false))
return;
// Delete failed on the sandbox, intercept and do not delete on
// the global object.
args.GetReturnValue().Set(false);
}
void ContextifyScript::Init(Environment* env, Local target) {
HandleScope scope(env->isolate());
Local class_name =
FIXED_ONE_BYTE_STRING(env->isolate(), "ContextifyScript");
Local script_tmpl = env->NewFunctionTemplate(New);
script_tmpl->InstanceTemplate()->SetInternalFieldCount(
ContextifyScript::kInternalFieldCount);
script_tmpl->SetClassName(class_name);
env->SetProtoMethod(script_tmpl, "createCachedData", CreateCachedData);
env->SetProtoMethod(script_tmpl, "runInContext", RunInContext);
env->SetProtoMethod(script_tmpl, "runInThisContext", RunInThisContext);
target->Set(env->context(), class_name,
script_tmpl->GetFunction(env->context()).ToLocalChecked()).Check();
env->set_script_context_constructor_template(script_tmpl);
}
void ContextifyScript::New(const FunctionCallbackInfo& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
Local context = env->context();
CHECK(args.IsConstructCall());
const int argc = args.Length();
CHECK_GE(argc, 2);
CHECK(args[0]->IsString());
Local code = args[0].As();
CHECK(args[1]->IsString());
Local filename = args[1].As();
Local line_offset;
Local column_offset;
Local cached_data_buf;
bool produce_cached_data = false;
Local parsing_context = context;
if (argc > 2) {
// new ContextifyScript(code, filename, lineOffset, columnOffset,
// cachedData, produceCachedData, parsingContext)
CHECK_EQ(argc, 7);
CHECK(args[2]->IsNumber());
line_offset = args[2].As();
CHECK(args[3]->IsNumber());
column_offset = args[3].As();
if (!args[4]->IsUndefined()) {
CHECK(args[4]->IsArrayBufferView());
cached_data_buf = args[4].As();
}
CHECK(args[5]->IsBoolean());
produce_cached_data = args[5]->IsTrue();
if (!args[6]->IsUndefined()) {
CHECK(args[6]->IsObject());
ContextifyContext* sandbox =
ContextifyContext::ContextFromContextifiedSandbox(
env, args[6].As());
CHECK_NOT_NULL(sandbox);
parsing_context = sandbox->context();
}
} else {
line_offset = Integer::New(isolate, 0);
column_offset = Integer::New(isolate, 0);
}
ContextifyScript* contextify_script =
new ContextifyScript(env, args.This());
if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
TRACING_CATEGORY_NODE2(vm, script)) != 0) {
Utf8Value fn(isolate, filename);
TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(
TRACING_CATEGORY_NODE2(vm, script),
"ContextifyScript::New",
contextify_script,
"filename", TRACE_STR_COPY(*fn));
}
ScriptCompiler::CachedData* cached_data = nullptr;
if (!cached_data_buf.IsEmpty()) {
uint8_t* data = static_cast(
cached_data_buf->Buffer()->GetBackingStore()->Data());
cached_data = new ScriptCompiler::CachedData(
data + cached_data_buf->ByteOffset(), cached_data_buf->ByteLength());
}
Local host_defined_options =
PrimitiveArray::New(isolate, loader::HostDefinedOptions::kLength);
host_defined_options->Set(isolate, loader::HostDefinedOptions::kType,
Number::New(isolate, loader::ScriptType::kScript));
host_defined_options->Set(isolate, loader::HostDefinedOptions::kID,
Number::New(isolate, contextify_script->id()));
ScriptOrigin origin(filename,
line_offset, // line offset
column_offset, // column offset
True(isolate), // is cross origin
Local(), // script id
Local(), // source map URL
False(isolate), // is opaque (?)
False(isolate), // is WASM
False(isolate), // is ES Module
host_defined_options);
ScriptCompiler::Source source(code, origin, cached_data);
ScriptCompiler::CompileOptions compile_options =
ScriptCompiler::kNoCompileOptions;
if (source.GetCachedData() != nullptr)
compile_options = ScriptCompiler::kConsumeCodeCache;
TryCatchScope try_catch(env);
ShouldNotAbortOnUncaughtScope no_abort_scope(env);
Context::Scope scope(parsing_context);
MaybeLocal v8_script = ScriptCompiler::CompileUnboundScript(
isolate,
&source,
compile_options);
if (v8_script.IsEmpty()) {
errors::DecorateErrorStack(env, try_catch);
no_abort_scope.Close();
if (!try_catch.HasTerminated())
try_catch.ReThrow();
TRACE_EVENT_NESTABLE_ASYNC_END0(
TRACING_CATEGORY_NODE2(vm, script),
"ContextifyScript::New",
contextify_script);
return;
}
contextify_script->script_.Reset(isolate, v8_script.ToLocalChecked());
if (compile_options == ScriptCompiler::kConsumeCodeCache) {
args.This()->Set(
env->context(),
env->cached_data_rejected_string(),
Boolean::New(isolate, source.GetCachedData()->rejected)).Check();
} else if (produce_cached_data) {
std::unique_ptr cached_data {
ScriptCompiler::CreateCodeCache(v8_script.ToLocalChecked()) };
bool cached_data_produced = cached_data != nullptr;
if (cached_data_produced) {
MaybeLocal buf = Buffer::Copy(
env,
reinterpret_cast(cached_data->data),
cached_data->length);
args.This()->Set(env->context(),
env->cached_data_string(),
buf.ToLocalChecked()).Check();
}
args.This()->Set(
env->context(),
env->cached_data_produced_string(),
Boolean::New(isolate, cached_data_produced)).Check();
}
TRACE_EVENT_NESTABLE_ASYNC_END0(
TRACING_CATEGORY_NODE2(vm, script),
"ContextifyScript::New",
contextify_script);
}
bool ContextifyScript::InstanceOf(Environment* env,
const Local& value) {
return !value.IsEmpty() &&
env->script_context_constructor_template()->HasInstance(value);
}
void ContextifyScript::CreateCachedData(
const FunctionCallbackInfo& args) {
Environment* env = Environment::GetCurrent(args);
ContextifyScript* wrapped_script;
ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder());
Local unbound_script =
PersistentToLocal::Default(env->isolate(), wrapped_script->script_);
std::unique_ptr cached_data(
ScriptCompiler::CreateCodeCache(unbound_script));
if (!cached_data) {
args.GetReturnValue().Set(Buffer::New(env, 0).ToLocalChecked());
} else {
MaybeLocal buf = Buffer::Copy(
env,
reinterpret_cast(cached_data->data),
cached_data->length);
args.GetReturnValue().Set(buf.ToLocalChecked());
}
}
void ContextifyScript::RunInThisContext(
const FunctionCallbackInfo& args) {
Environment* env = Environment::GetCurrent(args);
ContextifyScript* wrapped_script;
ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder());
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
TRACING_CATEGORY_NODE2(vm, script), "RunInThisContext", wrapped_script);
// TODO(addaleax): Use an options object or otherwise merge this with
// RunInContext().
CHECK_EQ(args.Length(), 4);
CHECK(args[0]->IsNumber());
int64_t timeout = args[0]->IntegerValue(env->context()).FromJust();
CHECK(args[1]->IsBoolean());
bool display_errors = args[1]->IsTrue();
CHECK(args[2]->IsBoolean());
bool break_on_sigint = args[2]->IsTrue();
CHECK(args[3]->IsBoolean());
bool break_on_first_line = args[3]->IsTrue();
// Do the eval within this context
EvalMachine(env,
timeout,
display_errors,
break_on_sigint,
break_on_first_line,
nullptr, // microtask_queue
args);
TRACE_EVENT_NESTABLE_ASYNC_END0(
TRACING_CATEGORY_NODE2(vm, script), "RunInThisContext", wrapped_script);
}
void ContextifyScript::RunInContext(const FunctionCallbackInfo& args) {
Environment* env = Environment::GetCurrent(args);
ContextifyScript* wrapped_script;
ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder());
CHECK_EQ(args.Length(), 5);
CHECK(args[0]->IsObject());
Local sandbox = args[0].As();
// Get the context from the sandbox
ContextifyContext* contextify_context =
ContextifyContext::ContextFromContextifiedSandbox(env, sandbox);
CHECK_NOT_NULL(contextify_context);
if (contextify_context->context().IsEmpty())
return;
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
TRACING_CATEGORY_NODE2(vm, script), "RunInContext", wrapped_script);
CHECK(args[1]->IsNumber());
int64_t timeout = args[1]->IntegerValue(env->context()).FromJust();
CHECK(args[2]->IsBoolean());
bool display_errors = args[2]->IsTrue();
CHECK(args[3]->IsBoolean());
bool break_on_sigint = args[3]->IsTrue();
CHECK(args[4]->IsBoolean());
bool break_on_first_line = args[4]->IsTrue();
// Do the eval within the context
Context::Scope context_scope(contextify_context->context());
EvalMachine(contextify_context->env(),
timeout,
display_errors,
break_on_sigint,
break_on_first_line,
contextify_context->microtask_queue(),
args);
TRACE_EVENT_NESTABLE_ASYNC_END0(
TRACING_CATEGORY_NODE2(vm, script), "RunInContext", wrapped_script);
}
bool ContextifyScript::EvalMachine(Environment* env,
const int64_t timeout,
const bool display_errors,
const bool break_on_sigint,
const bool break_on_first_line,
std::shared_ptr mtask_queue,
const FunctionCallbackInfo& args) {
if (!env->can_call_into_js())
return false;
if (!ContextifyScript::InstanceOf(env, args.Holder())) {
env->ThrowTypeError(
"Script methods can only be called on script instances.");
return false;
}
TryCatchScope try_catch(env);
ContextifyScript* wrapped_script;
ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder(), false);
Local unbound_script =
PersistentToLocal::Default(env->isolate(), wrapped_script->script_);
Local