• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
19 namespace v8 {
20 
21 template <typename T>
ToCData(v8::internal::Object obj)22 inline 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 
29 template <>
ToCData(v8::internal::Object obj)30 inline 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 
35 template <typename T>
FromCData(v8::internal::Isolate * isolate,T obj)36 inline 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 
44 template <>
FromCData(v8::internal::Isolate * isolate,v8::internal::Address obj)45 inline 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 
53 template <class From, class To>
Convert(v8::internal::Handle<From> obj)54 inline 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 
MAKE_TO_LOCAL(ToLocal,AccessorPair,debug::AccessorPair)73 MAKE_TO_LOCAL(ToLocal, AccessorPair, debug::AccessorPair)
74 MAKE_TO_LOCAL(ToLocal, Context, Context)
75 MAKE_TO_LOCAL(ToLocal, Object, Value)
76 MAKE_TO_LOCAL(ToLocal, Module, Module)
77 MAKE_TO_LOCAL(ToLocal, Name, Name)
78 MAKE_TO_LOCAL(ToLocal, String, String)
79 MAKE_TO_LOCAL(ToLocal, Symbol, Symbol)
80 MAKE_TO_LOCAL(ToLocal, JSRegExp, RegExp)
81 MAKE_TO_LOCAL(ToLocal, JSReceiver, Object)
82 MAKE_TO_LOCAL(ToLocal, JSObject, Object)
83 MAKE_TO_LOCAL(ToLocal, JSFunction, Function)
84 MAKE_TO_LOCAL(ToLocal, JSArray, Array)
85 MAKE_TO_LOCAL(ToLocal, JSMap, Map)
86 MAKE_TO_LOCAL(ToLocal, JSSet, Set)
87 MAKE_TO_LOCAL(ToLocal, JSProxy, Proxy)
88 MAKE_TO_LOCAL(ToLocal, JSArrayBuffer, ArrayBuffer)
89 MAKE_TO_LOCAL(ToLocal, JSArrayBufferView, ArrayBufferView)
90 MAKE_TO_LOCAL(ToLocal, JSDataView, DataView)
91 MAKE_TO_LOCAL(ToLocal, JSTypedArray, TypedArray)
92 MAKE_TO_LOCAL(ToLocalShared, JSArrayBuffer, SharedArrayBuffer)
93 
94 TYPED_ARRAYS(MAKE_TO_LOCAL_TYPED_ARRAY)
95 
96 MAKE_TO_LOCAL(ToLocal, FunctionTemplateInfo, FunctionTemplate)
97 MAKE_TO_LOCAL(ToLocal, ObjectTemplateInfo, ObjectTemplate)
98 MAKE_TO_LOCAL(SignatureToLocal, FunctionTemplateInfo, Signature)
99 MAKE_TO_LOCAL(AccessorSignatureToLocal, FunctionTemplateInfo, AccessorSignature)
100 MAKE_TO_LOCAL(MessageToLocal, Object, Message)
101 MAKE_TO_LOCAL(PromiseToLocal, JSObject, Promise)
102 MAKE_TO_LOCAL(StackTraceToLocal, FixedArray, StackTrace)
103 MAKE_TO_LOCAL(StackFrameToLocal, StackFrameInfo, StackFrame)
104 MAKE_TO_LOCAL(NumberToLocal, Object, Number)
105 MAKE_TO_LOCAL(IntegerToLocal, Object, Integer)
106 MAKE_TO_LOCAL(Uint32ToLocal, Object, Uint32)
107 MAKE_TO_LOCAL(ToLocal, BigInt, BigInt)
108 MAKE_TO_LOCAL(ExternalToLocal, JSObject, External)
109 MAKE_TO_LOCAL(CallableToLocal, JSReceiver, Function)
110 MAKE_TO_LOCAL(ToLocalPrimitive, Object, Primitive)
111 MAKE_TO_LOCAL(FixedArrayToLocal, FixedArray, FixedArray)
112 MAKE_TO_LOCAL(PrimitiveArrayToLocal, FixedArray, PrimitiveArray)
113 MAKE_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 
133 OPEN_HANDLE_LIST(MAKE_OPEN_HANDLE)
134 
135 #undef MAKE_OPEN_HANDLE
136 #undef OPEN_HANDLE_LIST
137 
138 template <bool do_callback>
139 class 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 
230 class V8_NODISCARD InternalEscapableScope : public EscapableHandleScope {
231  public:
InternalEscapableScope(i::Isolate * isolate)232   explicit inline InternalEscapableScope(i::Isolate* isolate)
233       : EscapableHandleScope(reinterpret_cast<v8::Isolate*>(isolate)) {}
234 };
235 
IsExecutionTerminatingCheck(i::Isolate * isolate)236 inline 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 
244 template <typename T>
CopySmiElementsToTypedBuffer(T * dst,uint32_t length,i::FixedArray elements)245 void 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 
255 template <typename T>
CopyDoubleElementsToTypedBuffer(T * dst,uint32_t length,i::FixedDoubleArray elements)256 void 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 
266 template <CTypeInfo::Identifier type_info_id, typename T>
CopyAndConvertArrayToCppBuffer(Local<Array> src,T * dst,uint32_t max_length)267 bool 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.
302 template <const CTypeInfo* type_info, typename T>
TryCopyAndConvertArrayToCppBuffer(Local<Array> src,T * dst,uint32_t max_length)303 inline 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 
310 template <CTypeInfo::Identifier type_info_id, typename T>
TryToCopyAndConvertArrayToCppBuffer(Local<Array> src,T * dst,uint32_t max_length)311 inline 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 
317 namespace internal {
318 
EnterContext(Context context)319 void 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 
EnterMicrotaskContext(Context context)327 void 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 
LastEnteredContext()335 Handle<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 
LastEnteredOrMicrotaskContext()349 Handle<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