dawn_node: add support for input flags

This change replaces the ".gpu" export with ".create()" function that
accepts an array of flags. These will be used by cmdline.ts to set flags
such as what dawn backend to use. We currenly environment variables, but
this will be more flexible.

Bug: dawn:1163
Change-Id: If2fb35811cac45e16121fbd828f997ef3d795f36
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/66960
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/dawn_node/Module.cpp b/src/dawn_node/Module.cpp
index d7669b8..96fce3f 100644
--- a/src/dawn_node/Module.cpp
+++ b/src/dawn_node/Module.cpp
@@ -13,10 +13,41 @@
 // limitations under the License.
 
 #include "dawn/dawn_proc.h"
+#include "src/dawn_node/binding/Flags.h"
 #include "src/dawn_node/binding/GPU.h"
 
+namespace {
+    Napi::Value CreateGPU(const Napi::CallbackInfo& info) {
+        const auto& env = info.Env();
+
+        std::tuple<std::vector<std::string>> args;
+        auto res = wgpu::interop::FromJS(info, args);
+        if (res != wgpu::interop::Success) {
+            Napi::Error::New(env, res.error).ThrowAsJavaScriptException();
+            return env.Undefined();
+        }
+
+        wgpu::binding::Flags flags;
+
+        // Parse out the key=value flags out of the input args array
+        for (const auto& arg : std::get<0>(args)) {
+            const size_t sep_index = arg.find("=");
+            if (sep_index == std::string::npos) {
+                Napi::Error::New(env, "Flags expected argument format is <key>=<value>")
+                    .ThrowAsJavaScriptException();
+                return env.Undefined();
+            }
+            flags.Set(arg.substr(0, sep_index), arg.substr(sep_index + 1));
+        }
+
+        // Construct a wgpu::interop::GPU interface, implemented by wgpu::bindings::GPU.
+        return wgpu::interop::GPU::Create<wgpu::binding::GPU>(env, std::move(flags));
+    }
+
+}  // namespace
+
 // Initialize() initializes the Dawn node module, registering all the WebGPU
-// types into the global object, and adding the 'gpu' property on the exported
+// types into the global object, and adding the 'create' function on the exported
 // object.
 Napi::Object Initialize(Napi::Env env, Napi::Object exports) {
     // Begin by setting the Dawn procedure function pointers.
@@ -25,10 +56,9 @@
     // Register all the interop types
     wgpu::interop::Initialize(env);
 
-    // Construct a wgpu::interop::GPU interface, implemented by
-    // wgpu::bindings::GPU. This will be the 'gpu' field of exported object.
-    auto gpu = wgpu::interop::GPU::Create<wgpu::binding::GPU>(env);
-    exports.Set(Napi::String::New(env, "gpu"), gpu);
+    // Export function that creates and returns the wgpu::interop::GPU interface
+    exports.Set(Napi::String::New(env, "create"), Napi::Function::New<CreateGPU>(env));
+
     return exports;
 }
 
diff --git a/src/dawn_node/binding/CMakeLists.txt b/src/dawn_node/binding/CMakeLists.txt
index 554459d..be0d508 100644
--- a/src/dawn_node/binding/CMakeLists.txt
+++ b/src/dawn_node/binding/CMakeLists.txt
@@ -19,6 +19,8 @@
     "Converter.h"
     "Errors.cpp"
     "Errors.h"
+    "Flags.cpp"
+    "Flags.h"
     "GPU.cpp"
     "GPU.h"
     "GPUAdapter.cpp"
diff --git a/src/dawn_node/binding/Flags.cpp b/src/dawn_node/binding/Flags.cpp
new file mode 100644
index 0000000..3602e92
--- /dev/null
+++ b/src/dawn_node/binding/Flags.cpp
@@ -0,0 +1,29 @@
+// Copyright 2021 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/dawn_node/binding/Flags.h"
+
+namespace wgpu { namespace binding {
+    void Flags::Set(const std::string& key, const std::string& value) {
+        flags_[key] = value;
+    }
+
+    std::optional<std::string> Flags::Get(const std::string& key) const {
+        auto iter = flags_.find(key);
+        if (iter != flags_.end()) {
+            return iter->second;
+        }
+        return {};
+    }
+}}  // namespace wgpu::binding
diff --git a/src/dawn_node/binding/Flags.h b/src/dawn_node/binding/Flags.h
new file mode 100644
index 0000000..1ca4c30
--- /dev/null
+++ b/src/dawn_node/binding/Flags.h
@@ -0,0 +1,35 @@
+// Copyright 2021 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef DAWN_NODE_BINDING_FLAGS_H_
+#define DAWN_NODE_BINDING_FLAGS_H_
+
+#include <optional>
+#include <string>
+#include <unordered_map>
+
+namespace wgpu { namespace binding {
+    // Flags maintains a key-value mapping of input flags passed into the module's create()
+    // function, used to configure dawn_node.
+    class Flags {
+      public:
+        void Set(const std::string& key, const std::string& value);
+        std::optional<std::string> Get(const std::string& key) const;
+
+      private:
+        std::unordered_map<std::string, std::string> flags_;
+    };
+}}  // namespace wgpu::binding
+
+#endif  // DAWN_NODE_BINDING_FLAGS_H_
diff --git a/src/dawn_node/binding/GPU.cpp b/src/dawn_node/binding/GPU.cpp
index 61e4d6f..52b5f9c 100644
--- a/src/dawn_node/binding/GPU.cpp
+++ b/src/dawn_node/binding/GPU.cpp
@@ -44,7 +44,7 @@
     ////////////////////////////////////////////////////////////////////////////////
     // wgpu::bindings::GPU
     ////////////////////////////////////////////////////////////////////////////////
-    GPU::GPU() {
+    GPU::GPU(Flags flags) : flags_(std::move(flags)) {
         // TODO: Disable in 'release'
         instance_.EnableBackendValidation(true);
         instance_.SetBackendValidationLevel(dawn_native::BackendValidationLevel::Full);
diff --git a/src/dawn_node/binding/GPU.h b/src/dawn_node/binding/GPU.h
index 131ce42..8f1999d 100644
--- a/src/dawn_node/binding/GPU.h
+++ b/src/dawn_node/binding/GPU.h
@@ -18,14 +18,14 @@
 #include "dawn/webgpu_cpp.h"
 #include "dawn_native/DawnNative.h"
 #include "napi.h"
+#include "src/dawn_node/binding/Flags.h"
 #include "src/dawn_node/interop/WebGPU.h"
 
 namespace wgpu { namespace binding {
-
     // GPU is an implementation of interop::GPU that wraps a dawn_native::Instance.
     class GPU final : public interop::GPU {
       public:
-        GPU();
+        GPU(Flags flags);
 
         // interop::GPU interface compliance
         interop::Promise<std::optional<interop::Interface<interop::GPUAdapter>>> requestAdapter(
@@ -33,6 +33,7 @@
             interop::GPURequestAdapterOptions options) override;
 
       private:
+        const Flags flags_;
         dawn_native::Instance instance_;
     };
 
diff --git a/src/dawn_node/interop/Core.h b/src/dawn_node/interop/Core.h
index dba1ad0..dec73a6 100644
--- a/src/dawn_node/interop/Core.h
+++ b/src/dawn_node/interop/Core.h
@@ -492,7 +492,7 @@
             auto arr = value.As<Napi::Array>();
             std::vector<T> vec(arr.Length());
             for (size_t i = 0; i < vec.size(); i++) {
-                auto res = Converter<T>::FromJS(env, arr[i], vec[i]);
+                auto res = Converter<T>::FromJS(env, arr[static_cast<uint32_t>(i)], vec[i]);
                 if (!res) {
                     return res.Append("for array element ", i);
                 }