Commit 533feaa4 authored by Kenton Varda's avatar Kenton Varda

Change capability pointers to be indexes into a separate cap list so that…

Change capability pointers to be indexes into a separate cap list so that CapDescriptors can be interpreted on receipt rather than delaying until the application actually traverses the message.  This massively simplifies a lot of things.
parent 66e2e032
......@@ -102,8 +102,7 @@ void BasicReaderArena::reportReadLimitReached() {
}
}
kj::Maybe<kj::Own<ClientHook>> BasicReaderArena::extractCap(
const _::StructReader& capDescriptor) {
kj::Maybe<kj::Own<ClientHook>> BasicReaderArena::extractCap(uint index) {
return nullptr;
}
......@@ -113,8 +112,10 @@ kj::Maybe<kj::Own<ClientHook>> BasicReaderArena::newBrokenCap(kj::StringPtr desc
// =======================================================================================
ImbuedReaderArena::ImbuedReaderArena(Arena* base, CapExtractorBase* capExtractor)
: base(base), capExtractor(capExtractor), segment0(nullptr) {}
ImbuedReaderArena::ImbuedReaderArena(Arena* base, BrokenCapFactory& brokenCapFactory,
kj::Array<kj::Own<ClientHook>>&& capTable)
: base(base), brokenCapFactory(brokenCapFactory), capTable(kj::mv(capTable)),
segment0(nullptr) {}
ImbuedReaderArena::~ImbuedReaderArena() noexcept(false) {}
SegmentReader* ImbuedReaderArena::imbue(SegmentReader* baseSegment) {
......@@ -159,15 +160,21 @@ void ImbuedReaderArena::reportReadLimitReached() {
return base->reportReadLimitReached();
}
kj::Maybe<kj::Own<ClientHook>> ImbuedReaderArena::extractCap(
const _::StructReader& capDescriptor) {
_::StructReader copy = capDescriptor;
copy.unimbue();
return capExtractor->extractCapInternal(copy);
kj::Maybe<kj::Own<ClientHook>> ImbuedReaderArena::extractCap(uint index) {
if (index < capTable.size()) {
return capTable[index]->addRef();
} else {
KJ_FAIL_ASSERT("Invalid capability descriptor in message.") {
// Work around http://gcc.gnu.org/bugzilla/show_bug.cgi?id=33799 and
// http://llvm.org/bugs/show_bug.cgi?id=12286.
break;
}
return brokenCapFactory.newBrokenCap("Calling capability from invalid descriptor.");
}
}
kj::Maybe<kj::Own<ClientHook>> ImbuedReaderArena::newBrokenCap(kj::StringPtr description) {
return capExtractor->newBrokenCapInternal(description);
return brokenCapFactory.newBrokenCap(description);
}
// =======================================================================================
......@@ -235,7 +242,7 @@ BasicBuilderArena::AllocateResult BasicBuilderArena::allocate(WordCount amount)
this, SegmentId(segmentState->builders.size() + 1),
message->allocateSegment(amount / WORDS), &this->dummyLimiter);
SegmentBuilder* result = newBuilder.get();
segmentState->builders.push_back(kj::mv(newBuilder));
segmentState->builders.add(kj::mv(newBuilder));
// Keep forOutput the right size so that we don't have to re-allocate during
// getSegmentsForOutput(), which callers might reasonably expect is a thread-safe method.
......@@ -304,8 +311,7 @@ void BasicBuilderArena::reportReadLimitReached() {
}
}
kj::Maybe<kj::Own<ClientHook>> BasicBuilderArena::extractCap(
const _::StructReader& capDescriptor) {
kj::Maybe<kj::Own<ClientHook>> BasicBuilderArena::extractCap(uint index) {
return nullptr;
}
......@@ -313,21 +319,21 @@ kj::Maybe<kj::Own<ClientHook>> BasicBuilderArena::newBrokenCap(kj::StringPtr des
return nullptr;
}
OrphanBuilder BasicBuilderArena::injectCap(kj::Own<ClientHook>&& cap) {
uint BasicBuilderArena::injectCap(kj::Own<ClientHook>&& cap) {
KJ_FAIL_REQUIRE("Cannot inject capability into a builder that has not been imbued with a "
"capability context.") {
return OrphanBuilder();
return 0;
}
}
void BasicBuilderArena::dropCap(const _::StructReader& capDescriptor) {
void BasicBuilderArena::dropCap(uint index) {
// They only way we could have a cap in the first place is if the error was already reported...
}
// =======================================================================================
ImbuedBuilderArena::ImbuedBuilderArena(BuilderArena* base, CapInjectorBase* capInjector)
: base(base), capInjector(capInjector), segment0(nullptr) {}
ImbuedBuilderArena::ImbuedBuilderArena(BuilderArena* base, BrokenCapFactory& brokenCapFactory)
: base(base), brokenCapFactory(brokenCapFactory), segment0(nullptr) {}
ImbuedBuilderArena::~ImbuedBuilderArena() noexcept(false) {}
SegmentBuilder* ImbuedBuilderArena::imbue(SegmentBuilder* baseSegment) {
......@@ -375,15 +381,21 @@ void ImbuedBuilderArena::reportReadLimitReached() {
base->reportReadLimitReached();
}
kj::Maybe<kj::Own<ClientHook>> ImbuedBuilderArena::extractCap(
const _::StructReader& capDescriptor) {
_::StructReader copy = capDescriptor;
copy.unimbue();
return capInjector->getInjectedCapInternal(copy);
kj::Maybe<kj::Own<ClientHook>> ImbuedBuilderArena::extractCap(uint index) {
if (index < capTable.size()) {
return capTable[index]->addRef();
} else {
KJ_FAIL_ASSERT("Invalid capability descriptor in message.") {
// Work around http://gcc.gnu.org/bugzilla/show_bug.cgi?id=33799 and
// http://llvm.org/bugs/show_bug.cgi?id=12286.
break;
}
return brokenCapFactory.newBrokenCap("Calling capability from invalid descriptor.");
}
}
kj::Maybe<kj::Own<ClientHook>> ImbuedBuilderArena::newBrokenCap(kj::StringPtr description) {
return capInjector->newBrokenCapInternal(description);
return brokenCapFactory.newBrokenCap(description);
}
SegmentBuilder* ImbuedBuilderArena::getSegment(SegmentId id) {
......@@ -396,14 +408,19 @@ BuilderArena::AllocateResult ImbuedBuilderArena::allocate(WordCount amount) {
return result;
}
OrphanBuilder ImbuedBuilderArena::injectCap(kj::Own<ClientHook>&& cap) {
return capInjector->injectCapInternal(this, kj::mv(cap));
uint ImbuedBuilderArena::injectCap(kj::Own<ClientHook>&& cap) {
// TODO(perf): Detect if the cap is already on the table and reuse the index? Perhaps this
// doesn't happen enough to be worth the effort.
uint result = capTable.size();
capTable.add(kj::mv(cap));
return result;
}
void ImbuedBuilderArena::dropCap(const StructReader& capDescriptor) {
_::StructReader copy = capDescriptor;
copy.unimbue();
capInjector->dropCapInternal(copy);
void ImbuedBuilderArena::dropCap(uint index) {
KJ_ASSERT(index < capTable.size(), "Invalid capability descriptor in message.") {
return;
}
capTable[index] = nullptr;
}
} // namespace _ (private)
......
......@@ -28,19 +28,18 @@
#error "This header is only meant to be included by Cap'n Proto's own source code."
#endif
#include <vector>
#include <unordered_map>
#include <kj/common.h>
#include <kj/mutex.h>
#include <kj/exception.h>
#include <kj/vector.h>
#include "common.h"
#include "message.h"
#include "layout.h"
#include "capability.h"
namespace capnp {
class CapExtractorBase;
class CapInjectorBase;
class ClientHook;
namespace _ { // private
......@@ -98,6 +97,14 @@ private:
KJ_DISALLOW_COPY(ReadLimiter);
};
class BrokenCapFactory {
// Callback for constructing broken caps. We use this so that we can avoid arena.c++ having a
// link-time dependency on capability code that lives in libcapnp-rpc.
public:
virtual kj::Own<ClientHook> newBrokenCap(kj::StringPtr description) = 0;
};
class SegmentReader {
public:
inline SegmentReader(Arena* arena, SegmentId id, kj::ArrayPtr<const word> ptr,
......@@ -134,11 +141,6 @@ class ImbuedSegmentReader: public SegmentReader {
public:
inline ImbuedSegmentReader(Arena* arena, SegmentReader* base);
inline ImbuedSegmentReader(decltype(nullptr));
inline SegmentReader* unimbue();
private:
SegmentReader* base;
};
class SegmentBuilder: public SegmentReader {
......@@ -183,11 +185,6 @@ public:
inline ImbuedSegmentBuilder(decltype(nullptr));
KJ_DISALLOW_COPY(ImbuedSegmentBuilder);
inline SegmentBuilder* unimbue();
private:
SegmentBuilder* base;
};
class Arena {
......@@ -202,9 +199,10 @@ public:
// the VALIDATE_INPUT() macro which may throw an exception; if it returns normally, the caller
// will need to continue with default values.
virtual kj::Maybe<kj::Own<ClientHook>> extractCap(const _::StructReader& capDescriptor) = 0;
// Given a StructReader for a capability descriptor embedded in the message, return the
// corresponding capability. Returns null if the message is not imbued with a capability context.
virtual kj::Maybe<kj::Own<ClientHook>> extractCap(uint index) = 0;
// Extract the capability at the given index. If the index is invalid, returns a dummy
// capability whose methods all throw. Returns null only if the message is not imbued with a
// capability context.
virtual kj::Maybe<kj::Own<ClientHook>> newBrokenCap(kj::StringPtr description) = 0;
// Returns a capability which, when called, always throws an exception with the given description.
......@@ -220,7 +218,7 @@ public:
// implements Arena ------------------------------------------------
SegmentReader* tryGetSegment(SegmentId id) override;
void reportReadLimitReached() override;
kj::Maybe<kj::Own<ClientHook>> extractCap(const _::StructReader& capDescriptor);
kj::Maybe<kj::Own<ClientHook>> extractCap(uint index);
kj::Maybe<kj::Own<ClientHook>> newBrokenCap(kj::StringPtr description);
private:
......@@ -243,7 +241,8 @@ private:
class ImbuedReaderArena final: public Arena {
public:
ImbuedReaderArena(Arena* base, CapExtractorBase* capExtractor);
ImbuedReaderArena(Arena* base, BrokenCapFactory& brokenCapFactory,
kj::Array<kj::Own<ClientHook>>&& capTable);
~ImbuedReaderArena() noexcept(false);
KJ_DISALLOW_COPY(ImbuedReaderArena);
......@@ -252,12 +251,13 @@ public:
// implements Arena ------------------------------------------------
SegmentReader* tryGetSegment(SegmentId id) override;
void reportReadLimitReached() override;
kj::Maybe<kj::Own<ClientHook>> extractCap(const _::StructReader& capDescriptor);
kj::Maybe<kj::Own<ClientHook>> extractCap(uint index);
kj::Maybe<kj::Own<ClientHook>> newBrokenCap(kj::StringPtr description);
private:
Arena* base;
CapExtractorBase* capExtractor;
BrokenCapFactory& brokenCapFactory;
kj::Array<kj::Own<ClientHook>> capTable;
// Optimize for single-segment messages so that small messages are handled quickly.
ImbuedSegmentReader segment0;
......@@ -284,11 +284,12 @@ public:
// the arena is guaranteed to succeed. Therefore callers should try to allocate from a specific
// segment first if there is one, then fall back to the arena.
virtual OrphanBuilder injectCap(kj::Own<ClientHook>&& cap) = 0;
// Add the capability to the message and initialize the given pointer as an interface pointer
// pointing to this cap.
virtual uint injectCap(kj::Own<ClientHook>&& cap) = 0;
// Add the capability to the message and return its index. If the same ClientHook is injected
// twice, this may return the same index both times, but in this case dropCap() needs to be
// called an equal number of times to actually remove the cap.
virtual void dropCap(const StructReader& capDescriptor) = 0;
virtual void dropCap(uint index) = 0;
// Remove a capability injected earlier. Called when the pointer is overwritten or zero'd out.
};
......@@ -310,14 +311,14 @@ public:
// implements Arena ------------------------------------------------
SegmentReader* tryGetSegment(SegmentId id) override;
void reportReadLimitReached() override;
kj::Maybe<kj::Own<ClientHook>> extractCap(const _::StructReader& capDescriptor);
kj::Maybe<kj::Own<ClientHook>> extractCap(uint index);
kj::Maybe<kj::Own<ClientHook>> newBrokenCap(kj::StringPtr description);
// implements BuilderArena -----------------------------------------
SegmentBuilder* getSegment(SegmentId id) override;
AllocateResult allocate(WordCount amount) override;
OrphanBuilder injectCap(kj::Own<ClientHook>&& cap);
void dropCap(const StructReader& capDescriptor);
uint injectCap(kj::Own<ClientHook>&& cap);
void dropCap(uint index);
private:
MessageBuilder* message;
......@@ -327,8 +328,8 @@ private:
kj::ArrayPtr<const word> segment0ForOutput;
struct MultiSegmentState {
std::vector<kj::Own<BasicSegmentBuilder>> builders;
std::vector<kj::ArrayPtr<const word>> forOutput;
kj::Vector<kj::Own<BasicSegmentBuilder>> builders;
kj::Vector<kj::ArrayPtr<const word>> forOutput;
};
kj::Maybe<kj::Own<MultiSegmentState>> moreSegments;
};
......@@ -337,33 +338,37 @@ class ImbuedBuilderArena final: public BuilderArena {
// A BuilderArena imbued with the ability to inject capabilities.
public:
ImbuedBuilderArena(BuilderArena* base, CapInjectorBase* capInjector);
ImbuedBuilderArena(BuilderArena* base, BrokenCapFactory& brokenCapFactory);
~ImbuedBuilderArena() noexcept(false);
KJ_DISALLOW_COPY(ImbuedBuilderArena);
SegmentBuilder* imbue(SegmentBuilder* baseSegment);
// Return an imbued SegmentBuilder corresponding to the given segment from the base arena.
inline kj::ArrayPtr<kj::Own<ClientHook>> getCapTable() { return capTable; }
// Release and return the capability table.
// implements Arena ------------------------------------------------
SegmentReader* tryGetSegment(SegmentId id) override;
void reportReadLimitReached() override;
kj::Maybe<kj::Own<ClientHook>> extractCap(const _::StructReader& capDescriptor);
kj::Maybe<kj::Own<ClientHook>> extractCap(uint index);
kj::Maybe<kj::Own<ClientHook>> newBrokenCap(kj::StringPtr description);
// implements BuilderArena -----------------------------------------
SegmentBuilder* getSegment(SegmentId id) override;
AllocateResult allocate(WordCount amount) override;
OrphanBuilder injectCap(kj::Own<ClientHook>&& cap);
void dropCap(const StructReader& capDescriptor);
uint injectCap(kj::Own<ClientHook>&& cap);
void dropCap(uint index);
private:
BuilderArena* base;
CapInjectorBase* capInjector;
BrokenCapFactory& brokenCapFactory;
kj::Vector<kj::Own<ClientHook>> capTable;
ImbuedSegmentBuilder segment0;
struct MultiSegmentState {
std::vector<kj::Maybe<kj::Own<ImbuedSegmentBuilder>>> builders;
kj::Vector<kj::Maybe<kj::Own<ImbuedSegmentBuilder>>> builders;
};
kj::Maybe<kj::Own<MultiSegmentState>> moreSegments;
};
......@@ -415,12 +420,9 @@ inline kj::ArrayPtr<const word> SegmentReader::getArray() { return ptr; }
inline void SegmentReader::unread(WordCount64 amount) { readLimiter->unread(amount); }
inline ImbuedSegmentReader::ImbuedSegmentReader(Arena* arena, SegmentReader* base)
: SegmentReader(arena, base->id, base->ptr, base->readLimiter), base(base) {}
: SegmentReader(arena, base->id, base->ptr, base->readLimiter) {}
inline ImbuedSegmentReader::ImbuedSegmentReader(decltype(nullptr))
: SegmentReader(nullptr, SegmentId(0), nullptr, nullptr), base(nullptr) {}
inline SegmentReader* ImbuedSegmentReader::unimbue() {
return base;
}
: SegmentReader(nullptr, SegmentId(0), nullptr, nullptr) {}
// -------------------------------------------------------------------
......@@ -470,13 +472,9 @@ inline BasicSegmentBuilder::BasicSegmentBuilder(
inline ImbuedSegmentBuilder::ImbuedSegmentBuilder(ImbuedBuilderArena* arena, SegmentBuilder* base)
: SegmentBuilder(arena, base->id,
kj::arrayPtr(const_cast<word*>(base->ptr.begin()), base->ptr.size()),
base->readLimiter, base->pos),
base(base) {}
base->readLimiter, base->pos) {}
inline ImbuedSegmentBuilder::ImbuedSegmentBuilder(decltype(nullptr))
: SegmentBuilder(nullptr, SegmentId(0), nullptr, nullptr, nullptr),
base(nullptr) {}
inline SegmentBuilder* ImbuedSegmentBuilder::unimbue() { return base; }
: SegmentBuilder(nullptr, SegmentId(0), nullptr, nullptr, nullptr) {}
} // namespace _ (private)
} // namespace capnp
......
......@@ -30,119 +30,70 @@
namespace capnp {
CapReaderContext::CapReaderContext(CapExtractorBase& extractor): extractor(&extractor) {}
namespace {
class BrokenCapFactoryImpl: public _::BrokenCapFactory {
public:
kj::Own<ClientHook> newBrokenCap(kj::StringPtr description) override {
return capnp::newBrokenCap(description);
}
};
static BrokenCapFactoryImpl brokenCapFactory;
} // namespace
CapReaderContext::CapReaderContext(kj::Array<kj::Own<ClientHook>>&& capTable)
: capTable(kj::mv(capTable)) {}
CapReaderContext::~CapReaderContext() noexcept(false) {
if (extractor == nullptr) {
if (capTable == nullptr) {
kj::dtor(arena());
}
}
AnyPointer::Reader CapReaderContext::imbue(AnyPointer::Reader base) {
KJ_REQUIRE(extractor != nullptr, "imbue() can only be called once.");
KJ_IF_MAYBE(oldArena, base.reader.getArena()) {
kj::ctor(arena(), oldArena, extractor);
static_assert(sizeof(arena()) <= sizeof(arenaSpace),
"arenaSpace is too small. Please increase it.");
kj::ctor(arena(), oldArena, brokenCapFactory,
kj::mv(KJ_REQUIRE_NONNULL(capTable, "imbue() can only be called once.")));
} else {
KJ_FAIL_REQUIRE("Cannot imbue unchecked message.");
}
extractor = nullptr;
capTable = nullptr;
return AnyPointer::Reader(base.reader.imbue(arena()));
}
CapBuilderContext::CapBuilderContext(CapInjectorBase& injector): injector(&injector) {}
CapBuilderContext::CapBuilderContext() {}
CapBuilderContext::~CapBuilderContext() noexcept(false) {
if (injector == nullptr) {
if (arenaAllocated) {
kj::dtor(arena());
}
}
AnyPointer::Builder CapBuilderContext::imbue(AnyPointer::Builder base) {
KJ_REQUIRE(injector != nullptr, "imbue() can only be called once.");
kj::ctor(arena(), base.builder.getArena(), injector);
injector = nullptr;
KJ_REQUIRE(!arenaAllocated, "imbue() can only be called once.");
static_assert(sizeof(arena()) <= sizeof(arenaSpace),
"arenaSpace is too small. Please increase it.");
kj::ctor(arena(), base.builder.getArena(), brokenCapFactory);
arenaAllocated = true;
return AnyPointer::Builder(base.builder.imbue(arena()));
}
// =======================================================================================
namespace _ { // private
// This is basically code for a struct defined as follows:
//
// struct TestCapDescriptor {
// index @0 :UInt32;
// }
//
// I have the code hand-written here because I didn't want to add yet another bootstrap file.
class LocalCapDescriptor::Reader {
public:
typedef LocalCapDescriptor Reads;
inline explicit Reader(_::StructReader base): _reader(base) {}
inline uint32_t getIndex() const {
return _reader.getDataField<uint32_t>(0 * ELEMENTS);
}
private:
_::StructReader _reader;
};
class LocalCapDescriptor::Builder {
public:
typedef LocalCapDescriptor Builds;
inline explicit Builder(_::StructBuilder base): _builder(base) {}
inline operator Reader() const { return Reader(_builder.asReader()); }
inline uint32_t getIndex() {
return _builder.getDataField<uint32_t>(0 * ELEMENTS);
}
inline void setIndex(uint32_t value) {
_builder.setDataField<uint32_t>(0 * ELEMENTS, value);
kj::ArrayPtr<kj::Own<ClientHook>> CapBuilderContext::getCapTable() {
if (arenaAllocated) {
return arena().getCapTable();
} else {
return nullptr;
}
private:
_::StructBuilder _builder;
};
template <>
constexpr StructSize structSize<LocalCapDescriptor>() {
return StructSize(1 * WORDS, 0 * POINTERS, FieldSize::FOUR_BYTES);
}
} // namespace _ (private)
// =======================================================================================
LocalMessage::LocalMessage(uint firstSegmentWords, AllocationStrategy allocationStrategy)
: message(firstSegmentWords, allocationStrategy),
capContext(*this),
root(capContext.imbue(message.getRoot<AnyPointer>())) {}
void LocalMessage::injectCap(_::LocalCapDescriptor::Builder descriptor, kj::Own<ClientHook>&& cap) {
auto lock = state.lockExclusive();
uint index = lock->counter++;
descriptor.setIndex(index);
lock->caps.add(kj::mv(cap));
}
kj::Own<ClientHook> LocalMessage::getInjectedCap(_::LocalCapDescriptor::Reader descriptor) {
auto lock = state.lockExclusive();
KJ_ASSERT(descriptor.getIndex() < lock->caps.size(),
"Invalid capability descriptor in message.") {
return newBrokenCap("Calling capability from invalid descriptor.");
}
return lock->caps[descriptor.getIndex()]->addRef();
}
void LocalMessage::dropCap(_::LocalCapDescriptor::Reader descriptor) {
auto lock = state.lockExclusive();
KJ_ASSERT(descriptor.getIndex() < lock->caps.size(),
"Invalid capability descriptor in message.") {
return;
}
lock->caps[descriptor.getIndex()] = nullptr;
}
// =======================================================================================
namespace {
......
......@@ -47,75 +47,6 @@ namespace capnp {
class ClientHook;
namespace _ { // private
class ImbuedReaderArena;
class ImbuedBuilderArena;
} // namespace _ (private)
class CapExtractorBase {
// Non-template base class for CapExtractor<T>.
private:
virtual kj::Own<ClientHook> extractCapInternal(const _::StructReader& capDescriptor) = 0;
virtual kj::Own<ClientHook> newBrokenCapInternal(kj::StringPtr description) = 0;
friend class _::ImbuedReaderArena;
};
class CapInjectorBase {
// Non-template base class for CapInjector<T>.
private:
virtual _::OrphanBuilder injectCapInternal(
_::BuilderArena* arena, kj::Own<ClientHook>&& cap) = 0;
virtual void dropCapInternal(const _::StructReader& capDescriptor) = 0;
virtual kj::Own<ClientHook> getInjectedCapInternal(const _::StructReader& capDescriptor) = 0;
virtual kj::Own<ClientHook> newBrokenCapInternal(kj::StringPtr description) = 0;
friend class _::ImbuedBuilderArena;
};
template <typename CapDescriptor>
class CapExtractor: public CapExtractorBase {
// Callback used to read a capability from a message, implemented by the RPC system.
// `CapDescriptor` is the struct type which the RPC implementation uses to represent
// capabilities. (On the wire, an interface pointer actually points to a struct of this type.)
public:
virtual kj::Own<ClientHook> extractCap(typename CapDescriptor::Reader descriptor) = 0;
// Given the descriptor read off the wire, construct a live capability.
private:
kj::Own<ClientHook> extractCapInternal(const _::StructReader& capDescriptor) override final;
kj::Own<ClientHook> newBrokenCapInternal(kj::StringPtr description) override final;
};
template <typename CapDescriptor>
class CapInjector: public CapInjectorBase {
// Callback used to write a capability into a message, implemented by the RPC system.
// `CapDescriptor` is the struct type which the RPC implementation uses to represent
// capabilities. (On the wire, an interface pointer actually points to a struct of this type.)
public:
virtual void injectCap(typename CapDescriptor::Builder descriptor, kj::Own<ClientHook>&& cap) = 0;
// Fill in the given descriptor so that it describes the given capability.
virtual kj::Own<ClientHook> getInjectedCap(typename CapDescriptor::Reader descriptor) = 0;
// Read back a cap that was previously injected with `injectCap`. This should return a new
// reference.
virtual void dropCap(typename CapDescriptor::Reader descriptor) = 0;
// Read back a cap that was previously injected with `injectCap`. This should return a new
// reference.
private:
_::OrphanBuilder injectCapInternal(_::BuilderArena* arena,
kj::Own<ClientHook>&& cap) override final;
void dropCapInternal(const _::StructReader& capDescriptor) override final;
kj::Own<ClientHook> getInjectedCapInternal(const _::StructReader& capDescriptor) override final;
kj::Own<ClientHook> newBrokenCapInternal(kj::StringPtr description) override final;
};
// -------------------------------------------------------------------
class CapReaderContext {
......@@ -125,13 +56,15 @@ class CapReaderContext {
// `imbue()` can only be called once per context.
public:
CapReaderContext(CapExtractorBase& extractor);
CapReaderContext(kj::Array<kj::Own<ClientHook>>&& capTable);
// `capTable` is the list of capabilities for this message.
~CapReaderContext() noexcept(false);
AnyPointer::Reader imbue(AnyPointer::Reader base);
private:
CapExtractorBase* extractor; // becomes null once arena is allocated
kj::Maybe<kj::Array<kj::Own<ClientHook>>> capTable; // becomes null once arena is allocated
void* arenaSpace[12 + sizeof(kj::MutexGuarded<void*>) / sizeof(void*)];
_::ImbuedReaderArena& arena() { return *reinterpret_cast<_::ImbuedReaderArena*>(arenaSpace); }
......@@ -146,14 +79,17 @@ class CapBuilderContext {
// `imbue()` can only be called once per context.
public:
CapBuilderContext(CapInjectorBase& injector);
CapBuilderContext();
~CapBuilderContext() noexcept(false);
AnyPointer::Builder imbue(AnyPointer::Builder base);
kj::ArrayPtr<kj::Own<ClientHook>> getCapTable();
// Return the table of capabilities injected into the message.
private:
CapInjectorBase* injector; // becomes null once arena is allocated
void* arenaSpace[13];
bool arenaAllocated = false;
void* arenaSpace[15];
_::ImbuedBuilderArena& arena() { return *reinterpret_cast<_::ImbuedBuilderArena*>(arenaSpace); }
......@@ -162,16 +98,7 @@ private:
// -------------------------------------------------------------------
namespace _ { // private
struct LocalCapDescriptor {
class Reader;
class Builder;
};
} // namespace _ (private)
class LocalMessage final: private CapInjector<_::LocalCapDescriptor> {
class LocalMessage final {
// An in-process message which can contain capabilities. Use in place of MallocMessageBuilder
// when you need to be able to construct a message in-memory that contains capabilities, and this
// message will never leave the process. You cannot serialize this message, since it doesn't
......@@ -188,16 +115,6 @@ private:
MallocMessageBuilder message;
CapBuilderContext capContext;
AnyPointer::Builder root;
struct State {
uint counter;
kj::Vector<kj::Own<ClientHook>> caps;
};
kj::MutexGuarded<State> state;
void injectCap(_::LocalCapDescriptor::Builder descriptor, kj::Own<ClientHook>&& cap) override;
kj::Own<ClientHook> getInjectedCap(_::LocalCapDescriptor::Reader descriptor) override;
void dropCap(_::LocalCapDescriptor::Reader descriptor) override;
};
kj::Own<ClientHook> newBrokenCap(kj::StringPtr reason);
......@@ -207,51 +124,6 @@ kj::Own<ClientHook> newBrokenCap(kj::Exception&& reason);
kj::Own<PipelineHook> newBrokenPipeline(kj::Exception&& reason);
// Helper function that creates a pipeline which simply throws exceptions when called.
// =======================================================================================
// inline implementation details
template <typename CapDescriptor>
kj::Own<ClientHook> CapExtractor<CapDescriptor>::extractCapInternal(
const _::StructReader& capDescriptor) {
return extractCap(typename CapDescriptor::Reader(capDescriptor));
}
template <typename CapDescriptor>
kj::Own<ClientHook> CapExtractor<CapDescriptor>::newBrokenCapInternal(kj::StringPtr description) {
// Notice that because this method was virtualized and then implemented in the template,
// we can call newBrokenCap which is only implemented in libcapnp-rpc even though arena.c++
// (in libcapnp proper) is the only caller of this method.
return newBrokenCap(description.cStr());
}
template <typename CapDescriptor>
_::OrphanBuilder CapInjector<CapDescriptor>::injectCapInternal(
_::BuilderArena* arena, kj::Own<ClientHook>&& cap) {
auto result = _::OrphanBuilder::initStruct(arena, _::structSize<CapDescriptor>());
injectCap(typename CapDescriptor::Builder(result.asStruct(_::structSize<CapDescriptor>())),
kj::mv(cap));
return kj::mv(result);
}
template <typename CapDescriptor>
void CapInjector<CapDescriptor>::dropCapInternal(const _::StructReader& capDescriptor) {
dropCap(typename CapDescriptor::Reader(capDescriptor));
}
template <typename CapDescriptor>
kj::Own<ClientHook> CapInjector<CapDescriptor>::getInjectedCapInternal(
const _::StructReader& capDescriptor) {
return getInjectedCap(typename CapDescriptor::Reader(capDescriptor));
}
template <typename CapDescriptor>
kj::Own<ClientHook> CapInjector<CapDescriptor>::newBrokenCapInternal(kj::StringPtr description) {
// Notice that because this method was virtualized and then implemented in the template,
// we can call newBrokenCap which is only implemented in libcapnp-rpc even though arena.c++
// (in libcapnp proper) is the only caller of this method.
return newBrokenCap(description.cStr());
}
} // namespace capnp
#endif // CAPNP_CAPABILITY_CONTEXT_H_
This diff is collapsed.
......@@ -767,7 +767,8 @@ private:
// FAR pointer.
word* location;
// Pointer to the object, or nullptr if the pointer is null.
// Pointer to the object, or nullptr if the pointer is null. For capabilities, we make this
// point at `tag` just so that it is non-null for operator==, but it is never used.
inline OrphanBuilder(const void* tagPtr, SegmentBuilder* segment, word* location)
: segment(segment), location(location) {
......
......@@ -77,8 +77,6 @@ private:
friend class Orphan;
friend class Orphanage;
friend class MessageBuilder;
template <typename>
friend class CapInjector;
};
class Orphanage: private kj::DisallowConstCopy {
......@@ -135,8 +133,6 @@ private:
struct NewOrphanListImpl;
friend class MessageBuilder;
template <typename>
friend class CapInjector;
};
// =======================================================================================
......
......@@ -79,17 +79,22 @@ public:
returnTypes[std::make_pair(sender, call.getQuestionId())] = resultType;
}
CapExtractorImpl extractor;
CapReaderContext context(extractor);
auto payload = call.getParams();
auto capTableReader = payload.getCapTable();
auto capTable = kj::heapArrayBuilder<kj::Own<ClientHook>>(capTableReader.size());
for (uint i = 0; i < capTableReader.size(); i++) {
capTable.add(newBrokenCap("fake cap"));
}
CapReaderContext context(capTable.finish());
auto params = kj::str(context.imbue(call.getParams()).getAs<DynamicStruct>(paramType));
auto params = kj::str(context.imbue(payload.getContent()).getAs<DynamicStruct>(paramType));
auto sendResultsTo = call.getSendResultsTo();
return kj::str(senderName, "(", call.getQuestionId(), "): call ",
call.getTarget(), " <- ", interfaceName, ".",
methodProto.getName(), params,
" caps:[", extractor.printCaps(), "]",
" caps:[", kj::strArray(payload.getCapTable(), ", "), "]",
sendResultsTo.isCaller() ? kj::str()
: kj::str(" sendResultsTo:", sendResultsTo));
}
......@@ -111,19 +116,25 @@ public:
break;
}
CapExtractorImpl extractor;
CapReaderContext context(extractor);
auto imbued = context.imbue(ret.getResults());
auto payload = ret.getResults();
auto capTableReader = payload.getCapTable();
auto capTable = kj::heapArrayBuilder<kj::Own<ClientHook>>(capTableReader.size());
for (uint i = 0; i < capTableReader.size(); i++) {
capTable.add(newBrokenCap("fake cap"));
}
CapReaderContext context(capTable.finish());
auto imbued = context.imbue(payload.getContent());
if (schema.getProto().isStruct()) {
auto results = kj::str(imbued.getAs<DynamicStruct>(schema.asStruct()));
return kj::str(senderName, "(", ret.getQuestionId(), "): return ", results,
" caps:[", extractor.printCaps(), "]");
" caps:[", kj::strArray(payload.getCapTable(), ", "), "]");
} else if (schema.getProto().isInterface()) {
imbued.getAs<DynamicCapability>(schema.asInterface());
return kj::str(senderName, "(", ret.getQuestionId(), "): return cap ",
extractor.printCaps());
kj::strArray(payload.getCapTable(), ", "));
} else {
break;
}
......@@ -148,21 +159,6 @@ public:
private:
std::map<uint64_t, InterfaceSchema> schemas;
std::map<std::pair<Sender, uint32_t>, Schema> returnTypes;
class CapExtractorImpl: public CapExtractor<rpc::CapDescriptor> {
public:
kj::Own<ClientHook> extractCap(rpc::CapDescriptor::Reader descriptor) override {
caps.add(kj::str(descriptor));
return newBrokenCap("fake cap");
}
kj::String printCaps() {
return kj::strArray(caps, ", ");
}
private:
kj::Vector<kj::String> caps;
};
};
// =======================================================================================
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -91,6 +91,16 @@ public:
builder.removeLast();
}
inline void resize(size_t size) {
if (size > builder.capacity()) grow(size);
while (builder.size() < size) {
builder.add(T());
}
while (builder.size() > size) {
builder.removeLast();
}
}
private:
ArrayBuilder<T> builder;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment