[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: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#76844}
This commit is contained in:
Jakob Kummerow 2021-09-15 14:59:42 +02:00 committed by V8 LUCI CQ
parent 62acef549e
commit bc3b9332ac
27 changed files with 1142 additions and 403 deletions

View File

@ -1851,8 +1851,10 @@ class WasmDecoder : public Decoder {
opcode =
decoder->read_prefixed_opcode<validate>(pc, &length, "gc_index");
switch (opcode) {
case kExprStructNew:
case kExprStructNewWithRtt:
case kExprStructNewDefault: {
case kExprStructNewDefault:
case kExprStructNewDefaultWithRtt: {
StructIndexImmediate<validate> imm(decoder, pc + length);
return length + imm.length;
}
@ -1863,8 +1865,10 @@ class WasmDecoder : public Decoder {
FieldImmediate<validate> imm(decoder, pc + length);
return length + imm.length;
}
case kExprArrayNew:
case kExprArrayNewWithRtt:
case kExprArrayNewDefault:
case kExprArrayNewDefaultWithRtt:
case kExprArrayGet:
case kExprArrayGetS:
case kExprArrayGetU:
@ -1889,7 +1893,11 @@ class WasmDecoder : public Decoder {
}
case kExprRttCanon:
case kExprRttSub:
case kExprRttFreshSub: {
case kExprRttFreshSub:
case kExprRefTestStatic:
case kExprRefCastStatic:
case kExprBrOnCastStatic:
case kExprBrOnCastStaticFail: {
IndexImmediate<validate> imm(decoder, pc + length, "type index");
return length + imm.length;
}
@ -2043,20 +2051,26 @@ class WasmDecoder : public Decoder {
case kGCPrefix: {
opcode = this->read_prefixed_opcode<validate>(pc);
switch (opcode) {
case kExprStructNewDefault:
case kExprStructNewDefaultWithRtt:
case kExprStructGet:
case kExprStructGetS:
case kExprStructGetU:
case kExprI31New:
case kExprI31GetS:
case kExprI31GetU:
case kExprArrayNewDefault:
case kExprArrayLen:
case kExprRttSub:
case kExprRttFreshSub:
case kExprRefTestStatic:
case kExprRefCastStatic:
case kExprBrOnCastStatic:
case kExprBrOnCastStaticFail:
return {1, 1};
case kExprStructSet:
return {2, 0};
case kExprArrayNewDefault:
case kExprArrayNew:
case kExprArrayNewDefaultWithRtt:
case kExprArrayGet:
case kExprArrayGetS:
case kExprArrayGetU:
@ -2070,6 +2084,7 @@ class WasmDecoder : public Decoder {
case kExprArrayCopy:
return {5, 0};
case kExprRttCanon:
case kExprStructNewDefault:
return {0, 1};
case kExprArrayNewWithRtt:
return {3, 1};
@ -2078,6 +2093,11 @@ class WasmDecoder : public Decoder {
CHECK(Validate(pc + 2, imm));
return {imm.struct_type->field_count() + 1, 1};
}
case kExprStructNew: {
StructIndexImmediate<validate> imm(this, pc + 2);
CHECK(Validate(pc + 2, imm));
return {imm.struct_type->field_count(), 1};
}
default:
UNREACHABLE();
}
@ -4002,22 +4022,32 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
int DecodeGCOpcode(WasmOpcode opcode, uint32_t opcode_length) {
switch (opcode) {
case kExprStructNew:
case kExprStructNewWithRtt: {
StructIndexImmediate<validate> imm(this, this->pc_ + opcode_length);
if (!this->Validate(this->pc_ + opcode_length, imm)) return 0;
Value rtt = Peek(0, imm.struct_type->field_count());
if (!VALIDATE(rtt.type.is_rtt() || rtt.type.is_bottom())) {
PopTypeError(imm.struct_type->field_count(), rtt, "rtt");
return 0;
}
// TODO(7748): Drop this check if {imm} is dropped from the proposal
// à la https://github.com/WebAssembly/function-references/pull/31.
if (!VALIDATE(
rtt.type.is_bottom() ||
(rtt.type.ref_index() == imm.index && rtt.type.has_depth()))) {
PopTypeError(imm.struct_type->field_count(), rtt,
"rtt with depth for type " + std::to_string(imm.index));
return 0;
Value rtt = opcode == kExprStructNew
? CreateValue(ValueType::Rtt(imm.index))
: Peek(0, imm.struct_type->field_count());
if (opcode == kExprStructNew) {
CALL_INTERFACE_IF_OK_AND_REACHABLE(RttCanon, imm.index, &rtt);
Push(rtt);
} else {
DCHECK_EQ(opcode, kExprStructNewWithRtt);
if (!VALIDATE(rtt.type.is_rtt() || rtt.type.is_bottom())) {
PopTypeError(imm.struct_type->field_count(), rtt, "rtt");
return 0;
}
// TODO(7748): Drop this check if {imm} is dropped from the proposal
// à la https://github.com/WebAssembly/function-references/pull/31.
if (!VALIDATE(rtt.type.is_bottom() ||
(rtt.type.ref_index() == imm.index &&
rtt.type.has_depth()))) {
PopTypeError(
imm.struct_type->field_count(), rtt,
"rtt with depth for type " + std::to_string(imm.index));
return 0;
}
}
ArgVector args = PeekArgs(imm.struct_type, 1);
Value value = CreateValue(ValueType::Ref(imm.index, kNonNullable));
@ -4028,7 +4058,8 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
Push(value);
return opcode_length + imm.length;
}
case kExprStructNewDefault: {
case kExprStructNewDefault:
case kExprStructNewDefaultWithRtt: {
NON_CONST_ONLY
StructIndexImmediate<validate> imm(this, this->pc_ + opcode_length);
if (!this->Validate(this->pc_ + opcode_length, imm)) return 0;
@ -4037,26 +4068,34 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
ValueType ftype = imm.struct_type->field(i);
if (!VALIDATE(ftype.is_defaultable())) {
this->DecodeError(
"struct.new_default_with_rtt: immediate struct type %d has "
"field %d of non-defaultable type %s",
imm.index, i, ftype.name().c_str());
"%s: struct type %d has field %d of non-defaultable type %s",
WasmOpcodes::OpcodeName(opcode), imm.index, i,
ftype.name().c_str());
return 0;
}
}
}
Value rtt = Peek(0, 0);
if (!VALIDATE(rtt.type.is_rtt() || rtt.type.is_bottom())) {
PopTypeError(0, rtt, "rtt");
return 0;
}
// TODO(7748): Drop this check if {imm} is dropped from the proposal
// à la https://github.com/WebAssembly/function-references/pull/31.
if (!VALIDATE(
rtt.type.is_bottom() ||
(rtt.type.ref_index() == imm.index && rtt.type.has_depth()))) {
PopTypeError(0, rtt,
"rtt with depth for type " + std::to_string(imm.index));
return 0;
Value rtt = opcode == kExprStructNewDefault
? CreateValue(ValueType::Rtt(imm.index))
: Peek(0, 0);
if (opcode == kExprStructNewDefault) {
CALL_INTERFACE_IF_OK_AND_REACHABLE(RttCanon, imm.index, &rtt);
Push(rtt);
} else {
DCHECK_EQ(opcode, kExprStructNewDefaultWithRtt);
if (!VALIDATE(rtt.type.is_rtt() || rtt.type.is_bottom())) {
PopTypeError(0, rtt, "rtt");
return 0;
}
// TODO(7748): Drop this check if {imm} is dropped from the proposal
// à la https://github.com/WebAssembly/function-references/pull/31.
if (!VALIDATE(rtt.type.is_bottom() ||
(rtt.type.ref_index() == imm.index &&
rtt.type.has_depth()))) {
PopTypeError(
0, rtt, "rtt with depth for type " + std::to_string(imm.index));
return 0;
}
}
Value value = CreateValue(ValueType::Ref(imm.index, kNonNullable));
CALL_INTERFACE_IF_OK_AND_REACHABLE(StructNewDefault, imm, rtt, &value);
@ -4130,23 +4169,32 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
Drop(2);
return opcode_length + field.length;
}
case kExprArrayNew:
case kExprArrayNewWithRtt: {
NON_CONST_ONLY
ArrayIndexImmediate<validate> imm(this, this->pc_ + opcode_length);
if (!this->Validate(this->pc_ + opcode_length, imm)) return 0;
Value rtt = Peek(0, 2);
if (!VALIDATE(rtt.type.is_rtt() || rtt.type.is_bottom())) {
PopTypeError(2, rtt, "rtt");
return 0;
}
// TODO(7748): Drop this check if {imm} is dropped from the proposal
// à la https://github.com/WebAssembly/function-references/pull/31.
if (!VALIDATE(
rtt.type.is_bottom() ||
(rtt.type.ref_index() == imm.index && rtt.type.has_depth()))) {
PopTypeError(2, rtt,
"rtt with depth for type " + std::to_string(imm.index));
return 0;
Value rtt = opcode == kExprArrayNew
? CreateValue(ValueType::Rtt(imm.index))
: Peek(0, 2);
if (opcode == kExprArrayNew) {
CALL_INTERFACE_IF_OK_AND_REACHABLE(RttCanon, imm.index, &rtt);
Push(rtt);
} else {
DCHECK_EQ(opcode, kExprArrayNewWithRtt);
if (!VALIDATE(rtt.type.is_rtt() || rtt.type.is_bottom())) {
PopTypeError(2, rtt, "rtt");
return 0;
}
// TODO(7748): Drop this check if {imm} is dropped from the proposal
// à la https://github.com/WebAssembly/function-references/pull/31.
if (!VALIDATE(rtt.type.is_bottom() ||
(rtt.type.ref_index() == imm.index &&
rtt.type.has_depth()))) {
PopTypeError(
2, rtt, "rtt with depth for type " + std::to_string(imm.index));
return 0;
}
}
Value length = Peek(1, 1, kWasmI32);
Value initial_value =
@ -4158,30 +4206,39 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
Push(value);
return opcode_length + imm.length;
}
case kExprArrayNewDefault: {
case kExprArrayNewDefault:
case kExprArrayNewDefaultWithRtt: {
NON_CONST_ONLY
ArrayIndexImmediate<validate> imm(this, this->pc_ + opcode_length);
if (!this->Validate(this->pc_ + opcode_length, imm)) return 0;
if (!VALIDATE(imm.array_type->element_type().is_defaultable())) {
this->DecodeError(
"array.new_default_with_rtt: immediate array type %d has "
"non-defaultable element type %s",
imm.index, imm.array_type->element_type().name().c_str());
"%s: array type %d has non-defaultable element type %s",
WasmOpcodes::OpcodeName(opcode), imm.index,
imm.array_type->element_type().name().c_str());
return 0;
}
Value rtt = Peek(0, 1);
if (!VALIDATE(rtt.type.is_rtt() || rtt.type.is_bottom())) {
PopTypeError(1, rtt, "rtt");
return 0;
}
// TODO(7748): Drop this check if {imm} is dropped from the proposal
// à la https://github.com/WebAssembly/function-references/pull/31.
if (!VALIDATE(
rtt.type.is_bottom() ||
(rtt.type.ref_index() == imm.index && rtt.type.has_depth()))) {
PopTypeError(1, rtt,
"rtt with depth for type " + std::to_string(imm.index));
return 0;
Value rtt = opcode == kExprArrayNewDefault
? CreateValue(ValueType::Rtt(imm.index))
: Peek(0, 1);
if (opcode == kExprArrayNewDefault) {
CALL_INTERFACE_IF_OK_AND_REACHABLE(RttCanon, imm.index, &rtt);
Push(rtt);
} else {
DCHECK_EQ(opcode, kExprArrayNewDefaultWithRtt);
if (!VALIDATE(rtt.type.is_rtt() || rtt.type.is_bottom())) {
PopTypeError(1, rtt, "rtt");
return 0;
}
// TODO(7748): Drop this check if {imm} is dropped from the proposal
// à la https://github.com/WebAssembly/function-references/pull/31.
if (!VALIDATE(rtt.type.is_bottom() ||
(rtt.type.ref_index() == imm.index &&
rtt.type.has_depth()))) {
PopTypeError(
1, rtt, "rtt with depth for type " + std::to_string(imm.index));
return 0;
}
}
Value length = Peek(1, 0, kWasmI32);
Value value = CreateValue(ValueType::Ref(imm.index, kNonNullable));
@ -4296,7 +4353,8 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
Drop(5);
return opcode_length + dst_imm.length + src_imm.length;
}
case kExprArrayInit: {
case kExprArrayInit:
case kExprArrayInitStatic: {
if (decoding_mode != kInitExpression) {
this->DecodeError("array.init is only allowed in init. expressions");
return 0;
@ -4314,12 +4372,18 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
length_imm.index, kV8MaxWasmArrayInitLength);
return 0;
}
Value rtt = opcode == kExprArrayInit
? Peek(0, elem_count, ValueType::Rtt(array_imm.index))
: CreateValue(ValueType::Rtt(array_imm.index));
if (opcode == kExprArrayInitStatic) {
CALL_INTERFACE_IF_OK_AND_REACHABLE(RttCanon, array_imm.index, &rtt);
Push(rtt);
}
ValueType element_type = array_imm.array_type->element_type();
std::vector<ValueType> element_types(elem_count,
element_type.Unpacked());
FunctionSig element_sig(0, elem_count, element_types.data());
ArgVector elements = PeekArgs(&element_sig, 1);
Value rtt = Peek(0, elem_count, ValueType::Rtt(array_imm.index));
Value result =
CreateValue(ValueType::Ref(array_imm.index, kNonNullable));
CALL_INTERFACE_IF_OK_AND_REACHABLE(ArrayInit, array_imm, elements, rtt,
@ -4359,7 +4423,8 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
IndexImmediate<validate> imm(this, this->pc_ + opcode_length,
"type index");
if (!this->ValidateType(this->pc_ + opcode_length, imm)) return 0;
Value value = CreateValue(ValueType::Rtt(imm.index, 0));
Value value = CreateValue(ValueType::Rtt(
imm.index, GetSubtypingDepth(this->module_, imm.index)));
CALL_INTERFACE_IF_OK_AND_REACHABLE(RttCanon, imm.index, &value);
Push(value);
return opcode_length + imm.length;
@ -4397,16 +4462,29 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
}
return opcode_length + imm.length;
}
case kExprRefTest: {
case kExprRefTest:
case kExprRefTestStatic: {
NON_CONST_ONLY
// "Tests whether {obj}'s runtime type is a runtime subtype of {rtt}."
Value rtt = Peek(0, 1);
Value rtt = Peek(0, 1); // This is safe for the ...Static instruction.
if (opcode == kExprRefTestStatic) {
IndexImmediate<validate> imm(this, this->pc_ + opcode_length,
"type index");
if (!this->ValidateType(this->pc_ + opcode_length, imm)) return 0;
opcode_length += imm.length;
rtt = CreateValue(ValueType::Rtt(
imm.index, GetSubtypingDepth(this->module_, imm.index)));
CALL_INTERFACE_IF_OK_AND_REACHABLE(RttCanon, imm.index, &rtt);
Push(rtt);
} else {
DCHECK_EQ(opcode, kExprRefTest);
if (!VALIDATE(rtt.type.is_rtt() || rtt.type.is_bottom())) {
PopTypeError(1, rtt, "rtt");
return 0;
}
}
Value obj = Peek(1, 0);
Value value = CreateValue(kWasmI32);
if (!VALIDATE(rtt.type.is_rtt() || rtt.type.is_bottom())) {
PopTypeError(1, rtt, "rtt");
return 0;
}
if (!VALIDATE(IsSubtypeOf(obj.type, kWasmFuncRef, this->module_) ||
IsSubtypeOf(obj.type,
ValueType::Ref(HeapType::kData, kNullable),
@ -4431,14 +4509,27 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
Push(value);
return opcode_length;
}
case kExprRefCast: {
case kExprRefCast:
case kExprRefCastStatic: {
NON_CONST_ONLY
Value rtt = Peek(0, 1);
Value obj = Peek(1, 0);
if (!VALIDATE(rtt.type.is_rtt() || rtt.type.is_bottom())) {
PopTypeError(1, rtt, "rtt");
return 0;
Value rtt = Peek(0, 1); // This is safe for the ...Static instruction.
if (opcode == kExprRefCastStatic) {
IndexImmediate<validate> imm(this, this->pc_ + opcode_length,
"type index");
if (!this->ValidateType(this->pc_ + opcode_length, imm)) return 0;
opcode_length += imm.length;
rtt = CreateValue(ValueType::Rtt(
imm.index, GetSubtypingDepth(this->module_, imm.index)));
CALL_INTERFACE_IF_OK_AND_REACHABLE(RttCanon, imm.index, &rtt);
Push(rtt);
} else {
DCHECK_EQ(opcode, kExprRefCast);
if (!VALIDATE(rtt.type.is_rtt() || rtt.type.is_bottom())) {
PopTypeError(1, rtt, "rtt");
return 0;
}
}
Value obj = Peek(1, 0);
if (!VALIDATE(IsSubtypeOf(obj.type, kWasmFuncRef, this->module_) ||
IsSubtypeOf(obj.type,
ValueType::Ref(HeapType::kData, kNullable),
@ -4477,7 +4568,8 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
Push(value);
return opcode_length;
}
case kExprBrOnCast: {
case kExprBrOnCast:
case kExprBrOnCastStatic: {
NON_CONST_ONLY
BranchDepthImmediate<validate> branch_depth(this,
this->pc_ + opcode_length);
@ -4485,10 +4577,22 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
control_.size())) {
return 0;
}
Value rtt = Peek(0, 1);
if (!VALIDATE(rtt.type.is_rtt() || rtt.type.is_bottom())) {
PopTypeError(1, rtt, "rtt");
return 0;
Value rtt = Peek(0, 1); // This is safe for the ...Static instruction.
if (opcode == kExprBrOnCastStatic) {
IndexImmediate<validate> imm(this, this->pc_ + opcode_length,
"type index");
if (!this->ValidateType(this->pc_ + opcode_length, imm)) return 0;
opcode_length += imm.length;
rtt = CreateValue(ValueType::Rtt(
imm.index, GetSubtypingDepth(this->module_, imm.index)));
CALL_INTERFACE_IF_OK_AND_REACHABLE(RttCanon, imm.index, &rtt);
Push(rtt);
} else {
DCHECK_EQ(opcode, kExprBrOnCast);
if (!VALIDATE(rtt.type.is_rtt() || rtt.type.is_bottom())) {
PopTypeError(1, rtt, "rtt");
return 0;
}
}
Value obj = Peek(1, 0);
if (!VALIDATE(IsSubtypeOf(obj.type, kWasmFuncRef, this->module_) ||
@ -4535,7 +4639,8 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
Push(obj); // Restore stack state on fallthrough.
return opcode_length + branch_depth.length;
}
case kExprBrOnCastFail: {
case kExprBrOnCastFail:
case kExprBrOnCastStaticFail: {
NON_CONST_ONLY
BranchDepthImmediate<validate> branch_depth(this,
this->pc_ + opcode_length);
@ -4543,10 +4648,22 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
control_.size())) {
return 0;
}
Value rtt = Peek(0, 1);
if (!VALIDATE(rtt.type.is_rtt() || rtt.type.is_bottom())) {
PopTypeError(1, rtt, "rtt");
return 0;
Value rtt = Peek(0, 1); // This is safe for the ...Static instruction.
if (opcode == kExprBrOnCastStaticFail) {
IndexImmediate<validate> imm(this, this->pc_ + opcode_length,
"type index");
if (!this->ValidateType(this->pc_ + opcode_length, imm)) return 0;
opcode_length += imm.length;
rtt = CreateValue(ValueType::Rtt(
imm.index, GetSubtypingDepth(this->module_, imm.index)));
CALL_INTERFACE_IF_OK_AND_REACHABLE(RttCanon, imm.index, &rtt);
Push(rtt);
} else {
DCHECK_EQ(opcode, kExprBrOnCastFail);
if (!VALIDATE(rtt.type.is_rtt() || rtt.type.is_bottom())) {
PopTypeError(1, rtt, "rtt");
return 0;
}
}
Value obj = Peek(1, 0);
if (!VALIDATE(IsSubtypeOf(obj.type, kWasmFuncRef, this->module_) ||
@ -4726,7 +4843,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
return opcode_length + branch_depth.length;
}
default:
this->DecodeError("invalid gc opcode");
this->DecodeError("invalid gc opcode: %x", opcode);
return 0;
}
}

View File

@ -550,35 +550,40 @@ class ModuleDecoderImpl : public Decoder {
}
void DecodeTypeSection() {
uint32_t signatures_count = consume_count("types count", kV8MaxWasmTypes);
module_->types.reserve(signatures_count);
for (uint32_t i = 0; ok() && i < signatures_count; ++i) {
uint32_t types_count = consume_count("types count", kV8MaxWasmTypes);
module_->types.reserve(types_count);
for (uint32_t i = 0; ok() && i < types_count; ++i) {
TRACE("DecodeSignature[%d] module+%d\n", i,
static_cast<int>(pc_ - start_));
uint8_t kind = consume_u8("type kind");
switch (kind) {
case kWasmFunctionTypeCode: {
case kWasmFunctionTypeCode:
case kWasmFunctionSubtypeCode: {
const FunctionSig* s = consume_sig(module_->signature_zone.get());
module_->add_signature(s);
uint32_t super_index = kNoSuperType;
if (kind == kWasmFunctionSubtypeCode) {
if (!enabled_features_.has_gc()) {
errorf(pc(),
"invalid function type definition, enable with "
"--experimental-wasm-gc");
break;
}
HeapType super_type = consume_super_type();
if (super_type == HeapType::kFunc) {
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_signature(s, super_index);
break;
}
case kWasmFunctionExtendingTypeCode: {
if (!enabled_features_.has_gc()) {
errorf(pc(),
"invalid function type definition, enable with "
"--experimental-wasm-gc");
break;
}
const FunctionSig* s = consume_sig(module_->signature_zone.get());
module_->add_signature(s);
uint32_t super_index = consume_u32v("supertype");
if (!module_->has_signature(super_index)) {
errorf(pc(), "invalid function supertype index: %d", super_index);
break;
}
break;
}
case kWasmStructTypeCode: {
case kWasmStructTypeCode:
case kWasmStructSubtypeCode: {
if (!enabled_features_.has_gc()) {
errorf(pc(),
"invalid struct type definition, enable with "
@ -586,28 +591,26 @@ class ModuleDecoderImpl : public Decoder {
break;
}
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
// {signature_map} does for function signatures?
break;
}
case kWasmStructExtendingTypeCode: {
if (!enabled_features_.has_gc()) {
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: {
case kWasmArrayTypeCode:
case kWasmArraySubtypeCode: {
if (!enabled_features_.has_gc()) {
errorf(pc(),
"invalid array type definition, enable with "
@ -615,23 +618,20 @@ class ModuleDecoderImpl : public Decoder {
break;
}
const ArrayType* type = consume_array(module_->signature_zone.get());
module_->add_array_type(type);
break;
}
case kWasmArrayExtendingTypeCode: {
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);
uint32_t super_index = consume_u32v("supertype");
if (!module_->has_array(super_index)) {
errorf(pc(), "invalid array supertype: %d", super_index);
break;
uint32_t super_index = kNoSuperType;
if (kind == kWasmArraySubtypeCode) {
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_array_type(type, super_index);
break;
}
default:
@ -639,6 +639,46 @@ class ModuleDecoderImpl : public Decoder {
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();
}
@ -1787,6 +1827,15 @@ class ModuleDecoderImpl : public Decoder {
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() {
uint8_t opcode = read_u8<kFullValidation>(this->pc());
switch (opcode) {

View File

@ -194,6 +194,41 @@ Handle<Map> CreateArrayMap(Isolate* isolate, const WasmModule* module,
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 {
// TODO(7748): Consider storing this array in Maps'
@ -661,28 +696,8 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
if (enabled_.has_gc()) {
Handle<FixedArray> maps = isolate_->factory()->NewFixedArray(
static_cast<int>(module_->type_kinds.size()));
for (int map_index = 0;
map_index < static_cast<int>(module_->type_kinds.size());
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);
for (uint32_t index = 0; index < module_->type_kinds.size(); index++) {
CreateMapForType(isolate_, module_, index, instance, maps);
}
instance->set_managed_object_maps(*maps);
}

View File

@ -50,9 +50,9 @@ enum ValueTypeCode : uint8_t {
constexpr uint8_t kWasmFunctionTypeCode = 0x60;
constexpr uint8_t kWasmStructTypeCode = 0x5f;
constexpr uint8_t kWasmArrayTypeCode = 0x5e;
constexpr uint8_t kWasmFunctionExtendingTypeCode = 0x5d;
constexpr uint8_t kWasmStructExtendingTypeCode = 0x5c;
constexpr uint8_t kWasmArrayExtendingTypeCode = 0x5b;
constexpr uint8_t kWasmFunctionSubtypeCode = 0x5d;
constexpr uint8_t kWasmStructSubtypeCode = 0x5c;
constexpr uint8_t kWasmArraySubtypeCode = 0x5b;
// Binary encoding of import/export kinds.
enum ImportExportKindCode : uint8_t {

View File

@ -39,7 +39,9 @@ ValueType WasmInitExpr::type(const WasmModule* module,
case kRefNullConst:
return ValueType::Ref(immediate().heap_type, kNullable);
case kStructNewWithRtt:
case kStructNew:
case kArrayInit:
case kArrayInitStatic:
return ValueType::Ref(immediate().index, kNonNullable);
case kRttCanon:
return ValueType::Rtt(immediate().heap_type, 0);

View File

@ -34,7 +34,9 @@ class WasmInitExpr {
kRefNullConst,
kRefFuncConst,
kStructNewWithRtt,
kStructNew,
kArrayInit,
kArrayInitStatic,
kRttCanon,
kRttSub,
kRttFreshSub,
@ -99,6 +101,15 @@ class WasmInitExpr {
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,
std::vector<WasmInitExpr> elements) {
WasmInitExpr expr;
@ -108,6 +119,15 @@ class WasmInitExpr {
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) {
WasmInitExpr expr;
expr.kind_ = kRttCanon;
@ -157,6 +177,7 @@ class WasmInitExpr {
case kRefNullConst:
return immediate().heap_type == other.immediate().heap_type;
case kStructNewWithRtt:
case kStructNew:
if (immediate().index != other.immediate().index) return false;
DCHECK_EQ(operands().size(), other.operands().size());
for (uint32_t i = 0; i < operands().size(); i++) {
@ -164,6 +185,7 @@ class WasmInitExpr {
}
return true;
case kArrayInit:
case kArrayInitStatic:
if (immediate().index != other.immediate().index) return false;
if (operands().size() != other.operands().size()) return false;
for (uint32_t i = 0; i < operands().size(); i++) {

View File

@ -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);
if (sig_entry != signature_map_.end()) return sig_entry->second;
uint32_t index = static_cast<uint32_t>(types_.size());
signature_map_.emplace(*sig, index);
types_.push_back(Type(sig));
types_.push_back(Type(sig, supertype));
return index;
}
@ -307,15 +307,16 @@ uint32_t WasmModuleBuilder::AddException(FunctionSig* type) {
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());
types_.push_back(Type(type));
types_.push_back(Type(type, supertype));
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());
types_.push_back(Type(type));
types_.push_back(Type(type, supertype));
return index;
}
@ -509,22 +510,30 @@ void WriteInitializerExpressionWithEnd(ZoneBuffer* buffer,
}
break;
}
case WasmInitExpr::kStructNew:
case WasmInitExpr::kStructNewWithRtt:
STATIC_ASSERT((kExprStructNew >> 8) == kGCPrefix);
STATIC_ASSERT((kExprStructNewWithRtt >> 8) == kGCPrefix);
for (const WasmInitExpr& operand : init.operands()) {
WriteInitializerExpressionWithEnd(buffer, operand, kWasmBottom);
}
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);
break;
case WasmInitExpr::kArrayInit:
case WasmInitExpr::kArrayInitStatic:
STATIC_ASSERT((kExprArrayInit >> 8) == kGCPrefix);
STATIC_ASSERT((kExprArrayInitStatic >> 8) == kGCPrefix);
for (const WasmInitExpr& operand : init.operands()) {
WriteInitializerExpressionWithEnd(buffer, operand, kWasmBottom);
}
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(static_cast<uint32_t>(init.operands().size() - 1));
break;
@ -568,10 +577,12 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const {
buffer->write_size(types_.size());
for (const Type& type : types_) {
bool has_super = type.supertype != kNoSuperType;
switch (type.kind) {
case Type::kFunctionSig: {
FunctionSig* sig = type.sig;
buffer->write_u8(kWasmFunctionTypeCode);
buffer->write_u8(has_super ? kWasmFunctionSubtypeCode
: kWasmFunctionTypeCode);
buffer->write_size(sig->parameter_count());
for (auto param : sig->parameters()) {
WriteValueType(buffer, param);
@ -580,23 +591,40 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const {
for (auto ret : sig->returns()) {
WriteValueType(buffer, ret);
}
if (type.supertype == kGenericSuperType) {
buffer->write_u8(kFuncRefCode);
} else if (has_super) {
buffer->write_i32v(type.supertype);
}
break;
}
case Type::kStructType: {
StructType* struct_type = type.struct_type;
buffer->write_u8(kWasmStructTypeCode);
buffer->write_u8(has_super ? kWasmStructSubtypeCode
: kWasmStructTypeCode);
buffer->write_size(struct_type->field_count());
for (uint32_t i = 0; i < struct_type->field_count(); i++) {
WriteValueType(buffer, struct_type->field(i));
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;
}
case Type::kArrayType: {
ArrayType* array_type = type.array_type;
buffer->write_u8(kWasmArrayTypeCode);
buffer->write_u8(has_super ? kWasmArraySubtypeCode
: kWasmArrayTypeCode);
WriteValueType(buffer, array_type->element_type());
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;
}
}

View File

@ -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
// exceeded.
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 AddStructType(StructType* type);
uint32_t AddArrayType(ArrayType* type);
uint32_t AddStructType(StructType* type, uint32_t supertype = kNoSuperType);
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 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 {
private:
struct Type {
enum Kind { kFunctionSig, kStructType, kArrayType };
explicit Type(FunctionSig* signature)
: kind(kFunctionSig), sig(signature) {}
explicit Type(StructType* struct_type)
: kind(kStructType), struct_type(struct_type) {}
explicit Type(ArrayType* array_type)
: kind(kArrayType), array_type(array_type) {}
explicit Type(FunctionSig* signature, uint32_t supertype)
: kind(kFunctionSig), supertype(supertype), sig(signature) {}
explicit Type(StructType* struct_type, uint32_t supertype)
: kind(kStructType), supertype(supertype), struct_type(struct_type) {}
explicit Type(ArrayType* array_type, uint32_t supertype)
: kind(kArrayType), supertype(supertype), array_type(array_type) {}
Kind kind;
uint32_t supertype;
union {
FunctionSig* sig;
StructType* struct_type;

View File

@ -113,6 +113,23 @@ int GetContainingWasmFunction(const WasmModule* module, uint32_t byte_offset) {
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,
WireBytesRef name) {
base::MutexGuard lock(&mutex_);

View File

@ -259,6 +259,11 @@ struct V8_EXPORT_PRIVATE WasmDebugSymbols {
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.
struct V8_EXPORT_PRIVATE WasmModule {
std::unique_ptr<Zone> signature_zone;
@ -288,6 +293,7 @@ struct V8_EXPORT_PRIVATE WasmModule {
WireBytesRef name = {0, 0};
std::vector<TypeDefinition> types; // 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.
// Note: right now, only functions are canonicalized, and arrays and structs
// map to themselves.
@ -295,9 +301,10 @@ struct V8_EXPORT_PRIVATE WasmModule {
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));
type_kinds.push_back(kWasmFunctionTypeCode);
supertypes.push_back(supertype);
uint32_t canonical_id = sig ? signature_map.FindOrInsert(*sig) : 0;
canonicalized_type_ids.push_back(canonical_id);
}
@ -309,9 +316,10 @@ struct V8_EXPORT_PRIVATE WasmModule {
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));
type_kinds.push_back(kWasmStructTypeCode);
supertypes.push_back(supertype);
// No canonicalization for structs.
canonicalized_type_ids.push_back(0);
}
@ -323,9 +331,10 @@ struct V8_EXPORT_PRIVATE WasmModule {
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));
type_kinds.push_back(kWasmArrayTypeCode);
supertypes.push_back(supertype);
// No canonicalization for arrays.
canonicalized_type_ids.push_back(0);
}
@ -337,6 +346,14 @@ struct V8_EXPORT_PRIVATE WasmModule {
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<WasmDataSegment> data_segments;
std::vector<WasmTable> tables;
@ -418,6 +435,12 @@ int GetContainingWasmFunction(const WasmModule* module, uint32_t byte_offset);
// contained within a function.
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.
// 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

View File

@ -382,12 +382,16 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
// GC operations.
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(StructGet, "struct.get")
CASE_OP(StructGetS, "struct.get_s")
CASE_OP(StructGetU, "struct.get_u")
CASE_OP(StructSet, "struct.set")
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(ArrayGet, "array.get")
CASE_OP(ArrayGetS, "array.get_s")
@ -396,6 +400,7 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_OP(ArrayLen, "array.len")
CASE_OP(ArrayCopy, "array.copy")
CASE_OP(ArrayInit, "array.init")
CASE_OP(ArrayInitStatic, "array.init_static")
CASE_OP(I31New, "i31.new")
CASE_OP(I31GetS, "i31.get_s")
CASE_OP(I31GetU, "i31.get_u")
@ -403,9 +408,13 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_OP(RttSub, "rtt.sub")
CASE_OP(RttFreshSub, "rtt.fresh_sub")
CASE_OP(RefTest, "ref.test")
CASE_OP(RefTestStatic, "ref.test_static")
CASE_OP(RefCast, "ref.cast")
CASE_OP(RefCastStatic, "ref.cast_static")
CASE_OP(BrOnCast, "br_on_cast")
CASE_OP(BrOnCastStatic, "br_on_cast_static")
CASE_OP(BrOnCastFail, "br_on_cast_fail")
CASE_OP(BrOnCastStaticFail, "br_on_cast_static_fail")
CASE_OP(RefIsFunc, "ref.is_func")
CASE_OP(RefIsData, "ref.is_data")
CASE_OP(RefIsI31, "ref.is_i31")

View File

@ -650,13 +650,15 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig,
#define FOREACH_GC_OPCODE(V) \
V(StructNewWithRtt, 0xfb01, _) \
V(StructNewDefault, 0xfb02, _) \
V(StructNewDefaultWithRtt, 0xfb02, _) \
V(StructGet, 0xfb03, _) \
V(StructGetS, 0xfb04, _) \
V(StructGetU, 0xfb05, _) \
V(StructSet, 0xfb06, _) \
V(StructNew, 0xfb07, _) \
V(StructNewDefault, 0xfb08, _) \
V(ArrayNewWithRtt, 0xfb11, _) \
V(ArrayNewDefault, 0xfb12, _) \
V(ArrayNewDefaultWithRtt, 0xfb12, _) \
V(ArrayGet, 0xfb13, _) \
V(ArrayGetS, 0xfb14, _) \
V(ArrayGetU, 0xfb15, _) \
@ -664,6 +666,9 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig,
V(ArrayLen, 0xfb17, _) \
V(ArrayCopy, 0xfb18, _) /* not standardized - V8 experimental */ \
V(ArrayInit, 0xfb19, _) /* not standardized - V8 experimental */ \
V(ArrayInitStatic, 0xfb1a, _) \
V(ArrayNew, 0xfb1b, _) \
V(ArrayNewDefault, 0xfb1c, _) \
V(I31New, 0xfb20, _) \
V(I31GetS, 0xfb21, _) \
V(I31GetU, 0xfb22, _) \
@ -674,6 +679,10 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig,
V(RefCast, 0xfb41, _) \
V(BrOnCast, 0xfb42, _) \
V(BrOnCastFail, 0xfb43, _) \
V(RefTestStatic, 0xfb44, _) \
V(RefCastStatic, 0xfb45, _) \
V(BrOnCastStatic, 0xfb46, _) \
V(BrOnCastStaticFail, 0xfb47, _) \
V(RefIsFunc, 0xfb50, _) \
V(RefIsData, 0xfb51, _) \
V(RefIsI31, 0xfb52, _) \

View File

@ -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,
const WasmModule* sub_module,
const WasmModule* super_module) {
@ -234,8 +236,10 @@ bool StructIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
return false;
}
TypeJudgementCache::instance()->cache_subtype(subtype_index, supertype_index,
sub_module, super_module);
if (!sub_module->has_supertype(subtype_index)) {
TypeJudgementCache::instance()->cache_subtype(
subtype_index, supertype_index, sub_module, super_module);
}
for (uint32_t i = 0; i < super_struct->field_count(); i++) {
bool sub_mut = sub_struct->mutability(i);
bool super_mut = super_struct->mutability(i);
@ -261,8 +265,10 @@ bool ArrayIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
super_module->types[supertype_index].array_type;
bool sub_mut = sub_array->mutability();
bool super_mut = super_array->mutability();
TypeJudgementCache::instance()->cache_subtype(subtype_index, supertype_index,
sub_module, super_module);
if (!sub_module->has_supertype(subtype_index)) {
TypeJudgementCache::instance()->cache_subtype(
subtype_index, supertype_index, sub_module, super_module);
}
if (sub_mut != super_mut ||
(sub_mut &&
!EquivalentTypes(sub_array->element_type(), super_array->element_type(),
@ -294,8 +300,10 @@ bool FunctionIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
return false;
}
TypeJudgementCache::instance()->cache_subtype(subtype_index, supertype_index,
sub_module, super_module);
if (!sub_module->has_supertype(subtype_index)) {
TypeJudgementCache::instance()->cache_subtype(
subtype_index, supertype_index, sub_module, super_module);
}
for (uint32_t i = 0; i < sub_func->parameter_count(); i++) {
// Contravariance for params.
@ -318,7 +326,6 @@ bool FunctionIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
return true;
}
} // namespace
V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
ValueType subtype, ValueType supertype, const WasmModule* sub_module,
@ -410,11 +417,35 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
DCHECK(super_heap.is_index());
uint32_t super_index = super_heap.ref_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];
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
// threads is protected by a lock.
base::RecursiveMutexGuard type_cache_access(

View File

@ -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.
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 internal
} // namespace v8

View File

@ -81,18 +81,20 @@ class WasmGCTester {
isolate_->factory()->undefined_value(), argc, args);
}
byte DefineStruct(std::initializer_list<F> fields) {
byte DefineStruct(std::initializer_list<F> fields,
uint32_t supertype = kNoSuperType) {
StructType::Builder type_builder(&zone_,
static_cast<uint32_t>(fields.size()));
for (F field : fields) {
type_builder.AddField(field.first, field.second);
}
return builder_.AddStructType(type_builder.Build());
return builder_.AddStructType(type_builder.Build(), supertype);
}
byte DefineArray(ValueType element_type, bool mutability) {
return builder_.AddArrayType(
zone_.New<ArrayType>(element_type, mutability));
byte DefineArray(ValueType element_type, bool mutability,
uint32_t supertype = kNoSuperType) {
return builder_.AddArrayType(zone_.New<ArrayType>(element_type, mutability),
supertype);
}
byte DefineSignature(FunctionSig* sig) { return builder_.AddSignature(sig); }
@ -276,6 +278,11 @@ WASM_COMPILED_EXEC_TEST(WasmBasicStruct) {
WASM_RTT_CANON(type_index)),
kExprEnd});
const byte kGetStructNominal = tester.DefineFunction(
&sig_q_v, {},
{WASM_STRUCT_NEW_DEFAULT(type_index), WASM_DROP,
WASM_STRUCT_NEW(type_index, WASM_I32V(42), WASM_I32V(64)), kExprEnd});
// Test struct.new, returning reference to an empty struct.
const byte kGetEmptyStruct = tester.DefineFunction(
&sig_qe_v, {},
@ -303,6 +310,9 @@ WASM_COMPILED_EXEC_TEST(WasmBasicStruct) {
tester.CheckResult(kGet1, 42);
tester.CheckResult(kGet2, 64);
CHECK(tester.GetResultObject(kGetStruct).ToHandleChecked()->IsWasmStruct());
CHECK(tester.GetResultObject(kGetStructNominal)
.ToHandleChecked()
->IsWasmStruct());
CHECK(tester.GetResultObject(kGetEmptyStruct)
.ToHandleChecked()
->IsWasmStruct());
@ -457,6 +467,30 @@ WASM_COMPILED_EXEC_TEST(BrOnCast) {
WASM_GC_OP(kExprStructGet), type_index, 0, WASM_LOCAL_GET(0),
kExprI32Add, kExprEnd});
const byte kTestStructStatic = tester.DefineFunction(
tester.sigs.i_v(), {kWasmI32, kDataRefNull},
{WASM_BLOCK_R(
ValueType::Ref(type_index, kNullable),
WASM_LOCAL_SET(0, WASM_I32V(111)),
// Pipe a struct through a local so it's statically typed
// as dataref.
WASM_LOCAL_SET(1, WASM_STRUCT_NEW(other_type_index, WASM_F32(1.0))),
WASM_LOCAL_GET(1),
// The type check fails, so this branch isn't taken.
WASM_BR_ON_CAST_STATIC(0, type_index), WASM_DROP,
WASM_LOCAL_SET(0, WASM_I32V(221)), // (Final result) - 1
WASM_LOCAL_SET(1, WASM_STRUCT_NEW(type_index, WASM_I32V(1))),
WASM_LOCAL_GET(1),
// This branch is taken.
WASM_BR_ON_CAST_STATIC(0, type_index),
WASM_GC_OP(kExprRefCastStatic), type_index,
// Not executed due to the branch.
WASM_LOCAL_SET(0, WASM_I32V(333))),
WASM_GC_OP(kExprStructGet), type_index, 0, WASM_LOCAL_GET(0),
kExprI32Add, kExprEnd});
const byte kTestNull = tester.DefineFunction(
tester.sigs.i_v(), {kWasmI32, kDataRefNull},
{WASM_BLOCK_R(ValueType::Ref(type_index, kNullable),
@ -488,6 +522,7 @@ WASM_COMPILED_EXEC_TEST(BrOnCast) {
tester.CompileModule();
tester.CheckResult(kTestStruct, 222);
tester.CheckResult(kTestStructStatic, 222);
tester.CheckResult(kTestNull, 222);
tester.CheckResult(kTypedAfterBranch, 42);
}
@ -542,8 +577,21 @@ WASM_COMPILED_EXEC_TEST(BrOnCastFail) {
WASM_RTT_CANON(type1)))});
#undef FUNCTION_BODY
const byte kBranchTakenStatic = tester.DefineFunction(
tester.sigs.i_v(), {kDataRefNull},
{WASM_LOCAL_SET(
0, WASM_STRUCT_NEW(type1, WASM_I64V(10), WASM_I32V(field1_value))),
WASM_BLOCK(
WASM_BLOCK_R(kDataRefNull, WASM_LOCAL_GET(0),
WASM_BR_ON_CAST_STATIC_FAIL(0, type0),
WASM_GC_OP(kExprStructGet), type0, 0, kExprReturn),
kExprBrOnNull, 0, WASM_GC_OP(kExprRefCastStatic), type1,
WASM_GC_OP(kExprStructGet), type1, 1, kExprReturn),
WASM_I32V(null_value), kExprEnd});
tester.CompileModule();
tester.CheckResult(kBranchTaken, field1_value);
tester.CheckResult(kBranchTakenStatic, field1_value);
tester.CheckResult(kBranchNotTaken, field0_value);
tester.CheckResult(kNull, null_value);
tester.CheckResult(kUnrelatedTypes, field1_value);
@ -779,19 +827,24 @@ WASM_COMPILED_EXEC_TEST(WasmBasicArray) {
WASM_RTT_CANON(type_index)),
kExprEnd});
const byte kAllocateStatic = tester.DefineFunction(
&sig_q_v, {},
{WASM_ARRAY_NEW_DEFAULT(type_index, WASM_I32V(2)), WASM_DROP,
WASM_ARRAY_NEW(type_index, WASM_I32V(42), WASM_I32V(2)), kExprEnd});
const uint32_t kLongLength = 1u << 16;
const byte kAllocateLarge = tester.DefineFunction(
&sig_q_v, {},
{WASM_ARRAY_NEW_DEFAULT(type_index, WASM_I32V(kLongLength),
WASM_RTT_CANON(type_index)),
{WASM_ARRAY_NEW_DEFAULT_WITH_RTT(type_index, WASM_I32V(kLongLength),
WASM_RTT_CANON(type_index)),
kExprEnd});
ArrayType array_type(kWasmI32, true);
const uint32_t kTooLong = WasmArray::MaxLength(&array_type) + 1;
const byte kAllocateTooLarge = tester.DefineFunction(
&sig_q_v, {},
{WASM_ARRAY_NEW_DEFAULT(type_index, WASM_I32V(kTooLong),
WASM_RTT_CANON(type_index)),
{WASM_ARRAY_NEW_DEFAULT_WITH_RTT(type_index, WASM_I32V(kTooLong),
WASM_RTT_CANON(type_index)),
kExprEnd});
// Tests that fp arrays work properly.
@ -818,11 +871,13 @@ WASM_COMPILED_EXEC_TEST(WasmBasicArray) {
tester.CheckResult(kGetLength, 42);
tester.CheckResult(kTestFpArray, static_cast<int32_t>(result_value));
MaybeHandle<Object> h_result = tester.GetResultObject(kAllocate);
CHECK(h_result.ToHandleChecked()->IsWasmArray());
#if OBJECT_PRINT
h_result.ToHandleChecked()->Print();
#endif
Handle<Object> h_result = tester.GetResultObject(kAllocate).ToHandleChecked();
CHECK(h_result->IsWasmArray());
CHECK_EQ(2, Handle<WasmArray>::cast(h_result)->length());
h_result = tester.GetResultObject(kAllocateStatic).ToHandleChecked();
CHECK(h_result->IsWasmArray());
CHECK_EQ(2, Handle<WasmArray>::cast(h_result)->length());
MaybeHandle<Object> maybe_large_result =
tester.GetResultObject(kAllocateLarge);
@ -913,8 +968,9 @@ WASM_COMPILED_EXEC_TEST(WasmArrayCopy) {
// Copies i32 ranges: local1[0..3] to local2[6..9].
const byte kCopyI32 = tester.DefineFunction(
tester.sigs.i_i(), {optref(array32_index), optref(array32_index)},
{WASM_LOCAL_SET(1, WASM_ARRAY_NEW_DEFAULT(array32_index, WASM_I32V(10),
WASM_RTT_CANON(array32_index))),
{WASM_LOCAL_SET(
1, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array32_index, WASM_I32V(10),
WASM_RTT_CANON(array32_index))),
WASM_ARRAY_SET(array32_index, WASM_LOCAL_GET(1), WASM_I32V(0),
WASM_I32V(0)),
WASM_ARRAY_SET(array32_index, WASM_LOCAL_GET(1), WASM_I32V(1),
@ -923,8 +979,9 @@ WASM_COMPILED_EXEC_TEST(WasmArrayCopy) {
WASM_I32V(2)),
WASM_ARRAY_SET(array32_index, WASM_LOCAL_GET(1), WASM_I32V(3),
WASM_I32V(3)),
WASM_LOCAL_SET(2, WASM_ARRAY_NEW_DEFAULT(array32_index, WASM_I32V(10),
WASM_RTT_CANON(array32_index))),
WASM_LOCAL_SET(
2, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array32_index, WASM_I32V(10),
WASM_RTT_CANON(array32_index))),
WASM_ARRAY_COPY(array32_index, array32_index, WASM_LOCAL_GET(2),
WASM_I32V(6), WASM_LOCAL_GET(1), WASM_I32V(0),
WASM_I32V(4)),
@ -934,8 +991,9 @@ WASM_COMPILED_EXEC_TEST(WasmArrayCopy) {
// Copies i16 ranges: local1[0..3] to local2[6..9].
const byte kCopyI16 = tester.DefineFunction(
tester.sigs.i_i(), {optref(array16_index), optref(array16_index)},
{WASM_LOCAL_SET(1, WASM_ARRAY_NEW_DEFAULT(array16_index, WASM_I32V(10),
WASM_RTT_CANON(array16_index))),
{WASM_LOCAL_SET(
1, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array16_index, WASM_I32V(10),
WASM_RTT_CANON(array16_index))),
WASM_ARRAY_SET(array16_index, WASM_LOCAL_GET(1), WASM_I32V(0),
WASM_I32V(0)),
WASM_ARRAY_SET(array16_index, WASM_LOCAL_GET(1), WASM_I32V(1),
@ -944,8 +1002,9 @@ WASM_COMPILED_EXEC_TEST(WasmArrayCopy) {
WASM_I32V(2)),
WASM_ARRAY_SET(array16_index, WASM_LOCAL_GET(1), WASM_I32V(3),
WASM_I32V(3)),
WASM_LOCAL_SET(2, WASM_ARRAY_NEW_DEFAULT(array16_index, WASM_I32V(10),
WASM_RTT_CANON(array16_index))),
WASM_LOCAL_SET(
2, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array16_index, WASM_I32V(10),
WASM_RTT_CANON(array16_index))),
WASM_ARRAY_COPY(array16_index, array16_index, WASM_LOCAL_GET(2),
WASM_I32V(6), WASM_LOCAL_GET(1), WASM_I32V(0),
WASM_I32V(4)),
@ -956,24 +1015,28 @@ WASM_COMPILED_EXEC_TEST(WasmArrayCopy) {
const byte kCopyRef = tester.DefineFunction(
FunctionSig::Build(tester.zone(), {optref(array32_index)}, {kWasmI32}),
{optref(arrayref_index), optref(arrayref_index)},
{WASM_LOCAL_SET(1,
WASM_ARRAY_NEW_DEFAULT(arrayref_index, WASM_I32V(10),
WASM_RTT_CANON(arrayref_index))),
WASM_ARRAY_SET(arrayref_index, WASM_LOCAL_GET(1), WASM_I32V(0),
WASM_ARRAY_NEW_DEFAULT(array32_index, WASM_I32V(6),
WASM_RTT_CANON(array32_index))),
WASM_ARRAY_SET(arrayref_index, WASM_LOCAL_GET(1), WASM_I32V(1),
WASM_ARRAY_NEW_DEFAULT(array32_index, WASM_I32V(7),
WASM_RTT_CANON(array32_index))),
WASM_ARRAY_SET(arrayref_index, WASM_LOCAL_GET(1), WASM_I32V(2),
WASM_ARRAY_NEW_DEFAULT(array32_index, WASM_I32V(8),
WASM_RTT_CANON(array32_index))),
WASM_ARRAY_SET(arrayref_index, WASM_LOCAL_GET(1), WASM_I32V(3),
WASM_ARRAY_NEW_DEFAULT(array32_index, WASM_I32V(9),
WASM_RTT_CANON(array32_index))),
WASM_LOCAL_SET(2,
WASM_ARRAY_NEW_DEFAULT(arrayref_index, WASM_I32V(10),
WASM_RTT_CANON(arrayref_index))),
{WASM_LOCAL_SET(
1, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(arrayref_index, WASM_I32V(10),
WASM_RTT_CANON(arrayref_index))),
WASM_ARRAY_SET(
arrayref_index, WASM_LOCAL_GET(1), WASM_I32V(0),
WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array32_index, WASM_I32V(6),
WASM_RTT_CANON(array32_index))),
WASM_ARRAY_SET(
arrayref_index, WASM_LOCAL_GET(1), WASM_I32V(1),
WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array32_index, WASM_I32V(7),
WASM_RTT_CANON(array32_index))),
WASM_ARRAY_SET(
arrayref_index, WASM_LOCAL_GET(1), WASM_I32V(2),
WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array32_index, WASM_I32V(8),
WASM_RTT_CANON(array32_index))),
WASM_ARRAY_SET(
arrayref_index, WASM_LOCAL_GET(1), WASM_I32V(3),
WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array32_index, WASM_I32V(9),
WASM_RTT_CANON(array32_index))),
WASM_LOCAL_SET(
2, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(arrayref_index, WASM_I32V(10),
WASM_RTT_CANON(arrayref_index))),
WASM_ARRAY_COPY(arrayref_index, arrayref_index, WASM_LOCAL_GET(2),
WASM_I32V(6), WASM_LOCAL_GET(1), WASM_I32V(0),
WASM_I32V(4)),
@ -984,21 +1047,25 @@ WASM_COMPILED_EXEC_TEST(WasmArrayCopy) {
const byte kCopyRefOverlapping = tester.DefineFunction(
FunctionSig::Build(tester.zone(), {optref(array32_index)}, {kWasmI32}),
{optref(arrayref_index)},
{WASM_LOCAL_SET(1,
WASM_ARRAY_NEW_DEFAULT(arrayref_index, WASM_I32V(10),
WASM_RTT_CANON(arrayref_index))),
WASM_ARRAY_SET(arrayref_index, WASM_LOCAL_GET(1), WASM_I32V(0),
WASM_ARRAY_NEW_DEFAULT(array32_index, WASM_I32V(2),
WASM_RTT_CANON(array32_index))),
WASM_ARRAY_SET(arrayref_index, WASM_LOCAL_GET(1), WASM_I32V(1),
WASM_ARRAY_NEW_DEFAULT(array32_index, WASM_I32V(3),
WASM_RTT_CANON(array32_index))),
WASM_ARRAY_SET(arrayref_index, WASM_LOCAL_GET(1), WASM_I32V(2),
WASM_ARRAY_NEW_DEFAULT(array32_index, WASM_I32V(4),
WASM_RTT_CANON(array32_index))),
WASM_ARRAY_SET(arrayref_index, WASM_LOCAL_GET(1), WASM_I32V(3),
WASM_ARRAY_NEW_DEFAULT(array32_index, WASM_I32V(5),
WASM_RTT_CANON(array32_index))),
{WASM_LOCAL_SET(
1, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(arrayref_index, WASM_I32V(10),
WASM_RTT_CANON(arrayref_index))),
WASM_ARRAY_SET(
arrayref_index, WASM_LOCAL_GET(1), WASM_I32V(0),
WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array32_index, WASM_I32V(2),
WASM_RTT_CANON(array32_index))),
WASM_ARRAY_SET(
arrayref_index, WASM_LOCAL_GET(1), WASM_I32V(1),
WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array32_index, WASM_I32V(3),
WASM_RTT_CANON(array32_index))),
WASM_ARRAY_SET(
arrayref_index, WASM_LOCAL_GET(1), WASM_I32V(2),
WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array32_index, WASM_I32V(4),
WASM_RTT_CANON(array32_index))),
WASM_ARRAY_SET(
arrayref_index, WASM_LOCAL_GET(1), WASM_I32V(3),
WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array32_index, WASM_I32V(5),
WASM_RTT_CANON(array32_index))),
WASM_ARRAY_COPY(arrayref_index, arrayref_index, WASM_LOCAL_GET(1),
WASM_I32V(2), WASM_LOCAL_GET(1), WASM_I32V(0),
WASM_I32V(4)),
@ -1007,10 +1074,12 @@ WASM_COMPILED_EXEC_TEST(WasmArrayCopy) {
const byte kOobSource = tester.DefineFunction(
tester.sigs.v_v(), {optref(array32_index), optref(array32_index)},
{WASM_LOCAL_SET(0, WASM_ARRAY_NEW_DEFAULT(array32_index, WASM_I32V(10),
WASM_RTT_CANON(array32_index))),
WASM_LOCAL_SET(1, WASM_ARRAY_NEW_DEFAULT(array32_index, WASM_I32V(10),
WASM_RTT_CANON(array32_index))),
{WASM_LOCAL_SET(
0, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array32_index, WASM_I32V(10),
WASM_RTT_CANON(array32_index))),
WASM_LOCAL_SET(
1, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array32_index, WASM_I32V(10),
WASM_RTT_CANON(array32_index))),
WASM_ARRAY_COPY(array32_index, array32_index, WASM_LOCAL_GET(1),
WASM_I32V(6), WASM_LOCAL_GET(0), WASM_I32V(8),
WASM_I32V(4)),
@ -1018,10 +1087,12 @@ WASM_COMPILED_EXEC_TEST(WasmArrayCopy) {
const byte kOobDestination = tester.DefineFunction(
tester.sigs.v_v(), {optref(array32_index), optref(array32_index)},
{WASM_LOCAL_SET(0, WASM_ARRAY_NEW_DEFAULT(array32_index, WASM_I32V(10),
WASM_RTT_CANON(array32_index))),
WASM_LOCAL_SET(1, WASM_ARRAY_NEW_DEFAULT(array32_index, WASM_I32V(10),
WASM_RTT_CANON(array32_index))),
{WASM_LOCAL_SET(
0, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array32_index, WASM_I32V(10),
WASM_RTT_CANON(array32_index))),
WASM_LOCAL_SET(
1, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array32_index, WASM_I32V(10),
WASM_RTT_CANON(array32_index))),
WASM_ARRAY_COPY(array32_index, array32_index, WASM_LOCAL_GET(1),
WASM_I32V(6), WASM_LOCAL_GET(0), WASM_I32V(3),
WASM_I32V(5)),
@ -1079,8 +1150,8 @@ WASM_COMPILED_EXEC_TEST(NewDefault) {
// Returns: struct[0] + f64_to_i32(struct[1]) + (struct[2].is_null ^ 1) == 0.
const byte allocate_struct = tester.DefineFunction(
tester.sigs.i_v(), {optref(struct_type)},
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_DEFAULT(struct_type,
WASM_RTT_CANON(struct_type))),
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
struct_type, WASM_RTT_CANON(struct_type))),
WASM_I32_ADD(
WASM_I32_ADD(WASM_STRUCT_GET(struct_type, 0, WASM_LOCAL_GET(0)),
WASM_I32_SCONVERT_F64(WASM_STRUCT_GET(
@ -1091,8 +1162,9 @@ WASM_COMPILED_EXEC_TEST(NewDefault) {
kExprEnd});
const byte allocate_array = tester.DefineFunction(
tester.sigs.i_v(), {optref(array_type)},
{WASM_LOCAL_SET(0, WASM_ARRAY_NEW_DEFAULT(array_type, WASM_I32V(2),
WASM_RTT_CANON(array_type))),
{WASM_LOCAL_SET(
0, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array_type, WASM_I32V(2),
WASM_RTT_CANON(array_type))),
WASM_I32_ADD(
WASM_ARRAY_GET(array_type, WASM_LOCAL_GET(0), WASM_I32V(0)),
WASM_ARRAY_GET(array_type, WASM_LOCAL_GET(0), WASM_I32V(1))),
@ -1227,7 +1299,7 @@ WASM_COMPILED_EXEC_TEST(RefTrivialCasts) {
const byte kRefTestUpcast = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_TEST(
WASM_STRUCT_NEW_DEFAULT(
WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
subtype_index,
WASM_RTT_SUB(subtype_index, WASM_RTT_CANON(type_index))),
WASM_RTT_CANON(type_index)),
@ -1239,7 +1311,7 @@ WASM_COMPILED_EXEC_TEST(RefTrivialCasts) {
const byte kRefTestUnrelated = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_TEST(
WASM_STRUCT_NEW_DEFAULT(
WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
subtype_index,
WASM_RTT_SUB(subtype_index, WASM_RTT_CANON(type_index))),
WASM_RTT_CANON(sig_index)),
@ -1250,9 +1322,9 @@ WASM_COMPILED_EXEC_TEST(RefTrivialCasts) {
kExprEnd});
const byte kRefTestUnrelatedNonNullable = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_TEST(
WASM_STRUCT_NEW_DEFAULT(type_index, WASM_RTT_CANON(type_index)),
WASM_RTT_CANON(sig_index)),
{WASM_REF_TEST(WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
type_index, WASM_RTT_CANON(type_index)),
WASM_RTT_CANON(sig_index)),
kExprEnd});
const byte kRefCastNull = tester.DefineFunction(
@ -1263,7 +1335,7 @@ WASM_COMPILED_EXEC_TEST(RefTrivialCasts) {
const byte kRefCastUpcast = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_NULL(WASM_REF_CAST(
WASM_STRUCT_NEW_DEFAULT(
WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
subtype_index,
WASM_RTT_SUB(subtype_index, WASM_RTT_CANON(type_index))),
WASM_RTT_CANON(type_index))),
@ -1276,7 +1348,7 @@ WASM_COMPILED_EXEC_TEST(RefTrivialCasts) {
const byte kRefCastUnrelated = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_NULL(WASM_REF_CAST(
WASM_STRUCT_NEW_DEFAULT(
WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
subtype_index,
WASM_RTT_SUB(subtype_index, WASM_RTT_CANON(type_index))),
WASM_RTT_CANON(sig_index))),
@ -1286,11 +1358,95 @@ WASM_COMPILED_EXEC_TEST(RefTrivialCasts) {
{WASM_REF_IS_NULL(WASM_REF_CAST(WASM_REF_NULL(subtype_index),
WASM_RTT_CANON(sig_index))),
kExprEnd});
const byte kRefCastUnrelatedNonNullable =
tester.DefineFunction(tester.sigs.i_v(), {},
{WASM_REF_IS_NULL(WASM_REF_CAST(
WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
type_index, WASM_RTT_CANON(type_index)),
WASM_RTT_CANON(sig_index))),
kExprEnd});
tester.CompileModule();
tester.CheckResult(kRefTestNull, 0);
tester.CheckResult(kRefTestUpcast, 1);
tester.CheckResult(kRefTestUpcastNull, 0);
tester.CheckResult(kRefTestUnrelated, 0);
tester.CheckResult(kRefTestUnrelatedNull, 0);
tester.CheckResult(kRefTestUnrelatedNonNullable, 0);
tester.CheckResult(kRefCastNull, 1);
tester.CheckResult(kRefCastUpcast, 0);
tester.CheckResult(kRefCastUpcastNull, 1);
tester.CheckHasThrown(kRefCastUnrelated);
tester.CheckResult(kRefCastUnrelatedNull, 1);
tester.CheckHasThrown(kRefCastUnrelatedNonNullable);
}
WASM_COMPILED_EXEC_TEST(RefTrivialCastsStatic) {
WasmGCTester tester(execution_tier);
byte type_index =
tester.DefineStruct({F(wasm::kWasmI32, true)}, kGenericSuperType);
byte subtype_index = tester.DefineStruct(
{F(wasm::kWasmI32, true), F(wasm::kWasmS128, false)}, type_index);
ValueType sig_types[] = {kWasmS128, kWasmI32, kWasmF64};
FunctionSig sig(1, 2, sig_types);
byte sig_index = tester.DefineSignature(&sig);
const byte kRefTestNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_TEST_STATIC(WASM_REF_NULL(type_index), subtype_index),
kExprEnd});
const byte kRefTestUpcast = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_TEST_STATIC(WASM_STRUCT_NEW_DEFAULT(subtype_index), type_index),
kExprEnd});
const byte kRefTestUpcastNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_TEST_STATIC(WASM_REF_NULL(subtype_index), type_index),
kExprEnd});
const byte kRefTestUnrelated = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_TEST_STATIC(WASM_STRUCT_NEW_DEFAULT(subtype_index), sig_index),
kExprEnd});
const byte kRefTestUnrelatedNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_TEST_STATIC(WASM_REF_NULL(subtype_index), sig_index),
kExprEnd});
const byte kRefTestUnrelatedNonNullable = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_TEST_STATIC(WASM_STRUCT_NEW_DEFAULT(type_index), sig_index),
kExprEnd});
const byte kRefCastNull =
tester.DefineFunction(tester.sigs.i_v(), {},
{WASM_REF_IS_NULL(WASM_REF_CAST_STATIC(
WASM_REF_NULL(type_index), subtype_index)),
kExprEnd});
const byte kRefCastUpcast = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_NULL(WASM_REF_CAST_STATIC(
WASM_STRUCT_NEW_DEFAULT(subtype_index), type_index)),
kExprEnd});
const byte kRefCastUpcastNull =
tester.DefineFunction(tester.sigs.i_v(), {},
{WASM_REF_IS_NULL(WASM_REF_CAST_STATIC(
WASM_REF_NULL(subtype_index), type_index)),
kExprEnd});
const byte kRefCastUnrelated = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_NULL(WASM_REF_CAST_STATIC(
WASM_STRUCT_NEW_DEFAULT(subtype_index), sig_index)),
kExprEnd});
const byte kRefCastUnrelatedNull =
tester.DefineFunction(tester.sigs.i_v(), {},
{WASM_REF_IS_NULL(WASM_REF_CAST_STATIC(
WASM_REF_NULL(subtype_index), sig_index)),
kExprEnd});
const byte kRefCastUnrelatedNonNullable = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_NULL(WASM_REF_CAST(
WASM_STRUCT_NEW_DEFAULT(type_index, WASM_RTT_CANON(type_index)),
WASM_RTT_CANON(sig_index))),
{WASM_REF_IS_NULL(WASM_REF_CAST_STATIC(
WASM_STRUCT_NEW_DEFAULT(type_index), sig_index)),
kExprEnd});
tester.CompileModule();
@ -1404,6 +1560,9 @@ WASM_COMPILED_EXEC_TEST(ArrayNewMap) {
{WASM_ARRAY_NEW_WITH_RTT(type_index, WASM_I32V(10), WASM_I32V(42),
WASM_RTT_CANON(type_index)),
kExprEnd});
const byte array_new_nominal = tester.DefineFunction(
&sig, {},
{WASM_ARRAY_NEW(type_index, WASM_I32V(10), WASM_I32V(42)), kExprEnd});
ValueType rtt_type = ValueType::Rtt(type_index, 0);
FunctionSig rtt_canon_sig(1, 0, &rtt_type);
@ -1418,6 +1577,10 @@ WASM_COMPILED_EXEC_TEST(ArrayNewMap) {
tester.GetResultObject(array_new_with_rtt).ToHandleChecked();
CHECK(result->IsWasmArray());
CHECK_EQ(Handle<WasmArray>::cast(result)->map(), *map);
result = tester.GetResultObject(array_new_nominal).ToHandleChecked();
CHECK(result->IsWasmArray());
CHECK_EQ(Handle<WasmArray>::cast(result)->map(), *map);
}
WASM_COMPILED_EXEC_TEST(FunctionRefs) {
@ -1593,18 +1756,18 @@ WASM_COMPILED_EXEC_TEST(AbstractTypeChecks) {
{WASM_LOCAL_SET(0, WASM_SEQ(value)), \
WASM_REF_IS_##type(WASM_LOCAL_GET(0)), kExprEnd})
byte kDataCheckSuccess =
TYPE_CHECK(DATA, WASM_ARRAY_NEW_DEFAULT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
byte kDataCheckSuccess = TYPE_CHECK(
DATA, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
byte kDataCheckFailure = TYPE_CHECK(DATA, WASM_I31_NEW(WASM_I32V(42)));
byte kFuncCheckSuccess = TYPE_CHECK(FUNC, WASM_REF_FUNC(function_index));
byte kFuncCheckFailure =
TYPE_CHECK(FUNC, WASM_ARRAY_NEW_DEFAULT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
byte kFuncCheckFailure = TYPE_CHECK(
FUNC, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
byte kI31CheckSuccess = TYPE_CHECK(I31, WASM_I31_NEW(WASM_I32V(42)));
byte kI31CheckFailure =
TYPE_CHECK(I31, WASM_ARRAY_NEW_DEFAULT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
byte kI31CheckFailure = TYPE_CHECK(
I31, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
#undef TYPE_CHECK
#define TYPE_CAST(type, value) \
@ -1613,18 +1776,18 @@ WASM_COMPILED_EXEC_TEST(AbstractTypeChecks) {
WASM_REF_AS_##type(WASM_LOCAL_GET(0)), WASM_DROP, \
WASM_I32V(1), kExprEnd})
byte kDataCastSuccess =
TYPE_CAST(DATA, WASM_ARRAY_NEW_DEFAULT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
byte kDataCastSuccess = TYPE_CAST(
DATA, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
byte kDataCastFailure = TYPE_CAST(DATA, WASM_I31_NEW(WASM_I32V(42)));
byte kFuncCastSuccess = TYPE_CAST(FUNC, WASM_REF_FUNC(function_index));
byte kFuncCastFailure =
TYPE_CAST(FUNC, WASM_ARRAY_NEW_DEFAULT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
byte kI31CastSuccess = TYPE_CAST(I31, WASM_I31_NEW(WASM_I32V(42)));
byte kI31CastFailure =
TYPE_CAST(I31, WASM_ARRAY_NEW_DEFAULT(array_index, WASM_I32V(10),
byte kFuncCastFailure = TYPE_CAST(
FUNC, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
byte kI31CastSuccess = TYPE_CAST(I31, WASM_I31_NEW(WASM_I32V(42)));
byte kI31CastFailure = TYPE_CAST(
I31, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
#undef TYPE_CAST
// If the branch is not taken, we return 0. If it is taken, then the respective
@ -1640,16 +1803,16 @@ WASM_COMPILED_EXEC_TEST(AbstractTypeChecks) {
byte kBrOnDataTaken =
BR_ON(DATA, Data,
WASM_ARRAY_NEW_DEFAULT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
byte kBrOnDataNotTaken = BR_ON(DATA, Data, WASM_REF_FUNC(function_index));
byte kBrOnFuncTaken = BR_ON(FUNC, Func, WASM_REF_FUNC(function_index));
byte kBrOnFuncNotTaken = BR_ON(FUNC, Func, WASM_I31_NEW(WASM_I32V(42)));
byte kBrOnI31Taken = BR_ON(I31, I31, WASM_I31_NEW(WASM_I32V(42)));
byte kBrOnI31NotTaken =
BR_ON(I31, I31,
WASM_ARRAY_NEW_DEFAULT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
#undef BR_ON
// If the branch is not taken, we return 1. If it is taken, then the respective
@ -1665,8 +1828,8 @@ WASM_COMPILED_EXEC_TEST(AbstractTypeChecks) {
byte kBrOnNonDataNotTaken =
BR_ON_NON(DATA, Data,
WASM_ARRAY_NEW_DEFAULT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
byte kBrOnNonDataTaken = BR_ON_NON(DATA, Data, WASM_REF_FUNC(function_index));
byte kBrOnNonFuncNotTaken =
BR_ON_NON(FUNC, Func, WASM_REF_FUNC(function_index));
@ -1674,8 +1837,8 @@ WASM_COMPILED_EXEC_TEST(AbstractTypeChecks) {
byte kBrOnNonI31NotTaken = BR_ON_NON(I31, I31, WASM_I31_NEW(WASM_I32V(42)));
byte kBrOnNonI31Taken =
BR_ON_NON(I31, I31,
WASM_ARRAY_NEW_DEFAULT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
WASM_ARRAY_NEW_DEFAULT_WITH_RTT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
#undef BR_ON_NON
tester.CompileModule();
@ -1769,9 +1932,9 @@ WASM_COMPILED_EXEC_TEST(CastsBenchmark) {
const byte Prepare = tester.DefineFunction(
tester.sigs.i_v(), {wasm::kWasmI32},
{// List = new eqref[kListLength];
WASM_GLOBAL_SET(List,
WASM_ARRAY_NEW_DEFAULT(ListType, WASM_I32V(kListLength),
WASM_GLOBAL_GET(RttList))),
WASM_GLOBAL_SET(List, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(
ListType, WASM_I32V(kListLength),
WASM_GLOBAL_GET(RttList))),
// for (int i = 0; i < kListLength; ) {
// List[i] = new Super(i);
// i++;

View File

@ -132,7 +132,7 @@ class TestingModuleBuilder {
byte AddSignature(const FunctionSig* sig) {
DCHECK_EQ(test_module_->types.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();
CHECK_GT(127, size);
return static_cast<byte>(size - 1);

View File

@ -491,10 +491,14 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
// Heap-allocated object operations.
//------------------------------------------------------------------------------
#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, ...) \
__VA_ARGS__, WASM_GC_OP(kExprStructNewWithRtt), static_cast<byte>(index)
#define WASM_STRUCT_NEW_DEFAULT(index, rtt) \
rtt, WASM_GC_OP(kExprStructNewDefault), static_cast<byte>(index)
#define WASM_STRUCT_NEW_DEFAULT(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) \
struct_obj, WASM_GC_OP(kExprStructGet), static_cast<byte>(typeidx), \
static_cast<byte>(fieldidx)
@ -513,13 +517,23 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_REF_AS_NON_NULL(val) val, kExprRefAsNonNull
#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_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_STATIC(ref, typeidx) \
ref, WASM_GC_OP(kExprRefCastStatic), static_cast<byte>(typeidx)
// Takes a reference value from the value stack to allow sequences of
// conditional branches.
#define WASM_BR_ON_CAST(depth, rtt) \
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) \
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_DATA(ref) ref, WASM_GC_OP(kExprRefIsData)
@ -539,11 +553,15 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_BR_ON_NON_I31(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) \
default_value, length, rtt, WASM_GC_OP(kExprArrayNewWithRtt), \
static_cast<byte>(index)
#define WASM_ARRAY_NEW_DEFAULT(index, length, rtt) \
length, rtt, WASM_GC_OP(kExprArrayNewDefault), static_cast<byte>(index)
#define WASM_ARRAY_NEW_DEFAULT(index, length) \
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) \
array, index, WASM_GC_OP(kExprArrayGet), static_cast<byte>(typeidx)
#define WASM_ARRAY_GET_U(typeidx, array, index) \
@ -562,6 +580,9 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_ARRAY_INIT(index, length, ...) \
__VA_ARGS__, WASM_GC_OP(kExprArrayInit), static_cast<byte>(index), \
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) \
kRttWithDepthCode, U32V_1(depth), U32V_1(typeidx)

View File

@ -810,7 +810,7 @@ class WasmGenerator {
if (new_default) {
builder_->EmitWithPrefix(kExprRttCanon);
builder_->EmitU32V(index);
builder_->EmitWithPrefix(kExprStructNewDefault);
builder_->EmitWithPrefix(kExprStructNewDefaultWithRtt);
builder_->EmitU32V(index);
} else {
StructType* struct_gen = builder_->builder()->GetStructType(index);
@ -828,7 +828,7 @@ class WasmGenerator {
Generate(kWasmI32, data);
builder_->EmitWithPrefix(kExprRttCanon);
builder_->EmitU32V(index);
builder_->EmitWithPrefix(kExprArrayNewDefault);
builder_->EmitWithPrefix(kExprArrayNewDefaultWithRtt);
builder_->EmitU32V(index);
} else {
Generate(builder_->builder()->GetArrayType(index)->element_type(),

View File

@ -14,7 +14,7 @@ builder.addFunction("main", kSig_i_i)
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprRttCanon, array_index,
kGCPrefix, kExprArrayNewDefault, array_index,
kGCPrefix, kExprArrayNewDefaultWithRtt, array_index,
kGCPrefix, kExprArrayLen, array_index,
])
.exportFunc();

View File

@ -32,11 +32,11 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
.addBody([
...wasmI32Const(array_length),
kGCPrefix, kExprRttCanon, array_index,
kGCPrefix, kExprArrayNewDefault, array_index,
kGCPrefix, kExprArrayNewDefaultWithRtt, array_index,
kExprGlobalSet, from.index,
...wasmI32Const(array_length),
kGCPrefix, kExprRttCanon, array_index,
kGCPrefix, kExprArrayNewDefault, array_index,
kGCPrefix, kExprArrayNewDefaultWithRtt, array_index,
kExprGlobalSet, to.index
])
.exportFunc();

View File

@ -6,26 +6,49 @@
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
var builder = new WasmModuleBuilder();
let struct1 = builder.addStruct([makeField(kWasmI32, true)]);
let struct2 = builder.addStructExtending(
[makeField(kWasmI32, true), makeField(kWasmI32, true)], struct1);
(function() {
var builder = new WasmModuleBuilder();
let struct1 = builder.addStructSubtype([makeField(kWasmI32, true)]);
let struct2 = builder.addStructSubtype(
[makeField(kWasmI32, true), makeField(kWasmI32, true)], struct1);
let array1 = builder.addArray(kWasmI32, true);
let array2 = builder.addArrayExtending(kWasmI32, true, array1);
let array1 = builder.addArraySubtype(kWasmI32, true);
let array2 = builder.addArraySubtype(kWasmI32, true, array1);
builder.addFunction("main", kSig_v_v)
.addLocals(wasmOptRefType(struct1), 1)
.addLocals(wasmOptRefType(array1), 1)
.addBody([
kGCPrefix, kExprRttCanon, struct2,
kGCPrefix, kExprStructNewDefault, struct2,
builder.addFunction("main", kSig_v_v)
.addLocals(wasmOptRefType(struct1), 1)
.addLocals(wasmOptRefType(array1), 1)
.addBody([
// Check that we can create a struct with explicit RTT...
kGCPrefix, kExprRttCanon, struct2, kGCPrefix,
kExprStructNewDefaultWithRtt, struct2,
// ...and upcast it.
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
kGCPrefix, kExprRttCanon, array2,
kGCPrefix, kExprArrayNewDefault, array2,
kExprLocalSet, 1
]);
kGCPrefix, kExprRttCanon, array2, kGCPrefix,
kExprArrayNewDefaultWithRtt, array2,
// ...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.
builder.instantiate();
})();
(function () {
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/);
})();

View File

@ -182,7 +182,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
.addBody([
kExprI32Const, 5,
kGCPrefix, kExprRttCanon, array,
kGCPrefix, kExprArrayNewDefault, array,
kGCPrefix, kExprArrayNewDefaultWithRtt, array,
kExprLocalSet, 1,
kExprLocalGet, 1, // a[i] = i for i = {0..4}
@ -308,7 +308,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
builder.addFunction("main", kSig_i_i)
.addBody([
kExprI32Const, 10, kGCPrefix, kExprRttCanon, array,
kGCPrefix, kExprArrayNewDefault, array,
kGCPrefix, kExprArrayNewDefaultWithRtt, array,
kExprI32Const, 7,
kExprCallFunction, tester.index,
])

View File

@ -18,15 +18,15 @@ let instance = (() => {
builder.addFunction('struct_producer', makeSig([], [kWasmDataRef]))
.addBody([
kGCPrefix, kExprRttCanon, struct, kGCPrefix, kExprStructNewDefault,
struct
kGCPrefix, kExprRttCanon, struct, kGCPrefix,
kExprStructNewDefaultWithRtt, struct
])
.exportFunc();
builder.addFunction('array_producer', makeSig([], [kWasmDataRef]))
.addBody([
kExprI32Const, 10, kGCPrefix, kExprRttCanon, array, kGCPrefix,
kExprArrayNewDefault, array
kExprArrayNewDefaultWithRtt, array
])
.exportFunc();

View File

@ -77,9 +77,9 @@ let kLocalNamesCode = 2;
let kWasmFunctionTypeForm = 0x60;
let kWasmStructTypeForm = 0x5f;
let kWasmArrayTypeForm = 0x5e;
let kWasmFunctionExtendingTypeForm = 0x5d;
let kWasmStructExtendingTypeForm = 0x5c;
let kWasmArrayExtendingTypeForm = 0x5b;
let kWasmFunctionSubtypeForm = 0x5d;
let kWasmStructSubtypeForm = 0x5c;
let kWasmArraySubtypeForm = 0x5b;
let kLimitsNoMaximum = 0x00;
let kLimitsWithMaximum = 0x01;
@ -469,13 +469,15 @@ for (let prefix in kPrefixOpcodes) {
// GC opcodes
let kExprStructNewWithRtt = 0x01;
let kExprStructNewDefault = 0x02;
let kExprStructNewDefaultWithRtt = 0x02;
let kExprStructGet = 0x03;
let kExprStructGetS = 0x04;
let kExprStructGetU = 0x05;
let kExprStructSet = 0x06;
let kExprStructNew = 0x07;
let kExprStructNewDefault = 0x08;
let kExprArrayNewWithRtt = 0x11;
let kExprArrayNewDefault = 0x12;
let kExprArrayNewDefaultWithRtt = 0x12;
let kExprArrayGet = 0x13;
let kExprArrayGetS = 0x14;
let kExprArrayGetU = 0x15;
@ -483,6 +485,9 @@ let kExprArraySet = 0x16;
let kExprArrayLen = 0x17;
let kExprArrayCopy = 0x18;
let kExprArrayInit = 0x19;
let kExprArrayInitStatic = 0x1a;
let kExprArrayNew = 0x1b;
let kExprArrayNewDefault = 0x1c;
let kExprI31New = 0x20;
let kExprI31GetS = 0x21;
let kExprI31GetU = 0x22;
@ -493,6 +498,10 @@ let kExprRefTest = 0x40;
let kExprRefCast = 0x41;
let kExprBrOnCast = 0x42;
let kExprBrOnCastFail = 0x43;
let kExprRefTestStatic = 0x44;
let kExprRefCastStatic = 0x45;
let kExprBrOnCastStatic = 0x46;
let kExprBrOnCastStaticFail = 0x47;
let kExprRefIsFunc = 0x50;
let kExprRefIsData = 0x51;
let kExprRefIsI31 = 0x52;
@ -1004,20 +1013,22 @@ class Binary {
this.emit_u8(kExprRefNull);
this.emit_heap_type(expr.value);
break;
case kExprStructNew:
case kExprStructNewWithRtt:
for (let operand of expr.operands) {
this.emit_init_expr_recursive(operand);
}
this.emit_u8(kGCPrefix);
this.emit_u8(kExprStructNewWithRtt);
this.emit_u8(expr.kind);
this.emit_u32v(expr.value);
break;
case kExprArrayInit:
case kExprArrayInitStatic:
for (let operand of expr.operands) {
this.emit_init_expr_recursive(operand);
}
this.emit_u8(kGCPrefix);
this.emit_u8(kExprArrayInit);
this.emit_u8(expr.kind);
this.emit_u32v(expr.value);
this.emit_u32v(expr.operands.length - 1);
break;
@ -1170,9 +1181,15 @@ class WasmInitExpr {
static StructNewWithRtt(type, args) {
return {kind: kExprStructNewWithRtt, value: type, operands: args};
}
static StructNew(type, args) {
return {kind: kExprStructNew, value: type, operands: args};
}
static ArrayInit(type, args) {
return {kind: kExprArrayInit, value: type, operands: args};
}
static ArrayInitStatic(type, args) {
return {kind: kExprArrayInitStatic, value: type, operands: args};
}
static RttCanon(type) {
return {kind: kExprRttCanon, value: type};
}
@ -1256,11 +1273,11 @@ class WasmStruct {
}
}
class WasmStructExtending extends WasmStruct {
class WasmStructSubtype extends WasmStruct {
constructor(fields, supertype_idx) {
super(fields);
this.supertype = supertype_idx;
this.type_form = kWasmStructExtendingTypeForm;
this.type_form = kWasmStructSubtypeForm;
}
}
@ -1273,11 +1290,11 @@ class WasmArray {
}
}
class WasmArrayExtending extends WasmArray {
class WasmArraySubtype extends WasmArray {
constructor(type, mutability, supertype_idx) {
super(type, mutability);
this.supertype = supertype_idx;
this.type_form = kWasmArrayExtendingTypeForm;
this.type_form = kWasmArraySubtypeForm;
}
}
class WasmElemSegment {
@ -1402,8 +1419,9 @@ class WasmModuleBuilder {
return this.types.length - 1;
}
addStructExtending(fields, supertype_idx) {
this.types.push(new WasmStructExtending(fields, supertype_idx));
kGenericSuperType = 0xFFFFFFFE;
addStructSubtype(fields, supertype_idx = this.kGenericSuperType) {
this.types.push(new WasmStructSubtype(fields, supertype_idx));
return this.types.length - 1;
}
@ -1412,8 +1430,8 @@ class WasmModuleBuilder {
return this.types.length - 1;
}
addArrayExtending(type, mutability, supertype_idx) {
this.types.push(new WasmArrayExtending(type, mutability, supertype_idx));
addArraySubtype(type, mutability, supertype_idx = this.kGenericSuperType) {
this.types.push(new WasmArraySubtype(type, mutability, supertype_idx));
return this.types.length - 1;
}
@ -1651,15 +1669,23 @@ class WasmModuleBuilder {
section.emit_type(field.type);
section.emit_u8(field.mutability ? 1 : 0);
}
if (type instanceof WasmStructExtending) {
section.emit_u32v(type.supertype);
if (type instanceof WasmStructSubtype) {
if (type.supertype === this.kGenericSuperType) {
section.emit_u8(kDataRefCode);
} else {
section.emit_heap_type(type.supertype);
}
}
} else if (type instanceof WasmArray) {
section.emit_u8(type.type_form);
section.emit_type(type.type);
section.emit_u8(type.mutability ? 1 : 0);
if (type instanceof WasmArrayExtending) {
section.emit_u32v(type.supertype);
if (type instanceof WasmArraySubtype) {
if (type.supertype === this.kGenericSuperType) {
section.emit_u8(kDataRefCode);
} else {
section.emit_heap_type(type.supertype);
}
}
} else {
section.emit_u8(kWasmFunctionTypeForm);

View File

@ -87,7 +87,7 @@ class TestModuleBuilder {
return static_cast<byte>(mod.globals.size() - 1);
}
byte AddSignature(const FunctionSig* sig) {
mod.add_signature(sig);
mod.add_signature(sig, kNoSuperType);
CHECK_LE(mod.types.size(), kMaxByteSizedLeb128);
return static_cast<byte>(mod.types.size() - 1);
}
@ -127,19 +127,20 @@ class TestModuleBuilder {
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(),
static_cast<uint32_t>(fields.size()));
for (F field : fields) {
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);
}
byte AddArray(ValueType type, bool 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);
}
@ -1123,11 +1124,12 @@ TEST_F(FunctionBodyDecoderTest, UnreachableRefTypes) {
WASM_GC_OP(kExprStructNewWithRtt), struct_index,
kExprCallFunction, struct_consumer});
ExpectValidates(sigs.v_v(),
{WASM_UNREACHABLE, WASM_GC_OP(kExprStructNewDefault),
{WASM_UNREACHABLE, WASM_GC_OP(kExprStructNewDefaultWithRtt),
struct_index, kExprDrop});
ExpectValidates(sigs.v_v(), {WASM_UNREACHABLE, WASM_RTT_CANON(struct_index),
WASM_GC_OP(kExprStructNewDefault), struct_index,
kExprCallFunction, struct_consumer});
ExpectValidates(sigs.v_v(),
{WASM_UNREACHABLE, WASM_RTT_CANON(struct_index),
WASM_GC_OP(kExprStructNewDefaultWithRtt), struct_index,
kExprCallFunction, struct_consumer});
ExpectValidates(sigs.v_v(),
{WASM_UNREACHABLE, WASM_GC_OP(kExprArrayNewWithRtt),
@ -1139,11 +1141,11 @@ TEST_F(FunctionBodyDecoderTest, UnreachableRefTypes) {
{WASM_UNREACHABLE, WASM_I32V(42), WASM_RTT_CANON(array_index),
WASM_GC_OP(kExprArrayNewWithRtt), array_index, kExprDrop});
ExpectValidates(sigs.v_v(),
{WASM_UNREACHABLE, WASM_GC_OP(kExprArrayNewDefault),
{WASM_UNREACHABLE, WASM_GC_OP(kExprArrayNewDefaultWithRtt),
array_index, kExprDrop});
ExpectValidates(sigs.v_v(),
{WASM_UNREACHABLE, WASM_RTT_CANON(array_index),
WASM_GC_OP(kExprArrayNewDefault), array_index, kExprDrop});
ExpectValidates(sigs.v_v(), {WASM_UNREACHABLE, WASM_RTT_CANON(array_index),
WASM_GC_OP(kExprArrayNewDefaultWithRtt),
array_index, kExprDrop});
ExpectValidates(sigs.i_v(), {WASM_UNREACHABLE, WASM_GC_OP(kExprRefTest),
struct_index, struct_index});
@ -1976,10 +1978,10 @@ TEST_F(FunctionBodyDecoderTest, TablesWithFunctionSubtyping) {
// that is a subtype of the table type.
ExpectValidates(
FunctionSig::Build(zone(), {ValueType::Ref(sub_struct, kNullable)}, {}),
{WASM_CALL_INDIRECT_TABLE(
table, function_type,
WASM_STRUCT_NEW_DEFAULT(super_struct, WASM_RTT_CANON(super_struct)),
WASM_ZERO)});
{WASM_CALL_INDIRECT_TABLE(table, function_type,
WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
super_struct, WASM_RTT_CANON(super_struct)),
WASM_ZERO)});
// table.set's subtyping works as expected.
ExpectValidates(sigs.v_i(), {WASM_TABLE_SET(0, WASM_LOCAL_GET(0),
@ -2688,13 +2690,13 @@ TEST_F(FunctionBodyDecoderTest, BrTableSubtyping) {
{F(kWasmI8, true), F(kWasmI16, false), F(kWasmI32, true)});
ExpectValidates(
sigs.v_v(),
{WASM_BLOCK_R(
wasm::ValueType::Ref(supertype1, kNonNullable),
WASM_BLOCK_R(
wasm::ValueType::Ref(supertype2, kNonNullable),
WASM_STRUCT_NEW_DEFAULT(subtype, WASM_RTT_CANON(subtype)),
WASM_BR_TABLE(WASM_I32V(5), 1, BR_TARGET(0), BR_TARGET(1))),
WASM_UNREACHABLE),
{WASM_BLOCK_R(wasm::ValueType::Ref(supertype1, kNonNullable),
WASM_BLOCK_R(wasm::ValueType::Ref(supertype2, kNonNullable),
WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
subtype, WASM_RTT_CANON(subtype)),
WASM_BR_TABLE(WASM_I32V(5), 1, BR_TARGET(0),
BR_TARGET(1))),
WASM_UNREACHABLE),
WASM_DROP});
}
@ -3630,7 +3632,7 @@ ValueType optref(byte type_index) {
return ValueType::Ref(type_index, kNullable);
}
TEST_F(FunctionBodyDecoderTest, StructNewDefault) {
TEST_F(FunctionBodyDecoderTest, StructNewDefaultWithRtt) {
WASM_FEATURE_SCOPE(reftypes);
WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(gc);
@ -3639,12 +3641,12 @@ TEST_F(FunctionBodyDecoderTest, StructNewDefault) {
byte type_index = builder.AddStruct({F(kWasmI32, true)});
byte bad_type_index = builder.AddStruct({F(ref(type_index), true)});
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)),
WASM_DROP});
ExpectFailure(sigs.v_v(),
{WASM_STRUCT_NEW_DEFAULT(bad_type_index,
WASM_RTT_CANON(bad_type_index)),
{WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
bad_type_index, WASM_RTT_CANON(bad_type_index)),
WASM_DROP});
}
{
@ -3653,16 +3655,43 @@ TEST_F(FunctionBodyDecoderTest, StructNewDefault) {
byte bad_type_index = builder.AddArray(ref(type_index), true);
module = builder.module();
ExpectValidates(sigs.v_v(),
{WASM_ARRAY_NEW_DEFAULT(type_index, WASM_I32V(3),
WASM_RTT_CANON(type_index)),
{WASM_ARRAY_NEW_DEFAULT_WITH_RTT(
type_index, WASM_I32V(3), WASM_RTT_CANON(type_index)),
WASM_DROP});
ExpectFailure(sigs.v_v(),
{WASM_ARRAY_NEW_DEFAULT(bad_type_index, WASM_I32V(3),
WASM_RTT_CANON(bad_type_index)),
WASM_DROP});
ExpectFailure(sigs.v_v(), {WASM_ARRAY_NEW_DEFAULT_WITH_RTT(
bad_type_index, WASM_I32V(3),
WASM_RTT_CANON(bad_type_index)),
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) {
WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(reftypes);
@ -4642,11 +4671,11 @@ TEST_F(FunctionBodyDecoderTest, LocalTeeTyping) {
AddLocals(ValueType::Ref(array_type, kNullable), 1);
ExpectFailure(
&sig,
{WASM_LOCAL_TEE(0, WASM_ARRAY_NEW_DEFAULT(array_type, WASM_I32V(5),
WASM_RTT_CANON(array_type)))},
kAppendEnd, "expected (ref 0), got (ref null 0)");
ExpectFailure(&sig,
{WASM_LOCAL_TEE(0, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(
array_type, WASM_I32V(5),
WASM_RTT_CANON(array_type)))},
kAppendEnd, "expected (ref 0), got (ref null 0)");
}
TEST_F(FunctionBodyDecoderTest, MergeNullableTypes) {
@ -4664,7 +4693,7 @@ TEST_F(FunctionBodyDecoderTest, MergeNullableTypes) {
// Regression test for crbug.com/1234453.
ExpectValidates(sigs.v_v(),
{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,
struct_type_index, kExprBr, 0)});
}

View File

@ -39,6 +39,8 @@ namespace module_decoder_unittest {
WASM_STRUCT_NEW_WITH_RTT(index, __VA_ARGS__), kExprEnd
#define WASM_INIT_EXPR_ARRAY_INIT(index, length, ...) \
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 REF_NULL_ELEMENT kExprRefNull, kFuncRefCode, kExprEnd
@ -1116,6 +1118,13 @@ TEST_F(WasmModuleVerifyTest, ArrayInitInitExpr) {
WASM_INIT_EXPR_ARRAY_INIT(0, 3, WASM_I32V(10), WASM_I32V(20),
WASM_I32V(30), WASM_RTT_CANON(0)))};
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[] = {
SECTION(Type, ENTRY_COUNT(2), // --
@ -1234,6 +1243,135 @@ TEST_F(WasmModuleVerifyTest, InvalidStructTypeDef) {
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) {
WASM_FEATURE_SCOPE(reftypes);
WASM_FEATURE_SCOPE(typed_funcref);

View File

@ -26,19 +26,21 @@ void DefineStruct(WasmModule* module, std::initializer_list<FieldInit> fields) {
for (FieldInit field : fields) {
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) {
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,
std::initializer_list<ValueType> params,
std::initializer_list<ValueType> returns) {
module->add_signature(
FunctionSig::Build(module->signature_zone.get(), returns, params));
FunctionSig::Build(module->signature_zone.get(), returns, params),
kNoSuperType);
}
TEST_F(WasmSubtypingTest, Subtyping) {