xref: /third_party/node/deps/v8/src/api/api-inl.h (revision 1cb0ef41)
1// Copyright 2018 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 V8_API_API_INL_H_
6#define V8_API_API_INL_H_
7
8#include "include/v8-fast-api-calls.h"
9#include "src/api/api.h"
10#include "src/execution/interrupts-scope.h"
11#include "src/execution/microtask-queue.h"
12#include "src/execution/protectors.h"
13#include "src/handles/handles-inl.h"
14#include "src/heap/heap-inl.h"
15#include "src/objects/foreign-inl.h"
16#include "src/objects/js-weak-refs.h"
17#include "src/objects/objects-inl.h"
18
19namespace v8 {
20
21template <typename T>
22inline T ToCData(v8::internal::Object obj) {
23  STATIC_ASSERT(sizeof(T) == sizeof(v8::internal::Address));
24  if (obj == v8::internal::Smi::zero()) return nullptr;
25  return reinterpret_cast<T>(
26      v8::internal::Foreign::cast(obj).foreign_address());
27}
28
29template <>
30inline v8::internal::Address ToCData(v8::internal::Object obj) {
31  if (obj == v8::internal::Smi::zero()) return v8::internal::kNullAddress;
32  return v8::internal::Foreign::cast(obj).foreign_address();
33}
34
35template <typename T>
36inline v8::internal::Handle<v8::internal::Object> FromCData(
37    v8::internal::Isolate* isolate, T obj) {
38  STATIC_ASSERT(sizeof(T) == sizeof(v8::internal::Address));
39  if (obj == nullptr) return handle(v8::internal::Smi::zero(), isolate);
40  return isolate->factory()->NewForeign(
41      reinterpret_cast<v8::internal::Address>(obj));
42}
43
44template <>
45inline v8::internal::Handle<v8::internal::Object> FromCData(
46    v8::internal::Isolate* isolate, v8::internal::Address obj) {
47  if (obj == v8::internal::kNullAddress) {
48    return handle(v8::internal::Smi::zero(), isolate);
49  }
50  return isolate->factory()->NewForeign(obj);
51}
52
53template <class From, class To>
54inline Local<To> Utils::Convert(v8::internal::Handle<From> obj) {
55  DCHECK(obj.is_null() || (obj->IsSmi() || !obj->IsTheHole()));
56  return Local<To>(reinterpret_cast<To*>(obj.location()));
57}
58
59// Implementations of ToLocal
60
61#define MAKE_TO_LOCAL(Name, From, To)                                       \
62  Local<v8::To> Utils::Name(v8::internal::Handle<v8::internal::From> obj) { \
63    return Convert<v8::internal::From, v8::To>(obj);                        \
64  }
65
66#define MAKE_TO_LOCAL_TYPED_ARRAY(Type, typeName, TYPE, ctype)        \
67  Local<v8::Type##Array> Utils::ToLocal##Type##Array(                 \
68      v8::internal::Handle<v8::internal::JSTypedArray> obj) {         \
69    DCHECK(obj->type() == v8::internal::kExternal##Type##Array);      \
70    return Convert<v8::internal::JSTypedArray, v8::Type##Array>(obj); \
71  }
72
73MAKE_TO_LOCAL(ToLocal, AccessorPair, debug::AccessorPair)
74MAKE_TO_LOCAL(ToLocal, Context, Context)
75MAKE_TO_LOCAL(ToLocal, Object, Value)
76MAKE_TO_LOCAL(ToLocal, Module, Module)
77MAKE_TO_LOCAL(ToLocal, Name, Name)
78MAKE_TO_LOCAL(ToLocal, String, String)
79MAKE_TO_LOCAL(ToLocal, Symbol, Symbol)
80MAKE_TO_LOCAL(ToLocal, JSRegExp, RegExp)
81MAKE_TO_LOCAL(ToLocal, JSReceiver, Object)
82MAKE_TO_LOCAL(ToLocal, JSObject, Object)
83MAKE_TO_LOCAL(ToLocal, JSFunction, Function)
84MAKE_TO_LOCAL(ToLocal, JSArray, Array)
85MAKE_TO_LOCAL(ToLocal, JSMap, Map)
86MAKE_TO_LOCAL(ToLocal, JSSet, Set)
87MAKE_TO_LOCAL(ToLocal, JSProxy, Proxy)
88MAKE_TO_LOCAL(ToLocal, JSArrayBuffer, ArrayBuffer)
89MAKE_TO_LOCAL(ToLocal, JSArrayBufferView, ArrayBufferView)
90MAKE_TO_LOCAL(ToLocal, JSDataView, DataView)
91MAKE_TO_LOCAL(ToLocal, JSTypedArray, TypedArray)
92MAKE_TO_LOCAL(ToLocalShared, JSArrayBuffer, SharedArrayBuffer)
93
94TYPED_ARRAYS(MAKE_TO_LOCAL_TYPED_ARRAY)
95
96MAKE_TO_LOCAL(ToLocal, FunctionTemplateInfo, FunctionTemplate)
97MAKE_TO_LOCAL(ToLocal, ObjectTemplateInfo, ObjectTemplate)
98MAKE_TO_LOCAL(SignatureToLocal, FunctionTemplateInfo, Signature)
99MAKE_TO_LOCAL(AccessorSignatureToLocal, FunctionTemplateInfo, AccessorSignature)
100MAKE_TO_LOCAL(MessageToLocal, Object, Message)
101MAKE_TO_LOCAL(PromiseToLocal, JSObject, Promise)
102MAKE_TO_LOCAL(StackTraceToLocal, FixedArray, StackTrace)
103MAKE_TO_LOCAL(StackFrameToLocal, StackFrameInfo, StackFrame)
104MAKE_TO_LOCAL(NumberToLocal, Object, Number)
105MAKE_TO_LOCAL(IntegerToLocal, Object, Integer)
106MAKE_TO_LOCAL(Uint32ToLocal, Object, Uint32)
107MAKE_TO_LOCAL(ToLocal, BigInt, BigInt)
108MAKE_TO_LOCAL(ExternalToLocal, JSObject, External)
109MAKE_TO_LOCAL(CallableToLocal, JSReceiver, Function)
110MAKE_TO_LOCAL(ToLocalPrimitive, Object, Primitive)
111MAKE_TO_LOCAL(FixedArrayToLocal, FixedArray, FixedArray)
112MAKE_TO_LOCAL(PrimitiveArrayToLocal, FixedArray, PrimitiveArray)
113MAKE_TO_LOCAL(ToLocal, ScriptOrModule, ScriptOrModule)
114
115#undef MAKE_TO_LOCAL_TYPED_ARRAY
116#undef MAKE_TO_LOCAL
117
118// Implementations of OpenHandle
119
120#define MAKE_OPEN_HANDLE(From, To)                                    \
121  v8::internal::Handle<v8::internal::To> Utils::OpenHandle(           \
122      const v8::From* that, bool allow_empty_handle) {                \
123    DCHECK(allow_empty_handle || that != nullptr);                    \
124    DCHECK(that == nullptr ||                                         \
125           v8::internal::Object(                                      \
126               *reinterpret_cast<const v8::internal::Address*>(that)) \
127               .Is##To());                                            \
128    return v8::internal::Handle<v8::internal::To>(                    \
129        reinterpret_cast<v8::internal::Address*>(                     \
130            const_cast<v8::From*>(that)));                            \
131  }
132
133OPEN_HANDLE_LIST(MAKE_OPEN_HANDLE)
134
135#undef MAKE_OPEN_HANDLE
136#undef OPEN_HANDLE_LIST
137
138template <bool do_callback>
139class V8_NODISCARD CallDepthScope {
140 public:
141  CallDepthScope(i::Isolate* isolate, Local<Context> context)
142      : isolate_(isolate),
143        context_(context),
144        did_enter_context_(false),
145        escaped_(false),
146        safe_for_termination_(isolate->next_v8_call_is_safe_for_termination()),
147        interrupts_scope_(isolate_, i::StackGuard::TERMINATE_EXECUTION,
148                          isolate_->only_terminate_in_safe_scope()
149                              ? (safe_for_termination_
150                                     ? i::InterruptsScope::kRunInterrupts
151                                     : i::InterruptsScope::kPostponeInterrupts)
152                              : i::InterruptsScope::kNoop) {
153    isolate_->thread_local_top()->IncrementCallDepth(this);
154    isolate_->set_next_v8_call_is_safe_for_termination(false);
155    if (!context.IsEmpty()) {
156      i::Handle<i::Context> env = Utils::OpenHandle(*context);
157      i::HandleScopeImplementer* impl = isolate->handle_scope_implementer();
158      if (isolate->context().is_null() ||
159          isolate->context().native_context() != env->native_context()) {
160        impl->SaveContext(isolate->context());
161        isolate->set_context(*env);
162        did_enter_context_ = true;
163      }
164    }
165    if (do_callback) isolate_->FireBeforeCallEnteredCallback();
166  }
167  ~CallDepthScope() {
168    i::MicrotaskQueue* microtask_queue = isolate_->default_microtask_queue();
169    if (!context_.IsEmpty()) {
170      if (did_enter_context_) {
171        i::HandleScopeImplementer* impl = isolate_->handle_scope_implementer();
172        isolate_->set_context(impl->RestoreContext());
173      }
174
175      i::Handle<i::Context> env = Utils::OpenHandle(*context_);
176      microtask_queue = env->native_context().microtask_queue();
177    }
178    if (!escaped_) isolate_->thread_local_top()->DecrementCallDepth(this);
179    if (do_callback) isolate_->FireCallCompletedCallback(microtask_queue);
180#ifdef DEBUG
181    if (do_callback) {
182      if (microtask_queue && microtask_queue->microtasks_policy() ==
183                                 v8::MicrotasksPolicy::kScoped) {
184        DCHECK(microtask_queue->GetMicrotasksScopeDepth() ||
185               !microtask_queue->DebugMicrotasksScopeDepthIsZero());
186      }
187    }
188#endif
189    DCHECK(CheckKeptObjectsClearedAfterMicrotaskCheckpoint(microtask_queue));
190    isolate_->set_next_v8_call_is_safe_for_termination(safe_for_termination_);
191  }
192
193  CallDepthScope(const CallDepthScope&) = delete;
194  CallDepthScope& operator=(const CallDepthScope&) = delete;
195
196  void Escape() {
197    DCHECK(!escaped_);
198    escaped_ = true;
199    auto thread_local_top = isolate_->thread_local_top();
200    thread_local_top->DecrementCallDepth(this);
201    bool clear_exception = thread_local_top->CallDepthIsZero() &&
202                           thread_local_top->try_catch_handler_ == nullptr;
203    isolate_->OptionalRescheduleException(clear_exception);
204  }
205
206 private:
207  bool CheckKeptObjectsClearedAfterMicrotaskCheckpoint(
208      i::MicrotaskQueue* microtask_queue) {
209    bool did_perform_microtask_checkpoint =
210        isolate_->thread_local_top()->CallDepthIsZero() && do_callback &&
211        microtask_queue &&
212        microtask_queue->microtasks_policy() == MicrotasksPolicy::kAuto;
213    return !did_perform_microtask_checkpoint ||
214           isolate_->heap()->weak_refs_keep_during_job().IsUndefined(isolate_);
215  }
216
217  i::Isolate* const isolate_;
218  Local<Context> context_;
219  bool did_enter_context_ : 1;
220  bool escaped_ : 1;
221  bool safe_for_termination_ : 1;
222  i::InterruptsScope interrupts_scope_;
223  i::Address previous_stack_height_;
224
225  friend class i::ThreadLocalTop;
226
227  DISALLOW_NEW_AND_DELETE()
228};
229
230class V8_NODISCARD InternalEscapableScope : public EscapableHandleScope {
231 public:
232  explicit inline InternalEscapableScope(i::Isolate* isolate)
233      : EscapableHandleScope(reinterpret_cast<v8::Isolate*>(isolate)) {}
234};
235
236inline bool IsExecutionTerminatingCheck(i::Isolate* isolate) {
237  if (isolate->has_scheduled_exception()) {
238    return isolate->scheduled_exception() ==
239           i::ReadOnlyRoots(isolate).termination_exception();
240  }
241  return false;
242}
243
244template <typename T>
245void CopySmiElementsToTypedBuffer(T* dst, uint32_t length,
246                                  i::FixedArray elements) {
247  for (uint32_t i = 0; i < length; ++i) {
248    double value = elements.get(static_cast<int>(i)).Number();
249    // TODO(mslekova): Avoid converting back-and-forth when possible, e.g
250    // avoid int->double->int conversions to boost performance.
251    dst[i] = i::ConvertDouble<T>(value);
252  }
253}
254
255template <typename T>
256void CopyDoubleElementsToTypedBuffer(T* dst, uint32_t length,
257                                     i::FixedDoubleArray elements) {
258  for (uint32_t i = 0; i < length; ++i) {
259    double value = elements.get_scalar(static_cast<int>(i));
260    // TODO(mslekova): There are certain cases, e.g. double->double, in which
261    // we could do a memcpy directly.
262    dst[i] = i::ConvertDouble<T>(value);
263  }
264}
265
266template <CTypeInfo::Identifier type_info_id, typename T>
267bool CopyAndConvertArrayToCppBuffer(Local<Array> src, T* dst,
268                                    uint32_t max_length) {
269  static_assert(
270      std::is_same<T, typename i::CTypeInfoTraits<
271                          CTypeInfo(type_info_id).GetType()>::ctype>::value,
272      "Type mismatch between the expected CTypeInfo::Type and the destination "
273      "array");
274
275  uint32_t length = src->Length();
276  if (length > max_length) {
277    return false;
278  }
279
280  i::DisallowGarbageCollection no_gc;
281  i::JSArray obj = *reinterpret_cast<i::JSArray*>(*src);
282  if (obj.IterationHasObservableEffects()) {
283    // The array has a custom iterator.
284    return false;
285  }
286
287  i::FixedArrayBase elements = obj.elements();
288  switch (obj.GetElementsKind()) {
289    case i::PACKED_SMI_ELEMENTS:
290      CopySmiElementsToTypedBuffer(dst, length, i::FixedArray::cast(elements));
291      return true;
292    case i::PACKED_DOUBLE_ELEMENTS:
293      CopyDoubleElementsToTypedBuffer(dst, length,
294                                      i::FixedDoubleArray::cast(elements));
295      return true;
296    default:
297      return false;
298  }
299}
300
301// Deprecated; to be removed.
302template <const CTypeInfo* type_info, typename T>
303inline bool V8_EXPORT TryCopyAndConvertArrayToCppBuffer(Local<Array> src,
304                                                        T* dst,
305                                                        uint32_t max_length) {
306  return CopyAndConvertArrayToCppBuffer<type_info->GetId(), T>(src, dst,
307                                                               max_length);
308}
309
310template <CTypeInfo::Identifier type_info_id, typename T>
311inline bool V8_EXPORT TryToCopyAndConvertArrayToCppBuffer(Local<Array> src,
312                                                          T* dst,
313                                                          uint32_t max_length) {
314  return CopyAndConvertArrayToCppBuffer<type_info_id, T>(src, dst, max_length);
315}
316
317namespace internal {
318
319void HandleScopeImplementer::EnterContext(Context context) {
320  DCHECK_EQ(entered_contexts_.capacity(), is_microtask_context_.capacity());
321  DCHECK_EQ(entered_contexts_.size(), is_microtask_context_.size());
322  DCHECK(context.IsNativeContext());
323  entered_contexts_.push_back(context);
324  is_microtask_context_.push_back(0);
325}
326
327void HandleScopeImplementer::EnterMicrotaskContext(Context context) {
328  DCHECK_EQ(entered_contexts_.capacity(), is_microtask_context_.capacity());
329  DCHECK_EQ(entered_contexts_.size(), is_microtask_context_.size());
330  DCHECK(context.IsNativeContext());
331  entered_contexts_.push_back(context);
332  is_microtask_context_.push_back(1);
333}
334
335Handle<Context> HandleScopeImplementer::LastEnteredContext() {
336  DCHECK_EQ(entered_contexts_.capacity(), is_microtask_context_.capacity());
337  DCHECK_EQ(entered_contexts_.size(), is_microtask_context_.size());
338
339  for (size_t i = 0; i < entered_contexts_.size(); ++i) {
340    size_t j = entered_contexts_.size() - i - 1;
341    if (!is_microtask_context_.at(j)) {
342      return Handle<Context>(entered_contexts_.at(j), isolate_);
343    }
344  }
345
346  return Handle<Context>::null();
347}
348
349Handle<Context> HandleScopeImplementer::LastEnteredOrMicrotaskContext() {
350  if (entered_contexts_.empty()) return Handle<Context>::null();
351  return Handle<Context>(entered_contexts_.back(), isolate_);
352}
353
354}  // namespace internal
355}  // namespace v8
356
357#endif  // V8_API_API_INL_H_
358