Dawn.node disallow constructing non-constructables

This prevents doing `new GPUDevice()` and `new GPUTexture()`

Bug: 419128706
Depends-On: Ia388562d8d7f02458e36b29db1a5acb8fcac03e2
Change-Id: Ie0b0969b3d1fae09913501fc8b6c1406e114c637
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/243295
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Gregg Tavares <gman@chromium.org>
diff --git a/src/dawn/node/interop/WebGPU.cpp.tmpl b/src/dawn/node/interop/WebGPU.cpp.tmpl
index 06e1885..991cdce 100644
--- a/src/dawn/node/interop/WebGPU.cpp.tmpl
+++ b/src/dawn/node/interop/WebGPU.cpp.tmpl
@@ -54,6 +54,11 @@
 
 namespace {
 
+// This is passed to constructors so that constructor can
+// check if it's being called legitimately from C++ or
+// illegitimately from JavaScript.
+static void* kInternalConstructionMarker = nullptr;
+
 {{template "Wrappers" $}}
 
 }  // namespace
@@ -201,7 +206,15 @@
       return func;
     }
 
-    W{{$.Name}}(const Napi::CallbackInfo& info) : ObjectWrap(info) {}
+    W{{$.Name}}(const Napi::CallbackInfo& info) : ObjectWrap(info) {
+{{-   if and (IsInterface $) (not (HasConstructor $))}}
+        // Check if the first argument is our internal Napi::External<void> marker
+        // to prevent JavaScript from constructing this non-constructable object.
+        if (info.Length() < 1 || !info[0].IsExternal() || info[0].As<Napi::External<void>>().Data() != &kInternalConstructionMarker) {
+           Napi::TypeError::New(info.Env(), "Failed to construct '{{$.Name}}': Illegal constructor").ThrowAsJavaScriptException();
+         }
+{{-   end}}
+    }
 
 {{-  if $s := SetlikeOf $}}
     Napi::Value has(const Napi::CallbackInfo& info) {
@@ -385,7 +398,10 @@
 
 Interface<{{$.Name}}> {{$.Name}}::Bind(Napi::Env env, std::unique_ptr<{{$.Name}}>&& impl) {
   auto* wrappers = Wrappers::For(env);
-  auto object = wrappers->{{$.Name}}_ctor.New({});
+  // Pass in kInternalConstructionMarker to allow this instance to actually be constructed.
+  auto object = wrappers->{{$.Name}}_ctor.New({
+    Napi::External<void>::New(env, &kInternalConstructionMarker)
+  });
   auto* wrapper = Wrappers::W{{$.Name}}::Unwrap(object);
   wrapper->impl = std::move(impl);
 
diff --git a/tools/src/cmd/idlgen/main.go b/tools/src/cmd/idlgen/main.go
index 4ddf628..8536382 100644
--- a/tools/src/cmd/idlgen/main.go
+++ b/tools/src/cmd/idlgen/main.go
@@ -126,6 +126,7 @@
 		"EnumEntryName":              enumEntryName,
 		"Eval":                       g.eval,
 		"HasAnnotation":              hasAnnotation,
+		"HasConstructor":             hasConstructor,
 		"FlattenedAttributesOf":      g.flattenedAttributesOf,
 		"FlattenedConstantsOf":       g.flattenedConstantsOf,
 		"FlattenedMethodsOf":         g.flattenedMethodsOf,
@@ -579,6 +580,17 @@
 	panic("Unhandled AST node type in hasAnnotation")
 }
 
+func hasConstructor(obj interface{}) bool {
+	iface := obj.(*ast.Interface)
+	for _, member := range iface.Members {
+		member := member.(*ast.Member)
+		if isInitializer(member) {
+			return true
+		}
+	}
+	return false
+}
+
 // Method describes a WebIDL interface method
 type Method struct {
 	// Name of the method