Chromium Code Reviews| Index: mojo/public/bindings/test.cc |
| diff --git a/mojo/public/bindings/test.cc b/mojo/public/bindings/test.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..1322e40a7f344140b04ab1b527ee7b9176a5bcec |
| --- /dev/null |
| +++ b/mojo/public/bindings/test.cc |
| @@ -0,0 +1,651 @@ |
| +/* |
| +// HYPOTHETICAL IDL: |
| + |
| +struct Bar { |
| + alpha @0 :uint8; |
| + beta @1 :uint8; |
| + gamma @2 :uint8; |
| +}; |
| + |
| +struct Foo { |
| + x @0 :int32; |
| + y @1 :int32; |
| + a @2 :bool; |
| + b @3 :bool; |
| + c @4 :bool; |
| + bar @5 :Bar; |
| + extra_bars @7 :array(Bar) [optional]; |
| + data @6 :array(uint8); |
| +}; |
| + |
| +interface Blah { |
| + Frobinate @0 (foo @0 :Foo, baz @1 :bool); |
| +}; |
| + |
| +*/ |
| + |
| +#include <assert.h> |
| +#include <stddef.h> |
| +#include <stdint.h> |
| +#include <stdio.h> |
| +#include <stdlib.h> |
| +#include <string.h> |
| + |
| +#include <new> |
| +#include <vector> |
| + |
| +// This would be part of some header file we'd provide. |
| + |
| +typedef uint32_t Handle; |
| + |
| +struct Message { |
| + uint32_t name; |
| + uint8_t* data_start; |
| + uint8_t* data_end; |
| + Handle* handles_start; |
| + Handle* handles_end; |
| +}; |
| + |
| +struct MessageHeader { |
| + uint32_t num_bytes; |
| + uint32_t name; |
| +}; |
| + |
| +struct StructHeader { |
| + uint32_t num_bytes; |
| + uint32_t num_fields; |
| +}; |
| + |
| +struct ArrayHeader { |
| + uint32_t num_bytes; |
| + uint32_t num_elements; |
| +}; |
| + |
| +template <typename T> class Array; |
| + |
| +template <typename T> |
| +union StructPointer { |
| + uint64_t offset; |
| + T* ptr; |
| +}; |
| + |
| +template <typename T> |
| +union ArrayPointer { |
| + uint64_t offset; |
| + Array<T>* ptr; |
| +}; |
| + |
| +inline void EncodePointer(void* address, uint64_t* offset) { |
| + if (!address) { |
| + *offset = 0; |
| + return; |
| + } |
| + uint8_t* p_obj = reinterpret_cast<uint8_t*>(address); |
| + uint8_t* p_slot = reinterpret_cast<uint8_t*>(offset); |
| + assert(p_obj > p_slot); |
| + *offset = p_obj - p_slot; |
| +} |
| + |
| +template <typename T> |
| +inline void DecodePointer(uint64_t* offset, T** ptr) { |
| + if (!*offset) { |
| + *ptr = NULL; |
| + return; |
| + } |
| + uint8_t* p_slot = reinterpret_cast<uint8_t*>(offset); |
| + *ptr = reinterpret_cast<T*>(p_slot + *offset); |
|
piman
2013/09/25 17:26:25
Note: strict aliasing rules only allow reinterpret
|
| +} |
| + |
| +template <typename T> |
| +struct ArrayTraits { |
| + typedef T ElementType; |
| + |
| + static T& ToElementRef(ElementType& e) { return e; } |
| + static T const& ToElementConstRef(const ElementType& e) { return e; } |
| + |
| + template <typename Allocator> |
| + static void CloneElements(Allocator* allocator, |
| + ArrayHeader* header, |
| + ElementType* elements) { |
| + } |
| + |
| + static void EncodePointersAndHandles(ArrayHeader* header, |
| + ElementType* elements, |
| + std::vector<Handle>* handles) { |
| + } |
| + static bool DecodePointersAndHandles(ArrayHeader* header, |
| + ElementType* elements, |
| + const Message& message) { |
| + return true; |
| + } |
| +}; |
| + |
| +template <typename P> |
| +struct ArrayTraits<P*> { |
| + typedef StructPointer<P> ElementType; |
| + |
| + static P*& ToElementRef(ElementType& e) { return e.ptr; } |
| + static P* const& ToElementConstRef(const ElementType& e) { return e.ptr; } |
| + |
| + template <typename Allocator> |
| + static void CloneElements(Allocator* allocator, |
| + ArrayHeader* header, |
| + ElementType* elements) { |
| + for (uint32_t i = 0; i < header->num_elements; ++i) { |
| + if (elements[i].ptr) |
| + elements[i].ptr = elements[i].ptr->Clone(allocator); |
| + } |
| + } |
| + |
| + static void EncodePointersAndHandles(ArrayHeader* header, |
| + ElementType* elements, |
| + std::vector<Handle>* handles) { |
| + for (uint32_t i = 0; i < header->num_elements; ++i) { |
| + if (elements[i].ptr) |
| + elements[i].ptr->EncodePointersAndHandles(handles); |
| + EncodePointer(elements[i].ptr, &elements[i].offset); |
| + } |
| + } |
| + static bool DecodePointersAndHandles(ArrayHeader* header, |
| + ElementType* elements, |
| + const Message& message) { |
| + for (uint32_t i = 0; i < header->num_elements; ++i) { |
| + DecodePointer(&elements[i].offset, &elements[i].ptr); |
| + if (elements[i].ptr) |
| + elements[i].ptr->DecodePointersAndHandles(message); |
| + } |
| + return true; |
| + } |
| +}; |
| + |
| +template <typename T> |
| +class Array { |
| + public: |
| + explicit Array(size_t num_elements) { |
| + // TODO: bools should get packed to a single bit? |
| + header_.num_bytes = |
| + sizeof(typename ArrayTraits<T>::ElementType) * num_elements + |
| + sizeof(ArrayHeader); |
| + header_.num_elements = num_elements; |
| + } |
| + |
| + template <typename Allocator> |
| + Array<T>* Clone(Allocator* allocator) const { |
| + Array<T>* array = |
| + reinterpret_cast<Array<T>*>(allocator->AllocBytes(header_.num_bytes)); |
| + memcpy(array, this, header_.num_bytes); |
| + |
| + ArrayTraits<T>::CloneElements(allocator, &array->header_, array->elements_); |
| + return array; |
| + } |
| + |
| + void EncodePointersAndHandles(std::vector<Handle>* handles) { |
| + ArrayTraits<T>::EncodePointersAndHandles(&header_, elements_, handles); |
| + } |
| + bool DecodePointersAndHandles(const Message& message) { |
| + return ArrayTraits<T>::DecodePointersAndHandles(&header_, elements_, |
| + message); |
| + } |
| + |
| + size_t size() const { return header_.num_elements; } |
| + |
| + T& at(size_t offset) { |
| + return ArrayTraits<T>::ToElementRef(elements_[offset]); |
| + } |
| + |
| + const T& at(size_t offset) const { |
| + return ArrayTraits<T>::ToElementConstRef(elements_[offset]); |
| + } |
| + |
| + T& operator[](size_t offset) { |
| + return ArrayTraits<T>::ToElementRef(elements_[offset]); |
| + } |
| + |
| + const T& operator[](size_t offset) const { |
| + return ArrayTraits<T>::ToElementConstRef(elements_[offset]); |
| + } |
| + |
| + private: |
| + ArrayHeader header_; |
| + typename ArrayTraits<T>::ElementType elements_[1]; // Extra elements follow. |
| +}; |
| + |
| +// The following is a cheezy arena allocator. |
| +class Buffer { |
| + public: |
| + Buffer() : ptr_(NULL), size_(0) { |
| + } |
| + ~Buffer() { free(ptr_); } |
| + |
| + const uint8_t* data() const { return ptr_; } |
| + uint8_t* data() { return ptr_; } |
| + |
| + size_t size() const { return size_; } |
| + |
| + uint8_t* AllocBytes(size_t size) { |
| + return Grow(size); |
| + } |
| + |
| + template <typename T> |
| + T* Alloc() { |
| + size_t size = sizeof(T); |
| + return new (Grow(size)) T(); |
| + } |
| + |
| + template <typename T> |
| + Array<T>* AllocArray(size_t count) { |
| + // (count - 1) because Array<T> has reserved space for the first element. |
| + size_t size = sizeof(Array<T>) + sizeof(T) * (count - 1); |
| + return new (Grow(size)) Array<T>(count); |
| + } |
| + |
| + private: |
| + uint8_t* Grow(size_t delta) { |
| + // TODO: Align allocations |
| + size_t old_size = size_; |
| + size_t new_size = old_size + delta; |
| + ptr_ = static_cast<uint8_t*>(realloc(ptr_, old_size + delta)); |
| + size_ = new_size; |
| + uint8_t* result = ptr_ + old_size; |
| + memset(result, 0, delta); |
| + return result; |
| + } |
| + |
| + uint8_t* ptr_; |
| + size_t size_; |
| + |
| + // NOT IMPLEMENTED |
| + Buffer(const Buffer&); |
| + void operator=(const Buffer&); |
| +}; |
| + |
| +//---- |
| +// Begin generated code. |
| + |
| +// What follows is class definitions corresponding to the structures indicated |
| +// by the IDL. |
| + |
| +class Bar { |
| + public: |
| + Bar() { |
| + header_.num_bytes = sizeof(*this) + sizeof(StructHeader); |
| + header_.num_fields = 3; |
| + } |
| + |
| + Bar* Clone(Buffer* buf) const { |
| + Bar* bar = buf->Alloc<Bar>(); |
| + memcpy(bar, this, sizeof(*this)); |
| + return bar; |
| + } |
| + |
| + void EncodePointersAndHandles(std::vector<Handle>* handles) { |
| + } |
| + bool DecodePointersAndHandles(const Message& message) { |
| + return true; |
| + } |
| + |
| + void set_alpha(uint8_t alpha) { d_.alpha = alpha; } |
| + void set_beta(uint8_t beta) { d_.beta = beta; } |
| + void set_gamma(uint8_t gamma) { d_.gamma = gamma; } |
| + |
| + uint8_t alpha() const { return d_.alpha; } |
| + uint8_t beta() const { return d_.beta; } |
| + uint8_t gamma() const { return d_.gamma; } |
| + |
| + private: |
| + StructHeader header_; |
|
Hajime Morrita
2013/09/25 20:03:55
One possible alternative is to keep the message cl
|
| + struct { |
| + uint8_t alpha; |
| + uint8_t beta; |
| + uint8_t gamma; |
| + } d_; |
| + |
| + ~Bar(); // NOT IMPLEMENTED |
| +}; |
| + |
| +class Foo { |
| + public: |
| + Foo() { |
| + header_.num_bytes = sizeof(*this) + sizeof(StructHeader); |
| + header_.num_fields = 8; |
| + } |
| + |
| + Foo* Clone(Buffer* buf) const { |
| + Foo* foo = buf->Alloc<Foo>(); |
| + memcpy(foo, this, sizeof(*this)); |
| + |
| + foo->set_bar(foo->bar()->Clone(buf)); |
| + foo->set_data(foo->data()->Clone(buf)); |
| + foo->set_extra_bars(foo->extra_bars()->Clone(buf)); |
| + |
| + return foo; |
| + } |
| + |
| + void EncodePointersAndHandles(std::vector<Handle>* handles) { |
| + if (d_.bar.ptr) |
| + d_.bar.ptr->EncodePointersAndHandles(handles); |
| + EncodePointer(d_.bar.ptr, &d_.bar.offset); |
| + |
| + if (d_.data.ptr) |
| + d_.data.ptr->EncodePointersAndHandles(handles); |
| + EncodePointer(d_.data.ptr, &d_.data.offset); |
| + |
| + if (d_.extra_bars.ptr) |
| + d_.extra_bars.ptr->EncodePointersAndHandles(handles); |
| + EncodePointer(d_.extra_bars.ptr, &d_.extra_bars.offset); |
| + } |
| + |
| + bool DecodePointersAndHandles(const Message& message) { |
| + DecodePointer(&d_.bar.offset, &d_.bar.ptr); |
| + if (d_.bar.ptr) { |
| + if (!d_.bar.ptr->DecodePointersAndHandles(message)) |
| + return false; |
| + } |
| + |
| + DecodePointer(&d_.data.offset, &d_.data.ptr); |
| + if (d_.data.ptr) { |
| + if (!d_.data.ptr->DecodePointersAndHandles(message)) |
| + return false; |
| + } |
| + |
| + if (header_.num_fields >= 8) { |
| + DecodePointer(&d_.extra_bars.offset, &d_.extra_bars.ptr); |
| + if (d_.extra_bars.ptr) { |
| + if (!d_.extra_bars.ptr->DecodePointersAndHandles(message)) |
| + return false; |
| + } |
| + } |
| + |
| + // TODO: validate |
| + return true; |
| + } |
| + |
| + void set_x(int32_t x) { d_.x = x; } |
| + void set_y(int32_t y) { d_.y = y; } |
| + void set_a(bool a) { d_.a = a; } |
| + void set_b(bool b) { d_.b = b; } |
| + void set_c(bool c) { d_.c = c; } |
| + void set_bar(Bar* bar) { d_.bar.ptr = bar; } |
| + void set_data(Array<uint8_t>* data) { d_.data.ptr = data; } |
| + void set_extra_bars(Array<Bar*>* extra_bars) { |
| + d_.extra_bars.ptr = extra_bars; |
| + } |
| + |
| + int32_t x() const { return d_.x; } |
| + int32_t y() const { return d_.y; } |
| + bool a() const { return d_.a; } |
| + bool b() const { return d_.b; } |
| + bool c() const { return d_.c; } |
| + const Bar* bar() const { return d_.bar.ptr; } |
| + const Array<uint8_t>* data() const { return d_.data.ptr; } |
| + const Array<Bar*>* extra_bars() const { |
| + // NOTE: extra_bars is an optional field! |
| + return header_.num_fields >= 8 ? d_.extra_bars.ptr : NULL; |
| + } |
| + |
| + private: |
| + StructHeader header_; |
| + struct { |
| + int32_t x; |
| + int32_t y; |
| + uint32_t a : 1; |
| + uint32_t b : 1; |
| + uint32_t c : 1; |
| + StructPointer<Bar> bar; |
| + ArrayPointer<uint8_t> data; |
| + ArrayPointer<Bar*> extra_bars; |
| + } d_; |
| + |
| + ~Foo(); // NOT IMPLEMENTED |
| +}; |
| + |
| +class Frobinate_Params { |
| + public: |
| + Frobinate_Params() { |
| + header_.num_bytes = sizeof(*this) + sizeof(StructHeader); |
| + header_.num_fields = 2; |
| + } |
| + |
| + void EncodePointersAndHandles(std::vector<Handle>* handles) { |
| + if (d_.foo.ptr) |
| + d_.foo.ptr->EncodePointersAndHandles(handles); |
| + EncodePointer(d_.foo.ptr, &d_.foo.offset); |
| + } |
| + bool DecodePointersAndHandles(const Message& message) { |
| + DecodePointer(&d_.foo.offset, &d_.foo.ptr); |
| + if (d_.foo.ptr) { |
| + if (!d_.foo.ptr->DecodePointersAndHandles(message)) |
| + return false; |
| + } |
| + // TODO: validate |
| + return true; |
| + } |
| + |
| + void set_foo(Foo* foo) { d_.foo.ptr = foo; } |
| + void set_baz(bool baz) { d_.baz = baz; } |
| + |
| + const Foo* foo() const { return d_.foo.ptr; } |
| + bool baz() const { return d_.baz; } |
| + |
| + private: |
| + StructHeader header_; |
| + struct { |
| + StructPointer<Foo> foo; |
| + uint32_t baz : 1; |
| + } d_; |
| + |
| + ~Frobinate_Params(); // NOT IMPLEMENTED |
| +}; |
| + |
| +const uint32_t kMessageID_Frobinate = 1; |
| + |
| +//---- |
| +// The following code would also be generated by our bindings system. |
| + |
| +class Blah { |
| + public: |
| + virtual void Frobinate(const Foo* foo, bool baz) = 0; |
| +}; |
| + |
| +class BlahStub : public Blah { |
| + public: |
| + bool OnMessageReceived(const Message& message) { |
| + switch (message.name) { |
| + case kMessageID_Frobinate: { |
| + Frobinate_Params* params = |
| + reinterpret_cast<Frobinate_Params*>(message.data_start); |
| + if (!params->DecodePointersAndHandles(message)) |
| + return false; |
| + Frobinate(params->foo(), params->baz()); |
| + break; |
| + } |
| + } |
| + return true; |
| + } |
| +}; |
| + |
| +class BlahProxy : public Blah { |
| + public: |
| + virtual void SendMessage(const Message& message) = 0; |
| + |
| + virtual void Frobinate(const Foo* foo, bool baz) { |
| + Buffer buf; |
| + |
| + // TODO: We should allocate the MessageHeader here to reserve space. |
| + //MessageHeader* header = buf.Alloc<MessageHeader>(); |
| + |
| + // We now go about allocating the anonymous Frobinate_Params struct. It |
| + // holds the parameters to the Frobinate message. |
| + // |
| + // Notice how foo is cloned. This causes a copy of foo to be generated |
| + // within the same buffer as the Frobinate_Params struct. That's what we |
| + // need in order to generate a continguous blob of message data. |
| + |
| + Frobinate_Params* params = buf.Alloc<Frobinate_Params>(); |
| + params->set_foo(foo->Clone(&buf)); |
| + params->set_baz(baz); |
| + |
| + // NOTE: If foo happened to be a graph with cycles, then Clone would not |
| + // have returned. |
| + |
| + // Last step before sending the message is to encode pointers and handles |
| + // so that messages become hermetic. Pointers become offsets and handles |
| + // becomes indices into the handles array. |
| + |
| + std::vector<Handle> handles; |
| + params->EncodePointersAndHandles(&handles); |
| + |
| + Message message; |
| + message.name = kMessageID_Frobinate; |
| + message.data_start = buf.data(); |
| + message.data_end = buf.data() + buf.size(); |
| + message.handles_start = &handles[0]; |
| + message.handles_end = &handles[0] + handles.size(); |
| + |
| + SendMessage(message); |
| + } |
| +}; |
| + |
| +//---- |
| +// User code goes here: |
| + |
| +static void PrintSpacer(int depth) { |
| + for (int i = 0; i < depth; ++i) |
| + printf(" "); |
| +} |
| + |
| +static void Print(int depth, const char* name, bool value) { |
| + PrintSpacer(depth); |
| + printf("%s: %s\n", name, value ? "true" : "false"); |
| +} |
| + |
| +static void Print(int depth, const char* name, int32_t value) { |
| + PrintSpacer(depth); |
| + printf("%s: %d\n", name, value); |
| +} |
| + |
| +static void Print(int depth, const char* name, uint8_t value) { |
| + PrintSpacer(depth); |
| + printf("%s: %u\n", name, value); |
| +} |
| + |
| +template <typename T> |
| +static void Print(int depth, const char* name, const Array<T>* array) { |
| + PrintSpacer(depth); |
| + printf("%s: %p\n", name, array); |
| + if (array) { |
| + ++depth; |
| + for (size_t i = 0; i < array->size(); ++i) { |
| + char buf[32]; |
| + sprintf(buf, "%lu", i); |
| + Print(depth, buf, array->at(i)); |
| + } |
| + --depth; |
| + } |
| +} |
| + |
| +static void Print(int depth, const char* name, const Bar* bar) { |
| + PrintSpacer(depth); |
| + printf("%s: %p\n", name, bar); |
| + if (bar) { |
| + ++depth; |
| + Print(depth, "alpha", bar->alpha()); |
| + Print(depth, "beta", bar->beta()); |
| + Print(depth, "gamma", bar->gamma()); |
| + --depth; |
| + } |
| +} |
| + |
| +static void Print(int depth, const char* name, const Foo* foo) { |
| + PrintSpacer(depth); |
| + printf("%s: %p\n", name, foo); |
| + if (foo) { |
| + ++depth; |
| + Print(depth, "x", foo->x()); |
| + Print(depth, "y", foo->y()); |
| + Print(depth, "a", foo->a()); |
| + Print(depth, "b", foo->b()); |
| + Print(depth, "c", foo->c()); |
| + Print(depth, "bar", foo->bar()); |
| + Print(depth, "data", foo->data()); |
| + Print(depth, "extra_bars", foo->extra_bars()); |
| + --depth; |
| + } |
| +} |
| + |
| +class BlahImpl : public BlahStub { |
| + public: |
| + virtual void Frobinate(const Foo* foo, bool baz) { |
| + // Users code goes here to handle the incoming Frobinate message. |
| + // We'll just dump the Foo structure and all of its members. |
| + |
| + printf("Frobinate:\n"); |
| + |
| + int depth = 1; |
| + Print(depth, "foo", foo); |
| + Print(depth, "baz", baz); |
| + } |
| +}; |
| + |
| +//---- |
| + |
| +class BlahProxyImpl : public BlahProxy { |
| + public: |
| + virtual void SendMessage(const Message& message) { |
| + // Imagine some IPC happened here. |
| + |
| + // In the receiving process, an implementation of BlahStub is known to the |
| + // system. It receives the incoming message. |
| + BlahImpl impl; |
| + BlahStub* stub = &impl; |
| + |
| + stub->OnMessageReceived(message); |
| + } |
| +}; |
| + |
| +int main() { |
| + // User has a proxy to a Blah somehow. |
| + Blah* blah = new BlahProxyImpl(); |
| + |
| + // User constructs a message to send. |
| + |
| + // Notice that it doesn't matter in what order the structs / arrays are |
| + // allocated. Here, the various members of Foo are allocated before Foo is |
| + // allocated. |
| + |
| + Buffer buf; |
| + |
| + Bar* bar = buf.Alloc<Bar>(); |
| + bar->set_alpha(20); |
| + bar->set_beta(40); |
| + bar->set_gamma(60); |
| + |
| + const size_t kNumDataElements = 10; |
| + Array<uint8_t>* data = buf.AllocArray<uint8_t>(kNumDataElements); |
| + for (size_t i = 0; i < kNumDataElements; ++i) |
| + (*data)[i] = static_cast<uint8_t>(kNumDataElements - i); |
| + |
| + const size_t kNumExtraBarsElements = 3; |
| + Array<Bar*>* extra_bars = buf.AllocArray<Bar*>(kNumExtraBarsElements); |
| + for (size_t i = 0; i < kNumExtraBarsElements; ++i) { |
| + Bar* bar = buf.Alloc<Bar>(); |
| + bar->set_alpha(i * 100); |
| + bar->set_beta(i * 100 + 20); |
| + bar->set_gamma(i * 100 + 40); |
| + (*extra_bars)[i] = bar; |
| + } |
| + |
| + Foo* foo = buf.Alloc<Foo>(); |
| + foo->set_x(1); |
| + foo->set_y(2); |
| + foo->set_a(false); |
| + foo->set_b(true); |
| + foo->set_c(false); |
| + foo->set_bar(bar); |
| + foo->set_data(data); |
| + foo->set_extra_bars(extra_bars); |
| + |
| + blah->Frobinate(foo, true); |
| + |
| + return 0; |
| +} |