Dawn.node, make C++ class inheritance reflected in JavaScript

This makes the webgpu:idl,inheritance tests pass for
GPUValidationError, GPUOutMemoryError, and GPUInternalError
pass the test that they inherit from GPUError

Bug: 419128706
Change-Id: Ib56a6403bef7f677e9f5f740193770012de30ad4
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/243274
Commit-Queue: Gregg Tavares <gman@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn/node/interop/Core.cpp b/src/dawn/node/interop/Core.cpp
index 710c9e6..26a2946 100644
--- a/src/dawn/node/interop/Core.cpp
+++ b/src/dawn/node/interop/Core.cpp
@@ -206,4 +206,30 @@
     return o;
 }
 
+// Chain the prototype of derivedClassConstructor to the prototype of baseClassValue
+// by calling the JavaScript function Object.setPrototypeOf
+void ChainPrototype(Napi::Value baseClassValue, Napi::Function derivedClassConstructor) {
+    // Look up our base constructor
+    Napi::Function baseClassConstructor = baseClassValue.As<Napi::Function>();
+
+    // Look up base prototype
+    Napi::Value baseClassPrototypeValue = baseClassConstructor.Get("prototype");
+    Napi::Object baseClassPrototype = baseClassPrototypeValue.As<Napi::Object>();
+
+    // Lookup our prototype
+    Napi::Object derivedPrototype = derivedClassConstructor.Get("prototype").As<Napi::Object>();
+
+    Napi::Object global = derivedClassConstructor.Env().Global();
+    Napi::Function setProtoType =
+        global.Get("Object").ToObject().Get("setPrototypeOf").As<Napi::Function>();
+
+    // Makes Derived super() call Base constructor
+    // JS = Object.setPrototypeOf(Derived, Base);
+    setProtoType.Call({derivedClassConstructor, baseClassConstructor});
+
+    // Make derived.someMethod call base.someMethod if someMethod does not exist on Derived.
+    // JS = Object.setPrototypeOf(Derived.constructor, Base.constructor)
+    setProtoType.Call({derivedPrototype, baseClassPrototype});
+}
+
 }  // namespace wgpu::interop
diff --git a/src/dawn/node/interop/Core.h b/src/dawn/node/interop/Core.h
index eb07517..64a7528 100644
--- a/src/dawn/node/interop/Core.h
+++ b/src/dawn/node/interop/Core.h
@@ -829,6 +829,8 @@
     return deferred.Promise();
 }
 
+void ChainPrototype(Napi::Value baseClassValue, Napi::Function derivedClassConstructor);
+
 }  // namespace wgpu::interop
 
 #endif  // SRC_DAWN_NODE_INTEROP_CORE_H_
diff --git a/src/dawn/node/interop/WebGPU.cpp.tmpl b/src/dawn/node/interop/WebGPU.cpp.tmpl
index 0122640..93900ff 100644
--- a/src/dawn/node/interop/WebGPU.cpp.tmpl
+++ b/src/dawn/node/interop/WebGPU.cpp.tmpl
@@ -96,7 +96,16 @@
   Wrappers(Napi::Env env) {
 {{-   range $ := .Declarations}}
 {{-     if IsInterfaceOrNamespace $}}
+{{-       if and (IsInterface $) ($.Inherits)}}
+    {
+      // It's an interface and it inherits from another class, pass the class it inherits
+      // from to its Class factory
+      Napi::Value baseClassValue = {{$.Inherits}}_ctor.Value();
+      {{$.Name}}_ctor = Napi::Persistent(W{{$.Name}}::Class(env, baseClassValue));
+    }
+{{-       else}}
     {{$.Name}}_ctor = Napi::Persistent(W{{$.Name}}::Class(env));
+{{-       end}}
 {{-     end}}
 {{-   end}}
   }
@@ -158,8 +167,14 @@
 {{-  if IsInterface $}}
     std::unique_ptr<{{$.Name}}> impl;
 {{-  end}}
+{{-  if and (IsInterface $) ($.Inherits) }}
+    // This class inherits from another, require the class factory to
+    // receive the base class so it can setup inheritance.
+    static Napi::Function Class(Napi::Env env, Napi::Value baseClassValue) {
+{{-  else}}
     static Napi::Function Class(Napi::Env env) {
-      return DefineClass(env, "{{$.Name}}", {
+{{-  end}}
+      auto func = DefineClass(env, "{{$.Name}}", {
 {{   if $s := SetlikeOf $}}
         InstanceMethod("has", &W{{$.Name}}::has),
         InstanceMethod("keys", &W{{$.Name}}::keys),
@@ -180,6 +195,12 @@
         StaticValue("{{$c.Name}}", ToJS(env, {{$.Name}}::{{$c.Name}}), napi_default_jsproperty),
 {{-  end}}
       });
+
+{{-  if and (IsInterface $) ($.Inherits)}}
+      // It inherits from another class, setup the prototype chain.
+      ChainPrototype(baseClassValue, func);
+{{-  end}}
+      return func;
     }
 
     W{{$.Name}}(const Napi::CallbackInfo& info) : ObjectWrap(info) {}