// 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_internals.h" #include "node_watchdog.h" #include "base_object-inl.h" #include "node_contextify.h" namespace node { namespace contextify { using v8::Array; using v8::ArrayBuffer; 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::Integer; using v8::Just; using v8::Local; using v8::Maybe; using v8::MaybeLocal; using v8::Name; using v8::NamedPropertyHandlerConfiguration; using v8::Nothing; using v8::Object; using v8::ObjectTemplate; using v8::Persistent; using v8::PropertyAttribute; using v8::PropertyCallbackInfo; using v8::PropertyDescriptor; using v8::Script; using v8::ScriptCompiler; using v8::ScriptOrigin; using v8::String; using v8::Symbol; using v8::TryCatch; using v8::Uint32; using v8::Uint8Array; using v8::UnboundScript; using v8::Value; using v8::WeakCallbackInfo; // 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, Local options_obj) : env_(env) { Local v8_context = CreateV8Context(env, sandbox_obj, options_obj); context_.Reset(env->isolate(), v8_context); // Allocation failure or maximum call stack size reached if (context_.IsEmpty()) return; context_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter); context_.MarkIndependent(); } ContextifyContext::~ContextifyContext() { context_.Reset(); } // 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. Local ContextifyContext::CreateDataWrapper(Environment* env) { EscapableHandleScope scope(env->isolate()); Local wrapper = env->script_data_constructor_function() ->NewInstance(env->context()).FromMaybe(Local()); if (wrapper.IsEmpty()) return scope.Escape(Local::New(env->isolate(), Local())); Wrap(wrapper, this); return scope.Escape(wrapper); } Local ContextifyContext::CreateV8Context( Environment* env, Local sandbox_obj, Local options_obj) { EscapableHandleScope scope(env->isolate()); Local function_template = FunctionTemplate::New(env->isolate()); function_template->SetClassName(sandbox_obj->GetConstructorName()); Local object_template = function_template->InstanceTemplate(); NamedPropertyHandlerConfiguration config(PropertyGetterCallback, PropertySetterCallback, PropertyDescriptorCallback, PropertyDeleterCallback, PropertyEnumeratorCallback, PropertyDefinerCallback, CreateDataWrapper(env)); IndexedPropertyHandlerConfiguration indexed_config( IndexedPropertyGetterCallback, IndexedPropertySetterCallback, IndexedPropertyDescriptorCallback, IndexedPropertyDeleterCallback, PropertyEnumeratorCallback, IndexedPropertyDefinerCallback, CreateDataWrapper(env)); object_template->SetHandler(config); object_template->SetHandler(indexed_config); Local ctx = NewContext(env->isolate(), object_template); if (ctx.IsEmpty()) { env->ThrowError("Could not instantiate context"); return Local(); } 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(kSandboxObjectIndex, sandbox_obj); sandbox_obj->SetPrivate(env->context(), env->contextify_global_private_symbol(), ctx->Global()); Local name = options_obj->Get(env->context(), env->name_string()) .ToLocalChecked(); CHECK(name->IsString()); Utf8Value name_val(env->isolate(), name); ContextInfo info(*name_val); Local origin = options_obj->Get(env->context(), FIXED_ONE_BYTE_STRING(env->isolate(), "origin")) .ToLocalChecked(); if (!origin->IsUndefined()) { CHECK(origin->IsString()); Utf8Value origin_val(env->isolate(), 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(1); env->set_script_data_constructor_function(function_template->GetFunction()); env->SetMethod(target, "makeContext", MakeContext); env->SetMethod(target, "isContext", IsContext); } void ContextifyContext::MakeContext(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); if (!args[0]->IsObject()) { return env->ThrowTypeError("sandbox argument must be an object."); } Local sandbox = args[0].As(); // Don't allow contextifying a sandbox multiple times. CHECK( !sandbox->HasPrivate( env->context(), env->contextify_context_private_symbol()).FromJust()); Local options = args[1].As(); CHECK(options->IsObject()); TryCatch try_catch(env->isolate()); ContextifyContext* context = new ContextifyContext(env, sandbox, options); if (try_catch.HasCaught()) { try_catch.ReThrow(); return; } if (context->context().IsEmpty()) return; sandbox->SetPrivate( env->context(), env->contextify_context_private_symbol(), External::New(env->isolate(), context)); } void ContextifyContext::IsContext(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); if (!args[0]->IsObject()) { env->ThrowTypeError("sandbox must be an object"); return; } 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 void ContextifyContext::PropertyGetterCallback( Local property, const PropertyCallbackInfo& args) { ContextifyContext* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As()); // 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; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As()); // 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); } ctx->sandbox()->Set(property, value); } // static void ContextifyContext::PropertyDescriptorCallback( Local property, const PropertyCallbackInfo& args) { ContextifyContext* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As()); // Still initializing if (ctx->context_.IsEmpty()) return; Local context = ctx->context(); Local sandbox = ctx->sandbox(); if (sandbox->HasOwnProperty(context, property).FromMaybe(false)) { args.GetReturnValue().Set( sandbox->GetOwnPropertyDescriptor(context, property) .ToLocalChecked()); } } // static void ContextifyContext::PropertyDefinerCallback( Local property, const PropertyDescriptor& desc, const PropertyCallbackInfo& args) { ContextifyContext* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As()); // Still initializing if (ctx->context_.IsEmpty()) return; Local context = ctx->context(); v8::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. sandbox->DefineProperty(context, property, *desc_for_sandbox) .FromJust(); }; if (desc.has_get() || desc.has_set()) { PropertyDescriptor desc_for_sandbox( desc.has_get() ? desc.get() : v8::Undefined(isolate).As(), desc.has_set() ? desc.set() : v8::Undefined(isolate).As()); define_prop_on_sandbox(&desc_for_sandbox); } else { Local value = desc.has_value() ? desc.value() : v8::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; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As()); // 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; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As()); // Still initializing if (ctx->context_.IsEmpty()) return; args.GetReturnValue().Set(ctx->sandbox()->GetPropertyNames()); } // static void ContextifyContext::IndexedPropertyGetterCallback( uint32_t index, const PropertyCallbackInfo& args) { ContextifyContext* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As()); // 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; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As()); // 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; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As()); // 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; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As()); // 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; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As()); // 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); } Maybe GetBreakOnSigintArg(Environment* env, Local options) { if (options->IsUndefined() || options->IsString()) { return Just(false); } if (!options->IsObject()) { env->ThrowTypeError("options must be an object"); return Nothing(); } Local key = FIXED_ONE_BYTE_STRING(env->isolate(), "breakOnSigint"); MaybeLocal maybe_value = options.As()->Get(env->context(), key); if (maybe_value.IsEmpty()) return Nothing(); Local value = maybe_value.ToLocalChecked(); return Just(value->IsTrue()); } Maybe GetTimeoutArg(Environment* env, Local options) { if (options->IsUndefined() || options->IsString()) { return Just(-1); } if (!options->IsObject()) { env->ThrowTypeError("options must be an object"); return Nothing(); } MaybeLocal maybe_value = options.As()->Get(env->context(), env->timeout_string()); if (maybe_value.IsEmpty()) return Nothing(); Local value = maybe_value.ToLocalChecked(); if (value->IsUndefined()) { return Just(-1); } Maybe timeout = value->IntegerValue(env->context()); if (timeout.IsJust() && timeout.ToChecked() <= 0) { env->ThrowRangeError("timeout must be a positive number"); return Nothing(); } return timeout; } MaybeLocal GetLineOffsetArg(Environment* env, Local options) { Local defaultLineOffset = Integer::New(env->isolate(), 0); if (!options->IsObject()) { return defaultLineOffset; } Local key = FIXED_ONE_BYTE_STRING(env->isolate(), "lineOffset"); MaybeLocal maybe_value = options.As()->Get(env->context(), key); if (maybe_value.IsEmpty()) return MaybeLocal(); Local value = maybe_value.ToLocalChecked(); if (value->IsUndefined()) return defaultLineOffset; return value->ToInteger(env->context()); } MaybeLocal GetColumnOffsetArg(Environment* env, Local options) { Local defaultColumnOffset = Integer::New(env->isolate(), 0); if (!options->IsObject()) { return defaultColumnOffset; } Local key = FIXED_ONE_BYTE_STRING(env->isolate(), "columnOffset"); MaybeLocal maybe_value = options.As()->Get(env->context(), key); if (maybe_value.IsEmpty()) return MaybeLocal(); Local value = maybe_value.ToLocalChecked(); if (value->IsUndefined()) return defaultColumnOffset; return value->ToInteger(env->context()); } MaybeLocal GetContextArg(Environment* env, Local options) { if (!options->IsObject()) return MaybeLocal(); MaybeLocal maybe_value = options.As()->Get(env->context(), env->vm_parsing_context_symbol()); Local value; if (!maybe_value.ToLocal(&value)) return MaybeLocal(); if (!value->IsObject()) { if (!value->IsNullOrUndefined()) { env->ThrowTypeError( "contextifiedSandbox argument must be an object."); } return MaybeLocal(); } ContextifyContext* sandbox = ContextifyContext::ContextFromContextifiedSandbox( env, value.As()); if (!sandbox) { env->ThrowTypeError( "sandbox argument must have been converted to a context."); return MaybeLocal(); } Local context = sandbox->context(); if (context.IsEmpty()) return MaybeLocal(); return context; } namespace { Maybe GetDisplayErrorsArg(Environment* env, Local options) { if (options->IsUndefined() || options->IsString()) { return Just(true); } if (!options->IsObject()) { env->ThrowTypeError("options must be an object"); return Nothing(); } Local key = FIXED_ONE_BYTE_STRING(env->isolate(), "displayErrors"); MaybeLocal maybe_value = options.As()->Get(env->context(), key); if (maybe_value.IsEmpty()) return Nothing(); Local value = maybe_value.ToLocalChecked(); if (value->IsUndefined()) return Just(true); return value->BooleanValue(env->context()); } MaybeLocal GetFilenameArg(Environment* env, Local options) { Local defaultFilename = FIXED_ONE_BYTE_STRING(env->isolate(), "evalmachine."); if (options->IsUndefined()) { return defaultFilename; } if (options->IsString()) { return options.As(); } if (!options->IsObject()) { env->ThrowTypeError("options must be an object"); return Local(); } Local key = FIXED_ONE_BYTE_STRING(env->isolate(), "filename"); MaybeLocal maybe_value = options.As()->Get(env->context(), key); if (maybe_value.IsEmpty()) return MaybeLocal(); Local value = maybe_value.ToLocalChecked(); if (value->IsUndefined()) return defaultFilename; return value->ToString(env->context()); } MaybeLocal GetCachedData(Environment* env, Local options) { if (!options->IsObject()) { return MaybeLocal(); } MaybeLocal maybe_value = options.As()->Get(env->context(), env->cached_data_string()); if (maybe_value.IsEmpty()) return MaybeLocal(); Local value = maybe_value.ToLocalChecked(); if (value->IsUndefined()) { return MaybeLocal(); } if (!value->IsUint8Array()) { env->ThrowTypeError("options.cachedData must be a Buffer instance"); return MaybeLocal(); } return value.As(); } Maybe GetProduceCachedData(Environment* env, Local options) { if (!options->IsObject()) { return Just(false); } MaybeLocal maybe_value = options.As()->Get(env->context(), env->produce_cached_data_string()); if (maybe_value.IsEmpty()) return Nothing(); Local value = maybe_value.ToLocalChecked(); return Just(value->IsTrue()); } } // anonymous namespace class ContextifyScript : public BaseObject { private: Persistent script_; public: static void 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(1); script_tmpl->SetClassName(class_name); env->SetProtoMethod(script_tmpl, "runInContext", RunInContext); env->SetProtoMethod(script_tmpl, "runInThisContext", RunInThisContext); target->Set(class_name, script_tmpl->GetFunction()); env->set_script_context_constructor_template(script_tmpl); Local parsing_context_symbol = Symbol::New(env->isolate(), FIXED_ONE_BYTE_STRING(env->isolate(), "script parsing context")); env->set_vm_parsing_context_symbol(parsing_context_symbol); target->Set(env->context(), FIXED_ONE_BYTE_STRING(env->isolate(), "kParsingContext"), parsing_context_symbol) .FromJust(); } // args: code, [options] static void New(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); if (!args.IsConstructCall()) { return env->ThrowError("Must call vm.Script as a constructor."); } ContextifyScript* contextify_script = new ContextifyScript(env, args.This()); TryCatch try_catch(env->isolate()); Environment::ShouldNotAbortOnUncaughtScope no_abort_scope(env); Local code = args[0]->ToString(env->context()).FromMaybe(Local()); Local options = args[1]; MaybeLocal filename = GetFilenameArg(env, options); MaybeLocal lineOffset = GetLineOffsetArg(env, options); MaybeLocal columnOffset = GetColumnOffsetArg(env, options); MaybeLocal cached_data_buf = GetCachedData(env, options); Maybe maybe_produce_cached_data = GetProduceCachedData(env, options); MaybeLocal maybe_context = GetContextArg(env, options); if (try_catch.HasCaught()) { no_abort_scope.Close(); try_catch.ReThrow(); return; } bool produce_cached_data = maybe_produce_cached_data.ToChecked(); ScriptCompiler::CachedData* cached_data = nullptr; Local ui8; if (cached_data_buf.ToLocal(&ui8)) { ArrayBuffer::Contents contents = ui8->Buffer()->GetContents(); cached_data = new ScriptCompiler::CachedData( static_cast(contents.Data()) + ui8->ByteOffset(), ui8->ByteLength()); } ScriptOrigin origin(filename.ToLocalChecked(), lineOffset.ToLocalChecked(), columnOffset.ToLocalChecked()); ScriptCompiler::Source source(code, origin, cached_data); ScriptCompiler::CompileOptions compile_options = ScriptCompiler::kNoCompileOptions; if (source.GetCachedData() != nullptr) compile_options = ScriptCompiler::kConsumeCodeCache; else if (produce_cached_data) compile_options = ScriptCompiler::kProduceCodeCache; Context::Scope scope(maybe_context.FromMaybe(env->context())); MaybeLocal v8_script = ScriptCompiler::CompileUnboundScript( env->isolate(), &source, compile_options); if (v8_script.IsEmpty()) { DecorateErrorStack(env, try_catch); no_abort_scope.Close(); try_catch.ReThrow(); return; } contextify_script->script_.Reset(env->isolate(), v8_script.ToLocalChecked()); if (compile_options == ScriptCompiler::kConsumeCodeCache) { args.This()->Set( env->cached_data_rejected_string(), Boolean::New(env->isolate(), source.GetCachedData()->rejected)); } else if (compile_options == ScriptCompiler::kProduceCodeCache) { const ScriptCompiler::CachedData* cached_data = source.GetCachedData(); 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->cached_data_string(), buf.ToLocalChecked()); } args.This()->Set( env->cached_data_produced_string(), Boolean::New(env->isolate(), cached_data_produced)); } } static bool InstanceOf(Environment* env, const Local& value) { return !value.IsEmpty() && env->script_context_constructor_template()->HasInstance(value); } // args: [options] static void RunInThisContext(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); // Assemble arguments TryCatch try_catch(args.GetIsolate()); Maybe maybe_timeout = GetTimeoutArg(env, args[0]); Maybe maybe_display_errors = GetDisplayErrorsArg(env, args[0]); Maybe maybe_break_on_sigint = GetBreakOnSigintArg(env, args[0]); if (try_catch.HasCaught()) { try_catch.ReThrow(); return; } int64_t timeout = maybe_timeout.ToChecked(); bool display_errors = maybe_display_errors.ToChecked(); bool break_on_sigint = maybe_break_on_sigint.ToChecked(); // Do the eval within this context EvalMachine(env, timeout, display_errors, break_on_sigint, args, &try_catch); } // args: sandbox, [options] static void RunInContext(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); int64_t timeout; bool display_errors; bool break_on_sigint; // Assemble arguments if (!args[0]->IsObject()) { return env->ThrowTypeError( "contextifiedSandbox argument must be an object."); } Local sandbox = args[0].As(); { TryCatch try_catch(env->isolate()); Maybe maybe_timeout = GetTimeoutArg(env, args[1]); Maybe maybe_display_errors = GetDisplayErrorsArg(env, args[1]); Maybe maybe_break_on_sigint = GetBreakOnSigintArg(env, args[1]); if (try_catch.HasCaught()) { try_catch.ReThrow(); return; } timeout = maybe_timeout.ToChecked(); display_errors = maybe_display_errors.ToChecked(); break_on_sigint = maybe_break_on_sigint.ToChecked(); } // Get the context from the sandbox ContextifyContext* contextify_context = ContextifyContext::ContextFromContextifiedSandbox(env, sandbox); if (contextify_context == nullptr) { return env->ThrowTypeError( "sandbox argument must have been converted to a context."); } if (contextify_context->context().IsEmpty()) return; { TryCatch try_catch(env->isolate()); // Do the eval within the context Context::Scope context_scope(contextify_context->context()); EvalMachine(contextify_context->env(), timeout, display_errors, break_on_sigint, args, &try_catch); if (try_catch.HasCaught()) { try_catch.ReThrow(); return; } } } static void DecorateErrorStack(Environment* env, const TryCatch& try_catch) { Local exception = try_catch.Exception(); if (!exception->IsObject()) return; Local err_obj = exception.As(); if (IsExceptionDecorated(env, err_obj)) return; AppendExceptionLine(env, exception, try_catch.Message(), CONTEXTIFY_ERROR); Local stack = err_obj->Get(env->stack_string()); MaybeLocal maybe_value = err_obj->GetPrivate( env->context(), env->arrow_message_private_symbol()); Local arrow; if (!(maybe_value.ToLocal(&arrow) && arrow->IsString())) { return; } if (stack.IsEmpty() || !stack->IsString()) { return; } Local decorated_stack = String::Concat( String::Concat(arrow.As(), FIXED_ONE_BYTE_STRING(env->isolate(), "\n")), stack.As()); err_obj->Set(env->stack_string(), decorated_stack); err_obj->SetPrivate( env->context(), env->decorated_private_symbol(), True(env->isolate())); } static bool EvalMachine(Environment* env, const int64_t timeout, const bool display_errors, const bool break_on_sigint, const FunctionCallbackInfo& args, TryCatch* try_catch) { if (!ContextifyScript::InstanceOf(env, args.Holder())) { env->ThrowTypeError( "Script methods can only be called on script instances."); return false; } ContextifyScript* wrapped_script; ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder(), false); Local unbound_script = PersistentToLocal(env->isolate(), wrapped_script->script_); Local