dawn.node: Implement the [Clamp] and [EnforceRange] WebIDL attributes.
These are implemented by wrapping the integer types in transparent ClampedInteger<> and EnforceRangeInteger<> structures. Some parts of the core needed to be updated after this, either to disambiguate conversions, or because of bugs (u32 vs u64). To make the CTS tests checking for this pass, the errors returned when conversion FromJS failed needed to be updated to TypeError and not just the generic Napi::Error. Bug: dawn:1123 Change-Id: Ife1d0baa7687e43d735a1814ec41883c49ae74a6 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/85640 Reviewed-by: Ben Clayton <bclayton@google.com> Commit-Queue: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
parent
e56b5f1097
commit
8033af0947
|
@ -464,27 +464,27 @@ namespace wgpu::binding {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Converter::Convert(wgpu::TextureUsage& out, const interop::GPUTextureUsageFlags& in) {
|
bool Converter::Convert(wgpu::TextureUsage& out, const interop::GPUTextureUsageFlags& in) {
|
||||||
out = static_cast<wgpu::TextureUsage>(in);
|
out = static_cast<wgpu::TextureUsage>(in.value);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Converter::Convert(wgpu::ColorWriteMask& out, const interop::GPUColorWriteFlags& in) {
|
bool Converter::Convert(wgpu::ColorWriteMask& out, const interop::GPUColorWriteFlags& in) {
|
||||||
out = static_cast<wgpu::ColorWriteMask>(in);
|
out = static_cast<wgpu::ColorWriteMask>(in.value);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Converter::Convert(wgpu::BufferUsage& out, const interop::GPUBufferUsageFlags& in) {
|
bool Converter::Convert(wgpu::BufferUsage& out, const interop::GPUBufferUsageFlags& in) {
|
||||||
out = static_cast<wgpu::BufferUsage>(in);
|
out = static_cast<wgpu::BufferUsage>(in.value);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Converter::Convert(wgpu::MapMode& out, const interop::GPUMapModeFlags& in) {
|
bool Converter::Convert(wgpu::MapMode& out, const interop::GPUMapModeFlags& in) {
|
||||||
out = static_cast<wgpu::MapMode>(in);
|
out = static_cast<wgpu::MapMode>(in.value);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Converter::Convert(wgpu::ShaderStage& out, const interop::GPUShaderStageFlags& in) {
|
bool Converter::Convert(wgpu::ShaderStage& out, const interop::GPUShaderStageFlags& in) {
|
||||||
out = static_cast<wgpu::ShaderStage>(in);
|
out = static_cast<wgpu::ShaderStage>(in.value);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -281,6 +281,20 @@ namespace wgpu::binding {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClampedInteger<T>
|
||||||
|
template <typename T>
|
||||||
|
inline bool Convert(T& out, const interop::ClampedInteger<T>& in) {
|
||||||
|
out = in;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnforceRangeInteger<T>
|
||||||
|
template <typename T>
|
||||||
|
inline bool Convert(T& out, const interop::EnforceRangeInteger<T>& in) {
|
||||||
|
out = in;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename OUT, typename... IN_TYPES>
|
template <typename OUT, typename... IN_TYPES>
|
||||||
inline bool Convert(OUT& out, const std::variant<IN_TYPES...>& in) {
|
inline bool Convert(OUT& out, const std::variant<IN_TYPES...>& in) {
|
||||||
return std::visit([&](auto&& i) { return Convert(out, i); }, in);
|
return std::visit([&](auto&& i) { return Convert(out, i); }, in);
|
||||||
|
|
|
@ -69,7 +69,7 @@ namespace wgpu::binding {
|
||||||
auto ctx = new Context{env, interop::Promise<void>(env, PROMISE_INFO), async_, state_};
|
auto ctx = new Context{env, interop::Promise<void>(env, PROMISE_INFO), async_, state_};
|
||||||
auto promise = ctx->promise;
|
auto promise = ctx->promise;
|
||||||
|
|
||||||
uint64_t s = size.has_value() ? size.value() : (desc_.size - offset);
|
uint64_t s = size.has_value() ? size.value().value : (desc_.size - offset);
|
||||||
|
|
||||||
state_ = State::MappingPending;
|
state_ = State::MappingPending;
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ namespace wgpu::binding {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t s = size.has_value() ? size.value() : (desc_.size - offset);
|
uint64_t s = size.has_value() ? size.value().value : (desc_.size - offset);
|
||||||
|
|
||||||
uint64_t start = offset;
|
uint64_t start = offset;
|
||||||
uint64_t end = offset + s;
|
uint64_t end = offset + s;
|
||||||
|
|
|
@ -156,7 +156,7 @@ namespace wgpu::binding {
|
||||||
Converter conv(env);
|
Converter conv(env);
|
||||||
|
|
||||||
wgpu::Buffer b{};
|
wgpu::Buffer b{};
|
||||||
uint32_t o = 0;
|
uint64_t o = 0;
|
||||||
|
|
||||||
if (!conv(b, indirectBuffer) || //
|
if (!conv(b, indirectBuffer) || //
|
||||||
!conv(o, indirectOffset)) {
|
!conv(o, indirectOffset)) {
|
||||||
|
@ -172,7 +172,7 @@ namespace wgpu::binding {
|
||||||
Converter conv(env);
|
Converter conv(env);
|
||||||
|
|
||||||
wgpu::Buffer b{};
|
wgpu::Buffer b{};
|
||||||
uint32_t o = 0;
|
uint64_t o = 0;
|
||||||
|
|
||||||
if (!conv(b, indirectBuffer) || //
|
if (!conv(b, indirectBuffer) || //
|
||||||
!conv(o, indirectOffset)) {
|
!conv(o, indirectOffset)) {
|
||||||
|
|
|
@ -218,7 +218,7 @@ namespace wgpu::binding {
|
||||||
Converter conv(env);
|
Converter conv(env);
|
||||||
|
|
||||||
wgpu::Buffer b{};
|
wgpu::Buffer b{};
|
||||||
uint32_t o = 0;
|
uint64_t o = 0;
|
||||||
|
|
||||||
if (!conv(b, indirectBuffer) || //
|
if (!conv(b, indirectBuffer) || //
|
||||||
!conv(o, indirectOffset)) {
|
!conv(o, indirectOffset)) {
|
||||||
|
@ -234,7 +234,7 @@ namespace wgpu::binding {
|
||||||
Converter conv(env);
|
Converter conv(env);
|
||||||
|
|
||||||
wgpu::Buffer b{};
|
wgpu::Buffer b{};
|
||||||
uint32_t o = 0;
|
uint64_t o = 0;
|
||||||
|
|
||||||
if (!conv(b, indirectBuffer) || //
|
if (!conv(b, indirectBuffer) || //
|
||||||
!conv(o, indirectOffset)) {
|
!conv(o, indirectOffset)) {
|
||||||
|
|
|
@ -69,6 +69,40 @@ namespace wgpu::interop {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using FrozenArray = std::vector<T>;
|
using FrozenArray = std::vector<T>;
|
||||||
|
|
||||||
|
// A wrapper class for integers that's as transparent as possible and is used to distinguish
|
||||||
|
// that the type is tagged with the [Clamp] WebIDL attribute.
|
||||||
|
template <typename T>
|
||||||
|
struct ClampedInteger {
|
||||||
|
static_assert(std::is_integral_v<T>);
|
||||||
|
|
||||||
|
using IntegerType = T;
|
||||||
|
ClampedInteger() : value(0) {
|
||||||
|
}
|
||||||
|
ClampedInteger(T value) : value(value) {
|
||||||
|
}
|
||||||
|
operator T() const {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
T value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A wrapper class for integers that's as transparent as possible and is used to distinguish
|
||||||
|
// that the type is tagged with the [EnforceRange] WebIDL attribute.
|
||||||
|
template <typename T>
|
||||||
|
struct EnforceRangeInteger {
|
||||||
|
static_assert(std::is_integral_v<T>);
|
||||||
|
|
||||||
|
using IntegerType = T;
|
||||||
|
EnforceRangeInteger() : value(0) {
|
||||||
|
}
|
||||||
|
EnforceRangeInteger(T value) : value(value) {
|
||||||
|
}
|
||||||
|
operator T() const {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
T value;
|
||||||
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Result
|
// Result
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -447,6 +481,75 @@ namespace wgpu::interop {
|
||||||
static Napi::Value ToJS(Napi::Env, double);
|
static Napi::Value ToJS(Napi::Env, double);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// [Clamp]ed integers must convert values outside of the integer range by clamping them.
|
||||||
|
template <typename T>
|
||||||
|
class Converter<ClampedInteger<T>> {
|
||||||
|
public:
|
||||||
|
static Result FromJS(Napi::Env env, Napi::Value value, ClampedInteger<T>& out) {
|
||||||
|
double doubleValue;
|
||||||
|
Result res = Converter<double>::FromJS(env, value, doubleValue);
|
||||||
|
if (!res) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for clamping first.
|
||||||
|
constexpr T kMin = std::numeric_limits<T>::min();
|
||||||
|
constexpr T kMax = std::numeric_limits<T>::max();
|
||||||
|
if (doubleValue < kMin) {
|
||||||
|
out = kMin;
|
||||||
|
return Success;
|
||||||
|
}
|
||||||
|
if (doubleValue > kMax) {
|
||||||
|
out = kMax;
|
||||||
|
return Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Yay, no clamping! We can convert the integer type as usual.
|
||||||
|
T correctValue;
|
||||||
|
res = Converter<T>::FromJS(env, value, correctValue);
|
||||||
|
if (!res) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
out = correctValue;
|
||||||
|
return Success;
|
||||||
|
}
|
||||||
|
static Napi::Value ToJS(Napi::Env env, const ClampedInteger<T>& value) {
|
||||||
|
return Converter<T>::ToJS(env, value.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// [EnforceRange] integers cause a TypeError when converted from out of range values
|
||||||
|
template <typename T>
|
||||||
|
class Converter<EnforceRangeInteger<T>> {
|
||||||
|
public:
|
||||||
|
static Result FromJS(Napi::Env env, Napi::Value value, EnforceRangeInteger<T>& out) {
|
||||||
|
double doubleValue;
|
||||||
|
Result res = Converter<double>::FromJS(env, value, doubleValue);
|
||||||
|
if (!res) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for out of range and throw a type error.
|
||||||
|
constexpr T kMin = std::numeric_limits<T>::min();
|
||||||
|
constexpr T kMax = std::numeric_limits<T>::max();
|
||||||
|
if (!(kMin <= doubleValue && doubleValue <= kMax)) {
|
||||||
|
return Error("Values are out of the range of that integer.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Yay, no error! We can convert the integer type as usual.
|
||||||
|
T correctValue;
|
||||||
|
res = Converter<T>::FromJS(env, value, correctValue);
|
||||||
|
if (!res) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
out = correctValue;
|
||||||
|
return Success;
|
||||||
|
}
|
||||||
|
static Napi::Value ToJS(Napi::Env env, const EnforceRangeInteger<T>& value) {
|
||||||
|
return Converter<T>::ToJS(env, value.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
class Converter<UndefinedType> {
|
class Converter<UndefinedType> {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -172,7 +172,7 @@ Wrappers* Wrappers::instance = nullptr;
|
||||||
if (res) {
|
if (res) {
|
||||||
return ToJS(info.Env(), impl->has(info.Env(), std::get<0>(args)));
|
return ToJS(info.Env(), impl->has(info.Env(), std::get<0>(args)));
|
||||||
}
|
}
|
||||||
Napi::Error::New(info.Env(), res.error).ThrowAsJavaScriptException();
|
Napi::TypeError::New(info.Env(), res.error).ThrowAsJavaScriptException();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
Napi::Value keys(const Napi::CallbackInfo& info) {
|
Napi::Value keys(const Napi::CallbackInfo& info) {
|
||||||
|
@ -217,7 +217,7 @@ Wrappers* Wrappers::instance = nullptr;
|
||||||
error = {{if $overloaded}}"\noverload {{$overload_idx}} failed to match:\n" + {{end}}res.error;
|
error = {{if $overloaded}}"\noverload {{$overload_idx}} failed to match:\n" + {{end}}res.error;
|
||||||
}
|
}
|
||||||
{{- end}}
|
{{- end}}
|
||||||
Napi::Error::New(info.Env(), "no overload matched for {{$m.Name}}:\n" + error).ThrowAsJavaScriptException();
|
Napi::TypeError::New(info.Env(), "no overload matched for {{$m.Name}}:\n" + error).ThrowAsJavaScriptException();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
{{- end}}
|
{{- end}}
|
||||||
|
@ -235,7 +235,7 @@ Wrappers* Wrappers::instance = nullptr;
|
||||||
impl->set{{Title $a.Name}}(info.Env(), std::move(v));
|
impl->set{{Title $a.Name}}(info.Env(), std::move(v));
|
||||||
} else {
|
} else {
|
||||||
res = res.Append("invalid value to {{$a.Name}}");
|
res = res.Append("invalid value to {{$a.Name}}");
|
||||||
Napi::Error::New(info.Env(), res.error).ThrowAsJavaScriptException();
|
Napi::TypeError::New(info.Env(), res.error).ThrowAsJavaScriptException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{{- end}}
|
{{- end}}
|
||||||
|
|
|
@ -146,7 +146,11 @@ public:
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
*/ -}}
|
*/ -}}
|
||||||
{{- define "Typedef"}}
|
{{- define "Typedef"}}
|
||||||
|
{{- if HasAnnotation $ "EnforceRange"}}
|
||||||
|
using {{$.Name}} = EnforceRangeInteger<{{template "Type" $.Type}}>;
|
||||||
|
{{- else}}
|
||||||
using {{$.Name}} = {{template "Type" $.Type}};
|
using {{$.Name}} = {{template "Type" $.Type}};
|
||||||
|
{{- end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,8 @@ See:
|
||||||
{{- if IsUndefinedType $}}void
|
{{- if IsUndefinedType $}}void
|
||||||
{{- else if IsTypeName $}}
|
{{- else if IsTypeName $}}
|
||||||
{{- if eq $.Name "boolean" }}bool
|
{{- if eq $.Name "boolean" }}bool
|
||||||
|
{{- else if eq $.Name "short" }}int16_t
|
||||||
|
{{- else if eq $.Name "unsigned short" }}uint16_t
|
||||||
{{- else if eq $.Name "long" }}int32_t
|
{{- else if eq $.Name "long" }}int32_t
|
||||||
{{- else if eq $.Name "unsigned long" }}uint32_t
|
{{- else if eq $.Name "unsigned long" }}uint32_t
|
||||||
{{- else if eq $.Name "long long" }}int64_t
|
{{- else if eq $.Name "long long" }}int64_t
|
||||||
|
@ -66,13 +68,29 @@ See:
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
*/ -}}
|
*/ -}}
|
||||||
{{- define "AttributeType" -}}
|
{{- define "AttributeType" -}}
|
||||||
{{- if $.Required }}{{template "Type" $.Type}}
|
{{- if $.Required }}{{template "AttributeClampHelper" $}}
|
||||||
{{- else if $.Init }}{{template "Type" $.Type}}
|
{{- else if $.Init }}{{template "AttributeClampHelper" $}}
|
||||||
{{- else }}std::optional<{{template "Type" $.Type}}>
|
{{- else }}std::optional<{{template "AttributeClampHelper" $}}>
|
||||||
{{- end}}
|
{{- end}}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
|
|
||||||
|
{{- /*
|
||||||
|
A helper for AttributeType that wraps integer types if necessary for WebIDL attributes.
|
||||||
|
Note that [Clamp] and [EnforceRange] are supposed to be an annotation on the type and not
|
||||||
|
the attribute, but webidlparser doesn't parse this correctly.
|
||||||
|
*/ -}}
|
||||||
|
{{- define "AttributeClampHelper" -}}
|
||||||
|
{{- if HasAnnotation $ "Clamp" }}
|
||||||
|
ClampedInteger<{{template "Type" $.Type}}>
|
||||||
|
{{- else if HasAnnotation $ "EnforceRange" }}
|
||||||
|
EnforceRangeInteger<{{template "Type" $.Type}}>
|
||||||
|
{{- else}}
|
||||||
|
{{template "Type" $.Type}}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
|
||||||
{{- /*
|
{{- /*
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Literal generates a C++ literal value using the following arguments:
|
-- Literal generates a C++ literal value using the following arguments:
|
||||||
|
|
|
@ -519,12 +519,16 @@ func findAnnotation(list []*ast.Annotation, name string) *ast.Annotation {
|
||||||
|
|
||||||
func hasAnnotation(obj interface{}, name string) bool {
|
func hasAnnotation(obj interface{}, name string) bool {
|
||||||
switch obj := obj.(type) {
|
switch obj := obj.(type) {
|
||||||
case *ast.Member:
|
|
||||||
return findAnnotation(obj.Annotations, name) != nil
|
|
||||||
case *ast.Interface:
|
case *ast.Interface:
|
||||||
return findAnnotation(obj.Annotations, name) != nil
|
return findAnnotation(obj.Annotations, name) != nil
|
||||||
|
case *ast.Member:
|
||||||
|
return findAnnotation(obj.Annotations, name) != nil
|
||||||
case *ast.Namespace:
|
case *ast.Namespace:
|
||||||
return findAnnotation(obj.Annotations, name) != nil
|
return findAnnotation(obj.Annotations, name) != nil
|
||||||
|
case *ast.Parameter:
|
||||||
|
return findAnnotation(obj.Annotations, name) != nil
|
||||||
|
case *ast.Typedef:
|
||||||
|
return findAnnotation(obj.Annotations, name) != nil || findAnnotation(obj.TypeAnnotations, name) != nil
|
||||||
}
|
}
|
||||||
panic("Unhandled AST node type in hasAnnotation")
|
panic("Unhandled AST node type in hasAnnotation")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue