1// Copyright 2021 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#ifndef INCLUDE_V8_CONTEXT_H_ 6#define INCLUDE_V8_CONTEXT_H_ 7 8#include <stdint.h> 9 10#include <vector> 11 12#include "v8-data.h" // NOLINT(build/include_directory) 13#include "v8-local-handle.h" // NOLINT(build/include_directory) 14#include "v8-maybe.h" // NOLINT(build/include_directory) 15#include "v8-snapshot.h" // NOLINT(build/include_directory) 16#include "v8config.h" // NOLINT(build/include_directory) 17 18namespace v8 { 19 20class Function; 21class MicrotaskQueue; 22class Object; 23class ObjectTemplate; 24class Value; 25class String; 26 27/** 28 * A container for extension names. 29 */ 30class V8_EXPORT ExtensionConfiguration { 31 public: 32 ExtensionConfiguration() : name_count_(0), names_(nullptr) {} 33 ExtensionConfiguration(int name_count, const char* names[]) 34 : name_count_(name_count), names_(names) {} 35 36 const char** begin() const { return &names_[0]; } 37 const char** end() const { return &names_[name_count_]; } 38 39 private: 40 const int name_count_; 41 const char** names_; 42}; 43 44/** 45 * A sandboxed execution context with its own set of built-in objects 46 * and functions. 47 */ 48class V8_EXPORT Context : public Data { 49 public: 50 /** 51 * Returns the global proxy object. 52 * 53 * Global proxy object is a thin wrapper whose prototype points to actual 54 * context's global object with the properties like Object, etc. This is done 55 * that way for security reasons (for more details see 56 * https://wiki.mozilla.org/Gecko:SplitWindow). 57 * 58 * Please note that changes to global proxy object prototype most probably 59 * would break VM---v8 expects only global object as a prototype of global 60 * proxy object. 61 */ 62 Local<Object> Global(); 63 64 /** 65 * Detaches the global object from its context before 66 * the global object can be reused to create a new context. 67 */ 68 void DetachGlobal(); 69 70 /** 71 * Creates a new context and returns a handle to the newly allocated 72 * context. 73 * 74 * \param isolate The isolate in which to create the context. 75 * 76 * \param extensions An optional extension configuration containing 77 * the extensions to be installed in the newly created context. 78 * 79 * \param global_template An optional object template from which the 80 * global object for the newly created context will be created. 81 * 82 * \param global_object An optional global object to be reused for 83 * the newly created context. This global object must have been 84 * created by a previous call to Context::New with the same global 85 * template. The state of the global object will be completely reset 86 * and only object identify will remain. 87 */ 88 static Local<Context> New( 89 Isolate* isolate, ExtensionConfiguration* extensions = nullptr, 90 MaybeLocal<ObjectTemplate> global_template = MaybeLocal<ObjectTemplate>(), 91 MaybeLocal<Value> global_object = MaybeLocal<Value>(), 92 DeserializeInternalFieldsCallback internal_fields_deserializer = 93 DeserializeInternalFieldsCallback(), 94 MicrotaskQueue* microtask_queue = nullptr); 95 96 /** 97 * Create a new context from a (non-default) context snapshot. There 98 * is no way to provide a global object template since we do not create 99 * a new global object from template, but we can reuse a global object. 100 * 101 * \param isolate See v8::Context::New. 102 * 103 * \param context_snapshot_index The index of the context snapshot to 104 * deserialize from. Use v8::Context::New for the default snapshot. 105 * 106 * \param embedder_fields_deserializer Optional callback to deserialize 107 * internal fields. It should match the SerializeInternalFieldCallback used 108 * to serialize. 109 * 110 * \param extensions See v8::Context::New. 111 * 112 * \param global_object See v8::Context::New. 113 */ 114 static MaybeLocal<Context> FromSnapshot( 115 Isolate* isolate, size_t context_snapshot_index, 116 DeserializeInternalFieldsCallback embedder_fields_deserializer = 117 DeserializeInternalFieldsCallback(), 118 ExtensionConfiguration* extensions = nullptr, 119 MaybeLocal<Value> global_object = MaybeLocal<Value>(), 120 MicrotaskQueue* microtask_queue = nullptr); 121 122 /** 123 * Returns an global object that isn't backed by an actual context. 124 * 125 * The global template needs to have access checks with handlers installed. 126 * If an existing global object is passed in, the global object is detached 127 * from its context. 128 * 129 * Note that this is different from a detached context where all accesses to 130 * the global proxy will fail. Instead, the access check handlers are invoked. 131 * 132 * It is also not possible to detach an object returned by this method. 133 * Instead, the access check handlers need to return nothing to achieve the 134 * same effect. 135 * 136 * It is possible, however, to create a new context from the global object 137 * returned by this method. 138 */ 139 static MaybeLocal<Object> NewRemoteContext( 140 Isolate* isolate, Local<ObjectTemplate> global_template, 141 MaybeLocal<Value> global_object = MaybeLocal<Value>()); 142 143 /** 144 * Sets the security token for the context. To access an object in 145 * another context, the security tokens must match. 146 */ 147 void SetSecurityToken(Local<Value> token); 148 149 /** Restores the security token to the default value. */ 150 void UseDefaultSecurityToken(); 151 152 /** Returns the security token of this context.*/ 153 Local<Value> GetSecurityToken(); 154 155 /** 156 * Enter this context. After entering a context, all code compiled 157 * and run is compiled and run in this context. If another context 158 * is already entered, this old context is saved so it can be 159 * restored when the new context is exited. 160 */ 161 void Enter(); 162 163 /** 164 * Exit this context. Exiting the current context restores the 165 * context that was in place when entering the current context. 166 */ 167 void Exit(); 168 169 /** 170 * Delegate to help with Deep freezing embedder-specific objects (such as 171 * JSApiObjects) that can not be frozen natively. 172 */ 173 class DeepFreezeDelegate { 174 public: 175 /** 176 * Performs embedder-specific operations to freeze the provided embedder 177 * object. The provided object *will* be frozen by DeepFreeze after this 178 * function returns, so only embedder-specific objects need to be frozen. 179 * This function *may not* create new JS objects or perform JS allocations. 180 * Any v8 objects reachable from the provided embedder object that should 181 * also be considered for freezing should be added to the children_out 182 * parameter. Returns true if the operation completed successfully. 183 */ 184 virtual bool FreezeEmbedderObjectAndGetChildren( 185 Local<Object> obj, std::vector<Local<Object>>& children_out) = 0; 186 }; 187 188 /** 189 * Attempts to recursively freeze all objects reachable from this context. 190 * Some objects (generators, iterators, non-const closures) can not be frozen 191 * and will cause this method to throw an error. An optional delegate can be 192 * provided to help freeze embedder-specific objects. 193 * 194 * Freezing occurs in two steps: 195 * 1. "Marking" where we iterate through all objects reachable by this 196 * context, accumulating a list of objects that need to be frozen and 197 * looking for objects that can't be frozen. This step is separated because 198 * it is more efficient when we can assume there is no garbage collection. 199 * 2. "Freezing" where we go through the list of objects and freezing them. 200 * This effectively requires copying them so it may trigger garbage 201 * collection. 202 */ 203 Maybe<void> DeepFreeze(DeepFreezeDelegate* delegate = nullptr); 204 205 /** Returns the isolate associated with a current context. */ 206 Isolate* GetIsolate(); 207 208 /** Returns the microtask queue associated with a current context. */ 209 MicrotaskQueue* GetMicrotaskQueue(); 210 211 /** Sets the microtask queue associated with the current context. */ 212 void SetMicrotaskQueue(MicrotaskQueue* queue); 213 214 /** 215 * The field at kDebugIdIndex used to be reserved for the inspector. 216 * It now serves no purpose. 217 */ 218 enum EmbedderDataFields { kDebugIdIndex = 0 }; 219 220 /** 221 * Return the number of fields allocated for embedder data. 222 */ 223 uint32_t GetNumberOfEmbedderDataFields(); 224 225 /** 226 * Gets the embedder data with the given index, which must have been set by a 227 * previous call to SetEmbedderData with the same index. 228 */ 229 V8_INLINE Local<Value> GetEmbedderData(int index); 230 231 /** 232 * Gets the binding object used by V8 extras. Extra natives get a reference 233 * to this object and can use it to "export" functionality by adding 234 * properties. Extra natives can also "import" functionality by accessing 235 * properties added by the embedder using the V8 API. 236 */ 237 Local<Object> GetExtrasBindingObject(); 238 239 /** 240 * Sets the embedder data with the given index, growing the data as 241 * needed. Note that index 0 currently has a special meaning for Chrome's 242 * debugger. 243 */ 244 void SetEmbedderData(int index, Local<Value> value); 245 246 /** 247 * Gets a 2-byte-aligned native pointer from the embedder data with the given 248 * index, which must have been set by a previous call to 249 * SetAlignedPointerInEmbedderData with the same index. Note that index 0 250 * currently has a special meaning for Chrome's debugger. 251 */ 252 V8_INLINE void* GetAlignedPointerFromEmbedderData(int index); 253 254 /** 255 * Sets a 2-byte-aligned native pointer in the embedder data with the given 256 * index, growing the data as needed. Note that index 0 currently has a 257 * special meaning for Chrome's debugger. 258 */ 259 void SetAlignedPointerInEmbedderData(int index, void* value); 260 261 /** 262 * Control whether code generation from strings is allowed. Calling 263 * this method with false will disable 'eval' and the 'Function' 264 * constructor for code running in this context. If 'eval' or the 265 * 'Function' constructor are used an exception will be thrown. 266 * 267 * If code generation from strings is not allowed the 268 * V8::AllowCodeGenerationFromStrings callback will be invoked if 269 * set before blocking the call to 'eval' or the 'Function' 270 * constructor. If that callback returns true, the call will be 271 * allowed, otherwise an exception will be thrown. If no callback is 272 * set an exception will be thrown. 273 */ 274 void AllowCodeGenerationFromStrings(bool allow); 275 276 /** 277 * Returns true if code generation from strings is allowed for the context. 278 * For more details see AllowCodeGenerationFromStrings(bool) documentation. 279 */ 280 bool IsCodeGenerationFromStringsAllowed() const; 281 282 /** 283 * Sets the error description for the exception that is thrown when 284 * code generation from strings is not allowed and 'eval' or the 'Function' 285 * constructor are called. 286 */ 287 void SetErrorMessageForCodeGenerationFromStrings(Local<String> message); 288 289 /** 290 * Sets the error description for the exception that is thrown when 291 * wasm code generation is not allowed. 292 */ 293 void SetErrorMessageForWasmCodeGeneration(Local<String> message); 294 295 /** 296 * Return data that was previously attached to the context snapshot via 297 * SnapshotCreator, and removes the reference to it. 298 * Repeated call with the same index returns an empty MaybeLocal. 299 */ 300 template <class T> 301 V8_INLINE MaybeLocal<T> GetDataFromSnapshotOnce(size_t index); 302 303 /** 304 * If callback is set, abort any attempt to execute JavaScript in this 305 * context, call the specified callback, and throw an exception. 306 * To unset abort, pass nullptr as callback. 307 */ 308 using AbortScriptExecutionCallback = void (*)(Isolate* isolate, 309 Local<Context> context); 310 void SetAbortScriptExecution(AbortScriptExecutionCallback callback); 311 312 /** 313 * Returns the value that was set or restored by 314 * SetContinuationPreservedEmbedderData(), if any. 315 */ 316 Local<Value> GetContinuationPreservedEmbedderData() const; 317 318 /** 319 * Sets a value that will be stored on continuations and reset while the 320 * continuation runs. 321 */ 322 void SetContinuationPreservedEmbedderData(Local<Value> context); 323 324 /** 325 * Set or clear hooks to be invoked for promise lifecycle operations. 326 * To clear a hook, set it to an empty v8::Function. Each function will 327 * receive the observed promise as the first argument. If a chaining 328 * operation is used on a promise, the init will additionally receive 329 * the parent promise as the second argument. 330 */ 331 void SetPromiseHooks(Local<Function> init_hook, Local<Function> before_hook, 332 Local<Function> after_hook, 333 Local<Function> resolve_hook); 334 335 bool HasTemplateLiteralObject(Local<Value> object); 336 /** 337 * Stack-allocated class which sets the execution context for all 338 * operations executed within a local scope. 339 */ 340 class V8_NODISCARD Scope { 341 public: 342 explicit V8_INLINE Scope(Local<Context> context) : context_(context) { 343 context_->Enter(); 344 } 345 V8_INLINE ~Scope() { context_->Exit(); } 346 347 private: 348 Local<Context> context_; 349 }; 350 351 /** 352 * Stack-allocated class to support the backup incumbent settings object 353 * stack. 354 * https://html.spec.whatwg.org/multipage/webappapis.html#backup-incumbent-settings-object-stack 355 */ 356 class V8_EXPORT V8_NODISCARD BackupIncumbentScope final { 357 public: 358 /** 359 * |backup_incumbent_context| is pushed onto the backup incumbent settings 360 * object stack. 361 */ 362 explicit BackupIncumbentScope(Local<Context> backup_incumbent_context); 363 ~BackupIncumbentScope(); 364 365 private: 366 friend class internal::Isolate; 367 368 uintptr_t JSStackComparableAddressPrivate() const { 369 return js_stack_comparable_address_; 370 } 371 372 Local<Context> backup_incumbent_context_; 373 uintptr_t js_stack_comparable_address_ = 0; 374 const BackupIncumbentScope* prev_ = nullptr; 375 }; 376 377 V8_INLINE static Context* Cast(Data* data); 378 379 private: 380 friend class Value; 381 friend class Script; 382 friend class Object; 383 friend class Function; 384 385 static void CheckCast(Data* obj); 386 387 internal::Address* GetDataFromSnapshotOnce(size_t index); 388 Local<Value> SlowGetEmbedderData(int index); 389 void* SlowGetAlignedPointerFromEmbedderData(int index); 390}; 391 392// --- Implementation --- 393 394Local<Value> Context::GetEmbedderData(int index) { 395#ifndef V8_ENABLE_CHECKS 396 using A = internal::Address; 397 using I = internal::Internals; 398 A ctx = internal::ValueHelper::ValueAsAddress(this); 399 A embedder_data = 400 I::ReadTaggedPointerField(ctx, I::kNativeContextEmbedderDataOffset); 401 int value_offset = 402 I::kEmbedderDataArrayHeaderSize + (I::kEmbedderDataSlotSize * index); 403 A value = I::ReadRawField<A>(embedder_data, value_offset); 404#ifdef V8_COMPRESS_POINTERS 405 // We read the full pointer value and then decompress it in order to avoid 406 // dealing with potential endiannes issues. 407 value = I::DecompressTaggedField(embedder_data, static_cast<uint32_t>(value)); 408#endif 409 410 auto isolate = reinterpret_cast<v8::Isolate*>( 411 internal::IsolateFromNeverReadOnlySpaceObject(ctx)); 412 return Local<Value>::New(isolate, value); 413#else 414 return SlowGetEmbedderData(index); 415#endif 416} 417 418void* Context::GetAlignedPointerFromEmbedderData(int index) { 419#if !defined(V8_ENABLE_CHECKS) 420 using A = internal::Address; 421 using I = internal::Internals; 422 A ctx = internal::ValueHelper::ValueAsAddress(this); 423 A embedder_data = 424 I::ReadTaggedPointerField(ctx, I::kNativeContextEmbedderDataOffset); 425 int value_offset = I::kEmbedderDataArrayHeaderSize + 426 (I::kEmbedderDataSlotSize * index) + 427 I::kEmbedderDataSlotExternalPointerOffset; 428 Isolate* isolate = I::GetIsolateForSandbox(ctx); 429 return reinterpret_cast<void*>( 430 I::ReadExternalPointerField<internal::kEmbedderDataSlotPayloadTag>( 431 isolate, embedder_data, value_offset)); 432#else 433 return SlowGetAlignedPointerFromEmbedderData(index); 434#endif 435} 436 437template <class T> 438MaybeLocal<T> Context::GetDataFromSnapshotOnce(size_t index) { 439 auto slot = GetDataFromSnapshotOnce(index); 440 if (slot) { 441 internal::PerformCastCheck(internal::ValueHelper::SlotAsValue<T>(slot)); 442 } 443 return Local<T>::FromSlot(slot); 444} 445 446Context* Context::Cast(v8::Data* data) { 447#ifdef V8_ENABLE_CHECKS 448 CheckCast(data); 449#endif 450 return static_cast<Context*>(data); 451} 452 453} // namespace v8 454 455#endif // INCLUDE_V8_CONTEXT_H_ 456