[dawn][emscripten] Implements getCompilationInfo future entry point.

Bug: 372080121
Change-Id: I03cdc8cdff57d2896faa7c8286bf620fa53bae88
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/210134
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Loko Kung <lokokung@google.com>
diff --git a/third_party/emdawnwebgpu/library_webgpu.js b/third_party/emdawnwebgpu/library_webgpu.js
index 6e52120..2c15d36 100644
--- a/third_party/emdawnwebgpu/library_webgpu.js
+++ b/third_party/emdawnwebgpu/library_webgpu.js
@@ -1660,8 +1660,7 @@
     return ptr;
   },
 
-  wgpuDeviceCreateShaderModule__deps: ['emwgpuCreateShaderModule'],
-  wgpuDeviceCreateShaderModule: (devicePtr, descriptor) => {
+  emwgpuDeviceCreateShaderModule: (devicePtr, descriptor, shaderModulePtr) => {
     {{{ gpu.makeCheck('descriptor') }}}
     var nextInChainPtr = {{{ makeGetValue('descriptor', C_STRUCTS.WGPUShaderModuleDescriptor.nextInChain, '*') }}};
 #if ASSERTIONS
@@ -1702,9 +1701,7 @@
     }
 
     var device = WebGPU.getJsObject(devicePtr);
-    var ptr = _emwgpuCreateShaderModule();
-    WebGPU.Internals.jsObjectInsert(ptr, device.createShaderModule(desc));
-    return ptr;
+    WebGPU.Internals.jsObjectInsert(shaderModulePtr, device.createShaderModule(desc));
   },
 
   wgpuDeviceCreateTexture__deps: ['emwgpuCreateTexture'],
@@ -2271,47 +2268,61 @@
   // Methods of ShaderModule
   // --------------------------------------------------------------------------
 
-  wgpuShaderModuleGetCompilationInfo__deps: ['$callUserCallback', '$stringToUTF8', '$lengthBytesUTF8', 'malloc', 'free'],
-  wgpuShaderModuleGetCompilationInfo: (shaderModulePtr, callback, userdata) => {
+  emwgpuShaderModuleGetCompilationInfo__i53abi: false,
+  emwgpuShaderModuleGetCompilationInfo__deps: ['emwgpuOnCompilationInfoCompleted', '$stringToUTF8', '$lengthBytesUTF8', 'malloc'],
+  emwgpuShaderModuleGetCompilationInfo: (shaderModulePtr, futureIdL, futureIdH, compilationInfoPtr) => {
     var shaderModule = WebGPU.getJsObject(shaderModulePtr);
     {{{ runtimeKeepalivePush() }}}
-    shaderModule.getCompilationInfo().then((compilationInfo) => {
+    WebGPU.Internals.futureInsert(futureIdL, futureIdH, shaderModule.getCompilationInfo().then((compilationInfo) => {
       {{{ runtimeKeepalivePop() }}}
-      callUserCallback(() => {
-        var compilationMessagesPtr = _malloc({{{ C_STRUCTS.WGPUCompilationMessage.__size__ }}} * compilationInfo.messages.length);
-        var messageStringPtrs = []; // save these to free later
-        for (var i = 0; i < compilationInfo.messages.length; ++i) {
-          var compilationMessage = compilationInfo.messages[i];
-          var compilationMessagePtr = compilationMessagesPtr + {{{ C_STRUCTS.WGPUCompilationMessage.__size__ }}} * i;
-          var messageSize = lengthBytesUTF8(compilationMessage.message) + 1;
-          var messagePtr = _malloc(messageSize);
-          messageStringPtrs.push(messagePtr);
-          stringToUTF8(compilationMessage.message, messagePtr, messageSize);
-          {{{ makeSetValue('compilationMessagePtr', C_STRUCTS.WGPUCompilationMessage.message, 'messagePtr', '*') }}};
-          {{{ makeSetValue('compilationMessagePtr', C_STRUCTS.WGPUCompilationMessage.type, 'WebGPU.Int_CompilationMessageType[compilationMessage.type]', 'i32') }}};
-          {{{ makeSetValue('compilationMessagePtr', C_STRUCTS.WGPUCompilationMessage.lineNum, 'compilationMessage.lineNum', 'i64') }}};
-          {{{ makeSetValue('compilationMessagePtr', C_STRUCTS.WGPUCompilationMessage.linePos, 'compilationMessage.linePos', 'i64') }}};
-          {{{ makeSetValue('compilationMessagePtr', C_STRUCTS.WGPUCompilationMessage.offset, 'compilationMessage.offset', 'i64') }}};
-          {{{ makeSetValue('compilationMessagePtr', C_STRUCTS.WGPUCompilationMessage.length, 'compilationMessage.length', 'i64') }}};
-          // TODO: Convert JavaScript's UTF-16-code-unit offsets to UTF-8-code-unit offsets.
-          // https://github.com/webgpu-native/webgpu-headers/issues/246
-          {{{ makeSetValue('compilationMessagePtr', C_STRUCTS.WGPUCompilationMessage.utf16LinePos, 'compilationMessage.linePos', 'i64') }}};
-          {{{ makeSetValue('compilationMessagePtr', C_STRUCTS.WGPUCompilationMessage.utf16Offset, 'compilationMessage.offset', 'i64') }}};
-          {{{ makeSetValue('compilationMessagePtr', C_STRUCTS.WGPUCompilationMessage.utf16Length, 'compilationMessage.length', 'i64') }}};
-        }
-        var compilationInfoPtr = _malloc({{{ C_STRUCTS.WGPUCompilationInfo.__size__ }}});
-        {{{ makeSetValue('compilationInfoPtr', C_STRUCTS.WGPUCompilationInfo.messageCount, 'compilationInfo.messages.length', '*') }}}
-        {{{ makeSetValue('compilationInfoPtr', C_STRUCTS.WGPUCompilationInfo.messages, 'compilationMessagesPtr', '*') }}};
+      // Calculate the total length of strings and offsets here to malloc them
+      // all at once. Note that we start at 1 instead of 0 for the total size
+      // to ensure there's enough space for the null terminator that is always
+      // added by stringToUTF8.
+      var totalMessagesSize = 1;
+      var messageLengths = [];
+      for (var i = 0; i < compilationInfo.messages.length; ++i) {
+        var messageLength = lengthBytesUTF8(compilationInfo.messages[i].message);
+        totalMessagesSize += messageLength;
+        messageLengths.push(messageLength);
+      }
+      var messagesPtr = _malloc(totalMessagesSize);
 
-        {{{ makeDynCall('vipp', 'callback') }}}({{{ gpu.CompilationInfoRequestStatus.Success }}}, compilationInfoPtr, userdata);
+      // Allocate and fill out each CompilationMessage.
+      var compilationMessagesPtr = _malloc({{{ C_STRUCTS.WGPUCompilationMessage.__size__ }}} * compilationInfo.messages.length);
+      for (var i = 0; i < compilationInfo.messages.length; ++i) {
+        var compilationMessage = compilationInfo.messages[i];
+        var compilationMessagePtr = compilationMessagesPtr + {{{ C_STRUCTS.WGPUCompilationMessage.__size__ }}} * i;
 
-        messageStringPtrs.forEach((ptr) => {
-          _free(ptr);
-        });
-        _free(compilationMessagesPtr);
-        _free(compilationInfoPtr);
-      });
-    });
+        // Write out the values to the CompilationMessage.
+        WebGPU.setStringView(compilationMessagePtr + {{{ C_STRUCTS.WGPUCompilationMessage.message }}}, messagesPtr, messageLengths[i]);
+        {{{ makeSetValue('compilationMessagePtr', C_STRUCTS.WGPUCompilationMessage.type, 'WebGPU.Int_CompilationMessageType[compilationMessage.type]', 'i32') }}};
+        {{{ makeSetValue('compilationMessagePtr', C_STRUCTS.WGPUCompilationMessage.lineNum, 'compilationMessage.lineNum', 'i64') }}};
+        {{{ makeSetValue('compilationMessagePtr', C_STRUCTS.WGPUCompilationMessage.linePos, 'compilationMessage.linePos', 'i64') }}};
+        {{{ makeSetValue('compilationMessagePtr', C_STRUCTS.WGPUCompilationMessage.offset, 'compilationMessage.offset', 'i64') }}};
+        {{{ makeSetValue('compilationMessagePtr', C_STRUCTS.WGPUCompilationMessage.length, 'compilationMessage.length', 'i64') }}};
+        // TODO: Convert JavaScript's UTF-16-code-unit offsets to UTF-8-code-unit offsets.
+        // https://github.com/webgpu-native/webgpu-headers/issues/246
+        {{{ makeSetValue('compilationMessagePtr', C_STRUCTS.WGPUCompilationMessage.utf16LinePos, 'compilationMessage.linePos', 'i64') }}};
+        {{{ makeSetValue('compilationMessagePtr', C_STRUCTS.WGPUCompilationMessage.utf16Offset, 'compilationMessage.offset', 'i64') }}};
+        {{{ makeSetValue('compilationMessagePtr', C_STRUCTS.WGPUCompilationMessage.utf16Length, 'compilationMessage.length', 'i64') }}};
+
+        // Write the string out to the allocated buffer. Note we have to add 1
+        // to the length of the string to ensure enough space for the null
+        // terminator. However, we only increment the pointer by the exact
+        // length so that we overwrite the last null terminator excpet for the last one.
+        stringToUTF8(compilationMessage.message, messagesPtr, messageLengths[i] + 1);
+        messagesPtr += messageLengths[i];
+      }
+
+      // Allocate and fill out the wrapping CompilationInfo struct.
+      {{{ makeSetValue('compilationInfoPtr', C_STRUCTS.WGPUCompilationInfo.messageCount, 'compilationInfo.messages.length', '*') }}}
+      {{{ makeSetValue('compilationInfoPtr', C_STRUCTS.WGPUCompilationInfo.messages, 'compilationMessagesPtr', '*') }}};
+
+      _emwgpuOnCompilationInfoCompleted(futureIdL, futureIdH, {{{ gpu.CompilationInfoRequestStatus.Success }}}, compilationInfoPtr);
+    }, () => {
+      _emwgpuOnCompilationInfoCompleted(futureIdL, futureIdH, {{{ gpu.CompilationInfoRequestStatus.Error }}}, compilationInfoPtr);
+    }));
   },
 
   wgpuShaderModuleSetLabel: (shaderModulePtr, labelPtr) => {
diff --git a/third_party/emdawnwebgpu/webgpu.cpp b/third_party/emdawnwebgpu/webgpu.cpp
index c811b4a..092c5d5 100644
--- a/third_party/emdawnwebgpu/webgpu.cpp
+++ b/third_party/emdawnwebgpu/webgpu.cpp
@@ -46,6 +46,10 @@
 void emwgpuDeviceCreateBuffer(WGPUDevice device,
                               const WGPUBufferDescriptor* descriptor,
                               WGPUBuffer buffer);
+void emwgpuDeviceCreateShaderModule(
+    WGPUDevice device,
+    const WGPUShaderModuleDescriptor* descriptor,
+    WGPUShaderModule shader);
 
 // Buffer mapping operations that has work that needs to be done on the JS side.
 void emwgpuBufferDestroy(WGPUBuffer buffer);
@@ -80,6 +84,9 @@
                                   FutureID futureId,
                                   const WGPURequestAdapterOptions* options);
 void emwgpuQueueOnSubmittedWorkDone(WGPUQueue queue, FutureID futureId);
+void emwgpuShaderModuleGetCompilationInfo(WGPUShaderModule shader,
+                                          FutureID futureId,
+                                          WGPUCompilationInfo* compilationInfo);
 }  // extern "C"
 
 // ----------------------------------------------------------------------------
@@ -293,7 +300,6 @@
   X(RenderPassEncoder)   \
   X(RenderPipeline)      \
   X(Sampler)             \
-  X(ShaderModule)        \
   X(Surface)             \
   X(Texture)             \
   X(TextureView)
@@ -308,6 +314,7 @@
   Shutdown,
 };
 enum class EventType {
+  CompilationInfo,
   CreateComputePipeline,
   CreateRenderPipeline,
   DeviceLost,
@@ -689,10 +696,90 @@
   static InstanceID GetNextInstanceId();
 };
 
+struct WGPUShaderModuleImpl final : public EventSource, public RefCounted {
+ public:
+  WGPUShaderModuleImpl(const EventSource* source);
+
+  WGPUFuture GetCompilationInfo(WGPUCompilationInfoCallbackInfo2 callbackInfo);
+
+ private:
+  friend class CompilationInfoEvent;
+
+  struct WGPUCompilationInfoDeleter {
+    void operator()(WGPUCompilationInfo* compilationInfo) {
+      if (!compilationInfo) {
+        return;
+      }
+
+      if (compilationInfo->messageCount) {
+        // Since we allocate all the messages in a single block, we only need to
+        // free the first pointer.
+        free(const_cast<char*>(compilationInfo->messages[0].message.data));
+      }
+      if (compilationInfo->messages) {
+        free(const_cast<WGPUCompilationMessage*>(compilationInfo->messages));
+      }
+      delete compilationInfo;
+    }
+  };
+  using CompilationInfo =
+      std::unique_ptr<WGPUCompilationInfo, WGPUCompilationInfoDeleter>;
+  CompilationInfo mCompilationInfo = nullptr;
+};
+
 // ----------------------------------------------------------------------------
 // Future events.
 // ----------------------------------------------------------------------------
 
+class CompilationInfoEvent final : public TrackedEvent {
+ public:
+  static constexpr EventType kType = EventType::CompilationInfo;
+
+  CompilationInfoEvent(InstanceID instance,
+                       WGPUShaderModule shader,
+                       const WGPUCompilationInfoCallbackInfo2& callbackInfo)
+      : TrackedEvent(instance, callbackInfo.mode), mShader(shader) {}
+
+  EventType GetType() override { return kType; }
+
+  void ReadyHook(WGPUCompilationInfoRequestStatus status,
+                 WGPUCompilationInfo* compilationInfo) {
+    WGPUShaderModuleImpl::CompilationInfo info(compilationInfo);
+    mStatus = status;
+    if (mStatus != WGPUCompilationInfoRequestStatus_Success) {
+      return;
+    }
+
+    if (!mShader->mCompilationInfo.get()) {
+      // If there wasn't already a cached version of the info, set it now.
+      mShader->mCompilationInfo = std::move(info);
+    }
+    assert(mShader->mCompilationInfo.get());
+  }
+
+  void Complete(FutureID, EventCompletionType type) override {
+    if (type == EventCompletionType::Shutdown) {
+      mStatus = WGPUCompilationInfoRequestStatus_InstanceDropped;
+    }
+    if (mCallback) {
+      mCallback(mStatus,
+                mStatus == WGPUCompilationInfoRequestStatus_Success
+                    ? mShader->mCompilationInfo.get()
+                    : nullptr,
+                mUserdata1, mUserdata2);
+    }
+  }
+
+ private:
+  WGPUCompilationInfoCallback2 mCallback = nullptr;
+  void* mUserdata1 = nullptr;
+  void* mUserdata2 = nullptr;
+
+  Ref<WGPUShaderModule> mShader;
+  WGPUCompilationInfoRequestStatus mStatus =
+      WGPUCompilationInfoRequestStatus_Success;
+};
+
 template <typename Pipeline, EventType Type, typename CallbackInfo>
 class CreatePipelineEventBase final : public TrackedEvent {
  public:
@@ -1029,6 +1116,120 @@
 };
 
 // ----------------------------------------------------------------------------
+// Definitions for C++ emwgpu functions (callable from library_webgpu.js)
+// ----------------------------------------------------------------------------
+extern "C" {
+
+// Object creation helpers that all return a pointer which is used as a key
+// in the JS object table in library_webgpu.js.
+#define DEFINE_EMWGPU_DEFAULT_CREATE(Name)                             \
+  WGPU##Name emwgpuCreate##Name(const EventSource* source = nullptr) { \
+    return new WGPU##Name##Impl(source);                               \
+  }
+WGPU_PASSTHROUGH_OBJECTS(DEFINE_EMWGPU_DEFAULT_CREATE)
+
+WGPUAdapter emwgpuCreateAdapter(const EventSource* source) {
+  return new WGPUAdapterImpl(source);
+}
+
+WGPUBuffer emwgpuCreateBuffer(const EventSource* source,
+                              bool mappedAtCreation = false) {
+  return new WGPUBufferImpl(source, mappedAtCreation);
+}
+
+WGPUDevice emwgpuCreateDevice(const EventSource* source, WGPUQueue queue) {
+  return new WGPUDeviceImpl(source, queue);
+}
+
+WGPUQueue emwgpuCreateQueue(const EventSource* source) {
+  return new WGPUQueueImpl(source);
+}
+
+WGPUShaderModule emwgpuCreateShaderModule(const EventSource* source) {
+  return new WGPUShaderModuleImpl(source);
+}
+
+// Future event callbacks.
+void emwgpuOnCompilationInfoCompleted(FutureID futureId,
+                                      WGPUCompilationInfoRequestStatus status,
+                                      WGPUCompilationInfo* compilationInfo) {
+  GetEventManager().SetFutureReady<CompilationInfoEvent>(futureId, status,
+                                                         compilationInfo);
+}
+void emwgpuOnCreateComputePipelineCompleted(
+    FutureID futureId,
+    WGPUCreatePipelineAsyncStatus status,
+    WGPUComputePipeline pipeline,
+    const char* message) {
+  GetEventManager().SetFutureReady<CreateComputePipelineEvent>(
+      futureId, status, pipeline, message);
+}
+void emwgpuOnCreateRenderPipelineCompleted(FutureID futureId,
+                                           WGPUCreatePipelineAsyncStatus status,
+                                           WGPURenderPipeline pipeline,
+                                           const char* message) {
+  GetEventManager().SetFutureReady<CreateRenderPipelineEvent>(
+      futureId, status, pipeline, message);
+}
+void emwgpuOnDeviceLostCompleted(FutureID futureId,
+                                 WGPUDeviceLostReason reason,
+                                 const char* message) {
+  GetEventManager().SetFutureReady<DeviceLostEvent>(futureId, reason, message);
+}
+void emwgpuOnMapAsyncCompleted(FutureID futureId,
+                               WGPUMapAsyncStatus status,
+                               const char* message) {
+  GetEventManager().SetFutureReady<MapAsyncEvent>(futureId, status, message);
+}
+void emwgpuOnPopErrorScopeCompleted(FutureID futureId,
+                                    WGPUPopErrorScopeStatus status,
+                                    WGPUErrorType errorType,
+                                    const char* message) {
+  GetEventManager().SetFutureReady<PopErrorScopeEvent>(futureId, status,
+                                                       errorType, message);
+}
+void emwgpuOnRequestAdapterCompleted(FutureID futureId,
+                                     WGPURequestAdapterStatus status,
+                                     WGPUAdapter adapter,
+                                     const char* message) {
+  GetEventManager().SetFutureReady<RequestAdapterEvent>(futureId, status,
+                                                        adapter, message);
+}
+void emwgpuOnRequestDeviceCompleted(FutureID futureId,
+                                    WGPURequestDeviceStatus status,
+                                    WGPUDevice device,
+                                    const char* message) {
+  // This handler should always have a device since we pre-allocate it before
+  // calling out to JS.
+  assert(device);
+  if (status == WGPURequestDeviceStatus_Success) {
+    GetEventManager().SetFutureReady<RequestDeviceEvent>(futureId, status,
+                                                         device, message);
+  } else {
+    // If the request failed, we need to resolve the DeviceLostEvent.
+    device->OnDeviceLost(WGPUDeviceLostReason_FailedCreation,
+                         "Device failed at creation.");
+    GetEventManager().SetFutureReady<RequestDeviceEvent>(futureId, status,
+                                                         nullptr, message);
+  }
+}
+void emwgpuOnWorkDoneCompleted(FutureID futureId,
+                               WGPUQueueWorkDoneStatus status) {
+  GetEventManager().SetFutureReady<WorkDoneEvent>(futureId, status);
+}
+
+// Uncaptured error handler is similar to the Future event callbacks, but it
+// doesn't go through the EventManager and just calls the callback on the Device
+// immediately.
+void emwgpuOnUncapturedError(WGPUDevice device,
+                             WGPUErrorType type,
+                             char const* message) {
+  device->OnUncapturedError(type, message);
+}
+
+}  // extern "C"
+
+// ----------------------------------------------------------------------------
 // WGPU struct implementations.
 // ----------------------------------------------------------------------------
 
@@ -1219,108 +1420,34 @@
 WGPUQueueImpl::WGPUQueueImpl(const EventSource* source) : EventSource(source) {}
 
 // ----------------------------------------------------------------------------
-// Definitions for C++ emwgpu functions (callable from library_webgpu.js)
+// WGPUShaderModuleImpl implementations.
 // ----------------------------------------------------------------------------
-extern "C" {
 
-// Object creation helpers that all return a pointer which is used as a key
-// in the JS object table in library_webgpu.js.
-#define DEFINE_EMWGPU_DEFAULT_CREATE(Name)                             \
-  WGPU##Name emwgpuCreate##Name(const EventSource* source = nullptr) { \
-    return new WGPU##Name##Impl(source);                               \
+WGPUShaderModuleImpl::WGPUShaderModuleImpl(const EventSource* source)
+    : EventSource(source) {}
+
+WGPUFuture WGPUShaderModuleImpl::GetCompilationInfo(
+    WGPUCompilationInfoCallbackInfo2 callbackInfo) {
+  auto [futureId, tracked] =
+      GetEventManager().TrackEvent(std::make_unique<CompilationInfoEvent>(
+          GetInstanceId(), this, callbackInfo));
+  if (!tracked) {
+    return WGPUFuture{kNullFutureId};
   }
-WGPU_PASSTHROUGH_OBJECTS(DEFINE_EMWGPU_DEFAULT_CREATE)
 
-WGPUAdapter emwgpuCreateAdapter(const EventSource* source) {
-  return new WGPUAdapterImpl(source);
-}
-
-WGPUBuffer emwgpuCreateBuffer(const EventSource* source,
-                              bool mappedAtCreation = false) {
-  return new WGPUBufferImpl(source, mappedAtCreation);
-}
-
-WGPUDevice emwgpuCreateDevice(const EventSource* source, WGPUQueue queue) {
-  return new WGPUDeviceImpl(source, queue);
-}
-
-WGPUQueue emwgpuCreateQueue(const EventSource* source) {
-  return new WGPUQueueImpl(source);
-}
-
-// Future event callbacks.
-void emwgpuOnCreateComputePipelineCompleted(
-    FutureID futureId,
-    WGPUCreatePipelineAsyncStatus status,
-    WGPUComputePipeline pipeline,
-    const char* message) {
-  GetEventManager().SetFutureReady<CreateComputePipelineEvent>(
-      futureId, status, pipeline, message);
-}
-void emwgpuOnCreateRenderPipelineCompleted(FutureID futureId,
-                                           WGPUCreatePipelineAsyncStatus status,
-                                           WGPURenderPipeline pipeline,
-                                           const char* message) {
-  GetEventManager().SetFutureReady<CreateRenderPipelineEvent>(
-      futureId, status, pipeline, message);
-}
-void emwgpuOnDeviceLostCompleted(FutureID futureId,
-                                 WGPUDeviceLostReason reason,
-                                 const char* message) {
-  GetEventManager().SetFutureReady<DeviceLostEvent>(futureId, reason, message);
-}
-void emwgpuOnMapAsyncCompleted(FutureID futureId,
-                               WGPUMapAsyncStatus status,
-                               const char* message) {
-  GetEventManager().SetFutureReady<MapAsyncEvent>(futureId, status, message);
-}
-void emwgpuOnPopErrorScopeCompleted(FutureID futureId,
-                                    WGPUPopErrorScopeStatus status,
-                                    WGPUErrorType errorType,
-                                    const char* message) {
-  GetEventManager().SetFutureReady<PopErrorScopeEvent>(futureId, status,
-                                                       errorType, message);
-}
-void emwgpuOnRequestAdapterCompleted(FutureID futureId,
-                                     WGPURequestAdapterStatus status,
-                                     WGPUAdapter adapter,
-                                     const char* message) {
-  GetEventManager().SetFutureReady<RequestAdapterEvent>(futureId, status,
-                                                        adapter, message);
-}
-void emwgpuOnRequestDeviceCompleted(FutureID futureId,
-                                    WGPURequestDeviceStatus status,
-                                    WGPUDevice device,
-                                    const char* message) {
-  // This handler should always have a device since we pre-allocate it before
-  // calling out to JS.
-  assert(device);
-  if (status == WGPURequestDeviceStatus_Success) {
-    GetEventManager().SetFutureReady<RequestDeviceEvent>(futureId, status,
-                                                         device, message);
+  // If we already have the compilation info cached, we don't need to call into
+  // JS.
+  if (mCompilationInfo) {
+    emwgpuOnCompilationInfoCompleted(futureId,
+                                     WGPUCompilationInfoRequestStatus_Success,
+                                     mCompilationInfo.get());
   } else {
-    // If the request failed, we need to resolve the DeviceLostEvent.
-    device->OnDeviceLost(WGPUDeviceLostReason_FailedCreation,
-                         "Device failed at creation.");
-    GetEventManager().SetFutureReady<RequestDeviceEvent>(futureId, status,
-                                                         nullptr, message);
+    WGPUCompilationInfo* compilationInfo = new WGPUCompilationInfo{
+        .nextInChain = nullptr, .messageCount = 0, .messages = nullptr};
+    emwgpuShaderModuleGetCompilationInfo(this, futureId, compilationInfo);
   }
+  return WGPUFuture{futureId};
 }
-void emwgpuOnWorkDoneCompleted(FutureID futureId,
-                               WGPUQueueWorkDoneStatus status) {
-  GetEventManager().SetFutureReady<WorkDoneEvent>(futureId, status);
-}
-
-// Uncaptured error handler is similar to the Future event callbacks, but it
-// doesn't go through the EventManager and just calls the callback on the Device
-// immediately.
-void emwgpuOnUncapturedError(WGPUDevice device,
-                             WGPUErrorType type,
-                             char const* message) {
-  device->OnUncapturedError(type, message);
-}
-
-}  // extern "C"
 
 // ----------------------------------------------------------------------------
 // WebGPU function definitions, with methods organized by "class". Note these
@@ -1553,6 +1680,14 @@
   return WGPUFuture{futureId};
 }
 
+WGPUShaderModule wgpuDeviceCreateShaderModule(
+    WGPUDevice device,
+    const WGPUShaderModuleDescriptor* descriptor) {
+  WGPUShaderModule shader = new WGPUShaderModuleImpl(device);
+  emwgpuDeviceCreateShaderModule(device, descriptor, shader);
+  return shader;
+}
+
 WGPUQueue wgpuDeviceGetQueue(WGPUDevice device) {
   return device->GetQueue();
 }
@@ -1667,6 +1802,12 @@
 // Methods of ShaderModule
 // ----------------------------------------------------------------------------
 
+WGPUFuture wgpuShaderModuleGetCompilationInfo2(
+    WGPUShaderModule shader,
+    WGPUCompilationInfoCallbackInfo2 callbackInfo) {
+  return shader->GetCompilationInfo(callbackInfo);
+}
+
 // ----------------------------------------------------------------------------
 // Methods of Surface
 // ----------------------------------------------------------------------------