[wasm] Extend support of "except_ref" global variables.

This extends the support for the "except_ref" type on global variables
to support mutable globals, as well as importing and exporting such
globals. Test coverage is also increased.

R=ahaas@chromium.org
TEST=mjsunit/wasm/exceptions-global
BUG=v8:8091

Change-Id: I816406e322ffb574a4f054947682491e7b40335f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1530802
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60327}
diff --git a/src/compiler/wasm-compiler.cc b/src/compiler/wasm-compiler.cc
index 5277d54..ab30659 100644
--- a/src/compiler/wasm-compiler.cc
+++ b/src/compiler/wasm-compiler.cc
@@ -5042,8 +5042,8 @@
   Node* FromJS(Node* node, Node* js_context, wasm::ValueType type) {
     DCHECK_NE(wasm::kWasmStmt, type);
 
-    // The parameter is of type AnyRef, we take it as is.
-    if (type == wasm::kWasmAnyRef) {
+    // The parameter is of type anyref or except_ref, we take it as is.
+    if (type == wasm::kWasmAnyRef || type == wasm::kWasmExceptRef) {
       return node;
     }
 
diff --git a/src/wasm/wasm-js.cc b/src/wasm/wasm-js.cc
index 2dbad07..75fa4b3 100644
--- a/src/wasm/wasm-js.cc
+++ b/src/wasm/wasm-js.cc
@@ -1313,7 +1313,7 @@
   v8::Isolate* isolate = args.GetIsolate();
   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
   HandleScope scope(isolate);
-  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Excepion()");
+  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Exception()");
   thrower.TypeError("WebAssembly.Exception cannot be called");
 }
 
@@ -1657,6 +1657,7 @@
       break;
     case i::wasm::kWasmAnyRef:
     case i::wasm::kWasmAnyFunc:
+    case i::wasm::kWasmExceptRef:
       return_value.Set(Utils::ToLocal(receiver->GetRef()));
       break;
     default:
@@ -1724,7 +1725,8 @@
       receiver->SetF64(f64_value);
       break;
     }
-    case i::wasm::kWasmAnyRef: {
+    case i::wasm::kWasmAnyRef:
+    case i::wasm::kWasmExceptRef: {
       receiver->SetAnyRef(Utils::OpenHandle(*args[0]));
       break;
     }
diff --git a/src/wasm/wasm-objects-inl.h b/src/wasm/wasm-objects-inl.h
index c7694bd..2ee72aa 100644
--- a/src/wasm/wasm-objects-inl.h
+++ b/src/wasm/wasm-objects-inl.h
@@ -164,7 +164,7 @@
 }
 
 Handle<Object> WasmGlobalObject::GetRef() {
-  // We use this getter also for anyfunc.
+  // We use this getter for anyref, anyfunc, and except_ref.
   DCHECK(wasm::ValueTypes::IsReferenceType(type()));
   return handle(tagged_buffer()->get(offset()), GetIsolate());
 }
@@ -186,7 +186,8 @@
 }
 
 void WasmGlobalObject::SetAnyRef(Handle<Object> value) {
-  DCHECK_EQ(type(), wasm::kWasmAnyRef);
+  // We use this getter anyref and except_ref.
+  DCHECK(type() == wasm::kWasmAnyRef || type() == wasm::kWasmExceptRef);
   tagged_buffer()->set(offset(), *value);
 }
 
diff --git a/test/mjsunit/wasm/exceptions-global.js b/test/mjsunit/wasm/exceptions-global.js
index 183fd3b..12fcda0 100644
--- a/test/mjsunit/wasm/exceptions-global.js
+++ b/test/mjsunit/wasm/exceptions-global.js
@@ -37,8 +37,102 @@
   assertEquals(null, instance.exports.push_and_return_except_ref());
 })();
 
-// TODO(mstarzinger): Add test coverage for the following:
-//   - Catching exception in wasm and storing into global.
-//   - Taking "except_ref" parameter and storing into global.
-//   - Rethrowing "except_ref" from global (or parameter).
-//   - Importing a global "except_ref" during instantiation.
+// Test storing a caught exception into an exported mutable "except_ref" global.
+(function TestGlobalExceptRefSetCaught() {
+  print(arguments.callee.name);
+  let builder = new WasmModuleBuilder();
+  let except = builder.addException(kSig_v_i);
+  let g = builder.addGlobal(kWasmExceptRef, true).exportAs("exn");
+  builder.addFunction('catch_and_set_except_ref', kSig_v_i)
+      .addBody([
+        kExprTry, kWasmStmt,
+          kExprGetLocal, 0,
+          kExprThrow, except,
+        kExprCatch,
+          kExprSetGlobal, g.index,
+        kExprEnd,
+      ]).exportFunc();
+  let instance = builder.instantiate();
+
+  assertDoesNotThrow(() => instance.exports.catch_and_set_except_ref(23));
+  let exception = instance.exports.exn.value;  // Exported mutable global.
+  assertInstanceof(exception, WebAssembly.RuntimeError);
+  assertEquals(except, %GetWasmExceptionId(exception, instance));
+})();
+
+// Test storing a parameter into an exported mutable "except_ref" global.
+(function TestGlobalExceptRefSetParameter() {
+  print(arguments.callee.name);
+  let builder = new WasmModuleBuilder();
+  let g = builder.addGlobal(kWasmExceptRef, true).exportAs("exn");
+  builder.addFunction('set_param_except_ref', kSig_v_e)
+      .addBody([
+        kExprTry, kWasmStmt,
+          kExprGetLocal, 0,
+          kExprRethrow,
+        kExprCatch,
+          kExprSetGlobal, g.index,
+        kExprEnd,
+      ]).exportFunc();
+  let exception = "my fancy exception";
+  let instance = builder.instantiate();
+
+  assertDoesNotThrow(() => instance.exports.set_param_except_ref(exception));
+  assertEquals(exception, instance.exports.exn.value);
+})();
+
+// Test loading an imported "except_ref" global and re-throwing the exception.
+(function TestGlobalExceptRefGetImportedAndRethrow() {
+  print(arguments.callee.name);
+  let builder = new WasmModuleBuilder();
+  let g_index = builder.addImportedGlobal("m", "exn", kWasmExceptRef);
+  builder.addFunction('rethrow_except_ref', kSig_v_v)
+      .addBody([
+        kExprGetGlobal, g_index,
+        kExprRethrow,
+      ]).exportFunc();
+  let exception = "my fancy exception";
+  let instance = builder.instantiate({ "m": { "exn": exception }});
+
+  assertThrowsEquals(() => instance.exports.rethrow_except_ref(), exception);
+})();
+
+// Test loading an exported mutable "except_ref" being changed from the outside.
+(function TestGlobalExceptRefGetExportedMutableAndRethrow() {
+  print(arguments.callee.name);
+  let builder = new WasmModuleBuilder();
+  let g = builder.addGlobal(kWasmExceptRef, true).exportAs("exn");
+  builder.addFunction('rethrow_except_ref', kSig_v_v)
+      .addBody([
+        kExprGetGlobal, g.index,
+        kExprRethrow,
+      ]).exportFunc();
+  let instance = builder.instantiate();
+
+  let exception1 = instance.exports.exn.value = "my fancy exception";
+  assertThrowsEquals(() => instance.exports.rethrow_except_ref(), exception1);
+  let exception2 = instance.exports.exn.value = "an even fancier exception";
+  assertThrowsEquals(() => instance.exports.rethrow_except_ref(), exception2);
+})();
+
+// TODO(mstarzinger): Add the following test once proposal makes it clear how
+// far interaction with the mutable globals proposal is intended to go.
+// Test loading an imported mutable "except_ref" being changed from the outside.
+/*(function TestGlobalExceptRefGetImportedMutableAndRethrow() {
+  print(arguments.callee.name);
+  let builder = new WasmModuleBuilder();
+  let g_index = builder.addImportedGlobal("m", "exn", kWasmExceptRef, true);
+  builder.addFunction('rethrow_except_ref', kSig_v_v)
+      .addBody([
+        kExprGetGlobal, g_index,
+        kExprRethrow,
+      ]).exportFunc();
+  let exception1 = "my fancy exception";
+  let desc = { value: 'except_ref', mutable: true };
+  let mutable_global = new WebAssembly.Global(desc, exception1);
+  let instance = builder.instantiate({ "m": { "exn": mutable_global }});
+
+  assertThrowsEquals(() => instance.exports.rethrow_except_ref(), exception1);
+  let exception2 = mutable_global.value = "an even fancier exception";
+  assertThrowsEquals(() => instance.exports.rethrow_except_ref(), exception2);
+})();*/
diff --git a/test/mjsunit/wasm/wasm-module-builder.js b/test/mjsunit/wasm/wasm-module-builder.js
index b4ea7c1..302d530 100644
--- a/test/mjsunit/wasm/wasm-module-builder.js
+++ b/test/mjsunit/wasm/wasm-module-builder.js
@@ -153,6 +153,7 @@
 let kSig_i_r = makeSig([kWasmAnyRef], [kWasmI32]);
 let kSig_v_r = makeSig([kWasmAnyRef], []);
 let kSig_v_a = makeSig([kWasmAnyFunc], []);
+let kSig_v_e = makeSig([kWasmExceptRef], []);
 let kSig_v_rr = makeSig([kWasmAnyRef, kWasmAnyRef], []);
 let kSig_v_aa = makeSig([kWasmAnyFunc, kWasmAnyFunc], []);
 let kSig_r_v = makeSig([], [kWasmAnyRef]);