blob: d94d60ba98d5b85a7e638b1326fe2fdb753b4833 [file]
// 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_