Commit bc3b9332 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by V8 LUCI CQ

[wasm-gc] Implement nominal types

Per https://github.com/WebAssembly/gc/issues/234, this implements
"nominal" type definitions with explicit supertypes, and statically
typed RTT-less instructions for allocation and testing/casting.
This should be fully backwards compatible with existing Wasm modules.

Spec: https://bit.ly/3cWcm6Q ("version 4")

Bug: v8:7748
Change-Id: Id5a1399b368fdfad22036cfd66f1bef593e640f7
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3144916
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarManos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#76844}
parent 62acef54
This diff is collapsed.
...@@ -550,35 +550,40 @@ class ModuleDecoderImpl : public Decoder { ...@@ -550,35 +550,40 @@ class ModuleDecoderImpl : public Decoder {
} }
void DecodeTypeSection() { void DecodeTypeSection() {
uint32_t signatures_count = consume_count("types count", kV8MaxWasmTypes); uint32_t types_count = consume_count("types count", kV8MaxWasmTypes);
module_->types.reserve(signatures_count); module_->types.reserve(types_count);
for (uint32_t i = 0; ok() && i < signatures_count; ++i) { for (uint32_t i = 0; ok() && i < types_count; ++i) {
TRACE("DecodeSignature[%d] module+%d\n", i, TRACE("DecodeSignature[%d] module+%d\n", i,
static_cast<int>(pc_ - start_)); static_cast<int>(pc_ - start_));
uint8_t kind = consume_u8("type kind"); uint8_t kind = consume_u8("type kind");
switch (kind) { switch (kind) {
case kWasmFunctionTypeCode: { case kWasmFunctionTypeCode:
case kWasmFunctionSubtypeCode: {
const FunctionSig* s = consume_sig(module_->signature_zone.get()); const FunctionSig* s = consume_sig(module_->signature_zone.get());
module_->add_signature(s); uint32_t super_index = kNoSuperType;
break; if (kind == kWasmFunctionSubtypeCode) {
} if (!enabled_features_.has_gc()) {
case kWasmFunctionExtendingTypeCode: { errorf(pc(),
if (!enabled_features_.has_gc()) { "invalid function type definition, enable with "
errorf(pc(), "--experimental-wasm-gc");
"invalid function type definition, enable with " break;
"--experimental-wasm-gc"); }
break; HeapType super_type = consume_super_type();
} if (super_type == HeapType::kFunc) {
const FunctionSig* s = consume_sig(module_->signature_zone.get()); super_index = kGenericSuperType;
module_->add_signature(s); } else if (super_type.is_index()) {
uint32_t super_index = consume_u32v("supertype"); super_index = super_type.representation();
if (!module_->has_signature(super_index)) { } else {
errorf(pc(), "invalid function supertype index: %d", super_index); errorf(pc(), "type %d: invalid supertype %d", i,
break; super_type.code());
break;
}
} }
module_->add_signature(s, super_index);
break; break;
} }
case kWasmStructTypeCode: { case kWasmStructTypeCode:
case kWasmStructSubtypeCode: {
if (!enabled_features_.has_gc()) { if (!enabled_features_.has_gc()) {
errorf(pc(), errorf(pc(),
"invalid struct type definition, enable with " "invalid struct type definition, enable with "
...@@ -586,39 +591,26 @@ class ModuleDecoderImpl : public Decoder { ...@@ -586,39 +591,26 @@ class ModuleDecoderImpl : public Decoder {
break; break;
} }
const StructType* s = consume_struct(module_->signature_zone.get()); const StructType* s = consume_struct(module_->signature_zone.get());
module_->add_struct_type(s); uint32_t super_index = kNoSuperType;
if (kind == kWasmStructSubtypeCode) {
HeapType super_type = consume_super_type();
if (super_type == HeapType::kData) {
super_index = kGenericSuperType;
} else if (super_type.is_index()) {
super_index = super_type.representation();
} else {
errorf(pc(), "type %d: invalid supertype %d", i,
super_type.code());
break;
}
}
module_->add_struct_type(s, super_index);
// TODO(7748): Should we canonicalize struct types, like // TODO(7748): Should we canonicalize struct types, like
// {signature_map} does for function signatures? // {signature_map} does for function signatures?
break; break;
} }
case kWasmStructExtendingTypeCode: { case kWasmArrayTypeCode:
if (!enabled_features_.has_gc()) { case kWasmArraySubtypeCode: {
errorf(pc(),
"invalid struct type definition, enable with "
"--experimental-wasm-gc");
break;
}
const StructType* s = consume_struct(module_->signature_zone.get());
module_->add_struct_type(s);
uint32_t super_index = consume_u32v("supertype");
if (!module_->has_struct(super_index)) {
errorf(pc(), "invalid struct supertype: %d", super_index);
break;
}
break;
}
case kWasmArrayTypeCode: {
if (!enabled_features_.has_gc()) {
errorf(pc(),
"invalid array type definition, enable with "
"--experimental-wasm-gc");
break;
}
const ArrayType* type = consume_array(module_->signature_zone.get());
module_->add_array_type(type);
break;
}
case kWasmArrayExtendingTypeCode: {
if (!enabled_features_.has_gc()) { if (!enabled_features_.has_gc()) {
errorf(pc(), errorf(pc(),
"invalid array type definition, enable with " "invalid array type definition, enable with "
...@@ -626,12 +618,20 @@ class ModuleDecoderImpl : public Decoder { ...@@ -626,12 +618,20 @@ class ModuleDecoderImpl : public Decoder {
break; break;
} }
const ArrayType* type = consume_array(module_->signature_zone.get()); const ArrayType* type = consume_array(module_->signature_zone.get());
module_->add_array_type(type); uint32_t super_index = kNoSuperType;
uint32_t super_index = consume_u32v("supertype"); if (kind == kWasmArraySubtypeCode) {
if (!module_->has_array(super_index)) { HeapType super_type = consume_super_type();
errorf(pc(), "invalid array supertype: %d", super_index); if (super_type == HeapType::kData) {
break; super_index = kGenericSuperType;
} else if (super_type.is_index()) {
super_index = super_type.representation();
} else {
errorf(pc(), "type %d: invalid supertype %d", i,
super_type.code());
break;
}
} }
module_->add_array_type(type, super_index);
break; break;
} }
default: default:
...@@ -639,6 +639,46 @@ class ModuleDecoderImpl : public Decoder { ...@@ -639,6 +639,46 @@ class ModuleDecoderImpl : public Decoder {
break; break;
} }
} }
// Check validity of explicitly defined supertypes.
const WasmModule* module = module_.get();
for (uint32_t i = 0; ok() && i < types_count; ++i) {
uint32_t explicit_super = module_->supertype(i);
if (explicit_super == kNoSuperType) continue;
if (explicit_super == kGenericSuperType) continue;
DCHECK_LT(explicit_super, types_count); // {consume_super_type} checks.
// Only types that have an explicit supertype themselves can be explicit
// supertypes of other types.
if (!module->has_supertype(explicit_super)) {
errorf("type %d has invalid explicit supertype %d", i, explicit_super);
continue;
}
int depth = GetSubtypingDepth(module, i);
if (depth > static_cast<int>(kV8MaxRttSubtypingDepth)) {
errorf("type %d: subtyping depth is greater than allowed", i);
continue;
}
if (depth == -1) {
errorf("type %d: cyclic inheritance", i);
continue;
}
switch (module_->type_kinds[i]) {
case kWasmStructTypeCode:
if (!module->has_struct(explicit_super)) break;
if (!StructIsSubtypeOf(i, explicit_super, module, module)) break;
continue;
case kWasmArrayTypeCode:
if (!module->has_array(explicit_super)) break;
if (!ArrayIsSubtypeOf(i, explicit_super, module, module)) break;
continue;
case kWasmFunctionTypeCode:
if (!module->has_signature(explicit_super)) break;
if (!FunctionIsSubtypeOf(i, explicit_super, module, module)) break;
continue;
default:
UNREACHABLE();
}
errorf("type %d has invalid explicit supertype %d", i, explicit_super);
}
module_->signature_map.Freeze(); module_->signature_map.Freeze();
} }
...@@ -1787,6 +1827,15 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1787,6 +1827,15 @@ class ModuleDecoderImpl : public Decoder {
return result; return result;
} }
HeapType consume_super_type() {
uint32_t type_length;
HeapType result = value_type_reader::read_heap_type<kFullValidation>(
this, this->pc(), &type_length, module_.get(),
origin_ == kWasmOrigin ? enabled_features_ : WasmFeatures::None());
consume_bytes(type_length, "supertype");
return result;
}
ValueType consume_storage_type() { ValueType consume_storage_type() {
uint8_t opcode = read_u8<kFullValidation>(this->pc()); uint8_t opcode = read_u8<kFullValidation>(this->pc());
switch (opcode) { switch (opcode) {
......
...@@ -194,6 +194,41 @@ Handle<Map> CreateArrayMap(Isolate* isolate, const WasmModule* module, ...@@ -194,6 +194,41 @@ Handle<Map> CreateArrayMap(Isolate* isolate, const WasmModule* module,
return map; return map;
} }
void CreateMapForType(Isolate* isolate, const WasmModule* module,
int type_index, Handle<WasmInstanceObject> instance,
Handle<FixedArray> maps) {
// Recursive calls for supertypes may already have created this map.
if (maps->get(type_index).IsMap()) return;
Handle<Map> rtt_parent;
// If the type with {type_index} has an explicit supertype, make sure the
// map for that supertype is created first, so that the supertypes list
// that's cached on every RTT can be set up correctly.
uint32_t supertype = module->supertype(type_index);
if (supertype != kNoSuperType && supertype != kGenericSuperType) {
// This recursion is safe, because kV8MaxRttSubtypingDepth limits the
// number of recursive steps, so we won't overflow the stack.
CreateMapForType(isolate, module, supertype, instance, maps);
rtt_parent = handle(Map::cast(maps->get(supertype)), isolate);
}
Handle<Map> map;
switch (module->type_kinds[type_index]) {
case kWasmStructTypeCode:
map = CreateStructMap(isolate, module, type_index, rtt_parent, instance);
break;
case kWasmArrayTypeCode:
map = CreateArrayMap(isolate, module, type_index, rtt_parent, instance);
break;
case kWasmFunctionTypeCode:
// TODO(7748): Think about canonicalizing rtts to make them work for
// identical function types.
map = Map::Copy(isolate, isolate->wasm_exported_function_map(),
"fresh function map for function type canonical rtt "
"initialization");
break;
}
maps->set(type_index, *map);
}
namespace { namespace {
// TODO(7748): Consider storing this array in Maps' // TODO(7748): Consider storing this array in Maps'
...@@ -661,28 +696,8 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() { ...@@ -661,28 +696,8 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
if (enabled_.has_gc()) { if (enabled_.has_gc()) {
Handle<FixedArray> maps = isolate_->factory()->NewFixedArray( Handle<FixedArray> maps = isolate_->factory()->NewFixedArray(
static_cast<int>(module_->type_kinds.size())); static_cast<int>(module_->type_kinds.size()));
for (int map_index = 0; for (uint32_t index = 0; index < module_->type_kinds.size(); index++) {
map_index < static_cast<int>(module_->type_kinds.size()); CreateMapForType(isolate_, module_, index, instance, maps);
map_index++) {
Handle<Map> map;
switch (module_->type_kinds[map_index]) {
case kWasmStructTypeCode:
map = CreateStructMap(isolate_, module_, map_index, Handle<Map>(),
instance);
break;
case kWasmArrayTypeCode:
map = CreateArrayMap(isolate_, module_, map_index, Handle<Map>(),
instance);
break;
case kWasmFunctionTypeCode:
// TODO(7748): Think about canonicalizing rtts to make them work for
// identical function types.
map = Map::Copy(isolate_, isolate_->wasm_exported_function_map(),
"fresh function map for function type canonical rtt "
"initialization");
break;
}
maps->set(map_index, *map);
} }
instance->set_managed_object_maps(*maps); instance->set_managed_object_maps(*maps);
} }
......
...@@ -50,9 +50,9 @@ enum ValueTypeCode : uint8_t { ...@@ -50,9 +50,9 @@ enum ValueTypeCode : uint8_t {
constexpr uint8_t kWasmFunctionTypeCode = 0x60; constexpr uint8_t kWasmFunctionTypeCode = 0x60;
constexpr uint8_t kWasmStructTypeCode = 0x5f; constexpr uint8_t kWasmStructTypeCode = 0x5f;
constexpr uint8_t kWasmArrayTypeCode = 0x5e; constexpr uint8_t kWasmArrayTypeCode = 0x5e;
constexpr uint8_t kWasmFunctionExtendingTypeCode = 0x5d; constexpr uint8_t kWasmFunctionSubtypeCode = 0x5d;
constexpr uint8_t kWasmStructExtendingTypeCode = 0x5c; constexpr uint8_t kWasmStructSubtypeCode = 0x5c;
constexpr uint8_t kWasmArrayExtendingTypeCode = 0x5b; constexpr uint8_t kWasmArraySubtypeCode = 0x5b;
// Binary encoding of import/export kinds. // Binary encoding of import/export kinds.
enum ImportExportKindCode : uint8_t { enum ImportExportKindCode : uint8_t {
......
...@@ -39,7 +39,9 @@ ValueType WasmInitExpr::type(const WasmModule* module, ...@@ -39,7 +39,9 @@ ValueType WasmInitExpr::type(const WasmModule* module,
case kRefNullConst: case kRefNullConst:
return ValueType::Ref(immediate().heap_type, kNullable); return ValueType::Ref(immediate().heap_type, kNullable);
case kStructNewWithRtt: case kStructNewWithRtt:
case kStructNew:
case kArrayInit: case kArrayInit:
case kArrayInitStatic:
return ValueType::Ref(immediate().index, kNonNullable); return ValueType::Ref(immediate().index, kNonNullable);
case kRttCanon: case kRttCanon:
return ValueType::Rtt(immediate().heap_type, 0); return ValueType::Rtt(immediate().heap_type, 0);
......
...@@ -34,7 +34,9 @@ class WasmInitExpr { ...@@ -34,7 +34,9 @@ class WasmInitExpr {
kRefNullConst, kRefNullConst,
kRefFuncConst, kRefFuncConst,
kStructNewWithRtt, kStructNewWithRtt,
kStructNew,
kArrayInit, kArrayInit,
kArrayInitStatic,
kRttCanon, kRttCanon,
kRttSub, kRttSub,
kRttFreshSub, kRttFreshSub,
...@@ -99,6 +101,15 @@ class WasmInitExpr { ...@@ -99,6 +101,15 @@ class WasmInitExpr {
return expr; return expr;
} }
static WasmInitExpr StructNew(uint32_t index,
std::vector<WasmInitExpr> elements) {
WasmInitExpr expr;
expr.kind_ = kStructNew;
expr.immediate_.index = index;
expr.operands_ = std::move(elements);
return expr;
}
static WasmInitExpr ArrayInit(uint32_t index, static WasmInitExpr ArrayInit(uint32_t index,
std::vector<WasmInitExpr> elements) { std::vector<WasmInitExpr> elements) {
WasmInitExpr expr; WasmInitExpr expr;
...@@ -108,6 +119,15 @@ class WasmInitExpr { ...@@ -108,6 +119,15 @@ class WasmInitExpr {
return expr; return expr;
} }
static WasmInitExpr ArrayInitStatic(uint32_t index,
std::vector<WasmInitExpr> elements) {
WasmInitExpr expr;
expr.kind_ = kArrayInitStatic;
expr.immediate_.index = index;
expr.operands_ = std::move(elements);
return expr;
}
static WasmInitExpr RttCanon(uint32_t index) { static WasmInitExpr RttCanon(uint32_t index) {
WasmInitExpr expr; WasmInitExpr expr;
expr.kind_ = kRttCanon; expr.kind_ = kRttCanon;
...@@ -157,6 +177,7 @@ class WasmInitExpr { ...@@ -157,6 +177,7 @@ class WasmInitExpr {
case kRefNullConst: case kRefNullConst:
return immediate().heap_type == other.immediate().heap_type; return immediate().heap_type == other.immediate().heap_type;
case kStructNewWithRtt: case kStructNewWithRtt:
case kStructNew:
if (immediate().index != other.immediate().index) return false; if (immediate().index != other.immediate().index) return false;
DCHECK_EQ(operands().size(), other.operands().size()); DCHECK_EQ(operands().size(), other.operands().size());
for (uint32_t i = 0; i < operands().size(); i++) { for (uint32_t i = 0; i < operands().size(); i++) {
...@@ -164,6 +185,7 @@ class WasmInitExpr { ...@@ -164,6 +185,7 @@ class WasmInitExpr {
} }
return true; return true;
case kArrayInit: case kArrayInit:
case kArrayInitStatic:
if (immediate().index != other.immediate().index) return false; if (immediate().index != other.immediate().index) return false;
if (operands().size() != other.operands().size()) return false; if (operands().size() != other.operands().size()) return false;
for (uint32_t i = 0; i < operands().size(); i++) { for (uint32_t i = 0; i < operands().size(); i++) {
......
...@@ -290,12 +290,12 @@ void WasmModuleBuilder::AddDataSegment(const byte* data, uint32_t size, ...@@ -290,12 +290,12 @@ void WasmModuleBuilder::AddDataSegment(const byte* data, uint32_t size,
} }
} }
uint32_t WasmModuleBuilder::AddSignature(FunctionSig* sig) { uint32_t WasmModuleBuilder::AddSignature(FunctionSig* sig, uint32_t supertype) {
auto sig_entry = signature_map_.find(*sig); auto sig_entry = signature_map_.find(*sig);
if (sig_entry != signature_map_.end()) return sig_entry->second; if (sig_entry != signature_map_.end()) return sig_entry->second;
uint32_t index = static_cast<uint32_t>(types_.size()); uint32_t index = static_cast<uint32_t>(types_.size());
signature_map_.emplace(*sig, index); signature_map_.emplace(*sig, index);
types_.push_back(Type(sig)); types_.push_back(Type(sig, supertype));
return index; return index;
} }
...@@ -307,15 +307,16 @@ uint32_t WasmModuleBuilder::AddException(FunctionSig* type) { ...@@ -307,15 +307,16 @@ uint32_t WasmModuleBuilder::AddException(FunctionSig* type) {
return except_index; return except_index;
} }
uint32_t WasmModuleBuilder::AddStructType(StructType* type) { uint32_t WasmModuleBuilder::AddStructType(StructType* type,
uint32_t supertype) {
uint32_t index = static_cast<uint32_t>(types_.size()); uint32_t index = static_cast<uint32_t>(types_.size());
types_.push_back(Type(type)); types_.push_back(Type(type, supertype));
return index; return index;
} }
uint32_t WasmModuleBuilder::AddArrayType(ArrayType* type) { uint32_t WasmModuleBuilder::AddArrayType(ArrayType* type, uint32_t supertype) {
uint32_t index = static_cast<uint32_t>(types_.size()); uint32_t index = static_cast<uint32_t>(types_.size());
types_.push_back(Type(type)); types_.push_back(Type(type, supertype));
return index; return index;
} }
...@@ -509,22 +510,30 @@ void WriteInitializerExpressionWithEnd(ZoneBuffer* buffer, ...@@ -509,22 +510,30 @@ void WriteInitializerExpressionWithEnd(ZoneBuffer* buffer,
} }
break; break;
} }
case WasmInitExpr::kStructNew:
case WasmInitExpr::kStructNewWithRtt: case WasmInitExpr::kStructNewWithRtt:
STATIC_ASSERT((kExprStructNew >> 8) == kGCPrefix);
STATIC_ASSERT((kExprStructNewWithRtt >> 8) == kGCPrefix); STATIC_ASSERT((kExprStructNewWithRtt >> 8) == kGCPrefix);
for (const WasmInitExpr& operand : init.operands()) { for (const WasmInitExpr& operand : init.operands()) {
WriteInitializerExpressionWithEnd(buffer, operand, kWasmBottom); WriteInitializerExpressionWithEnd(buffer, operand, kWasmBottom);
} }
buffer->write_u8(kGCPrefix); buffer->write_u8(kGCPrefix);
buffer->write_u8(static_cast<uint8_t>(kExprStructNewWithRtt)); buffer->write_u8(static_cast<uint8_t>(
init.kind() == WasmInitExpr::kStructNew ? kExprStructNew
: kExprStructNewWithRtt));
buffer->write_u32v(init.immediate().index); buffer->write_u32v(init.immediate().index);
break; break;
case WasmInitExpr::kArrayInit: case WasmInitExpr::kArrayInit:
case WasmInitExpr::kArrayInitStatic:
STATIC_ASSERT((kExprArrayInit >> 8) == kGCPrefix); STATIC_ASSERT((kExprArrayInit >> 8) == kGCPrefix);
STATIC_ASSERT((kExprArrayInitStatic >> 8) == kGCPrefix);
for (const WasmInitExpr& operand : init.operands()) { for (const WasmInitExpr& operand : init.operands()) {
WriteInitializerExpressionWithEnd(buffer, operand, kWasmBottom); WriteInitializerExpressionWithEnd(buffer, operand, kWasmBottom);
} }
buffer->write_u8(kGCPrefix); buffer->write_u8(kGCPrefix);
buffer->write_u8(static_cast<uint8_t>(kExprArrayInit)); buffer->write_u8(static_cast<uint8_t>(
init.kind() == WasmInitExpr::kArrayInit ? kExprArrayInit
: kExprArrayInitStatic));
buffer->write_u32v(init.immediate().index); buffer->write_u32v(init.immediate().index);
buffer->write_u32v(static_cast<uint32_t>(init.operands().size() - 1)); buffer->write_u32v(static_cast<uint32_t>(init.operands().size() - 1));
break; break;
...@@ -568,10 +577,12 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const { ...@@ -568,10 +577,12 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const {
buffer->write_size(types_.size()); buffer->write_size(types_.size());
for (const Type& type : types_) { for (const Type& type : types_) {
bool has_super = type.supertype != kNoSuperType;
switch (type.kind) { switch (type.kind) {
case Type::kFunctionSig: { case Type::kFunctionSig: {
FunctionSig* sig = type.sig; FunctionSig* sig = type.sig;
buffer->write_u8(kWasmFunctionTypeCode); buffer->write_u8(has_super ? kWasmFunctionSubtypeCode
: kWasmFunctionTypeCode);
buffer->write_size(sig->parameter_count()); buffer->write_size(sig->parameter_count());
for (auto param : sig->parameters()) { for (auto param : sig->parameters()) {
WriteValueType(buffer, param); WriteValueType(buffer, param);
...@@ -580,23 +591,40 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const { ...@@ -580,23 +591,40 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const {
for (auto ret : sig->returns()) { for (auto ret : sig->returns()) {
WriteValueType(buffer, ret); WriteValueType(buffer, ret);
} }
if (type.supertype == kGenericSuperType) {
buffer->write_u8(kFuncRefCode);
} else if (has_super) {
buffer->write_i32v(type.supertype);
}
break; break;
} }
case Type::kStructType: { case Type::kStructType: {
StructType* struct_type = type.struct_type; StructType* struct_type = type.struct_type;
buffer->write_u8(kWasmStructTypeCode); buffer->write_u8(has_super ? kWasmStructSubtypeCode
: kWasmStructTypeCode);
buffer->write_size(struct_type->field_count()); buffer->write_size(struct_type->field_count());
for (uint32_t i = 0; i < struct_type->field_count(); i++) { for (uint32_t i = 0; i < struct_type->field_count(); i++) {
WriteValueType(buffer, struct_type->field(i)); WriteValueType(buffer, struct_type->field(i));
buffer->write_u8(struct_type->mutability(i) ? 1 : 0); buffer->write_u8(struct_type->mutability(i) ? 1 : 0);
} }
if (type.supertype == kGenericSuperType) {
buffer->write_u8(kDataRefCode);
} else if (has_super) {
buffer->write_i32v(type.supertype);
}
break; break;
} }
case Type::kArrayType: { case Type::kArrayType: {
ArrayType* array_type = type.array_type; ArrayType* array_type = type.array_type;
buffer->write_u8(kWasmArrayTypeCode); buffer->write_u8(has_super ? kWasmArraySubtypeCode
: kWasmArrayTypeCode);
WriteValueType(buffer, array_type->element_type()); WriteValueType(buffer, array_type->element_type());
buffer->write_u8(array_type->mutability() ? 1 : 0); buffer->write_u8(array_type->mutability() ? 1 : 0);
if (type.supertype == kGenericSuperType) {
buffer->write_u8(kDataRefCode);
} else if (has_super) {
buffer->write_i32v(type.supertype);
}
break; break;
} }
} }
......
...@@ -331,10 +331,10 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject { ...@@ -331,10 +331,10 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
// size, or the maximum uint32_t value if the maximum table size has been // size, or the maximum uint32_t value if the maximum table size has been
// exceeded. // exceeded.
uint32_t IncreaseTableMinSize(uint32_t table_index, uint32_t count); uint32_t IncreaseTableMinSize(uint32_t table_index, uint32_t count);
uint32_t AddSignature(FunctionSig* sig); uint32_t AddSignature(FunctionSig* sig, uint32_t supertype = kNoSuperType);
uint32_t AddException(FunctionSig* type); uint32_t AddException(FunctionSig* type);
uint32_t AddStructType(StructType* type); uint32_t AddStructType(StructType* type, uint32_t supertype = kNoSuperType);
uint32_t AddArrayType(ArrayType* type); uint32_t AddArrayType(ArrayType* type, uint32_t supertype = kNoSuperType);
uint32_t AddTable(ValueType type, uint32_t min_size); uint32_t AddTable(ValueType type, uint32_t min_size);
uint32_t AddTable(ValueType type, uint32_t min_size, uint32_t max_size); uint32_t AddTable(ValueType type, uint32_t min_size, uint32_t max_size);
uint32_t AddTable(ValueType type, uint32_t min_size, uint32_t max_size, uint32_t AddTable(ValueType type, uint32_t min_size, uint32_t max_size,
...@@ -399,13 +399,14 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject { ...@@ -399,13 +399,14 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
private: private:
struct Type { struct Type {
enum Kind { kFunctionSig, kStructType, kArrayType }; enum Kind { kFunctionSig, kStructType, kArrayType };
explicit Type(FunctionSig* signature) explicit Type(FunctionSig* signature, uint32_t supertype)
: kind(kFunctionSig), sig(signature) {} : kind(kFunctionSig), supertype(supertype), sig(signature) {}
explicit Type(StructType* struct_type) explicit Type(StructType* struct_type, uint32_t supertype)
: kind(kStructType), struct_type(struct_type) {} : kind(kStructType), supertype(supertype), struct_type(struct_type) {}
explicit Type(ArrayType* array_type) explicit Type(ArrayType* array_type, uint32_t supertype)
: kind(kArrayType), array_type(array_type) {} : kind(kArrayType), supertype(supertype), array_type(array_type) {}
Kind kind; Kind kind;
uint32_t supertype;
union { union {
FunctionSig* sig; FunctionSig* sig;
StructType* struct_type; StructType* struct_type;
......
...@@ -113,6 +113,23 @@ int GetContainingWasmFunction(const WasmModule* module, uint32_t byte_offset) { ...@@ -113,6 +113,23 @@ int GetContainingWasmFunction(const WasmModule* module, uint32_t byte_offset) {
return func_index; return func_index;
} }
// TODO(7748): Measure whether this iterative implementation is fast enough.
// We could cache the result on the module, in yet another vector indexed by
// type index.
int GetSubtypingDepth(const WasmModule* module, uint32_t type_index) {
uint32_t starting_point = type_index;
int depth = 0;
while ((type_index = module->supertype(type_index)) != kGenericSuperType) {
if (type_index == starting_point) return -1; // Cycle detected.
// This is disallowed and will be rejected by validation, but might occur
// when this function is called.
if (type_index == kNoSuperType) break;
depth++;
if (depth > static_cast<int>(kV8MaxRttSubtypingDepth)) break;
}
return depth;
}
void LazilyGeneratedNames::AddForTesting(int function_index, void LazilyGeneratedNames::AddForTesting(int function_index,
WireBytesRef name) { WireBytesRef name) {
base::MutexGuard lock(&mutex_); base::MutexGuard lock(&mutex_);
......
...@@ -259,6 +259,11 @@ struct V8_EXPORT_PRIVATE WasmDebugSymbols { ...@@ -259,6 +259,11 @@ struct V8_EXPORT_PRIVATE WasmDebugSymbols {
struct WasmTable; struct WasmTable;
// End of a chain of explicit supertypes.
constexpr uint32_t kGenericSuperType = 0xFFFFFFFE;
// Used for types that have no explicit supertype.
constexpr uint32_t kNoSuperType = 0xFFFFFFFF;
// Static representation of a module. // Static representation of a module.
struct V8_EXPORT_PRIVATE WasmModule { struct V8_EXPORT_PRIVATE WasmModule {
std::unique_ptr<Zone> signature_zone; std::unique_ptr<Zone> signature_zone;
...@@ -288,6 +293,7 @@ struct V8_EXPORT_PRIVATE WasmModule { ...@@ -288,6 +293,7 @@ struct V8_EXPORT_PRIVATE WasmModule {
WireBytesRef name = {0, 0}; WireBytesRef name = {0, 0};
std::vector<TypeDefinition> types; // by type index std::vector<TypeDefinition> types; // by type index
std::vector<uint8_t> type_kinds; // by type index std::vector<uint8_t> type_kinds; // by type index
std::vector<uint32_t> supertypes; // by type index
// Map from each type index to the index of its corresponding canonical type. // Map from each type index to the index of its corresponding canonical type.
// Note: right now, only functions are canonicalized, and arrays and structs // Note: right now, only functions are canonicalized, and arrays and structs
// map to themselves. // map to themselves.
...@@ -295,9 +301,10 @@ struct V8_EXPORT_PRIVATE WasmModule { ...@@ -295,9 +301,10 @@ struct V8_EXPORT_PRIVATE WasmModule {
bool has_type(uint32_t index) const { return index < types.size(); } bool has_type(uint32_t index) const { return index < types.size(); }
void add_signature(const FunctionSig* sig) { void add_signature(const FunctionSig* sig, uint32_t supertype) {
types.push_back(TypeDefinition(sig)); types.push_back(TypeDefinition(sig));
type_kinds.push_back(kWasmFunctionTypeCode); type_kinds.push_back(kWasmFunctionTypeCode);
supertypes.push_back(supertype);
uint32_t canonical_id = sig ? signature_map.FindOrInsert(*sig) : 0; uint32_t canonical_id = sig ? signature_map.FindOrInsert(*sig) : 0;
canonicalized_type_ids.push_back(canonical_id); canonicalized_type_ids.push_back(canonical_id);
} }
...@@ -309,9 +316,10 @@ struct V8_EXPORT_PRIVATE WasmModule { ...@@ -309,9 +316,10 @@ struct V8_EXPORT_PRIVATE WasmModule {
return types[index].function_sig; return types[index].function_sig;
} }
void add_struct_type(const StructType* type) { void add_struct_type(const StructType* type, uint32_t supertype) {
types.push_back(TypeDefinition(type)); types.push_back(TypeDefinition(type));
type_kinds.push_back(kWasmStructTypeCode); type_kinds.push_back(kWasmStructTypeCode);
supertypes.push_back(supertype);
// No canonicalization for structs. // No canonicalization for structs.
canonicalized_type_ids.push_back(0); canonicalized_type_ids.push_back(0);
} }
...@@ -323,9 +331,10 @@ struct V8_EXPORT_PRIVATE WasmModule { ...@@ -323,9 +331,10 @@ struct V8_EXPORT_PRIVATE WasmModule {
return types[index].struct_type; return types[index].struct_type;
} }
void add_array_type(const ArrayType* type) { void add_array_type(const ArrayType* type, uint32_t supertype) {
types.push_back(TypeDefinition(type)); types.push_back(TypeDefinition(type));
type_kinds.push_back(kWasmArrayTypeCode); type_kinds.push_back(kWasmArrayTypeCode);
supertypes.push_back(supertype);
// No canonicalization for arrays. // No canonicalization for arrays.
canonicalized_type_ids.push_back(0); canonicalized_type_ids.push_back(0);
} }
...@@ -337,6 +346,14 @@ struct V8_EXPORT_PRIVATE WasmModule { ...@@ -337,6 +346,14 @@ struct V8_EXPORT_PRIVATE WasmModule {
return types[index].array_type; return types[index].array_type;
} }
uint32_t supertype(uint32_t index) const {
DCHECK(index < supertypes.size());
return supertypes[index];
}
bool has_supertype(uint32_t index) const {
return supertype(index) != kNoSuperType;
}
std::vector<WasmFunction> functions; std::vector<WasmFunction> functions;
std::vector<WasmDataSegment> data_segments; std::vector<WasmDataSegment> data_segments;
std::vector<WasmTable> tables; std::vector<WasmTable> tables;
...@@ -418,6 +435,12 @@ int GetContainingWasmFunction(const WasmModule* module, uint32_t byte_offset); ...@@ -418,6 +435,12 @@ int GetContainingWasmFunction(const WasmModule* module, uint32_t byte_offset);
// contained within a function. // contained within a function.
int GetNearestWasmFunction(const WasmModule* module, uint32_t byte_offset); int GetNearestWasmFunction(const WasmModule* module, uint32_t byte_offset);
// Gets the explicitly defined subtyping depth for the given type.
// Returns 0 if the type has no explicit supertype.
// The result is capped to {kV8MaxRttSubtypingDepth + 1}.
// Invalid cyclic hierarchies will return -1.
int GetSubtypingDepth(const WasmModule* module, uint32_t type_index);
// Interface to the storage (wire bytes) of a wasm module. // Interface to the storage (wire bytes) of a wasm module.
// It is illegal for anyone receiving a ModuleWireBytes to store pointers based // It is illegal for anyone receiving a ModuleWireBytes to store pointers based
// on module_bytes, as this storage is only guaranteed to be alive as long as // on module_bytes, as this storage is only guaranteed to be alive as long as
......
...@@ -382,12 +382,16 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) { ...@@ -382,12 +382,16 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
// GC operations. // GC operations.
CASE_OP(StructNewWithRtt, "struct.new_with_rtt") CASE_OP(StructNewWithRtt, "struct.new_with_rtt")
CASE_OP(StructNewDefaultWithRtt, "struct.new_default_with_rtt")
CASE_OP(StructNew, "struct.new")
CASE_OP(StructNewDefault, "struct.new_default") CASE_OP(StructNewDefault, "struct.new_default")
CASE_OP(StructGet, "struct.get") CASE_OP(StructGet, "struct.get")
CASE_OP(StructGetS, "struct.get_s") CASE_OP(StructGetS, "struct.get_s")
CASE_OP(StructGetU, "struct.get_u") CASE_OP(StructGetU, "struct.get_u")
CASE_OP(StructSet, "struct.set") CASE_OP(StructSet, "struct.set")
CASE_OP(ArrayNewWithRtt, "array.new_with_rtt") CASE_OP(ArrayNewWithRtt, "array.new_with_rtt")
CASE_OP(ArrayNewDefaultWithRtt, "array.new_default_with_rtt")
CASE_OP(ArrayNew, "array.new")
CASE_OP(ArrayNewDefault, "array.new_default") CASE_OP(ArrayNewDefault, "array.new_default")
CASE_OP(ArrayGet, "array.get") CASE_OP(ArrayGet, "array.get")
CASE_OP(ArrayGetS, "array.get_s") CASE_OP(ArrayGetS, "array.get_s")
...@@ -396,6 +400,7 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) { ...@@ -396,6 +400,7 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_OP(ArrayLen, "array.len") CASE_OP(ArrayLen, "array.len")
CASE_OP(ArrayCopy, "array.copy") CASE_OP(ArrayCopy, "array.copy")
CASE_OP(ArrayInit, "array.init") CASE_OP(ArrayInit, "array.init")
CASE_OP(ArrayInitStatic, "array.init_static")
CASE_OP(I31New, "i31.new") CASE_OP(I31New, "i31.new")
CASE_OP(I31GetS, "i31.get_s") CASE_OP(I31GetS, "i31.get_s")
CASE_OP(I31GetU, "i31.get_u") CASE_OP(I31GetU, "i31.get_u")
...@@ -403,9 +408,13 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) { ...@@ -403,9 +408,13 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_OP(RttSub, "rtt.sub") CASE_OP(RttSub, "rtt.sub")
CASE_OP(RttFreshSub, "rtt.fresh_sub") CASE_OP(RttFreshSub, "rtt.fresh_sub")
CASE_OP(RefTest, "ref.test") CASE_OP(RefTest, "ref.test")
CASE_OP(RefTestStatic, "ref.test_static")
CASE_OP(RefCast, "ref.cast") CASE_OP(RefCast, "ref.cast")
CASE_OP(RefCastStatic, "ref.cast_static")
CASE_OP(BrOnCast, "br_on_cast") CASE_OP(BrOnCast, "br_on_cast")
CASE_OP(BrOnCastStatic, "br_on_cast_static")
CASE_OP(BrOnCastFail, "br_on_cast_fail") CASE_OP(BrOnCastFail, "br_on_cast_fail")
CASE_OP(BrOnCastStaticFail, "br_on_cast_static_fail")
CASE_OP(RefIsFunc, "ref.is_func") CASE_OP(RefIsFunc, "ref.is_func")
CASE_OP(RefIsData, "ref.is_data") CASE_OP(RefIsData, "ref.is_data")
CASE_OP(RefIsI31, "ref.is_i31") CASE_OP(RefIsI31, "ref.is_i31")
......
...@@ -650,13 +650,15 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig, ...@@ -650,13 +650,15 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig,
#define FOREACH_GC_OPCODE(V) \ #define FOREACH_GC_OPCODE(V) \
V(StructNewWithRtt, 0xfb01, _) \ V(StructNewWithRtt, 0xfb01, _) \
V(StructNewDefault, 0xfb02, _) \ V(StructNewDefaultWithRtt, 0xfb02, _) \
V(StructGet, 0xfb03, _) \ V(StructGet, 0xfb03, _) \
V(StructGetS, 0xfb04, _) \ V(StructGetS, 0xfb04, _) \
V(StructGetU, 0xfb05, _) \ V(StructGetU, 0xfb05, _) \
V(StructSet, 0xfb06, _) \ V(StructSet, 0xfb06, _) \
V(StructNew, 0xfb07, _) \
V(StructNewDefault, 0xfb08, _) \
V(ArrayNewWithRtt, 0xfb11, _) \ V(ArrayNewWithRtt, 0xfb11, _) \
V(ArrayNewDefault, 0xfb12, _) \ V(ArrayNewDefaultWithRtt, 0xfb12, _) \
V(ArrayGet, 0xfb13, _) \ V(ArrayGet, 0xfb13, _) \
V(ArrayGetS, 0xfb14, _) \ V(ArrayGetS, 0xfb14, _) \
V(ArrayGetU, 0xfb15, _) \ V(ArrayGetU, 0xfb15, _) \
...@@ -664,6 +666,9 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig, ...@@ -664,6 +666,9 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig,
V(ArrayLen, 0xfb17, _) \ V(ArrayLen, 0xfb17, _) \
V(ArrayCopy, 0xfb18, _) /* not standardized - V8 experimental */ \ V(ArrayCopy, 0xfb18, _) /* not standardized - V8 experimental */ \
V(ArrayInit, 0xfb19, _) /* not standardized - V8 experimental */ \ V(ArrayInit, 0xfb19, _) /* not standardized - V8 experimental */ \
V(ArrayInitStatic, 0xfb1a, _) \
V(ArrayNew, 0xfb1b, _) \
V(ArrayNewDefault, 0xfb1c, _) \
V(I31New, 0xfb20, _) \ V(I31New, 0xfb20, _) \
V(I31GetS, 0xfb21, _) \ V(I31GetS, 0xfb21, _) \
V(I31GetU, 0xfb22, _) \ V(I31GetU, 0xfb22, _) \
...@@ -674,6 +679,10 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig, ...@@ -674,6 +679,10 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig,
V(RefCast, 0xfb41, _) \ V(RefCast, 0xfb41, _) \
V(BrOnCast, 0xfb42, _) \ V(BrOnCast, 0xfb42, _) \
V(BrOnCastFail, 0xfb43, _) \ V(BrOnCastFail, 0xfb43, _) \
V(RefTestStatic, 0xfb44, _) \
V(RefCastStatic, 0xfb45, _) \
V(BrOnCastStatic, 0xfb46, _) \
V(BrOnCastStaticFail, 0xfb47, _) \
V(RefIsFunc, 0xfb50, _) \ V(RefIsFunc, 0xfb50, _) \
V(RefIsData, 0xfb51, _) \ V(RefIsData, 0xfb51, _) \
V(RefIsI31, 0xfb52, _) \ V(RefIsI31, 0xfb52, _) \
......
...@@ -223,6 +223,8 @@ V8_INLINE bool EquivalentIndices(uint32_t index1, uint32_t index2, ...@@ -223,6 +223,8 @@ V8_INLINE bool EquivalentIndices(uint32_t index1, uint32_t index2,
} }
} }
} // namespace
bool StructIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index, bool StructIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
const WasmModule* sub_module, const WasmModule* sub_module,
const WasmModule* super_module) { const WasmModule* super_module) {
...@@ -234,8 +236,10 @@ bool StructIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index, ...@@ -234,8 +236,10 @@ bool StructIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
return false; return false;
} }
TypeJudgementCache::instance()->cache_subtype(subtype_index, supertype_index, if (!sub_module->has_supertype(subtype_index)) {
sub_module, super_module); TypeJudgementCache::instance()->cache_subtype(
subtype_index, supertype_index, sub_module, super_module);
}
for (uint32_t i = 0; i < super_struct->field_count(); i++) { for (uint32_t i = 0; i < super_struct->field_count(); i++) {
bool sub_mut = sub_struct->mutability(i); bool sub_mut = sub_struct->mutability(i);
bool super_mut = super_struct->mutability(i); bool super_mut = super_struct->mutability(i);
...@@ -261,8 +265,10 @@ bool ArrayIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index, ...@@ -261,8 +265,10 @@ bool ArrayIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
super_module->types[supertype_index].array_type; super_module->types[supertype_index].array_type;
bool sub_mut = sub_array->mutability(); bool sub_mut = sub_array->mutability();
bool super_mut = super_array->mutability(); bool super_mut = super_array->mutability();
TypeJudgementCache::instance()->cache_subtype(subtype_index, supertype_index, if (!sub_module->has_supertype(subtype_index)) {
sub_module, super_module); TypeJudgementCache::instance()->cache_subtype(
subtype_index, supertype_index, sub_module, super_module);
}
if (sub_mut != super_mut || if (sub_mut != super_mut ||
(sub_mut && (sub_mut &&
!EquivalentTypes(sub_array->element_type(), super_array->element_type(), !EquivalentTypes(sub_array->element_type(), super_array->element_type(),
...@@ -294,8 +300,10 @@ bool FunctionIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index, ...@@ -294,8 +300,10 @@ bool FunctionIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
return false; return false;
} }
TypeJudgementCache::instance()->cache_subtype(subtype_index, supertype_index, if (!sub_module->has_supertype(subtype_index)) {
sub_module, super_module); TypeJudgementCache::instance()->cache_subtype(
subtype_index, supertype_index, sub_module, super_module);
}
for (uint32_t i = 0; i < sub_func->parameter_count(); i++) { for (uint32_t i = 0; i < sub_func->parameter_count(); i++) {
// Contravariance for params. // Contravariance for params.
...@@ -318,7 +326,6 @@ bool FunctionIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index, ...@@ -318,7 +326,6 @@ bool FunctionIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
return true; return true;
} }
} // namespace
V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl( V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
ValueType subtype, ValueType supertype, const WasmModule* sub_module, ValueType subtype, ValueType supertype, const WasmModule* sub_module,
...@@ -410,11 +417,35 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl( ...@@ -410,11 +417,35 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
DCHECK(super_heap.is_index()); DCHECK(super_heap.is_index());
uint32_t super_index = super_heap.ref_index(); uint32_t super_index = super_heap.ref_index();
DCHECK(super_module->has_type(super_index)); DCHECK(super_module->has_type(super_index));
// The {IsSubtypeOf} entry point already has a fast path checking ValueType
// equality; here we catch (ref $x) being a subtype of (ref null $x).
if (sub_module == super_module && sub_index == super_index) return true;
uint8_t sub_kind = sub_module->type_kinds[sub_index]; uint8_t sub_kind = sub_module->type_kinds[sub_index];
if (sub_kind != super_module->type_kinds[super_index]) return false; if (sub_kind != super_module->type_kinds[super_index]) return false;
// Types with explicit supertypes just check those.
if (sub_module->has_supertype(sub_index)) {
// TODO(7748): Figure out cross-module story.
if (sub_module != super_module) return false;
uint32_t explicit_super = sub_module->supertype(sub_index);
while (true) {
if (explicit_super == super_index) return true;
// Reached the end of the explicitly defined inheritance chain.
if (explicit_super == kGenericSuperType) return false;
// Types without explicit supertype can't occur here, they would have
// failed validation.
DCHECK_NE(explicit_super, kNoSuperType);
explicit_super = sub_module->supertype(explicit_super);
}
} else {
// A structural type (without explicit supertype) is never a subtype of
// a nominal type (with explicit supertype).
if (super_module->has_supertype(super_index)) return false;
}
// Accessing the caches for subtyping and equivalence from multiple background // Accessing the caches for subtyping and equivalence from multiple background
// threads is protected by a lock. // threads is protected by a lock.
base::RecursiveMutexGuard type_cache_access( base::RecursiveMutexGuard type_cache_access(
......
...@@ -97,6 +97,20 @@ V8_INLINE bool IsHeapSubtypeOf(uint32_t subtype_index, uint32_t supertype_index, ...@@ -97,6 +97,20 @@ V8_INLINE bool IsHeapSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
// case another WasmModule gets allocated in the same address later. // case another WasmModule gets allocated in the same address later.
void DeleteCachedTypeJudgementsForModule(const WasmModule* module); void DeleteCachedTypeJudgementsForModule(const WasmModule* module);
// Checks whether {subtype_index} is a legal subtype of {supertype_index}.
// These are the same checks that {IsSubtypeOf} uses for comparing types without
// explicitly given supertypes; for validating such explicit supertypes they
// can be called directly.
bool StructIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
const WasmModule* sub_module,
const WasmModule* super_module);
bool ArrayIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
const WasmModule* sub_module,
const WasmModule* super_module);
bool FunctionIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
const WasmModule* sub_module,
const WasmModule* super_module);
} // namespace wasm } // namespace wasm
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
This diff is collapsed.
...@@ -132,7 +132,7 @@ class TestingModuleBuilder { ...@@ -132,7 +132,7 @@ class TestingModuleBuilder {
byte AddSignature(const FunctionSig* sig) { byte AddSignature(const FunctionSig* sig) {
DCHECK_EQ(test_module_->types.size(), DCHECK_EQ(test_module_->types.size(),
test_module_->canonicalized_type_ids.size()); test_module_->canonicalized_type_ids.size());
test_module_->add_signature(sig); test_module_->add_signature(sig, kNoSuperType);
size_t size = test_module_->types.size(); size_t size = test_module_->types.size();
CHECK_GT(127, size); CHECK_GT(127, size);
return static_cast<byte>(size - 1); return static_cast<byte>(size - 1);
......
...@@ -491,10 +491,14 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) { ...@@ -491,10 +491,14 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
// Heap-allocated object operations. // Heap-allocated object operations.
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#define WASM_GC_OP(op) kGCPrefix, static_cast<byte>(op) #define WASM_GC_OP(op) kGCPrefix, static_cast<byte>(op)
#define WASM_STRUCT_NEW(index, ...) \
__VA_ARGS__, WASM_GC_OP(kExprStructNew), static_cast<byte>(index)
#define WASM_STRUCT_NEW_WITH_RTT(index, ...) \ #define WASM_STRUCT_NEW_WITH_RTT(index, ...) \
__VA_ARGS__, WASM_GC_OP(kExprStructNewWithRtt), static_cast<byte>(index) __VA_ARGS__, WASM_GC_OP(kExprStructNewWithRtt), static_cast<byte>(index)
#define WASM_STRUCT_NEW_DEFAULT(index, rtt) \ #define WASM_STRUCT_NEW_DEFAULT(index) \
rtt, WASM_GC_OP(kExprStructNewDefault), static_cast<byte>(index) WASM_GC_OP(kExprStructNewDefault), static_cast<byte>(index)
#define WASM_STRUCT_NEW_DEFAULT_WITH_RTT(index, rtt) \
rtt, WASM_GC_OP(kExprStructNewDefaultWithRtt), static_cast<byte>(index)
#define WASM_STRUCT_GET(typeidx, fieldidx, struct_obj) \ #define WASM_STRUCT_GET(typeidx, fieldidx, struct_obj) \
struct_obj, WASM_GC_OP(kExprStructGet), static_cast<byte>(typeidx), \ struct_obj, WASM_GC_OP(kExprStructGet), static_cast<byte>(typeidx), \
static_cast<byte>(fieldidx) static_cast<byte>(fieldidx)
...@@ -513,13 +517,23 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) { ...@@ -513,13 +517,23 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_REF_AS_NON_NULL(val) val, kExprRefAsNonNull #define WASM_REF_AS_NON_NULL(val) val, kExprRefAsNonNull
#define WASM_REF_EQ(lhs, rhs) lhs, rhs, kExprRefEq #define WASM_REF_EQ(lhs, rhs) lhs, rhs, kExprRefEq
#define WASM_REF_TEST(ref, rtt) ref, rtt, WASM_GC_OP(kExprRefTest) #define WASM_REF_TEST(ref, rtt) ref, rtt, WASM_GC_OP(kExprRefTest)
#define WASM_REF_TEST_STATIC(ref, typeidx) \
ref, WASM_GC_OP(kExprRefTestStatic), static_cast<byte>(typeidx)
#define WASM_REF_CAST(ref, rtt) ref, rtt, WASM_GC_OP(kExprRefCast) #define WASM_REF_CAST(ref, rtt) ref, rtt, WASM_GC_OP(kExprRefCast)
#define WASM_REF_CAST_STATIC(ref, typeidx) \
ref, WASM_GC_OP(kExprRefCastStatic), static_cast<byte>(typeidx)
// Takes a reference value from the value stack to allow sequences of // Takes a reference value from the value stack to allow sequences of
// conditional branches. // conditional branches.
#define WASM_BR_ON_CAST(depth, rtt) \ #define WASM_BR_ON_CAST(depth, rtt) \
rtt, WASM_GC_OP(kExprBrOnCast), static_cast<byte>(depth) rtt, WASM_GC_OP(kExprBrOnCast), static_cast<byte>(depth)
#define WASM_BR_ON_CAST_STATIC(depth, typeidx) \
WASM_GC_OP(kExprBrOnCastStatic), static_cast<byte>(depth), \
static_cast<byte>(typeidx)
#define WASM_BR_ON_CAST_FAIL(depth, rtt) \ #define WASM_BR_ON_CAST_FAIL(depth, rtt) \
rtt, WASM_GC_OP(kExprBrOnCastFail), static_cast<byte>(depth) rtt, WASM_GC_OP(kExprBrOnCastFail), static_cast<byte>(depth)
#define WASM_BR_ON_CAST_STATIC_FAIL(depth, typeidx) \
WASM_GC_OP(kExprBrOnCastStaticFail), static_cast<byte>(depth), \
static_cast<byte>(typeidx)
#define WASM_REF_IS_FUNC(ref) ref, WASM_GC_OP(kExprRefIsFunc) #define WASM_REF_IS_FUNC(ref) ref, WASM_GC_OP(kExprRefIsFunc)
#define WASM_REF_IS_DATA(ref) ref, WASM_GC_OP(kExprRefIsData) #define WASM_REF_IS_DATA(ref) ref, WASM_GC_OP(kExprRefIsData)
...@@ -539,11 +553,15 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) { ...@@ -539,11 +553,15 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_BR_ON_NON_I31(depth) \ #define WASM_BR_ON_NON_I31(depth) \
WASM_GC_OP(kExprBrOnNonI31), static_cast<byte>(depth) WASM_GC_OP(kExprBrOnNonI31), static_cast<byte>(depth)
#define WASM_ARRAY_NEW(index, default_value, length) \
default_value, length, WASM_GC_OP(kExprArrayNew), static_cast<byte>(index)
#define WASM_ARRAY_NEW_WITH_RTT(index, default_value, length, rtt) \ #define WASM_ARRAY_NEW_WITH_RTT(index, default_value, length, rtt) \
default_value, length, rtt, WASM_GC_OP(kExprArrayNewWithRtt), \ default_value, length, rtt, WASM_GC_OP(kExprArrayNewWithRtt), \
static_cast<byte>(index) static_cast<byte>(index)
#define WASM_ARRAY_NEW_DEFAULT(index, length, rtt) \ #define WASM_ARRAY_NEW_DEFAULT(index, length) \
length, rtt, WASM_GC_OP(kExprArrayNewDefault), static_cast<byte>(index) length, WASM_GC_OP(kExprArrayNewDefault), static_cast<byte>(index)
#define WASM_ARRAY_NEW_DEFAULT_WITH_RTT(index, length, rtt) \
length, rtt, WASM_GC_OP(kExprArrayNewDefaultWithRtt), static_cast<byte>(index)
#define WASM_ARRAY_GET(typeidx, array, index) \ #define WASM_ARRAY_GET(typeidx, array, index) \
array, index, WASM_GC_OP(kExprArrayGet), static_cast<byte>(typeidx) array, index, WASM_GC_OP(kExprArrayGet), static_cast<byte>(typeidx)
#define WASM_ARRAY_GET_U(typeidx, array, index) \ #define WASM_ARRAY_GET_U(typeidx, array, index) \
...@@ -562,6 +580,9 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) { ...@@ -562,6 +580,9 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_ARRAY_INIT(index, length, ...) \ #define WASM_ARRAY_INIT(index, length, ...) \
__VA_ARGS__, WASM_GC_OP(kExprArrayInit), static_cast<byte>(index), \ __VA_ARGS__, WASM_GC_OP(kExprArrayInit), static_cast<byte>(index), \
static_cast<byte>(length) static_cast<byte>(length)
#define WASM_ARRAY_INIT_STATIC(index, length, ...) \
__VA_ARGS__, WASM_GC_OP(kExprArrayInitStatic), static_cast<byte>(index), \
static_cast<byte>(length)
#define WASM_RTT_WITH_DEPTH(depth, typeidx) \ #define WASM_RTT_WITH_DEPTH(depth, typeidx) \
kRttWithDepthCode, U32V_1(depth), U32V_1(typeidx) kRttWithDepthCode, U32V_1(depth), U32V_1(typeidx)
......
...@@ -810,7 +810,7 @@ class WasmGenerator { ...@@ -810,7 +810,7 @@ class WasmGenerator {
if (new_default) { if (new_default) {
builder_->EmitWithPrefix(kExprRttCanon); builder_->EmitWithPrefix(kExprRttCanon);
builder_->EmitU32V(index); builder_->EmitU32V(index);
builder_->EmitWithPrefix(kExprStructNewDefault); builder_->EmitWithPrefix(kExprStructNewDefaultWithRtt);
builder_->EmitU32V(index); builder_->EmitU32V(index);
} else { } else {
StructType* struct_gen = builder_->builder()->GetStructType(index); StructType* struct_gen = builder_->builder()->GetStructType(index);
...@@ -828,7 +828,7 @@ class WasmGenerator { ...@@ -828,7 +828,7 @@ class WasmGenerator {
Generate(kWasmI32, data); Generate(kWasmI32, data);
builder_->EmitWithPrefix(kExprRttCanon); builder_->EmitWithPrefix(kExprRttCanon);
builder_->EmitU32V(index); builder_->EmitU32V(index);
builder_->EmitWithPrefix(kExprArrayNewDefault); builder_->EmitWithPrefix(kExprArrayNewDefaultWithRtt);
builder_->EmitU32V(index); builder_->EmitU32V(index);
} else { } else {
Generate(builder_->builder()->GetArrayType(index)->element_type(), Generate(builder_->builder()->GetArrayType(index)->element_type(),
......
...@@ -14,7 +14,7 @@ builder.addFunction("main", kSig_i_i) ...@@ -14,7 +14,7 @@ builder.addFunction("main", kSig_i_i)
.addBody([ .addBody([
kExprLocalGet, 0, kExprLocalGet, 0,
kGCPrefix, kExprRttCanon, array_index, kGCPrefix, kExprRttCanon, array_index,
kGCPrefix, kExprArrayNewDefault, array_index, kGCPrefix, kExprArrayNewDefaultWithRtt, array_index,
kGCPrefix, kExprArrayLen, array_index, kGCPrefix, kExprArrayLen, array_index,
]) ])
.exportFunc(); .exportFunc();
......
...@@ -32,11 +32,11 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); ...@@ -32,11 +32,11 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
.addBody([ .addBody([
...wasmI32Const(array_length), ...wasmI32Const(array_length),
kGCPrefix, kExprRttCanon, array_index, kGCPrefix, kExprRttCanon, array_index,
kGCPrefix, kExprArrayNewDefault, array_index, kGCPrefix, kExprArrayNewDefaultWithRtt, array_index,
kExprGlobalSet, from.index, kExprGlobalSet, from.index,
...wasmI32Const(array_length), ...wasmI32Const(array_length),
kGCPrefix, kExprRttCanon, array_index, kGCPrefix, kExprRttCanon, array_index,
kGCPrefix, kExprArrayNewDefault, array_index, kGCPrefix, kExprArrayNewDefaultWithRtt, array_index,
kExprGlobalSet, to.index kExprGlobalSet, to.index
]) ])
.exportFunc(); .exportFunc();
......
...@@ -6,26 +6,49 @@ ...@@ -6,26 +6,49 @@
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
var builder = new WasmModuleBuilder(); (function() {
let struct1 = builder.addStruct([makeField(kWasmI32, true)]); var builder = new WasmModuleBuilder();
let struct2 = builder.addStructExtending( let struct1 = builder.addStructSubtype([makeField(kWasmI32, true)]);
[makeField(kWasmI32, true), makeField(kWasmI32, true)], struct1); let struct2 = builder.addStructSubtype(
[makeField(kWasmI32, true), makeField(kWasmI32, true)], struct1);
let array1 = builder.addArray(kWasmI32, true); let array1 = builder.addArraySubtype(kWasmI32, true);
let array2 = builder.addArrayExtending(kWasmI32, true, array1); let array2 = builder.addArraySubtype(kWasmI32, true, array1);
builder.addFunction("main", kSig_v_v) builder.addFunction("main", kSig_v_v)
.addLocals(wasmOptRefType(struct1), 1) .addLocals(wasmOptRefType(struct1), 1)
.addLocals(wasmOptRefType(array1), 1) .addLocals(wasmOptRefType(array1), 1)
.addBody([ .addBody([
kGCPrefix, kExprRttCanon, struct2, // Check that we can create a struct with explicit RTT...
kGCPrefix, kExprStructNewDefault, struct2, kGCPrefix, kExprRttCanon, struct2, kGCPrefix,
kExprStructNewDefaultWithRtt, struct2,
// ...and upcast it.
kExprLocalSet, 0, kExprLocalSet, 0,
// Check that we can create a struct with implicit RTT.
kGCPrefix, kExprStructNewDefault, struct2, kExprLocalSet, 0,
// Check that we can create an array with explicit RTT...
kExprI32Const, 10, // length kExprI32Const, 10, // length
kGCPrefix, kExprRttCanon, array2, kGCPrefix, kExprRttCanon, array2, kGCPrefix,
kGCPrefix, kExprArrayNewDefault, array2, kExprArrayNewDefaultWithRtt, array2,
kExprLocalSet, 1 // ...and upcast it.
]); kExprLocalSet, 1,
// Check that we can create an array with implicit RTT.
kExprI32Const, 10, // length
kGCPrefix, kExprArrayNewDefault, array2, kExprLocalSet, 1
])
.exportFunc();
// This test is only interested in type checking.
builder.instantiate();
})();
// This test is only interested in type checking. (function () {
builder.instantiate(); let builder = new WasmModuleBuilder();
let t0 = builder.addStructSubtype([]);
for (let i = 0; i < 32; i++) {
builder.addStructSubtype([], i);
}
assertThrows(
() => builder.instantiate(), WebAssembly.CompileError,
/subtyping depth is greater than allowed/);
})();
...@@ -182,7 +182,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); ...@@ -182,7 +182,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
.addBody([ .addBody([
kExprI32Const, 5, kExprI32Const, 5,
kGCPrefix, kExprRttCanon, array, kGCPrefix, kExprRttCanon, array,
kGCPrefix, kExprArrayNewDefault, array, kGCPrefix, kExprArrayNewDefaultWithRtt, array,
kExprLocalSet, 1, kExprLocalSet, 1,
kExprLocalGet, 1, // a[i] = i for i = {0..4} kExprLocalGet, 1, // a[i] = i for i = {0..4}
...@@ -308,7 +308,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); ...@@ -308,7 +308,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
builder.addFunction("main", kSig_i_i) builder.addFunction("main", kSig_i_i)
.addBody([ .addBody([
kExprI32Const, 10, kGCPrefix, kExprRttCanon, array, kExprI32Const, 10, kGCPrefix, kExprRttCanon, array,
kGCPrefix, kExprArrayNewDefault, array, kGCPrefix, kExprArrayNewDefaultWithRtt, array,
kExprI32Const, 7, kExprI32Const, 7,
kExprCallFunction, tester.index, kExprCallFunction, tester.index,
]) ])
......
...@@ -18,15 +18,15 @@ let instance = (() => { ...@@ -18,15 +18,15 @@ let instance = (() => {
builder.addFunction('struct_producer', makeSig([], [kWasmDataRef])) builder.addFunction('struct_producer', makeSig([], [kWasmDataRef]))
.addBody([ .addBody([
kGCPrefix, kExprRttCanon, struct, kGCPrefix, kExprStructNewDefault, kGCPrefix, kExprRttCanon, struct, kGCPrefix,
struct kExprStructNewDefaultWithRtt, struct
]) ])
.exportFunc(); .exportFunc();
builder.addFunction('array_producer', makeSig([], [kWasmDataRef])) builder.addFunction('array_producer', makeSig([], [kWasmDataRef]))
.addBody([ .addBody([
kExprI32Const, 10, kGCPrefix, kExprRttCanon, array, kGCPrefix, kExprI32Const, 10, kGCPrefix, kExprRttCanon, array, kGCPrefix,
kExprArrayNewDefault, array kExprArrayNewDefaultWithRtt, array
]) ])
.exportFunc(); .exportFunc();
......
...@@ -77,9 +77,9 @@ let kLocalNamesCode = 2; ...@@ -77,9 +77,9 @@ let kLocalNamesCode = 2;
let kWasmFunctionTypeForm = 0x60; let kWasmFunctionTypeForm = 0x60;
let kWasmStructTypeForm = 0x5f; let kWasmStructTypeForm = 0x5f;
let kWasmArrayTypeForm = 0x5e; let kWasmArrayTypeForm = 0x5e;
let kWasmFunctionExtendingTypeForm = 0x5d; let kWasmFunctionSubtypeForm = 0x5d;
let kWasmStructExtendingTypeForm = 0x5c; let kWasmStructSubtypeForm = 0x5c;
let kWasmArrayExtendingTypeForm = 0x5b; let kWasmArraySubtypeForm = 0x5b;
let kLimitsNoMaximum = 0x00; let kLimitsNoMaximum = 0x00;
let kLimitsWithMaximum = 0x01; let kLimitsWithMaximum = 0x01;
...@@ -469,13 +469,15 @@ for (let prefix in kPrefixOpcodes) { ...@@ -469,13 +469,15 @@ for (let prefix in kPrefixOpcodes) {
// GC opcodes // GC opcodes
let kExprStructNewWithRtt = 0x01; let kExprStructNewWithRtt = 0x01;
let kExprStructNewDefault = 0x02; let kExprStructNewDefaultWithRtt = 0x02;
let kExprStructGet = 0x03; let kExprStructGet = 0x03;
let kExprStructGetS = 0x04; let kExprStructGetS = 0x04;
let kExprStructGetU = 0x05; let kExprStructGetU = 0x05;
let kExprStructSet = 0x06; let kExprStructSet = 0x06;
let kExprStructNew = 0x07;
let kExprStructNewDefault = 0x08;
let kExprArrayNewWithRtt = 0x11; let kExprArrayNewWithRtt = 0x11;
let kExprArrayNewDefault = 0x12; let kExprArrayNewDefaultWithRtt = 0x12;
let kExprArrayGet = 0x13; let kExprArrayGet = 0x13;
let kExprArrayGetS = 0x14; let kExprArrayGetS = 0x14;
let kExprArrayGetU = 0x15; let kExprArrayGetU = 0x15;
...@@ -483,6 +485,9 @@ let kExprArraySet = 0x16; ...@@ -483,6 +485,9 @@ let kExprArraySet = 0x16;
let kExprArrayLen = 0x17; let kExprArrayLen = 0x17;
let kExprArrayCopy = 0x18; let kExprArrayCopy = 0x18;
let kExprArrayInit = 0x19; let kExprArrayInit = 0x19;
let kExprArrayInitStatic = 0x1a;
let kExprArrayNew = 0x1b;
let kExprArrayNewDefault = 0x1c;
let kExprI31New = 0x20; let kExprI31New = 0x20;
let kExprI31GetS = 0x21; let kExprI31GetS = 0x21;
let kExprI31GetU = 0x22; let kExprI31GetU = 0x22;
...@@ -493,6 +498,10 @@ let kExprRefTest = 0x40; ...@@ -493,6 +498,10 @@ let kExprRefTest = 0x40;
let kExprRefCast = 0x41; let kExprRefCast = 0x41;
let kExprBrOnCast = 0x42; let kExprBrOnCast = 0x42;
let kExprBrOnCastFail = 0x43; let kExprBrOnCastFail = 0x43;
let kExprRefTestStatic = 0x44;
let kExprRefCastStatic = 0x45;
let kExprBrOnCastStatic = 0x46;
let kExprBrOnCastStaticFail = 0x47;
let kExprRefIsFunc = 0x50; let kExprRefIsFunc = 0x50;
let kExprRefIsData = 0x51; let kExprRefIsData = 0x51;
let kExprRefIsI31 = 0x52; let kExprRefIsI31 = 0x52;
...@@ -1004,20 +1013,22 @@ class Binary { ...@@ -1004,20 +1013,22 @@ class Binary {
this.emit_u8(kExprRefNull); this.emit_u8(kExprRefNull);
this.emit_heap_type(expr.value); this.emit_heap_type(expr.value);
break; break;
case kExprStructNew:
case kExprStructNewWithRtt: case kExprStructNewWithRtt:
for (let operand of expr.operands) { for (let operand of expr.operands) {
this.emit_init_expr_recursive(operand); this.emit_init_expr_recursive(operand);
} }
this.emit_u8(kGCPrefix); this.emit_u8(kGCPrefix);
this.emit_u8(kExprStructNewWithRtt); this.emit_u8(expr.kind);
this.emit_u32v(expr.value); this.emit_u32v(expr.value);
break; break;
case kExprArrayInit: case kExprArrayInit:
case kExprArrayInitStatic:
for (let operand of expr.operands) { for (let operand of expr.operands) {
this.emit_init_expr_recursive(operand); this.emit_init_expr_recursive(operand);
} }
this.emit_u8(kGCPrefix); this.emit_u8(kGCPrefix);
this.emit_u8(kExprArrayInit); this.emit_u8(expr.kind);
this.emit_u32v(expr.value); this.emit_u32v(expr.value);
this.emit_u32v(expr.operands.length - 1); this.emit_u32v(expr.operands.length - 1);
break; break;
...@@ -1170,9 +1181,15 @@ class WasmInitExpr { ...@@ -1170,9 +1181,15 @@ class WasmInitExpr {
static StructNewWithRtt(type, args) { static StructNewWithRtt(type, args) {
return {kind: kExprStructNewWithRtt, value: type, operands: args}; return {kind: kExprStructNewWithRtt, value: type, operands: args};
} }
static StructNew(type, args) {
return {kind: kExprStructNew, value: type, operands: args};
}
static ArrayInit(type, args) { static ArrayInit(type, args) {
return {kind: kExprArrayInit, value: type, operands: args}; return {kind: kExprArrayInit, value: type, operands: args};
} }
static ArrayInitStatic(type, args) {
return {kind: kExprArrayInitStatic, value: type, operands: args};
}
static RttCanon(type) { static RttCanon(type) {
return {kind: kExprRttCanon, value: type}; return {kind: kExprRttCanon, value: type};
} }
...@@ -1256,11 +1273,11 @@ class WasmStruct { ...@@ -1256,11 +1273,11 @@ class WasmStruct {
} }
} }
class WasmStructExtending extends WasmStruct { class WasmStructSubtype extends WasmStruct {
constructor(fields, supertype_idx) { constructor(fields, supertype_idx) {
super(fields); super(fields);
this.supertype = supertype_idx; this.supertype = supertype_idx;
this.type_form = kWasmStructExtendingTypeForm; this.type_form = kWasmStructSubtypeForm;
} }
} }
...@@ -1273,11 +1290,11 @@ class WasmArray { ...@@ -1273,11 +1290,11 @@ class WasmArray {
} }
} }
class WasmArrayExtending extends WasmArray { class WasmArraySubtype extends WasmArray {
constructor(type, mutability, supertype_idx) { constructor(type, mutability, supertype_idx) {
super(type, mutability); super(type, mutability);
this.supertype = supertype_idx; this.supertype = supertype_idx;
this.type_form = kWasmArrayExtendingTypeForm; this.type_form = kWasmArraySubtypeForm;
} }
} }
class WasmElemSegment { class WasmElemSegment {
...@@ -1402,8 +1419,9 @@ class WasmModuleBuilder { ...@@ -1402,8 +1419,9 @@ class WasmModuleBuilder {
return this.types.length - 1; return this.types.length - 1;
} }
addStructExtending(fields, supertype_idx) { kGenericSuperType = 0xFFFFFFFE;
this.types.push(new WasmStructExtending(fields, supertype_idx)); addStructSubtype(fields, supertype_idx = this.kGenericSuperType) {
this.types.push(new WasmStructSubtype(fields, supertype_idx));
return this.types.length - 1; return this.types.length - 1;
} }
...@@ -1412,8 +1430,8 @@ class WasmModuleBuilder { ...@@ -1412,8 +1430,8 @@ class WasmModuleBuilder {
return this.types.length - 1; return this.types.length - 1;
} }
addArrayExtending(type, mutability, supertype_idx) { addArraySubtype(type, mutability, supertype_idx = this.kGenericSuperType) {
this.types.push(new WasmArrayExtending(type, mutability, supertype_idx)); this.types.push(new WasmArraySubtype(type, mutability, supertype_idx));
return this.types.length - 1; return this.types.length - 1;
} }
...@@ -1651,15 +1669,23 @@ class WasmModuleBuilder { ...@@ -1651,15 +1669,23 @@ class WasmModuleBuilder {
section.emit_type(field.type); section.emit_type(field.type);
section.emit_u8(field.mutability ? 1 : 0); section.emit_u8(field.mutability ? 1 : 0);
} }
if (type instanceof WasmStructExtending) { if (type instanceof WasmStructSubtype) {
section.emit_u32v(type.supertype); if (type.supertype === this.kGenericSuperType) {
section.emit_u8(kDataRefCode);
} else {
section.emit_heap_type(type.supertype);
}
} }
} else if (type instanceof WasmArray) { } else if (type instanceof WasmArray) {
section.emit_u8(type.type_form); section.emit_u8(type.type_form);
section.emit_type(type.type); section.emit_type(type.type);
section.emit_u8(type.mutability ? 1 : 0); section.emit_u8(type.mutability ? 1 : 0);
if (type instanceof WasmArrayExtending) { if (type instanceof WasmArraySubtype) {
section.emit_u32v(type.supertype); if (type.supertype === this.kGenericSuperType) {
section.emit_u8(kDataRefCode);
} else {
section.emit_heap_type(type.supertype);
}
} }
} else { } else {
section.emit_u8(kWasmFunctionTypeForm); section.emit_u8(kWasmFunctionTypeForm);
......
...@@ -87,7 +87,7 @@ class TestModuleBuilder { ...@@ -87,7 +87,7 @@ class TestModuleBuilder {
return static_cast<byte>(mod.globals.size() - 1); return static_cast<byte>(mod.globals.size() - 1);
} }
byte AddSignature(const FunctionSig* sig) { byte AddSignature(const FunctionSig* sig) {
mod.add_signature(sig); mod.add_signature(sig, kNoSuperType);
CHECK_LE(mod.types.size(), kMaxByteSizedLeb128); CHECK_LE(mod.types.size(), kMaxByteSizedLeb128);
return static_cast<byte>(mod.types.size() - 1); return static_cast<byte>(mod.types.size() - 1);
} }
...@@ -127,19 +127,20 @@ class TestModuleBuilder { ...@@ -127,19 +127,20 @@ class TestModuleBuilder {
return static_cast<byte>(mod.tables.size() - 1); return static_cast<byte>(mod.tables.size() - 1);
} }
byte AddStruct(std::initializer_list<F> fields) { byte AddStruct(std::initializer_list<F> fields,
uint32_t supertype = kNoSuperType) {
StructType::Builder type_builder(mod.signature_zone.get(), StructType::Builder type_builder(mod.signature_zone.get(),
static_cast<uint32_t>(fields.size())); static_cast<uint32_t>(fields.size()));
for (F field : fields) { for (F field : fields) {
type_builder.AddField(field.first, field.second); type_builder.AddField(field.first, field.second);
} }
mod.add_struct_type(type_builder.Build()); mod.add_struct_type(type_builder.Build(), supertype);
return static_cast<byte>(mod.type_kinds.size() - 1); return static_cast<byte>(mod.type_kinds.size() - 1);
} }
byte AddArray(ValueType type, bool mutability) { byte AddArray(ValueType type, bool mutability) {
ArrayType* array = mod.signature_zone->New<ArrayType>(type, mutability); ArrayType* array = mod.signature_zone->New<ArrayType>(type, mutability);
mod.add_array_type(array); mod.add_array_type(array, kNoSuperType);
return static_cast<byte>(mod.type_kinds.size() - 1); return static_cast<byte>(mod.type_kinds.size() - 1);
} }
...@@ -1123,11 +1124,12 @@ TEST_F(FunctionBodyDecoderTest, UnreachableRefTypes) { ...@@ -1123,11 +1124,12 @@ TEST_F(FunctionBodyDecoderTest, UnreachableRefTypes) {
WASM_GC_OP(kExprStructNewWithRtt), struct_index, WASM_GC_OP(kExprStructNewWithRtt), struct_index,
kExprCallFunction, struct_consumer}); kExprCallFunction, struct_consumer});
ExpectValidates(sigs.v_v(), ExpectValidates(sigs.v_v(),
{WASM_UNREACHABLE, WASM_GC_OP(kExprStructNewDefault), {WASM_UNREACHABLE, WASM_GC_OP(kExprStructNewDefaultWithRtt),
struct_index, kExprDrop}); struct_index, kExprDrop});
ExpectValidates(sigs.v_v(), {WASM_UNREACHABLE, WASM_RTT_CANON(struct_index), ExpectValidates(sigs.v_v(),
WASM_GC_OP(kExprStructNewDefault), struct_index, {WASM_UNREACHABLE, WASM_RTT_CANON(struct_index),
kExprCallFunction, struct_consumer}); WASM_GC_OP(kExprStructNewDefaultWithRtt), struct_index,
kExprCallFunction, struct_consumer});
ExpectValidates(sigs.v_v(), ExpectValidates(sigs.v_v(),
{WASM_UNREACHABLE, WASM_GC_OP(kExprArrayNewWithRtt), {WASM_UNREACHABLE, WASM_GC_OP(kExprArrayNewWithRtt),
...@@ -1139,11 +1141,11 @@ TEST_F(FunctionBodyDecoderTest, UnreachableRefTypes) { ...@@ -1139,11 +1141,11 @@ TEST_F(FunctionBodyDecoderTest, UnreachableRefTypes) {
{WASM_UNREACHABLE, WASM_I32V(42), WASM_RTT_CANON(array_index), {WASM_UNREACHABLE, WASM_I32V(42), WASM_RTT_CANON(array_index),
WASM_GC_OP(kExprArrayNewWithRtt), array_index, kExprDrop}); WASM_GC_OP(kExprArrayNewWithRtt), array_index, kExprDrop});
ExpectValidates(sigs.v_v(), ExpectValidates(sigs.v_v(),
{WASM_UNREACHABLE, WASM_GC_OP(kExprArrayNewDefault), {WASM_UNREACHABLE, WASM_GC_OP(kExprArrayNewDefaultWithRtt),
array_index, kExprDrop}); array_index, kExprDrop});
ExpectValidates(sigs.v_v(), ExpectValidates(sigs.v_v(), {WASM_UNREACHABLE, WASM_RTT_CANON(array_index),
{WASM_UNREACHABLE, WASM_RTT_CANON(array_index), WASM_GC_OP(kExprArrayNewDefaultWithRtt),
WASM_GC_OP(kExprArrayNewDefault), array_index, kExprDrop}); array_index, kExprDrop});
ExpectValidates(sigs.i_v(), {WASM_UNREACHABLE, WASM_GC_OP(kExprRefTest), ExpectValidates(sigs.i_v(), {WASM_UNREACHABLE, WASM_GC_OP(kExprRefTest),
struct_index, struct_index}); struct_index, struct_index});
...@@ -1976,10 +1978,10 @@ TEST_F(FunctionBodyDecoderTest, TablesWithFunctionSubtyping) { ...@@ -1976,10 +1978,10 @@ TEST_F(FunctionBodyDecoderTest, TablesWithFunctionSubtyping) {
// that is a subtype of the table type. // that is a subtype of the table type.
ExpectValidates( ExpectValidates(
FunctionSig::Build(zone(), {ValueType::Ref(sub_struct, kNullable)}, {}), FunctionSig::Build(zone(), {ValueType::Ref(sub_struct, kNullable)}, {}),
{WASM_CALL_INDIRECT_TABLE( {WASM_CALL_INDIRECT_TABLE(table, function_type,
table, function_type, WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
WASM_STRUCT_NEW_DEFAULT(super_struct, WASM_RTT_CANON(super_struct)), super_struct, WASM_RTT_CANON(super_struct)),
WASM_ZERO)}); WASM_ZERO)});
// table.set's subtyping works as expected. // table.set's subtyping works as expected.
ExpectValidates(sigs.v_i(), {WASM_TABLE_SET(0, WASM_LOCAL_GET(0), ExpectValidates(sigs.v_i(), {WASM_TABLE_SET(0, WASM_LOCAL_GET(0),
...@@ -2688,13 +2690,13 @@ TEST_F(FunctionBodyDecoderTest, BrTableSubtyping) { ...@@ -2688,13 +2690,13 @@ TEST_F(FunctionBodyDecoderTest, BrTableSubtyping) {
{F(kWasmI8, true), F(kWasmI16, false), F(kWasmI32, true)}); {F(kWasmI8, true), F(kWasmI16, false), F(kWasmI32, true)});
ExpectValidates( ExpectValidates(
sigs.v_v(), sigs.v_v(),
{WASM_BLOCK_R( {WASM_BLOCK_R(wasm::ValueType::Ref(supertype1, kNonNullable),
wasm::ValueType::Ref(supertype1, kNonNullable), WASM_BLOCK_R(wasm::ValueType::Ref(supertype2, kNonNullable),
WASM_BLOCK_R( WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
wasm::ValueType::Ref(supertype2, kNonNullable), subtype, WASM_RTT_CANON(subtype)),
WASM_STRUCT_NEW_DEFAULT(subtype, WASM_RTT_CANON(subtype)), WASM_BR_TABLE(WASM_I32V(5), 1, BR_TARGET(0),
WASM_BR_TABLE(WASM_I32V(5), 1, BR_TARGET(0), BR_TARGET(1))), BR_TARGET(1))),
WASM_UNREACHABLE), WASM_UNREACHABLE),
WASM_DROP}); WASM_DROP});
} }
...@@ -3630,7 +3632,7 @@ ValueType optref(byte type_index) { ...@@ -3630,7 +3632,7 @@ ValueType optref(byte type_index) {
return ValueType::Ref(type_index, kNullable); return ValueType::Ref(type_index, kNullable);
} }
TEST_F(FunctionBodyDecoderTest, StructNewDefault) { TEST_F(FunctionBodyDecoderTest, StructNewDefaultWithRtt) {
WASM_FEATURE_SCOPE(reftypes); WASM_FEATURE_SCOPE(reftypes);
WASM_FEATURE_SCOPE(typed_funcref); WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(gc); WASM_FEATURE_SCOPE(gc);
...@@ -3639,12 +3641,12 @@ TEST_F(FunctionBodyDecoderTest, StructNewDefault) { ...@@ -3639,12 +3641,12 @@ TEST_F(FunctionBodyDecoderTest, StructNewDefault) {
byte type_index = builder.AddStruct({F(kWasmI32, true)}); byte type_index = builder.AddStruct({F(kWasmI32, true)});
byte bad_type_index = builder.AddStruct({F(ref(type_index), true)}); byte bad_type_index = builder.AddStruct({F(ref(type_index), true)});
module = builder.module(); module = builder.module();
ExpectValidates(sigs.v_v(), {WASM_STRUCT_NEW_DEFAULT( ExpectValidates(sigs.v_v(), {WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
type_index, WASM_RTT_CANON(type_index)), type_index, WASM_RTT_CANON(type_index)),
WASM_DROP}); WASM_DROP});
ExpectFailure(sigs.v_v(), ExpectFailure(sigs.v_v(),
{WASM_STRUCT_NEW_DEFAULT(bad_type_index, {WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
WASM_RTT_CANON(bad_type_index)), bad_type_index, WASM_RTT_CANON(bad_type_index)),
WASM_DROP}); WASM_DROP});
} }
{ {
...@@ -3653,16 +3655,43 @@ TEST_F(FunctionBodyDecoderTest, StructNewDefault) { ...@@ -3653,16 +3655,43 @@ TEST_F(FunctionBodyDecoderTest, StructNewDefault) {
byte bad_type_index = builder.AddArray(ref(type_index), true); byte bad_type_index = builder.AddArray(ref(type_index), true);
module = builder.module(); module = builder.module();
ExpectValidates(sigs.v_v(), ExpectValidates(sigs.v_v(),
{WASM_ARRAY_NEW_DEFAULT(type_index, WASM_I32V(3), {WASM_ARRAY_NEW_DEFAULT_WITH_RTT(
WASM_RTT_CANON(type_index)), type_index, WASM_I32V(3), WASM_RTT_CANON(type_index)),
WASM_DROP}); WASM_DROP});
ExpectFailure(sigs.v_v(), ExpectFailure(sigs.v_v(), {WASM_ARRAY_NEW_DEFAULT_WITH_RTT(
{WASM_ARRAY_NEW_DEFAULT(bad_type_index, WASM_I32V(3), bad_type_index, WASM_I32V(3),
WASM_RTT_CANON(bad_type_index)), WASM_RTT_CANON(bad_type_index)),
WASM_DROP}); WASM_DROP});
} }
} }
TEST_F(FunctionBodyDecoderTest, NominalStructSubtyping) {
WASM_FEATURE_SCOPE(reftypes);
WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(gc);
byte structural_type = builder.AddStruct({F(kWasmI32, true)});
byte nominal_type = builder.AddStruct({F(kWasmI32, true)}, kGenericSuperType);
AddLocals(optref(structural_type), 1);
AddLocals(optref(nominal_type), 1);
// Try to assign a nominally-typed value to a structurally-typed local.
ExpectFailure(sigs.v_v(),
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_DEFAULT(nominal_type))},
kAppendEnd, "expected type (ref null 0)");
// Try to assign a structurally-typed value to a nominally-typed local.
ExpectFailure(sigs.v_v(),
{WASM_LOCAL_SET(
1, WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
structural_type, WASM_RTT_CANON(structural_type)))},
kAppendEnd, "expected type (ref null 1)");
// But assigning to the correctly typed local works.
ExpectValidates(sigs.v_v(),
{WASM_LOCAL_SET(1, WASM_STRUCT_NEW_DEFAULT(nominal_type))});
ExpectValidates(sigs.v_v(),
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
structural_type,
WASM_RTT_CANON(structural_type)))});
}
TEST_F(FunctionBodyDecoderTest, DefaultableLocal) { TEST_F(FunctionBodyDecoderTest, DefaultableLocal) {
WASM_FEATURE_SCOPE(typed_funcref); WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(reftypes); WASM_FEATURE_SCOPE(reftypes);
...@@ -4642,11 +4671,11 @@ TEST_F(FunctionBodyDecoderTest, LocalTeeTyping) { ...@@ -4642,11 +4671,11 @@ TEST_F(FunctionBodyDecoderTest, LocalTeeTyping) {
AddLocals(ValueType::Ref(array_type, kNullable), 1); AddLocals(ValueType::Ref(array_type, kNullable), 1);
ExpectFailure( ExpectFailure(&sig,
&sig, {WASM_LOCAL_TEE(0, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(
{WASM_LOCAL_TEE(0, WASM_ARRAY_NEW_DEFAULT(array_type, WASM_I32V(5), array_type, WASM_I32V(5),
WASM_RTT_CANON(array_type)))}, WASM_RTT_CANON(array_type)))},
kAppendEnd, "expected (ref 0), got (ref null 0)"); kAppendEnd, "expected (ref 0), got (ref null 0)");
} }
TEST_F(FunctionBodyDecoderTest, MergeNullableTypes) { TEST_F(FunctionBodyDecoderTest, MergeNullableTypes) {
...@@ -4664,7 +4693,7 @@ TEST_F(FunctionBodyDecoderTest, MergeNullableTypes) { ...@@ -4664,7 +4693,7 @@ TEST_F(FunctionBodyDecoderTest, MergeNullableTypes) {
// Regression test for crbug.com/1234453. // Regression test for crbug.com/1234453.
ExpectValidates(sigs.v_v(), ExpectValidates(sigs.v_v(),
{WASM_GC_OP(kExprRttCanon), struct_type_index, {WASM_GC_OP(kExprRttCanon), struct_type_index,
WASM_GC_OP(kExprStructNewDefault), struct_type_index, WASM_GC_OP(kExprStructNewDefaultWithRtt), struct_type_index,
WASM_LOOP_X(loop_sig_index, kExprDrop, kExprRefNull, WASM_LOOP_X(loop_sig_index, kExprDrop, kExprRefNull,
struct_type_index, kExprBr, 0)}); struct_type_index, kExprBr, 0)});
} }
......
...@@ -39,6 +39,8 @@ namespace module_decoder_unittest { ...@@ -39,6 +39,8 @@ namespace module_decoder_unittest {
WASM_STRUCT_NEW_WITH_RTT(index, __VA_ARGS__), kExprEnd WASM_STRUCT_NEW_WITH_RTT(index, __VA_ARGS__), kExprEnd
#define WASM_INIT_EXPR_ARRAY_INIT(index, length, ...) \ #define WASM_INIT_EXPR_ARRAY_INIT(index, length, ...) \
WASM_ARRAY_INIT(index, length, __VA_ARGS__), kExprEnd WASM_ARRAY_INIT(index, length, __VA_ARGS__), kExprEnd
#define WASM_INIT_EXPR_ARRAY_INIT_STATIC(index, length, ...) \
WASM_ARRAY_INIT_STATIC(index, length, __VA_ARGS__), kExprEnd
#define WASM_INIT_EXPR_RTT_CANON(index) WASM_RTT_CANON(index), kExprEnd #define WASM_INIT_EXPR_RTT_CANON(index) WASM_RTT_CANON(index), kExprEnd
#define REF_NULL_ELEMENT kExprRefNull, kFuncRefCode, kExprEnd #define REF_NULL_ELEMENT kExprRefNull, kFuncRefCode, kExprEnd
...@@ -1116,6 +1118,13 @@ TEST_F(WasmModuleVerifyTest, ArrayInitInitExpr) { ...@@ -1116,6 +1118,13 @@ TEST_F(WasmModuleVerifyTest, ArrayInitInitExpr) {
WASM_INIT_EXPR_ARRAY_INIT(0, 3, WASM_I32V(10), WASM_I32V(20), WASM_INIT_EXPR_ARRAY_INIT(0, 3, WASM_I32V(10), WASM_I32V(20),
WASM_I32V(30), WASM_RTT_CANON(0)))}; WASM_I32V(30), WASM_RTT_CANON(0)))};
EXPECT_VERIFIES(basic); EXPECT_VERIFIES(basic);
static const byte basic_nominal[] = {
SECTION(Type, ENTRY_COUNT(1), WASM_ARRAY_DEF(kI16Code, true)),
SECTION(Global, ENTRY_COUNT(1), // --
kRefCode, 0, 0, // type, mutability
WASM_INIT_EXPR_ARRAY_INIT_STATIC(0, 3, WASM_I32V(10),
WASM_I32V(20), WASM_I32V(30)))};
EXPECT_VERIFIES(basic_nominal);
static const byte type_error[] = { static const byte type_error[] = {
SECTION(Type, ENTRY_COUNT(2), // -- SECTION(Type, ENTRY_COUNT(2), // --
...@@ -1234,6 +1243,135 @@ TEST_F(WasmModuleVerifyTest, InvalidStructTypeDef) { ...@@ -1234,6 +1243,135 @@ TEST_F(WasmModuleVerifyTest, InvalidStructTypeDef) {
EXPECT_FAILURE_WITH_MSG(invalid_mutability, "invalid mutability"); EXPECT_FAILURE_WITH_MSG(invalid_mutability, "invalid mutability");
} }
TEST_F(WasmModuleVerifyTest, NominalStructTypeDef) {
WASM_FEATURE_SCOPE(reftypes);
WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(gc);
// Inheritance: t1 <: t2 <: t0
static const byte all_good[] = {
SECTION(Type, ENTRY_COUNT(3), // --
kWasmStructSubtypeCode, // type #0
1, // field count
kI32Code, 1, // mut i32
kDataRefCode, // root of type hierarchy
kWasmStructSubtypeCode, // type #1
2, // field count
kI32Code, 1, // mut i32 (inherited)
kI64Code, 1, // mut i32 (added)
2, // supertype
kWasmStructSubtypeCode, // type #2
1, // field count
kI32Code, 1, // mut i32 (inherited)
0)}; // supertype
EXPECT_VERIFIES(all_good);
ModuleResult result = DecodeModule(all_good, all_good + sizeof(all_good));
EXPECT_OK(result);
WasmModule* module = result.value().get();
EXPECT_EQ(kGenericSuperType, module->supertype(0));
EXPECT_EQ(2u, module->supertype(1));
EXPECT_EQ(0u, module->supertype(2));
static const byte self_or_mutual_ref[] = {
SECTION(Type, ENTRY_COUNT(4), // --
kWasmStructSubtypeCode, 0, // empty struct
kDataRefCode, // root of hierarchy
kWasmStructSubtypeCode, // type1
1, // field count
kOptRefCode, 1, 1, // mut optref type1
0, // supertype
kWasmStructSubtypeCode, // type 2
1, // field count
kOptRefCode, 3, 1, // mut optref type3
0, // supertype
kWasmStructSubtypeCode, // type 3
1, // field count
kOptRefCode, 2, 1, // mut optref type2
0)}; // supertype
EXPECT_VERIFIES(self_or_mutual_ref);
static const byte mutual_ref_with_subtyping[] = {
SECTION(Type,
ENTRY_COUNT(3), // --
kWasmStructSubtypeCode, //
1, // field count
kOptRefCode, 0, 0, // ref type0
kDataRefCode, // root of hierarchy
kWasmStructSubtypeCode, // --
1, // field count
kOptRefCode, 2, 0, // ref type2
0, // supertype
kWasmStructSubtypeCode, // --
1, // field count
kOptRefCode, 1, 0, // ref type1
0)}; // supertype
EXPECT_VERIFIES(mutual_ref_with_subtyping);
static const byte inheritance_cycle[] = {
SECTION(Type, ENTRY_COUNT(2), // --
kWasmStructSubtypeCode, 0, 1, // no fields, supertype 1
kWasmStructSubtypeCode, 0, 0)}; // no fields, supertype 0
EXPECT_FAILURE_WITH_MSG(inheritance_cycle, "cyclic inheritance");
static const byte invalid_field[] = {
SECTION(Type, ENTRY_COUNT(2), // --
kWasmStructTypeCode, U32V_1(1), kI32Code, 1, // t0: [i32]
kWasmStructSubtypeCode, U32V_1(2), // t1:
kI64Code, 1, // i64 (invalid inheritance)
kI32Code, 1, U32V_1(0))}; // i32 (added), supertype 0
EXPECT_FAILURE_WITH_MSG(invalid_field, "invalid explicit supertype");
static const byte structural_supertype[] = {
SECTION(Type, ENTRY_COUNT(2), // --
kWasmStructTypeCode, 0, // empty struct
kWasmStructSubtypeCode, 0, // also empty
0)}; // supertype is structural type
EXPECT_FAILURE_WITH_MSG(structural_supertype, "invalid explicit supertype");
static const byte supertype_oob[] = {
SECTION(Type, ENTRY_COUNT(1), // --
kWasmStructSubtypeCode,
0, // empty struct
13)}; // supertype with invalid index
EXPECT_FAILURE_WITH_MSG(supertype_oob, "Type index 13 is out of bounds");
}
TEST_F(WasmModuleVerifyTest, NominalFunctionTypeDef) {
WASM_FEATURE_SCOPE(reftypes);
WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(gc);
EXPERIMENTAL_FLAG_SCOPE(gc); // Needed for subtype checking.
static const byte all_good[] = {
SECTION(Type, ENTRY_COUNT(2), // --
kWasmFunctionSubtypeCode, // type #0
1, // params count
kRefCode, 0, // ref #0
1, // results count
kOptRefCode, 0, // optref #0
kFuncRefCode, // root of type hierarchy
kWasmFunctionSubtypeCode, // type #1
1, // params count
kOptRefCode, 0, // refined (contravariant)
1, // results count
kRefCode, 0, // refined (covariant)
0)}; // supertype
EXPECT_VERIFIES(all_good);
ModuleResult result = DecodeModule(all_good, all_good + sizeof(all_good));
EXPECT_OK(result);
WasmModule* module = result.value().get();
EXPECT_EQ(kGenericSuperType, module->supertype(0));
EXPECT_EQ(0u, module->supertype(1));
}
TEST_F(WasmModuleVerifyTest, InvalidArrayTypeDef) { TEST_F(WasmModuleVerifyTest, InvalidArrayTypeDef) {
WASM_FEATURE_SCOPE(reftypes); WASM_FEATURE_SCOPE(reftypes);
WASM_FEATURE_SCOPE(typed_funcref); WASM_FEATURE_SCOPE(typed_funcref);
......
...@@ -26,19 +26,21 @@ void DefineStruct(WasmModule* module, std::initializer_list<FieldInit> fields) { ...@@ -26,19 +26,21 @@ void DefineStruct(WasmModule* module, std::initializer_list<FieldInit> fields) {
for (FieldInit field : fields) { for (FieldInit field : fields) {
builder.AddField(field.first, field.second); builder.AddField(field.first, field.second);
} }
return module->add_struct_type(builder.Build()); return module->add_struct_type(builder.Build(), kNoSuperType);
} }
void DefineArray(WasmModule* module, FieldInit element_type) { void DefineArray(WasmModule* module, FieldInit element_type) {
module->add_array_type(module->signature_zone->New<ArrayType>( module->add_array_type(module->signature_zone->New<ArrayType>(
element_type.first, element_type.second)); element_type.first, element_type.second),
kNoSuperType);
} }
void DefineSignature(WasmModule* module, void DefineSignature(WasmModule* module,
std::initializer_list<ValueType> params, std::initializer_list<ValueType> params,
std::initializer_list<ValueType> returns) { std::initializer_list<ValueType> returns) {
module->add_signature( module->add_signature(
FunctionSig::Build(module->signature_zone.get(), returns, params)); FunctionSig::Build(module->signature_zone.get(), returns, params),
kNoSuperType);
} }
TEST_F(WasmSubtypingTest, Subtyping) { TEST_F(WasmSubtypingTest, Subtyping) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment