fix reading of upgraded pointer lists

This commit is contained in:
David Renshaw 2022-11-23 10:46:22 -05:00 committed by Semisol
parent 257f646ec1
commit 3e2034f45d
Signed by: Semisol
GPG key ID: 0949D3C25C7FD14F
4 changed files with 85 additions and 21 deletions

View file

@ -113,6 +113,38 @@ public class EncodingTest {
} }
} }
@org.junit.Test
public void testUpgradeStructReadAsOld() {
MessageBuilder builder = new MessageBuilder();
Test.TestAnyPointer.Builder root = builder.initRoot(Test.TestAnyPointer.factory);
{
Test.TestNewVersion.Builder newVersion = root.getAnyPointerField().initAs(Test.TestNewVersion.factory);
newVersion.setOld1(123);
newVersion.setOld2("foo");
Test.TestNewVersion.Builder sub = newVersion.initOld3();
sub.setOld1(456);
sub.setOld2("bar");
StructList.Builder<Test.TestNewVersion.UpgradedFromText.Builder> names =
newVersion.initOld4(2);
names.get(0).setTextField("alice");
names.get(1).setTextField("bob");
}
{
Test.TestOldVersion.Reader oldVersion = root.getAnyPointerField().asReader().getAs(Test.TestOldVersion.factory);
Assert.assertEquals(oldVersion.getOld1(), 123);
Assert.assertEquals(oldVersion.getOld2().toString(), "foo");
TextList.Reader names = oldVersion.getOld4();
Assert.assertEquals(names.size(), 2);
Assert.assertEquals("alice", names.get(0).toString());
Assert.assertEquals("bob", names.get(1).toString());
}
}
@org.junit.Test @org.junit.Test
public void testUpgradeStructInBuilder() { public void testUpgradeStructInBuilder() {
MessageBuilder builder = new MessageBuilder(); MessageBuilder builder = new MessageBuilder();

View file

@ -319,6 +319,7 @@ struct TestOldVersion {
old1 @0 :Int64; old1 @0 :Int64;
old2 @1 :Text; old2 @1 :Text;
old3 @2 :TestOldVersion; old3 @2 :TestOldVersion;
old4 @3 :List(Text);
} }
struct TestNewVersion { struct TestNewVersion {
@ -326,9 +327,16 @@ struct TestNewVersion {
old1 @0 :Int64; old1 @0 :Int64;
old2 @1 :Text; old2 @1 :Text;
old3 @2 :TestNewVersion; old3 @2 :TestNewVersion;
new1 @3 :Int64 = 987;
new2 @4 :Text = "baz"; struct UpgradedFromText {
new3 @5 :Data; textField @0 :Text;
int32Field @1 :Int32;
dataField @2 :Data;
}
old4 @3 :List(UpgradedFromText);
new1 @4 :Int64 = 987;
new2 @5 :Text = "baz";
new3 @6 :TestDefaults;
} }
struct TestGenerics(Foo, Bar) { struct TestGenerics(Foo, Bar) {

View file

@ -131,9 +131,13 @@ public class ListReader extends CapTableReader.ReaderContext {
} }
protected <T> T _getPointerElement(FromPointerReader<T> factory, int index) { protected <T> T _getPointerElement(FromPointerReader<T> factory, int index) {
return factory.fromPointerReader(this.segment, return factory.fromPointerReader(
this.segment,
this.capTable, this.capTable,
(this.ptr + (int)((long)index * this.step / Constants.BITS_PER_BYTE)) / Constants.BYTES_PER_WORD, (this.ptr +
(this.structDataSize / Constants.BITS_PER_BYTE) +
(int)((long)index * this.step / Constants.BITS_PER_BYTE))
/ Constants.BYTES_PER_WORD,
this.nestingLimit); this.nestingLimit);
} }

View file

@ -1255,8 +1255,8 @@ final class WireHelpers {
throw new DecodeException("Message contains non-list pointer where list was expected."); throw new DecodeException("Message contains non-list pointer where list was expected.");
} }
byte elementSize = ListPointer.elementSize(resolved.ref); byte oldSize = ListPointer.elementSize(resolved.ref);
switch (elementSize) { switch (oldSize) {
case ElementSize.INLINE_COMPOSITE : { case ElementSize.INLINE_COMPOSITE : {
int wordCount = ListPointer.inlineCompositeWordCount(resolved.ref); int wordCount = ListPointer.inlineCompositeWordCount(resolved.ref);
@ -1269,7 +1269,8 @@ final class WireHelpers {
} }
int size = WirePointer.inlineCompositeListElementCount(tag); int size = WirePointer.inlineCompositeListElementCount(tag);
int dataSize = StructPointer.dataSize(tag);
short ptrCount = (short)StructPointer.ptrCount(tag);
int wordsPerElement = StructPointer.wordSize(tag); int wordsPerElement = StructPointer.wordSize(tag);
if ((long)size * wordsPerElement > wordCount) { if ((long)size * wordsPerElement > wordCount) {
@ -1282,14 +1283,33 @@ final class WireHelpers {
resolved.segment.arena.checkReadLimit(size); resolved.segment.arena.checkReadLimit(size);
} }
// TODO check whether the size is compatible switch (expectedElementSize) {
case ElementSize.VOID: break;
case ElementSize.BIT: {
throw new DecodeException("Found struct list where bit list was expected");
}
case ElementSize.BYTE:
case ElementSize.TWO_BYTES:
case ElementSize.FOUR_BYTES:
case ElementSize.EIGHT_BYTES:
if (dataSize == 0) {
throw new DecodeException(
"Expected a primitive list, but got a list of pointer-only structs");
}
case ElementSize.POINTER:
if (ptrCount == 0) {
throw new DecodeException(
"Expected a pointer list, but got a list of data-only structs");
}
default: break;
}
return factory.constructReader(resolved.segment, capTable, return factory.constructReader(resolved.segment,
ptr * Constants.BYTES_PER_WORD, ptr * Constants.BYTES_PER_WORD,
size, size,
wordsPerElement * Constants.BITS_PER_WORD, wordsPerElement * Constants.BITS_PER_WORD,
StructPointer.dataSize(tag) * Constants.BITS_PER_WORD, dataSize * Constants.BITS_PER_WORD,
(short)StructPointer.ptrCount(tag), ptrCount,
nestingLimit - 1); nestingLimit - 1);
} }
default : { default : {
@ -1297,8 +1317,8 @@ final class WireHelpers {
//# lists can also be interpreted as struct lists. We //# lists can also be interpreted as struct lists. We
//# need to compute the data size and pointer count for //# need to compute the data size and pointer count for
//# such structs. //# such structs.
int dataSize = ElementSize.dataBitsPerElement(elementSize); int dataSize = ElementSize.dataBitsPerElement(oldSize);
int pointerCount = ElementSize.pointersPerElement(elementSize); int pointerCount = ElementSize.pointersPerElement(oldSize);
int elementCount = ListPointer.elementCount(resolved.ref); int elementCount = ListPointer.elementCount(resolved.ref);
int step = dataSize + pointerCount * Constants.BITS_PER_POINTER; int step = dataSize + pointerCount * Constants.BITS_PER_POINTER;
@ -1309,7 +1329,7 @@ final class WireHelpers {
throw new DecodeException("Message contains out-of-bounds list pointer"); throw new DecodeException("Message contains out-of-bounds list pointer");
} }
if (elementSize == ElementSize.VOID) { if (oldSize == ElementSize.VOID) {
// Watch out for lists of void, which can claim to be arbitrarily large without // Watch out for lists of void, which can claim to be arbitrarily large without
// having sent actual data. // having sent actual data.
resolved.segment.arena.checkReadLimit(elementCount); resolved.segment.arena.checkReadLimit(elementCount);