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