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