diff --git a/runtime/src/main/java/org/capnproto/AnyPointer.java b/runtime/src/main/java/org/capnproto/AnyPointer.java index 12c0e6ce..d4065947 100644 --- a/runtime/src/main/java/org/capnproto/AnyPointer.java +++ b/runtime/src/main/java/org/capnproto/AnyPointer.java @@ -29,27 +29,47 @@ public static final class Factory public final Reader fromPointerReader(SegmentReader segment, int pointer, int nestingLimit) { return new Reader(segment, pointer, nestingLimit); } + public final Reader fromPointerReader(SegmentReader segment, CapTableReader capTable, int pointer, int nestingLimit) { + return new Reader(segment, capTable, pointer, nestingLimit); + } public final Builder fromPointerBuilder(SegmentBuilder segment, int pointer) { return new Builder(segment, pointer); } + public final Builder fromPointerBuilder(SegmentBuilder segment, CapTableBuilder capTable, int pointer) { + return new Builder(segment, capTable, pointer); + } public final Builder initFromPointerBuilder(SegmentBuilder segment, int pointer, int elementCount) { Builder result = new Builder(segment, pointer); result.clear(); return result; } + public final Builder initFromPointerBuilder(SegmentBuilder segment, CapTableBuilder capTable, int pointer, int elementCount) { + Builder result = new Builder(segment, capTable, pointer); + result.clear(); + return result; + } public void setPointerBuilder(SegmentBuilder segment, int pointer, Reader value) { if (value.isNull()) { - WireHelpers.zeroObject(segment, pointer); + WireHelpers.zeroObject(segment, null, pointer); WireHelpers.zeroPointerAndFars(segment, pointer); } else { - WireHelpers.copyPointer(segment, pointer, value.segment, value.pointer, value.nestingLimit); + WireHelpers.copyPointer(segment, null, pointer, value.segment, value.capTable, value.pointer, value.nestingLimit); + } + } + public void setPointerBuilder(SegmentBuilder segment, CapTableBuilder capTable, int pointer, Reader value) { + if (value.isNull()) { + WireHelpers.zeroObject(segment, capTable, pointer); + WireHelpers.zeroPointerAndFars(segment, pointer); + } + else { + WireHelpers.copyPointer(segment, capTable, pointer, value.segment, value.capTable, value.pointer, value.nestingLimit); } } } public static final Factory factory = new Factory(); - public final static class Reader { + public final static class Reader extends CapTableReader.ReaderContext { final SegmentReader segment; final int pointer; // offset in words final int nestingLimit; @@ -60,16 +80,29 @@ public Reader(SegmentReader segment, int pointer, int nestingLimit) { this.nestingLimit = nestingLimit; } + public Reader(SegmentReader segment, CapTableReader capTable, int pointer, int nestingLimit) { + this.segment = segment; + this.pointer = pointer; + this.nestingLimit = nestingLimit; + this.capTable = capTable; + } + + final Reader imbue(CapTableReader capTable) { + Reader result = new Reader(segment, pointer, nestingLimit); + result.capTable = capTable; + return result; + } + public final boolean isNull() { return WirePointer.isNull(this.segment.buffer.getLong(this.pointer * Constants.BYTES_PER_WORD)); } public final T getAs(FromPointerReader factory) { - return factory.fromPointerReader(this.segment, this.pointer, this.nestingLimit); + return factory.fromPointerReader(this.segment, this.capTable, this.pointer, this.nestingLimit); } } - public static final class Builder { + public static final class Builder extends CapTableBuilder.BuilderContext { final SegmentBuilder segment; final int pointer; @@ -78,34 +111,43 @@ public Builder(SegmentBuilder segment, int pointer) { this.pointer = pointer; } + Builder(SegmentBuilder segment, CapTableBuilder capTable, int pointer) { + this.segment = segment; + this.pointer = pointer; + this.capTable = capTable; + } + + final Builder imbue(CapTableBuilder capTable) { + return new Builder(segment, capTable, pointer); + } + public final boolean isNull() { return WirePointer.isNull(this.segment.buffer.getLong(this.pointer * Constants.BYTES_PER_WORD)); } public final T getAs(FromPointerBuilder factory) { - return factory.fromPointerBuilder(this.segment, this.pointer); + return factory.fromPointerBuilder(this.segment, this.capTable, this.pointer); } public final T initAs(FromPointerBuilder factory) { - return factory.initFromPointerBuilder(this.segment, this.pointer, 0); + return factory.initFromPointerBuilder(this.segment, this.capTable, this.pointer, 0); } public final T initAs(FromPointerBuilder factory, int elementCount) { - return factory.initFromPointerBuilder(this.segment, this.pointer, elementCount); + return factory.initFromPointerBuilder(this.segment, this.capTable, this.pointer, elementCount); } public final void setAs(SetPointerBuilder factory, U reader) { - factory.setPointerBuilder(this.segment, this.pointer, reader); + factory.setPointerBuilder(this.segment, this.capTable, this.pointer, reader); } public final Reader asReader() { - return new Reader(segment, pointer, java.lang.Integer.MAX_VALUE); + return new Reader(segment, this.capTable, pointer, java.lang.Integer.MAX_VALUE); } public final void clear() { - WireHelpers.zeroObject(this.segment, this.pointer); + WireHelpers.zeroObject(this.segment, this.capTable, this.pointer); this.segment.buffer.putLong(this.pointer * 8, 0L); } } - } diff --git a/runtime/src/main/java/org/capnproto/BuilderArena.java b/runtime/src/main/java/org/capnproto/BuilderArena.java index 4f063cd7..9710de34 100644 --- a/runtime/src/main/java/org/capnproto/BuilderArena.java +++ b/runtime/src/main/java/org/capnproto/BuilderArena.java @@ -24,6 +24,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; +import java.util.List; public final class BuilderArena implements Arena { public enum AllocationStrategy { @@ -38,6 +39,34 @@ public enum AllocationStrategy { public final ArrayList segments; private final Allocator allocator; + private final CapTableBuilder localCapTable = new CapTableBuilder() { + + private final List capTable = new ArrayList<>(); + + @Override + public int injectCap(ClientHook cap) { + int result = this.capTable.size(); + this.capTable.add(cap); + return result; + } + + @Override + public void dropCap(int index) { + if (index >= this.capTable.size()) { + assert false : "Invalid capability descriptor in message."; + return; + } + this.capTable.set(index, null); + } + + @Override + public ClientHook extractCap(int index) { + return index < this.capTable.size() + ? this.capTable.get(index) + : null; + } + }; + public BuilderArena(int firstSegmentSizeWords, AllocationStrategy allocationStrategy) { this.segments = new ArrayList(); { @@ -64,7 +93,17 @@ public BuilderArena(Allocator allocator, ByteBuffer firstSegment) { this.allocator = allocator; } - /** + /** + * Return a CapTableBuilder that merely implements local loopback. That is, you can set + * capabilities, then read the same capabilities back, but there is no intent ever to transmit + * these capabilities. A MessageBuilder that isn't imbued with some other CapTable uses this + * by default. + */ + public CapTableBuilder getLocalCapTable() { + return this.localCapTable; + } + + /** * Constructs a BuilderArena from a ReaderArena and uses the size of the largest segment * as the next allocation size. */ diff --git a/runtime/src/main/java/org/capnproto/CapTableBuilder.java b/runtime/src/main/java/org/capnproto/CapTableBuilder.java new file mode 100644 index 00000000..8f137a70 --- /dev/null +++ b/runtime/src/main/java/org/capnproto/CapTableBuilder.java @@ -0,0 +1,16 @@ +package org.capnproto; + +public interface CapTableBuilder extends CapTableReader { + + class BuilderContext { + public CapTableBuilder capTable; + } + + int injectCap(ClientHook cap); + + void dropCap(int index); + + default ClientHook[] getTable() { + return new ClientHook[0]; + } +} diff --git a/runtime/src/main/java/org/capnproto/CapTableReader.java b/runtime/src/main/java/org/capnproto/CapTableReader.java new file mode 100644 index 00000000..b6d6f8de --- /dev/null +++ b/runtime/src/main/java/org/capnproto/CapTableReader.java @@ -0,0 +1,10 @@ +package org.capnproto; + +public interface CapTableReader { + + class ReaderContext { + public CapTableReader capTable; + } + + ClientHook extractCap(int index); +} diff --git a/runtime/src/main/java/org/capnproto/Capability.java b/runtime/src/main/java/org/capnproto/Capability.java new file mode 100644 index 00000000..0f69157d --- /dev/null +++ b/runtime/src/main/java/org/capnproto/Capability.java @@ -0,0 +1,24 @@ +package org.capnproto; + +import static org.capnproto.ClientHook.BROKEN_CAPABILITY_BRAND; +import static org.capnproto.ClientHook.NULL_CAPABILITY_BRAND; + +public final class Capability { + + public static ClientHook newBrokenCap(String reason) { + return newBrokenClient(BROKEN_CAPABILITY_BRAND); + } + + public static ClientHook newNullCap() { + return newBrokenClient(NULL_CAPABILITY_BRAND); + } + + private static ClientHook newBrokenClient(Object brand) { + return new ClientHook() { + @Override + public Object getBrand() { + return brand; + } + }; + } +} diff --git a/runtime/src/main/java/org/capnproto/ClientHook.java b/runtime/src/main/java/org/capnproto/ClientHook.java new file mode 100644 index 00000000..cf9bec6f --- /dev/null +++ b/runtime/src/main/java/org/capnproto/ClientHook.java @@ -0,0 +1,29 @@ +package org.capnproto; + +public interface ClientHook { + + static final Object NULL_CAPABILITY_BRAND = new Object(); + static final Object BROKEN_CAPABILITY_BRAND = new Object(); + + /** + Returns an opaque object that identifies who made this client. This can be used by an RPC adapter to + discover when a capability it needs to marshal is one that it created in the first place, and + therefore it can transfer the capability without proxying. + */ + Object getBrand(); + + /** + * Returns true if the capability was created as a result of assigning a Client to null or by + * reading a null pointer out of a Cap'n Proto message. + */ + default boolean isNull() { + return getBrand() == NULL_CAPABILITY_BRAND; + } + + /** + * Returns true if the capability was created by newBrokenCap(). + */ + default boolean isError() { + return getBrand() == BROKEN_CAPABILITY_BRAND; + } +} diff --git a/runtime/src/main/java/org/capnproto/Data.java b/runtime/src/main/java/org/capnproto/Data.java index dec66223..b97def10 100644 --- a/runtime/src/main/java/org/capnproto/Data.java +++ b/runtime/src/main/java/org/capnproto/Data.java @@ -39,35 +39,42 @@ public final Reader fromPointerReader(SegmentReader segment, int pointer, int ne return WireHelpers.readDataPointer(segment, pointer, null, 0, 0); } + @Override + public final Reader fromPointerReader(SegmentReader segment, CapTableReader capTable, int pointer, int nestingLimit) { + return WireHelpers.readDataPointer(segment, pointer, null, 0, 0); + } + @Override public final Builder fromPointerBuilderBlobDefault( SegmentBuilder segment, + CapTableBuilder capTable, int pointer, java.nio.ByteBuffer defaultBuffer, int defaultOffset, int defaultSize) { return WireHelpers.getWritableDataPointer(pointer, segment, + capTable, defaultBuffer, defaultOffset, defaultSize); } - @Override - public final Builder fromPointerBuilder(SegmentBuilder segment, int pointer) { + public final Builder fromPointerBuilder(SegmentBuilder segment, CapTableBuilder capTable, int pointer) { return WireHelpers.getWritableDataPointer(pointer, segment, + capTable, null, 0, 0); } @Override - public final Builder initFromPointerBuilder(SegmentBuilder segment, int pointer, int size) { - return WireHelpers.initDataPointer(pointer, segment, size); + public final Builder initFromPointerBuilder(SegmentBuilder segment, CapTableBuilder capTable, int pointer, int size) { + return WireHelpers.initDataPointer(pointer, segment, capTable, size); } @Override - public final void setPointerBuilder(SegmentBuilder segment, int pointer, Reader value) { - WireHelpers.setDataPointer(pointer, segment, value); + public final void setPointerBuilder(SegmentBuilder segment, CapTableBuilder capTable, int pointer, Reader value) { + WireHelpers.setDataPointer(pointer, segment, capTable, value); } } public static final Factory factory = new Factory(); diff --git a/runtime/src/main/java/org/capnproto/FromPointerBuilder.java b/runtime/src/main/java/org/capnproto/FromPointerBuilder.java index 9fa715b7..ca6877e5 100644 --- a/runtime/src/main/java/org/capnproto/FromPointerBuilder.java +++ b/runtime/src/main/java/org/capnproto/FromPointerBuilder.java @@ -22,6 +22,15 @@ package org.capnproto; public interface FromPointerBuilder { - T fromPointerBuilder(SegmentBuilder segment, int pointer); - T initFromPointerBuilder(SegmentBuilder segment, int pointer, int elementCount); + default T fromPointerBuilder(SegmentBuilder segment, int pointer) { + return fromPointerBuilder(segment, null, pointer); + } + + T fromPointerBuilder(SegmentBuilder segment, CapTableBuilder capTable, int pointer); + + default T initFromPointerBuilder(SegmentBuilder segment, int pointer, int elementCount) { + return initFromPointerBuilder(segment, null, pointer, elementCount); + } + + T initFromPointerBuilder(SegmentBuilder segment, CapTableBuilder capTable, int pointer, int elementCount); } diff --git a/runtime/src/main/java/org/capnproto/FromPointerBuilderBlobDefault.java b/runtime/src/main/java/org/capnproto/FromPointerBuilderBlobDefault.java index 377fb3a6..3004beec 100644 --- a/runtime/src/main/java/org/capnproto/FromPointerBuilderBlobDefault.java +++ b/runtime/src/main/java/org/capnproto/FromPointerBuilderBlobDefault.java @@ -22,6 +22,11 @@ package org.capnproto; public interface FromPointerBuilderBlobDefault { - T fromPointerBuilderBlobDefault(SegmentBuilder segment, int pointer, + default T fromPointerBuilderBlobDefault(SegmentBuilder segment, int pointer, + java.nio.ByteBuffer defaultBuffer, int defaultOffset, int defaultSize) { + return fromPointerBuilderBlobDefault(segment, null, pointer, defaultBuffer, defaultOffset, defaultSize); + } + + T fromPointerBuilderBlobDefault(SegmentBuilder segment, CapTableBuilder capTable, int pointer, java.nio.ByteBuffer defaultBuffer, int defaultOffset, int defaultSize); } diff --git a/runtime/src/main/java/org/capnproto/FromPointerBuilderRefDefault.java b/runtime/src/main/java/org/capnproto/FromPointerBuilderRefDefault.java index 50c8b3d2..44d56e85 100644 --- a/runtime/src/main/java/org/capnproto/FromPointerBuilderRefDefault.java +++ b/runtime/src/main/java/org/capnproto/FromPointerBuilderRefDefault.java @@ -22,5 +22,10 @@ package org.capnproto; public interface FromPointerBuilderRefDefault { - T fromPointerBuilderRefDefault(SegmentBuilder segment, int pointer, SegmentReader defaultSegment, int defaultOffset); + + default T fromPointerBuilderRefDefault(SegmentBuilder segment, int pointer, SegmentReader defaultSegment, int defaultOffset) { + return fromPointerBuilderRefDefault(segment, null, pointer, defaultSegment, defaultOffset); + } + + T fromPointerBuilderRefDefault(SegmentBuilder segment, CapTableBuilder capTable, int pointer, SegmentReader defaultSegment, int defaultOffset); } diff --git a/runtime/src/main/java/org/capnproto/FromPointerReader.java b/runtime/src/main/java/org/capnproto/FromPointerReader.java index 1a1a71b0..5738cc57 100644 --- a/runtime/src/main/java/org/capnproto/FromPointerReader.java +++ b/runtime/src/main/java/org/capnproto/FromPointerReader.java @@ -22,5 +22,8 @@ package org.capnproto; public interface FromPointerReader { - T fromPointerReader(SegmentReader segment, int pointer, int nestingLimit); + default T fromPointerReader(SegmentReader segment, int pointer, int nestingLimit) { + return fromPointerReader(segment, null, pointer, nestingLimit); + } + T fromPointerReader(SegmentReader segment, CapTableReader capTable, int pointer, int nestingLimit); } diff --git a/runtime/src/main/java/org/capnproto/FromPointerReaderRefDefault.java b/runtime/src/main/java/org/capnproto/FromPointerReaderRefDefault.java index e6d05e02..cf301638 100644 --- a/runtime/src/main/java/org/capnproto/FromPointerReaderRefDefault.java +++ b/runtime/src/main/java/org/capnproto/FromPointerReaderRefDefault.java @@ -22,5 +22,8 @@ package org.capnproto; public interface FromPointerReaderRefDefault { - T fromPointerReaderRefDefault(SegmentReader segment, int pointer, SegmentReader defaultSegment, int defaultOffset, int nestingLimit); + default T fromPointerReaderRefDefault(SegmentReader segment, int pointer, SegmentReader defaultSegment, int defaultOffset, int nestingLimit) { + return fromPointerReaderRefDefault(segment, null, pointer, defaultSegment, defaultOffset, nestingLimit); + } + T fromPointerReaderRefDefault(SegmentReader segment, CapTableReader capTable, int pointer, SegmentReader defaultSegment, int defaultOffset, int nestingLimit); } diff --git a/runtime/src/main/java/org/capnproto/ListBuilder.java b/runtime/src/main/java/org/capnproto/ListBuilder.java index 15cff8c8..b39ef46b 100644 --- a/runtime/src/main/java/org/capnproto/ListBuilder.java +++ b/runtime/src/main/java/org/capnproto/ListBuilder.java @@ -21,11 +21,20 @@ package org.capnproto; -public class ListBuilder { +public class ListBuilder extends CapTableBuilder.BuilderContext { public interface Factory { T constructBuilder(SegmentBuilder segment, int ptr, int elementCount, int step, int structDataSize, short structPointerCount); + default T constructBuilder(SegmentBuilder segment, CapTableBuilder capTable, int ptr, + int elementCount, int step, + int structDataSize, short structPointerCount) { + T result = constructBuilder(segment, ptr, elementCount, step, structDataSize, structPointerCount); + if (result instanceof CapTableBuilder.BuilderContext) { + ((CapTableBuilder.BuilderContext) result).capTable = capTable; + } + return result; + } } final SegmentBuilder segment; @@ -38,12 +47,19 @@ T constructBuilder(SegmentBuilder segment, int ptr, public ListBuilder(SegmentBuilder segment, int ptr, int elementCount, int step, int structDataSize, short structPointerCount) { + this(segment, null, ptr, elementCount, step, structDataSize, structPointerCount); + } + + public ListBuilder(SegmentBuilder segment, CapTableBuilder capTable, int ptr, + int elementCount, int step, + int structDataSize, short structPointerCount) { this.segment = segment; this.ptr = ptr; this.elementCount = elementCount; this.step = step; this.structDataSize = structDataSize; this.structPointerCount = structPointerCount; + this.capTable = capTable; } public int size() { @@ -119,6 +135,7 @@ protected final T _getStructElement(StructBuilder.Factory factory, int in int structPointers = (structData + (this.structDataSize / 8)) / 8; return factory.constructBuilder(this.segment, + this.capTable, structData, structPointers, this.structDataSize, @@ -128,18 +145,21 @@ protected final T _getStructElement(StructBuilder.Factory factory, int in protected final T _getPointerElement(FromPointerBuilder factory, int index) { return factory.fromPointerBuilder( this.segment, + this.capTable, (this.ptr + (int)((long)index * this.step / Constants.BITS_PER_BYTE)) / Constants.BYTES_PER_WORD); } protected final T _initPointerElement(FromPointerBuilder factory, int index, int elementCount) { return factory.initFromPointerBuilder( this.segment, + this.capTable, (this.ptr + (int)((long)index * this.step / Constants.BITS_PER_BYTE)) / Constants.BYTES_PER_WORD, elementCount); } protected final void _setPointerElement(SetPointerBuilder factory, int index, Reader value) { factory.setPointerBuilder(this.segment, + this.capTable, (this.ptr + (int)((long)index * this.step / Constants.BITS_PER_BYTE)) / Constants.BYTES_PER_WORD, value); } diff --git a/runtime/src/main/java/org/capnproto/ListFactory.java b/runtime/src/main/java/org/capnproto/ListFactory.java index bbb84447..61752c2d 100644 --- a/runtime/src/main/java/org/capnproto/ListFactory.java +++ b/runtime/src/main/java/org/capnproto/ListFactory.java @@ -21,7 +21,7 @@ package org.capnproto; -public abstract class ListFactory +public abstract class ListFactory implements ListBuilder.Factory, FromPointerBuilderRefDefault, SetPointerBuilder, @@ -32,45 +32,48 @@ public abstract class ListFactory final byte elementSize; ListFactory(byte elementSize) {this.elementSize = elementSize;} - public final Reader fromPointerReaderRefDefault(SegmentReader segment, int pointer, + public final Reader fromPointerReaderRefDefault(SegmentReader segment, CapTableReader capTable, int pointer, SegmentReader defaultSegment, int defaultOffset, int nestingLimit) { return WireHelpers.readListPointer(this, segment, pointer, + capTable, defaultSegment, defaultOffset, this.elementSize, nestingLimit); } - public final Reader fromPointerReader(SegmentReader segment, int pointer, int nestingLimit) { - return fromPointerReaderRefDefault(segment, pointer, null, 0, nestingLimit); + public final Reader fromPointerReader(SegmentReader segment, CapTableReader capTable, int pointer, int nestingLimit) { + return fromPointerReaderRefDefault(segment, capTable, pointer, null, 0, nestingLimit); } - public Builder fromPointerBuilderRefDefault(SegmentBuilder segment, int pointer, + public Builder fromPointerBuilderRefDefault(SegmentBuilder segment, CapTableBuilder capTable, int pointer, SegmentReader defaultSegment, int defaultOffset) { return WireHelpers.getWritableListPointer(this, pointer, segment, + capTable, this.elementSize, defaultSegment, defaultOffset); } - public Builder fromPointerBuilder(SegmentBuilder segment, int pointer) { + public Builder fromPointerBuilder(SegmentBuilder segment, CapTableBuilder capTable, int pointer) { return WireHelpers.getWritableListPointer(this, pointer, segment, + capTable, this.elementSize, null, 0); } - public Builder initFromPointerBuilder(SegmentBuilder segment, int pointer, int elementCount) { - return WireHelpers.initListPointer(this, pointer, segment, elementCount, this.elementSize); + public Builder initFromPointerBuilder(SegmentBuilder segment, CapTableBuilder capTable, int pointer, int elementCount) { + return WireHelpers.initListPointer(this, capTable, pointer, segment, elementCount, this.elementSize); } - public final void setPointerBuilder(SegmentBuilder segment, int pointer, Reader value) { - WireHelpers.setListPointer(segment, pointer, value); + public final void setPointerBuilder(SegmentBuilder segment, CapTableBuilder capTable, int pointer, Reader value) { + WireHelpers.setListPointer(segment, capTable, pointer, value); } } diff --git a/runtime/src/main/java/org/capnproto/ListList.java b/runtime/src/main/java/org/capnproto/ListList.java index 13488aa2..88d387b6 100644 --- a/runtime/src/main/java/org/capnproto/ListList.java +++ b/runtime/src/main/java/org/capnproto/ListList.java @@ -22,7 +22,7 @@ package org.capnproto; public final class ListList { - public static final class Factory + public static final class Factory extends ListFactory, Reader> { public final ListFactory factory; @@ -68,7 +68,7 @@ public T get(int index) { } } - public static final class Builder extends ListBuilder { + public static final class Builder extends ListBuilder { private final ListFactory factory; public Builder(ListFactory factory, diff --git a/runtime/src/main/java/org/capnproto/ListReader.java b/runtime/src/main/java/org/capnproto/ListReader.java index f1b2970e..6fd6b4eb 100644 --- a/runtime/src/main/java/org/capnproto/ListReader.java +++ b/runtime/src/main/java/org/capnproto/ListReader.java @@ -21,13 +21,22 @@ package org.capnproto; -public class ListReader { +public class ListReader extends CapTableReader.ReaderContext { public interface Factory { T constructReader(SegmentReader segment, int ptr, int elementCount, int step, int structDataSize, short structPointerCount, int nestingLimit); + default T constructReader(SegmentReader segment, CapTableReader capTable, int ptr, + int elementCount, int step, + int structDataSize, short structPointerCount, int nestingLimit) { + T result = constructReader(segment, ptr, elementCount, step, structDataSize, structPointerCount, nestingLimit); + if (result instanceof CapTableReader.ReaderContext) { + ((CapTableReader.ReaderContext) result).capTable = capTable; + } + return result; + } } final SegmentReader segment; @@ -46,12 +55,22 @@ public ListReader() { this.structDataSize = 0; this.structPointerCount = 0; this.nestingLimit = 0x7fffffff; + this.capTable = null; } public ListReader(SegmentReader segment, int ptr, int elementCount, int step, int structDataSize, short structPointerCount, int nestingLimit) { + this(segment, null, ptr, elementCount, step, structDataSize, structPointerCount, nestingLimit); + } + + public ListReader(SegmentReader segment, + CapTableReader capTable, + int ptr, + int elementCount, int step, + int structDataSize, short structPointerCount, + int nestingLimit) { this.segment = segment; this.ptr = ptr; this.elementCount = elementCount; @@ -59,7 +78,11 @@ public ListReader(SegmentReader segment, int ptr, this.structDataSize = structDataSize; this.structPointerCount = structPointerCount; this.nestingLimit = nestingLimit; + this.capTable = capTable; + } + ListReader imbue(CapTableReader capTable) { + return new ListReader(segment, capTable, ptr, elementCount, step, structDataSize, structPointerCount, nestingLimit); } public int size() { @@ -103,13 +126,14 @@ protected T _getStructElement(StructReader.Factory factory, int index) { int structData = this.ptr + (int)(indexBit / Constants.BITS_PER_BYTE); int structPointers = structData + (this.structDataSize / Constants.BITS_PER_BYTE); - return factory.constructReader(this.segment, structData, structPointers / 8, this.structDataSize, + return factory.constructReader(this.segment, this.capTable, structData, structPointers / 8, this.structDataSize, this.structPointerCount, this.nestingLimit - 1); } protected T _getPointerElement(FromPointerReader factory, int index) { return factory.fromPointerReader( this.segment, + this.capTable, (this.ptr + (this.structDataSize / Constants.BITS_PER_BYTE) + (int)((long)index * this.step / Constants.BITS_PER_BYTE)) diff --git a/runtime/src/main/java/org/capnproto/SetPointerBuilder.java b/runtime/src/main/java/org/capnproto/SetPointerBuilder.java index 636c3a23..cdc7f72d 100644 --- a/runtime/src/main/java/org/capnproto/SetPointerBuilder.java +++ b/runtime/src/main/java/org/capnproto/SetPointerBuilder.java @@ -22,5 +22,8 @@ package org.capnproto; public interface SetPointerBuilder { - void setPointerBuilder(SegmentBuilder segment, int pointer, Reader value); + default void setPointerBuilder(SegmentBuilder segment, int pointer, Reader value) { + setPointerBuilder(segment, null, pointer, value); + } + void setPointerBuilder(SegmentBuilder segment, CapTableBuilder capTable, int pointer, Reader value); } diff --git a/runtime/src/main/java/org/capnproto/StructBuilder.java b/runtime/src/main/java/org/capnproto/StructBuilder.java index 8c3e4f78..f9543771 100644 --- a/runtime/src/main/java/org/capnproto/StructBuilder.java +++ b/runtime/src/main/java/org/capnproto/StructBuilder.java @@ -21,10 +21,18 @@ package org.capnproto; -public class StructBuilder { +public class StructBuilder extends CapTableBuilder.BuilderContext { public interface Factory { T constructBuilder(SegmentBuilder segment, int data, int pointers, int dataSize, short pointerCount); + default T constructBuilder(SegmentBuilder segment, CapTableBuilder capTable, int data, int pointers, int dataSize, + short pointerCount) { + T result = constructBuilder(segment, data, pointers, dataSize, pointerCount); + if (result instanceof CapTableBuilder.BuilderContext) { + ((CapTableBuilder.BuilderContext) result).capTable = capTable; + } + return result; + } StructSize structSize(); } @@ -36,11 +44,17 @@ T constructBuilder(SegmentBuilder segment, int data, int pointers, int dataSize, public StructBuilder(SegmentBuilder segment, int data, int pointers, int dataSize, short pointerCount) { + this(segment, null, data, pointers, dataSize, pointerCount); + } + + public StructBuilder(SegmentBuilder segment, CapTableBuilder capTable, int data, + int pointers, int dataSize, short pointerCount) { this.segment = segment; this.data = data; this.pointers = pointers; this.dataSize = dataSize; this.pointerCount = pointerCount; + this.capTable = capTable; } protected final boolean _getBooleanField(int offset) { @@ -172,30 +186,30 @@ protected final boolean _pointerFieldIsNull(int ptrIndex) { protected final void _clearPointerField(int ptrIndex) { int pointer = this.pointers + ptrIndex; - WireHelpers.zeroObject(this.segment, pointer); + WireHelpers.zeroObject(this.segment, this.capTable, pointer); this.segment.buffer.putLong(pointer * 8, 0L); } protected final T _getPointerField(FromPointerBuilder factory, int index) { - return factory.fromPointerBuilder(this.segment, this.pointers + index); + return factory.fromPointerBuilder(this.segment, this.capTable, this.pointers + index); } protected final T _getPointerField(FromPointerBuilderRefDefault factory, int index, SegmentReader defaultSegment, int defaultOffset) { - return factory.fromPointerBuilderRefDefault(this.segment, this.pointers + index, defaultSegment, defaultOffset); + return factory.fromPointerBuilderRefDefault(this.segment, this.capTable, this.pointers + index, defaultSegment, defaultOffset); } protected final T _getPointerField(FromPointerBuilderBlobDefault factory, int index, java.nio.ByteBuffer defaultBuffer, int defaultOffset, int defaultSize) { - return factory.fromPointerBuilderBlobDefault(this.segment, this.pointers + index, defaultBuffer, defaultOffset, defaultSize); + return factory.fromPointerBuilderBlobDefault(this.segment, this.capTable, this.pointers + index, defaultBuffer, defaultOffset, defaultSize); } protected final T _initPointerField(FromPointerBuilder factory, int index, int elementCount) { - return factory.initFromPointerBuilder(this.segment, this.pointers + index, elementCount); + return factory.initFromPointerBuilder(this.segment, this.capTable, this.pointers + index, elementCount); } protected final void _setPointerField(SetPointerBuilder factory, int index, Reader value) { - factory.setPointerBuilder(this.segment, this.pointers + index, value); + factory.setPointerBuilder(this.segment, this.capTable, this.pointers + index, value); } protected final void _copyContentFrom(StructReader other) { @@ -244,14 +258,16 @@ protected final void _copyContentFrom(StructReader other) { // Zero out all pointers in the target. for (int ii = 0; ii < this.pointerCount; ++ii) { - WireHelpers.zeroObject(this.segment, this.pointers + ii); + WireHelpers.zeroObject(this.segment, this.capTable, this.pointers + ii); } this.segment.buffer.putLong(this.pointers * Constants.BYTES_PER_WORD, 0); for (int ii = 0; ii < sharedPointerCount; ++ii) { WireHelpers.copyPointer(this.segment, + this.capTable, this.pointers + ii, other.segment, + other.capTable, other.pointers + ii, other.nestingLimit); } diff --git a/runtime/src/main/java/org/capnproto/StructFactory.java b/runtime/src/main/java/org/capnproto/StructFactory.java index 5ff08e0d..615cae70 100644 --- a/runtime/src/main/java/org/capnproto/StructFactory.java +++ b/runtime/src/main/java/org/capnproto/StructFactory.java @@ -21,40 +21,42 @@ package org.capnproto; -public abstract class StructFactory +public abstract class StructFactory implements PointerFactory, FromPointerBuilderRefDefault, StructBuilder.Factory, SetPointerBuilder, FromPointerReaderRefDefault, StructReader.Factory { - public final Reader fromPointerReaderRefDefault(SegmentReader segment, int pointer, + public final Reader fromPointerReaderRefDefault(SegmentReader segment, CapTableReader capTable, int pointer, SegmentReader defaultSegment, int defaultOffset, int nestingLimit) { return WireHelpers.readStructPointer(this, segment, + capTable, pointer, defaultSegment, defaultOffset, nestingLimit); } - public final Reader fromPointerReader(SegmentReader segment, int pointer, int nestingLimit) { - return fromPointerReaderRefDefault(segment, pointer, null, 0, nestingLimit); + + public final Reader fromPointerReader(SegmentReader segment, CapTableReader capTable, int pointer, int nestingLimit) { + return fromPointerReaderRefDefault(segment, capTable, pointer, null, 0, nestingLimit); } - public final Builder fromPointerBuilderRefDefault(SegmentBuilder segment, int pointer, + public final Builder fromPointerBuilderRefDefault(SegmentBuilder segment, CapTableBuilder capTable, int pointer, SegmentReader defaultSegment, int defaultOffset) { - return WireHelpers.getWritableStructPointer(this, pointer, segment, this.structSize(), + return WireHelpers.getWritableStructPointer(this, pointer, segment, capTable, this.structSize(), defaultSegment, defaultOffset); } - public final Builder fromPointerBuilder(SegmentBuilder segment, int pointer) { - return WireHelpers.getWritableStructPointer(this, pointer, segment, this.structSize(), - null, 0); + public final Builder fromPointerBuilder(SegmentBuilder segment, CapTableBuilder capTable, int pointer) { + return WireHelpers.getWritableStructPointer(this, pointer, segment, capTable, this.structSize(), + null, 0); } - public final Builder initFromPointerBuilder(SegmentBuilder segment, int pointer, int elementCount) { - return WireHelpers.initStructPointer(this, pointer, segment, this.structSize()); + public final Builder initFromPointerBuilder(SegmentBuilder segment, CapTableBuilder capTable, int pointer, int elementCount) { + return WireHelpers.initStructPointer(this, pointer, segment, capTable, this.structSize()); } - public final void setPointerBuilder(SegmentBuilder segment, int pointer, Reader value) { - WireHelpers.setStructPointer(segment, pointer, value); + public final void setPointerBuilder(SegmentBuilder segment, CapTableBuilder capTable, int pointer, Reader value) { + WireHelpers.setStructPointer(segment, capTable, pointer, value); } public abstract Reader asReader(Builder builder); diff --git a/runtime/src/main/java/org/capnproto/StructList.java b/runtime/src/main/java/org/capnproto/StructList.java index b97ad924..414fcb6d 100644 --- a/runtime/src/main/java/org/capnproto/StructList.java +++ b/runtime/src/main/java/org/capnproto/StructList.java @@ -56,9 +56,31 @@ public final Builder constructBuilder(SegmentBuilder segment, } @Override - public final Builder fromPointerBuilderRefDefault(SegmentBuilder segment, int pointer, + public final Reader constructReader(SegmentReader segment, + CapTableReader capTable, + int ptr, + int elementCount, int step, + int structDataSize, short structPointerCount, + int nestingLimit) { + return new Reader(factory, capTable, + segment, ptr, elementCount, step, structDataSize, structPointerCount, nestingLimit); + } + + @Override + public final Builder constructBuilder(SegmentBuilder segment, + CapTableBuilder capTable, + int ptr, + int elementCount, int step, + int structDataSize, short structPointerCount) { + return new Builder (factory, capTable, + segment, ptr, elementCount, step, structDataSize, structPointerCount); + } + + @Override + public final Builder fromPointerBuilderRefDefault(SegmentBuilder segment, CapTableBuilder capTable, int pointer, SegmentReader defaultSegment, int defaultOffset) { return WireHelpers.getWritableStructListPointer(this, + capTable, pointer, segment, factory.structSize(), @@ -67,8 +89,9 @@ public final Builder fromPointerBuilderRefDefault(SegmentBuilder } @Override - public final Builder fromPointerBuilder(SegmentBuilder segment, int pointer) { + public final Builder fromPointerBuilder(SegmentBuilder segment, CapTableBuilder capTable, int pointer) { return WireHelpers.getWritableStructListPointer(this, + capTable, pointer, segment, factory.structSize(), @@ -76,9 +99,9 @@ public final Builder fromPointerBuilder(SegmentBuilder segment, } @Override - public final Builder initFromPointerBuilder(SegmentBuilder segment, int pointer, + public final Builder initFromPointerBuilder(SegmentBuilder segment, CapTableBuilder capTable, int pointer, int elementCount) { - return WireHelpers.initStructListPointer(this, pointer, segment, elementCount, factory.structSize()); + return WireHelpers.initStructListPointer(this, capTable, pointer, segment, elementCount, factory.structSize()); } } @@ -95,6 +118,17 @@ public Reader(StructReader.Factory factory, this.factory = factory; } + public Reader(StructReader.Factory factory, + CapTableReader capTable, + SegmentReader segment, + int ptr, + int elementCount, int step, + int structDataSize, short structPointerCount, + int nestingLimit) { + super(segment, capTable, ptr, elementCount, step, structDataSize, structPointerCount, nestingLimit); + this.factory = factory; + } + public T get(int index) { return _getStructElement(factory, index); } @@ -130,7 +164,15 @@ public Builder(StructBuilder.Factory factory, SegmentBuilder segment, int ptr, int elementCount, int step, int structDataSize, short structPointerCount){ - super(segment, ptr, elementCount, step, structDataSize, structPointerCount); + this(factory, null, segment, ptr, elementCount, step, structDataSize, structPointerCount); + } + + public Builder(StructBuilder.Factory factory, + CapTableBuilder capTable, + SegmentBuilder segment, int ptr, + int elementCount, int step, + int structDataSize, short structPointerCount) { + super(segment, capTable, ptr, elementCount, step, structDataSize, structPointerCount); this.factory = factory; } diff --git a/runtime/src/main/java/org/capnproto/StructReader.java b/runtime/src/main/java/org/capnproto/StructReader.java index 8a21a216..c4574312 100644 --- a/runtime/src/main/java/org/capnproto/StructReader.java +++ b/runtime/src/main/java/org/capnproto/StructReader.java @@ -21,11 +21,20 @@ package org.capnproto; -public class StructReader { +public class StructReader extends CapTableReader.ReaderContext { public interface Factory { - abstract T constructReader(SegmentReader segment, int data, int pointers, + T constructReader(SegmentReader segment, int data, int pointers, int dataSize, short pointerCount, int nestingLimit); + default T constructReader(SegmentReader segment, CapTableReader capTable, int data, int pointers, + int dataSize, short pointerCount, + int nestingLimit) { + T result = constructReader(segment, data, pointers, dataSize, pointerCount, nestingLimit); + if (result instanceof CapTableReader.ReaderContext) { + ((CapTableReader.ReaderContext) result).capTable = capTable; + } + return result; + } } protected final SegmentReader segment; @@ -42,17 +51,26 @@ public StructReader() { this.dataSize = 0; this.pointerCount = 0; this.nestingLimit = 0x7fffffff; + this.capTable = null; } public StructReader(SegmentReader segment, int data, int pointers, int dataSize, short pointerCount, int nestingLimit) { + this(segment, null, data, pointers, dataSize, pointerCount, nestingLimit); + } + + public StructReader(SegmentReader segment, CapTableReader capTable, + int data, + int pointers, int dataSize, short pointerCount, + int nestingLimit) { this.segment = segment; this.data = data; this.pointers = pointers; this.dataSize = dataSize; this.pointerCount = pointerCount; this.nestingLimit = nestingLimit; + this.capTable = capTable; } protected final boolean _getBooleanField(int offset) { @@ -157,10 +175,12 @@ protected final boolean _pointerFieldIsNull(int ptrIndex) { protected final T _getPointerField(FromPointerReader factory, int ptrIndex) { if (ptrIndex < this.pointerCount) { return factory.fromPointerReader(this.segment, + this.capTable, this.pointers + ptrIndex, this.nestingLimit); } else { return factory.fromPointerReader(SegmentReader.EMPTY, + this.capTable, 0, this.nestingLimit); } @@ -171,12 +191,14 @@ protected final T _getPointerField(FromPointerReaderRefDefault factory, i SegmentReader defaultSegment, int defaultOffset) { if (ptrIndex < this.pointerCount) { return factory.fromPointerReaderRefDefault(this.segment, + this.capTable, this.pointers + ptrIndex, defaultSegment, defaultOffset, this.nestingLimit); } else { return factory.fromPointerReaderRefDefault(SegmentReader.EMPTY, + this.capTable, 0, defaultSegment, defaultOffset, diff --git a/runtime/src/main/java/org/capnproto/Text.java b/runtime/src/main/java/org/capnproto/Text.java index fdf62cc4..50d15f4f 100644 --- a/runtime/src/main/java/org/capnproto/Text.java +++ b/runtime/src/main/java/org/capnproto/Text.java @@ -37,35 +37,37 @@ public final Reader fromPointerReaderBlobDefault(SegmentReader segment, int poin } @Override - public final Reader fromPointerReader(SegmentReader segment, int pointer, int nestingLimit) { + public final Reader fromPointerReader(SegmentReader segment, CapTableReader capTable, int pointer, int nestingLimit) { return WireHelpers.readTextPointer(segment, pointer, null, 0, 0); } @Override - public final Builder fromPointerBuilderBlobDefault(SegmentBuilder segment, int pointer, - java.nio.ByteBuffer defaultBuffer, int defaultOffset, int defaultSize) { + public final Builder fromPointerBuilderBlobDefault(SegmentBuilder segment, CapTableBuilder capTable, int pointer, + java.nio.ByteBuffer defaultBuffer, int defaultOffset, int defaultSize) { return WireHelpers.getWritableTextPointer(pointer, segment, + capTable, defaultBuffer, defaultOffset, defaultSize); } @Override - public final Builder fromPointerBuilder(SegmentBuilder segment, int pointer) { + public final Builder fromPointerBuilder(SegmentBuilder segment, CapTableBuilder capTable, int pointer) { return WireHelpers.getWritableTextPointer(pointer, segment, + capTable, null, 0, 0); } @Override - public final Builder initFromPointerBuilder(SegmentBuilder segment, int pointer, int size) { - return WireHelpers.initTextPointer(pointer, segment, size); + public Builder initFromPointerBuilder(SegmentBuilder segment, CapTableBuilder capTable, int pointer, int size) { + return WireHelpers.initTextPointer(pointer, segment, capTable, size); } @Override - public final void setPointerBuilder(SegmentBuilder segment, int pointer, Reader value) { - WireHelpers.setTextPointer(pointer, segment, value); + public void setPointerBuilder(SegmentBuilder segment, CapTableBuilder capTable, int pointer, Reader value) { + WireHelpers.setTextPointer(pointer, segment, capTable, value); } } public static final Factory factory = new Factory(); diff --git a/runtime/src/main/java/org/capnproto/WireHelpers.java b/runtime/src/main/java/org/capnproto/WireHelpers.java index 95ebf0bc..cdb87fc9 100644 --- a/runtime/src/main/java/org/capnproto/WireHelpers.java +++ b/runtime/src/main/java/org/capnproto/WireHelpers.java @@ -57,12 +57,13 @@ static class AllocateResult { static AllocateResult allocate(int refOffset, SegmentBuilder segment, + CapTableBuilder capTable, int amount, // in words byte kind) { long ref = segment.get(refOffset); if (!WirePointer.isNull(ref)) { - zeroObject(segment, refOffset); + zeroObject(segment, capTable, refOffset); } if (amount == 0 && kind == WirePointer.STRUCT) { @@ -182,7 +183,7 @@ static FollowFarsResult followFars(long ref, int refTarget, SegmentReader segmen } } - static void zeroObject(SegmentBuilder segment, int refOffset) { + static void zeroObject(SegmentBuilder segment, CapTableBuilder capTable, int refOffset) { //# Zero out the pointed-to object. Use when the pointer is //# about to be overwritten making the target object no longer //# reachable. @@ -195,7 +196,7 @@ static void zeroObject(SegmentBuilder segment, int refOffset) { switch (WirePointer.kind(ref)) { case WirePointer.STRUCT: case WirePointer.LIST: - zeroObject(segment, ref, WirePointer.target(refOffset, ref)); + zeroObject(segment, capTable, ref, WirePointer.target(refOffset, ref)); break; case WirePointer.FAR: { segment = segment.getArena().getSegment(FarPointer.getSegmentId(ref)); @@ -205,13 +206,13 @@ static void zeroObject(SegmentBuilder segment, int refOffset) { if (FarPointer.isDoubleFar(ref)) { SegmentBuilder otherSegment = segment.getArena().getSegment(FarPointer.getSegmentId(ref)); if (otherSegment.isWritable()) { - zeroObject(otherSegment, padOffset + 1, FarPointer.positionInSegment(pad)); + zeroObject(otherSegment, capTable, padOffset + 1, FarPointer.positionInSegment(pad)); } segment.buffer.putLong(padOffset * 8, 0L); segment.buffer.putLong((padOffset + 1) * 8, 0L); } else { - zeroObject(segment, padOffset); + zeroObject(segment, capTable, padOffset); segment.buffer.putLong(padOffset * 8, 0L); } } @@ -219,12 +220,20 @@ static void zeroObject(SegmentBuilder segment, int refOffset) { break; } case WirePointer.OTHER: { - // TODO + assert WirePointer.isCapability(ref) : "Unknown pointer type"; + if (WirePointer.isCapability(ref)) { + int capIndex = WirePointer.upper32Bits(ref); + assert capTable != null: "Cannot zero out capability pointer with no capTable"; + if (capTable != null) { + capTable.dropCap(capIndex); + } + } + break; } } } - static void zeroObject(SegmentBuilder segment, long tag, int ptr) { + static void zeroObject(SegmentBuilder segment, CapTableBuilder capTable, long tag, int ptr) { //# We shouldn't zero out external data linked into the message. if (!segment.isWritable()) return; @@ -233,7 +242,7 @@ static void zeroObject(SegmentBuilder segment, long tag, int ptr) { int pointerSection = ptr + StructPointer.dataSize(tag); int count = StructPointer.ptrCount(tag); for (int ii = 0; ii < count; ++ii) { - zeroObject(segment, pointerSection + ii); + zeroObject(segment, capTable, pointerSection + ii); } memset(segment.buffer, ptr * Constants.BYTES_PER_WORD, (byte)0, StructPointer.wordSize(tag) * Constants.BYTES_PER_WORD); @@ -256,7 +265,7 @@ static void zeroObject(SegmentBuilder segment, long tag, int ptr) { case ElementSize.POINTER: { int count = ListPointer.elementCount(tag); for (int ii = 0; ii < count; ++ii) { - zeroObject(segment, ptr + ii); + zeroObject(segment, capTable, ptr + ii); } memset(segment.buffer, ptr * Constants.BYTES_PER_WORD, (byte)0, count * Constants.BYTES_PER_WORD); @@ -275,7 +284,7 @@ static void zeroObject(SegmentBuilder segment, long tag, int ptr) { for (int ii = 0; ii < count; ++ii) { pos += dataSize; for (int jj = 0; jj < pointerCount; ++jj) { - zeroObject(segment, pos); + zeroObject(segment, capTable, pos); pos += Constants.POINTER_SIZE_IN_WORDS; } } @@ -400,9 +409,17 @@ static T initStructPointer(StructBuilder.Factory factory, int refOffset, SegmentBuilder segment, StructSize size) { - AllocateResult allocation = allocate(refOffset, segment, size.total(), WirePointer.STRUCT); + return initStructPointer(factory, refOffset, segment, null, size); + } + + static T initStructPointer(StructBuilder.Factory factory, + int refOffset, + SegmentBuilder segment, + CapTableBuilder capTable, + StructSize size) { + AllocateResult allocation = allocate(refOffset, segment, capTable, size.total(), WirePointer.STRUCT); StructPointer.setFromStructSize(allocation.segment.buffer, allocation.refOffset, size); - return factory.constructBuilder(allocation.segment, allocation.ptr * Constants.BYTES_PER_WORD, + return factory.constructBuilder(allocation.segment, capTable, allocation.ptr * Constants.BYTES_PER_WORD, allocation.ptr + size.data, size.data * 64, size.pointers); } @@ -410,6 +427,7 @@ static T initStructPointer(StructBuilder.Factory factory, static T getWritableStructPointer(StructBuilder.Factory factory, int refOffset, SegmentBuilder segment, + CapTableBuilder capTable, StructSize size, SegmentReader defaultSegment, int defaultOffset) { @@ -417,7 +435,7 @@ static T getWritableStructPointer(StructBuilder.Factory factory, int target = WirePointer.target(refOffset, ref); if (WirePointer.isNull(ref)) { if (defaultSegment == null) { - return initStructPointer(factory, refOffset, segment, size); + return initStructPointer(factory, refOffset, segment, capTable, size); } else { throw new RuntimeException("unimplemented"); } @@ -440,7 +458,7 @@ static T getWritableStructPointer(StructBuilder.Factory factory, //# Don't let allocate() zero out the object just yet. zeroPointerAndFars(segment, refOffset); - AllocateResult allocation = allocate(refOffset, segment, + AllocateResult allocation = allocate(refOffset, segment, capTable, totalSize, WirePointer.STRUCT); StructPointer.set(allocation.segment.buffer, allocation.refOffset, @@ -466,11 +484,11 @@ static T getWritableStructPointer(StructBuilder.Factory factory, memset(resolved.segment.buffer, resolved.ptr * Constants.BYTES_PER_WORD, (byte)0, (oldDataSize + oldPointerCount * Constants.WORDS_PER_POINTER) * Constants.BYTES_PER_WORD); - return factory.constructBuilder(allocation.segment, allocation.ptr * Constants.BYTES_PER_WORD, + return factory.constructBuilder(allocation.segment, capTable, allocation.ptr * Constants.BYTES_PER_WORD, newPointerSection, newDataSize * Constants.BITS_PER_WORD, newPointerCount); } else { - return factory.constructBuilder(resolved.segment, resolved.ptr * Constants.BYTES_PER_WORD, + return factory.constructBuilder(resolved.segment, capTable, resolved.ptr * Constants.BYTES_PER_WORD, oldPointerSection, oldDataSize * Constants.BITS_PER_WORD, (short)oldPointerCount); } @@ -478,6 +496,7 @@ static T getWritableStructPointer(StructBuilder.Factory factory, } static T initListPointer(ListBuilder.Factory factory, + CapTableBuilder capTable, int refOffset, SegmentBuilder segment, int elementCount, @@ -488,16 +507,17 @@ static T initListPointer(ListBuilder.Factory factory, int pointerCount = ElementSize.pointersPerElement(elementSize); int step = dataSize + pointerCount * Constants.BITS_PER_POINTER; int wordCount = roundBitsUpToWords((long)elementCount * (long)step); - AllocateResult allocation = allocate(refOffset, segment, wordCount, WirePointer.LIST); + AllocateResult allocation = allocate(refOffset, segment, capTable, wordCount, WirePointer.LIST); ListPointer.set(allocation.segment.buffer, allocation.refOffset, elementSize, elementCount); - return factory.constructBuilder(allocation.segment, + return factory.constructBuilder(allocation.segment, capTable, allocation.ptr * Constants.BYTES_PER_WORD, elementCount, step, dataSize, (short)pointerCount); } static T initStructListPointer(ListBuilder.Factory factory, + CapTableBuilder capTable, int refOffset, SegmentBuilder segment, int elementCount, @@ -506,7 +526,7 @@ static T initStructListPointer(ListBuilder.Factory factory, //# Allocate the list, prefixed by a single WirePointer. int wordCount = elementCount * wordsPerElement; - AllocateResult allocation = allocate(refOffset, segment, Constants.POINTER_SIZE_IN_WORDS + wordCount, + AllocateResult allocation = allocate(refOffset, segment, capTable, Constants.POINTER_SIZE_IN_WORDS + wordCount, WirePointer.LIST); //# Initialize the pointer. @@ -515,7 +535,7 @@ static T initStructListPointer(ListBuilder.Factory factory, WirePointer.STRUCT, elementCount); StructPointer.setFromStructSize(allocation.segment.buffer, allocation.ptr, elementSize); - return factory.constructBuilder(allocation.segment, + return factory.constructBuilder(allocation.segment, capTable, (allocation.ptr + 1) * Constants.BYTES_PER_WORD, elementCount, wordsPerElement * Constants.BITS_PER_WORD, elementSize.data * Constants.BITS_PER_WORD, elementSize.pointers); @@ -524,6 +544,7 @@ static T initStructListPointer(ListBuilder.Factory factory, static T getWritableListPointer(ListBuilder.Factory factory, int origRefOffset, SegmentBuilder origSegment, + CapTableBuilder capTable, byte elementSize, SegmentReader defaultSegment, int defaultOffset) { @@ -573,13 +594,14 @@ static T getWritableListPointer(ListBuilder.Factory factory, int step = dataSize + pointerCount * Constants.BITS_PER_POINTER; - return factory.constructBuilder(resolved.segment, resolved.ptr * Constants.BYTES_PER_WORD, + return factory.constructBuilder(resolved.segment, capTable, resolved.ptr * Constants.BYTES_PER_WORD, ListPointer.elementCount(resolved.ref), step, dataSize, (short) pointerCount); } } static T getWritableStructListPointer(ListBuilder.Factory factory, + CapTableBuilder capTable, int origRefOffset, SegmentBuilder origSegment, StructSize elementSize, @@ -615,7 +637,7 @@ static T getWritableStructListPointer(ListBuilder.Factory factory, if (oldDataSize >= elementSize.data && oldPointerCount >= elementSize.pointers) { //# Old size is at least as large as we need. Ship it. - return factory.constructBuilder(resolved.segment, oldPtr * Constants.BYTES_PER_WORD, + return factory.constructBuilder(resolved.segment, capTable, oldPtr * Constants.BYTES_PER_WORD, elementCount, oldStep * Constants.BITS_PER_WORD, oldDataSize * Constants.BITS_PER_WORD, @@ -633,7 +655,7 @@ static T getWritableStructListPointer(ListBuilder.Factory factory, //# Don't let allocate() zero out the object just yet. zeroPointerAndFars(origSegment, origRefOffset); - AllocateResult allocation = allocate(origRefOffset, origSegment, + AllocateResult allocation = allocate(origRefOffset, origSegment, capTable, totalSize + Constants.POINTER_SIZE_IN_WORDS, WirePointer.LIST); @@ -672,7 +694,7 @@ static T getWritableStructListPointer(ListBuilder.Factory factory, memset(resolved.segment.buffer, resolved.ptr * Constants.BYTES_PER_WORD, (byte)0, (1 + oldStep * elementCount) * Constants.BYTES_PER_WORD); - return factory.constructBuilder(allocation.segment, newPtr * Constants.BYTES_PER_WORD, + return factory.constructBuilder(allocation.segment, capTable, newPtr * Constants.BYTES_PER_WORD, elementCount, newStep * Constants.BITS_PER_WORD, newDataSize * Constants.BITS_PER_WORD, @@ -687,7 +709,7 @@ static T getWritableStructListPointer(ListBuilder.Factory factory, if (oldSize == ElementSize.VOID) { //# Nothing to copy, just allocate a new list. - return initStructListPointer(factory, origRefOffset, origSegment, + return initStructListPointer(factory, capTable, origRefOffset, origSegment, elementCount, elementSize); } else { //# Upgrading to an inline composite list. @@ -713,7 +735,7 @@ static T getWritableStructListPointer(ListBuilder.Factory factory, //# Don't let allocate() zero out the object just yet. zeroPointerAndFars(origSegment, origRefOffset); - AllocateResult allocation = allocate(origRefOffset, origSegment, + AllocateResult allocation = allocate(origRefOffset, origSegment, capTable, totalWords + Constants.POINTER_SIZE_IN_WORDS, WirePointer.LIST); @@ -751,7 +773,7 @@ static T getWritableStructListPointer(ListBuilder.Factory factory, memset(resolved.segment.buffer, resolved.ptr * Constants.BYTES_PER_WORD, (byte)0, roundBitsUpToBytes(oldStep * elementCount)); - return factory.constructBuilder(allocation.segment, newPtr * Constants.BYTES_PER_WORD, + return factory.constructBuilder(allocation.segment, capTable, newPtr * Constants.BYTES_PER_WORD, elementCount, newStep * Constants.BITS_PER_WORD, newDataSize * Constants.BITS_PER_WORD, @@ -763,12 +785,13 @@ static T getWritableStructListPointer(ListBuilder.Factory factory, // size is in bytes static Text.Builder initTextPointer(int refOffset, SegmentBuilder segment, + CapTableBuilder capTable, int size) { //# The byte list must include a NUL terminator. int byteSize = size + 1; //# Allocate the space. - AllocateResult allocation = allocate(refOffset, segment, roundBytesUpToWords(byteSize), + AllocateResult allocation = allocate(refOffset, segment, capTable, roundBytesUpToWords(byteSize), WirePointer.LIST); //# Initialize the pointer. @@ -779,8 +802,9 @@ static Text.Builder initTextPointer(int refOffset, static Text.Builder setTextPointer(int refOffset, SegmentBuilder segment, + CapTableBuilder capTable, Text.Reader value) { - Text.Builder builder = initTextPointer(refOffset, segment, value.size); + Text.Builder builder = initTextPointer(refOffset, segment, capTable, value.size); ByteBuffer slice = value.buffer.duplicate(); slice.position(value.offset); @@ -792,6 +816,7 @@ static Text.Builder setTextPointer(int refOffset, static Text.Builder getWritableTextPointer(int refOffset, SegmentBuilder segment, + CapTableBuilder capTable, ByteBuffer defaultBuffer, int defaultOffset, int defaultSize) { @@ -801,7 +826,7 @@ static Text.Builder getWritableTextPointer(int refOffset, if (defaultBuffer == null) { return new Text.Builder(); } else { - Text.Builder builder = initTextPointer(refOffset, segment, defaultSize); + Text.Builder builder = initTextPointer(refOffset, segment, capTable, defaultSize); // TODO is there a way to do this with bulk methods? for (int i = 0; i < builder.size; ++i) { builder.buffer.put(builder.offset + i, defaultBuffer.get(defaultOffset * 8 + i)); @@ -835,9 +860,10 @@ static Text.Builder getWritableTextPointer(int refOffset, // size is in bytes static Data.Builder initDataPointer(int refOffset, SegmentBuilder segment, + CapTableBuilder capTable, int size) { //# Allocate the space. - AllocateResult allocation = allocate(refOffset, segment, roundBytesUpToWords(size), + AllocateResult allocation = allocate(refOffset, segment, capTable, roundBytesUpToWords(size), WirePointer.LIST); //# Initialize the pointer. @@ -848,8 +874,9 @@ static Data.Builder initDataPointer(int refOffset, static Data.Builder setDataPointer(int refOffset, SegmentBuilder segment, + CapTableBuilder capTable, Data.Reader value) { - Data.Builder builder = initDataPointer(refOffset, segment, value.size); + Data.Builder builder = initDataPointer(refOffset, segment, capTable, value.size); // TODO is there a way to do this with bulk methods? for (int i = 0; i < builder.size; ++i) { @@ -860,6 +887,7 @@ static Data.Builder setDataPointer(int refOffset, static Data.Builder getWritableDataPointer(int refOffset, SegmentBuilder segment, + CapTableBuilder capTable, ByteBuffer defaultBuffer, int defaultOffset, int defaultSize) { @@ -869,7 +897,7 @@ static Data.Builder getWritableDataPointer(int refOffset, if (defaultBuffer == null) { return new Data.Builder(); } else { - Data.Builder builder = initDataPointer(refOffset, segment, defaultSize); + Data.Builder builder = initDataPointer(refOffset, segment, capTable, defaultSize); // TODO is there a way to do this with bulk methods? for (int i = 0; i < builder.size; ++i) { builder.buffer.put(builder.offset + i, defaultBuffer.get(defaultOffset * 8 + i)); @@ -896,6 +924,7 @@ static Data.Builder getWritableDataPointer(int refOffset, static T readStructPointer(StructReader.Factory factory, SegmentReader segment, + CapTableReader capTable, int refOffset, SegmentReader defaultSegment, int defaultOffset, @@ -932,19 +961,19 @@ static T readStructPointer(StructReader.Factory factory, } return factory.constructReader(resolved.segment, + capTable, resolved.ptr * Constants.BYTES_PER_WORD, (resolved.ptr + dataSizeWords), dataSizeWords * Constants.BITS_PER_WORD, (short) ptrCount, nestingLimit - 1); - } - static SegmentBuilder setStructPointer(SegmentBuilder segment, int refOffset, StructReader value) { + static SegmentBuilder setStructPointer(SegmentBuilder segment, CapTableBuilder capTable, int refOffset, StructReader value) { int dataSize = roundBitsUpToWords(value.dataSize); int totalSize = dataSize + value.pointerCount * Constants.POINTER_SIZE_IN_WORDS; - AllocateResult allocation = allocate(refOffset, segment, totalSize, WirePointer.STRUCT); + AllocateResult allocation = allocate(refOffset, segment, capTable, totalSize, WirePointer.STRUCT); StructPointer.set(allocation.segment.buffer, allocation.refOffset, (short)dataSize, value.pointerCount); @@ -957,25 +986,25 @@ static SegmentBuilder setStructPointer(SegmentBuilder segment, int refOffset, St int pointerSection = allocation.ptr + dataSize; for (int i = 0; i < value.pointerCount; ++i) { - copyPointer(allocation.segment, pointerSection + i, value.segment, value.pointers + i, + copyPointer(allocation.segment, capTable, pointerSection + i, value.segment, value.capTable, value.pointers + i, value.nestingLimit); } return allocation.segment; }; - static SegmentBuilder setListPointer(SegmentBuilder segment, int refOffset, ListReader value) { + static SegmentBuilder setListPointer(SegmentBuilder segment, CapTableBuilder capTable, int refOffset, ListReader value) { int totalSize = roundBitsUpToWords((long) value.elementCount * value.step); if (value.step <= Constants.BITS_PER_WORD) { //# List of non-structs. - AllocateResult allocation = allocate(refOffset, segment, totalSize, WirePointer.LIST); + AllocateResult allocation = allocate(refOffset, segment, capTable, totalSize, WirePointer.LIST); if (value.structPointerCount == 1) { //# List of pointers. ListPointer.set(allocation.segment.buffer, allocation.refOffset, ElementSize.POINTER, value.elementCount); for (int i = 0; i < value.elementCount; ++i) { - copyPointer(allocation.segment, allocation.ptr + i, - value.segment, value.ptr / Constants.BYTES_PER_WORD + i, value.nestingLimit); + copyPointer(allocation.segment, capTable,allocation.ptr + i, + value.segment, value.capTable, value.ptr / Constants.BYTES_PER_WORD + i, value.nestingLimit); } } else { //# List of data. @@ -998,7 +1027,7 @@ static SegmentBuilder setListPointer(SegmentBuilder segment, int refOffset, List return allocation.segment; } else { //# List of structs. - AllocateResult allocation = allocate(refOffset, segment, totalSize + Constants.POINTER_SIZE_IN_WORDS, WirePointer.LIST); + AllocateResult allocation = allocate(refOffset, segment, capTable, totalSize + Constants.POINTER_SIZE_IN_WORDS, WirePointer.LIST); ListPointer.setInlineComposite(allocation.segment.buffer, allocation.refOffset, totalSize); short dataSize = (short)roundBitsUpToWords(value.structDataSize); short pointerCount = value.structPointerCount; @@ -1019,7 +1048,7 @@ static SegmentBuilder setListPointer(SegmentBuilder segment, int refOffset, List srcOffset += dataSize; for (int j = 0; j < pointerCount; ++j) { - copyPointer(allocation.segment, dstOffset, value.segment, srcOffset, value.nestingLimit); + copyPointer(allocation.segment, capTable, dstOffset, value.segment, value.capTable, srcOffset, value.nestingLimit); dstOffset += Constants.POINTER_SIZE_IN_WORDS; srcOffset += Constants.POINTER_SIZE_IN_WORDS; } @@ -1045,8 +1074,8 @@ static void memcpy(ByteBuffer dstBuffer, int dstByteOffset, ByteBuffer srcBuffer dstDup.put(srcDup); } - static SegmentBuilder copyPointer(SegmentBuilder dstSegment, int dstOffset, - SegmentReader srcSegment, int srcOffset, int nestingLimit) { + static SegmentBuilder copyPointer(SegmentBuilder dstSegment, CapTableBuilder dstCapTable, int dstOffset, + SegmentReader srcSegment, CapTableReader srcCapTable, int srcOffset, int nestingLimit) { // Deep-copy the object pointed to by src into dst. It turns out we can't reuse // readStructPointer(), etc. because they do type checking whereas here we want to accept any // valid pointer. @@ -1067,8 +1096,9 @@ static SegmentBuilder copyPointer(SegmentBuilder dstSegment, int dstOffset, throw new DecodeException("Message is too deeply nested or contains cycles. See org.capnproto.ReaderOptions."); } resolved.segment.arena.checkReadLimit(StructPointer.wordSize(resolved.ref)); - return setStructPointer(dstSegment, dstOffset, + return setStructPointer(dstSegment, dstCapTable, dstOffset, new StructReader(resolved.segment, + srcCapTable, resolved.ptr * Constants.BYTES_PER_WORD, resolved.ptr + StructPointer.dataSize(resolved.ref), StructPointer.dataSize(resolved.ref) * Constants.BITS_PER_WORD, @@ -1102,7 +1132,7 @@ static SegmentBuilder copyPointer(SegmentBuilder dstSegment, int dstOffset, resolved.segment.arena.checkReadLimit(elementCount); } - return setListPointer(dstSegment, dstOffset, + return setListPointer(dstSegment, dstCapTable, dstOffset, new ListReader(resolved.segment, ptr * Constants.BYTES_PER_WORD, elementCount, @@ -1125,7 +1155,7 @@ static SegmentBuilder copyPointer(SegmentBuilder dstSegment, int dstOffset, resolved.segment.arena.checkReadLimit(elementCount); } - return setListPointer(dstSegment, dstOffset, + return setListPointer(dstSegment, dstCapTable, dstOffset, new ListReader(resolved.segment, resolved.ptr * Constants.BYTES_PER_WORD, elementCount, @@ -1138,7 +1168,14 @@ static SegmentBuilder copyPointer(SegmentBuilder dstSegment, int dstOffset, case WirePointer.FAR : throw new DecodeException("Unexpected FAR pointer."); case WirePointer.OTHER : - throw new RuntimeException("copyPointer is unimplemented for OTHER pointers"); + if (WirePointer.isCapability(srcRef)) { + ClientHook cap = readCapabilityPointer(srcSegment, srcCapTable, srcOffset, 0); + setCapabilityPointer(dstSegment, dstCapTable, dstOffset, cap); + return dstSegment; + } + else { + throw new RuntimeException("copyPointer is unimplemented for OTHER pointers"); + } } throw new RuntimeException("unreachable"); } @@ -1146,6 +1183,7 @@ static SegmentBuilder copyPointer(SegmentBuilder dstSegment, int dstOffset, static T readListPointer(ListReader.Factory factory, SegmentReader segment, int refOffset, + CapTableReader capTable, SegmentReader defaultSegment, int defaultOffset, byte expectedElementSize, @@ -1155,7 +1193,7 @@ static T readListPointer(ListReader.Factory factory, if (WirePointer.isNull(ref)) { if (defaultSegment == null) { - return factory.constructReader(SegmentReader.EMPTY, 0, 0, 0, 0, (short) 0, 0x7fffffff); + return factory.constructReader(SegmentReader.EMPTY, capTable, 0, 0, 0, 0, (short) 0, 0x7fffffff); } else { segment = defaultSegment; refOffset = defaultOffset; @@ -1224,7 +1262,7 @@ static T readListPointer(ListReader.Factory factory, default: break; } - return factory.constructReader(resolved.segment, + return factory.constructReader(resolved.segment, capTable, ptr * Constants.BYTES_PER_WORD, size, wordsPerElement * Constants.BITS_PER_WORD, @@ -1275,6 +1313,7 @@ static T readListPointer(ListReader.Factory factory, } return factory.constructReader(resolved.segment, + capTable, resolved.ptr * Constants.BYTES_PER_WORD, elementCount, step, @@ -1357,4 +1396,46 @@ static Data.Reader readDataPointer(SegmentReader segment, return new Data.Reader(resolved.segment.buffer, resolved.ptr, size); } + static void setCapabilityPointer(SegmentBuilder segment, CapTableBuilder capTable, int refOffset, ClientHook cap) { + long ref = segment.get(refOffset); + + if (!WirePointer.isNull(ref)) { + zeroObject(segment, capTable, refOffset); + } + + if (cap == null) { + // TODO check zeroMemory behaviour + zeroPointerAndFars(segment, refOffset); + } + else if (capTable != null) { + WirePointer.setCapability(segment.buffer, refOffset, capTable.injectCap(cap)); + } + else { + assert false: "Cannot set capability pointer without capTable"; + } + } + + static ClientHook readCapabilityPointer(SegmentReader segment, CapTableReader capTable, int refOffset, int maxValue) { + long ref = segment.get(refOffset); + + if (WirePointer.isNull(ref)) { + return Capability.newNullCap(); + } + + if (WirePointer.kind(ref) != WirePointer.OTHER) { + return Capability.newBrokenCap("Calling capability extracted from a non-capability pointer."); + } + + if (capTable == null) { + return Capability.newBrokenCap("Cannot read capability pointer without capTable."); + } + + int index = WirePointer.upper32Bits(ref); + ClientHook cap = capTable.extractCap(index); + if (cap == null) { + return Capability.newBrokenCap("Calling invalid capability pointer."); + } + return cap; + } + } diff --git a/runtime/src/main/java/org/capnproto/WirePointer.java b/runtime/src/main/java/org/capnproto/WirePointer.java index 4387f8ef..5e75c951 100644 --- a/runtime/src/main/java/org/capnproto/WirePointer.java +++ b/runtime/src/main/java/org/capnproto/WirePointer.java @@ -85,4 +85,14 @@ public static void setKindAndInlineCompositeListElementCount(ByteBuffer buffer, public static int upper32Bits(long wirePointer) { return (int)(wirePointer >>> 32); } + + public static boolean isCapability(long wirePointer) { + // lower 30 bits are all zero + return offsetAndKind(wirePointer) == OTHER; + } + + public static void setCapability(ByteBuffer buffer, int offset, int cap) { + setOffsetAndKind(buffer, offset, OTHER); + buffer.putInt(offset*8 + 4, cap); + } } diff --git a/runtime/src/test/java/org/capnproto/LayoutTest.java b/runtime/src/test/java/org/capnproto/LayoutTest.java index 43b498bf..7f937683 100644 --- a/runtime/src/test/java/org/capnproto/LayoutTest.java +++ b/runtime/src/test/java/org/capnproto/LayoutTest.java @@ -28,7 +28,7 @@ public void testSimpleRawDataStruct() { ReaderArena arena = new ReaderArena(new ByteBuffer[]{ buffer }, 0x7fffffffffffffffL); - StructReader reader = WireHelpers.readStructPointer(new BareStructReader(), arena.tryGetSegment(0), 0, null, 0, MAX_NESTING_LIMIT); + StructReader reader = WireHelpers.readStructPointer(new BareStructReader(), arena.tryGetSegment(0), null, 0, null, 0, MAX_NESTING_LIMIT); Assert.assertEquals(reader._getLongField(0), 0xefcdab8967452301L); Assert.assertEquals(reader._getLongField(1), 0L); @@ -86,7 +86,7 @@ public void readStructPointerShouldThrowDecodeExceptionOnOutOfBoundsStructPointe ReaderArena arena = new ReaderArena(new ByteBuffer[]{ buffer }, 0x7fffffffffffffffL); - StructReader reader = WireHelpers.readStructPointer(new BareStructReader(), arena.tryGetSegment(0), 0, null, 0, MAX_NESTING_LIMIT); + StructReader reader = WireHelpers.readStructPointer(new BareStructReader(), arena.tryGetSegment(0), null, 0, null, 0, MAX_NESTING_LIMIT); } @@ -114,7 +114,7 @@ public void readListPointerShouldThrowDecodeExceptionOnOutOfBoundsCompositeListP ReaderArena arena = new ReaderArena(new ByteBuffer[]{buffer}, 0x7fffffffffffffffL); - ListReader reader = WireHelpers.readListPointer(new BareListReader(), arena.tryGetSegment(0), 0, null, 0, (byte) 0, MAX_NESTING_LIMIT); + ListReader reader = WireHelpers.readListPointer(new BareListReader(), arena.tryGetSegment(0), 0, null, null, 0, (byte) 0, MAX_NESTING_LIMIT); } private class BareStructBuilder implements StructBuilder.Factory {