// Copyright 2021 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef INCLUDE_V8_CONTEXT_H_ #define INCLUDE_V8_CONTEXT_H_ #include #include #include "v8-data.h" // NOLINT(build/include_directory) #include "v8-local-handle.h" // NOLINT(build/include_directory) #include "v8-maybe.h" // NOLINT(build/include_directory) #include "v8-snapshot.h" // NOLINT(build/include_directory) #include "v8config.h" // NOLINT(build/include_directory) namespace v8 { class Function; class MicrotaskQueue; class Object; class ObjectTemplate; class Value; class String; /** * A container for extension names. */ class V8_EXPORT ExtensionConfiguration { public: ExtensionConfiguration() : name_count_(0), names_(nullptr) {} ExtensionConfiguration(int name_count, const char* names[]) : name_count_(name_count), names_(names) {} const char** begin() const { return &names_[0]; } const char** end() const { return &names_[name_count_]; } private: const int name_count_; const char** names_; }; /** * A sandboxed execution context with its own set of built-in objects * and functions. */ class V8_EXPORT Context : public Data { public: /** * Returns the global proxy object. * * Global proxy object is a thin wrapper whose prototype points to actual * context's global object with the properties like Object, etc. This is done * that way for security reasons (for more details see * https://wiki.mozilla.org/Gecko:SplitWindow). * * Please note that changes to global proxy object prototype most probably * would break VM---v8 expects only global object as a prototype of global * proxy object. */ Local Global(); /** * Detaches the global object from its context before * the global object can be reused to create a new context. */ void DetachGlobal(); /** * Creates a new context and returns a handle to the newly allocated * context. * * \param isolate The isolate in which to create the context. * * \param extensions An optional extension configuration containing * the extensions to be installed in the newly created context. * * \param global_template An optional object template from which the * global object for the newly created context will be created. * * \param global_object An optional global object to be reused for * the newly created context. This global object must have been * created by a previous call to Context::New with the same global * template. The state of the global object will be completely reset * and only object identify will remain. * * \param internal_fields_deserializer An optional callback used * to deserialize fields set by * v8::Object::SetAlignedPointerInInternalField() in wrapper objects * from the default context snapshot. It should match the * SerializeInternalFieldsCallback() used by * v8::SnapshotCreator::SetDefaultContext() when the default context * snapshot is created. It does not need to be configured if the default * context snapshot contains no wrapper objects with pointer internal * fields, or if no custom startup snapshot is configured * in the v8::CreateParams used to create the isolate. * * \param microtask_queue An optional microtask queue used to manage * the microtasks created in this context. If not set the per-isolate * default microtask queue would be used. * * \param context_data_deserializer An optional callback used * to deserialize embedder data set by * v8::Context::SetAlignedPointerInEmbedderData() in the default * context from the default context snapshot. It does not need to be * configured if the default context snapshot contains no pointer embedder * data, or if no custom startup snapshot is configured in the * v8::CreateParams used to create the isolate. * * \param api_wrapper_deserializer An optional callback used to deserialize * API wrapper objects that was initially set with v8::Object::Wrap() and then * serialized using SerializeAPIWrapperCallback. */ static Local New( Isolate* isolate, ExtensionConfiguration* extensions = nullptr, MaybeLocal global_template = MaybeLocal(), MaybeLocal global_object = MaybeLocal(), DeserializeInternalFieldsCallback internal_fields_deserializer = DeserializeInternalFieldsCallback(), MicrotaskQueue* microtask_queue = nullptr, DeserializeContextDataCallback context_data_deserializer = DeserializeContextDataCallback(), DeserializeAPIWrapperCallback api_wrapper_deserializer = DeserializeAPIWrapperCallback()); /** * Create a new context from a (non-default) context snapshot. There * is no way to provide a global object template since we do not create * a new global object from template, but we can reuse a global object. * * \param isolate See v8::Context::New(). * * \param context_snapshot_index The index of the context snapshot to * deserialize from. Use v8::Context::New() for the default snapshot. * * \param internal_fields_deserializer An optional callback used * to deserialize fields set by * v8::Object::SetAlignedPointerInInternalField() in wrapper objects * from the default context snapshot. It does not need to be * configured if there are no wrapper objects with no internal * pointer fields in the default context snapshot or if no startup * snapshot is configured when the isolate is created. * * \param extensions See v8::Context::New(). * * \param global_object See v8::Context::New(). * * \param internal_fields_deserializer Similar to * internal_fields_deserializer in v8::Context::New() but applies to * the context specified by the context_snapshot_index. * * \param microtask_queue See v8::Context::New(). * * \param context_data_deserializer Similar to * context_data_deserializer in v8::Context::New() but applies to * the context specified by the context_snapshot_index. * *\param api_wrapper_deserializer Similar to api_wrapper_deserializer in * v8::Context::New() but applies to the context specified by the * context_snapshot_index. */ static MaybeLocal FromSnapshot( Isolate* isolate, size_t context_snapshot_index, DeserializeInternalFieldsCallback internal_fields_deserializer = DeserializeInternalFieldsCallback(), ExtensionConfiguration* extensions = nullptr, MaybeLocal global_object = MaybeLocal(), MicrotaskQueue* microtask_queue = nullptr, DeserializeContextDataCallback context_data_deserializer = DeserializeContextDataCallback(), DeserializeAPIWrapperCallback api_wrapper_deserializer = DeserializeAPIWrapperCallback()); /** * Returns an global object that isn't backed by an actual context. * * The global template needs to have access checks with handlers installed. * If an existing global object is passed in, the global object is detached * from its context. * * Note that this is different from a detached context where all accesses to * the global proxy will fail. Instead, the access check handlers are invoked. * * It is also not possible to detach an object returned by this method. * Instead, the access check handlers need to return nothing to achieve the * same effect. * * It is possible, however, to create a new context from the global object * returned by this method. */ static MaybeLocal NewRemoteContext( Isolate* isolate, Local global_template, MaybeLocal global_object = MaybeLocal()); /** * Sets the security token for the context. To access an object in * another context, the security tokens must match. */ void SetSecurityToken(Local token); /** Restores the security token to the default value. */ void UseDefaultSecurityToken(); /** Returns the security token of this context.*/ Local GetSecurityToken(); /** * Enter this context. After entering a context, all code compiled * and run is compiled and run in this context. If another context * is already entered, this old context is saved so it can be * restored when the new context is exited. */ void Enter(); /** * Exit this context. Exiting the current context restores the * context that was in place when entering the current context. */ void Exit(); /** * Delegate to help with Deep freezing embedder-specific objects (such as * JSApiObjects) that can not be frozen natively. */ class DeepFreezeDelegate { public: /** * Performs embedder-specific operations to freeze the provided embedder * object. The provided object *will* be frozen by DeepFreeze after this * function returns, so only embedder-specific objects need to be frozen. * This function *may not* create new JS objects or perform JS allocations. * Any v8 objects reachable from the provided embedder object that should * also be considered for freezing should be added to the children_out * parameter. Returns true if the operation completed successfully. */ virtual bool FreezeEmbedderObjectAndGetChildren( Local obj, LocalVector& children_out) = 0; }; /** * Attempts to recursively freeze all objects reachable from this context. * Some objects (generators, iterators, non-const closures) can not be frozen * and will cause this method to throw an error. An optional delegate can be * provided to help freeze embedder-specific objects. * * Freezing occurs in two steps: * 1. "Marking" where we iterate through all objects reachable by this * context, accumulating a list of objects that need to be frozen and * looking for objects that can't be frozen. This step is separated because * it is more efficient when we can assume there is no garbage collection. * 2. "Freezing" where we go through the list of objects and freezing them. * This effectively requires copying them so it may trigger garbage * collection. */ Maybe DeepFreeze(DeepFreezeDelegate* delegate = nullptr); /** Returns the isolate associated with a current context. */ Isolate* GetIsolate(); /** Returns the microtask queue associated with a current context. */ MicrotaskQueue* GetMicrotaskQueue(); /** Sets the microtask queue associated with the current context. */ void SetMicrotaskQueue(MicrotaskQueue* queue); /** * The field at kDebugIdIndex used to be reserved for the inspector. * It now serves no purpose. */ enum EmbedderDataFields { kDebugIdIndex = 0 }; /** * Return the number of fields allocated for embedder data. */ uint32_t GetNumberOfEmbedderDataFields(); /** * Gets the embedder data with the given index, which must have been set by a * previous call to SetEmbedderData with the same index. */ V8_INLINE Local GetEmbedderData(int index); /** * Gets the binding object used by V8 extras. Extra natives get a reference * to this object and can use it to "export" functionality by adding * properties. Extra natives can also "import" functionality by accessing * properties added by the embedder using the V8 API. */ Local GetExtrasBindingObject(); /** * Sets the embedder data with the given index, growing the data as * needed. Note that index 0 currently has a special meaning for Chrome's * debugger. */ void SetEmbedderData(int index, Local value); /** * Gets a 2-byte-aligned native pointer from the embedder data with the given * index, which must have been set by a previous call to * SetAlignedPointerInEmbedderData with the same index. Note that index 0 * currently has a special meaning for Chrome's debugger. */ V8_INLINE void* GetAlignedPointerFromEmbedderData(Isolate* isolate, int index); V8_INLINE void* GetAlignedPointerFromEmbedderData(int index); /** * Sets a 2-byte-aligned native pointer in the embedder data with the given * index, growing the data as needed. Note that index 0 currently has a * special meaning for Chrome's debugger. */ void SetAlignedPointerInEmbedderData(int index, void* value); /** * Control whether code generation from strings is allowed. Calling * this method with false will disable 'eval' and the 'Function' * constructor for code running in this context. If 'eval' or the * 'Function' constructor are used an exception will be thrown. * * If code generation from strings is not allowed the * V8::ModifyCodeGenerationFromStringsCallback callback will be invoked if * set before blocking the call to 'eval' or the 'Function' * constructor. If that callback returns true, the call will be * allowed, otherwise an exception will be thrown. If no callback is * set an exception will be thrown. */ void AllowCodeGenerationFromStrings(bool allow); /** * Returns true if code generation from strings is allowed for the context. * For more details see AllowCodeGenerationFromStrings(bool) documentation. */ bool IsCodeGenerationFromStringsAllowed() const; /** * Sets the error description for the exception that is thrown when * code generation from strings is not allowed and 'eval' or the 'Function' * constructor are called. */ void SetErrorMessageForCodeGenerationFromStrings(Local message); /** * Sets the error description for the exception that is thrown when * wasm code generation is not allowed. */ void SetErrorMessageForWasmCodeGeneration(Local message); /** * Return data that was previously attached to the context snapshot via * SnapshotCreator, and removes the reference to it. * Repeated call with the same index returns an empty MaybeLocal. */ template V8_INLINE MaybeLocal GetDataFromSnapshotOnce(size_t index); /** * If callback is set, abort any attempt to execute JavaScript in this * context, call the specified callback, and throw an exception. * To unset abort, pass nullptr as callback. */ using AbortScriptExecutionCallback = void (*)(Isolate* isolate, Local context); void SetAbortScriptExecution(AbortScriptExecutionCallback callback); /** * Set or clear hooks to be invoked for promise lifecycle operations. * To clear a hook, set it to an empty v8::Function. Each function will * receive the observed promise as the first argument. If a chaining * operation is used on a promise, the init will additionally receive * the parent promise as the second argument. */ void SetPromiseHooks(Local init_hook, Local before_hook, Local after_hook, Local resolve_hook); bool HasTemplateLiteralObject(Local object); /** * Stack-allocated class which sets the execution context for all * operations executed within a local scope. */ class V8_NODISCARD Scope { public: explicit V8_INLINE Scope(Local context) : context_(context) { context_->Enter(); } V8_INLINE ~Scope() { context_->Exit(); } private: Local context_; }; /** * Stack-allocated class to support the backup incumbent settings object * stack. * https://html.spec.whatwg.org/multipage/webappapis.html#backup-incumbent-settings-object-stack */ class V8_EXPORT V8_NODISCARD BackupIncumbentScope final { public: /** * |backup_incumbent_context| is pushed onto the backup incumbent settings * object stack. */ explicit BackupIncumbentScope(Local backup_incumbent_context); ~BackupIncumbentScope(); private: friend class internal::Isolate; uintptr_t JSStackComparableAddressPrivate() const { return js_stack_comparable_address_; } Local backup_incumbent_context_; uintptr_t js_stack_comparable_address_ = 0; const BackupIncumbentScope* prev_ = nullptr; }; V8_INLINE static Context* Cast(Data* data); private: friend class Value; friend class Script; friend class Object; friend class Function; static void CheckCast(Data* obj); internal::Address* GetDataFromSnapshotOnce(size_t index); Local SlowGetEmbedderData(int index); void* SlowGetAlignedPointerFromEmbedderData(int index); }; // --- Implementation --- Local Context::GetEmbedderData(int index) { #ifndef V8_ENABLE_CHECKS using A = internal::Address; using I = internal::Internals; A ctx = internal::ValueHelper::ValueAsAddress(this); A embedder_data = I::ReadTaggedPointerField(ctx, I::kNativeContextEmbedderDataOffset); int value_offset = I::kEmbedderDataArrayHeaderSize + (I::kEmbedderDataSlotSize * index); A value = I::ReadRawField(embedder_data, value_offset); #ifdef V8_COMPRESS_POINTERS // We read the full pointer value and then decompress it in order to avoid // dealing with potential endiannes issues. value = I::DecompressTaggedField(embedder_data, static_cast(value)); #endif auto isolate = reinterpret_cast( internal::IsolateFromNeverReadOnlySpaceObject(ctx)); return Local::New(isolate, value); #else return SlowGetEmbedderData(index); #endif } void* Context::GetAlignedPointerFromEmbedderData(Isolate* isolate, int index) { #if !defined(V8_ENABLE_CHECKS) using A = internal::Address; using I = internal::Internals; A ctx = internal::ValueHelper::ValueAsAddress(this); A embedder_data = I::ReadTaggedPointerField(ctx, I::kNativeContextEmbedderDataOffset); int value_offset = I::kEmbedderDataArrayHeaderSize + (I::kEmbedderDataSlotSize * index) + I::kEmbedderDataSlotExternalPointerOffset; return reinterpret_cast( I::ReadExternalPointerField( isolate, embedder_data, value_offset)); #else return SlowGetAlignedPointerFromEmbedderData(index); #endif } void* Context::GetAlignedPointerFromEmbedderData(int index) { #if !defined(V8_ENABLE_CHECKS) using A = internal::Address; using I = internal::Internals; A ctx = internal::ValueHelper::ValueAsAddress(this); A embedder_data = I::ReadTaggedPointerField(ctx, I::kNativeContextEmbedderDataOffset); int value_offset = I::kEmbedderDataArrayHeaderSize + (I::kEmbedderDataSlotSize * index) + I::kEmbedderDataSlotExternalPointerOffset; Isolate* isolate = I::GetIsolateForSandbox(ctx); return reinterpret_cast( I::ReadExternalPointerField( isolate, embedder_data, value_offset)); #else return SlowGetAlignedPointerFromEmbedderData(index); #endif } template MaybeLocal Context::GetDataFromSnapshotOnce(size_t index) { if (auto slot = GetDataFromSnapshotOnce(index); slot) { internal::PerformCastCheck( internal::ValueHelper::SlotAsValue(slot)); return Local::FromSlot(slot); } return {}; } Context* Context::Cast(v8::Data* data) { #ifdef V8_ENABLE_CHECKS CheckCast(data); #endif return static_cast(data); } } // namespace v8 #endif // INCLUDE_V8_CONTEXT_H_