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; |
+} |