Enable customized Go method bindings. Allow Clownfish methods to be bound to Go using custom signatures, and allow arbitrary methods to be added. In both cases, the expectation is that the user will supply the implementation code, as customizing the signature disables autogenerating the default binding (which would presumably not match the custom signature).
Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/905b30ac Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/905b30ac Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/905b30ac Branch: refs/heads/master Commit: 905b30ac531a852fd45060367a380a9110b13d6c Parents: 98c71c1 Author: Marvin Humphrey <[email protected]> Authored: Sun Apr 12 09:24:13 2015 -0700 Committer: Marvin Humphrey <[email protected]> Committed: Wed May 6 14:28:16 2015 -0700 ---------------------------------------------------------------------- compiler/go/cfc/cfc.go | 14 +++++++++ compiler/src/CFCGoClass.c | 69 +++++++++++++++++++++++++---------------- compiler/src/CFCGoClass.h | 3 ++ compiler/src/CFCGoMethod.c | 41 +++++++++++++++++++++--- compiler/src/CFCGoMethod.h | 13 ++++++-- runtime/go/build.go | 11 +++++++ 6 files changed, 119 insertions(+), 32 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/905b30ac/compiler/go/cfc/cfc.go ---------------------------------------------------------------------- diff --git a/compiler/go/cfc/cfc.go b/compiler/go/cfc/cfc.go index 8d09880..fa9b8d7 100644 --- a/compiler/go/cfc/cfc.go +++ b/compiler/go/cfc/cfc.go @@ -266,3 +266,17 @@ func (obj *BindGoClass) finalize() { func (obj *BindGoClass) Register() { C.CFCGoClass_register(obj.ref) } + +func (obj *BindGoClass) SpecMethod(name, sig string) { + var nameC *C.char + if name != "" { + nameC = C.CString(name) + defer C.free(unsafe.Pointer(nameC)) + } + var sigC *C.char + if sig != "" { + sigC = C.CString(sig) + defer C.free(unsafe.Pointer(sigC)) + } + C.CFCGoClass_spec_method(obj.ref, nameC, sigC) +} http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/905b30ac/compiler/src/CFCGoClass.c ---------------------------------------------------------------------- diff --git a/compiler/src/CFCGoClass.c b/compiler/src/CFCGoClass.c index d75eaad..6e32f68 100644 --- a/compiler/src/CFCGoClass.c +++ b/compiler/src/CFCGoClass.c @@ -190,50 +190,35 @@ CFCGoClass_go_typing(CFCGoClass *self) { go_struct_def = CFCUtil_strdup(""); } - // Temporary hack until it's possible to customize interfaces. - char *temp_hack_iface_methods; - if (strcmp(self->class_name, "Clownfish::Obj") == 0) { - temp_hack_iface_methods = CFCUtil_strdup("\tTOPTR() uintptr\n"); - } - else if (strcmp(self->class_name, "Clownfish::Err") == 0) { - temp_hack_iface_methods = CFCUtil_strdup("\tError() string\n"); - } - else { - temp_hack_iface_methods = CFCUtil_strdup(""); - } - char *novel_iface = CFCUtil_strdup(""); S_lazy_init_method_bindings(self); for (int i = 0; self->method_bindings[i] != NULL; i++) { CFCGoMethod *meth_binding = self->method_bindings[i]; CFCMethod *method = CFCGoMethod_get_client(meth_binding); - if (!CFCMethod_novel(method)) { - continue; - } - const char *sym = CFCMethod_get_macro_sym(method); - if (!CFCClass_fresh_method(self->client, sym)) { - continue; + if (method) { + if (!CFCMethod_novel(method)) { + continue; + } + const char *sym = CFCMethod_get_macro_sym(method); + if (!CFCClass_fresh_method(self->client, sym)) { + continue; + } } - char *iface_sig = CFCGoMethod_iface_sig(meth_binding); - novel_iface - = CFCUtil_cat(novel_iface, "\t", iface_sig, "\n", NULL); - FREEMEM(iface_sig); + const char *sig = CFCGoMethod_get_sig(meth_binding); + novel_iface = CFCUtil_cat(novel_iface, "\t", sig, "\n", NULL); } char pattern[] = "type %s interface {\n" "%s" "%s" - "%s" "}\n" "\n" "%s" ; content = CFCUtil_sprintf(pattern, short_struct, parent_iface, - temp_hack_iface_methods, novel_iface, - go_struct_def); - FREEMEM(temp_hack_iface_methods); + novel_iface, go_struct_def); FREEMEM(go_struct_def); FREEMEM(parent_iface); } @@ -328,3 +313,35 @@ CFCGoClass_gen_meth_glue(CFCGoClass *self) { return meth_defs; } +void +CFCGoClass_spec_method(CFCGoClass *self, const char *name, const char *sig) { + CFCUTIL_NULL_CHECK(sig); + S_lazy_init_method_bindings(self); + if (!name) { + CFCGoMethod *meth_binding = CFCGoMethod_new(NULL); + CFCGoMethod_customize(meth_binding, sig); + + size_t size = (self->num_bound + 2) * sizeof(CFCGoMethod*); + self->method_bindings + = (CFCGoMethod**)REALLOCATE(self->method_bindings, size); + self->method_bindings[self->num_bound] = meth_binding; + self->num_bound++; + self->method_bindings[self->num_bound] = NULL; + } + else { + CFCGoMethod *binding = NULL; + for (int i = 0; self->method_bindings[i] != NULL; i++) { + CFCGoMethod *candidate = self->method_bindings[i]; + CFCMethod *meth = CFCGoMethod_get_client(candidate); + if (meth && strcmp(name, CFCMethod_get_macro_sym(meth)) == 0) { + binding = candidate; + break; + } + } + if (!binding) { + CFCUtil_die("Can't find a method named '%s'", name); + } + CFCGoMethod_customize(binding, sig); + } +} + http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/905b30ac/compiler/src/CFCGoClass.h ---------------------------------------------------------------------- diff --git a/compiler/src/CFCGoClass.h b/compiler/src/CFCGoClass.h index 98d5fce..b074a4f 100644 --- a/compiler/src/CFCGoClass.h +++ b/compiler/src/CFCGoClass.h @@ -73,6 +73,9 @@ CFCGoClass_boilerplate_funcs(CFCGoClass *self); char* CFCGoClass_gen_meth_glue(CFCGoClass *self); +void +CFCGoClass_spec_method(CFCGoClass *self, const char *name, const char *sig); + #ifdef __cplusplus } #endif http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/905b30ac/compiler/src/CFCGoMethod.c ---------------------------------------------------------------------- diff --git a/compiler/src/CFCGoMethod.c b/compiler/src/CFCGoMethod.c index 2575153..fb05659 100644 --- a/compiler/src/CFCGoMethod.c +++ b/compiler/src/CFCGoMethod.c @@ -42,6 +42,7 @@ struct CFCGoMethod { CFCBase base; CFCMethod *method; + char *sig; }; static void @@ -58,12 +59,14 @@ CFCGoMethod_new(CFCMethod *method) { CFCGoMethod *self = (CFCGoMethod*)CFCBase_allocate(&CFCGOMETHOD_META); self->method = (CFCMethod*)CFCBase_incref((CFCBase*)method); + self->sig = NULL; return self; } static void S_CFCGoMethod_destroy(CFCGoMethod *self) { CFCBase_decref((CFCBase*)self->method); + FREEMEM(self->sig); CFCBase_destroy((CFCBase*)self); } @@ -72,8 +75,21 @@ CFCGoMethod_get_client(CFCGoMethod *self) { return self->method; } -char* -CFCGoMethod_iface_sig(CFCGoMethod *self) { +void +CFCGoMethod_customize(CFCGoMethod *self, const char *sig) { + FREEMEM(self->sig); + self->sig = CFCUtil_strdup(sig); + if (self->method) { + CFCMethod_exclude_from_host(self->method); + } +} + +static void +S_lazy_init_sig(CFCGoMethod *self) { + if (self->sig || !self->method) { + return; + } + CFCMethod *method = self->method; CFCParcel *parcel = CFCMethod_get_parcel(method); CFCType *return_type = CFCMethod_get_return_type(method); @@ -96,12 +112,25 @@ CFCGoMethod_iface_sig(CFCGoMethod *self) { FREEMEM(go_type); } - char *sig = CFCUtil_sprintf("%s(%s) %s", name, args, go_ret_type); + self->sig = CFCUtil_sprintf("%s(%s) %s", name, args, go_ret_type); FREEMEM(args); FREEMEM(go_ret_type); FREEMEM(name); - return sig; +} + +const char* +CFCGoMethod_get_sig(CFCGoMethod *self) { + if (self->sig) { + return self->sig; + } + else if (!self->method) { + return ""; + } + else { + S_lazy_init_sig(self); + return self->sig; + } } #define GO_NAME_BUF_SIZE 128 @@ -149,6 +178,10 @@ S_prep_cfargs(CFCClass *invoker, CFCParamList *param_list) { char* CFCGoMethod_func_def(CFCGoMethod *self, CFCClass *invoker) { + if (!self->method || CFCMethod_excluded_from_host(self->method)) { + return CFCUtil_strdup(""); + } + CFCMethod *novel_method = CFCMethod_find_novel_method(self->method); CFCParcel *parcel = CFCClass_get_parcel(invoker); CFCParamList *param_list = CFCMethod_get_param_list(novel_method); http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/905b30ac/compiler/src/CFCGoMethod.h ---------------------------------------------------------------------- diff --git a/compiler/src/CFCGoMethod.h b/compiler/src/CFCGoMethod.h index 55314d8..2d956e9 100644 --- a/compiler/src/CFCGoMethod.h +++ b/compiler/src/CFCGoMethod.h @@ -34,8 +34,17 @@ CFCGoMethod_new(struct CFCMethod *method); struct CFCMethod* CFCGoMethod_get_client(CFCGoMethod *self); -char* -CFCGoMethod_iface_sig(CFCGoMethod *self); +/** Customize the a Go method binding. Supply a method signature to be + * inserted into the Go interface and suppress the default method + * implementation. + */ +void +CFCGoMethod_customize(CFCGoMethod *self, const char *sig); + +/** Retrieve the Go interface method signature. + */ +const char* +CFCGoMethod_get_sig(CFCGoMethod *self); char* CFCGoMethod_func_def(CFCGoMethod *self, struct CFCClass *invoker); http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/905b30ac/runtime/go/build.go ---------------------------------------------------------------------- diff --git a/runtime/go/build.go b/runtime/go/build.go index cf4cb82..4a182c7 100644 --- a/runtime/go/build.go +++ b/runtime/go/build.go @@ -127,12 +127,23 @@ func runCFC() { goBinding.SetHeader(autogenHeader) goBinding.SetSuppressInit(true) parcel := cfc.FetchParcel("Clownfish") + specMethods(parcel) packageDir := path.Join(buildDir, "clownfish") goBinding.WriteBindings(parcel, packageDir) hierarchy.WriteLog() } } +func specMethods(parcel *cfc.Parcel) { + objBinding := cfc.NewGoClass(parcel, "Clownfish::Obj") + objBinding.SpecMethod("", "TOPTR() uintptr") + objBinding.Register() + + errBinding := cfc.NewGoClass(parcel, "Clownfish::Err") + errBinding.SpecMethod("", "Error() string") + errBinding.Register() +} + func prep() { configure() runCFC()
