[node] implement several UNIMPLEMENTED methods

Change-Id: I314cf30c1286577a3219318af8605690c4a83dfa
Bug: dawn:1777
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/142465
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: snek <snek@chromium.org>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/dawn/node/README.md b/src/dawn/node/README.md
index 8c1932e..5a8c972 100644
--- a/src/dawn/node/README.md
+++ b/src/dawn/node/README.md
@@ -190,11 +190,7 @@
 
 ## Known issues
 
-- Many WebGPU CTS tests are currently known to fail
-- Dawn uses special token values for some parameters / fields. These are currently passed straight through to dawn from the JavaScript. discussions: [1](https://dawn-review.googlesource.com/c/dawn/+/64907/5/src/dawn/node/binding/Converter.cpp#167), [2](https://dawn-review.googlesource.com/c/dawn/+/64907/5/src/dawn/node/binding/Converter.cpp#928), [3](https://dawn-review.googlesource.com/c/dawn/+/64909/4/src/dawn/node/binding/GPUTexture.cpp#42)
-- Backend validation is currently always set to 'full' to aid in debugging. This can be extremely slow. [discussion](https://dawn-review.googlesource.com/c/dawn/+/64916/4/src/dawn/node/binding/GPU.cpp#25)
-- Attempting to call `new T` in JavaScript, where `T` is an IDL interface type, should result in a TypeError "Illegal constructor". [discussion](https://dawn-review.googlesource.com/c/dawn/+/64902/9/src/dawn/node/interop/WebGPU.cpp.tmpl#293)
-- `GPUDevice` currently maintains a list of "lost promises". This should return the same promise. [discussion](https://dawn-review.googlesource.com/c/dawn/+/64906/6/src/dawn/node/binding/GPUDevice.h#107)
+See https://bugs.chromium.org/p/dawn/issues/list?q=component%3ADawnNode&can=2 for tracked bugs, and `TODO`s in the code.
 
 ## Remaining work
 
diff --git a/src/dawn/node/binding/Converter.cpp b/src/dawn/node/binding/Converter.cpp
index 0686889..452b523 100644
--- a/src/dawn/node/binding/Converter.cpp
+++ b/src/dawn/node/binding/Converter.cpp
@@ -1448,7 +1448,7 @@
     }
     if (auto* res = std::get_if<interop::Interface<interop::GPUExternalTexture>>(&in.resource)) {
         // TODO(crbug.com/dawn/1129): External textures
-        UNIMPLEMENTED();
+        UNIMPLEMENTED(env, {});
     }
     Napi::Error::New(env, "invalid value for GPUBindGroupEntry.resource")
         .ThrowAsJavaScriptException();
@@ -1660,6 +1660,7 @@
         case wgpu::FeatureName::ImplicitDeviceSynchronization:
         case wgpu::FeatureName::SurfaceCapabilities:
         case wgpu::FeatureName::TransientAttachments:
+        case wgpu::FeatureName::MSAARenderToSingleSampled:
         case wgpu::FeatureName::Undefined:
             return false;
     }
@@ -1750,7 +1751,6 @@
 
 bool Converter::Convert(wgpu::RenderPipelineDescriptor& out,
                         const interop::GPURenderPipelineDescriptor& in) {
-    wgpu::RenderPipelineDescriptor desc{};
     return Convert(out.label, in.label) &&                //
            Convert(out.layout, in.layout) &&              //
            Convert(out.vertex, in.vertex) &&              //
diff --git a/src/dawn/node/binding/GPU.cpp b/src/dawn/node/binding/GPU.cpp
index 9a3ff79..eaf78ab 100644
--- a/src/dawn/node/binding/GPU.cpp
+++ b/src/dawn/node/binding/GPU.cpp
@@ -237,21 +237,19 @@
 }
 
 interop::GPUTextureFormat GPU::getPreferredCanvasFormat(Napi::Env) {
-    UNIMPLEMENTED();
+#if defined(__ANDROID__)
+    return interop::GPUTextureFormat::kRgba8Unorm;
+#else
+    return interop::GPUTextureFormat::kBgra8Unorm;
+#endif  // defined(__ANDROID__)
 }
 
 interop::Interface<interop::WGSLLanguageFeatures> GPU::getWgslLanguageFeatures(Napi::Env env) {
     // TODO(crbug.com/dawn/1777)
     struct Features : public interop::WGSLLanguageFeatures {
         ~Features() = default;
-        bool has(Napi::Env, std::string) {
-            UNIMPLEMENTED();
-            return false;
-        }
-        std::vector<std::string> keys(Napi::Env) {
-            UNIMPLEMENTED();
-            return {};
-        }
+        bool has(Napi::Env env, std::string) { UNIMPLEMENTED(env, {}); }
+        std::vector<std::string> keys(Napi::Env env) { UNIMPLEMENTED(env, {}); }
     };
     return interop::WGSLLanguageFeatures::Create<Features>(env);
 }
diff --git a/src/dawn/node/binding/GPUAdapter.cpp b/src/dawn/node/binding/GPUAdapter.cpp
index 922755c..ab86320 100644
--- a/src/dawn/node/binding/GPUAdapter.cpp
+++ b/src/dawn/node/binding/GPUAdapter.cpp
@@ -94,7 +94,9 @@
 }
 
 bool GPUAdapter::getIsFallbackAdapter(Napi::Env) {
-    UNIMPLEMENTED();
+    WGPUAdapterProperties adapterProperties = {};
+    adapter_.GetProperties(&adapterProperties);
+    return adapterProperties.adapterType == WGPUAdapterType_CPU;
 }
 
 interop::Promise<interop::Interface<interop::GPUDevice>> GPUAdapter::requestDevice(
@@ -143,7 +145,7 @@
 
     auto wgpu_device = adapter_.CreateDevice(&desc);
     if (wgpu_device) {
-        promise.Resolve(interop::GPUDevice::Create<GPUDevice>(env, env, wgpu_device));
+        promise.Resolve(interop::GPUDevice::Create<GPUDevice>(env, env, desc, wgpu_device));
     } else {
         promise.Reject(binding::Errors::OperationError(env, "failed to create device"));
     }
diff --git a/src/dawn/node/binding/GPUBindGroup.cpp b/src/dawn/node/binding/GPUBindGroup.cpp
index eef344f..6c4d8dc 100644
--- a/src/dawn/node/binding/GPUBindGroup.cpp
+++ b/src/dawn/node/binding/GPUBindGroup.cpp
@@ -23,14 +23,16 @@
 ////////////////////////////////////////////////////////////////////////////////
 // wgpu::bindings::GPUBindGroup
 ////////////////////////////////////////////////////////////////////////////////
-GPUBindGroup::GPUBindGroup(wgpu::BindGroup group) : group_(std::move(group)) {}
+GPUBindGroup::GPUBindGroup(const wgpu::BindGroupDescriptor& desc, wgpu::BindGroup group)
+    : group_(std::move(group)), label_(desc.label ? desc.label : "") {}
 
 std::string GPUBindGroup::getLabel(Napi::Env) {
-    UNIMPLEMENTED();
+    return label_;
 }
 
 void GPUBindGroup::setLabel(Napi::Env, std::string value) {
-    UNIMPLEMENTED();
+    group_.SetLabel(value.c_str());
+    label_ = value;
 }
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUBindGroup.h b/src/dawn/node/binding/GPUBindGroup.h
index 92d9ed7..4c8b877 100644
--- a/src/dawn/node/binding/GPUBindGroup.h
+++ b/src/dawn/node/binding/GPUBindGroup.h
@@ -27,7 +27,7 @@
 // GPUBindGroup is an implementation of interop::GPUBindGroup that wraps a wgpu::BindGroup.
 class GPUBindGroup final : public interop::GPUBindGroup {
   public:
-    explicit GPUBindGroup(wgpu::BindGroup group);
+    GPUBindGroup(const wgpu::BindGroupDescriptor& desc, wgpu::BindGroup group);
 
     // Implicit cast operator to Dawn GPU object
     inline operator const wgpu::BindGroup&() const { return group_; }
@@ -38,6 +38,7 @@
 
   private:
     wgpu::BindGroup group_;
+    std::string label_;
 };
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUBindGroupLayout.cpp b/src/dawn/node/binding/GPUBindGroupLayout.cpp
index 9538737..cc6a331 100644
--- a/src/dawn/node/binding/GPUBindGroupLayout.cpp
+++ b/src/dawn/node/binding/GPUBindGroupLayout.cpp
@@ -23,14 +23,17 @@
 ////////////////////////////////////////////////////////////////////////////////
 // wgpu::bindings::GPUBindGroupLayout
 ////////////////////////////////////////////////////////////////////////////////
-GPUBindGroupLayout::GPUBindGroupLayout(wgpu::BindGroupLayout layout) : layout_(std::move(layout)) {}
+GPUBindGroupLayout::GPUBindGroupLayout(const wgpu::BindGroupLayoutDescriptor& desc,
+                                       wgpu::BindGroupLayout layout)
+    : layout_(std::move(layout)), label_(desc.label ? desc.label : "") {}
 
 std::string GPUBindGroupLayout::getLabel(Napi::Env) {
-    UNIMPLEMENTED();
+    return label_;
 }
 
 void GPUBindGroupLayout::setLabel(Napi::Env, std::string value) {
-    UNIMPLEMENTED();
+    layout_.SetLabel(value.c_str());
+    label_ = value;
 }
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUBindGroupLayout.h b/src/dawn/node/binding/GPUBindGroupLayout.h
index 919acd1..993a1d3 100644
--- a/src/dawn/node/binding/GPUBindGroupLayout.h
+++ b/src/dawn/node/binding/GPUBindGroupLayout.h
@@ -28,7 +28,7 @@
 // wgpu::BindGroupLayout.
 class GPUBindGroupLayout final : public interop::GPUBindGroupLayout {
   public:
-    explicit GPUBindGroupLayout(wgpu::BindGroupLayout layout);
+    GPUBindGroupLayout(const wgpu::BindGroupLayoutDescriptor& desc, wgpu::BindGroupLayout layout);
 
     // Implicit cast operator to Dawn GPU object
     inline operator const wgpu::BindGroupLayout&() const { return layout_; }
@@ -39,6 +39,7 @@
 
   private:
     wgpu::BindGroupLayout layout_;
+    std::string label_;
 };
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUBuffer.cpp b/src/dawn/node/binding/GPUBuffer.cpp
index 02c6904..4ad7aeb 100644
--- a/src/dawn/node/binding/GPUBuffer.cpp
+++ b/src/dawn/node/binding/GPUBuffer.cpp
@@ -35,7 +35,8 @@
     : buffer_(std::move(buffer)),
       desc_(desc),
       device_(std::move(device)),
-      async_(std::move(async)) {
+      async_(std::move(async)),
+      label_(desc.label ? desc.label : "") {
     if (desc.mappedAtCreation) {
         state_ = State::MappedAtCreation;
     }
@@ -203,11 +204,12 @@
 }
 
 std::string GPUBuffer::getLabel(Napi::Env) {
-    UNIMPLEMENTED();
+    return label_;
 }
 
 void GPUBuffer::setLabel(Napi::Env, std::string value) {
-    UNIMPLEMENTED();
+    buffer_.SetLabel(value.c_str());
+    label_ = value;
 }
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUBuffer.h b/src/dawn/node/binding/GPUBuffer.h
index cc298cb..b756b9a 100644
--- a/src/dawn/node/binding/GPUBuffer.h
+++ b/src/dawn/node/binding/GPUBuffer.h
@@ -82,6 +82,7 @@
     std::shared_ptr<AsyncRunner> async_;
     State state_ = State::Unmapped;
     std::vector<Mapping> mapped_;
+    std::string label_;
 };
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUCommandBuffer.cpp b/src/dawn/node/binding/GPUCommandBuffer.cpp
index 4e7794e..ac9602f 100644
--- a/src/dawn/node/binding/GPUCommandBuffer.cpp
+++ b/src/dawn/node/binding/GPUCommandBuffer.cpp
@@ -24,14 +24,17 @@
 // wgpu::bindings::GPUCommandBuffer
 ////////////////////////////////////////////////////////////////////////////////
 
-GPUCommandBuffer::GPUCommandBuffer(wgpu::CommandBuffer cmd_buf) : cmd_buf_(std::move(cmd_buf)) {}
+GPUCommandBuffer::GPUCommandBuffer(const wgpu::CommandBufferDescriptor& desc,
+                                   wgpu::CommandBuffer cmd_buf)
+    : cmd_buf_(std::move(cmd_buf)), label_(desc.label ? desc.label : "") {}
 
 std::string GPUCommandBuffer::getLabel(Napi::Env) {
-    UNIMPLEMENTED();
+    return label_;
 }
 
 void GPUCommandBuffer::setLabel(Napi::Env, std::string value) {
-    UNIMPLEMENTED();
+    cmd_buf_.SetLabel(value.c_str());
+    label_ = value;
 }
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUCommandBuffer.h b/src/dawn/node/binding/GPUCommandBuffer.h
index b8ab08e..49c7cf2 100644
--- a/src/dawn/node/binding/GPUCommandBuffer.h
+++ b/src/dawn/node/binding/GPUCommandBuffer.h
@@ -28,7 +28,7 @@
 // wgpu::CommandBuffer.
 class GPUCommandBuffer final : public interop::GPUCommandBuffer {
   public:
-    explicit GPUCommandBuffer(wgpu::CommandBuffer cmd_buf);
+    GPUCommandBuffer(const wgpu::CommandBufferDescriptor& desc, wgpu::CommandBuffer cmd_buf);
 
     // Implicit cast operator to Dawn GPU object
     inline operator const wgpu::CommandBuffer&() const { return cmd_buf_; }
@@ -39,6 +39,7 @@
 
   private:
     wgpu::CommandBuffer cmd_buf_;
+    std::string label_;
 };
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUCommandEncoder.cpp b/src/dawn/node/binding/GPUCommandEncoder.cpp
index 6df3fce..2bfc064 100644
--- a/src/dawn/node/binding/GPUCommandEncoder.cpp
+++ b/src/dawn/node/binding/GPUCommandEncoder.cpp
@@ -31,8 +31,10 @@
 ////////////////////////////////////////////////////////////////////////////////
 // wgpu::bindings::GPUCommandEncoder
 ////////////////////////////////////////////////////////////////////////////////
-GPUCommandEncoder::GPUCommandEncoder(wgpu::Device device, wgpu::CommandEncoder enc)
-    : device_(std::move(device)), enc_(std::move(enc)) {}
+GPUCommandEncoder::GPUCommandEncoder(wgpu::Device device,
+                                     const wgpu::CommandEncoderDescriptor& desc,
+                                     wgpu::CommandEncoder enc)
+    : device_(std::move(device)), enc_(std::move(enc)), label_(desc.label ? desc.label : "") {}
 
 interop::Interface<interop::GPURenderPassEncoder> GPUCommandEncoder::beginRenderPass(
     Napi::Env env,
@@ -58,7 +60,7 @@
         return {};
     }
 
-    return interop::GPURenderPassEncoder::Create<GPURenderPassEncoder>(env,
+    return interop::GPURenderPassEncoder::Create<GPURenderPassEncoder>(env, desc,
                                                                        enc_.BeginRenderPass(&desc));
 }
 
@@ -72,9 +74,14 @@
         return {};
     }
 
+    Converter conv(env, device_);
+
     wgpu::ComputePassDescriptor desc{};
+    if (!conv(desc.label, descriptor.label)) {
+        return {};
+    }
     return interop::GPUComputePassEncoder::Create<GPUComputePassEncoder>(
-        env, enc_.BeginComputePass(&desc));
+        env, desc, enc_.BeginComputePass(&desc));
 }
 
 void GPUCommandEncoder::clearBuffer(Napi::Env env,
@@ -224,16 +231,21 @@
 interop::Interface<interop::GPUCommandBuffer> GPUCommandEncoder::finish(
     Napi::Env env,
     interop::GPUCommandBufferDescriptor descriptor) {
+    Converter conv(env);
     wgpu::CommandBufferDescriptor desc{};
-    return interop::GPUCommandBuffer::Create<GPUCommandBuffer>(env, enc_.Finish(&desc));
+    if (!conv(desc.label, descriptor.label)) {
+        return {};
+    }
+    return interop::GPUCommandBuffer::Create<GPUCommandBuffer>(env, desc, enc_.Finish(&desc));
 }
 
 std::string GPUCommandEncoder::getLabel(Napi::Env) {
-    UNIMPLEMENTED();
+    return label_;
 }
 
 void GPUCommandEncoder::setLabel(Napi::Env, std::string value) {
-    UNIMPLEMENTED();
+    enc_.SetLabel(value.c_str());
+    label_ = value;
 }
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUCommandEncoder.h b/src/dawn/node/binding/GPUCommandEncoder.h
index 3573274..2b14a06 100644
--- a/src/dawn/node/binding/GPUCommandEncoder.h
+++ b/src/dawn/node/binding/GPUCommandEncoder.h
@@ -28,7 +28,9 @@
 // wgpu::CommandEncoder.
 class GPUCommandEncoder final : public interop::GPUCommandEncoder {
   public:
-    GPUCommandEncoder(wgpu::Device device, wgpu::CommandEncoder enc);
+    GPUCommandEncoder(wgpu::Device device,
+                      const wgpu::CommandEncoderDescriptor& desc,
+                      wgpu::CommandEncoder enc);
 
     // interop::GPUCommandEncoder interface compliance
     interop::Interface<interop::GPURenderPassEncoder> beginRenderPass(
@@ -80,6 +82,7 @@
   private:
     wgpu::Device device_;
     wgpu::CommandEncoder enc_;
+    std::string label_;
 };
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUComputePassEncoder.cpp b/src/dawn/node/binding/GPUComputePassEncoder.cpp
index 88d6de2..849ba8f 100644
--- a/src/dawn/node/binding/GPUComputePassEncoder.cpp
+++ b/src/dawn/node/binding/GPUComputePassEncoder.cpp
@@ -28,7 +28,9 @@
 ////////////////////////////////////////////////////////////////////////////////
 // wgpu::bindings::GPUComputePassEncoder
 ////////////////////////////////////////////////////////////////////////////////
-GPUComputePassEncoder::GPUComputePassEncoder(wgpu::ComputePassEncoder enc) : enc_(std::move(enc)) {}
+GPUComputePassEncoder::GPUComputePassEncoder(const wgpu::ComputePassDescriptor& desc,
+                                             wgpu::ComputePassEncoder enc)
+    : enc_(std::move(enc)), label_(desc.label ? desc.label : "") {}
 
 void GPUComputePassEncoder::setPipeline(Napi::Env,
                                         interop::Interface<interop::GPUComputePipeline> pipeline) {
@@ -115,11 +117,12 @@
 }
 
 std::string GPUComputePassEncoder::getLabel(Napi::Env) {
-    UNIMPLEMENTED();
+    return label_;
 }
 
 void GPUComputePassEncoder::setLabel(Napi::Env, std::string value) {
-    UNIMPLEMENTED();
+    enc_.SetLabel(value.c_str());
+    label_ = value;
 }
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUComputePassEncoder.h b/src/dawn/node/binding/GPUComputePassEncoder.h
index 0428513..30b3c58 100644
--- a/src/dawn/node/binding/GPUComputePassEncoder.h
+++ b/src/dawn/node/binding/GPUComputePassEncoder.h
@@ -29,7 +29,7 @@
 // wgpu::ComputePassEncoder.
 class GPUComputePassEncoder final : public interop::GPUComputePassEncoder {
   public:
-    explicit GPUComputePassEncoder(wgpu::ComputePassEncoder enc);
+    GPUComputePassEncoder(const wgpu::ComputePassDescriptor& desc, wgpu::ComputePassEncoder enc);
 
     // Implicit cast operator to Dawn GPU object
     inline operator const wgpu::ComputePassEncoder&() const { return enc_; }
@@ -62,6 +62,7 @@
 
   private:
     wgpu::ComputePassEncoder enc_;
+    std::string label_;
 };
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUComputePipeline.cpp b/src/dawn/node/binding/GPUComputePipeline.cpp
index c2bfedf..bc211f0 100644
--- a/src/dawn/node/binding/GPUComputePipeline.cpp
+++ b/src/dawn/node/binding/GPUComputePipeline.cpp
@@ -25,22 +25,28 @@
 ////////////////////////////////////////////////////////////////////////////////
 // wgpu::bindings::GPUComputePipeline
 ////////////////////////////////////////////////////////////////////////////////
-GPUComputePipeline::GPUComputePipeline(wgpu::ComputePipeline pipeline)
-    : pipeline_(std::move(pipeline)) {}
+GPUComputePipeline::GPUComputePipeline(const wgpu::ComputePipelineDescriptor& desc,
+                                       wgpu::ComputePipeline pipeline)
+    : pipeline_(std::move(pipeline)), label_(desc.label ? desc.label : "") {}
+
+GPUComputePipeline::GPUComputePipeline(wgpu::ComputePipeline pipeline, std::string label)
+    : pipeline_(std::move(pipeline)), label_(label) {}
 
 interop::Interface<interop::GPUBindGroupLayout> GPUComputePipeline::getBindGroupLayout(
     Napi::Env env,
     uint32_t index) {
+    wgpu::BindGroupLayoutDescriptor desc{};
     return interop::GPUBindGroupLayout::Create<GPUBindGroupLayout>(
-        env, pipeline_.GetBindGroupLayout(index));
+        env, desc, pipeline_.GetBindGroupLayout(index));
 }
 
 std::string GPUComputePipeline::getLabel(Napi::Env) {
-    UNIMPLEMENTED();
+    return label_;
 }
 
 void GPUComputePipeline::setLabel(Napi::Env, std::string value) {
-    UNIMPLEMENTED();
+    pipeline_.SetLabel(value.c_str());
+    label_ = value;
 }
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUComputePipeline.h b/src/dawn/node/binding/GPUComputePipeline.h
index 45af9f7..6716fd5 100644
--- a/src/dawn/node/binding/GPUComputePipeline.h
+++ b/src/dawn/node/binding/GPUComputePipeline.h
@@ -28,7 +28,8 @@
 // wgpu::ComputePipeline.
 class GPUComputePipeline final : public interop::GPUComputePipeline {
   public:
-    explicit GPUComputePipeline(wgpu::ComputePipeline pipeline);
+    GPUComputePipeline(const wgpu::ComputePipelineDescriptor& desc, wgpu::ComputePipeline pipeline);
+    GPUComputePipeline(wgpu::ComputePipeline pipeline, std::string label);
 
     // Implicit cast operator to Dawn GPU object
     inline operator const wgpu::ComputePipeline&() const { return pipeline_; }
@@ -41,6 +42,7 @@
 
   private:
     wgpu::ComputePipeline pipeline_;
+    std::string label_;
 };
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUDevice.cpp b/src/dawn/node/binding/GPUDevice.cpp
index 020ffa1..9877211 100644
--- a/src/dawn/node/binding/GPUDevice.cpp
+++ b/src/dawn/node/binding/GPUDevice.cpp
@@ -126,11 +126,12 @@
 ////////////////////////////////////////////////////////////////////////////////
 // wgpu::bindings::GPUDevice
 ////////////////////////////////////////////////////////////////////////////////
-GPUDevice::GPUDevice(Napi::Env env, wgpu::Device device)
+GPUDevice::GPUDevice(Napi::Env env, const wgpu::DeviceDescriptor& desc, wgpu::Device device)
     : env_(env),
       device_(device),
       async_(std::make_shared<AsyncRunner>(env, device)),
-      lost_promise_(env, PROMISE_INFO) {
+      lost_promise_(env, PROMISE_INFO),
+      label_(desc.label ? desc.label : "") {
     device_.SetLoggingCallback(
         [](WGPULoggingType type, char const* message, void* userdata) {
             printf("%s:\n", str(type));
@@ -238,7 +239,8 @@
         !conv(desc.viewFormats, desc.viewFormatCount, descriptor.viewFormats)) {
         return {};
     }
-    return interop::GPUTexture::Create<GPUTexture>(env, device_, device_.CreateTexture(&desc));
+    return interop::GPUTexture::Create<GPUTexture>(env, device_, desc,
+                                                   device_.CreateTexture(&desc));
 }
 
 interop::Interface<interop::GPUSampler> GPUDevice::createSampler(
@@ -260,13 +262,13 @@
         !conv(desc.maxAnisotropy, descriptor.maxAnisotropy)) {
         return {};
     }
-    return interop::GPUSampler::Create<GPUSampler>(env, device_.CreateSampler(&desc));
+    return interop::GPUSampler::Create<GPUSampler>(env, desc, device_.CreateSampler(&desc));
 }
 
 interop::Interface<interop::GPUExternalTexture> GPUDevice::importExternalTexture(
-    Napi::Env,
+    Napi::Env env,
     interop::GPUExternalTextureDescriptor descriptor) {
-    UNIMPLEMENTED();
+    UNIMPLEMENTED(env, {});
 }
 
 interop::Interface<interop::GPUBindGroupLayout> GPUDevice::createBindGroupLayout(
@@ -281,7 +283,7 @@
     }
 
     return interop::GPUBindGroupLayout::Create<GPUBindGroupLayout>(
-        env, device_.CreateBindGroupLayout(&desc));
+        env, desc, device_.CreateBindGroupLayout(&desc));
 }
 
 interop::Interface<interop::GPUPipelineLayout> GPUDevice::createPipelineLayout(
@@ -296,7 +298,7 @@
     }
 
     return interop::GPUPipelineLayout::Create<GPUPipelineLayout>(
-        env, device_.CreatePipelineLayout(&desc));
+        env, desc, device_.CreatePipelineLayout(&desc));
 }
 
 interop::Interface<interop::GPUBindGroup> GPUDevice::createBindGroup(
@@ -310,7 +312,7 @@
         return {};
     }
 
-    return interop::GPUBindGroup::Create<GPUBindGroup>(env, device_.CreateBindGroup(&desc));
+    return interop::GPUBindGroup::Create<GPUBindGroup>(env, desc, device_.CreateBindGroup(&desc));
 }
 
 interop::Interface<interop::GPUShaderModule> GPUDevice::createShaderModule(
@@ -326,7 +328,7 @@
     sm_desc.nextInChain = &wgsl_desc;
 
     return interop::GPUShaderModule::Create<GPUShaderModule>(
-        env, device_.CreateShaderModule(&sm_desc), async_);
+        env, sm_desc, device_.CreateShaderModule(&sm_desc), async_);
 }
 
 interop::Interface<interop::GPUComputePipeline> GPUDevice::createComputePipeline(
@@ -340,7 +342,7 @@
     }
 
     return interop::GPUComputePipeline::Create<GPUComputePipeline>(
-        env, device_.CreateComputePipeline(&desc));
+        env, desc, device_.CreateComputePipeline(&desc));
 }
 
 interop::Interface<interop::GPURenderPipeline> GPUDevice::createRenderPipeline(
@@ -354,7 +356,7 @@
     }
 
     return interop::GPURenderPipeline::Create<GPURenderPipeline>(
-        env, device_.CreateRenderPipeline(&desc));
+        env, desc, device_.CreateRenderPipeline(&desc));
 }
 
 interop::Promise<interop::Interface<interop::GPUComputePipeline>>
@@ -375,8 +377,10 @@
         Napi::Env env;
         Promise promise;
         AsyncTask task;
+        std::string label;
     };
-    auto ctx = new Context{env, Promise(env, PROMISE_INFO), AsyncTask(async_)};
+    auto ctx = new Context{env, Promise(env, PROMISE_INFO), AsyncTask(async_),
+                           desc.label ? desc.label : ""};
     auto promise = ctx->promise;
 
     device_.CreateComputePipelineAsync(
@@ -387,8 +391,8 @@
 
             switch (status) {
                 case WGPUCreatePipelineAsyncStatus::WGPUCreatePipelineAsyncStatus_Success:
-                    c->promise.Resolve(
-                        interop::GPUComputePipeline::Create<GPUComputePipeline>(c->env, pipeline));
+                    c->promise.Resolve(interop::GPUComputePipeline::Create<GPUComputePipeline>(
+                        c->env, pipeline, c->label));
                     break;
                 default:
                     c->promise.Reject(Errors::OperationError(c->env));
@@ -418,8 +422,10 @@
         Napi::Env env;
         Promise promise;
         AsyncTask task;
+        std::string label;
     };
-    auto ctx = new Context{env, Promise(env, PROMISE_INFO), AsyncTask(async_)};
+    auto ctx = new Context{env, Promise(env, PROMISE_INFO), AsyncTask(async_),
+                           desc.label ? desc.label : ""};
     auto promise = ctx->promise;
 
     device_.CreateRenderPipelineAsync(
@@ -430,8 +436,8 @@
 
             switch (status) {
                 case WGPUCreatePipelineAsyncStatus::WGPUCreatePipelineAsyncStatus_Success:
-                    c->promise.Resolve(
-                        interop::GPURenderPipeline::Create<GPURenderPipeline>(c->env, pipeline));
+                    c->promise.Resolve(interop::GPURenderPipeline::Create<GPURenderPipeline>(
+                        c->env, pipeline, c->label));
                     break;
                 default:
                     c->promise.Reject(Errors::OperationError(c->env));
@@ -446,9 +452,13 @@
 interop::Interface<interop::GPUCommandEncoder> GPUDevice::createCommandEncoder(
     Napi::Env env,
     interop::GPUCommandEncoderDescriptor descriptor) {
+    Converter conv(env, device_);
     wgpu::CommandEncoderDescriptor desc{};
+    if (!conv(desc.label, descriptor.label)) {
+        return {};
+    }
     return interop::GPUCommandEncoder::Create<GPUCommandEncoder>(
-        env, device_, device_.CreateCommandEncoder(&desc));
+        env, device_, desc, device_.CreateCommandEncoder(&desc));
 }
 
 interop::Interface<interop::GPURenderBundleEncoder> GPUDevice::createRenderBundleEncoder(
@@ -467,7 +477,7 @@
     }
 
     return interop::GPURenderBundleEncoder::Create<GPURenderBundleEncoder>(
-        env, device_.CreateRenderBundleEncoder(&desc));
+        env, desc, device_.CreateRenderBundleEncoder(&desc));
 }
 
 interop::Interface<interop::GPUQuerySet> GPUDevice::createQuerySet(
@@ -481,7 +491,7 @@
         return {};
     }
 
-    return interop::GPUQuerySet::Create<GPUQuerySet>(env, device_.CreateQuerySet(&desc));
+    return interop::GPUQuerySet::Create<GPUQuerySet>(env, desc, device_.CreateQuerySet(&desc));
 }
 
 interop::Promise<interop::Interface<interop::GPUDeviceLostInfo>> GPUDevice::getLost(Napi::Env env) {
@@ -553,44 +563,46 @@
 }
 
 std::string GPUDevice::getLabel(Napi::Env) {
-    UNIMPLEMENTED();
+    return label_;
 }
 
 void GPUDevice::setLabel(Napi::Env, std::string value) {
-    UNIMPLEMENTED();
+    device_.SetLabel(value.c_str());
+    label_ = value;
 }
 
-interop::Interface<interop::EventHandler> GPUDevice::getOnuncapturederror(Napi::Env) {
+interop::Interface<interop::EventHandler> GPUDevice::getOnuncapturederror(Napi::Env env) {
     // TODO(dawn:1348): Implement support for the "unhandlederror" event.
-    UNIMPLEMENTED();
+    UNIMPLEMENTED(env, {});
 }
 
-void GPUDevice::setOnuncapturederror(Napi::Env, interop::Interface<interop::EventHandler> value) {
+void GPUDevice::setOnuncapturederror(Napi::Env env,
+                                     interop::Interface<interop::EventHandler> value) {
     // TODO(dawn:1348): Implement support for the "unhandlederror" event.
-    UNIMPLEMENTED();
+    UNIMPLEMENTED(env);
 }
 
 void GPUDevice::addEventListener(
-    Napi::Env,
+    Napi::Env env,
     std::string type,
     std::optional<interop::Interface<interop::EventListener>> callback,
     std::optional<std::variant<interop::AddEventListenerOptions, bool>> options) {
     // TODO(dawn:1348): Implement support for the "unhandlederror" event.
-    UNIMPLEMENTED();
+    UNIMPLEMENTED(env);
 }
 
 void GPUDevice::removeEventListener(
-    Napi::Env,
+    Napi::Env env,
     std::string type,
     std::optional<interop::Interface<interop::EventListener>> callback,
     std::optional<std::variant<interop::EventListenerOptions, bool>> options) {
     // TODO(dawn:1348): Implement support for the "unhandlederror" event.
-    UNIMPLEMENTED();
+    UNIMPLEMENTED(env);
 }
 
-bool GPUDevice::dispatchEvent(Napi::Env, interop::Interface<interop::Event> event) {
+bool GPUDevice::dispatchEvent(Napi::Env env, interop::Interface<interop::Event> event) {
     // TODO(dawn:1348): Implement support for the "unhandlederror" event.
-    UNIMPLEMENTED();
+    UNIMPLEMENTED(env, {});
 }
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUDevice.h b/src/dawn/node/binding/GPUDevice.h
index 9c53e9a..aba4b95 100644
--- a/src/dawn/node/binding/GPUDevice.h
+++ b/src/dawn/node/binding/GPUDevice.h
@@ -27,7 +27,7 @@
 // GPUDevice is an implementation of interop::GPUDevice that wraps a wgpu::Device.
 class GPUDevice final : public interop::GPUDevice {
   public:
-    GPUDevice(Napi::Env env, wgpu::Device device);
+    GPUDevice(Napi::Env env, const wgpu::DeviceDescriptor& desc, wgpu::Device device);
     ~GPUDevice();
 
     // interop::GPUDevice interface compliance
@@ -111,6 +111,7 @@
     // This promise's JS object lives as long as the device because it is stored in .lost
     // of the wrapper JS object.
     interop::Promise<interop::Interface<interop::GPUDeviceLostInfo>> lost_promise_;
+    std::string label_;
 
     bool destroyed_ = false;
 };
diff --git a/src/dawn/node/binding/GPUPipelineLayout.cpp b/src/dawn/node/binding/GPUPipelineLayout.cpp
index e6f1c1a..b95b94c 100644
--- a/src/dawn/node/binding/GPUPipelineLayout.cpp
+++ b/src/dawn/node/binding/GPUPipelineLayout.cpp
@@ -23,14 +23,17 @@
 ////////////////////////////////////////////////////////////////////////////////
 // wgpu::bindings::GPUPipelineLayout
 ////////////////////////////////////////////////////////////////////////////////
-GPUPipelineLayout::GPUPipelineLayout(wgpu::PipelineLayout layout) : layout_(std::move(layout)) {}
+GPUPipelineLayout::GPUPipelineLayout(const wgpu::PipelineLayoutDescriptor& desc,
+                                     wgpu::PipelineLayout layout)
+    : layout_(std::move(layout)), label_(desc.label ? desc.label : "") {}
 
 std::string GPUPipelineLayout::getLabel(Napi::Env) {
-    UNIMPLEMENTED();
+    return label_;
 }
 
 void GPUPipelineLayout::setLabel(Napi::Env, std::string value) {
-    UNIMPLEMENTED();
+    layout_.SetLabel(value.c_str());
+    label_ = value;
 }
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUPipelineLayout.h b/src/dawn/node/binding/GPUPipelineLayout.h
index f432e7a..26b2fd1 100644
--- a/src/dawn/node/binding/GPUPipelineLayout.h
+++ b/src/dawn/node/binding/GPUPipelineLayout.h
@@ -28,7 +28,7 @@
 // wgpu::PipelineLayout.
 class GPUPipelineLayout final : public interop::GPUPipelineLayout {
   public:
-    explicit GPUPipelineLayout(wgpu::PipelineLayout layout);
+    GPUPipelineLayout(const wgpu::PipelineLayoutDescriptor& desc, wgpu::PipelineLayout layout);
 
     // Implicit cast operator to Dawn GPU object
     inline operator const wgpu::PipelineLayout&() const { return layout_; }
@@ -39,6 +39,7 @@
 
   private:
     wgpu::PipelineLayout layout_;
+    std::string label_;
 };
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUQuerySet.cpp b/src/dawn/node/binding/GPUQuerySet.cpp
index e8b7850..eb0af7e 100644
--- a/src/dawn/node/binding/GPUQuerySet.cpp
+++ b/src/dawn/node/binding/GPUQuerySet.cpp
@@ -24,7 +24,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 // wgpu::bindings::GPUQuerySet
 ////////////////////////////////////////////////////////////////////////////////
-GPUQuerySet::GPUQuerySet(wgpu::QuerySet query_set) : query_set_(std::move(query_set)) {}
+GPUQuerySet::GPUQuerySet(const wgpu::QuerySetDescriptor& desc, wgpu::QuerySet query_set)
+    : query_set_(std::move(query_set)), label_(desc.label ? desc.label : "") {}
 
 void GPUQuerySet::destroy(Napi::Env) {
     query_set_.Destroy();
@@ -48,11 +49,12 @@
 }
 
 std::string GPUQuerySet::getLabel(Napi::Env) {
-    UNIMPLEMENTED();
+    return label_;
 }
 
 void GPUQuerySet::setLabel(Napi::Env, std::string value) {
-    UNIMPLEMENTED();
+    query_set_.SetLabel(value.c_str());
+    label_ = value;
 }
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUQuerySet.h b/src/dawn/node/binding/GPUQuerySet.h
index 9efe3d0..5044b64 100644
--- a/src/dawn/node/binding/GPUQuerySet.h
+++ b/src/dawn/node/binding/GPUQuerySet.h
@@ -27,7 +27,7 @@
 // GPUQuerySet is an implementation of interop::GPUQuerySet that wraps a wgpu::QuerySet.
 class GPUQuerySet final : public interop::GPUQuerySet {
   public:
-    explicit GPUQuerySet(wgpu::QuerySet query_set);
+    GPUQuerySet(const wgpu::QuerySetDescriptor& desc, wgpu::QuerySet query_set);
 
     // Implicit cast operator to Dawn GPU object
     inline operator const wgpu::QuerySet&() const { return query_set_; }
@@ -41,6 +41,7 @@
 
   private:
     wgpu::QuerySet query_set_;
+    std::string label_;
 };
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUQueue.cpp b/src/dawn/node/binding/GPUQueue.cpp
index d2ab912..28cb4a5 100644
--- a/src/dawn/node/binding/GPUQueue.cpp
+++ b/src/dawn/node/binding/GPUQueue.cpp
@@ -30,7 +30,7 @@
 // wgpu::bindings::GPUQueue
 ////////////////////////////////////////////////////////////////////////////////
 GPUQueue::GPUQueue(wgpu::Queue queue, std::shared_ptr<AsyncRunner> async)
-    : queue_(std::move(queue)), async_(std::move(async)) {}
+    : queue_(std::move(queue)), async_(std::move(async)), label_("") {}
 
 void GPUQueue::submit(Napi::Env env,
                       std::vector<interop::Interface<interop::GPUCommandBuffer>> commandBuffers) {
@@ -141,19 +141,20 @@
     queue_.WriteTexture(&dst, src.data, src.size, &layout, &sz);
 }
 
-void GPUQueue::copyExternalImageToTexture(Napi::Env,
+void GPUQueue::copyExternalImageToTexture(Napi::Env env,
                                           interop::GPUImageCopyExternalImage source,
                                           interop::GPUImageCopyTextureTagged destination,
                                           interop::GPUExtent3D copySize) {
-    UNIMPLEMENTED();
+    UNIMPLEMENTED(env);
 }
 
 std::string GPUQueue::getLabel(Napi::Env) {
-    UNIMPLEMENTED();
+    return label_;
 }
 
 void GPUQueue::setLabel(Napi::Env, std::string value) {
-    UNIMPLEMENTED();
+    queue_.SetLabel(value.c_str());
+    label_ = value;
 }
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUQueue.h b/src/dawn/node/binding/GPUQueue.h
index 58e5209..2e542d1 100644
--- a/src/dawn/node/binding/GPUQueue.h
+++ b/src/dawn/node/binding/GPUQueue.h
@@ -57,6 +57,7 @@
   private:
     wgpu::Queue queue_;
     std::shared_ptr<AsyncRunner> async_;
+    std::string label_;
 };
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPURenderBundle.cpp b/src/dawn/node/binding/GPURenderBundle.cpp
index 5c0ff2e..d4a1b03 100644
--- a/src/dawn/node/binding/GPURenderBundle.cpp
+++ b/src/dawn/node/binding/GPURenderBundle.cpp
@@ -26,14 +26,17 @@
 ////////////////////////////////////////////////////////////////////////////////
 // wgpu::bindings::GPURenderBundle
 ////////////////////////////////////////////////////////////////////////////////
-GPURenderBundle::GPURenderBundle(wgpu::RenderBundle bundle) : bundle_(std::move(bundle)) {}
+GPURenderBundle::GPURenderBundle(const wgpu::RenderBundleDescriptor& desc,
+                                 wgpu::RenderBundle bundle)
+    : bundle_(std::move(bundle)), label_(desc.label ? desc.label : "") {}
 
 std::string GPURenderBundle::getLabel(Napi::Env) {
-    UNIMPLEMENTED();
+    return label_;
 }
 
 void GPURenderBundle::setLabel(Napi::Env, std::string value) {
-    UNIMPLEMENTED();
+    bundle_.SetLabel(value.c_str());
+    label_ = value;
 }
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPURenderBundle.h b/src/dawn/node/binding/GPURenderBundle.h
index 23d0ad2..8d9ad8f 100644
--- a/src/dawn/node/binding/GPURenderBundle.h
+++ b/src/dawn/node/binding/GPURenderBundle.h
@@ -28,7 +28,7 @@
 // wgpu::RenderBundle.
 class GPURenderBundle final : public interop::GPURenderBundle {
   public:
-    explicit GPURenderBundle(wgpu::RenderBundle bundle);
+    GPURenderBundle(const wgpu::RenderBundleDescriptor& desc, wgpu::RenderBundle bundle);
 
     // Implicit cast operator to Dawn GPU object
     inline operator const wgpu::RenderBundle&() const { return bundle_; }
@@ -39,6 +39,7 @@
 
   private:
     wgpu::RenderBundle bundle_;
+    std::string label_;
 };
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPURenderBundleEncoder.cpp b/src/dawn/node/binding/GPURenderBundleEncoder.cpp
index 1f4f142..6eccf3d 100644
--- a/src/dawn/node/binding/GPURenderBundleEncoder.cpp
+++ b/src/dawn/node/binding/GPURenderBundleEncoder.cpp
@@ -28,15 +28,20 @@
 ////////////////////////////////////////////////////////////////////////////////
 // wgpu::bindings::GPURenderBundleEncoder
 ////////////////////////////////////////////////////////////////////////////////
-GPURenderBundleEncoder::GPURenderBundleEncoder(wgpu::RenderBundleEncoder enc)
-    : enc_(std::move(enc)) {}
+GPURenderBundleEncoder::GPURenderBundleEncoder(const RenderBundleEncoderDescriptor& desc,
+                                               wgpu::RenderBundleEncoder enc)
+    : enc_(std::move(enc)), label_(desc.label ? desc.label : "") {}
 
 interop::Interface<interop::GPURenderBundle> GPURenderBundleEncoder::finish(
     Napi::Env env,
     interop::GPURenderBundleDescriptor descriptor) {
     wgpu::RenderBundleDescriptor desc{};
+    Converter conv(env);
+    if (!conv(desc.label, descriptor.label)) {
+        return {};
+    }
 
-    return interop::GPURenderBundle::Create<GPURenderBundle>(env, enc_.Finish(&desc));
+    return interop::GPURenderBundle::Create<GPURenderBundle>(env, desc, enc_.Finish(&desc));
 }
 
 void GPURenderBundleEncoder::setBindGroup(
@@ -184,11 +189,12 @@
 }
 
 std::string GPURenderBundleEncoder::getLabel(Napi::Env) {
-    UNIMPLEMENTED();
+    return label_;
 }
 
 void GPURenderBundleEncoder::setLabel(Napi::Env, std::string value) {
-    UNIMPLEMENTED();
+    enc_.SetLabel(value.c_str());
+    label_ = value;
 }
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPURenderBundleEncoder.h b/src/dawn/node/binding/GPURenderBundleEncoder.h
index f9b087e..fd36420 100644
--- a/src/dawn/node/binding/GPURenderBundleEncoder.h
+++ b/src/dawn/node/binding/GPURenderBundleEncoder.h
@@ -29,7 +29,8 @@
 // wgpu::RenderBundleEncoder.
 class GPURenderBundleEncoder final : public interop::GPURenderBundleEncoder {
   public:
-    explicit GPURenderBundleEncoder(wgpu::RenderBundleEncoder enc);
+    GPURenderBundleEncoder(const wgpu::RenderBundleEncoderDescriptor& desc,
+                           wgpu::RenderBundleEncoder enc);
 
     // interop::GPURenderBundleEncoder interface compliance
     interop::Interface<interop::GPURenderBundle> finish(
@@ -81,6 +82,7 @@
 
   private:
     wgpu::RenderBundleEncoder enc_;
+    std::string label_;
 };
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPURenderPassEncoder.cpp b/src/dawn/node/binding/GPURenderPassEncoder.cpp
index 5fc95e4..bb5e953 100644
--- a/src/dawn/node/binding/GPURenderPassEncoder.cpp
+++ b/src/dawn/node/binding/GPURenderPassEncoder.cpp
@@ -29,7 +29,9 @@
 ////////////////////////////////////////////////////////////////////////////////
 // wgpu::bindings::GPURenderPassEncoder
 ////////////////////////////////////////////////////////////////////////////////
-GPURenderPassEncoder::GPURenderPassEncoder(wgpu::RenderPassEncoder enc) : enc_(std::move(enc)) {}
+GPURenderPassEncoder::GPURenderPassEncoder(const wgpu::RenderPassDescriptor& desc,
+                                           wgpu::RenderPassEncoder enc)
+    : enc_(std::move(enc)), label_(desc.label ? desc.label : "") {}
 
 void GPURenderPassEncoder::setViewport(Napi::Env,
                                        float x,
@@ -244,11 +246,12 @@
 }
 
 std::string GPURenderPassEncoder::getLabel(Napi::Env) {
-    UNIMPLEMENTED();
+    return label_;
 }
 
 void GPURenderPassEncoder::setLabel(Napi::Env, std::string value) {
-    UNIMPLEMENTED();
+    enc_.SetLabel(value.c_str());
+    label_ = value;
 }
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPURenderPassEncoder.h b/src/dawn/node/binding/GPURenderPassEncoder.h
index 5f682b7..ae7ee63 100644
--- a/src/dawn/node/binding/GPURenderPassEncoder.h
+++ b/src/dawn/node/binding/GPURenderPassEncoder.h
@@ -29,7 +29,7 @@
 // wgpu::RenderPassEncoder.
 class GPURenderPassEncoder final : public interop::GPURenderPassEncoder {
   public:
-    explicit GPURenderPassEncoder(wgpu::RenderPassEncoder enc);
+    GPURenderPassEncoder(const wgpu::RenderPassDescriptor& desc, wgpu::RenderPassEncoder enc);
 
     // Implicit cast operator to Dawn GPU object
     inline operator const wgpu::RenderPassEncoder&() const { return enc_; }
@@ -100,6 +100,7 @@
 
   private:
     wgpu::RenderPassEncoder enc_;
+    std::string label_;
 };
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPURenderPipeline.cpp b/src/dawn/node/binding/GPURenderPipeline.cpp
index cd236c0..c8129e4 100644
--- a/src/dawn/node/binding/GPURenderPipeline.cpp
+++ b/src/dawn/node/binding/GPURenderPipeline.cpp
@@ -25,22 +25,28 @@
 ////////////////////////////////////////////////////////////////////////////////
 // wgpu::bindings::GPURenderPipeline
 ////////////////////////////////////////////////////////////////////////////////
-GPURenderPipeline::GPURenderPipeline(wgpu::RenderPipeline pipeline)
-    : pipeline_(std::move(pipeline)) {}
+GPURenderPipeline::GPURenderPipeline(const wgpu::RenderPipelineDescriptor& desc,
+                                     wgpu::RenderPipeline pipeline)
+    : pipeline_(std::move(pipeline)), label_(desc.label ? desc.label : "") {}
+
+GPURenderPipeline::GPURenderPipeline(wgpu::RenderPipeline pipeline, std::string label)
+    : pipeline_(std::move(pipeline)), label_(label) {}
 
 interop::Interface<interop::GPUBindGroupLayout> GPURenderPipeline::getBindGroupLayout(
     Napi::Env env,
     uint32_t index) {
+    wgpu::BindGroupLayoutDescriptor desc{};
     return interop::GPUBindGroupLayout::Create<GPUBindGroupLayout>(
-        env, pipeline_.GetBindGroupLayout(index));
+        env, desc, pipeline_.GetBindGroupLayout(index));
 }
 
 std::string GPURenderPipeline::getLabel(Napi::Env) {
-    UNIMPLEMENTED();
+    return label_;
 }
 
 void GPURenderPipeline::setLabel(Napi::Env, std::string value) {
-    UNIMPLEMENTED();
+    pipeline_.SetLabel(value.c_str());
+    label_ = value;
 }
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPURenderPipeline.h b/src/dawn/node/binding/GPURenderPipeline.h
index 26f8ad6..f43b2a9 100644
--- a/src/dawn/node/binding/GPURenderPipeline.h
+++ b/src/dawn/node/binding/GPURenderPipeline.h
@@ -28,7 +28,8 @@
 // wgpu::RenderPipeline.
 class GPURenderPipeline final : public interop::GPURenderPipeline {
   public:
-    explicit GPURenderPipeline(wgpu::RenderPipeline pipeline);
+    GPURenderPipeline(const wgpu::RenderPipelineDescriptor& desc, wgpu::RenderPipeline pipeline);
+    GPURenderPipeline(wgpu::RenderPipeline pipeline, std::string label);
 
     // Implicit cast operator to Dawn GPU object
     inline operator const wgpu::RenderPipeline&() const { return pipeline_; }
@@ -41,6 +42,7 @@
 
   private:
     wgpu::RenderPipeline pipeline_;
+    std::string label_;
 };
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUSampler.cpp b/src/dawn/node/binding/GPUSampler.cpp
index 77f395d..b0c0a20 100644
--- a/src/dawn/node/binding/GPUSampler.cpp
+++ b/src/dawn/node/binding/GPUSampler.cpp
@@ -24,14 +24,16 @@
 ////////////////////////////////////////////////////////////////////////////////
 // wgpu::bindings::GPUSampler
 ////////////////////////////////////////////////////////////////////////////////
-GPUSampler::GPUSampler(wgpu::Sampler sampler) : sampler_(std::move(sampler)) {}
+GPUSampler::GPUSampler(const wgpu::SamplerDescriptor& desc, wgpu::Sampler sampler)
+    : sampler_(std::move(sampler)), label_(desc.label ? desc.label : "") {}
 
 std::string GPUSampler::getLabel(Napi::Env) {
-    UNIMPLEMENTED();
+    return label_;
 }
 
 void GPUSampler::setLabel(Napi::Env, std::string value) {
-    UNIMPLEMENTED();
+    sampler_.SetLabel(value.c_str());
+    label_ = value;
 }
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUSampler.h b/src/dawn/node/binding/GPUSampler.h
index 578469d..61b2d33 100644
--- a/src/dawn/node/binding/GPUSampler.h
+++ b/src/dawn/node/binding/GPUSampler.h
@@ -26,7 +26,7 @@
 // GPUSampler is an implementation of interop::GPUSampler that wraps a wgpu::Sampler.
 class GPUSampler final : public interop::GPUSampler {
   public:
-    explicit GPUSampler(wgpu::Sampler sampler);
+    GPUSampler(const wgpu::SamplerDescriptor& desc, wgpu::Sampler sampler);
 
     // Implicit cast operator to Dawn GPU object
     inline operator const wgpu::Sampler&() const { return sampler_; }
@@ -37,6 +37,7 @@
 
   private:
     wgpu::Sampler sampler_;
+    std::string label_;
 };
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUShaderModule.cpp b/src/dawn/node/binding/GPUShaderModule.cpp
index 953c3e2..d7c87da 100644
--- a/src/dawn/node/binding/GPUShaderModule.cpp
+++ b/src/dawn/node/binding/GPUShaderModule.cpp
@@ -25,8 +25,10 @@
 ////////////////////////////////////////////////////////////////////////////////
 // wgpu::bindings::GPUShaderModule
 ////////////////////////////////////////////////////////////////////////////////
-GPUShaderModule::GPUShaderModule(wgpu::ShaderModule shader, std::shared_ptr<AsyncRunner> async)
-    : shader_(std::move(shader)), async_(std::move(async)) {}
+GPUShaderModule::GPUShaderModule(const wgpu::ShaderModuleDescriptor& desc,
+                                 wgpu::ShaderModule shader,
+                                 std::shared_ptr<AsyncRunner> async)
+    : shader_(std::move(shader)), async_(std::move(async)), label_(desc.label ? desc.label : "") {}
 
 interop::Promise<interop::Interface<interop::GPUCompilationInfo>>
 GPUShaderModule::getCompilationInfo(Napi::Env env) {
@@ -44,7 +46,7 @@
                 case WGPUCompilationMessageType_Info:
                     return interop::GPUCompilationMessageType::kInfo;
                 default:
-                    UNIMPLEMENTED();
+                    UNREACHABLE();
             }
         }
         uint64_t getLineNum(Napi::Env) override { return message.lineNum; }
@@ -105,11 +107,12 @@
 }
 
 std::string GPUShaderModule::getLabel(Napi::Env) {
-    UNIMPLEMENTED();
+    return label_;
 }
 
 void GPUShaderModule::setLabel(Napi::Env, std::string value) {
-    UNIMPLEMENTED();
+    shader_.SetLabel(value.c_str());
+    label_ = value;
 }
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUShaderModule.h b/src/dawn/node/binding/GPUShaderModule.h
index dc4deca..7f964e6 100644
--- a/src/dawn/node/binding/GPUShaderModule.h
+++ b/src/dawn/node/binding/GPUShaderModule.h
@@ -30,7 +30,9 @@
 // wgpu::ShaderModule.
 class GPUShaderModule final : public interop::GPUShaderModule {
   public:
-    GPUShaderModule(wgpu::ShaderModule shader, std::shared_ptr<AsyncRunner> async);
+    GPUShaderModule(const wgpu::ShaderModuleDescriptor& desc,
+                    wgpu::ShaderModule shader,
+                    std::shared_ptr<AsyncRunner> async);
 
     // Implicit cast operator to Dawn GPU object
     inline operator const wgpu::ShaderModule&() const { return shader_; }
@@ -44,6 +46,7 @@
   private:
     wgpu::ShaderModule shader_;
     std::shared_ptr<AsyncRunner> async_;
+    std::string label_;
 };
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUTexture.cpp b/src/dawn/node/binding/GPUTexture.cpp
index be2b898..ac57893 100644
--- a/src/dawn/node/binding/GPUTexture.cpp
+++ b/src/dawn/node/binding/GPUTexture.cpp
@@ -26,8 +26,12 @@
 ////////////////////////////////////////////////////////////////////////////////
 // wgpu::bindings::GPUTexture
 ////////////////////////////////////////////////////////////////////////////////
-GPUTexture::GPUTexture(wgpu::Device device, wgpu::Texture texture)
-    : device_(std::move(device)), texture_(std::move(texture)) {}
+GPUTexture::GPUTexture(wgpu::Device device,
+                       const wgpu::TextureDescriptor& desc,
+                       wgpu::Texture texture)
+    : device_(std::move(device)),
+      texture_(std::move(texture)),
+      label_(desc.label ? desc.label : "") {}
 
 interop::Interface<interop::GPUTextureView> GPUTexture::createView(
     Napi::Env env,
@@ -45,10 +49,10 @@
         !conv(desc.arrayLayerCount, descriptor.arrayLayerCount) ||  //
         !conv(desc.format, descriptor.format) ||                    //
         !conv(desc.dimension, descriptor.dimension) ||              //
-        !conv(desc.aspect, descriptor.aspect)) {
+        !conv(desc.aspect, descriptor.aspect) || !conv(desc.label, descriptor.label)) {
         return {};
     }
-    return interop::GPUTextureView::Create<GPUTextureView>(env, texture_.CreateView(&desc));
+    return interop::GPUTextureView::Create<GPUTextureView>(env, desc, texture_.CreateView(&desc));
 }
 
 void GPUTexture::destroy(Napi::Env) {
@@ -115,11 +119,12 @@
 }
 
 std::string GPUTexture::getLabel(Napi::Env) {
-    UNIMPLEMENTED();
+    return label_;
 }
 
 void GPUTexture::setLabel(Napi::Env, std::string value) {
-    UNIMPLEMENTED();
+    texture_.SetLabel(value.c_str());
+    label_ = value;
 }
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUTexture.h b/src/dawn/node/binding/GPUTexture.h
index 0a3d936..677a1ff 100644
--- a/src/dawn/node/binding/GPUTexture.h
+++ b/src/dawn/node/binding/GPUTexture.h
@@ -27,7 +27,7 @@
 // GPUTexture is an implementation of interop::GPUTexture that wraps a wgpu::Texture.
 class GPUTexture final : public interop::GPUTexture {
   public:
-    explicit GPUTexture(wgpu::Device device, wgpu::Texture texture);
+    GPUTexture(wgpu::Device device, const wgpu::TextureDescriptor& desc, wgpu::Texture texture);
 
     // Implicit cast operator to Dawn GPU object
     inline operator const wgpu::Texture&() const { return texture_; }
@@ -51,6 +51,7 @@
   private:
     wgpu::Device device_;
     wgpu::Texture texture_;
+    std::string label_;
 };
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUTextureView.cpp b/src/dawn/node/binding/GPUTextureView.cpp
index 98ce3a04..6215472 100644
--- a/src/dawn/node/binding/GPUTextureView.cpp
+++ b/src/dawn/node/binding/GPUTextureView.cpp
@@ -23,14 +23,16 @@
 ////////////////////////////////////////////////////////////////////////////////
 // wgpu::bindings::GPUTextureView
 ////////////////////////////////////////////////////////////////////////////////
-GPUTextureView::GPUTextureView(wgpu::TextureView view) : view_(std::move(view)) {}
+GPUTextureView::GPUTextureView(const wgpu::TextureViewDescriptor& desc, wgpu::TextureView view)
+    : view_(std::move(view)), label_(desc.label ? desc.label : "") {}
 
 std::string GPUTextureView::getLabel(Napi::Env) {
-    UNIMPLEMENTED();
+    return label_;
 }
 
 void GPUTextureView::setLabel(Napi::Env, std::string value) {
-    UNIMPLEMENTED();
+    view_.SetLabel(value.c_str());
+    label_ = value;
 }
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/binding/GPUTextureView.h b/src/dawn/node/binding/GPUTextureView.h
index 494c307..2ad0da8 100644
--- a/src/dawn/node/binding/GPUTextureView.h
+++ b/src/dawn/node/binding/GPUTextureView.h
@@ -28,7 +28,7 @@
 // wgpu::TextureView.
 class GPUTextureView final : public interop::GPUTextureView {
   public:
-    explicit GPUTextureView(wgpu::TextureView view);
+    GPUTextureView(const wgpu::TextureViewDescriptor& desc, wgpu::TextureView view);
 
     // Implicit cast operator to Dawn GPU object
     inline operator const wgpu::TextureView&() const { return view_; }
@@ -39,6 +39,7 @@
 
   private:
     wgpu::TextureView view_;
+    std::string label_;
 };
 
 }  // namespace wgpu::binding
diff --git a/src/dawn/node/interop/Core.h b/src/dawn/node/interop/Core.h
index 4f90b19..8bbfb80 100644
--- a/src/dawn/node/interop/Core.h
+++ b/src/dawn/node/interop/Core.h
@@ -661,7 +661,7 @@
 template <typename T>
 class Converter<Promise<T>> {
   public:
-    static inline Result FromJS(Napi::Env, Napi::Value, Promise<T>&) { UNIMPLEMENTED(); }
+    static inline Result FromJS(Napi::Env env, Napi::Value, Promise<T>&) { UNIMPLEMENTED(env, {}); }
     static inline Napi::Value ToJS(Napi::Env, Promise<T> promise) { return promise; }
 };
 
diff --git a/src/dawn/node/utils/Debug.h b/src/dawn/node/utils/Debug.h
index 66cd4da..f919303 100644
--- a/src/dawn/node/utils/Debug.h
+++ b/src/dawn/node/utils/Debug.h
@@ -128,12 +128,12 @@
                          ##__VA_ARGS__)                                                           \
         << std::endl
 
-// UNIMPLEMENTED() prints 'UNIMPLEMENTED' with the current file, line and
-// function to stdout, along with the optional message, then calls abort().
-// The macro calls Fatal(), which is annotated with [[noreturn]].
-// Used to stub code that has not yet been implemented.
-#define UNIMPLEMENTED(...) \
-    ::wgpu::utils::Fatal("UNIMPLEMENTED", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)
+// UNIMPLEMENTED(env) raises a JS exception. Used to stub code that has not yet been implemented.
+#define UNIMPLEMENTED(env, ...)                                              \
+    do {                                                                     \
+        Napi::Error::New(env, "UNIMPLEMENTED").ThrowAsJavaScriptException(); \
+        return __VA_ARGS__;                                                  \
+    } while (false)
 
 // UNREACHABLE() prints 'UNREACHABLE' with the current file, line and
 // function to stdout, along with the optional message, then calls abort().