[emdawnwebgpu] Fix string-to-int tables with Closure, enable Closure in tests

Fix string-to-int tables by (1) making them library-level items that are
explicit dependencies (separate from $WebGPU) and (2) wrapping them to
prevent Emscripten from removing the quotation marks on the keys, that
signal to Closure that they shouldn't be minified.

This has the added benefit of Emscripten being able to eliminate tables
that are unused by the application.

Enable --closure=1 in Release builds, and add a test which explicitly
tests this. (The GetCompilationInfo tests also catch this.)

Fixed: 424836759
Bug: 377760848
Change-Id: Iaee6d18516a5d54a49869ba76ec975c12cc0cc60
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/252255
Reviewed-by: Loko Kung <lokokung@google.com>
Commit-Queue: Kai Ninomiya <kainino@chromium.org>
diff --git a/generator/templates/emdawnwebgpu/library_webgpu_enum_tables.js b/generator/templates/emdawnwebgpu/library_webgpu_enum_tables.js
index e7b678d..8249e11 100644
--- a/generator/templates/emdawnwebgpu/library_webgpu_enum_tables.js
+++ b/generator/templates/emdawnwebgpu/library_webgpu_enum_tables.js
@@ -26,9 +26,8 @@
 //* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //*
 //*
-//* This generator is used to produce the number-to-string mappings for
-//* Emscripten's library_webgpu.js.
-//* https://github.com/emscripten-core/emscripten/blob/main/src/library_webgpu.js
+//* This generator is used to produce number<->string mappings for webgpu.h
+//* enums. The strings defined here are used by library_webgpu.js.
 //*
 {{'{{{'}}
     globalThis.__HAVE_EMDAWNWEBGPU_ENUM_TABLES = true;
@@ -48,26 +47,33 @@
 
     // Maps from enum string back to enum number, for callbacks.
     // These appear in the final build result so should be kept minimal.
+    // (These are library-level items, which means they can be eliminated if
+    // they're unreachable. The $ prefix means they're used only by other JS
+    // code, not imported to Wasm. They're wrapped as strings so that Emscripten
+    // inserts them verbatim rather than parsing then reserializing them, to
+    // preserve the quotation marks needed to prevent Closure minification.)
     globalThis.WEBGPU_STRING_TO_INT_TABLES = `
         {% for type in by_category["enum"] if type.json_data.get("emscripten_string_to_int", False) %}
-            Int_{{type.name.CamelCase()}}: {
+            $emwgpuStringToInt_{{type.name.CamelCase()}}: \`{
                 {% for value in type.values if value.json_data.get("emscripten_string_to_int", True) %}
                     {% if type.name.name == 'device lost reason' and value.name.name == 'unknown' %}
                         'undefined': {{value.value}},  // For older browsers
                     {% endif %}
                     {{as_jsEnumValue(value)}}: {{value.value}},
                 {% endfor %}
-            },
+            }\`,
         {% endfor %}
-        Int_PreferredFormat: {
+        $emwgpuStringToInt_PreferredFormat: \`{
             {% for value in types['texture format'].values if value.name.name in ['RGBA8 unorm', 'BGRA8 unorm'] %}
                 {{as_jsEnumValue(value)}}: {{value.value}},
             {% endfor %}
-        },
+        }\`,
 `;
 
     // Maps from enum number to enum string.
     // These appear in the final build result so should be kept minimal.
+    // TODO(crbug.com/377760848): Make these library-level items like the
+    // string-to-int tables, so they can be dead-code-eliminated.
     globalThis.WEBGPU_INT_TO_STRING_TABLES = `
         {% for type in by_category["enum"] if not type.json_data.get("emscripten_no_enum_table") %}
             //* Use an array if the enum is contiguous, allowing up to an arbitrary 10 blank entries
diff --git a/src/emdawnwebgpu/CMakeLists.txt b/src/emdawnwebgpu/CMakeLists.txt
index d089020..7eb79ac 100644
--- a/src/emdawnwebgpu/CMakeLists.txt
+++ b/src/emdawnwebgpu/CMakeLists.txt
@@ -324,9 +324,12 @@
             PUBLIC
                 ${emdawnwebgpu_test_deps}
         )
-        target_link_options(emdawnwebgpu_tests_asyncify PUBLIC
-            # We need Asyncify or JSPI for Future tests.
-            "-sASYNCIFY=1"
+        target_link_options(emdawnwebgpu_tests_asyncify
+            PUBLIC
+                # We need Asyncify or JSPI for Future tests.
+                "-sASYNCIFY=1"
+                # Test that Closure minification and externs work.
+                "$<$<CONFIG:Release>:--closure=1>"
         )
 
         add_executable(emdawnwebgpu_tests_jspi ${emdawnwebgpu_test_sources})
@@ -337,9 +340,12 @@
             PUBLIC
                 ${emdawnwebgpu_test_deps}
         )
-        target_link_options(emdawnwebgpu_tests_jspi PUBLIC
-            # We need Asyncify or JSPI for Future tests.
-            "-sJSPI=1"
+        target_link_options(emdawnwebgpu_tests_jspi
+            PUBLIC
+                # We need Asyncify or JSPI for Future tests.
+                "-sJSPI=1"
+                # Test that Closure minification and externs work.
+                "$<$<CONFIG:Release>:--closure=1>"
         )
 
         DawnJSONGenerator(
diff --git a/src/emdawnwebgpu/tests/SpotTests.cpp b/src/emdawnwebgpu/tests/SpotTests.cpp
index e5dba35..394de00 100644
--- a/src/emdawnwebgpu/tests/SpotTests.cpp
+++ b/src/emdawnwebgpu/tests/SpotTests.cpp
@@ -88,4 +88,12 @@
     EXPECT_EQ(querySet.GetType(), querySetDesc.type);
 }
 
+TEST_F(SpotTests, BufferGetMapState) {
+    // Spot test one of the string-to-int tables (Int_BufferMapState) to make sure
+    // that Closure's minification didn't minify its keys.
+    wgpu::BufferDescriptor bufferDesc{.usage = wgpu::BufferUsage::CopyDst, .size = 4};
+    wgpu::Buffer buffer = device.CreateBuffer(&bufferDesc);
+    EXPECT_EQ(buffer.GetMapState(), wgpu::BufferMapState::Unmapped);
+}
+
 }  // namespace
diff --git a/third_party/emdawnwebgpu/pkg/webgpu/src/library_webgpu.js b/third_party/emdawnwebgpu/pkg/webgpu/src/library_webgpu.js
index d37e68b..45fef50 100644
--- a/third_party/emdawnwebgpu/pkg/webgpu/src/library_webgpu.js
+++ b/third_party/emdawnwebgpu/pkg/webgpu/src/library_webgpu.js
@@ -621,13 +621,13 @@
       {{{ makeSetValue('infoStruct', C_STRUCTS.WGPUAdapterInfo.deviceID, '0', 'i32') }}};
     },
 
-    // Maps from enum string back to enum number, for callbacks.
-    {{{ WEBGPU_STRING_TO_INT_TABLES }}}
-
     // Maps from enum number to enum string.
     {{{ WEBGPU_INT_TO_STRING_TABLES }}}
   },
 
+  // Maps from enum string back to enum number, for callbacks.
+  {{{ WEBGPU_STRING_TO_INT_TABLES }}}
+
   // TODO(crbug.com/374150686): Remove this once it has been fully deprecated in users.
   emscripten_webgpu_get_device__deps: ['wgpuDeviceAddRef'],
   emscripten_webgpu_get_device: () => {
@@ -695,10 +695,11 @@
   },
 #endif
 
+  emwgpuGetPreferredFormat__deps: ['$emwgpuStringToInt_PreferredFormat'],
   emwgpuGetPreferredFormat__sig: 'i',
   emwgpuGetPreferredFormat: () => {
     var format = navigator["gpu"]["getPreferredCanvasFormat"]();
-    return WebGPU.Int_PreferredFormat[format];
+    return emwgpuStringToInt_PreferredFormat[format];
   },
 
   // --------------------------------------------------------------------------
@@ -764,7 +765,7 @@
     return adapter.features.has(WebGPU.FeatureName[featureEnumValue]);
   },
 
-  emwgpuAdapterRequestDevice__deps: ['emwgpuOnDeviceLostCompleted', 'emwgpuOnRequestDeviceCompleted', 'emwgpuOnUncapturedError'],
+  emwgpuAdapterRequestDevice__deps: ['emwgpuOnDeviceLostCompleted', 'emwgpuOnRequestDeviceCompleted', 'emwgpuOnUncapturedError', '$emwgpuStringToInt_DeviceLostReason'],
   emwgpuAdapterRequestDevice__sig: 'vpjjppp',
   emwgpuAdapterRequestDevice: (adapterPtr, futureId, deviceLostFutureId, devicePtr, queuePtr, descriptor) => {
     var adapter = WebGPU.getJsObject(adapterPtr);
@@ -867,7 +868,7 @@
           device.onuncapturederror = (ev) => {};
           var sp = stackSave();
           var messagePtr = stringToUTF8OnStack(info.message);
-          _emwgpuOnDeviceLostCompleted(deviceLostFutureId, WebGPU.Int_DeviceLostReason[info.reason],
+          _emwgpuOnDeviceLostCompleted(deviceLostFutureId, emwgpuStringToInt_DeviceLostReason[info.reason],
             {{{ gpu.passAsPointer('messagePtr') }}});
           stackRestore(sp);
         }));
@@ -2371,7 +2372,7 @@
   // Methods of ShaderModule
   // --------------------------------------------------------------------------
 
-  emwgpuShaderModuleGetCompilationInfo__deps: ['emwgpuOnCompilationInfoCompleted', '$stringToUTF8', '$lengthBytesUTF8', 'malloc'],
+  emwgpuShaderModuleGetCompilationInfo__deps: ['emwgpuOnCompilationInfoCompleted', '$stringToUTF8', '$lengthBytesUTF8', 'malloc', '$emwgpuStringToInt_CompilationMessageType'],
   emwgpuShaderModuleGetCompilationInfo__sig: 'vpjp',
   emwgpuShaderModuleGetCompilationInfo: (shaderModulePtr, futureId, compilationInfoPtr) => {
     var shaderModule = WebGPU.getJsObject(shaderModulePtr);
@@ -2404,7 +2405,7 @@
         // 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.nextInChain, 'utf16Ptr', '*') }}};
-        {{{ makeSetValue('compilationMessagePtr', C_STRUCTS.WGPUCompilationMessage.type, 'WebGPU.Int_CompilationMessageType[compilationMessage.type]', 'i32') }}};
+        {{{ makeSetValue('compilationMessagePtr', C_STRUCTS.WGPUCompilationMessage.type, 'emwgpuStringToInt_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') }}};