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..ac9605b9db90517af4d388ba346b5bc83eb83486 |
| --- /dev/null |
| +++ b/mojo/public/bindings/test.cc |
| @@ -0,0 +1,580 @@ |
| +/* |
| +// 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 <stddef.h> |
| +#include <stdint.h> |
| +#include <stdio.h> |
| +#include <stdlib.h> |
| +#include <string.h> |
| +#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 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); |
| + *offset = p_obj - p_slot; |
|
yzshen1
2013/09/24 18:32:56
Is it intended to use an unsigned number for |offs
|
| +} |
| + |
| +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); |
| +} |
| + |
| +template <typename T> |
| +struct ArrayTraits { |
| + typedef T ElementType; |
| + |
| + static T& ToElementRef(ElementType& e) { return e; } |
| + static T const& ToElementConstRef(const ElementType& e) { return e; } |
| + |
| + 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; } |
| + |
| + 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: |
| + void Initialize(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; |
| + } |
| + |
| + 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]; |
| +}; |
| + |
| +//---- |
| +// Begin generated code. |
| + |
| +// What follows is class definitions corresponding to the structures indicated |
| +// by the IDL. |
| + |
| +class Bar { |
| + public: |
| + void Initialize() { |
| + header_.num_bytes = sizeof(*this) - sizeof(StructHeader); |
| + header_.num_fields = 3; |
| + } |
| + |
| + 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_; |
| + struct { |
| + uint8_t alpha; |
| + uint8_t beta; |
| + uint8_t gamma; |
| + } d_; |
| + |
| + // NOT IMPLEMENTED |
| + Bar(); |
| + ~Bar(); |
| +}; |
| + |
| +class Foo { |
| + public: |
| + void Initialize() { |
| + header_.num_bytes = sizeof(*this) - sizeof(StructHeader); |
| + header_.num_fields = 8; |
| + } |
| + |
| + 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_; |
| + |
| + // NOT IMPLEMENTED |
| + Foo(); |
| + ~Foo(); |
| +}; |
| + |
| +class Frobinate_Params { |
| + public: |
| + void Initialize() { |
| + 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_; |
| + |
| + // NOT IMPLEMENTED |
| + Frobinate_Params(); |
| + ~Frobinate_Params(); |
| +}; |
| + |
| +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; |
| + } |
| +}; |
| + |
| +#if 0 |
| +// TODO: Figure out how to make the client-side work nicely. |
| +class BlahProxy : public Blah { |
| + public: |
| + virtual void SendMessage(const Message& message) = 0; |
| + |
| + virtual void Frobinate(const Foo* foo, bool baz) { |
| + } |
| + |
| + virtual void Frobinate(const Frobinate_Params* params) { |
| + } |
| +}; |
| +#endif |
| + |
| +//---- |
| +// 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); |
| + } |
| +}; |
| + |
| +//---- |
| + |
| +// The following is just some cheezy buffer allocation code. |
| +// We'd want to sugar this somehow. |
| + |
| +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_; } |
| + |
| + template <typename T> |
| + T* Alloc() { |
| + T* ptr = reinterpret_cast<T*>(Grow(sizeof(T))); |
| + ptr->Initialize(); |
| + return ptr; |
| + } |
| + |
| + template <typename T> |
| + Array<T>* AllocArray(size_t count) { |
| + Array<T>* ptr = reinterpret_cast<Array<T>*>( |
| + Grow(sizeof(Array<T>) + sizeof(T) * (count - 1))); |
| + ptr->Initialize(count); |
| + return ptr; |
| + } |
| + |
| + private: |
| + uint8_t* Grow(size_t delta) { |
| + 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&); |
| +}; |
| + |
| +int main() { |
| + // User constructs a message to send. TODO: Add more sugar to this! |
| + |
| + Buffer buf; |
| + |
| + Frobinate_Params* params = buf.Alloc<Frobinate_Params>(); |
| + params->set_baz(true); |
| + |
| + 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); |
| + |
| + Bar* bar = buf.Alloc<Bar>(); |
| + bar->set_alpha(20); |
| + bar->set_beta(40); |
| + bar->set_gamma(60); |
| + foo->set_bar(bar); |
| + |
| + const size_t kNumDataElements = 10; |
| + Array<uint8_t>* data = buf.AllocArray<uint8_t>(kNumDataElements); |
| + for (size_t i = 0; i < kNumDataElements; ++i) |
| + data->at(i) = static_cast<uint8_t>(kNumDataElements - i); |
| + foo->set_data(data); |
| + |
| + 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->at(i) = bar; |
| + } |
| + foo->set_extra_bars(extra_bars); |
| + |
| + params->set_foo(foo); |
| + |
| + // 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(); |
| + |
| + // At this point, the user would normally call WriteMessage to write buf |
| + // to a pipe. Here, we are leaving that part out. We'll just forward buf |
| + // as a simulated incoming message to the generated bindings for handling |
| + // a message. OnMessageReceived would ordinarily be prefixed with a call |
| + // to ReadMessage to read an incoming message from a pipe. |
| + |
| + BlahStub* stub = new BlahImpl(); |
| + stub->OnMessageReceived(message); |
| + return 0; |
| +} |