blob: 1066633daf65d430b5f46e1b226ee3cf70979c33 [file] [log] [blame] [edit]
#ifndef SRC_NODE_CONTEXTIFY_H_
#define SRC_NODE_CONTEXTIFY_H_
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#include "base_object-inl.h"
#include "cppgc_helpers-inl.h"
#include "node_context_data.h"
#include "node_errors.h"
namespace node {
class ExternalReferenceRegistry;
namespace contextify {
struct ContextOptions {
v8::Local<v8::String> name;
v8::Local<v8::String> origin;
v8::Local<v8::Boolean> allow_code_gen_strings;
v8::Local<v8::Boolean> allow_code_gen_wasm;
std::unique_ptr<v8::MicrotaskQueue> own_microtask_queue;
v8::Local<v8::Symbol> host_defined_options_id;
bool vanilla = false;
};
/**
* The memory management of a vm context is as follows:
*
* user code
* │
* As global proxy or ▼
* ┌──────────────┐ kSandboxObject embedder data ┌────────────────┐
* ┌─► │ V8 Context │────────────────────────────────►│ Wrapper holder │
* │ └──────────────┘ └───────┬────────┘
* │ ▲ Object constructor/creation context │
* │ │ │
* │ ┌──────┴────────────┐ contextify_context_private_symbol │
* │ │ ContextifyContext │◄────────────────────────────────────┘
* │ │ JS Wrapper │◄──────────► ┌─────────────────────────┐
* │ └───────────────────┘ cppgc │ node::ContextifyContext │
* │ │ C++ Object │
* └──────────────────────────────────► └─────────────────────────┘
* v8::TracedReference / ContextEmbedderIndex::kContextifyContext
*
* There are two possibilities for the "wrapper holder":
*
* 1. When vm.constants.DONT_CONTEXTIFY is used, the wrapper holder is the V8
* context's global proxy object
* 2. Otherwise it's the arbitrary "sandbox object" that users pass into
* vm.createContext() or a new empty object created internally if they pass
* undefined.
*
* In 2, the global object of the new V8 context is created using
* global_object_template with interceptors that perform any requested
* operations on the global object in the context first on the sandbox object
* living outside of the new context, then fall back to the global proxy of the
* new context.
*
* It's critical for the user-accessible wrapper holder to keep the
* ContextifyContext wrapper alive via contextify_context_private_symbol
* so that the V8 context is always available to the user while they still
* hold the vm "context" object alive.
*
* It's also critical for the V8 context to keep the wrapper holder
* (specifically, the "sandbox object" if users pass one) as well as the
* node::ContextifyContext C++ object alive, so that when the code
* runs inside the object and accesses the global object, the interceptors
* can still access the "sandbox object" and perform operations
* on them, even if users already relinquish access to the outer
* "sandbox object".
*
* The v8::TracedReference and the ContextEmbedderIndex::kContextifyContext
* slot in the context only act as shortcuts between
* the node::ContextifyContext C++ object and the V8 context.
*/
class ContextifyContext final : CPPGC_MIXIN(ContextifyContext) {
public:
SET_CPPGC_NAME(ContextifyContext)
void Trace(cppgc::Visitor* visitor) const final;
SET_NO_MEMORY_INFO()
ContextifyContext(Environment* env,
v8::Local<v8::Object> wrapper,
v8::Local<v8::Context> v8_context,
ContextOptions* options);
// The destructors don't need to do anything because when the wrapper is
// going away, the context is already going away or otherwise it would've
// been holding the wrapper alive, so there's no need to reset the pointers
// in the context. Also, any global handles to the context would've been
// empty at this point, and the per-Environment context tracking code is
// capable of dealing with empty handles from contexts purged elsewhere.
~ContextifyContext() = default;
static v8::MaybeLocal<v8::Context> CreateV8Context(
v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> object_template,
const SnapshotData* snapshot_data,
v8::MicrotaskQueue* queue);
static void CreatePerIsolateProperties(IsolateData* isolate_data,
v8::Local<v8::ObjectTemplate> target);
static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
static ContextifyContext* ContextFromContextifiedSandbox(
Environment* env, const v8::Local<v8::Object>& wrapper_holder);
inline v8::Local<v8::Context> context() const {
return context_.Get(env()->isolate());
}
inline v8::Local<v8::Object> global_proxy() const {
return context()->Global();
}
inline v8::Local<v8::Object> sandbox() const {
// Only vanilla contexts have undefined sandboxes. sandbox() is only used by
// interceptors who are not supposed to be called on vanilla contexts.
v8::Local<v8::Value> result =
context()->GetEmbedderData(ContextEmbedderIndex::kSandboxObject);
CHECK(!result->IsUndefined());
return result.As<v8::Object>();
}
inline v8::MicrotaskQueue* microtask_queue() const {
return microtask_queue_.get();
}
template <typename T>
static ContextifyContext* Get(const v8::PropertyCallbackInfo<T>& args);
static ContextifyContext* Get(v8::Local<v8::Object> object);
static void InitializeGlobalTemplates(IsolateData* isolate_data);
private:
static ContextifyContext* New(Environment* env,
v8::Local<v8::Object> sandbox_obj,
ContextOptions* options);
// Initialize a context created from CreateV8Context()
static ContextifyContext* New(v8::Local<v8::Context> ctx,
Environment* env,
v8::Local<v8::Object> sandbox_obj,
ContextOptions* options);
static bool IsStillInitializing(const ContextifyContext* ctx);
static void MakeContext(const v8::FunctionCallbackInfo<v8::Value>& args);
static void IsContext(const v8::FunctionCallbackInfo<v8::Value>& args);
static v8::Intercepted PropertyQueryCallback(
v8::Local<v8::Name> property,
const v8::PropertyCallbackInfo<v8::Integer>& args);
static v8::Intercepted PropertyGetterCallback(
v8::Local<v8::Name> property,
const v8::PropertyCallbackInfo<v8::Value>& args);
static v8::Intercepted PropertySetterCallback(
v8::Local<v8::Name> property,
v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<void>& args);
static v8::Intercepted PropertyDescriptorCallback(
v8::Local<v8::Name> property,
const v8::PropertyCallbackInfo<v8::Value>& args);
static v8::Intercepted PropertyDefinerCallback(
v8::Local<v8::Name> property,
const v8::PropertyDescriptor& desc,
const v8::PropertyCallbackInfo<void>& args);
static v8::Intercepted PropertyDeleterCallback(
v8::Local<v8::Name> property,
const v8::PropertyCallbackInfo<v8::Boolean>& args);
static void PropertyEnumeratorCallback(
const v8::PropertyCallbackInfo<v8::Array>& args);
static v8::Intercepted IndexedPropertyQueryCallback(
uint32_t index, const v8::PropertyCallbackInfo<v8::Integer>& args);
static v8::Intercepted IndexedPropertyGetterCallback(
uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& args);
static v8::Intercepted IndexedPropertySetterCallback(
uint32_t index,
v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<void>& args);
static v8::Intercepted IndexedPropertyDescriptorCallback(
uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& args);
static v8::Intercepted IndexedPropertyDefinerCallback(
uint32_t index,
const v8::PropertyDescriptor& desc,
const v8::PropertyCallbackInfo<void>& args);
static v8::Intercepted IndexedPropertyDeleterCallback(
uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>& args);
static void IndexedPropertyEnumeratorCallback(
const v8::PropertyCallbackInfo<v8::Array>& args);
v8::TracedReference<v8::Context> context_;
std::unique_ptr<v8::MicrotaskQueue> microtask_queue_;
};
class ContextifyScript final : CPPGC_MIXIN(ContextifyScript) {
public:
SET_CPPGC_NAME(ContextifyScript)
void Trace(cppgc::Visitor* visitor) const final;
SET_NO_MEMORY_INFO()
ContextifyScript(Environment* env, v8::Local<v8::Object> object);
~ContextifyScript() override;
v8::Local<v8::UnboundScript> unbound_script() const;
void set_unbound_script(v8::Local<v8::UnboundScript>);
static void CreatePerIsolateProperties(IsolateData* isolate_data,
v8::Local<v8::ObjectTemplate> target);
static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
static ContextifyScript* New(Environment* env, v8::Local<v8::Object> object);
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
static bool InstanceOf(Environment* env, const v8::Local<v8::Value>& args);
static void CreateCachedData(const v8::FunctionCallbackInfo<v8::Value>& args);
static void RunInContext(const v8::FunctionCallbackInfo<v8::Value>& args);
static bool EvalMachine(v8::Local<v8::Context> context,
Environment* env,
const int64_t timeout,
const bool display_errors,
const bool break_on_sigint,
const bool break_on_first_line,
v8::MicrotaskQueue* microtask_queue,
const v8::FunctionCallbackInfo<v8::Value>& args);
private:
v8::TracedReference<v8::UnboundScript> script_;
};
class ContextifyFunction final {
public:
static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
static void CreatePerIsolateProperties(IsolateData* isolate_data,
v8::Local<v8::ObjectTemplate> target);
static void CompileFunction(const v8::FunctionCallbackInfo<v8::Value>& args);
static v8::MaybeLocal<v8::Object> CompileFunctionAndCacheResult(
Environment* env,
v8::Local<v8::Context> parsing_context,
v8::ScriptCompiler::Source* source,
v8::LocalVector<v8::String> params,
v8::LocalVector<v8::Object> context_extensions,
v8::ScriptCompiler::CompileOptions options,
bool produce_cached_data,
v8::Local<v8::Symbol> id_symbol,
const errors::TryCatchScope& try_catch);
private:
ContextifyFunction() = delete;
~ContextifyFunction() = delete;
};
v8::Maybe<void> StoreCodeCacheResult(
Environment* env,
v8::Local<v8::Object> target,
v8::ScriptCompiler::CompileOptions compile_options,
const v8::ScriptCompiler::Source& source,
bool produce_cached_data,
std::unique_ptr<v8::ScriptCompiler::CachedData> new_cached_data);
} // namespace contextify
} // namespace node
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#endif // SRC_NODE_CONTEXTIFY_H_