| // 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_H_ |
| #define V8_API_API_ARGUMENTS_H_ |
| |
| #include "include/v8-template.h" |
| #include "src/base/small-vector.h" |
| #include "src/builtins/builtins-utils.h" |
| #include "src/execution/isolate.h" |
| #include "src/objects/slots.h" |
| #include "src/objects/visitors.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| // This class also serves as a side effects detection scope (JavaScript code |
| // execution). It is used for ensuring correctness of the interceptor callback |
| // implementations. The idea is that the interceptor callback that does not |
| // intercept an operation must not produce side effects. If the callback |
| // signals that it has handled the operation (by either returning a respective |
| // result or by throwing an exception) then the AcceptSideEffects() method |
| // must be called to "accept" the side effects that have happened during the |
| // lifetime of the PropertyCallbackArguments object. |
| class PropertyCallbackArguments final : public Relocatable { |
| public: |
| using T = PropertyCallbackInfo<Value>; |
| using Super = CustomArguments<T>; |
| static constexpr int kMandatoryArgsLength = T::kMandatoryArgsLength; |
| static constexpr int kFullArgsLength = T::kFullArgsLength; |
| static constexpr int kMandatoryApiArgsLength = T::kMandatoryApiArgsLength; |
| static constexpr int kFullApiArgsLength = T::kFullApiArgsLength; |
| |
| static constexpr int kGetterApiArgsLength = T::kMandatoryApiArgsLength; |
| static constexpr int kSetterApiArgsLength = T::kFullApiArgsLength; |
| |
| static constexpr int kFrameTypeIndex = T::kFrameTypeIndex; |
| static constexpr int kCallbackInfoIndex = T::kCallbackInfoIndex; |
| static constexpr int kHolderIndex = T::kHolderIndex; |
| static constexpr int kIsolateIndex = T::kIsolateIndex; |
| static constexpr int kReturnValueIndex = T::kReturnValueIndex; |
| static constexpr int kShouldThrowOnErrorIndex = T::kShouldThrowOnErrorIndex; |
| static constexpr int kPropertyKeyIndex = T::kPropertyKeyIndex; |
| static constexpr int kValueIndex = T::kValueIndex; |
| |
| // Helper for converting Api arguments indices to [0..kFullApiArgsLength) |
| // value. |
| static constexpr uint32_t ApiArgIndex(uint32_t index) { |
| DCHECK_GE(index, T::kFirstApiArgumentIndex); |
| return index - T::kFirstApiArgumentIndex; |
| } |
| |
| // This constructor leaves kPropertyKeyIndex, kReturnValueIndex and |
| // kCallbackInfoIndex slots uninitialized in order to let them be |
| // initialized by the subsequent CallXXX(..) and avoid double initialization. |
| // As a consequence, there must be no GC call between this constructor and |
| // CallXXX(..). In debug mode these slots are zapped, so GC should be able |
| // to detect misuse of this object. |
| inline PropertyCallbackArguments(Isolate* isolate, Tagged<JSObject> holder); |
| inline PropertyCallbackArguments(Isolate* isolate, Tagged<JSObject> holder, |
| Maybe<ShouldThrow> should_throw); |
| inline ~PropertyCallbackArguments(); |
| |
| // Don't copy PropertyCallbackArguments, because they would both have the |
| // same prev_ pointer. |
| PropertyCallbackArguments(const PropertyCallbackArguments&) = delete; |
| PropertyCallbackArguments& operator=(const PropertyCallbackArguments&) = |
| delete; |
| |
| // ------------------------------------------------------------------------- |
| // Accessor Callbacks |
| |
| // Returns the result of [[Get]] operation or throws an exception. |
| // In case of exception empty handle is returned. |
| // TODO(ishell, 328490288): stop returning empty handles. |
| inline DirectHandle<JSAny> CallAccessorGetter(Isolate* isolate, |
| DirectHandle<AccessorInfo> info, |
| DirectHandle<Name> name); |
| // Returns the result of [[Set]] operation or throws an exception. |
| V8_WARN_UNUSED_RESULT |
| inline bool CallAccessorSetter(Isolate* isolate, |
| DirectHandle<AccessorInfo> info, |
| DirectHandle<Name> name, |
| DirectHandle<Object> value); |
| |
| // ------------------------------------------------------------------------- |
| // Named Interceptor Callbacks |
| |
| // Empty handle means that the request was not intercepted. |
| // Pending exception handling should be done by the caller. |
| inline DirectHandle<Object> CallNamedQuery( |
| Isolate* isolate, DirectHandle<InterceptorInfo> interceptor, |
| DirectHandle<Name> name); |
| inline DirectHandle<JSAny> CallNamedGetter( |
| Isolate* isolate, DirectHandle<InterceptorInfo> interceptor, |
| DirectHandle<Name> name); |
| |
| // Calls Setter/Definer/Deleter callback and returns whether the request |
| // was intercepted. |
| // Pending exception handling and interpretation of the result should be |
| // done by the caller using GetBooleanReturnValue(..). |
| inline v8::Intercepted CallNamedSetter( |
| Isolate* isolate, DirectHandle<InterceptorInfo> interceptor, |
| DirectHandle<Name> name, DirectHandle<Object> value); |
| inline v8::Intercepted CallNamedDefiner( |
| Isolate* isolate, DirectHandle<InterceptorInfo> interceptor, |
| DirectHandle<Name> name, const v8::PropertyDescriptor& desc); |
| inline v8::Intercepted CallNamedDeleter( |
| Isolate* isolate, DirectHandle<InterceptorInfo> interceptor, |
| DirectHandle<Name> name); |
| |
| // Empty handle means that the request was not intercepted. |
| // Pending exception handling should be done by the caller. |
| inline Handle<JSAny> CallNamedDescriptor( |
| Isolate* isolate, DirectHandle<InterceptorInfo> interceptor, |
| DirectHandle<Name> name); |
| // Returns JSArray-like object with property names or undefined. |
| inline DirectHandle<JSObjectOrUndefined> CallNamedEnumerator( |
| Isolate* isolate, DirectHandle<InterceptorInfo> interceptor); |
| |
| // ------------------------------------------------------------------------- |
| // Indexed Interceptor Callbacks |
| |
| // Empty handle means that the request was not intercepted. |
| // Pending exception handling should be done by the caller. |
| inline DirectHandle<Object> CallIndexedQuery( |
| Isolate* isolate, DirectHandle<InterceptorInfo> interceptor, |
| uint32_t index); |
| inline DirectHandle<JSAny> CallIndexedGetter( |
| Isolate* isolate, DirectHandle<InterceptorInfo> interceptor, |
| uint32_t index); |
| |
| // Calls Setter/Definer/Deleter callback and returns whether the request |
| // was intercepted. |
| // Pending exception handling and interpretation of the result should be |
| // done by the caller using GetBooleanReturnValue(..). |
| inline v8::Intercepted CallIndexedSetter( |
| Isolate* isolate, DirectHandle<InterceptorInfo> interceptor, |
| uint32_t index, DirectHandle<Object> value); |
| inline v8::Intercepted CallIndexedDefiner( |
| Isolate* isolate, DirectHandle<InterceptorInfo> interceptor, |
| uint32_t index, const v8::PropertyDescriptor& desc); |
| inline v8::Intercepted CallIndexedDeleter( |
| Isolate* isolate, DirectHandle<InterceptorInfo> interceptor, |
| uint32_t index); |
| |
| // Empty handle means that the request was not intercepted. |
| // Pending exception handling should be done by the caller. |
| inline Handle<JSAny> CallIndexedDescriptor( |
| Isolate* isolate, DirectHandle<InterceptorInfo> interceptor, |
| uint32_t index); |
| // Returns JSArray-like object with property names or undefined. |
| inline DirectHandle<JSObjectOrUndefined> CallIndexedEnumerator( |
| Isolate* isolate, DirectHandle<InterceptorInfo> interceptor); |
| |
| // Pending exception handling should be done by the caller. |
| inline uint32_t CallIndexedIndexOf(Isolate* isolate, |
| DirectHandle<InterceptorInfo> interceptor, |
| DirectHandle<Object> value, |
| uint32_t start_index, uint32_t end_index, |
| uint32_t* in_out_length); |
| |
| // Pending exception handling should be done by the caller. |
| inline DirectHandle<JSAny> CallIndexedIterableToList( |
| Isolate* isolate, DirectHandle<InterceptorInfo> interceptor); |
| |
| // Accept potential JavaScript side effects that might occur during life |
| // time of this object. |
| inline void AcceptSideEffects() { |
| #ifdef DEBUG |
| javascript_execution_counter_ = 0; |
| #endif // DEBUG |
| } |
| |
| // Converts the result of Setter/Definer/Deleter interceptor callback to |
| // Maybe<InterceptorResult>. |
| // Currently, in certain scenarios the actual boolean result returned by |
| // the Setter/Definer operation is ignored and thus we don't need to process |
| // the actual return value. |
| inline Maybe<InterceptorResult> GetBooleanReturnValue( |
| Isolate* isolate, v8::Intercepted intercepted, |
| const char* callback_kind_for_error_message, |
| bool ignore_return_value = false); |
| |
| // TODO(ishell): cleanup this hack by embedding the PropertyCallbackInfo |
| // into PropertyCallbackArguments object. |
| template <typename T> |
| const v8::PropertyCallbackInfo<T>& GetPropertyCallbackInfo() { |
| return *(reinterpret_cast<PropertyCallbackInfo<T>*>(&values_[0])); |
| } |
| |
| // Forwards ShouldThrowOnError() request to the underlying |
| // v8::PropertyCallbackInfo<> object. |
| bool ShouldThrowOnError() { |
| return GetPropertyCallbackInfo<Value>().ShouldThrowOnError(); |
| } |
| |
| // Returns AccessorInfo stored in v8::PropertyCallbackInfo<T>. |
| template <typename T> |
| static DirectHandle<AccessorInfo> GetAccessorInfo( |
| const PropertyCallbackInfo<T>& info) { |
| return Cast<AccessorInfo>( |
| DirectHandle<Object>::FromSlot(&info.args_[kCallbackInfoIndex])); |
| } |
| |
| // Returns InterceptorInfo stored in v8::PropertyCallbackInfo<T>. |
| template <typename T> |
| static DirectHandle<InterceptorInfo> GetInterceptorInfo( |
| const PropertyCallbackInfo<T>& info) { |
| return Cast<InterceptorInfo>( |
| DirectHandle<Object>::FromSlot(&info.args_[kCallbackInfoIndex])); |
| } |
| |
| // Returns whether given v8::PropertyCallbackInfo<T> object is named/indexed. |
| template <typename T> |
| static bool IsNamed(const PropertyCallbackInfo<T>& info) { |
| return info.IsNamed(); |
| } |
| |
| // Returns property name stored in v8::PropertyCallbackInfo<T> (for named |
| // accessors/interceptors). |
| template <typename T> |
| static DirectHandle<Name> GetPropertyName( |
| const PropertyCallbackInfo<T>& info) { |
| DCHECK(info.IsNamed()); |
| return Cast<Name>( |
| DirectHandle<Object>::FromSlot(&info.args_[kPropertyKeyIndex])); |
| } |
| |
| // Returns property index stored in v8::PropertyCallbackInfo<T> (for indexed |
| // interceptors). |
| template <typename T> |
| static uint32_t GetPropertyIndex(const PropertyCallbackInfo<T>& info) { |
| DCHECK(!info.IsNamed()); |
| return static_cast<uint32_t>(info.args_[kPropertyKeyIndex]); |
| } |
| |
| // Returns true if it's an arguments object for named callback, otherwise |
| // it's one for an indexed callback. |
| inline bool is_named() const; |
| |
| // Set property key and a respective frame type (named vs. indexed). |
| inline void set_property_key(Tagged<Name> name); |
| inline void set_property_key(uint32_t index); |
| |
| inline DirectHandle<JSObject> holder() const; |
| |
| private: |
| inline void Initialize(Isolate* isolate, Tagged<JSObject> holder); |
| // Returns JSArray-like object with property names or undefined. |
| inline DirectHandle<JSObjectOrUndefined> CallPropertyEnumerator( |
| Isolate* isolate, DirectHandle<InterceptorInfo> interceptor); |
| |
| void IterateInstance(RootVisitor* v) override; |
| |
| template <typename V> |
| Handle<V> GetReturnValue() const; |
| |
| inline FullObjectSlot slot_at(int index) const { |
| // This allows index == kFullArgsLength so "one past the end" slots |
| // can be retrieved for iterating purposes. |
| DCHECK_LE(static_cast<unsigned>(index), |
| static_cast<unsigned>(kFullArgsLength)); |
| return FullObjectSlot(values_ + index); |
| } |
| |
| #ifdef DEBUG |
| // Used for checking that the way this object was constructed matches the |
| // following CallXxx(..). |
| const bool is_setter_definer_deleter_; |
| |
| // This stores current value of Isolate::javascript_execution_counter(). |
| // It's used for detecting whether JavaScript code was executed between |
| // PropertyCallbackArguments's constructor and destructor. |
| uint32_t javascript_execution_counter_ = 0; |
| #endif // DEBUG |
| |
| Address values_[kFullArgsLength]; |
| }; |
| |
| class FunctionCallbackArguments : public Relocatable { |
| public: |
| using T = FunctionCallbackInfo<Value>; |
| using Super = CustomArguments<T>; |
| static constexpr int kArgsLength = T::kArgsLength; |
| |
| // Frame arguments block, the values are located on stack in the frame. |
| static constexpr int kArgcIndex = T::kArgcIndex; |
| static constexpr int kNewTargetIndex = T::kNewTargetIndex; |
| static constexpr int kFrameSPIndex = T::kFrameSPIndex; |
| static constexpr int kFrameTypeIndex = T::kFrameTypeIndex; |
| |
| // Api arguments block, the values are located on stack right above PC. |
| static constexpr int kIsolateIndex = T::kIsolateIndex; |
| static constexpr int kReturnValueIndex = T::kReturnValueIndex; |
| static constexpr int kContextIndex = T::kContextIndex; |
| static constexpr int kTargetIndex = T::kTargetIndex; |
| static constexpr int kApiArgsLength = T::kApiArgsLength; |
| |
| // JS arguments block, follows Api arguments. |
| static constexpr int kReceiverIndex = T::kReceiverIndex; |
| static constexpr int kFirstJSArgumentIndex = T::kFirstJSArgumentIndex; |
| |
| // Helper for converting Api arguments indices to [0..kApiArgsLength) value. |
| static constexpr uint32_t ApiArgIndex(uint32_t index) { |
| DCHECK_GE(index, T::kFirstApiArgumentIndex); |
| return index - T::kFirstApiArgumentIndex; |
| } |
| |
| // Arguments for [[Call]] operation. |
| template <typename ArgT> |
| inline FunctionCallbackArguments(Isolate* isolate, |
| Tagged<FunctionTemplateInfo> target, |
| Tagged<Object> receiver, |
| const base::Vector<const ArgT> args); |
| // Arguments for [[Construct]] operation. |
| template <typename ArgT> |
| inline FunctionCallbackArguments(Isolate* isolate, |
| Tagged<FunctionTemplateInfo> target, |
| Tagged<HeapObject> new_target, |
| Tagged<Object> receiver, |
| const base::Vector<const ArgT> args); |
| inline ~FunctionCallbackArguments(); |
| |
| // Performs [[Call]] of [[Construct]] operation for a given function |
| // and new_target. |
| // Exception is supposed to be checked by the caller. |
| // It explicitly returns raw value in order to enforce the caller to create |
| // a handle if necessary. |
| inline Tagged<JSAny> CallOrConstruct(Isolate* isolate, |
| Tagged<FunctionTemplateInfo> function, |
| bool is_construct); |
| |
| // Unofficial way of getting target FunctionTemplateInfo from |
| // v8::FunctionCallbackInfo<T>. |
| template <typename T> |
| static Tagged<Object> GetTarget(const FunctionCallbackInfo<T>& info) { |
| return Tagged<Object>(info.values_[kTargetIndex]); |
| } |
| |
| private: |
| template <bool is_construct, typename ArgT> |
| requires(std::is_same_v<ArgT, DirectHandle<Object>> || |
| std::is_same_v<ArgT, Address>) |
| inline void Initialize(Isolate* isolate, Tagged<FunctionTemplateInfo> target, |
| Tagged<Object> new_target, Tagged<Object> receiver, |
| const base::Vector<const ArgT> args); |
| |
| inline FullObjectSlot slot_at(uint32_t index) const { |
| // Shift index to accommodate for unconditionally allocated "optional" part. |
| index += T::kOptionalArgsLength; |
| // This allows index == values_.size() so "one past the end" slots |
| // can be retrieved for iterating purposes. |
| DCHECK_LE(index, values_.size()); |
| // Don't use operator[] because it doesn't allow one past end index. |
| return FullObjectSlot(&values_.data()[index]); |
| } |
| |
| void IterateInstance(RootVisitor* v) override; |
| |
| // This default size is enough for passing up to 4 JS arguments. |
| base::SmallVector<Address, 16> values_; |
| }; |
| |
| static_assert(BuiltinArguments::kNumExtraArgs == |
| BuiltinExitFrameConstants::kNumExtraArgs); |
| static_assert(BuiltinArguments::kNumExtraArgsWithReceiver == |
| BuiltinExitFrameConstants::kNumExtraArgsWithReceiver); |
| |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_API_API_ARGUMENTS_H_ |