// Copyright 2021 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 INCLUDE_V8_FUNCTION_CALLBACK_H_ #define INCLUDE_V8_FUNCTION_CALLBACK_H_ #include "v8-local-handle.h" // NOLINT(build/include_directory) #include "v8-primitive.h" // NOLINT(build/include_directory) #include "v8config.h" // NOLINT(build/include_directory) namespace v8 { template class BasicTracedReference; template class Global; class Object; class Value; namespace internal { class FunctionCallbackArguments; class PropertyCallbackArguments; class Builtins; } // namespace internal namespace debug { class ConsoleCallArguments; } // namespace debug template class ReturnValue { public: template V8_INLINE ReturnValue(const ReturnValue& that) : value_(that.value_) { static_assert(std::is_base_of::value, "type check"); } // Local setters template V8_INLINE void Set(const Global& handle); template V8_INLINE void Set(const BasicTracedReference& handle); template V8_INLINE void Set(const Local handle); // Fast primitive setters V8_INLINE void Set(bool value); V8_INLINE void Set(double i); V8_INLINE void Set(int32_t i); V8_INLINE void Set(uint32_t i); // Fast JS primitive setters V8_INLINE void SetNull(); V8_INLINE void SetUndefined(); V8_INLINE void SetEmptyString(); // Convenience getter for Isolate V8_INLINE Isolate* GetIsolate() const; // Pointer setter: Uncompilable to prevent inadvertent misuse. template V8_INLINE void Set(S* whatever); // Getter. Creates a new Local<> so it comes with a certain performance // hit. If the ReturnValue was not yet set, this will return the undefined // value. V8_INLINE Local Get() const; private: template friend class ReturnValue; template friend class FunctionCallbackInfo; template friend class PropertyCallbackInfo; template friend class PersistentValueMapBase; V8_INLINE void SetInternal(internal::Address value) { *value_ = value; } V8_INLINE internal::Address GetDefaultValue(); V8_INLINE explicit ReturnValue(internal::Address* slot); // See FunctionCallbackInfo. static constexpr int kIsolateValueIndex = -2; internal::Address* value_; }; /** * The argument information given to function call callbacks. This * class provides access to information about the context of the call, * including the receiver, the number and values of arguments, and * the holder of the function. */ template class FunctionCallbackInfo { public: /** The number of available arguments. */ V8_INLINE int Length() const; /** * Accessor for the available arguments. Returns `undefined` if the index * is out of bounds. */ V8_INLINE Local operator[](int i) const; /** Returns the receiver. This corresponds to the "this" value. */ V8_INLINE Local This() const; /** * If the callback was created without a Signature, this is the same * value as This(). If there is a signature, and the signature didn't match * This() but one of its hidden prototypes, this will be the respective * hidden prototype. * * Note that this is not the prototype of This() on which the accessor * referencing this callback was found (which in V8 internally is often * referred to as holder [sic]). */ V8_INLINE Local Holder() const; /** For construct calls, this returns the "new.target" value. */ V8_INLINE Local NewTarget() const; /** Indicates whether this is a regular call or a construct call. */ V8_INLINE bool IsConstructCall() const; /** The data argument specified when creating the callback. */ V8_INLINE Local Data() const; /** The current Isolate. */ V8_INLINE Isolate* GetIsolate() const; /** The ReturnValue for the call. */ V8_INLINE ReturnValue GetReturnValue() const; private: friend class internal::FunctionCallbackArguments; friend class internal::CustomArguments; friend class debug::ConsoleCallArguments; friend class internal::Builtins; static constexpr int kHolderIndex = 0; static constexpr int kIsolateIndex = 1; static constexpr int kUnusedIndex = 2; static constexpr int kReturnValueIndex = 3; static constexpr int kDataIndex = 4; static constexpr int kNewTargetIndex = 5; static constexpr int kArgsLength = 6; static constexpr int kArgsLengthWithReceiver = kArgsLength + 1; // Codegen constants: static constexpr int kSize = 3 * internal::kApiSystemPointerSize; static constexpr int kImplicitArgsOffset = 0; static constexpr int kValuesOffset = kImplicitArgsOffset + internal::kApiSystemPointerSize; static constexpr int kLengthOffset = kValuesOffset + internal::kApiSystemPointerSize; static constexpr int kThisValuesIndex = -1; static_assert(ReturnValue::kIsolateValueIndex == kIsolateIndex - kReturnValueIndex); V8_INLINE FunctionCallbackInfo(internal::Address* implicit_args, internal::Address* values, int length); internal::Address* implicit_args_; internal::Address* values_; int length_; }; /** * The information passed to a property callback about the context * of the property access. */ template class PropertyCallbackInfo { public: /** * \return The isolate of the property access. */ V8_INLINE Isolate* GetIsolate() const; /** * \return The data set in the configuration, i.e., in * `NamedPropertyHandlerConfiguration` or * `IndexedPropertyHandlerConfiguration.` */ V8_INLINE Local Data() const; /** * \return The receiver. In many cases, this is the object on which the * property access was intercepted. When using * `Reflect.get`, `Function.prototype.call`, or similar functions, it is the * object passed in as receiver or thisArg. * * \code * void GetterCallback(Local name, * const v8::PropertyCallbackInfo& info) { * auto context = info.GetIsolate()->GetCurrentContext(); * * v8::Local a_this = * info.This() * ->GetRealNamedProperty(context, v8_str("a")) * .ToLocalChecked(); * v8::Local a_holder = * info.Holder() * ->GetRealNamedProperty(context, v8_str("a")) * .ToLocalChecked(); * * CHECK(v8_str("r")->Equals(context, a_this).FromJust()); * CHECK(v8_str("obj")->Equals(context, a_holder).FromJust()); * * info.GetReturnValue().Set(name); * } * * v8::Local templ = * v8::FunctionTemplate::New(isolate); * templ->InstanceTemplate()->SetHandler( * v8::NamedPropertyHandlerConfiguration(GetterCallback)); * LocalContext env; * env->Global() * ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local()) * .ToLocalChecked() * ->NewInstance(env.local()) * .ToLocalChecked()) * .FromJust(); * * CompileRun("obj.a = 'obj'; var r = {a: 'r'}; Reflect.get(obj, 'x', r)"); * \endcode */ V8_INLINE Local This() const; /** * \return The object in the prototype chain of the receiver that has the * interceptor. Suppose you have `x` and its prototype is `y`, and `y` * has an interceptor. Then `info.This()` is `x` and `info.Holder()` is `y`. * The Holder() could be a hidden object (the global object, rather * than the global proxy). * * \note For security reasons, do not pass the object back into the runtime. */ V8_INLINE Local Holder() const; /** * \return The return value of the callback. * Can be changed by calling Set(). * \code * info.GetReturnValue().Set(...) * \endcode * */ V8_INLINE ReturnValue GetReturnValue() const; /** * \return True if the intercepted function should throw if an error occurs. * Usually, `true` corresponds to `'use strict'`. * * \note Always `false` when intercepting `Reflect.set()` * independent of the language mode. */ V8_INLINE bool ShouldThrowOnError() const; private: friend class MacroAssembler; friend class internal::PropertyCallbackArguments; friend class internal::CustomArguments; static constexpr int kShouldThrowOnErrorIndex = 0; static constexpr int kHolderIndex = 1; static constexpr int kIsolateIndex = 2; static constexpr int kUnusedIndex = 3; static constexpr int kReturnValueIndex = 4; static constexpr int kDataIndex = 5; static constexpr int kThisIndex = 6; static constexpr int kArgsLength = 7; static constexpr int kSize = 1 * internal::kApiSystemPointerSize; V8_INLINE explicit PropertyCallbackInfo(internal::Address* args) : args_(args) {} internal::Address* args_; }; using FunctionCallback = void (*)(const FunctionCallbackInfo& info); // --- Implementation --- template ReturnValue::ReturnValue(internal::Address* slot) : value_(slot) {} template template void ReturnValue::Set(const Global& handle) { static_assert(std::is_base_of::value, "type check"); if (V8_UNLIKELY(handle.IsEmpty())) { *value_ = GetDefaultValue(); } else { *value_ = handle.ptr(); } } template template void ReturnValue::Set(const BasicTracedReference& handle) { static_assert(std::is_base_of::value, "type check"); if (V8_UNLIKELY(handle.IsEmpty())) { *value_ = GetDefaultValue(); } else { *value_ = handle.ptr(); } } template template void ReturnValue::Set(const Local handle) { static_assert(std::is_void::value || std::is_base_of::value, "type check"); if (V8_UNLIKELY(handle.IsEmpty())) { *value_ = GetDefaultValue(); } else { *value_ = handle.ptr(); } } template void ReturnValue::Set(double i) { static_assert(std::is_base_of::value, "type check"); Set(Number::New(GetIsolate(), i)); } template void ReturnValue::Set(int32_t i) { static_assert(std::is_base_of::value, "type check"); using I = internal::Internals; if (V8_LIKELY(I::IsValidSmi(i))) { *value_ = I::IntToSmi(i); return; } Set(Integer::New(GetIsolate(), i)); } template void ReturnValue::Set(uint32_t i) { static_assert(std::is_base_of::value, "type check"); // Can't simply use INT32_MAX here for whatever reason. bool fits_into_int32_t = (i & (1U << 31)) == 0; if (V8_LIKELY(fits_into_int32_t)) { Set(static_cast(i)); return; } Set(Integer::NewFromUnsigned(GetIsolate(), i)); } template void ReturnValue::Set(bool value) { static_assert(std::is_base_of::value, "type check"); using I = internal::Internals; int root_index; if (value) { root_index = I::kTrueValueRootIndex; } else { root_index = I::kFalseValueRootIndex; } *value_ = I::GetRoot(GetIsolate(), root_index); } template void ReturnValue::SetNull() { static_assert(std::is_base_of::value, "type check"); using I = internal::Internals; *value_ = I::GetRoot(GetIsolate(), I::kNullValueRootIndex); } template void ReturnValue::SetUndefined() { static_assert(std::is_base_of::value, "type check"); using I = internal::Internals; *value_ = I::GetRoot(GetIsolate(), I::kUndefinedValueRootIndex); } template void ReturnValue::SetEmptyString() { static_assert(std::is_base_of::value, "type check"); using I = internal::Internals; *value_ = I::GetRoot(GetIsolate(), I::kEmptyStringRootIndex); } template Isolate* ReturnValue::GetIsolate() const { return *reinterpret_cast(&value_[kIsolateValueIndex]); } template Local ReturnValue::Get() const { using I = internal::Internals; #if V8_STATIC_ROOTS_BOOL if (I::is_identical(*value_, I::StaticReadOnlyRoot::kTheHoleValue)) { #else if (*value_ == I::GetRoot(GetIsolate(), I::kTheHoleValueRootIndex)) { #endif return Undefined(GetIsolate()); } return Local::New(GetIsolate(), reinterpret_cast(value_)); } template template void ReturnValue::Set(S* whatever) { static_assert(sizeof(S) < 0, "incompilable to prevent inadvertent misuse"); } template internal::Address ReturnValue::GetDefaultValue() { using I = internal::Internals; return I::GetRoot(GetIsolate(), I::kTheHoleValueRootIndex); } template FunctionCallbackInfo::FunctionCallbackInfo(internal::Address* implicit_args, internal::Address* values, int length) : implicit_args_(implicit_args), values_(values), length_(length) {} template Local FunctionCallbackInfo::operator[](int i) const { // values_ points to the first argument (not the receiver). if (i < 0 || length_ <= i) return Undefined(GetIsolate()); return Local::FromSlot(values_ + i); } template Local FunctionCallbackInfo::This() const { // values_ points to the first argument (not the receiver). return Local::FromSlot(values_ + kThisValuesIndex); } template Local FunctionCallbackInfo::Holder() const { return Local::FromSlot(&implicit_args_[kHolderIndex]); } template Local FunctionCallbackInfo::NewTarget() const { return Local::FromSlot(&implicit_args_[kNewTargetIndex]); } template Local FunctionCallbackInfo::Data() const { return Local::FromSlot(&implicit_args_[kDataIndex]); } template Isolate* FunctionCallbackInfo::GetIsolate() const { return *reinterpret_cast(&implicit_args_[kIsolateIndex]); } template ReturnValue FunctionCallbackInfo::GetReturnValue() const { return ReturnValue(&implicit_args_[kReturnValueIndex]); } template bool FunctionCallbackInfo::IsConstructCall() const { return !NewTarget()->IsUndefined(); } template int FunctionCallbackInfo::Length() const { return length_; } template Isolate* PropertyCallbackInfo::GetIsolate() const { return *reinterpret_cast(&args_[kIsolateIndex]); } template Local PropertyCallbackInfo::Data() const { return Local::FromSlot(&args_[kDataIndex]); } template Local PropertyCallbackInfo::This() const { return Local::FromSlot(&args_[kThisIndex]); } template Local PropertyCallbackInfo::Holder() const { return Local::FromSlot(&args_[kHolderIndex]); } template ReturnValue PropertyCallbackInfo::GetReturnValue() const { return ReturnValue(&args_[kReturnValueIndex]); } template bool PropertyCallbackInfo::ShouldThrowOnError() const { using I = internal::Internals; if (args_[kShouldThrowOnErrorIndex] != I::IntToSmi(I::kInferShouldThrowMode)) { return args_[kShouldThrowOnErrorIndex] != I::IntToSmi(I::kDontThrow); } return v8::internal::ShouldThrowOnError( reinterpret_cast(GetIsolate())); } } // namespace v8 #endif // INCLUDE_V8_FUNCTION_CALLBACK_H_