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