dawn.node: Implement setlike.size

Bug: None
Change-Id: I406f6fa87034dd777d6555a401d2e46869774c8b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/166422
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/dawn/node/binding/GPU.cpp b/src/dawn/node/binding/GPU.cpp
index 2e247c5..a08423d 100644
--- a/src/dawn/node/binding/GPU.cpp
+++ b/src/dawn/node/binding/GPU.cpp
@@ -295,6 +295,7 @@
             }
             return out;
         }
+        size_t getSize(Napi::Env env) { return features_.size(); }
 
         InteropWGSLFeatureSet features_;
     };
diff --git a/src/dawn/node/binding/GPUSupportedFeatures.cpp b/src/dawn/node/binding/GPUSupportedFeatures.cpp
index 74eeed8..0fa29f7 100644
--- a/src/dawn/node/binding/GPUSupportedFeatures.cpp
+++ b/src/dawn/node/binding/GPUSupportedFeatures.cpp
@@ -67,4 +67,8 @@
     return out;
 }
 
+size_t GPUSupportedFeatures::getSize(Napi::Env) {
+    return enabled_.size();
+}
+
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUSupportedFeatures.h b/src/dawn/node/binding/GPUSupportedFeatures.h
index dca8edf..1ab84be 100644
--- a/src/dawn/node/binding/GPUSupportedFeatures.h
+++ b/src/dawn/node/binding/GPUSupportedFeatures.h
@@ -47,6 +47,7 @@
     // interop::GPUSupportedFeatures interface compliance
     bool has(Napi::Env, std::string name) override;
     std::vector<std::string> keys(Napi::Env) override;
+    size_t getSize(Napi::Env) override;
 
   private:
     std::unordered_set<interop::GPUFeatureName> enabled_;
diff --git a/src/dawn/node/interop/Core.cpp b/src/dawn/node/interop/Core.cpp
index 8b820ca..ddce33f 100644
--- a/src/dawn/node/interop/Core.cpp
+++ b/src/dawn/node/interop/Core.cpp
@@ -148,6 +148,27 @@
     return Napi::Value::From(env, value);
 }
 
+Result
+Converter<std::conditional_t<detail::kSizetIsUniqueType, size_t, detail::InvalidType>>::FromJS(
+    Napi::Env env,
+    Napi::Value value,
+    size_t& out) {
+    if (value.IsNumber()) {
+        // Note that the JS Number type only stores doubles, so the max integer
+        // range of values without precision loss is -2^53 to 2^53 (52 bit mantissa
+        // with 1 implicit bit). This is why there's no UInt64Value() function.
+        out = static_cast<size_t>(value.ToNumber().Int64Value());
+        return Success;
+    }
+    return Error("value is not a number");
+}
+Napi::Value
+Converter<std::conditional_t<detail::kSizetIsUniqueType, size_t, detail::InvalidType>>::ToJS(
+    Napi::Env env,
+    size_t value) {
+    return Napi::Value::From(env, value);
+}
+
 Result Converter<float>::FromJS(Napi::Env env, Napi::Value value, float& out) {
     if (value.IsNumber()) {
         out = value.ToNumber().FloatValue();
diff --git a/src/dawn/node/interop/Core.h b/src/dawn/node/interop/Core.h
index 688c2c5..1352e8b 100644
--- a/src/dawn/node/interop/Core.h
+++ b/src/dawn/node/interop/Core.h
@@ -459,6 +459,21 @@
     static Napi::Value ToJS(Napi::Env, uint64_t);
 };
 
+// Sometimes size_t is uint32_t, or uint64_t. Only define the conversion to size_t if it is a
+// unique type, otherwise we have C++ compilation error for the redefinition of a template.
+namespace detail {
+struct InvalidType;
+static constexpr bool kSizetIsUniqueType =
+    !std::is_same_v<size_t, uint32_t> && !std::is_same_v<size_t, uint64_t>;
+}  // namespace detail
+
+template <>
+class Converter<std::conditional_t<detail::kSizetIsUniqueType, size_t, detail::InvalidType>> {
+  public:
+    static Result FromJS(Napi::Env, Napi::Value, size_t&);
+    static Napi::Value ToJS(Napi::Env, size_t);
+};
+
 template <>
 class Converter<float> {
   public:
diff --git a/src/dawn/node/interop/WebGPU.cpp.tmpl b/src/dawn/node/interop/WebGPU.cpp.tmpl
index e66b6f5..a7f9815 100644
--- a/src/dawn/node/interop/WebGPU.cpp.tmpl
+++ b/src/dawn/node/interop/WebGPU.cpp.tmpl
@@ -163,6 +163,7 @@
 {{   if $s := SetlikeOf $}}
         InstanceMethod("has", &W{{$.Name}}::has),
         InstanceMethod("keys", &W{{$.Name}}::keys),
+        InstanceAccessor("size", &W{{$.Name}}::getSize, nullptr),
 {{-  end}}
 {{-  range $m := $methods}}
         InstanceMethod("{{$m.Name}}", &W{{$.Name}}::{{$m.Name}}, static_cast<napi_property_attributes>(napi_writable | napi_configurable)),
@@ -195,6 +196,9 @@
     Napi::Value keys(const Napi::CallbackInfo& info) {
       return ToJS(info.Env(), impl->keys(info.Env()));
     }
+    Napi::Value getSize(const Napi::CallbackInfo& info) {
+      return ToJS(info.Env(), impl->getSize(info.Env()));
+    }
 {{-  end}}
 {{-  range $m := $methods}}
 {{-    $implSuffix := ""}}
diff --git a/src/dawn/node/interop/WebGPU.h.tmpl b/src/dawn/node/interop/WebGPU.h.tmpl
index 5ce2cce..4ab52d5 100644
--- a/src/dawn/node/interop/WebGPU.h.tmpl
+++ b/src/dawn/node/interop/WebGPU.h.tmpl
@@ -215,8 +215,9 @@
 {{- define "InterfaceSetlike"}}
   virtual bool has(Napi::Env, {{template "Type" $.Elem}}) = 0;
   virtual std::vector<{{template "Type" $.Elem}}> keys(Napi::Env) = 0;
+  virtual size_t getSize(Napi::Env) = 0;
 {{- /* TODO(crbug.com/dawn/1143):
-       entries, forEach, size, values
+       entries, forEach, values
        read-write: add, clear, or delete
 */}}
 {{- end }}