| // Copyright 2016 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef V8_API_API_ARGUMENTS_INL_H_ |
| #define V8_API_API_ARGUMENTS_INL_H_ |
| |
| #include "src/api/api-arguments.h" |
| #include "src/api/api-inl.h" |
| #include "src/debug/debug.h" |
| #include "src/execution/vm-state-inl.h" |
| #include "src/logging/runtime-call-stats-scope.h" |
| #include "src/objects/api-callbacks.h" |
| #include "src/objects/instance-type.h" |
| #include "src/objects/slots-inl.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| #if DEBUG |
| bool Object::IsApiCallResultType() const { |
| if (IsSmi()) return true; |
| DCHECK(IsHeapObject()); |
| return (IsString() || IsSymbol() || IsJSReceiver() || IsHeapNumber() || |
| IsBigInt() || IsUndefined() || IsTrue() || IsFalse() || IsNull()); |
| } |
| #endif // DEBUG |
| |
| CustomArgumentsBase::CustomArgumentsBase(Isolate* isolate) |
| : Relocatable(isolate) {} |
| |
| template <typename T> |
| CustomArguments<T>::~CustomArguments() { |
| slot_at(kReturnValueIndex).store(Object(kHandleZapValue)); |
| } |
| |
| template <typename T> |
| template <typename V> |
| Handle<V> CustomArguments<T>::GetReturnValue(Isolate* isolate) const { |
| // Check the ReturnValue. |
| FullObjectSlot slot = slot_at(kReturnValueIndex); |
| // Nothing was set, return empty handle as per previous behaviour. |
| Object raw_object = *slot; |
| if (raw_object.IsTheHole(isolate)) return Handle<V>(); |
| DCHECK(raw_object.IsApiCallResultType()); |
| return Handle<V>::cast(Handle<Object>(slot.location())); |
| } |
| |
| inline JSObject PropertyCallbackArguments::holder() const { |
| return JSObject::cast(*slot_at(T::kHolderIndex)); |
| } |
| |
| inline Object PropertyCallbackArguments::receiver() const { |
| return *slot_at(T::kThisIndex); |
| } |
| |
| inline JSReceiver FunctionCallbackArguments::holder() const { |
| return JSReceiver::cast(*slot_at(T::kHolderIndex)); |
| } |
| |
| #define DCHECK_NAME_COMPATIBLE(interceptor, name) \ |
| DCHECK(interceptor->is_named()); \ |
| DCHECK(!name->IsPrivate()); \ |
| DCHECK_IMPLIES(name->IsSymbol(), interceptor->can_intercept_symbols()); |
| |
| #define PREPARE_CALLBACK_INFO_ACCESSOR(ISOLATE, F, API_RETURN_TYPE, \ |
| ACCESSOR_INFO, RECEIVER, ACCESSOR_KIND) \ |
| if (ISOLATE->should_check_side_effects() && \ |
| !ISOLATE->debug()->PerformSideEffectCheckForAccessor( \ |
| ACCESSOR_INFO, RECEIVER, ACCESSOR_KIND)) { \ |
| return {}; \ |
| } \ |
| ExternalCallbackScope call_scope(ISOLATE, FUNCTION_ADDR(F)); \ |
| PropertyCallbackInfo<API_RETURN_TYPE> callback_info(values_); |
| |
| #define PREPARE_CALLBACK_INFO_INTERCEPTOR(ISOLATE, F, API_RETURN_TYPE, \ |
| INTERCEPTOR_INFO) \ |
| if (ISOLATE->should_check_side_effects() && \ |
| !ISOLATE->debug()->PerformSideEffectCheckForInterceptor( \ |
| INTERCEPTOR_INFO)) { \ |
| return {}; \ |
| } \ |
| ExternalCallbackScope call_scope(ISOLATE, FUNCTION_ADDR(F)); \ |
| PropertyCallbackInfo<API_RETURN_TYPE> callback_info(values_); |
| |
| Handle<Object> FunctionCallbackArguments::Call(CallHandlerInfo handler) { |
| Isolate* isolate = this->isolate(); |
| RCS_SCOPE(isolate, RuntimeCallCounterId::kFunctionCallback); |
| v8::FunctionCallback f = |
| reinterpret_cast<v8::FunctionCallback>(handler.callback()); |
| Handle<Object> receiver_check_unsupported; |
| if (isolate->should_check_side_effects() && |
| !isolate->debug()->PerformSideEffectCheckForCallback( |
| handle(handler, isolate))) { |
| return {}; |
| } |
| ExternalCallbackScope call_scope(isolate, FUNCTION_ADDR(f)); |
| FunctionCallbackInfo<v8::Value> info(values_, argv_, argc_); |
| f(info); |
| return GetReturnValue<Object>(isolate); |
| } |
| |
| PropertyCallbackArguments::~PropertyCallbackArguments(){ |
| #ifdef DEBUG |
| // TODO(chromium:1310062): enable this check. |
| // if (javascript_execution_counter_) { |
| // CHECK_WITH_MSG(javascript_execution_counter_ == |
| // isolate()->javascript_execution_counter(), |
| // "Unexpected side effect detected"); |
| // } |
| #endif // DEBUG |
| } |
| |
| // ------------------------------------------------------------------------- |
| // Named Interceptor callbacks. |
| |
| Handle<JSObject> PropertyCallbackArguments::CallNamedEnumerator( |
| Handle<InterceptorInfo> interceptor) { |
| DCHECK(interceptor->is_named()); |
| RCS_SCOPE(isolate(), RuntimeCallCounterId::kNamedEnumeratorCallback); |
| return CallPropertyEnumerator(interceptor); |
| } |
| |
| Handle<Object> PropertyCallbackArguments::CallNamedQuery( |
| Handle<InterceptorInfo> interceptor, Handle<Name> name) { |
| DCHECK_NAME_COMPATIBLE(interceptor, name); |
| Isolate* isolate = this->isolate(); |
| RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedQueryCallback); |
| Handle<Object> receiver_check_unsupported; |
| GenericNamedPropertyQueryCallback f = |
| ToCData<GenericNamedPropertyQueryCallback>(interceptor->query()); |
| PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Integer, interceptor); |
| f(v8::Utils::ToLocal(name), callback_info); |
| return GetReturnValue<Object>(isolate); |
| } |
| |
| Handle<Object> PropertyCallbackArguments::CallNamedGetter( |
| Handle<InterceptorInfo> interceptor, Handle<Name> name) { |
| DCHECK_NAME_COMPATIBLE(interceptor, name); |
| Isolate* isolate = this->isolate(); |
| RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedGetterCallback); |
| GenericNamedPropertyGetterCallback f = |
| ToCData<GenericNamedPropertyGetterCallback>(interceptor->getter()); |
| PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Value, interceptor); |
| f(v8::Utils::ToLocal(name), callback_info); |
| return GetReturnValue<Object>(isolate); |
| } |
| |
| Handle<Object> PropertyCallbackArguments::CallNamedDescriptor( |
| Handle<InterceptorInfo> interceptor, Handle<Name> name) { |
| DCHECK_NAME_COMPATIBLE(interceptor, name); |
| Isolate* isolate = this->isolate(); |
| RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedDescriptorCallback); |
| GenericNamedPropertyDescriptorCallback f = |
| ToCData<GenericNamedPropertyDescriptorCallback>( |
| interceptor->descriptor()); |
| PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Value, interceptor); |
| f(v8::Utils::ToLocal(name), callback_info); |
| return GetReturnValue<Object>(isolate); |
| } |
| |
| Handle<Object> PropertyCallbackArguments::CallNamedSetter( |
| Handle<InterceptorInfo> interceptor, Handle<Name> name, |
| Handle<Object> value) { |
| DCHECK_NAME_COMPATIBLE(interceptor, name); |
| Isolate* isolate = this->isolate(); |
| RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedSetterCallback); |
| GenericNamedPropertySetterCallback f = |
| ToCData<GenericNamedPropertySetterCallback>(interceptor->setter()); |
| Handle<InterceptorInfo> has_side_effects; |
| PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Value, has_side_effects); |
| f(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), callback_info); |
| return GetReturnValue<Object>(isolate); |
| } |
| |
| Handle<Object> PropertyCallbackArguments::CallNamedDefiner( |
| Handle<InterceptorInfo> interceptor, Handle<Name> name, |
| const v8::PropertyDescriptor& desc) { |
| DCHECK_NAME_COMPATIBLE(interceptor, name); |
| Isolate* isolate = this->isolate(); |
| RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedDefinerCallback); |
| GenericNamedPropertyDefinerCallback f = |
| ToCData<GenericNamedPropertyDefinerCallback>(interceptor->definer()); |
| Handle<InterceptorInfo> has_side_effects; |
| PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Value, has_side_effects); |
| f(v8::Utils::ToLocal(name), desc, callback_info); |
| return GetReturnValue<Object>(isolate); |
| } |
| |
| Handle<Object> PropertyCallbackArguments::CallNamedDeleter( |
| Handle<InterceptorInfo> interceptor, Handle<Name> name) { |
| DCHECK_NAME_COMPATIBLE(interceptor, name); |
| Isolate* isolate = this->isolate(); |
| RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedDeleterCallback); |
| GenericNamedPropertyDeleterCallback f = |
| ToCData<GenericNamedPropertyDeleterCallback>(interceptor->deleter()); |
| Handle<InterceptorInfo> has_side_effects; |
| PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Boolean, has_side_effects); |
| f(v8::Utils::ToLocal(name), callback_info); |
| return GetReturnValue<Object>(isolate); |
| } |
| |
| // ------------------------------------------------------------------------- |
| // Indexed Interceptor callbacks. |
| |
| Handle<JSObject> PropertyCallbackArguments::CallIndexedEnumerator( |
| Handle<InterceptorInfo> interceptor) { |
| DCHECK(!interceptor->is_named()); |
| RCS_SCOPE(isolate(), RuntimeCallCounterId::kIndexedEnumeratorCallback); |
| return CallPropertyEnumerator(interceptor); |
| } |
| |
| Handle<Object> PropertyCallbackArguments::CallIndexedQuery( |
| Handle<InterceptorInfo> interceptor, uint32_t index) { |
| DCHECK(!interceptor->is_named()); |
| Isolate* isolate = this->isolate(); |
| RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedQueryCallback); |
| IndexedPropertyQueryCallback f = |
| ToCData<IndexedPropertyQueryCallback>(interceptor->query()); |
| PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Integer, interceptor); |
| f(index, callback_info); |
| return GetReturnValue<Object>(isolate); |
| } |
| |
| Handle<Object> PropertyCallbackArguments::CallIndexedGetter( |
| Handle<InterceptorInfo> interceptor, uint32_t index) { |
| DCHECK(!interceptor->is_named()); |
| RCS_SCOPE(isolate(), RuntimeCallCounterId::kNamedGetterCallback); |
| IndexedPropertyGetterCallback f = |
| ToCData<IndexedPropertyGetterCallback>(interceptor->getter()); |
| Isolate* isolate = this->isolate(); |
| PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Value, interceptor); |
| f(index, callback_info); |
| return GetReturnValue<Object>(isolate); |
| } |
| |
| Handle<Object> PropertyCallbackArguments::CallIndexedDescriptor( |
| Handle<InterceptorInfo> interceptor, uint32_t index) { |
| DCHECK(!interceptor->is_named()); |
| Isolate* isolate = this->isolate(); |
| RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedDescriptorCallback); |
| IndexedPropertyDescriptorCallback f = |
| ToCData<IndexedPropertyDescriptorCallback>(interceptor->descriptor()); |
| PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Value, interceptor); |
| f(index, callback_info); |
| return GetReturnValue<Object>(isolate); |
| } |
| |
| Handle<Object> PropertyCallbackArguments::CallIndexedSetter( |
| Handle<InterceptorInfo> interceptor, uint32_t index, Handle<Object> value) { |
| DCHECK(!interceptor->is_named()); |
| Isolate* isolate = this->isolate(); |
| RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedSetterCallback); |
| IndexedPropertySetterCallback f = |
| ToCData<IndexedPropertySetterCallback>(interceptor->setter()); |
| Handle<InterceptorInfo> has_side_effects; |
| PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Value, has_side_effects); |
| f(index, v8::Utils::ToLocal(value), callback_info); |
| return GetReturnValue<Object>(isolate); |
| } |
| |
| Handle<Object> PropertyCallbackArguments::CallIndexedDefiner( |
| Handle<InterceptorInfo> interceptor, uint32_t index, |
| const v8::PropertyDescriptor& desc) { |
| DCHECK(!interceptor->is_named()); |
| Isolate* isolate = this->isolate(); |
| RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedDefinerCallback); |
| IndexedPropertyDefinerCallback f = |
| ToCData<IndexedPropertyDefinerCallback>(interceptor->definer()); |
| Handle<InterceptorInfo> has_side_effects; |
| PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Value, has_side_effects); |
| f(index, desc, callback_info); |
| return GetReturnValue<Object>(isolate); |
| } |
| |
| Handle<Object> PropertyCallbackArguments::CallIndexedDeleter( |
| Handle<InterceptorInfo> interceptor, uint32_t index) { |
| DCHECK(!interceptor->is_named()); |
| Isolate* isolate = this->isolate(); |
| RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedDeleterCallback); |
| IndexedPropertyDeleterCallback f = |
| ToCData<IndexedPropertyDeleterCallback>(interceptor->deleter()); |
| PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Boolean, interceptor); |
| f(index, callback_info); |
| return GetReturnValue<Object>(isolate); |
| } |
| |
| Handle<JSObject> PropertyCallbackArguments::CallPropertyEnumerator( |
| Handle<InterceptorInfo> interceptor) { |
| // For now there is a single enumerator for indexed and named properties. |
| IndexedPropertyEnumeratorCallback f = |
| v8::ToCData<IndexedPropertyEnumeratorCallback>(interceptor->enumerator()); |
| // TODO(cbruni): assert same type for indexed and named callback. |
| Isolate* isolate = this->isolate(); |
| PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Array, interceptor); |
| f(callback_info); |
| return GetReturnValue<JSObject>(isolate); |
| } |
| |
| // ------------------------------------------------------------------------- |
| // Accessors |
| |
| Handle<Object> PropertyCallbackArguments::CallAccessorGetter( |
| Handle<AccessorInfo> info, Handle<Name> name) { |
| Isolate* isolate = this->isolate(); |
| RCS_SCOPE(isolate, RuntimeCallCounterId::kAccessorGetterCallback); |
| // Unlike interceptor callbacks we know that the property exists, so |
| // the callback is allowed to have side effects. |
| AcceptSideEffects(); |
| |
| AccessorNameGetterCallback f = |
| reinterpret_cast<AccessorNameGetterCallback>(info->getter()); |
| PREPARE_CALLBACK_INFO_ACCESSOR(isolate, f, v8::Value, info, |
| handle(receiver(), isolate), ACCESSOR_GETTER); |
| f(v8::Utils::ToLocal(name), callback_info); |
| return GetReturnValue<Object>(isolate); |
| } |
| |
| Handle<Object> PropertyCallbackArguments::CallAccessorSetter( |
| Handle<AccessorInfo> accessor_info, Handle<Name> name, |
| Handle<Object> value) { |
| Isolate* isolate = this->isolate(); |
| RCS_SCOPE(isolate, RuntimeCallCounterId::kAccessorSetterCallback); |
| // Unlike interceptor callbacks we know that the property exists, so |
| // the callback is allowed to have side effects. |
| AcceptSideEffects(); |
| |
| AccessorNameSetterCallback f = |
| reinterpret_cast<AccessorNameSetterCallback>(accessor_info->setter()); |
| PREPARE_CALLBACK_INFO_ACCESSOR(isolate, f, void, accessor_info, |
| handle(receiver(), isolate), ACCESSOR_SETTER); |
| f(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), callback_info); |
| return GetReturnValue<Object>(isolate); |
| } |
| |
| #undef PREPARE_CALLBACK_INFO_ACCESSOR |
| #undef PREPARE_CALLBACK_INFO_INTERCEPTOR |
| |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_API_API_ARGUMENTS_INL_H_ |