wip
Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/40a5c015 Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/40a5c015 Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/40a5c015 Branch: refs/heads/py_exp1 Commit: 40a5c015de020fb6f846e329b6fe2e82b5632227 Parents: 0f3b369 Author: Marvin Humphrey <[email protected]> Authored: Sat Dec 6 13:56:40 2014 -0800 Committer: Marvin Humphrey <[email protected]> Committed: Sat Dec 13 09:46:39 2014 -0800 ---------------------------------------------------------------------- compiler/go/cfc/cfc.go | 82 +++++++ compiler/include/CFC.h | 6 + compiler/src/CFCGo.c | 467 ++++++++++++++++++++++++++++++++++++++++ compiler/src/CFCGo.h | 69 ++++++ compiler/src/CFCGoClass.c | 349 ++++++++++++++++++++++++++++++ compiler/src/CFCGoClass.h | 143 ++++++++++++ compiler/src/CFCGoFunc.c | 174 +++++++++++++++ compiler/src/CFCGoFunc.h | 104 +++++++++ compiler/src/CFCGoMethod.c | 243 +++++++++++++++++++++ compiler/src/CFCGoMethod.h | 77 +++++++ runtime/go/build.go | 12 +- 11 files changed, 1725 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/40a5c015/compiler/go/cfc/cfc.go ---------------------------------------------------------------------- diff --git a/compiler/go/cfc/cfc.go b/compiler/go/cfc/cfc.go index 7c12a6c..0e515e9 100644 --- a/compiler/go/cfc/cfc.go +++ b/compiler/go/cfc/cfc.go @@ -67,6 +67,10 @@ func DoStuff() { hierarchy.Build() } +type Parcel struct { + ref *C.CFCParcel +} + type Hierarchy struct { ref *C.CFCHierarchy } @@ -79,6 +83,30 @@ type BindC struct { ref *C.CFCC } +type BindGo struct { + ref *C.CFCGo +} + +type GoClass struct { + ref *C.CFCGoClass +} + +func FetchParcel(name string) *Parcel { + nameC := C.CString(name) + defer C.free(unsafe.Pointer(nameC)) + parcelC := C.CFCParcel_fetch(nameC) + if parcelC == nil { + return nil + } + obj := &Parcel{parcelC} + runtime.SetFinalizer(obj, (*Parcel).finalize) + return obj +} + +func (obj *Parcel) finalize() { + C.CFCBase_decref((*C.CFCBase)(unsafe.Pointer(obj.ref))) +} + func NewHierarchy(dest string) *Hierarchy { destCString := C.CString(dest) defer C.free(unsafe.Pointer(destCString)) @@ -159,3 +187,57 @@ func (obj *BindC) WriteCallbacks() { func (obj *BindC) WriteHostDefs() { C.CFCC_write_hostdefs(obj.ref) } + +func NewBindGo(hierarchy *Hierarchy) *BindGo { + obj := &BindGo{ + C.CFCGo_new(hierarchy.ref), + } + runtime.SetFinalizer(obj, (*BindGo).finalize) + return obj +} + +func (obj *BindGo) finalize() { + C.CFCBase_decref((*C.CFCBase)(unsafe.Pointer(obj.ref))) +} + +func (obj *BindGo) SetHeader(header string) { + headerCString := C.CString(header) + defer C.free(unsafe.Pointer(headerCString)) + C.CFCGo_set_header(obj.ref, headerCString) +} + +func (obj *BindGo) SetFooter(header string) { + headerCString := C.CString(header) + defer C.free(unsafe.Pointer(headerCString)) + C.CFCGo_set_header(obj.ref, headerCString) +} + +func (obj *BindGo) WriteBindings(parcelName string, dest string) { + parcelNameC := C.CString(parcelName) + destC := C.CString(dest) + defer C.free(unsafe.Pointer(parcelNameC)) + defer C.free(unsafe.Pointer(destC)) + C.CFCGo_write_bindings(obj.ref, parcelNameC, destC) +} + +func NewGoClass(parcel *Parcel, className string) *GoClass { + var parcelC *C.CFCParcel + if parcel != nil { + parcelC = parcel.ref + } + classNameC := C.CString(className) + defer C.free(unsafe.Pointer(classNameC)) + obj := &GoClass{ C.CFCGoClass_new(parcelC, classNameC) } + runtime.SetFinalizer(obj, (*GoClass).finalize) + return obj +} + +func (obj *GoClass) finalize() { + C.CFCBase_decref((*C.CFCBase)(unsafe.Pointer(obj.ref))) +} + +func RegisterGoClass(binding *GoClass) { + C.CFCGoClass_register(binding.ref) +} + + http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/40a5c015/compiler/include/CFC.h ---------------------------------------------------------------------- diff --git a/compiler/include/CFC.h b/compiler/include/CFC.h index d1eda29..0ef3b09 100644 --- a/compiler/include/CFC.h +++ b/compiler/include/CFC.h @@ -44,6 +44,11 @@ #include "CFCBindFunction.h" #include "CFCBindMethod.h" +#include "CFCGo.h" +#include "CFCGoFunc.h" +#include "CFCGoMethod.h" +#include "CFCGoClass.h" + #include "CFCPerl.h" #include "CFCPerlSub.h" #include "CFCPerlMethod.h" @@ -52,5 +57,6 @@ #include "CFCPerlPod.h" #include "CFCPerlTypeMap.h" + #include "CFCRuby.h" http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/40a5c015/compiler/src/CFCGo.c ---------------------------------------------------------------------- diff --git a/compiler/src/CFCGo.c b/compiler/src/CFCGo.c new file mode 100644 index 0000000..8da8a0f --- /dev/null +++ b/compiler/src/CFCGo.c @@ -0,0 +1,467 @@ +/* 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 "charmony.h" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> + +#define CFC_NEED_BASE_STRUCT_DEF +#include "CFCBase.h" +#include "CFCGo.h" +#include "CFCParcel.h" +#include "CFCClass.h" +#include "CFCMethod.h" +#include "CFCHierarchy.h" +#include "CFCUtil.h" +#include "CFCGoClass.h" +#include "CFCGoFunc.h" +#include "CFCGoMethod.h" +// #include "CFCGoConstructor.h" +#include "CFCGoTypeMap.h" +#include "CFCBindCore.h" + +static void +S_write_callbacks(CFCGo *self, CFCParcel *parcel, const char *h_includes); + +struct CFCGo { + CFCBase base; + CFCHierarchy *hierarchy; + char *header; + char *footer; +}; + +static const CFCMeta CFCGO_META = { + "Clownfish::CFC::Binding::Go", + sizeof(CFCGo), + (CFCBase_destroy_t)CFCGo_destroy +}; + +CFCGo* +CFCGo_new(CFCHierarchy *hierarchy) { + CFCGo *self = (CFCGo*)CFCBase_allocate(&CFCGO_META); + return CFCGo_init(self, hierarchy); +} + +CFCGo* +CFCGo_init(CFCGo *self, CFCHierarchy *hierarchy) { + CFCUTIL_NULL_CHECK(hierarchy); + self->hierarchy = (CFCHierarchy*)CFCBase_incref((CFCBase*)hierarchy); + self->header = CFCUtil_strdup(""); + self->footer = CFCUtil_strdup(""); + return self; +} + +void +CFCGo_destroy(CFCGo *self) { + CFCBase_decref((CFCBase*)self->hierarchy); + FREEMEM(self->header); + FREEMEM(self->footer); + CFCBase_destroy((CFCBase*)self); +} + +void +CFCGo_set_header(CFCGo *self, const char *header) { + CFCUTIL_NULL_CHECK(header); + free(self->header); + self->header = CFCUtil_strdup(header); +} + +void +CFCGo_set_footer(CFCGo *self, const char *footer) { + CFCUTIL_NULL_CHECK(footer); + free(self->footer); + self->footer = CFCUtil_strdup(footer); +} + +void +S_write_hostdefs(CFCGo *self) { + const char pattern[] = + "%s\n" + "\n" + "#ifndef H_CFISH_HOSTDEFS\n" + "#define H_CFISH_HOSTDEFS 1\n" + "\n" + "#define CFISH_OBJ_HEAD \\\n" + " size_t refcount;\n" + "\n" + "#endif /* H_CFISH_HOSTDEFS */\n" + "\n" + "%s\n"; + char *content + = CFCUtil_sprintf(pattern, self->header, self->footer); + + // Write if the content has changed. + const char *inc_dest = CFCHierarchy_get_include_dest(self->hierarchy); + char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "cfish_hostdefs.h", + inc_dest); + CFCUtil_write_if_changed(filepath, content, strlen(content)); + + FREEMEM(filepath); + FREEMEM(content); +} + +static char* +S_gen_init(CFCGo *self, CFCParcel *parcel) { + const char *prefix = CFCParcel_get_prefix(parcel); + const char pattern[] = + "func init() {\n" + " C.%sbootstrap_parcel()\n" + " C.%sglue_cgo_symbols()\n" + "}\n"; + return CFCUtil_sprintf(pattern, prefix, prefix); +} + +static void +S_write_cfbind_go(CFCGo *self, const char *dest, const char *go_short_package, + const char *cgo_comment, const char *init_code, + const char *custom_go, const char *autogen_go) { + const char pattern[] = + "%s" + "\n" + "package %s\n" + "\n" + "/*\n" + "%s\n" + "*/\n" + "import \"C\"\n" + "import \"unsafe\"\n" + "import \"runtime\"\n" + "\n" + "%s\n" + "\n" + "%s\n" + "\n" + "%s\n" + "\n" + "//export DummyExport%s\n" + "func DummyExport%s() int {\n" + "\treturn 1\n" + "}\n" + "%s"; + char *content + = CFCUtil_sprintf(pattern, self->header, go_short_package, + cgo_comment, init_code, custom_go, autogen_go, + go_short_package, go_short_package, + self->footer); + + char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "cfbind.go", dest); + CFCUtil_write_if_changed(filepath, content, strlen(content)); + + FREEMEM(filepath); + FREEMEM(content); +} + +static char* +S_gen_glue_cgo_symbols(CFCGo *self, CFCParcel *parcel) { + // Generate implementation files containing callback definitions. + CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy); + char *decs = CFCUtil_strdup(""); + char *glue = CFCUtil_strdup(""); + for (size_t i = 0; ordered[i] != NULL; i++) { + CFCClass *klass = ordered[i]; + if (CFCClass_included(klass) + || CFCClass_inert(klass) + || CFCClass_get_parcel(klass) != parcel + ) { + continue; + } + + CFCMethod **fresh_methods = CFCClass_fresh_methods(klass); + for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) { + CFCMethod *method = fresh_methods[meth_num]; + + if (CFCMethod_novel(method) + && !CFCMethod_final(method) + && CFCGoMethod_can_be_bound(method) + ) { + const char *meth_type = CFCMethod_full_typedef(method, NULL); + const char *override_sym = CFCMethod_full_override_sym(method); + decs = CFCUtil_cat(decs, "extern ", meth_type, " ", + override_sym, "_wrapper;\n", NULL); + glue = CFCUtil_cat(glue, "\n ", override_sym, + "_wrapper = ", override_sym, "_internal;", + NULL); + } + } + FREEMEM(fresh_methods); + } + + char pattern[] = + "%s" + "\n" + "static CHY_INLINE void\n" + "%sglue_cgo_symbols(void) {%s\n" + "}\n" + "\n"; + char *content = CFCUtil_sprintf(pattern, decs, + CFCParcel_get_prefix(parcel), glue); + + FREEMEM(decs); + FREEMEM(glue); + FREEMEM(ordered); + + return content; +} + +static void +S_write_cfbind_c(CFCGo *self, const char *dest, const char *glue_cgo_symbols) { + char pattern[] = + "%s\n" + "\n" + "#include \"_cgo_export.h\"\n" + "\n" + "%s\n" + "\n" + "%s\n"; + char *content = CFCUtil_sprintf(pattern, self->header, glue_cgo_symbols, + self->footer); + char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "cfbind.c", dest); + CFCUtil_write_if_changed(filepath, content, strlen(content)); + FREEMEM(content); + FREEMEM(filepath); +} + +static char* +S_gen_callbacks_cgo(CFCGo *self, CFCParcel *parcel) { + CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy); + char *callbacks = CFCUtil_strdup(""); + + for (size_t i = 0; ordered[i] != NULL; i++) { + CFCClass *klass = ordered[i]; + if (CFCClass_included(klass) + || CFCClass_inert(klass) + || CFCClass_get_parcel(klass) != parcel + ) { + continue; + } + + CFCMethod **fresh_methods = CFCClass_fresh_methods(klass); + for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) { + CFCMethod *method = fresh_methods[meth_num]; + if (CFCMethod_novel(method) + && !CFCMethod_final(method) + && CFCGoMethod_can_be_bound(method) + ) { + char *cb_cgo = CFCGoMethod_callback_cgo(method); + callbacks = CFCUtil_cat(callbacks, cb_cgo, "\n", NULL); + FREEMEM(cb_cgo); + } + } + FREEMEM(fresh_methods); + } + + FREEMEM(ordered); + return callbacks; +} + + +void +CFCGo_write_bindings(CFCGo *self, const char *parcel_name, const char *dest) { + //CFCGoClass **registry = CFCGoClass_registry(); + CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy); + + // The Go short package name is the last component of the dot-separated + // Clownfish parcel name. + const char *parcel_frag = strrchr(parcel_name, '.'); + if (parcel_frag) { + parcel_frag += 1; + } + else { + parcel_frag = parcel_name; + } + // TODO: Don't downcase package name once caps are forbidden in Clownfish + // parcel names. + char *go_short_package = CFCUtil_strdup(parcel_frag); + for (int i = 0; go_short_package[i] != '\0'; i++) { + go_short_package[i] = tolower(go_short_package[i]); + } + + CFCParcel *parcel = NULL; + CFCParcel **parcels = CFCParcel_all_parcels(); + for (size_t i = 0; parcels[i] != NULL; i++) { + if (strcmp(CFCParcel_get_name(parcels[0]), parcel_name) == 0) { + parcel = parcels[i]; + break; + } + } + if (!parcel) { + CFCUtil_die("Can't bind unknown parcel '%s'", parcel_name); + } + + // Start binding files. + char *h_includes = CFCUtil_strdup(""); + char *cgo_comment = CFCUtil_strdup(""); + char *autogen_go = CFCUtil_strdup(""); + char *custom_go = CFCUtil_strdup(""); + + // Bake in parcel privacy define, so that binding code can be compiled + // without extra compiler flags. + { + const char *privacy_sym = CFCParcel_get_privacy_sym(parcel); + cgo_comment = CFCUtil_cat(cgo_comment, "#define ", privacy_sym, + "\n\n", NULL); + } + + // Pound-includes for generated headers. + for (size_t i = 0; ordered[i] != NULL; i++) { + CFCClass *klass = ordered[i]; + const char *include_h = CFCClass_include_h(klass); + h_includes = CFCUtil_cat(h_includes, "#include \"", include_h, + "\"\n", NULL); + } + cgo_comment = CFCUtil_cat(cgo_comment, h_includes, "\n", NULL); + + // Prep routine for exposing CGO wrappers outside this Go package. + char *glue_cgo_symbols = S_gen_glue_cgo_symbols(self, parcel); + cgo_comment = CFCUtil_cat(cgo_comment, "void\n", + CFCParcel_get_prefix(parcel), + "glue_cgo_symbols(void);\n\n", NULL); + + // Generate reflect-based wrappers for invoking methods overridden in Go. + { + char *callbacks_cgo = S_gen_callbacks_cgo(self, parcel); + autogen_go = CFCUtil_cat(autogen_go, callbacks_cgo, NULL); + FREEMEM(callbacks_cgo); + } + + for (size_t i = 0; ordered[i] != NULL; i++) { + CFCClass *klass = ordered[i]; + if (CFCClass_included(klass) + || CFCClass_get_parcel(klass) != parcel + ) { + continue; + } + + /* + // Constructors. + CFCGoConstructor **constructors + = CFCGoClass_constructor_bindings(klass); + for (size_t j = 0; constructors[j] != NULL; j++) { + // Add the function body. + char *ctor_def = CFCGoConstructor_func_def(constructors[j]); + autogen_go = CFCUtil_cat(autogen_go, ctor_def, "\n", NULL); + FREEMEM(ctor_def); + } + FREEMEM(constructors); + */ + + // Methods. + CFCGoMethod **meth_bindings = CFCGoClass_method_bindings(klass); + for (size_t j = 0; meth_bindings[j] != NULL; j++) { + CFCGoMethod *meth_binding = meth_bindings[j]; + + // Add the method body. + char *method_def = CFCGoMethod_func_def(meth_binding); + autogen_go = CFCUtil_cat(autogen_go, method_def, "\n", NULL); + FREEMEM(method_def); + } + FREEMEM(meth_bindings); + } + + /* + // Hand-rolled binding code. + for (size_t i = 0; registry[i] != NULL; i++) { + CFCGoClass *binding = registry[i]; + if (CFCClass_get_parcel(CFCGoClass_get_client(binding)) != parcel) { + continue; + } + const char *custom_code = CFCGoClass_get_custom_go(registry[i]); + custom_go = CFCUtil_cat(custom_go, custom_code, "\n", NULL); + } + */ + + char *init_code = S_gen_init(self, parcel); + + // Write out if there have been any changes. + S_write_cfbind_go(self, dest, go_short_package, cgo_comment, init_code, + custom_go, autogen_go); + + // Write out callbacks, hostdefs. + S_write_cfbind_c(self, dest, glue_cgo_symbols); + S_write_hostdefs(self); + S_write_callbacks(self, parcel, h_includes); + + FREEMEM(glue_cgo_symbols); + FREEMEM(go_short_package); + FREEMEM(init_code); + FREEMEM(h_includes); + FREEMEM(cgo_comment); + FREEMEM(ordered); +} + +static void +S_write_callbacks(CFCGo *self, CFCParcel *parcel, const char *h_includes) { + // Generate header files declaring callbacks. + // TODO: This creates header files for all parcels in source directories, + // not just the parcel being processed right this moment. It should be + // harmless, but it would be better to limit generation to one parcel. + CFCBindCore *core_binding + = CFCBindCore_new(self->hierarchy, self->header, self->footer); + CFCBindCore_write_callbacks_h(core_binding); + CFCBase_decref((CFCBase*)core_binding); + + // Generate implementation files containing callback definitions. + CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy); + char *callbacks = CFCUtil_strdup(""); + + for (size_t i = 0; ordered[i] != NULL; i++) { + CFCClass *klass = ordered[i]; + if (CFCClass_included(klass) + || CFCClass_inert(klass) + || CFCClass_get_parcel(klass) != parcel + ) { + continue; + } + + CFCMethod **fresh_methods = CFCClass_fresh_methods(klass); + for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) { + CFCMethod *method = fresh_methods[meth_num]; + + // Define callback. + if (CFCMethod_novel(method) && !CFCMethod_final(method)) { + char *cb_def = CFCGoMethod_callback_def(method); + callbacks = CFCUtil_cat(callbacks, cb_def, "\n", NULL); + FREEMEM(cb_def); + } + } + FREEMEM(fresh_methods); + } + + static const char pattern[] = + "%s\n" + "%s\n" + "\n" + "%s\n" + "\n" + "%s"; + char *content = CFCUtil_sprintf(pattern, self->header, h_includes, + callbacks, self->footer); + + // Write if changed. + const char *src_dest = CFCHierarchy_get_source_dest(self->hierarchy); + char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "callbacks.c", + src_dest); + CFCUtil_write_if_changed(filepath, content, strlen(content)); + + FREEMEM(filepath); + FREEMEM(content); + FREEMEM(callbacks); + FREEMEM(ordered); +} + http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/40a5c015/compiler/src/CFCGo.h ---------------------------------------------------------------------- diff --git a/compiler/src/CFCGo.h b/compiler/src/CFCGo.h new file mode 100644 index 0000000..ac81667 --- /dev/null +++ b/compiler/src/CFCGo.h @@ -0,0 +1,69 @@ +/* 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 H_CFCGO +#define H_CFCGO + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct CFCGo CFCGo; +struct CFCParcel; +struct CFCHierarchy; + +/** Clownfish::CFC::Binding::Go - Go bindings for a + * Clownfish::CFC::Model::Hierarchy. + * + * Clownfish::CFC::Binding::Go presents an interface for auto-generating CGO + * code to bind C code for a Clownfish class hierarchy to Go. + */ + +/** + * @param hierarchy A CFCHierarchy. + */ + +CFCGo* +CFCGo_new(struct CFCHierarchy *hierarchy); + +CFCGo* +CFCGo_init(CFCGo *self, struct CFCHierarchy *hierarchy); + +void +CFCGo_destroy(CFCGo *self); + +/** Set the text which will be prepended to generated CGO files -- for + * instance, an "autogenerated file" warning. + */ +void +CFCGo_set_header(CFCGo *self, const char *header); + +/** Set the text which will be appended to the end of generated CGO files. + */ +void +CFCGo_set_footer(CFCGo *self, const char *footer); + +/** Generate CGO bindings for the specified parcel. + */ +void +CFCGo_write_bindings(CFCGo *self, const char *parcel_name, const char *dest); + +#ifdef __cplusplus +} +#endif + +#endif /* H_CFCGO */ + http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/40a5c015/compiler/src/CFCGoClass.c ---------------------------------------------------------------------- diff --git a/compiler/src/CFCGoClass.c b/compiler/src/CFCGoClass.c new file mode 100644 index 0000000..5fe7858 --- /dev/null +++ b/compiler/src/CFCGoClass.c @@ -0,0 +1,349 @@ +/* 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 <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#define CFC_NEED_BASE_STRUCT_DEF +#include "CFCBase.h" +#include "CFCGoClass.h" +#include "CFCUtil.h" +#include "CFCClass.h" +#include "CFCMethod.h" +#include "CFCParcel.h" +#include "CFCParamList.h" +#include "CFCFunction.h" +#include "CFCSymbol.h" +#include "CFCVariable.h" +#include "CFCType.h" +#include "CFCGoFunc.h" +#include "CFCGoMethod.h" +//#include "CFCGoConstructor.h" +#include "CFCGoTypeMap.h" + +struct CFCGoClass { + CFCBase base; + CFCParcel *parcel; + char *class_name; + CFCClass *client; + char **cons_aliases; + char **cons_inits; + size_t num_cons; + int exclude_cons; +}; + +static CFCGoClass **registry = NULL; +static size_t registry_size = 0; +static size_t registry_cap = 0; + +static const CFCMeta CFCGOCLASS_META = { + "Clownfish::CFC::Binding::Go::Class", + sizeof(CFCGoClass), + (CFCBase_destroy_t)CFCGoClass_destroy +}; + +CFCGoClass* +CFCGoClass_new(CFCParcel *parcel, const char *class_name) { + CFCGoClass *self = (CFCGoClass*)CFCBase_allocate(&CFCGOCLASS_META); + return CFCGoClass_init(self, parcel, class_name); +} + +CFCGoClass* +CFCGoClass_init(CFCGoClass *self, CFCParcel *parcel, + const char *class_name) { + CFCUTIL_NULL_CHECK(parcel); + CFCUTIL_NULL_CHECK(class_name); + self->parcel = (CFCParcel*)CFCBase_incref((CFCBase*)parcel); + self->class_name = CFCUtil_strdup(class_name); + // Client may be NULL, since fetch_singleton() does not always succeed. + CFCClass *client = CFCClass_fetch_singleton(parcel, class_name); + self->client = (CFCClass*)CFCBase_incref((CFCBase*)client); + self->cons_aliases = NULL; + self->cons_inits = NULL; + self->num_cons = 0; + self->exclude_cons = 0; + return self; +} + +void +CFCGoClass_destroy(CFCGoClass *self) { + CFCBase_decref((CFCBase*)self->parcel); + CFCBase_decref((CFCBase*)self->client); + FREEMEM(self->class_name); + for (size_t i = 0; i < self->num_cons; i++) { + FREEMEM(self->cons_aliases[i]); + FREEMEM(self->cons_inits[i]); + } + FREEMEM(self->cons_aliases); + FREEMEM(self->cons_inits); + CFCBase_destroy((CFCBase*)self); +} + +static int +S_compare_cfcgoclass(const void *va, const void *vb) { + CFCGoClass *a = *(CFCGoClass**)va; + CFCGoClass *b = *(CFCGoClass**)vb; + return strcmp(a->class_name, b->class_name); +} + +void +CFCGoClass_register(CFCGoClass *self) { + if (registry_size == registry_cap) { + size_t new_cap = registry_cap + 10; + registry = (CFCGoClass**)REALLOCATE(registry, + (new_cap + 1) * sizeof(CFCGoClass*)); + for (size_t i = registry_cap; i <= new_cap; i++) { + registry[i] = NULL; + } + registry_cap = new_cap; + } + CFCGoClass *existing = CFCGoClass_singleton(self->class_name); + if (existing) { + CFCUtil_die("Class '%s' already registered", self->class_name); + } + registry[registry_size] = (CFCGoClass*)CFCBase_incref((CFCBase*)self); + registry_size++; + qsort(registry, registry_size, sizeof(CFCGoClass*), + S_compare_cfcgoclass); +} + +CFCGoClass* +CFCGoClass_singleton(const char *class_name) { + CFCUTIL_NULL_CHECK(class_name); + for (size_t i = 0; i < registry_size; i++) { + CFCGoClass *existing = registry[i]; + if (strcmp(class_name, existing->class_name) == 0) { + return existing; + } + } + return NULL; +} + +CFCGoClass** +CFCGoClass_registry() { + if (!registry) { + registry = (CFCGoClass**)CALLOCATE(1, sizeof(CFCGoClass*)); + } + return registry; +} + +void +CFCGoClass_clear_registry(void) { + for (size_t i = 0; i < registry_size; i++) { + CFCBase_decref((CFCBase*)registry[i]); + } + FREEMEM(registry); + registry_size = 0; + registry_cap = 0; + registry = NULL; +} +#if 0 + +void +CFCGoClass_exclude_method(CFCGoClass *self, const char *meth_name) { + if (!self->client) { + CFCUtil_die("Can't exclude_method %s -- can't find client for %s", + meth_name, self->class_name); + } + CFCMethod *method = CFCClass_method(self->client, meth_name); + if (!method) { + CFCUtil_die("Can't exclude_method %s -- method not found in %s", + meth_name, self->class_name); + } + if (strcmp(CFCMethod_get_class_name(method), self->class_name) != 0) { + CFCUtil_die("Can't exclude_method %s -- method not fresh in %s", + meth_name, self->class_name); + } + CFCMethod_exclude_from_host(method); +} + +void +CFCGoClass_bind_constructor(CFCGoClass *self, const char *alias, + const char *initializer) { + alias = alias ? alias : "new"; + initializer = initializer ? initializer : "init"; + size_t size = (self->num_cons + 1) * sizeof(char*); + self->cons_aliases = (char**)REALLOCATE(self->cons_aliases, size); + self->cons_inits = (char**)REALLOCATE(self->cons_inits, size); + self->cons_aliases[self->num_cons] = (char*)CFCUtil_strdup(alias); + self->cons_inits[self->num_cons] = (char*)CFCUtil_strdup(initializer); + self->num_cons++; + if (!self->client) { + CFCUtil_die("Can't bind_constructor %s -- can't find client for %s", + alias, self->class_name); + } +} + +void +CFCGoClass_exclude_constructor(CFCGoClass *self) { + self->exclude_cons = 1; +} + +#endif +CFCGoMethod** +CFCGoClass_method_bindings(CFCClass *klass) { + CFCClass *parent = CFCClass_get_parent(klass); + size_t num_bound = 0; + CFCMethod **fresh_methods = CFCClass_fresh_methods(klass); + CFCGoMethod **bound + = (CFCGoMethod**)CALLOCATE(1, sizeof(CFCGoMethod*)); + + // Iterate over the class's fresh methods. + for (size_t i = 0; fresh_methods[i] != NULL; i++) { + CFCMethod *method = fresh_methods[i]; + + // Skip methods which have been explicitly excluded. + if (CFCMethod_excluded_from_host(method)) { + continue; + } + + // Skip methods that shouldn't be bound. + if (!CFCGoMethod_can_be_bound(method)) { + continue; + } + + /* Create the binding, add it to the array. + */ + CFCGoMethod *meth_binding = CFCGoMethod_new(method); + size_t size = (num_bound + 2) * sizeof(CFCGoMethod*); + bound = (CFCGoMethod**)REALLOCATE(bound, size); + bound[num_bound] = meth_binding; + num_bound++; + bound[num_bound] = NULL; + } + + FREEMEM(fresh_methods); + + return bound; +} +#if 0 + +static const char NEW[] = "new"; + +CFCPerlConstructor** +CFCGoClass_constructor_bindings(CFCClass *klass) { + const char *class_name = CFCClass_get_class_name(klass); + CFCGoClass *perl_class = CFCGoClass_singleton(class_name); + CFCFunction **functions = CFCClass_functions(klass); + size_t num_bound = 0; + CFCPerlConstructor **bound + = (CFCPerlConstructor**)CALLOCATE(1, sizeof(CFCPerlConstructor*)); + + // Iterate over the list of possible initialization functions. + for (size_t i = 0; functions[i] != NULL; i++) { + CFCFunction *function = functions[i]; + const char *micro_sym = CFCFunction_micro_sym(function); + const char *alias = NULL; + + // Find user-specified alias. + if (perl_class == NULL) { + // Bind init() to new() when possible. + if (strcmp(micro_sym, "init") == 0 + && CFCPerlSub_can_be_bound(function) + ) { + alias = NEW; + } + } + else { + for (size_t j = 0; j < perl_class->num_cons; j++) { + if (strcmp(micro_sym, perl_class->cons_inits[j]) == 0) { + alias = perl_class->cons_aliases[j]; + if (!CFCPerlSub_can_be_bound(function)) { + CFCUtil_die("Can't bind %s as %s" + " -- types can't be mapped", + micro_sym, alias); + } + break; + } + } + + // Automatically bind init() to new() when possible. + if (!alias + && !perl_class->exclude_cons + && strcmp(micro_sym, "init") == 0 + && CFCPerlSub_can_be_bound(function) + ) { + int saw_new = 0; + for (size_t j = 0; j < perl_class->num_cons; j++) { + if (strcmp(perl_class->cons_aliases[j], "new") == 0) { + saw_new = 1; + } + } + if (!saw_new) { + alias = NEW; + } + } + } + + if (!alias) { + continue; + } + + // Create the binding, add it to the array. + CFCPerlConstructor *cons_binding + = CFCPerlConstructor_new(klass, alias, micro_sym); + size_t size = (num_bound + 2) * sizeof(CFCPerlConstructor*); + bound = (CFCPerlConstructor**)REALLOCATE(bound, size); + bound[num_bound] = cons_binding; + num_bound++; + bound[num_bound] = NULL; + } + + return bound; +} + +CFCClass* +CFCGoClass_get_client(CFCGoClass *self) { + return self->client; +} + +const char* +CFCGoClass_get_class_name(CFCGoClass *self) { + return self->class_name; +} + +// Generate C code which initializes method metadata. +char* +CFCGoClass_method_metadata_code(CFCGoClass *self) { + const char *class_var = CFCClass_full_class_var(self->client); + CFCMethod **fresh_methods = CFCClass_fresh_methods(self->client); + char *code = CFCUtil_strdup(""); + + for (int i = 0; fresh_methods[i] != NULL; i++) { + CFCMethod *method = fresh_methods[i]; + if (!CFCMethod_novel(method)) { continue; } + + const char *macro_sym = CFCMethod_get_macro_sym(method); + const char *alias = CFCMethod_get_host_alias(method); + if (alias) { + code = CFCUtil_cat(code, " CFISH_Class_Add_Host_Method_Alias(", + class_var, ", \"", alias, "\", \"", macro_sym, + "\");\n", NULL); + } + if (CFCMethod_excluded_from_host(method)) { + code = CFCUtil_cat(code, " CFISH_Class_Exclude_Host_Method(", + class_var, ", \"", macro_sym, "\");\n", NULL); + } + } + + FREEMEM(fresh_methods); + return code; +} + + +#endif http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/40a5c015/compiler/src/CFCGoClass.h ---------------------------------------------------------------------- diff --git a/compiler/src/CFCGoClass.h b/compiler/src/CFCGoClass.h new file mode 100644 index 0000000..a9fb973 --- /dev/null +++ b/compiler/src/CFCGoClass.h @@ -0,0 +1,143 @@ +/* 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 H_CFCGOCLASS +#define H_CFCGOCLASS + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct CFCGoClass CFCGoClass; +struct CFCParcel; +struct CFCClass; +struct CFCFunction; +struct CFCGoMethod; +struct CFCGoConstructor; + +/** Clownfish::CFC::Binding::Go::Class - Generate Go binding code for a + * Clownfish::CFC::Model::Class. + */ + +/** + * @param parcel A Clownfish::CFC::Model::Parcel. + * @param class_name The name of the class to be registered. + */ +CFCGoClass* +CFCGoClass_new(struct CFCParcel *parcel, const char *class_name); + +CFCGoClass* +CFCGoClass_init(CFCGoClass *self, struct CFCParcel *parcel, + const char *class_name); + +void +CFCGoClass_destroy(CFCGoClass *self); + +/** Add a new class binding to the registry. Each unique parcel/class-name + * combination may only be registered once. + */ +void +CFCGoClass_register(CFCGoClass *self); + +/** Given a class name, return a class binding if one exists. + */ +CFCGoClass* +CFCGoClass_singleton(const char *class_name); + +/** All registered class bindings. + */ +CFCGoClass** +CFCGoClass_registry(); + +/** Release all memory and references held by the registry. + */ +void +CFCGoClass_clear_registry(void); + +#if 0 + +/** Specify that a constructor should be made available from Go-space. + * + * The default alias for the constructor will follow Go conventions: + * `NewMyType()` for a `MyType` object. + * + * @param alias Name to override the default alias for the constructor. + * @param initializer The Clownfish name for the initialization function which + * will be invoked (default "init"). + */ +void +CFCGoClass_bind_constructor(CFCGoClass *self, const char *alias, + const char *initializer); + +#endif +/** Block the automatic generation of a method binding. + */ +void +CFCGoClass_exclude_method(CFCGoClass *self, const char *method); +#if 0 + +/** Block the automatic generation of a constructor binding. + */ +void +CFCGoClass_exclude_constructor(CFCGoClass *self); +#endif + +/** Return an array of Clownfish::CFC::Binding::Perl::Method objects + * representing all bound methods. + */ +struct CFCGoMethod** +CFCGoClass_method_bindings(struct CFCClass *klass); + +#if 0 +/** Return an array of Clownfish::CFC::Binding::Perl::Constructor objects + * representing all bound constructors. + */ +struct CFCPerlConstructor** +CFCGoClass_constructor_bindings(struct CFCClass *klass); + +/** Accessor for the Clownfish::CFC::Model::Class module to be bound. + */ +struct CFCClass* +CFCGoClass_get_client(CFCGoClass *self); + +/** Accessor for class name. + */ +const char* +CFCGoClass_get_class_name(CFCGoClass *self); + +/** Concatenate verbatim XS code. + */ +void +CFCGoClass_append_xs(CFCGoClass *self, const char *xs); + +/** Accessor for verbatim XS code. + */ +const char* +CFCGoClass_get_xs_code(CFCGoClass *self); + +/** Return C code which initializes method metadata. + */ +char* +CFCGoClass_method_metadata_code(CFCGoClass *self); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif /* H_CFCGOCLASS */ + http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/40a5c015/compiler/src/CFCGoFunc.c ---------------------------------------------------------------------- diff --git a/compiler/src/CFCGoFunc.c b/compiler/src/CFCGoFunc.c new file mode 100644 index 0000000..84fd211 --- /dev/null +++ b/compiler/src/CFCGoFunc.c @@ -0,0 +1,174 @@ +/* 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 <string.h> +#include <stdio.h> +#define CFC_NEED_BASE_STRUCT_DEF +#define CFC_NEED_GOFUNC_STRUCT_DEF +#include "CFCGoFunc.h" +#include "CFCGoTypeMap.h" +#include "CFCBase.h" +#include "CFCFunction.h" +#include "CFCUtil.h" +#include "CFCParcel.h" +#include "CFCParamList.h" +#include "CFCVariable.h" +#include "CFCType.h" + +#ifndef true + #define true 1 + #define false 0 +#endif + +CFCGoFunc* +CFCGoFunc_init(CFCGoFunc *self, CFCParcel *parcel, + CFCParamList *param_list) { + CFCUTIL_NULL_CHECK(param_list); + CFCUTIL_NULL_CHECK(parcel); + self->param_list = (CFCParamList*)CFCBase_incref((CFCBase*)param_list); + self->parcel = (CFCParcel*)CFCBase_incref((CFCBase*)parcel); + self->go_name = NULL; + return self; +} + +void +CFCGoFunc_destroy(CFCGoFunc *self) { + CFCBase_decref((CFCBase*)self->param_list); + CFCBase_decref((CFCBase*)self->parcel); + FREEMEM(self->go_name); + CFCBase_destroy((CFCBase*)self); +} + +int +CFCGoFunc_can_be_bound(CFCFunction *function) { + // Test whether parameters can be mapped automatically. + CFCParamList *param_list = CFCFunction_get_param_list(function); + CFCVariable **arg_vars = CFCParamList_get_variables(param_list); + for (size_t i = 0; arg_vars[i] != NULL; i++) { + CFCType *type = CFCVariable_get_type(arg_vars[i]); + if (!CFCType_is_object(type) && !CFCType_is_primitive(type)) { + return false; + } + } + + // Test whether return type can be mapped automatically. + CFCType *return_type = CFCFunction_get_return_type(function); + if (!CFCType_is_void(return_type) + && !CFCType_is_object(return_type) + && !CFCType_is_primitive(return_type) + ) { + return false; + } + + return true; +} + +char* +CFCGoFunc_func_start(const char *name, CFCParamList *param_list, + CFCParcel *parcel, CFCType *return_type, int is_method) { + CFCVariable **param_vars = CFCParamList_get_variables(param_list); + char *invocant; + if (is_method) { + CFCVariable *var = param_vars[0]; + CFCType *type = CFCVariable_get_type(var); + char *go_type_name = CFCGoTypeMap_go_type_name(type, parcel); + invocant = CFCUtil_sprintf("(%s %s) ", CFCVariable_micro_sym(var), + go_type_name); + FREEMEM(go_type_name); + } + else { + invocant = CFCUtil_strdup(""); + } + + char *params = CFCUtil_strdup(""); + int start = is_method ? 1 : 0; + for (int i = start; param_vars[i] != NULL; i++) { + CFCVariable *var = param_vars[i]; + CFCType *type = CFCVariable_get_type(var); + char *go_type_name = CFCGoTypeMap_go_type_name(type, parcel); + if (i > start) { + params = CFCUtil_cat(params, ", ", NULL); + } + params = CFCUtil_cat(params, CFCVariable_micro_sym(var), " ", + go_type_name, NULL); + FREEMEM(go_type_name); + } + + char *ret_type_str; + if (CFCType_is_void(return_type)) { + ret_type_str = CFCUtil_strdup(""); + } + else { + ret_type_str = CFCGoTypeMap_go_type_name(return_type, parcel); + if (ret_type_str == NULL) { + CFCUtil_die("Can't convert invalid type in method %s", name); + } + ret_type_str = CFCUtil_cat(ret_type_str, " ", NULL); + } + + char pattern[] = "func %s%s(%s) %s{"; + char *content = CFCUtil_sprintf(pattern, invocant, name, params, + ret_type_str); + + FREEMEM(invocant); + FREEMEM(params); + FREEMEM(ret_type_str); + + return content; +} + +char* +CFCGoFunc_go_meth_name(const char *orig) { + char *go_name = CFCUtil_strdup(orig); + for (int i = 0, j = 0, max = strlen(go_name) + 1; i < max; i++) { + if (go_name[i] != '_') { + go_name[j++] = go_name[i]; + } + } + return go_name; +} + + +CFCParamList* +CFCGoFunc_get_param_list(CFCGoFunc *self) { + return self->param_list; +} + +CFCParcel* +CFCGoFunc_get_parcel(CFCGoFunc *self) { + return self->parcel; +} + +void +CFCGoFunc_set_go_name(CFCGoFunc *self, const char *go_name) { + FREEMEM(self->go_name); + self->go_name = CFCUtil_strdup(go_name); +} + +const char* +CFCGoFunc_get_go_name(CFCGoFunc *self) { + if (!self->go_name) { + CFCUtil_die("go name not yet set"); + } + return self->go_name; +} + +const char* +CFCGoFunc_c_name_list(CFCGoFunc *self) { + return CFCParamList_name_list(self->param_list); +} + http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/40a5c015/compiler/src/CFCGoFunc.h ---------------------------------------------------------------------- diff --git a/compiler/src/CFCGoFunc.h b/compiler/src/CFCGoFunc.h new file mode 100644 index 0000000..3d67e75 --- /dev/null +++ b/compiler/src/CFCGoFunc.h @@ -0,0 +1,104 @@ +/* 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 H_CFCGOFUNC +#define H_CFCGOFUNC + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct CFCGoFunc CFCGoFunc; +struct CFCParcel; +struct CFCFunction; +struct CFCParamList; +struct CFCType; + +#ifdef CFC_NEED_GOFUNC_STRUCT_DEF +#define CFC_NEED_BASE_STRUCT_DEF +#include "CFCBase.h" +struct CFCGoFunc { + CFCBase base; + struct CFCParcel *parcel; + struct CFCParamList *param_list; + char *go_name; +}; +#endif + +/** Clownfish::CFC::Binding::Go::Function + * + * This class is used to generate binding code for invoking Clownfish's + * inert functions the Go/C barrier. + */ + +/** Constructor. + * + * @param parcel The Clownfish parcel the function belongs to. + * @param param_list A Clownfish::CFC::Model::ParamList. + */ +CFCGoFunc* +CFCGoFunc_init(CFCGoFunc *self, struct CFCParcel *parcel, + struct CFCParamList *param_list); + +void +CFCGoFunc_destroy(CFCGoFunc *self); + +/** Test whether bindings can be generated for a CFCFunction. + */ +int +CFCGoFunc_can_be_bound(struct CFCFunction *function); + +struct CFCParamList* +CFCGoFunc_get_param_list(CFCGoFunc *self); + +struct CFCParcel* +CFCGoFunc_get_parcel(CFCGoFunc *self); + +/** + * Set the name of the Go wrapper func. + */ +void +CFCGoFunc_set_go_name(CFCGoFunc *self, const char *go_name); + +/** + * Return the fully-qualified Go func name. Throws an error if set_name() has + * not yet been called. + */ +const char* +CFCGoFunc_get_go_name(CFCGoFunc *self); + +/** + * @return a string containing the names of arguments to feed to bound C + * function, joined by commas. + */ +const char* +CFCGoFunc_c_name_list(CFCGoFunc *self); + +char* +CFCGoFunc_func_start(const char *name, struct CFCParamList *param_list, + struct CFCParcel *parcel, struct CFCType *return_type, + int is_method); + +char* +CFCGoFunc_go_meth_name(const char *cf_name); + +#ifdef __cplusplus +} +#endif + +#endif /* H_CFCGOFUNC */ + http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/40a5c015/compiler/src/CFCGoMethod.c ---------------------------------------------------------------------- diff --git a/compiler/src/CFCGoMethod.c b/compiler/src/CFCGoMethod.c new file mode 100644 index 0000000..76a1a9c --- /dev/null +++ b/compiler/src/CFCGoMethod.c @@ -0,0 +1,243 @@ +/* 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 <string.h> +#include <stdio.h> +#include <ctype.h> + +#define CFC_NEED_GOFUNC_STRUCT_DEF 1 +#include "CFCGoFunc.h" +#include "CFCGoMethod.h" +#include "CFCUtil.h" +#include "CFCClass.h" +#include "CFCFunction.h" +#include "CFCMethod.h" +#include "CFCSymbol.h" +#include "CFCType.h" +#include "CFCParcel.h" +#include "CFCParamList.h" +#include "CFCGoTypeMap.h" +#include "CFCVariable.h" + +struct CFCGoMethod { + CFCBase base; + CFCMethod *method; +}; + +/* Take a NULL-terminated list of CFCVariables and build up a string of + * directives like: + * + * UNUSED_VAR(var1); + * UNUSED_VAR(var2); + */ +static char* +S_build_unused_vars(CFCVariable **vars); + +/* Create an unreachable return statement if necessary, in order to thwart + * compiler warnings. */ +static char* +S_maybe_unreachable(CFCType *return_type); + +static const CFCMeta CFCPERLMETHOD_META = { + "Clownfish::CFC::Binding::Perl::Method", + sizeof(CFCGoMethod), + (CFCBase_destroy_t)CFCGoMethod_destroy +}; + +CFCGoMethod* +CFCGoMethod_new(CFCMethod *method) { + CFCGoMethod *self + = (CFCGoMethod*)CFCBase_allocate(&CFCPERLMETHOD_META); + return CFCGoMethod_init(self, method); +} + +CFCGoMethod* +CFCGoMethod_init(CFCGoMethod *self, CFCMethod *method) { + self->method = (CFCMethod*)CFCBase_incref((CFCBase*)method); + return self; +} + +void +CFCGoMethod_destroy(CFCGoMethod *self) { + CFCBase_decref((CFCBase*)self->method); + CFCBase_destroy((CFCBase*)self); +} + +char* +CFCGoMethod_func_def(CFCGoMethod *self) { + CFCMethod *method = self->method; + CFCParcel *parcel = CFCMethod_get_parcel(method); + CFCParamList *param_list = CFCMethod_get_param_list(method); + CFCType *ret_type = CFCMethod_get_return_type(method); + char *name = CFCGoFunc_go_meth_name(CFCMethod_get_macro_sym(method)); + char *start = CFCGoFunc_func_start(name, param_list, parcel, ret_type, 1); + char pattern[] = + "%s\n" + "}\n"; + char *content = CFCUtil_sprintf(pattern, start); + + FREEMEM(name); + FREEMEM(start); + return content; +} + +int +CFCGoMethod_can_be_bound(CFCMethod *method) { + // Check for + // - private methods + // - methods with types which cannot be mapped automatically + return !CFCSymbol_private((CFCSymbol*)method) + && CFCGoFunc_can_be_bound((CFCFunction*)method); +} + +char* +CFCGoMethod_callback_def(CFCMethod *method) { + CFCParamList *param_list = CFCMethod_get_param_list(method); + CFCType *return_type = CFCMethod_get_return_type(method); + char *meth_typedef = CFCMethod_full_typedef(method, NULL); + char *meth_sym = CFCMethod_full_method_sym(method, NULL); + const char *ret_type_str = CFCType_to_c(return_type); + const char *override_sym = CFCMethod_full_override_sym(method); + const char *params = CFCParamList_to_c(param_list); + const char *name_list = CFCParamList_name_list(param_list); + const char *maybe_return = CFCType_is_void(return_type) + ? "" : "return "; + char *content; + + if (CFCGoMethod_can_be_bound(method)) { + const char pattern[] = + "%s %s_wrapper;\n" + "%s\n" + "%s(%s) {\n" + " %s%s_wrapper(%s);\n" + "}\n"; + content = CFCUtil_sprintf(pattern, meth_typedef, override_sym, + ret_type_str, override_sym, params, + maybe_return, override_sym, name_list); + } + else { + CFCVariable **vars = CFCParamList_get_variables(param_list); + char *unused = S_build_unused_vars(vars); + char *unreachable = S_maybe_unreachable(return_type); + const char pattern[] = + "%s\n" + "%s(%s) {%s\n" + " CFISH_THROW(CFISH_ERR, \"Can't override %s via binding\");%s\n" + "}\n"; + content = CFCUtil_sprintf(pattern, ret_type_str, override_sym, + params, unused, meth_sym, unreachable); + FREEMEM(unused); + FREEMEM(unreachable); + } + + FREEMEM(meth_sym); + FREEMEM(meth_typedef); + return content; +} + +char* +CFCGoMethod_callback_cgo(CFCMethod *method) { + const char *override_sym = CFCMethod_full_override_sym(method); + CFCParcel *parcel = CFCMethod_get_parcel(method); + CFCParamList *param_list = CFCMethod_get_param_list(method); + CFCVariable **param_vars = CFCParamList_get_variables(param_list); + CFCType *self_type = CFCMethod_self_type(method); + CFCType *return_type = CFCMethod_get_return_type(method); + char *meth_body = ""; + + char *self_type_str = CFCGoTypeMap_cgo_type_name(self_type); + if (self_type_str == NULL) { + CFCUtil_die("Can't convert invalid type in method %s", + CFCMethod_full_method_sym(method, NULL)); + } + + char *ret_type_str; + if (CFCType_is_void(return_type)) { + ret_type_str = CFCUtil_strdup(""); + } + else { + ret_type_str = CFCGoTypeMap_cgo_type_name(return_type); + if (ret_type_str == NULL) { + CFCUtil_die("Can't convert invalid type in method %s", + CFCMethod_full_method_sym(method, NULL)); + } + ret_type_str = CFCUtil_cat(ret_type_str, " ", NULL); + } + + char *params = CFCUtil_strdup(""); + for (int i = 1; param_vars[i] != NULL; i++) { + CFCVariable *var = param_vars[i]; + CFCType *type = CFCVariable_get_type(var); + char *cgo_type_name = CFCGoTypeMap_cgo_type_name(type); + if (cgo_type_name == NULL) { + CFCUtil_die("Can't convert invalid type in method %s", + CFCMethod_full_method_sym(method, NULL)); + } + if (i > 1) { + params = CFCUtil_cat(params, ", ", NULL); + } + params = CFCUtil_cat(params, CFCVariable_micro_sym(var), " ", + cgo_type_name, NULL); + FREEMEM(cgo_type_name); + } + + char pattern[] = + "//export %s_internal\n" + "func (self %s) %s_internal(%s) %s{\n" + "%s" + "}\n"; + char *content = CFCUtil_sprintf(pattern, override_sym, self_type_str, + override_sym, params, ret_type_str, + meth_body); + + FREEMEM(self_type_str); + FREEMEM(ret_type_str); + FREEMEM(params); + return content; +} + +static char* +S_build_unused_vars(CFCVariable **vars) { + char *unused = CFCUtil_strdup(""); + + for (int i = 0; vars[i] != NULL; i++) { + const char *var_name = CFCVariable_micro_sym(vars[i]); + size_t size = strlen(unused) + strlen(var_name) + 80; + unused = (char*)REALLOCATE(unused, size); + strcat(unused, "\n CFISH_UNUSED_VAR("); + strcat(unused, var_name); + strcat(unused, ");"); + } + + return unused; +} + +static char* +S_maybe_unreachable(CFCType *return_type) { + char *return_statement; + if (CFCType_is_void(return_type)) { + return_statement = CFCUtil_strdup(""); + } + else { + const char *ret_type_str = CFCType_to_c(return_type); + char pattern[] = "\n CFISH_UNREACHABLE_RETURN(%s);"; + return_statement = CFCUtil_sprintf(pattern, ret_type_str); + } + return return_statement; +} + + http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/40a5c015/compiler/src/CFCGoMethod.h ---------------------------------------------------------------------- diff --git a/compiler/src/CFCGoMethod.h b/compiler/src/CFCGoMethod.h new file mode 100644 index 0000000..b1f079b --- /dev/null +++ b/compiler/src/CFCGoMethod.h @@ -0,0 +1,77 @@ +/* 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 H_CFCGOMETHOD +#define H_CFCGOMETHOD + +#ifdef __cplusplus +extern "C" { +#endif + +/** Clownfish::CFC::Binding::Go::Method - Binding for an object method. + * + * + * This class isa Clownfish::CFC::Binding::Go::Subroutine -- see its + * documentation for various code-generating routines. + * + * Method bindings use labeled parameters if the C function takes more than one + * argument (other than "self"). If there is only one argument, the binding + * will be set up to accept a single positional argument. + */ +typedef struct CFCGoMethod CFCGoMethod; +struct CFCMethod; + +CFCGoMethod* +CFCGoMethod_new(struct CFCMethod *method); + +/** + * @param method A Clownfish::CFC::Model::Method. + */ +CFCGoMethod* +CFCGoMethod_init(CFCGoMethod *self, struct CFCMethod *method); + +void +CFCGoMethod_destroy(CFCGoMethod *self); + +/** Return code for invoking the method from Go-space. + */ +char* +CFCGoMethod_func_def(CFCGoMethod *self); + +/** Test whether bindings should be generated for a method. + */ +int +CFCGoMethod_can_be_bound(struct CFCMethod *method); + +/** Return CGO code which uses the go `reflect` package to dispatch to a + * method overridden in Go. + */ +char* +CFCGoMethod_callback_cgo(struct CFCMethod *method); + +/** Return C code which knows how to call back into Go for this method. This + * code is run when a Go subclass has overridden a method in a Clownfish base + * class. + */ +char* +CFCGoMethod_callback_def(struct CFCMethod *method); + +#ifdef __cplusplus +} +#endif + +#endif /* H_CFCGOMETHOD */ + http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/40a5c015/runtime/go/build.go ---------------------------------------------------------------------- diff --git a/runtime/go/build.go b/runtime/go/build.go index a82cc36..26d7bb4 100644 --- a/runtime/go/build.go +++ b/runtime/go/build.go @@ -121,13 +121,23 @@ func runCFC() { coreBinding := cfc.NewBindCore(hierarchy, autogenHeader, "") modified := coreBinding.WriteAllModified(false) if modified { + bindClasses() cBinding := cfc.NewBindC(hierarchy, autogenHeader, "") cBinding.WriteCallbacks() - cBinding.WriteHostDefs() + goBinding := cfc.NewBindGo(hierarchy) + goBinding.SetHeader(autogenHeader) + packageDir := path.Join(buildDir, "clownfish") + goBinding.WriteBindings("Clownfish", packageDir) hierarchy.WriteLog() } } +func bindClasses() { + parcel := cfc.FetchParcel("Clownfish") + hashBinding := cfc.NewGoClass(parcel, "Clownfish::Hash") + cfc.RegisterGoClass(hashBinding) +} + func prep() { configure() runCFC()
