[fastcall] Introduce a builder pattern for specifying type modifiers

- Add a CFunctionBuilder interface to allow adding modifier flags
  to argument types. This will be used to support IDL attributes
  like [EnforceRange], [Clamp], and [AllowShared]. This CL adds
  only the interface, but the actual modifier flags do not exist
  yet as they would not be implemented.
- Remove the internals of the old CFunction type inference and
  implement it on top of CFunctionBuilder.

Bug: chromium:1052746
Change-Id: I09a7cba07105097517a8426a8eeb891393883ac6
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2686686
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Reviewed-by: Maya Lekova <mslekova@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
Cr-Commit-Position: refs/heads/master@{#73024}
This commit is contained in:
Austin Eng 2021-02-10 00:47:06 -08:00 committed by Commit Bot
parent d98b12d3df
commit db34c5a140
3 changed files with 271 additions and 158 deletions

View File

@ -205,39 +205,106 @@ class CTypeInfo {
kV8Value,
};
// kCallbackOptionsType and kInvalidType are not part of the Type enum
// because they are only used internally. Use values 255 and 254 that
// are larger than any valid Type enum.
// kCallbackOptionsType is not part of the Type enum
// because it is only used internally. Use value 255 that is larger
// than any valid Type enum.
static constexpr Type kCallbackOptionsType = Type(255);
static constexpr Type kInvalidType = Type(254);
enum class ArgFlags : uint8_t {
enum class Flags : uint8_t {
kNone = 0,
};
explicit constexpr CTypeInfo(Type type, ArgFlags flags = ArgFlags::kNone)
explicit constexpr CTypeInfo(Type type, Flags flags = Flags::kNone)
: type_(type), flags_(flags) {}
constexpr Type GetType() const { return type_; }
constexpr ArgFlags GetFlags() const { return flags_; }
static const CTypeInfo& Invalid() {
static CTypeInfo invalid = CTypeInfo(kInvalidType);
return invalid;
}
constexpr Flags GetFlags() const { return flags_; }
private:
Type type_;
ArgFlags flags_;
Flags flags_;
};
class CFunctionInfo {
class V8_EXPORT CFunctionInfo {
public:
virtual const CTypeInfo& ReturnInfo() const = 0;
virtual unsigned int ArgumentCount() const = 0;
virtual const CTypeInfo& ArgumentInfo(unsigned int index) const = 0;
virtual bool HasOptions() const = 0;
// Construct a struct to hold a CFunction's type information.
// |return_info| describes the function's return type.
// |arg_info| is an array of |arg_count| CTypeInfos describing the
// arguments. Only the last argument may be of the special type
// CTypeInfo::kCallbackOptionsType.
CFunctionInfo(const CTypeInfo& return_info, unsigned int arg_count,
const CTypeInfo* arg_info);
const CTypeInfo& ReturnInfo() const { return return_info_; }
// The argument count, not including the v8::FastApiCallbackOptions
// if present.
unsigned int ArgumentCount() const {
return HasOptions() ? arg_count_ - 1 : arg_count_;
}
// |index| must be less than ArgumentCount().
// Note: if the last argument passed on construction of CFunctionInfo
// has type CTypeInfo::kCallbackOptionsType, it is not included in
// ArgumentCount().
const CTypeInfo& ArgumentInfo(unsigned int index) const;
bool HasOptions() const {
// The options arg is always the last one.
return arg_count_ > 0 && arg_info_[arg_count_ - 1].GetType() ==
CTypeInfo::kCallbackOptionsType;
}
private:
const CTypeInfo return_info_;
const unsigned int arg_count_;
const CTypeInfo* arg_info_;
};
class V8_EXPORT CFunction {
public:
constexpr CFunction() : address_(nullptr), type_info_(nullptr) {}
const CTypeInfo& ReturnInfo() const { return type_info_->ReturnInfo(); }
const CTypeInfo& ArgumentInfo(unsigned int index) const {
return type_info_->ArgumentInfo(index);
}
unsigned int ArgumentCount() const { return type_info_->ArgumentCount(); }
const void* GetAddress() const { return address_; }
const CFunctionInfo* GetTypeInfo() const { return type_info_; }
template <typename F>
static CFunction Make(F* func) {
return ArgUnwrap<F*>::Make(func);
}
template <typename F>
V8_DEPRECATED("Use CFunctionBuilder instead.")
static CFunction MakeWithFallbackSupport(F* func) {
return ArgUnwrap<F*>::Make(func);
}
CFunction(const void* address, const CFunctionInfo* type_info);
private:
const void* address_;
const CFunctionInfo* type_info_;
template <typename F>
class ArgUnwrap {
static_assert(sizeof(F) != sizeof(F),
"CFunction must be created from a function pointer.");
};
template <typename R, typename... Args>
class ArgUnwrap<R (*)(Args...)> {
public:
static CFunction Make(R (*func)(Args...));
};
};
struct ApiObject {
@ -272,37 +339,6 @@ struct FastApiCallbackOptions {
namespace internal {
template <typename T>
struct GetCType;
#define SPECIALIZE_GET_C_TYPE_FOR(ctype, ctypeinfo) \
template <> \
struct GetCType<ctype> { \
static constexpr CTypeInfo Get() { \
return CTypeInfo(CTypeInfo::Type::ctypeinfo); \
} \
};
#define SUPPORTED_C_TYPES(V) \
V(void, kVoid) \
V(bool, kBool) \
V(int32_t, kInt32) \
V(uint32_t, kUint32) \
V(int64_t, kInt64) \
V(uint64_t, kUint64) \
V(float, kFloat32) \
V(double, kFloat64) \
V(ApiObject, kV8Value)
SUPPORTED_C_TYPES(SPECIALIZE_GET_C_TYPE_FOR)
template <>
struct GetCType<FastApiCallbackOptions&> {
static constexpr CTypeInfo Get() {
return CTypeInfo(CTypeInfo::kCallbackOptionsType);
}
};
// Helper to count the number of occurances of `T` in `List`
template <typename T, typename... List>
struct count : std::integral_constant<int, 0> {};
@ -312,107 +348,178 @@ struct count<T, T, Args...>
template <typename T, typename U, typename... Args>
struct count<T, U, Args...> : count<T, Args...> {};
template <typename R, typename... Args>
template <typename RetBuilder, typename... ArgBuilders>
class CFunctionInfoImpl : public CFunctionInfo {
public:
static constexpr int kOptionsArgCount =
count<FastApiCallbackOptions&, Args...>();
count<FastApiCallbackOptions&, ArgBuilders...>();
static constexpr int kReceiverCount = 1;
CFunctionInfoImpl()
: return_info_(internal::GetCType<R>::Get()),
arg_count_(sizeof...(Args) - kOptionsArgCount),
arg_info_{internal::GetCType<Args>::Get()...} {
static_assert(kOptionsArgCount == 0 || kOptionsArgCount == 1,
"Only one options parameter is supported.");
static_assert(sizeof...(Args) >= kOptionsArgCount + kReceiverCount,
"The receiver or the fallback argument is missing.");
constexpr CTypeInfo::Type type = internal::GetCType<R>::Get().GetType();
static_assert(type == CTypeInfo::Type::kVoid ||
type == CTypeInfo::Type::kBool ||
type == CTypeInfo::Type::kInt32 ||
type == CTypeInfo::Type::kUint32 ||
type == CTypeInfo::Type::kFloat32 ||
type == CTypeInfo::Type::kFloat64,
static_assert(kOptionsArgCount == 0 || kOptionsArgCount == 1,
"Only one options parameter is supported.");
static_assert(sizeof...(ArgBuilders) >= kOptionsArgCount + kReceiverCount,
"The receiver or the options argument is missing.");
public:
constexpr CFunctionInfoImpl()
: CFunctionInfo(RetBuilder::Build(), sizeof...(ArgBuilders),
arg_info_storage_),
arg_info_storage_{ArgBuilders::Build()...} {
constexpr CTypeInfo::Type kReturnType = RetBuilder::Build().GetType();
static_assert(kReturnType == CTypeInfo::Type::kVoid ||
kReturnType == CTypeInfo::Type::kBool ||
kReturnType == CTypeInfo::Type::kInt32 ||
kReturnType == CTypeInfo::Type::kUint32 ||
kReturnType == CTypeInfo::Type::kFloat32 ||
kReturnType == CTypeInfo::Type::kFloat64,
"64-bit int and api object values are not currently "
"supported return types.");
}
const CTypeInfo& ReturnInfo() const override { return return_info_; }
unsigned int ArgumentCount() const override { return arg_count_; }
const CTypeInfo& ArgumentInfo(unsigned int index) const override {
if (index >= ArgumentCount()) {
return CTypeInfo::Invalid();
}
return arg_info_[index];
private:
const CTypeInfo arg_info_storage_[sizeof...(ArgBuilders)];
};
template <typename T>
struct TypeInfoHelper {
static_assert(sizeof(T) != sizeof(T), "This type is not supported");
};
#define SPECIALIZE_GET_TYPE_INFO_HELPER_FOR(T, Enum) \
template <> \
struct TypeInfoHelper<T> { \
static constexpr CTypeInfo::Flags Flags() { \
return CTypeInfo::Flags::kNone; \
} \
\
static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::Enum; } \
};
#define BASIC_C_TYPES(V) \
V(void, kVoid) \
V(bool, kBool) \
V(int32_t, kInt32) \
V(uint32_t, kUint32) \
V(int64_t, kInt64) \
V(uint64_t, kUint64) \
V(float, kFloat32) \
V(double, kFloat64) \
V(ApiObject, kV8Value)
BASIC_C_TYPES(SPECIALIZE_GET_TYPE_INFO_HELPER_FOR)
#undef BASIC_C_TYPES
template <>
struct TypeInfoHelper<FastApiCallbackOptions&> {
static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; }
static constexpr CTypeInfo::Type Type() {
return CTypeInfo::kCallbackOptionsType;
}
};
template <typename T, CTypeInfo::Flags... Flags>
class CTypeInfoBuilder {
public:
using BaseType = T;
static constexpr CTypeInfo Build() {
// Get the flags and merge in any additional flags.
uint8_t flags = uint8_t(TypeInfoHelper<T>::Flags());
int unused[] = {0, (flags |= uint8_t(Flags), 0)...};
// With C++17, we could use a "..." fold expression over a parameter pack.
// Since we're still using C++14, we have to evaluate an OR expresion while
// constructing an unused list of 0's. This applies the binary operator
// for each value in Flags.
(void)unused;
// Return the same type with the merged flags.
return CTypeInfo(TypeInfoHelper<T>::Type(), CTypeInfo::Flags(flags));
}
};
template <typename RetBuilder, typename... ArgBuilders>
class CFunctionBuilderWithFunction {
public:
explicit constexpr CFunctionBuilderWithFunction(const void* fn) : fn_(fn) {}
template <CTypeInfo::Flags... Flags>
constexpr auto Ret() {
return CFunctionBuilderWithFunction<
CTypeInfoBuilder<typename RetBuilder::BaseType, Flags...>,
ArgBuilders...>(fn_);
}
template <unsigned int N, CTypeInfo::Flags... Flags>
constexpr auto Arg() {
// Return a copy of the builder with the Nth arg builder merged with
// template parameter pack Flags.
return ArgImpl<N, Flags...>(
std::make_index_sequence<sizeof...(ArgBuilders)>());
}
auto Build() {
static CFunctionInfoImpl<RetBuilder, ArgBuilders...> instance;
return CFunction(fn_, &instance);
}
bool HasOptions() const override { return kOptionsArgCount == 1; }
private:
const CTypeInfo return_info_;
const unsigned int arg_count_;
const CTypeInfo arg_info_[sizeof...(Args)];
template <bool Merge, unsigned int N, CTypeInfo::Flags... Flags>
struct GetArgBuilder;
// Returns the same ArgBuilder as the one at index N, including its flags.
// Flags in the template parameter pack are ignored.
template <unsigned int N, CTypeInfo::Flags... Flags>
struct GetArgBuilder<false, N, Flags...> {
using type =
typename std::tuple_element<N, std::tuple<ArgBuilders...>>::type;
};
// Returns an ArgBuilder with the same base type as the one at index N,
// but merges the flags with the flags in the template parameter pack.
template <unsigned int N, CTypeInfo::Flags... Flags>
struct GetArgBuilder<true, N, Flags...> {
using type = CTypeInfoBuilder<
typename std::tuple_element<N,
std::tuple<ArgBuilders...>>::type::BaseType,
std::tuple_element<N, std::tuple<ArgBuilders...>>::type::Build()
.GetFlags(),
Flags...>;
};
// Return a copy of the CFunctionBuilder, but merges the Flags on ArgBuilder
// index N with the new Flags passed in the template parameter pack.
template <unsigned int N, CTypeInfo::Flags... Flags, size_t... I>
constexpr auto ArgImpl(std::index_sequence<I...>) {
return CFunctionBuilderWithFunction<
RetBuilder, typename GetArgBuilder<N == I, I, Flags...>::type...>(fn_);
}
const void* fn_;
};
class CFunctionBuilder {
public:
constexpr CFunctionBuilder() {}
template <typename R, typename... Args>
constexpr auto Fn(R (*fn)(Args...)) {
return CFunctionBuilderWithFunction<CTypeInfoBuilder<R>,
CTypeInfoBuilder<Args>...>(
reinterpret_cast<const void*>(fn));
}
};
} // namespace internal
class V8_EXPORT CFunction {
public:
constexpr CFunction() : address_(nullptr), type_info_(nullptr) {}
// static
template <typename R, typename... Args>
CFunction CFunction::ArgUnwrap<R (*)(Args...)>::Make(R (*func)(Args...)) {
return internal::CFunctionBuilder().Fn(func).Build();
}
const CTypeInfo& ReturnInfo() const { return type_info_->ReturnInfo(); }
const CTypeInfo& ArgumentInfo(unsigned int index) const {
return type_info_->ArgumentInfo(index);
}
unsigned int ArgumentCount() const { return type_info_->ArgumentCount(); }
const void* GetAddress() const { return address_; }
const CFunctionInfo* GetTypeInfo() const { return type_info_; }
template <typename F>
static CFunction Make(F* func) {
return ArgUnwrap<F*>::Make(func);
}
template <typename F>
V8_DEPRECATED("Use CFunction::Make instead.")
static CFunction MakeWithFallbackSupport(F* func) {
return ArgUnwrap<F*>::Make(func);
}
template <typename F>
static CFunction Make(F* func, const CFunctionInfo* type_info) {
return CFunction(reinterpret_cast<const void*>(func), type_info);
}
private:
const void* address_;
const CFunctionInfo* type_info_;
CFunction(const void* address, const CFunctionInfo* type_info);
template <typename R, typename... Args>
static CFunctionInfo* GetCFunctionInfo() {
static internal::CFunctionInfoImpl<R, Args...> instance;
return &instance;
}
template <typename F>
class ArgUnwrap {
static_assert(sizeof(F) != sizeof(F),
"CFunction must be created from a function pointer.");
};
template <typename R, typename... Args>
class ArgUnwrap<R (*)(Args...)> {
public:
static CFunction Make(R (*func)(Args...)) {
return CFunction(reinterpret_cast<const void*>(func),
GetCFunctionInfo<R, Args...>());
}
};
};
using CFunctionBuilder = internal::CFunctionBuilder;
} // namespace v8

View File

@ -9987,6 +9987,21 @@ CFunction::CFunction(const void* address, const CFunctionInfo* type_info)
CHECK_NOT_NULL(type_info_);
}
CFunctionInfo::CFunctionInfo(const CTypeInfo& return_info,
unsigned int arg_count, const CTypeInfo* arg_info)
: return_info_(return_info), arg_count_(arg_count), arg_info_(arg_info) {
if (arg_count_ > 0) {
for (unsigned int i = 0; i < arg_count_ - 1; ++i) {
DCHECK(arg_info_[i].GetType() != CTypeInfo::kCallbackOptionsType);
}
}
}
const CTypeInfo& CFunctionInfo::ArgumentInfo(unsigned int index) const {
DCHECK_LT(index, ArgumentCount());
return arg_info_[index];
}
RegisterState::RegisterState()
: pc(nullptr), sp(nullptr), fp(nullptr), lr(nullptr) {}
RegisterState::~RegisterState() = default;

View File

@ -27937,37 +27937,28 @@ void CallWithUnexpectedObjectType(v8::Local<v8::Value> receiver) {
}
class TestCFunctionInfo : public v8::CFunctionInfo {
const v8::CTypeInfo& ReturnInfo() const override {
static v8::CTypeInfo return_info =
v8::CTypeInfo(v8::CTypeInfo::Type::kVoid);
return return_info;
}
static constexpr unsigned int kArgCount = 2u;
unsigned int ArgumentCount() const override { return 2; }
public:
TestCFunctionInfo()
: v8::CFunctionInfo(v8::CTypeInfo(v8::CTypeInfo::Type::kVoid), kArgCount,
arg_info_storage_),
arg_info_storage_{
v8::CTypeInfo(v8::CTypeInfo::Type::kV8Value),
v8::CTypeInfo(v8::CTypeInfo::Type::kBool),
} {}
const v8::CTypeInfo& ArgumentInfo(unsigned int index) const override {
static v8::CTypeInfo type_info0 =
v8::CTypeInfo(v8::CTypeInfo::Type::kV8Value);
static v8::CTypeInfo type_info1 = v8::CTypeInfo(v8::CTypeInfo::Type::kBool);
switch (index) {
case 0:
return type_info0;
case 1:
return type_info1;
default:
UNREACHABLE();
}
}
bool HasOptions() const override { return false; }
private:
const v8::CTypeInfo arg_info_storage_[kArgCount];
};
void CheckDynamicTypeInfo() {
LocalContext env;
static TestCFunctionInfo type_info;
v8::CFunction c_func =
v8::CFunction::Make(ApiNumberChecker<bool>::FastCallback, &type_info);
v8::CFunction c_func = v8::CFunction(
reinterpret_cast<const void*>(ApiNumberChecker<bool>::FastCallback),
&type_info);
CHECK_EQ(c_func.ArgumentCount(), 2);
CHECK_EQ(c_func.ArgumentInfo(0).GetType(), v8::CTypeInfo::Type::kV8Value);
CHECK_EQ(c_func.ArgumentInfo(1).GetType(), v8::CTypeInfo::Type::kBool);