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