[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:
parent
62acef549e
commit
bc3b9332ac
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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++) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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_);
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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, _) \
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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++;
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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(),
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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/);
|
||||
})();
|
||||
|
@ -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,
|
||||
])
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)});
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user