Permalink
Browse files

LUCY-234 Brittle object struct ABI

Add a proof-of-concept project illustrating one approach to addressing the
brittle object struct ABI issue, adapting techniques developed from C++.


git-svn-id: https://svn.apache.org/repos/asf/lucy/branches/LUCY-234-struct-ABI@1332895 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information...
1 parent 9ae5a0f commit 8ca9b0596a2d95ab82434238cde0b987bf482eb8 @rectang rectang committed May 2, 2012
Showing with 1,000 additions and 0 deletions.
  1. +209 −0 README
  2. +46 −0 app.c
  3. +234 −0 core.c
  4. +181 −0 core.h
  5. +162 −0 module.c
  6. +168 −0 module.h
View
209 README
@@ -0,0 +1,209 @@
+========
+SYNOPSIS
+========
+
+Clownfish's current handling of object struct definitions has certain
+drawbacks:
+
+ * If an upstream parcel adds, removes or rearranges its member variables,
+ downstream extensions will break.
+ * Subclasses have direct access to all ancestor class member variables,
+ including those defined in upstream parcels.
+ * The current implementation violates "strict aliasing".
+
+The files in this directory are a proof-of-concept which illustrates one
+possible strategy for addressing these issues, adapting techniques used
+for implementing multiple inheritance in C++.
+
+=======
+DETAILS
+=======
+
+In the current Clownfish implementation, subclasses tack their member
+variables onto the end of the parent struct.
+
+ struct cfish_Obj {
+ cfish_VTable *vtable;
+ cfish_ref_t ref;
+ };
+ struct Query {
+ cfish_VTable *vtable;
+ cfish_ref_t ref;
+ float boost;
+ };
+ struct lucy_TermQuery {
+ cfish_VTable *vtable;
+ cfish_ref_t ref;
+ float boost;
+ cfish_CharBuf *field;
+ cfish_Obj *term;
+ };
+
+This is true even when the subclass is in a different parcel:
+
+ struct ext_MyTermQuery {
+ cfish_VTable *vtable;
+ cfish_ref_t ref;
+ float boost;
+ cfish_CharBuf *field;
+ cfish_Obj *term;
+ int32_t number;
+ };
+
+Laying out subclass structs identically to their ancestors allows us to cast
+the object pointers between different types.
+
+ TermQuery *as_term_query = (TermQuery*)my_term_query;
+ Query *as_query = (Query*)my_term_query;
+ printf("As MyTermQuery: %f\n", my_term_query->boost);
+ printf("As TermQuery: %f\n", as_term_query->boost);
+ printf("As Query: %f\n", as_query->boost);
+
+However, it also freezes the upstream struct layout. For example, Lucy
+cannot switch the positions of "term" and "field" within the TermQuery struct
+without causing the downstream extension MyTermQuery to break.
+
+Additionally, the present system violates the C standard's "strict aliasing"
+rules, which state that different data pointer types may not reference the
+same memory location. Here's an excerpt from a thread on the Python
+developers list where they wrestle with the same issue:
+
+ http://mail.python.org/pipermail/python-dev/2003-July/036909.html
+
+ > Does this mean that code like:
+ >
+ > void f (PyObject *a, PyDictObject *b)
+ > {
+ > a->ob_refcnt += 1;
+ > b->ob_refcnt -= 1;
+ > }
+ > [...]
+ > f((PyObject *)somedict, somedict);
+ >
+ > is disallowed?
+
+ Correct.
+
+There isn't really a good way for Clownfish to declare the upstream struct
+opaque while tacking new variables onto the end of it:
+
+ // Invalid unless the struct definition for lucy_TermQuery is known.
+ struct ext_MyTermQuery {
+ struct lucy_TermQuery;
+ int32_t number;
+ };
+
+We could pull some funny business with runtime pointer math, but it would make
+writing extensions awkward because struct members cannot be accessed directly.
+
+ struct MyTermQueryData {
+ int32_t number;
+ };
+
+ static inline MyTermQueryData*
+ S_child_data(MyTermQuery *self) {
+ size_t offset = Cfish_VTable_Get_Obj_Alloc_Size(LUCY_TERMQUERY);
+ return (MyTermQueryData*)((char*)self + offset);
+ }
+
+ static int32_t
+ S_MyTermQuery_get_number(MyTermQuery *self) {
+ return S_child_data(self)->number; // <---------- yuck.
+ }
+
+(Note that certain memory alignment pitfalls are not dealt with in that code
+sample or others which follow.)
+
+However, if we grow the struct definition *backwards*, so that the parent
+struct comes *after* the subclass member variables in memory, the subclass
+gets direct access to its own variables.
+
+ struct ext_MyTermQuery {
+ cfish_VTable *vtable;
+ int32_t number;
+ // struct lucy_TermQuery superself; <--- implicit
+ };
+
+ static int32_t
+ S_MyTermQuery_get_number(MyTermQuery *self) {
+ return self->number; // <---------- idomatic, efficient C
+ }
+
+At runtime, when heap memory is allocated for a MyTermQuery object, the size
+of the parent TermQuery struct is known. Here's some verbose sample code
+demonstrating how a MyTermQuery object can be initialized:
+
+ size_t size = sizeof(ext_MyTermQuery)
+ + Cfish_VTable_Get_Obj_Alloc_Size(LUCY_TERMQUERY);
+ ext_MyTermQuery *self = (ext_MyTermQuery*)malloc(size);
+ self->vtable = EXT_MYTERMQUERY;
+ self->number = number;
+ lucy_TermQuery *superself
+ = (lucy_TermQuery*)((char*)self + sizeof(ext_MyTermQuery));
+ superself->vtable = LUCY_TERMQUERY;
+ lucy_TermQuery_init(superself, field, term);
+ return self;
+
+Within a parcel, parent object structs can be embedded directly...
+
+ struct lucy_TermQuery {
+ cfish_VTable *vtable;
+ float boost;
+ cfish_CharBuf *field;
+ cfish_Obj *term;
+ lucy_Query superself; // <---- explicit
+ };
+
+... which allows subclasses direct access:
+
+ float boost = term_query->superself.boost;
+
+Of course the problem now is that once subclasses no longer duplicate the
+memory layout of their ancestors, a simple cast no longer suffices -- we need
+to use "pointer fixups" a la C++ to make sure that the "self" that gets sent
+to a method has the layout the method needs.
+
+This proof-of-concept project adapts Clownfish-style OFFSET vars to encode
+both the method offset and fixup information. The "pointer fixup" goes in the
+upper 32-bits, and the method offset goes in the lower 32 bits.
+
+ static inline void
+ Dog_speak(Dog *self) {
+ const uint64_t offsets = Dog_speak_OFFSETS;
+ void *const view = (char*)self + (int32_t)(offsets >> 32);
+ char *const method_address = *(char**)self + (uint32_t)offsets;
+ Dog_speak_t method = *((Dog_speak_t*)method_address);
+ method(view);
+ }
+
+For more information, see the source files.
+
+PRO:
+
+ * Upstream parcels can add, remove, or rearrange member variables at will
+ without breaking the ABI.
+ * Our basic object model will no longer violate strict aliasing rules. We
+ may eventually be able to compile without -fno-strict-aliasing and reap
+ the optimization benefits.
+ * Encapsulation-friendly. A subclass only has direct access to the member
+ vars of ancestor classes defined within the same parcel.
+
+CON:
+
+ * Increased complexity, though that complexity is mostly hidden away in
+ CFC and autogenerated code.
+ * Increased object memory footprint: 1 pointer for every ancestor. (We
+ should be able to cut this down to "1 pointer for each ancestor with
+ member variables".)
+ * Each method invocation now has a couple more ops.
+ * Additional boot code.
+ * More OFFSET vars: we're back to needing approximately one for each
+ method/invocant pairing.
+ * Changes to Lucy code will be required in addition to the changes to CFC.
+
+The additional ops per method invocation and startup costs should be measured,
+but hopefully the method invocation ops will prove negligible on a modern
+pipelining processor -- the memory fetch costs per invocation have not
+changed.
+
+
View
46 app.c
@@ -0,0 +1,46 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include "core.h"
+#include "module.h"
+
+int main() {
+ Core_bootstrap();
+ Module_bootstrap();
+
+ Dog *fido = Dog_new("Fido");
+ printf("This is my dog, Fido:\n ");
+ Dog_speak(fido);
+ printf("Here, Fido!\n ");
+ Dog_ignore_name(fido);
+ printf("OK, never mind then. Bye Fido!\n\n");
+ Dog_destroy(fido);
+
+ Boxer *drago = Boxer_new("Drago");
+ printf("This is my my friend's dog, Drago:\n ");
+ Boxer_speak(drago);
+ printf("Here, Drago!\n ");
+ Boxer_ignore_name(drago);
+ printf("OK, never mind.\nDrago is a boxer.\n ");
+ Boxer_drool(drago);
+ printf("Bye, Drago!\n");
+ Boxer_destroy(drago);
+
+ Module_tear_down();
+ Core_tear_down();
+}
+
View
234 core.c
@@ -0,0 +1,234 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "core.h"
+
+struct Object {
+ MetaClass *metaclass;
+};
+
+struct MetaClass {
+ MetaClass *metaclass;
+ MetaClass *parent;
+ char *name;
+ size_t obj_alloc_size;
+ size_t num_methods;
+ size_t method_count;
+ Object superself;
+};
+
+uint64_t Obj_destroy_OFFSETS;
+static void
+S_Obj_destroy(Object *self) {
+ ptrdiff_t fixup = (ptrdiff_t)self->metaclass->obj_alloc_size
+ - (ptrdiff_t)sizeof(Object);
+ char *ptr = (char*)self - fixup;
+ free(ptr);
+}
+
+/************************************************************************/
+
+// Methods are allocated immediately after the end of the MetaClass struct.
+method_t*
+S_methods(MetaClass *self) {
+ return (method_t*)((char*)self + sizeof(MetaClass));
+}
+
+uint64_t MetaClass_destroy_OFFSETS;
+static void
+S_MetaClass_destroy(MetaClass *self);
+
+uint64_t MetaClass_make_obj_OFFSETS;
+static void*
+S_MetaClass_make_obj(MetaClass *self);
+
+uint64_t MetaClass_add_novel_method_OFFSETS;
+static uint64_t
+S_MetaClass_add_novel_method(MetaClass *self, method_t method);
+
+uint64_t MetaClass_override_method_OFFSETS;
+static uint64_t
+S_MetaClass_override_method(MetaClass *self, method_t method, uint64_t offsets);
+
+uint64_t MetaClass_inherit_method_OFFSETS;
+static uint64_t
+S_MetaClass_inherit_method(MetaClass *self, uint64_t offsets);
+
+uint64_t MetaClass_get_num_methods_OFFSETS;
+static size_t
+S_MetaClass_get_num_methods(MetaClass *self);
+
+uint64_t MetaClass_get_obj_alloc_size_OFFSETS;
+static size_t
+S_MetaClass_get_obj_alloc_size(MetaClass *self);
+
+MetaClass *cMETACLASS = NULL;
+MetaClass *cOBJECT = NULL;
+
+void
+Core_bootstrap(void) {
+ // Init Object's MetaClass.
+ cOBJECT = MetaClass_new(NULL, "Object", sizeof(Object), 1);
+ Obj_destroy_OFFSETS
+ = S_MetaClass_add_novel_method(cOBJECT, (method_t)S_Obj_destroy);
+
+ // Init MetaClass's MetaClass.
+ cMETACLASS = MetaClass_new(cOBJECT, "MetaClass", sizeof(MetaClass), 8);
+ MetaClass_destroy_OFFSETS
+ = S_MetaClass_add_novel_method(cMETACLASS, (method_t)S_MetaClass_destroy);
+ MetaClass_make_obj_OFFSETS
+ = S_MetaClass_add_novel_method(cMETACLASS, (method_t)S_MetaClass_make_obj);
+ MetaClass_add_novel_method_OFFSETS
+ = S_MetaClass_add_novel_method(cMETACLASS, (method_t)S_MetaClass_add_novel_method);
+ MetaClass_override_method_OFFSETS
+ = S_MetaClass_add_novel_method(cMETACLASS, (method_t)S_MetaClass_override_method);
+ MetaClass_inherit_method_OFFSETS
+ = S_MetaClass_add_novel_method(cMETACLASS, (method_t)S_MetaClass_inherit_method);
+ MetaClass_get_num_methods_OFFSETS
+ = S_MetaClass_add_novel_method(cMETACLASS, (method_t)S_MetaClass_get_num_methods);
+ MetaClass_get_obj_alloc_size_OFFSETS
+ = S_MetaClass_add_novel_method(cMETACLASS, (method_t)S_MetaClass_get_obj_alloc_size);
+
+ // Hack around circular dependency.
+ cOBJECT->metaclass = cMETACLASS;
+}
+
+void
+Core_tear_down(void) {
+ S_MetaClass_destroy(cOBJECT);
+ S_MetaClass_destroy(cMETACLASS);
+}
+
+static void
+S_dummy_method(void *self) {
+ (void)self;
+ abort();
+}
+
+MetaClass*
+MetaClass_new(MetaClass *parent, const char *name, size_t obj_alloc_size,
+ size_t num_methods) {
+ size_t size = sizeof(MetaClass) + sizeof(method_t) * num_methods;
+ MetaClass *self = (MetaClass*)calloc(size, 1);
+ self->metaclass = cMETACLASS;
+ self->name = strdup(name);
+ self->num_methods = num_methods;
+ self->method_count = 0;
+ self->obj_alloc_size = obj_alloc_size;
+ self->parent = parent;
+ self->superself.metaclass = cMETACLASS;
+
+ // Dupe parent's method pointers, use a dummy placeholder for the rest.
+ method_t *methods = S_methods(self);
+ if (parent) {
+ method_t *parent_methods = S_methods(parent);
+ while (self->method_count < parent->method_count) {
+ methods[self->method_count] = parent_methods[self->method_count];
+ self->method_count++;
+ }
+ }
+ for (size_t i = self->method_count; i < self->num_methods; i++) {
+ methods[i] = S_dummy_method;
+ }
+
+ return self;
+}
+
+static void
+S_MetaClass_destroy(MetaClass *self) {
+ free(self->name);
+ free(self);
+}
+
+static void*
+S_MetaClass_make_obj(MetaClass *self) {
+ Object *obj = (Object*)calloc(1, self->obj_alloc_size);
+ MetaClass *ancestor = self;
+ Object *view = obj;
+ while (ancestor) {
+ view->metaclass = self;
+ if (ancestor->parent) {
+ size_t fixup = ancestor->obj_alloc_size
+ - ancestor->parent->obj_alloc_size;
+ view = (Object*)((char*)view + fixup);
+ }
+ ancestor = ancestor->parent;
+ }
+ return obj;
+}
+
+static uint64_t
+S_MetaClass_add_novel_method(MetaClass *self, method_t method) {
+ if (self->method_count >= self->num_methods) {
+ abort();
+ }
+
+ // Store the method pointer at the next location.
+ size_t offset = sizeof(MetaClass) + self->method_count * sizeof(method_t);
+ union { char *char_ptr; method_t *func_ptr; } pointer;
+ pointer.char_ptr = ((char*)self) + offset;
+ pointer.func_ptr[0] = method;
+ self->method_count++;
+
+ // Encode method vtable offset in the lower 32 bits. The upper 32-bits,
+ // used to encode the "self" pointer fixup, are left all zeroes because no
+ // fixup is necessary for a "fresh" method implementation.
+ if (offset > UINT32_MAX) {
+ abort(); // Guard against unlikely integer overflow.
+ }
+ return offset;
+}
+
+static uint64_t
+S_MetaClass_override_method(MetaClass *self, method_t method,
+ uint64_t offsets) {
+ // Store the supplied method pointer at the specified location.
+ union { char *char_ptr; method_t *func_ptr; } pointer;
+ pointer.char_ptr = ((char*)self) + (uint32_t)offsets;
+ pointer.func_ptr[0] = method;
+
+ // Return an offsets var with the method offset in the lower 32 bits, and
+ // the top 32 bits all zeros to encode a fixup of 0 for this "fresh"
+ // method.
+ return (offsets & 0xFFFFFFFF);
+}
+
+static uint64_t
+S_MetaClass_inherit_method(MetaClass *self, uint64_t offsets) {
+ // Encode "self" pointer fixup in top 32 bits of OFFSETS var.
+ int32_t fixup = (int32_t)(offsets >> 32);
+ fixup += (self->obj_alloc_size - self->parent->obj_alloc_size);
+ int64_t new_offsets = ((int64_t)fixup) << 32;
+
+ // Encode method vtable offset in lower 32 bits.
+ new_offsets |= (offsets & 0xFFFFFFFF);
+
+ return new_offsets;
+}
+
+static size_t
+S_MetaClass_get_num_methods(MetaClass *self) {
+ return self->num_methods;
+}
+
+static size_t
+S_MetaClass_get_obj_alloc_size(MetaClass *self) {
+ return self->obj_alloc_size;
+}
View
181 core.h
@@ -0,0 +1,181 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CORE_H
+#define CORE_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+// Generic method pointer.
+typedef void
+(*method_t)(void *self);
+
+/** Boostrap the core.
+ */
+void
+Core_bootstrap(void);
+
+/** Clean up after Core_bootstrap().
+ */
+void
+Core_tear_down(void);
+
+/** Base object type.
+ */
+typedef struct Object Object;
+
+/** Destructor.
+ */
+typedef void
+(*Obj_destroy_t)(Object *self);
+extern uint64_t Obj_destroy_OFFSETS;
+static inline void
+Obj_destroy(Object *self) {
+ const uint64_t offsets = Obj_destroy_OFFSETS;
+ void *const view = (char*)self + (int32_t)(offsets >> 32);
+ char *const method_address = *(char**)self + (uint32_t)offsets;
+ Obj_destroy_t method = *((Obj_destroy_t*)method_address);
+ method(view);
+}
+
+/** Class data, including virtual method table.
+ */
+typedef struct MetaClass MetaClass;
+
+// Core MetaClasses.
+extern MetaClass *cMETACLASS;
+extern MetaClass *cOBJECT;
+
+static inline method_t
+MetaClass_method_pointer(MetaClass *self, int64_t offsets) {
+ char *const method_address = *(char**)self + (uint32_t)offsets;
+ return *((method_t*)method_address);
+}
+
+/** Constructor.
+ */
+MetaClass*
+MetaClass_new(MetaClass *superclass, const char *name, size_t obj_alloc_size,
+ size_t num_methods);
+
+/** Destructor.
+ */
+typedef void
+(*MetaClass_destroy_t)(MetaClass *self);
+extern uint64_t MetaClass_destroy_OFFSETS;
+static inline void
+MetaClass_destroy(MetaClass *self) {
+ const uint64_t offsets = MetaClass_destroy_OFFSETS;
+ void *const view = (char*)self + (int32_t)(offsets >> 32);
+ char *const method_address = *(char**)self + (uint32_t)offsets;
+ MetaClass_destroy_t method = *((MetaClass_destroy_t*)method_address);
+ method(view);
+}
+
+/** Allocate a new object.
+ */
+typedef void*
+(*MetaClass_make_obj_t)(MetaClass *self);
+extern uint64_t MetaClass_make_obj_OFFSETS;
+static inline void*
+MetaClass_make_obj(MetaClass *self) {
+ const uint64_t offsets = MetaClass_make_obj_OFFSETS;
+ void *const view = (char*)self + (int32_t)(offsets >> 32);
+ char *const method_address = *(char**)self + (uint32_t)offsets;
+ MetaClass_make_obj_t method = *((MetaClass_make_obj_t*)method_address);
+ return method(view);
+}
+
+/** Add a new method to the MetaClass. Return the offsets data needed to invoke
+ * the method.
+ */
+typedef uint64_t
+(*MetaClass_add_novel_method_t)(MetaClass *self, method_t method);
+extern uint64_t MetaClass_add_novel_method_OFFSETS;
+static inline uint64_t
+MetaClass_add_novel_method(MetaClass *self, method_t method) {
+ const uint64_t offsets = MetaClass_add_novel_method_OFFSETS;
+ void *const view = (char*)self + (int32_t)(offsets >> 32);
+ char *const method_address = *(char**)self + (uint32_t)offsets;
+ MetaClass_add_novel_method_t meth = *((MetaClass_add_novel_method_t*)method_address);
+ return meth(view, method);
+}
+
+/** Override an existing method, installing <code>method</code> at the
+ * location specified by <code>offsets</code>. Return the offsets data needed
+ * to invoke the method.
+ */
+typedef uint64_t
+(*MetaClass_override_method_t)(MetaClass *self, method_t method, uint64_t offsets);
+extern uint64_t MetaClass_override_method_OFFSETS;
+static inline uint64_t
+MetaClass_override_method(MetaClass *self, method_t method, uint64_t offsets) {
+ const uint64_t offs = MetaClass_override_method_OFFSETS;
+ void *const view = (char*)self + (int32_t)(offs >> 32);
+ char *const method_address = *(char**)self + (uint32_t)offs;
+ MetaClass_override_method_t meth = *((MetaClass_override_method_t*)method_address);
+ return meth(view, method, offsets);
+}
+
+/** Inherit a method from a parent class and generate the necessary offset
+ * invocation data.
+ *
+ * NOTE: This task will be performed by autogenerated code in production.
+ */
+typedef uint64_t
+(*MetaClass_inherit_method_t)(MetaClass *self, uint64_t offsets);
+extern uint64_t MetaClass_inherit_method_OFFSETS;
+static inline uint64_t
+MetaClass_inherit_method(MetaClass *self, uint64_t offsets) {
+ const uint64_t offs = MetaClass_inherit_method_OFFSETS;
+ void *const view = (char*)self + (int32_t)(offs >> 32);
+ char *const method_address = *(char**)self + (uint32_t)offs;
+ MetaClass_inherit_method_t method = *((MetaClass_inherit_method_t*)method_address);
+ return method(view, offsets);
+}
+
+/** Return the number of methods that the VTable will have when it is
+ * finished initializing.
+ */
+typedef size_t
+(*MetaClass_get_num_methods_t)(MetaClass *self);
+extern uint64_t MetaClass_get_num_methods_OFFSETS;
+static inline size_t
+MetaClass_get_num_methods(MetaClass *self) {
+ const uint64_t offsets = MetaClass_get_num_methods_OFFSETS;
+ void *const view = (char*)self + (int32_t)(offsets >> 32);
+ char *const method_address = *(char**)self + (uint32_t)offsets;
+ MetaClass_get_num_methods_t method = *((MetaClass_get_num_methods_t*)method_address);
+ return method(view);
+}
+
+/** Return the size of the an object belonging to the specified class.
+ */
+typedef size_t
+(*MetaClass_get_obj_alloc_size_t)(MetaClass *self);
+extern uint64_t MetaClass_get_obj_alloc_size_OFFSETS;
+static inline size_t
+MetaClass_get_obj_alloc_size(MetaClass *self) {
+ const uint64_t offsets = MetaClass_get_obj_alloc_size_OFFSETS;
+ void *const view = (char*)self + (int32_t)(offsets >> 32);
+ char *const method_address = *(char**)self + (uint32_t)offsets;
+ MetaClass_get_obj_alloc_size_t method = *((MetaClass_get_obj_alloc_size_t*)method_address);
+ return method(view);
+}
+
+#endif /* CORE_H */
+
View
162 module.c
@@ -0,0 +1,162 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "module.h"
+
+struct Animal {
+ MetaClass *metaclass;
+ void *superself;
+};
+
+struct Dog {
+ MetaClass *metaclass;
+ char *name;
+ Animal superself;
+};
+
+struct Boxer {
+ MetaClass *metaclass;
+ Dog superself;
+};
+
+MetaClass *cANIMAL = NULL;
+MetaClass *cDOG = NULL;
+MetaClass *cBOXER = NULL;
+
+uint64_t Animal_destroy_OFFSETS;
+uint64_t Dog_destroy_OFFSETS;
+uint64_t Boxer_destroy_OFFSETS;
+
+uint64_t Animal_speak_OFFSETS;
+uint64_t Dog_speak_OFFSETS;
+uint64_t Boxer_speak_OFFSETS;
+
+uint64_t Dog_ignore_name_OFFSETS;
+uint64_t Boxer_ignore_name_OFFSETS;
+
+uint64_t Boxer_drool_OFFSETS;
+
+Animal*
+Animal_init(Animal *self) {
+ return self;
+}
+
+static void
+S_Animal_speak(Animal *self) {
+ (void)self;
+ printf("*noise*\n");
+}
+
+Dog*
+Dog_new(const char *name) {
+ Dog *self = MetaClass_make_obj(cDOG);
+ return Dog_init(self, name);
+}
+
+Dog*
+Dog_init(Dog *self, const char *name) {
+ Animal_init(&self->superself);
+ self->name = strdup(name);
+ return self;
+}
+
+static void
+S_Dog_destroy(Dog *self) {
+ free(self->name);
+ Animal_destroy_t super_destroy =
+ (Animal_destroy_t)MetaClass_method_pointer(cANIMAL, Animal_destroy_OFFSETS);
+ void *view = (char*)&self->superself + (Animal_destroy_OFFSETS >> 32);
+ super_destroy(view);
+}
+
+static void
+S_Dog_speak(Dog *self) {
+ (void)self;
+ printf("Woof!\n");
+}
+
+static void
+S_Dog_ignore_name(Dog *self) {
+ printf("My name isn't \"%s\", it's \"cookie\".\n", self->name);
+}
+
+Boxer*
+Boxer_new(const char *name) {
+ Boxer *self = (Boxer*)MetaClass_make_obj(cBOXER);
+ return Boxer_init(self, name);
+}
+
+Boxer*
+Boxer_init(Boxer *self, const char *name) {
+ Dog_init(&self->superself, name);
+ return self;
+}
+
+static void
+S_Boxer_drool(Dog *self) {
+ (void)self;
+ printf("*slobber*\n");
+}
+
+/**********************************************************************/
+
+void
+Module_bootstrap() {
+ size_t num_methods = MetaClass_get_num_methods(cOBJECT);
+ size_t base_size = MetaClass_get_obj_alloc_size(cOBJECT) - sizeof(void*);
+
+ // Animal
+ num_methods += 1; // speak
+ cANIMAL = MetaClass_new(cOBJECT, "Animal", sizeof(Animal) + base_size,
+ num_methods);
+ Animal_destroy_OFFSETS
+ = MetaClass_inherit_method(cANIMAL, Obj_destroy_OFFSETS);
+ Animal_speak_OFFSETS
+ = MetaClass_add_novel_method(cANIMAL, (method_t)S_Animal_speak);
+
+ // Dog
+ num_methods += 1; // ignore_name
+ cDOG = MetaClass_new(cANIMAL, "Dog", sizeof(Dog) + base_size, num_methods);
+ Dog_destroy_OFFSETS
+ = MetaClass_override_method(cDOG, (method_t)S_Dog_destroy, Animal_destroy_OFFSETS);
+ Dog_speak_OFFSETS
+ = MetaClass_override_method(cDOG, (method_t)S_Dog_speak, Animal_speak_OFFSETS);
+ Dog_ignore_name_OFFSETS
+ = MetaClass_add_novel_method(cDOG, (method_t)S_Dog_ignore_name);
+
+ // Boxer
+ num_methods += 1; // drool
+ cBOXER = MetaClass_new(cDOG, "Boxer", sizeof(Boxer) + base_size, num_methods);
+ Boxer_destroy_OFFSETS
+ = MetaClass_inherit_method(cBOXER, Dog_destroy_OFFSETS);
+ Boxer_speak_OFFSETS
+ = MetaClass_inherit_method(cBOXER, Dog_speak_OFFSETS);
+ Boxer_ignore_name_OFFSETS
+ = MetaClass_inherit_method(cBOXER, Dog_ignore_name_OFFSETS);
+ Boxer_drool_OFFSETS
+ = MetaClass_add_novel_method(cBOXER, (method_t)S_Boxer_drool);
+}
+
+void
+Module_tear_down() {
+ MetaClass_destroy(cBOXER);
+ MetaClass_destroy(cDOG);
+ MetaClass_destroy(cANIMAL);
+}
View
168 module.h
@@ -0,0 +1,168 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MODULE_H
+#define MODULE_H
+
+#include "core.h"
+
+typedef struct Animal Animal;
+typedef struct Dog Dog;
+typedef struct Boxer Boxer;
+
+extern MetaClass *cANIMAL;
+extern MetaClass *cDOG;
+extern MetaClass *cBOXER;
+
+void
+Module_bootstrap(void);
+
+void
+Module_tear_down(void);
+
+Animal*
+Animal_init(Animal *self);
+
+typedef void
+(*Animal_destroy_t)(Animal *self);
+extern uint64_t Animal_destroy_OFFSETS;
+static inline void
+Animal_destroy(Animal *self) {
+ const uint64_t offsets = Animal_destroy_OFFSETS;
+ void *const view = (char*)self + (int32_t)(offsets >> 32);
+ char *const method_address = *(char**)self + (uint32_t)offsets;
+ Animal_destroy_t method = *((Animal_destroy_t*)method_address);
+ method(view);
+}
+
+typedef void
+(*Animal_speak_t)(Animal *self);
+extern uint64_t Animal_speak_OFFSETS;
+static inline void
+Animal_speak(Animal *self) {
+ const uint64_t offsets = Animal_speak_OFFSETS;
+ void *const view = (char*)self + (int32_t)(offsets >> 32);
+ char *const method_address = *(char**)self + (uint32_t)offsets;
+ Animal_speak_t method = *((Animal_speak_t*)method_address);
+ method(view);
+}
+
+Dog*
+Dog_new(const char *name);
+
+Dog*
+Dog_init(Dog *self, const char *name);
+
+typedef void
+(*Dog_destroy_t)(Dog *self);
+extern uint64_t Dog_destroy_OFFSETS;
+static inline void
+Dog_destroy(Dog *self) {
+ const uint64_t offsets = Dog_destroy_OFFSETS;
+ void *const view = (char*)self + (int32_t)(offsets >> 32);
+ char *const method_address = *(char**)self + (uint32_t)offsets;
+ Dog_destroy_t method = *((Dog_destroy_t*)method_address);
+ method(view);
+}
+
+static inline void
+Dog_destroy_NEXT(Dog *self) {
+ const uint64_t offsets = Dog_destroy_OFFSETS;
+ void *const view = (char*)self + (int32_t)(offsets >> 32);
+ char *const method_address = *(char**)self + (uint32_t)offsets;
+ Dog_destroy_t method = *((Dog_destroy_t*)method_address);
+ method(view);
+}
+
+typedef void
+(*Dog_speak_t)(Dog *self);
+extern uint64_t Dog_speak_OFFSETS;
+static inline void
+Dog_speak(Dog *self) {
+ const uint64_t offsets = Dog_speak_OFFSETS;
+ void *const view = (char*)self + (int32_t)(offsets >> 32);
+ char *const method_address = *(char**)self + (uint32_t)offsets;
+ Dog_speak_t method = *((Dog_speak_t*)method_address);
+ method(view);
+}
+
+typedef void
+(*Dog_ignore_name_t)(Dog *self);
+extern uint64_t Dog_ignore_name_OFFSETS;
+static inline void
+Dog_ignore_name(Dog *self) {
+ const uint64_t offsets = Dog_ignore_name_OFFSETS;
+ void *const view = (char*)self + (int32_t)(offsets >> 32);
+ char *const method_address = *(char**)self + (uint32_t)offsets;
+ Dog_ignore_name_t method = *((Dog_ignore_name_t*)method_address);
+ method(view);
+}
+
+Boxer*
+Boxer_new(const char *name);
+
+Boxer*
+Boxer_init(Boxer *self, const char *name);
+
+typedef void
+(*Boxer_destroy_t)(Boxer *self);
+extern uint64_t Boxer_destroy_OFFSETS;
+static inline void
+Boxer_destroy(Boxer *self) {
+ const uint64_t offsets = Boxer_destroy_OFFSETS;
+ void *const view = (char*)self + (int32_t)(offsets >> 32);
+ char *const method_address = *(char**)self + (uint32_t)offsets;
+ Boxer_destroy_t method = *((Boxer_destroy_t*)method_address);
+ method(view);
+}
+
+typedef void
+(*Boxer_speak_t)(Boxer *self);
+extern uint64_t Boxer_speak_OFFSETS;
+static inline void
+Boxer_speak(Boxer *self) {
+ const uint64_t offsets = Boxer_speak_OFFSETS;
+ void *const view = (char*)self + (int32_t)(offsets >> 32);
+ char *const method_address = *(char**)self + (uint32_t)offsets;
+ Boxer_speak_t method = *((Boxer_speak_t*)method_address);
+ method(view);
+}
+
+typedef void
+(*Boxer_ignore_name_t)(Boxer *self);
+extern uint64_t Boxer_ignore_name_OFFSETS;
+static inline void
+Boxer_ignore_name(Boxer *self) {
+ const uint64_t offsets = Boxer_ignore_name_OFFSETS;
+ void *const view = (char*)self + (int32_t)(offsets >> 32);
+ char *const method_address = *(char**)self + (uint32_t)offsets;
+ Boxer_ignore_name_t method = *((Boxer_ignore_name_t*)method_address);
+ method(view);
+}
+
+typedef void
+(*Boxer_drool_t)(Boxer *self);
+extern uint64_t Boxer_drool_OFFSETS;
+static inline void
+Boxer_drool(Boxer *self) {
+ const uint64_t offsets = Boxer_drool_OFFSETS;
+ void *const view = (char*)self + (int32_t)(offsets >> 32);
+ char *const method_address = *(char**)self + (uint32_t)offsets;
+ Boxer_drool_t method = *((Boxer_drool_t*)method_address);
+ method(view);
+}
+
+#endif /* MODULE_H */

0 comments on commit 8ca9b05

Please sign in to comment.