// 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 "base_object-inl.h" #include "memory_tracker-inl.h" #include "module_wrap.h" #include "node_context_data.h" #include "node_errors.h" #include "node_external_reference.h" #include "node_internals.h" #include "node_snapshot_builder.h" #include "node_watchdog.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::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::HandleScope; using v8::IndexedPropertyHandlerConfiguration; using v8::Int32; using v8::Isolate; using v8::Just; 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::Nothing; 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::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 BaseObjectPtr ContextifyContext::New( Environment* env, Local sandbox_obj, const ContextOptions& options) { HandleScope scope(env->isolate()); InitializeGlobalTemplates(env->isolate_data()); Local object_template = env->contextify_global_template(); DCHECK(!object_template.IsEmpty()); const SnapshotData* snapshot_data = env->isolate_data()->snapshot_data(); MicrotaskQueue* queue = options.microtask_queue_wrap ? options.microtask_queue_wrap->microtask_queue().get() : env->isolate()->GetCurrentContext()->GetMicrotaskQueue(); Local v8_context; if (!(CreateV8Context(env->isolate(), object_template, snapshot_data, queue) .ToLocal(&v8_context))) { // Allocation failure, maximum call stack size reached, termination, etc. return BaseObjectPtr(); } return New(v8_context, env, sandbox_obj, options); } void ContextifyContext::MemoryInfo(MemoryTracker* tracker) const { if (microtask_queue_wrap_) { tracker->TrackField("microtask_queue_wrap", microtask_queue_wrap_->object()); } } ContextifyContext::ContextifyContext(Environment* env, Local wrapper, Local v8_context, const ContextOptions& options) : BaseObject(env, wrapper), microtask_queue_wrap_(options.microtask_queue_wrap) { context_.Reset(env->isolate(), v8_context); // This should only be done after the initial initializations of the context // global object is finished. DCHECK_NULL(v8_context->GetAlignedPointerFromEmbedderData( ContextEmbedderIndex::kContextifyContext)); v8_context->SetAlignedPointerInEmbedderData( ContextEmbedderIndex::kContextifyContext, this); // It's okay to make this reference weak - V8 would create an internal // reference to this context via the constructor of the wrapper. // As long as the wrapper is alive, it's constructor is alive, and so // is the context. context_.SetWeak(); } ContextifyContext::~ContextifyContext() { Isolate* isolate = env()->isolate(); HandleScope scope(isolate); env()->UntrackContext(PersistentToLocal::Weak(isolate, context_)); context_.Reset(); } void ContextifyContext::InitializeGlobalTemplates(IsolateData* isolate_data) { if (!isolate_data->contextify_global_template().IsEmpty()) { return; } DCHECK(isolate_data->contextify_wrapper_template().IsEmpty()); Local global_func_template = FunctionTemplate::New(isolate_data->isolate()); Local global_object_template = global_func_template->InstanceTemplate(); NamedPropertyHandlerConfiguration config( PropertyGetterCallback, PropertySetterCallback, PropertyDescriptorCallback, PropertyDeleterCallback, PropertyEnumeratorCallback, PropertyDefinerCallback, {}, PropertyHandlerFlags::kHasNoSideEffect); IndexedPropertyHandlerConfiguration indexed_config( IndexedPropertyGetterCallback, IndexedPropertySetterCallback, IndexedPropertyDescriptorCallback, IndexedPropertyDeleterCallback, PropertyEnumeratorCallback, IndexedPropertyDefinerCallback, {}, PropertyHandlerFlags::kHasNoSideEffect); global_object_template->SetHandler(config); global_object_template->SetHandler(indexed_config); isolate_data->set_contextify_global_template(global_object_template); Local wrapper_func_template = BaseObject::MakeLazilyInitializedJSTemplate(isolate_data); Local wrapper_object_template = wrapper_func_template->InstanceTemplate(); isolate_data->set_contextify_wrapper_template(wrapper_object_template); } MaybeLocal ContextifyContext::CreateV8Context( Isolate* isolate, Local object_template, const SnapshotData* snapshot_data, MicrotaskQueue* queue) { EscapableHandleScope scope(isolate); Local ctx; if (snapshot_data == nullptr) { ctx = Context::New(isolate, nullptr, // extensions object_template, {}, // global object {}, // deserialization callback queue); if (ctx.IsEmpty() || InitializeBaseContextForSnapshot(ctx).IsNothing()) { return MaybeLocal(); } } else if (!Context::FromSnapshot(isolate, SnapshotData::kNodeVMContextIndex, {}, // deserialization callback nullptr, // extensions {}, // global object queue) .ToLocal(&ctx)) { return MaybeLocal(); } return scope.Escape(ctx); } BaseObjectPtr ContextifyContext::New( Local v8_context, Environment* env, Local sandbox_obj, const ContextOptions& options) { HandleScope scope(env->isolate()); // This only initializes part of the context. The primordials are // only initialized when needed because even deserializing them slows // things down significantly and they are only needed in rare occasions // in the vm contexts. if (InitializeContextRuntime(v8_context).IsNothing()) { return BaseObjectPtr(); } Local main_context = env->context(); Local new_context_global = v8_context->Global(); v8_context->SetSecurityToken(main_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. The sandbox uses a private symbol to hold a reference // to the ContextifyContext wrapper which in turn internally references // the context from its constructor. v8_context->SetEmbedderData(ContextEmbedderIndex::kSandboxObject, sandbox_obj); // Delegate the code generation validation to // node::ModifyCodeGenerationFromStrings. v8_context->AllowCodeGenerationFromStrings(false); v8_context->SetEmbedderData( ContextEmbedderIndex::kAllowCodeGenerationFromStrings, options.allow_code_gen_strings); v8_context->SetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration, options.allow_code_gen_wasm); Utf8Value name_val(env->isolate(), options.name); ContextInfo info(*name_val); if (!options.origin.IsEmpty()) { Utf8Value origin_val(env->isolate(), options.origin); info.origin = *origin_val; } BaseObjectPtr result; Local wrapper; { Context::Scope context_scope(v8_context); Local ctor_name = sandbox_obj->GetConstructorName(); if (!ctor_name->Equals(v8_context, env->object_string()).FromMaybe(false) && new_context_global ->DefineOwnProperty( v8_context, v8::Symbol::GetToStringTag(env->isolate()), ctor_name, static_cast(v8::DontEnum)) .IsNothing()) { return BaseObjectPtr(); } env->AssignToContext(v8_context, nullptr, info); if (!env->contextify_wrapper_template() ->NewInstance(v8_context) .ToLocal(&wrapper)) { return BaseObjectPtr(); } result = MakeBaseObject(env, wrapper, v8_context, options); // The only strong reference to the wrapper will come from the sandbox. result->MakeWeak(); } if (sandbox_obj ->SetPrivate( v8_context, env->contextify_context_private_symbol(), wrapper) .IsNothing()) { return BaseObjectPtr(); } return result; } void ContextifyContext::Init(Environment* env, Local target) { Local context = env->context(); SetMethod(context, target, "makeContext", MakeContext); SetMethod(context, target, "isContext", IsContext); SetMethod(context, target, "compileFunction", CompileFunction); } void ContextifyContext::RegisterExternalReferences( ExternalReferenceRegistry* registry) { registry->Register(MakeContext); registry->Register(IsContext); registry->Register(CompileFunction); registry->Register(PropertyGetterCallback); registry->Register(PropertySetterCallback); registry->Register(PropertyDescriptorCallback); registry->Register(PropertyDeleterCallback); registry->Register(PropertyEnumeratorCallback); registry->Register(PropertyDefinerCallback); registry->Register(IndexedPropertyGetterCallback); registry->Register(IndexedPropertySetterCallback); registry->Register(IndexedPropertyDescriptorCallback); registry->Register(IndexedPropertyDeleterCallback); registry->Register(IndexedPropertyDefinerCallback); } // 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); BaseObjectPtr context_ptr = ContextifyContext::New(env, sandbox, options); if (try_catch.HasCaught()) { if (!try_catch.HasTerminated()) try_catch.ReThrow(); return; } } 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) { Local context_global; if (sandbox ->GetPrivate(env->context(), env->contextify_context_private_symbol()) .ToLocal(&context_global) && context_global->IsObject()) { return Unwrap(context_global.As()); } return nullptr; } template ContextifyContext* ContextifyContext::Get(const PropertyCallbackInfo& args) { return Get(args.This()); } ContextifyContext* ContextifyContext::Get(Local object) { Local context; if (!object->GetCreationContext().ToLocal(&context)) { return nullptr; } if (!ContextEmbedderTag::IsNodeContext(context)) { return nullptr; } return static_cast( context->GetAlignedPointerFromEmbedderData( ContextEmbedderIndex::kContextifyContext)); } bool ContextifyContext::IsStillInitializing(const ContextifyContext* ctx) { return ctx == nullptr || ctx->context_.IsEmpty(); } // static void ContextifyContext::PropertyGetterCallback( Local property, const PropertyCallbackInfo& args) { ContextifyContext* ctx = ContextifyContext::Get(args); // Still initializing if (IsStillInitializing(ctx)) 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 (IsStillInitializing(ctx)) return; Local context = ctx->context(); PropertyAttribute attributes = PropertyAttribute::None; bool is_declared_on_global_proxy = ctx->global_proxy() ->GetRealNamedPropertyAttributes(context, property) .To(&attributes); bool read_only = static_cast(attributes) & static_cast(PropertyAttribute::ReadOnly); bool is_declared_on_sandbox = ctx->sandbox() ->GetRealNamedPropertyAttributes(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 (ctx->sandbox()->Set(context, property, value).IsNothing()) return; Local desc; if (is_declared_on_sandbox && ctx->sandbox() ->GetOwnPropertyDescriptor(context, property) .ToLocal(&desc)) { Environment* env = Environment::GetCurrent(context); Local desc_obj = desc.As(); // We have to specify the return value for any contextual or get/set // property if (desc_obj->HasOwnProperty(context, env->get_string()).FromMaybe(false) || desc_obj->HasOwnProperty(context, env->set_string()).FromMaybe(false)) args.GetReturnValue().Set(value); } } // static void ContextifyContext::PropertyDescriptorCallback( Local property, const PropertyCallbackInfo& args) { ContextifyContext* ctx = ContextifyContext::Get(args); // Still initializing if (IsStillInitializing(ctx)) 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 (IsStillInitializing(ctx)) return; Local context = ctx->context(); Isolate* isolate = context->GetIsolate(); PropertyAttribute attributes = PropertyAttribute::None; bool is_declared = ctx->global_proxy()->GetRealNamedPropertyAttributes(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 (IsStillInitializing(ctx)) 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 (IsStillInitializing(ctx)) 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 (IsStillInitializing(ctx)) 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 (IsStillInitializing(ctx)) 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 (IsStillInitializing(ctx)) 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 (IsStillInitializing(ctx)) 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 (IsStillInitializing(ctx)) 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) { Isolate* isolate = env->isolate(); HandleScope scope(env->isolate()); Local class_name = FIXED_ONE_BYTE_STRING(env->isolate(), "ContextifyScript"); Local script_tmpl = NewFunctionTemplate(isolate, New); script_tmpl->InstanceTemplate()->SetInternalFieldCount( ContextifyScript::kInternalFieldCount); script_tmpl->SetClassName(class_name); SetProtoMethod(isolate, script_tmpl, "createCachedData", CreateCachedData); SetProtoMethod(isolate, script_tmpl, "runInContext", RunInContext); Local context = env->context(); target->Set(context, class_name, script_tmpl->GetFunction(context).ToLocalChecked()).Check(); env->set_script_context_constructor_template(script_tmpl); } void ContextifyScript::RegisterExternalReferences( ExternalReferenceRegistry* registry) { registry->Register(New); registry->Register(CreateCachedData); registry->Register(RunInContext); } 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(); int line_offset = 0; int column_offset = 0; 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()->Value(); CHECK(args[3]->IsNumber()); column_offset = args[3].As()->Value(); 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(); } } 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_BEGIN1(TRACING_CATEGORY_NODE2(vm, script), "ContextifyScript::New", "filename", TRACE_STR_COPY(*fn)); } ScriptCompiler::CachedData* cached_data = nullptr; if (!cached_data_buf.IsEmpty()) { uint8_t* data = static_cast(cached_data_buf->Buffer()->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(isolate, filename, line_offset, // line offset column_offset, // column offset true, // is cross origin -1, // script id Local(), // source map URL false, // is opaque (?) false, // is WASM false, // 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 maybe_v8_script = ScriptCompiler::CompileUnboundScript(isolate, &source, compile_options); Local v8_script; if (!maybe_v8_script.ToLocal(&v8_script)) { errors::DecorateErrorStack(env, try_catch); no_abort_scope.Close(); if (!try_catch.HasTerminated()) try_catch.ReThrow(); TRACE_EVENT_END0(TRACING_CATEGORY_NODE2(vm, script), "ContextifyScript::New"); return; } contextify_script->script_.Reset(isolate, v8_script); std::unique_ptr new_cached_data; if (produce_cached_data) { new_cached_data.reset(ScriptCompiler::CreateCodeCache(v8_script)); } if (StoreCodeCacheResult(env, args.This(), compile_options, source, produce_cached_data, std::move(new_cached_data)) .IsNothing()) { return; } if (args.This() ->Set(env->context(), env->source_map_url_string(), v8_script->GetSourceMappingURL()) .IsNothing()) return; TRACE_EVENT_END0(TRACING_CATEGORY_NODE2(vm, script), "ContextifyScript::New"); } Maybe StoreCodeCacheResult( Environment* env, Local target, ScriptCompiler::CompileOptions compile_options, const v8::ScriptCompiler::Source& source, bool produce_cached_data, std::unique_ptr new_cached_data) { Local context; if (!target->GetCreationContext().ToLocal(&context)) { return Nothing(); } if (compile_options == ScriptCompiler::kConsumeCodeCache) { if (target ->Set( context, env->cached_data_rejected_string(), Boolean::New(env->isolate(), source.GetCachedData()->rejected)) .IsNothing()) { return Nothing(); } } if (produce_cached_data) { bool cached_data_produced = new_cached_data != nullptr; if (cached_data_produced) { MaybeLocal buf = Buffer::Copy(env, reinterpret_cast(new_cached_data->data), new_cached_data->length); if (target->Set(context, env->cached_data_string(), buf.ToLocalChecked()) .IsNothing()) { return Nothing(); } } if (target ->Set(context, env->cached_data_produced_string(), Boolean::New(env->isolate(), cached_data_produced)) .IsNothing()) { return Nothing(); } } return Just(true); } 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::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() || args[0]->IsNull()); Local context; std::shared_ptr microtask_queue; if (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); CHECK_EQ(contextify_context->env(), env); context = contextify_context->context(); if (context.IsEmpty()) return; microtask_queue = contextify_context->microtask_queue(); } else { context = env->context(); } TRACE_EVENT0(TRACING_CATEGORY_NODE2(vm, script), "RunInContext"); 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 EvalMachine(context, env, timeout, display_errors, break_on_sigint, break_on_first_line, microtask_queue, args); } bool ContextifyScript::EvalMachine(Local context, 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) { Context::Scope context_scope(context); if (!env->can_call_into_js()) return false; if (!ContextifyScript::InstanceOf(env, args.Holder())) { THROW_ERR_INVALID_THIS( env, "Script methods can only be called on script instances."); return false; } TryCatchScope try_catch(env); Isolate::SafeForTerminationScope safe_for_termination(env->isolate()); ContextifyScript* wrapped_script; ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder(), false); Local unbound_script = PersistentToLocal::Default(env->isolate(), wrapped_script->script_); Local