Bug 1604118 - Rename isRef, and tidy up. r=rhunt
authorLars T Hansen <lhansen@mozilla.com>
Tue, 17 Dec 2019 13:39:41 +0000
changeset 507507 4e7dd9fe1cde8e852a5653698614119941a08e0f
parent 507506 58075bcbdcef7e4d3eb7a42a472e11c80a9b1907
child 507508 a5c69456f4438bfc83da634c3fbf3437bfa4a0ac
push id36929
push useropoprus@mozilla.com
push dateWed, 18 Dec 2019 16:02:51 +0000
treeherdermozilla-central@609b5711fb9d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrhunt
bugs1604118
milestone73.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1604118 - Rename isRef, and tidy up. r=rhunt Renames isRef() as isTypeIndex() and slightly change some error messages to distinguish indexed reference types from the more generic ones. Adds predicates on ValType to test various reference type subtypes, this reduces clutter. Slightly changes the code around existing uses of isRef() to improve error checking and provide resilience as we go forward toward other reference types, by calling out struct types specifically when they are the types we care about, and in one case changing from isRef() to !isAnyRef(), since that was the intent when we only had only the two cases AnyRef and Ref. Differential Revision: https://phabricator.services.mozilla.com/D57330
js/src/jit-test/tests/wasm/gc/ref-global.js
js/src/jit-test/tests/wasm/gc/ref-restrict.js
js/src/wasm/WasmAST.h
js/src/wasm/WasmBaselineCompile.cpp
js/src/wasm/WasmJS.cpp
js/src/wasm/WasmModule.cpp
js/src/wasm/WasmOpIter.h
js/src/wasm/WasmTextToBinary.cpp
js/src/wasm/WasmTypes.h
js/src/wasm/WasmValidate.cpp
js/src/wasm/WasmValidate.h
--- a/js/src/jit-test/tests/wasm/gc/ref-global.js
+++ b/js/src/jit-test/tests/wasm/gc/ref-global.js
@@ -105,25 +105,25 @@
 {
     let bin = wasmTextToBinary(
         `(module
           (gc_feature_opt_in 3)
           (type $box (struct (field $val i32)))
           (import "m" "g" (global (mut (ref $box)))))`);
 
     assertErrorMessage(() => new WebAssembly.Module(bin), WebAssembly.CompileError,
-                       /cannot expose reference type/);
+                       /cannot expose indexed reference type/);
 }
 
 // We can't export a global of a reference type because we can't later import
 // it.  (Once we can export it, the value setter must also perform the necessary
 // subtype check, which implies we have some notion of exporting types, and we
 // don't have that yet.)
 {
     let bin = wasmTextToBinary(
         `(module
           (gc_feature_opt_in 3)
           (type $box (struct (field $val i32)))
           (global $boxg (export "box") (mut (ref $box)) (ref.null)))`);
 
     assertErrorMessage(() => new WebAssembly.Module(bin), WebAssembly.CompileError,
-                       /cannot expose reference type/);
+                       /cannot expose indexed reference type/);
 }
--- a/js/src/jit-test/tests/wasm/gc/ref-restrict.js
+++ b/js/src/jit-test/tests/wasm/gc/ref-restrict.js
@@ -50,85 +50,85 @@ function wasmCompile(text) {
 // Exported function can't take ref type parameter, but anyref is OK.
 
 assertErrorMessage(() => wasmCompile(
     `(module
       (gc_feature_opt_in 3)
       (type $box (struct (field $x i32)))
       (func (export "f") (param (ref $box)) (unreachable)))`),
                    WebAssembly.CompileError,
-                   /cannot expose reference type/);
+                   /cannot expose indexed reference type/);
 
 assertEq(typeof wasmCompile(
     `(module
       (func (export "f") (param anyref) (unreachable)))`),
          "object");
 
 // Exported function can't return ref result, but anyref is OK.
 
 assertErrorMessage(() => wasmCompile(
     `(module
       (gc_feature_opt_in 3)
       (type $box (struct (field $x i32)))
       (func (export "f") (result (ref $box)) (ref.null)))`),
                    WebAssembly.CompileError,
-                   /cannot expose reference type/);
+                   /cannot expose indexed reference type/);
 
 assertEq(typeof wasmCompile(
     `(module
       (func (export "f") (result anyref) (ref.null)))`),
          "object");
 
 // Imported function can't take ref parameter, but anyref is OK.
 
 assertErrorMessage(() => wasmCompile(
     `(module
       (gc_feature_opt_in 3)
       (type $box (struct (field $x i32)))
       (import "m" "f" (param (ref $box))))`),
                    WebAssembly.CompileError,
-                   /cannot expose reference type/);
+                   /cannot expose indexed reference type/);
 
 assertEq(typeof wasmCompile(
     `(module
       (import "m" "f" (param anyref)))`),
          "object");
 
 // Imported function can't return ref type, but anyref is OK.
 
 assertErrorMessage(() => wasmCompile(
     `(module
       (gc_feature_opt_in 3)
       (type $box (struct (field $x i32)))
       (import "m" "f" (param i32) (result (ref $box))))`),
                    WebAssembly.CompileError,
-                   /cannot expose reference type/);
+                   /cannot expose indexed reference type/);
 
 assertEq(typeof wasmCompile(
     `(module
       (import "m" "f" (param i32) (result anyref)))`),
          "object");
 
 // Imported global can't be of Ref type (irrespective of mutability), though anyref is OK.
 
 assertErrorMessage(() => wasmCompile(
     `(module
       (gc_feature_opt_in 3)
       (type $box (struct (field $val i32)))
       (import "m" "g" (global (mut (ref $box)))))`),
                    WebAssembly.CompileError,
-                   /cannot expose reference type/);
+                   /cannot expose indexed reference type/);
 
 assertErrorMessage(() => wasmCompile(
     `(module
       (gc_feature_opt_in 3)
       (type $box (struct (field $val i32)))
       (import "m" "g" (global (ref $box))))`),
                    WebAssembly.CompileError,
-                   /cannot expose reference type/);
+                   /cannot expose indexed reference type/);
 
 assertEq(typeof wasmCompile(
     `(module
       (import "m" "g" (global (mut anyref))))`),
          "object");
 
 assertEq(typeof wasmCompile(
     `(module
@@ -138,25 +138,25 @@ assertEq(typeof wasmCompile(
 // Exported global can't be of Ref type (irrespective of mutability), though anyref is OK.
 
 assertErrorMessage(() => wasmCompile(
     `(module
       (gc_feature_opt_in 3)
       (type $box (struct (field $val i32)))
       (global $boxg (export "box") (mut (ref $box)) (ref.null)))`),
                    WebAssembly.CompileError,
-                   /cannot expose reference type/);
+                   /cannot expose indexed reference type/);
 
 assertErrorMessage(() => wasmCompile(
     `(module
       (gc_feature_opt_in 3)
       (type $box (struct (field $val i32)))
       (global $boxg (export "box") (ref $box) (ref.null)))`),
                    WebAssembly.CompileError,
-                   /cannot expose reference type/);
+                   /cannot expose indexed reference type/);
 
 assertEq(typeof wasmCompile(
     `(module
       (global $boxg (export "box") (mut anyref) (ref.null)))`),
          "object");
 
 assertEq(typeof wasmCompile(
     `(module
@@ -168,27 +168,27 @@ assertEq(typeof wasmCompile(
 assertErrorMessage(() => wasmCompile(
     `(module
       (gc_feature_opt_in 3)
       (type $box (struct (field $val i32)))
       (table (export "tbl") 1 funcref)
       (elem (i32.const 0) $f1)
       (func $f1 (param (ref $box)) (unreachable)))`),
                    WebAssembly.CompileError,
-                   /cannot expose reference type/);
+                   /cannot expose indexed reference type/);
 
 assertErrorMessage(() => wasmCompile(
     `(module
       (gc_feature_opt_in 3)
       (type $box (struct (field $val i32)))
       (table (export "tbl") 1 funcref)
       (elem (i32.const 0) $f1)
       (func $f1 (result (ref $box)) (ref.null)))`),
                    WebAssembly.CompileError,
-                   /cannot expose reference type/);
+                   /cannot expose indexed reference type/);
 
 assertEq(typeof wasmCompile(
     `(module
       (table (export "tbl") 1 funcref)
       (elem (i32.const 0) $f1)
       (func $f1 (param anyref) (unreachable)))`),
          "object");
 
@@ -204,27 +204,27 @@ assertEq(typeof wasmCompile(
 assertErrorMessage(() => wasmCompile(
     `(module
       (gc_feature_opt_in 3)
       (type $box (struct (field $val i32)))
       (import "m" "tbl" (table 1 funcref))
       (elem (i32.const 0) $f1)
       (func $f1 (param (ref $box)) (unreachable)))`),
                    WebAssembly.CompileError,
-                   /cannot expose reference type/);
+                   /cannot expose indexed reference type/);
 
 assertErrorMessage(() => wasmCompile(
     `(module
       (gc_feature_opt_in 3)
       (type $box (struct (field $val i32)))
       (import "m" "tbl" (table 1 funcref))
       (elem (i32.const 0) $f1)
       (func $f1 (result (ref $box)) (ref.null)))`),
                    WebAssembly.CompileError,
-                   /cannot expose reference type/);
+                   /cannot expose indexed reference type/);
 
 assertEq(typeof wasmCompile(
     `(module
       (import "m" "tbl" (table 1 funcref))
       (elem (i32.const 0) $f1)
       (func $f1 (param anyref) (unreachable)))`),
          "object");
 
@@ -241,28 +241,28 @@ assertErrorMessage(() => wasmCompile(
     `(module
       (gc_feature_opt_in 3)
       (type $box (struct (field $val i32)))
       (type $fn (func (param (ref $box))))
       (table (export "tbl") 1 funcref)
       (func (param i32)
        (call_indirect $fn (ref.null) (local.get 0))))`),
                    WebAssembly.CompileError,
-                   /cannot expose reference type/);
+                   /cannot expose indexed reference type/);
 
 assertErrorMessage(() => wasmCompile(
     `(module
       (gc_feature_opt_in 3)
       (type $box (struct (field $val i32)))
       (type $fn (func (result (ref $box))))
       (table (export "tbl") 1 funcref)
       (func (param i32) (result (ref $box))
        (call_indirect $fn (local.get 0))))`),
                    WebAssembly.CompileError,
-                   /cannot expose reference type/);
+                   /cannot expose indexed reference type/);
 
 assertEq(typeof wasmCompile(
     `(module
       (type $fn (func (param anyref)))
       (table (export "tbl") 1 funcref)
       (func (param i32)
        (call_indirect $fn (ref.null) (local.get 0))))`),
          "object");
@@ -281,28 +281,28 @@ assertErrorMessage(() => wasmCompile(
     `(module
       (gc_feature_opt_in 3)
       (type $box (struct (field $val i32)))
       (type $fn (func (param (ref $box))))
       (import "m" "tbl" (table 1 funcref))
       (func (param i32)
        (call_indirect $fn (ref.null) (local.get 0))))`),
                    WebAssembly.CompileError,
-                   /cannot expose reference type/);
+                   /cannot expose indexed reference type/);
 
 assertErrorMessage(() => wasmCompile(
     `(module
       (gc_feature_opt_in 3)
       (type $box (struct (field $val i32)))
       (type $fn (func (result (ref $box))))
       (import "m" "tbl" (table 1 funcref))
       (func (param i32) (result (ref $box))
        (call_indirect $fn (local.get 0))))`),
                    WebAssembly.CompileError,
-                   /cannot expose reference type/);
+                   /cannot expose indexed reference type/);
 
 assertEq(typeof wasmCompile(
     `(module
       (type $fn (func (param anyref)))
       (import "m" "tbl" (table 1 funcref))
       (func (param i32)
        (call_indirect $fn (ref.null) (local.get 0))))`),
          "object");
--- a/js/src/wasm/WasmAST.h
+++ b/js/src/wasm/WasmAST.h
@@ -128,17 +128,22 @@ class AstValType {
     }
   }
 
 #ifdef ENABLE_WASM_GC
   bool isNarrowType() const {
     if (which_ == IsAstRef) {
       return true;
     }
-    return type_ == RefType::any() || type_.isRef();
+    // This test is just heuristic; to do better (ie to tell whether it is a
+    // struct type) we need a type environment, but we have none.
+    //
+    // In most cases, we won't have a typeIndex here anyway, because types will
+    // not have been resolved.
+    return type_.isAnyRef() || type_.isTypeIndex();
   }
 #endif
 
   bool isValid() const { return !(which_ == IsValType && !type_.isValid()); }
 
   bool isResolved() const { return which_ == IsValType; }
 
   AstRef& asRef() { return ref_; }
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -11484,34 +11484,31 @@ bool BaseCompiler::emitStructNarrow() {
   }
 
   if (deadCode_) {
     return true;
   }
 
   // struct.narrow validation ensures that these hold.
 
-  MOZ_ASSERT(inputType.refTypeKind() == RefType::Any ||
-             inputType.refTypeKind() == RefType::TypeIndex);
-  MOZ_ASSERT(outputType.refTypeKind() == RefType::Any ||
-             outputType.refTypeKind() == RefType::TypeIndex);
-  MOZ_ASSERT_IF(outputType.refTypeKind() == RefType::Any,
-                inputType.refTypeKind() == RefType::Any);
+  MOZ_ASSERT(inputType.isAnyRef() || env_.isStructType(inputType));
+  MOZ_ASSERT(outputType.isAnyRef() || env_.isStructType(outputType));
+  MOZ_ASSERT_IF(outputType.isAnyRef(), inputType.isAnyRef());
 
   // AnyRef -> AnyRef is a no-op, just leave the value on the stack.
 
-  if (inputType == RefType::any() && outputType == RefType::any()) {
+  if (inputType.isAnyRef() && outputType.isAnyRef()) {
     return true;
   }
 
   RegPtr rp = popRef();
 
   // AnyRef -> (ref T) must first unbox; leaves rp or null
 
-  bool mustUnboxAnyref = inputType == RefType::any();
+  bool mustUnboxAnyref = inputType.isAnyRef();
 
   // Dynamic downcast (ref T) -> (ref U), leaves rp or null
   const StructType& outputStruct =
       env_.types[outputType.refType().typeIndex()].structType();
 
   pushI32(mustUnboxAnyref);
   pushI32(outputStruct.moduleIndex_);
   pushRef(rp);
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -461,19 +461,20 @@ bool js::wasm::GetImports(JSContext* cx,
               if (global.type() == ValType::I64) {
                 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                                          JSMSG_WASM_BAD_I64_LINK);
                 return false;
               }
             }
           } else {
             MOZ_ASSERT(global.type().isReference());
-            if (!v.isNull() && !v.isObject() && global.type().isRef()) {
+            if (!global.type().isAnyRef() && !v.isObjectOrNull()) {
               return ThrowBadImportType(cx, import.field.get(),
-                                        "Object-or-null");
+                                        "Object-or-null value required for "
+                                        "non-anyref reference type");
             }
           }
 
           if (global.isMutable()) {
             JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                                      JSMSG_WASM_BAD_GLOB_MUT_LINK);
             return false;
           }
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -1231,17 +1231,18 @@ bool Module::makeStructTypeDescrs(
         }
       } else {
         // TypedObjects don't yet have a sufficient notion of type
         // constraints on TypedObject properties.  Thus we handle fields
         // of type (ref T) by marking them as immutable for JS and by
         // rendering the objects non-constructible from JS.  Wasm
         // however sees properly-typed (ref T) fields with appropriate
         // mutability.
-        if (v.isRef()) {
+        if (v.isTypeIndex()) {
+          // Validation ensures that v references a struct type here.
           sf.isMutable = false;
           allowConstruct = false;
         }
 
         if (!MakeStructField(cx, v, sf.isMutable, "_%d", k++, &ids,
                              &fieldTypeObjs, &fieldProps)) {
           return false;
         }
--- a/js/src/wasm/WasmOpIter.h
+++ b/js/src/wasm/WasmOpIter.h
@@ -2063,18 +2063,19 @@ inline bool OpIter<Policy>::readCallIndi
 
   // FIXME: Remove this check when full multi-value function returns land.
   // Bug 1585909.
   if (funcType.results().length() > MaxFuncResults) {
     return fail("too many returns in signature");
   }
 
 #ifdef WASM_PRIVATE_REFTYPES
-  if (env_.tables[*tableIndex].importedOrExported && funcType.exposesRef()) {
-    return fail("cannot expose reference type");
+  if (env_.tables[*tableIndex].importedOrExported &&
+      funcType.exposesTypeIndex()) {
+    return fail("cannot expose indexed reference type");
   }
 #endif
 
   if (!popCallArgs(funcType.args(), argValues)) {
     return false;
   }
 
   return push(ResultType::Vector(funcType.results()));
@@ -2702,31 +2703,31 @@ inline bool OpIter<Policy>::readStructNa
   if (!readReferenceType(inputType, "struct.narrow")) {
     return false;
   }
 
   if (!readReferenceType(outputType, "struct.narrow")) {
     return false;
   }
 
-  if (inputType->isRef()) {
-    if (!outputType->isRef()) {
+  if (env_.isStructType(*inputType)) {
+    if (!env_.isStructType(*outputType)) {
       return fail("invalid type combination in struct.narrow");
     }
 
     const StructType& inputStruct =
         env_.types[inputType->refType().typeIndex()].structType();
     const StructType& outputStruct =
         env_.types[outputType->refType().typeIndex()].structType();
 
     if (!outputStruct.hasPrefix(inputStruct)) {
       return fail("invalid narrowing operation");
     }
-  } else if (*outputType == RefType::any()) {
-    if (*inputType != RefType::any()) {
+  } else if (outputType->isAnyRef()) {
+    if (!inputType->isAnyRef()) {
       return fail("invalid type combination in struct.narrow");
     }
   }
 
   if (!popWithType(*inputType, ptr)) {
     return false;
   }
 
--- a/js/src/wasm/WasmTextToBinary.cpp
+++ b/js/src/wasm/WasmTextToBinary.cpp
@@ -4710,26 +4710,26 @@ static bool ParseGlobalType(WasmParseCon
   }
 
   return true;
 }
 
 static bool ParseElemType(WasmParseContext& c, TableKind* tableKind) {
   WasmToken token;
   if (c.ts.getIf(WasmToken::ValueType, &token)) {
-    if (token.valueType() == RefType::func()) {
+    if (token.valueType().isFuncRef()) {
       *tableKind = TableKind::FuncRef;
       return true;
     }
 #ifdef ENABLE_WASM_REFTYPES
-    if (token.valueType() == RefType::any()) {
+    if (token.valueType().isAnyRef()) {
       *tableKind = TableKind::AnyRef;
       return true;
     }
-    if (token.valueType() == RefType::null()) {
+    if (token.valueType().isNullRef()) {
       *tableKind = TableKind::NullRef;
       return true;
     }
 #endif
   }
   c.ts.generateError(token, "generic reference type required", c.error);
   return false;
 }
@@ -7401,20 +7401,19 @@ static bool EncodeElemSegment(Encoder& e
   bool hasRefNull = false;
   for (const AstElem& elem : segment.elems()) {
     if (elem.is<AstNullValue>()) {
       hasRefNull = true;
       break;
     }
   }
 
-  ElemSegmentPayload payload =
-      hasRefNull || segment.elemType() != RefType::func()
-          ? ElemSegmentPayload::ElemExpression
-          : ElemSegmentPayload::ExternIndex;
+  ElemSegmentPayload payload = hasRefNull || !segment.elemType().isFuncRef()
+                                   ? ElemSegmentPayload::ElemExpression
+                                   : ElemSegmentPayload::ExternIndex;
 
   ElemSegmentKind kind;
   switch (segment.kind()) {
     case AstElemSegmentKind::Active: {
       kind = segment.targetTable().index() ||
                      payload != ElemSegmentPayload::ExternIndex
                  ? ElemSegmentKind::ActiveWithTableIndex
                  : ElemSegmentKind::Active;
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -462,17 +462,27 @@ class ValType {
     return tc_;
   }
 
   uint32_t bitsUnsafe() const {
     MOZ_ASSERT(isValid());
     return PackedTypeCodeToBits(tc_);
   }
 
-  bool isRef() const {
+  bool isAnyRef() const { return UnpackTypeCodeType(tc_) == TypeCode::AnyRef; }
+
+  bool isNullRef() const {
+    return UnpackTypeCodeType(tc_) == TypeCode::NullRef;
+  }
+
+  bool isFuncRef() const {
+    return UnpackTypeCodeType(tc_) == TypeCode::FuncRef;
+  }
+
+  bool isTypeIndex() const {
     MOZ_ASSERT(isValid());
     return UnpackTypeCodeType(tc_) == TypeCode::Ref;
   }
 
   bool isReference() const {
     MOZ_ASSERT(isValid());
     return IsReferenceType(tc_);
   }
@@ -928,17 +938,17 @@ class MOZ_NON_PARAM Val : public LitVal 
   explicit Val(uint64_t i64) : LitVal(i64) {}
   explicit Val(float f32) : LitVal(f32) {}
   explicit Val(double f64) : LitVal(f64) {}
   explicit Val(ValType type, AnyRef val) : LitVal(type, AnyRef::null()) {
     MOZ_ASSERT(type.isReference());
     u.ref_ = val;
   }
   explicit Val(ValType type, FuncRef val) : LitVal(type, AnyRef::null()) {
-    MOZ_ASSERT(type == RefType::func());
+    MOZ_ASSERT(type.isFuncRef());
     u.ref_ = val.asAnyRef();
   }
   void trace(JSTracer* trc);
 };
 
 typedef Rooted<Val> RootedVal;
 typedef Handle<Val> HandleVal;
 typedef MutableHandle<Val> MutableHandleVal;
@@ -1015,74 +1025,74 @@ class FuncType {
       }
     }
     return false;
   }
   // For JS->wasm jit entries, AnyRef parameters and returns are allowed,
   // as are all reference types apart from TypeIndex.
   bool temporarilyUnsupportedReftypeForEntry() const {
     for (ValType arg : args()) {
-      if (arg.isReference() && arg != RefType::any()) {
+      if (arg.isReference() && !arg.isAnyRef()) {
         return true;
       }
     }
     for (ValType result : results()) {
-      if (result.isRef()) {
+      if (result.isTypeIndex()) {
         return true;
       }
     }
     return false;
   }
   // For inlined JS->wasm jit entries, AnyRef parameters and returns are
   // allowed, as are all reference types apart from TypeIndex.
   bool temporarilyUnsupportedReftypeForInlineEntry() const {
     for (ValType arg : args()) {
-      if (arg.isReference() && arg != RefType::any()) {
+      if (arg.isReference() && !arg.isAnyRef()) {
         return true;
       }
     }
     for (ValType result : results()) {
-      if (result.isRef()) {
+      if (result.isTypeIndex()) {
         return true;
       }
     }
     return false;
   }
   // For wasm->JS jit exits, AnyRef parameters and returns are allowed, as are
   // reference type parameters of all types except TypeIndex.
   bool temporarilyUnsupportedReftypeForExit() const {
     for (ValType arg : args()) {
-      if (arg.isRef()) {
+      if (arg.isTypeIndex()) {
         return true;
       }
     }
     for (ValType result : results()) {
-      if (result.isReference() && result != RefType::any()) {
+      if (result.isReference() && !result.isAnyRef()) {
         return true;
       }
     }
     return false;
   }
   bool jitExitRequiresArgCheck() const {
     for (ValType arg : args()) {
       if (arg.isEncodedAsJSValueOnEscape()) {
         return true;
       }
     }
     return false;
   }
 #ifdef WASM_PRIVATE_REFTYPES
-  bool exposesRef() const {
+  bool exposesTypeIndex() const {
     for (const ValType& arg : args()) {
-      if (arg.isRef()) {
+      if (arg.isTypeIndex()) {
         return true;
       }
     }
     for (const ValType& result : results()) {
-      if (result.isRef()) {
+      if (result.isTypeIndex()) {
         return true;
       }
     }
     return false;
   }
 #endif
 
   WASM_DECLARE_SERIALIZABLE(FuncType)
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -1262,17 +1262,17 @@ static bool DecodePreamble(Decoder& d) {
 }
 
 enum class TypeState { None, Struct, ForwardStruct, Func };
 
 typedef Vector<TypeState, 0, SystemAllocPolicy> TypeStateVector;
 
 static bool ValidateTypeState(Decoder& d, TypeStateVector* typeState,
                               ValType type) {
-  if (!type.isRef()) {
+  if (!type.isTypeIndex()) {
     return true;
   }
 
   uint32_t refTypeIndex = type.refType().typeIndex();
   switch ((*typeState)[refTypeIndex]) {
     case TypeState::None:
       (*typeState)[refTypeIndex] = TypeState::ForwardStruct;
       break;
@@ -1282,18 +1282,18 @@ static bool ValidateTypeState(Decoder& d
     case TypeState::Func:
       return d.fail("ref does not reference a struct type");
   }
   return true;
 }
 
 #ifdef WASM_PRIVATE_REFTYPES
 static bool FuncTypeIsJSCompatible(Decoder& d, const FuncType& ft) {
-  if (ft.exposesRef()) {
-    return d.fail("cannot expose reference type");
+  if (ft.exposesTypeIndex()) {
+    return d.fail("cannot expose indexed reference type");
   }
   return true;
 }
 #endif
 
 static bool DecodeTypeVector(Decoder& d, ModuleEnvironment* env,
                              TypeStateVector* typeState, uint32_t count,
                              ValTypeVector* types) {
@@ -1712,17 +1712,17 @@ static bool GlobalIsJSCompatible(Decoder
     case ValType::Ref:
       switch (type.refTypeKind()) {
         case RefType::Func:
         case RefType::Any:
         case RefType::Null:
           break;
         case RefType::TypeIndex:
 #ifdef WASM_PRIVATE_REFTYPES
-          return d.fail("cannot expose reference type");
+          return d.fail("cannot expose indexed reference type");
 #else
           break;
 #endif
         default:
           return d.fail("unexpected variable type in global import/export");
       }
       break;
     default:
@@ -2049,17 +2049,17 @@ static bool DecodeInitializerExpression(
       *init = InitExpr(LitVal(f64));
       break;
     }
     case uint16_t(Op::RefNull): {
       if (!expected.isReference()) {
         return d.fail(
             "type mismatch: initializer type and expected type don't match");
       }
-      MOZ_ASSERT_IF(expected.isRef(), env->gcTypesEnabled());
+      MOZ_ASSERT_IF(env->isStructType(expected), env->gcTypesEnabled());
       *init = InitExpr(LitVal(expected, AnyRef::null()));
       break;
     }
     case uint16_t(Op::GetGlobal): {
       uint32_t i;
       const GlobalDescVector& globals = env->globals;
       if (!d.readVarU32(&i)) {
         return d.fail(
@@ -2069,21 +2069,23 @@ static bool DecodeInitializerExpression(
         return d.fail("global index out of range in initializer expression");
       }
       if (!globals[i].isImport() || globals[i].isMutable()) {
         return d.fail(
             "initializer expression must reference a global immutable import");
       }
       if (expected.isReference()) {
         bool fail = false;
-        if ((expected.isRef() || globals[i].type().isRef()) &&
-            !env->gcTypesEnabled()) {
+        if (!globals[i].type().isReference()) {
           fail = true;
-        } else if (!(globals[i].type().isReference() &&
-                     env->isRefSubtypeOf(globals[i].type(), expected))) {
+        } else if ((env->isStructType(expected) ||
+                    env->isStructType(globals[i].type())) &&
+                   !env->gcTypesEnabled()) {
+          fail = true;
+        } else if (!env->isRefSubtypeOf(globals[i].type(), expected)) {
           fail = true;
         }
         if (fail) {
           return d.fail(
               "type mismatch: initializer type and expected type don't match");
         }
         *init = InitExpr(i, expected);
       } else {
--- a/js/src/wasm/WasmValidate.h
+++ b/js/src/wasm/WasmValidate.h
@@ -237,26 +237,29 @@ struct ModuleEnvironment {
   bool funcIsImport(uint32_t funcIndex) const {
     return funcIndex < funcImportGlobalDataOffsets.length();
   }
   bool isRefSubtypeOf(ValType one, ValType two) const {
     MOZ_ASSERT(one.isReference());
     MOZ_ASSERT(two.isReference());
 #if defined(ENABLE_WASM_REFTYPES)
 #  if defined(ENABLE_WASM_GC)
-    return one == two || two == RefType::any() || one == RefType::null() ||
-           (one.isRef() && two.isRef() && gcTypesEnabled() &&
+    return one == two || two.isAnyRef() || one.isNullRef() ||
+           (isStructType(one) && isStructType(two) && gcTypesEnabled() &&
             isStructPrefixOf(two, one));
 #  else
-    return one == two || two == RefType::any() || one == RefType::null();
+    return one == two || two.isAnyRef() || one.isNullRef();
 #  endif
 #else
     return one == two;
 #endif
   }
+  bool isStructType(ValType t) const {
+    return t.isTypeIndex() && types[t.refType().typeIndex()].isStructType();
+  }
 
  private:
   bool isStructPrefixOf(ValType a, ValType b) const {
     const StructType& other = types[a.refType().typeIndex()].structType();
     return types[b.refType().typeIndex()].structType().hasPrefix(other);
   }
 };
 
@@ -400,17 +403,17 @@ class Encoder {
   // Variable-length encodings that all use LEB128.
 
   MOZ_MUST_USE bool writeVarU32(uint32_t i) { return writeVarU<uint32_t>(i); }
   MOZ_MUST_USE bool writeVarS32(int32_t i) { return writeVarS<int32_t>(i); }
   MOZ_MUST_USE bool writeVarU64(uint64_t i) { return writeVarU<uint64_t>(i); }
   MOZ_MUST_USE bool writeVarS64(int64_t i) { return writeVarS<int64_t>(i); }
   MOZ_MUST_USE bool writeValType(ValType type) {
     static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits");
-    if (type.isRef()) {
+    if (type.isTypeIndex()) {
       return writeFixedU8(uint8_t(TypeCode::Ref)) &&
              writeVarU32(type.refType().typeIndex());
     }
     TypeCode tc = UnpackTypeCodeType(type.packed());
     MOZ_ASSERT(size_t(tc) < size_t(TypeCode::Limit));
     return writeFixedU8(uint8_t(tc));
   }
   MOZ_MUST_USE bool writeOp(Op op) {
@@ -721,18 +724,19 @@ class Decoder {
     }
   }
   MOZ_MUST_USE bool readValType(const TypeDefVector& types,
                                 bool refTypesEnabled, bool gcTypesEnabled,
                                 ValType* type) {
     if (!readValType(types.length(), refTypesEnabled, gcTypesEnabled, type)) {
       return false;
     }
-    if (type->isRef() && !types[type->refType().typeIndex()].isStructType()) {
-      return fail("ref does not reference a struct type");
+    if (type->isTypeIndex() &&
+        !types[type->refType().typeIndex()].isStructType()) {
+      return fail("type index does not reference a struct type");
     }
     return true;
   }
   MOZ_MUST_USE bool readOp(OpBytes* op) {
     static_assert(size_t(Op::Limit) == 256, "fits");
     uint8_t u8;
     if (!readFixedU8(&u8)) {
       return false;