You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucy.apache.org by ma...@apache.org on 2015/08/03 23:47:40 UTC

[1/7] lucy-clownfish git commit: Identify convertible core types more robustly.

Repository: lucy-clownfish
Updated Branches:
  refs/heads/master 570984e73 -> 1fc8c00a1


Identify convertible core types more robustly.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/978a011e
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/978a011e
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/978a011e

Branch: refs/heads/master
Commit: 978a011e36d376319e3afb106995f7ca9822e06a
Parents: 570984e
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Sat Jul 25 17:51:10 2015 -0700
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Fri Jul 31 11:03:02 2015 -0700

----------------------------------------------------------------------
 compiler/src/CFCType.c | 116 ++++++++++++++++++++++++++++++++++----------
 compiler/src/CFCType.h |  81 ++++++++++++++++++++++++-------
 2 files changed, 154 insertions(+), 43 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/978a011e/compiler/src/CFCType.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCType.c b/compiler/src/CFCType.c
index ee04abf..0c882cf 100644
--- a/compiler/src/CFCType.c
+++ b/compiler/src/CFCType.c
@@ -63,18 +63,25 @@ S_check_flags(int supplied, int acceptable, const char *type_name) {
     int bad = (supplied & ~acceptable);
     if (bad) {
         char bad_flag[20];
-        if ((bad & CFCTYPE_CONST))            { strcpy(bad_flag, "CONST"); }
-        else if ((bad & CFCTYPE_NULLABLE))    { strcpy(bad_flag, "NULLABLE"); }
-        else if ((bad & CFCTYPE_INCREMENTED)) { strcpy(bad_flag, "INCREMENTED"); }
-        else if ((bad & CFCTYPE_DECREMENTED)) { strcpy(bad_flag, "DECREMENTED"); }
-        else if ((bad & CFCTYPE_OBJECT))      { strcpy(bad_flag, "OBJECT"); }
-        else if ((bad & CFCTYPE_PRIMITIVE))   { strcpy(bad_flag, "PRIMITIVE"); }
-        else if ((bad & CFCTYPE_INTEGER))     { strcpy(bad_flag, "INTEGER"); }
-        else if ((bad & CFCTYPE_FLOATING))    { strcpy(bad_flag, "FLOATING"); }
-        else if ((bad & CFCTYPE_STRING_TYPE)) { strcpy(bad_flag, "STRING_TYPE"); }
-        else if ((bad & CFCTYPE_VA_LIST))     { strcpy(bad_flag, "VA_LIST"); }
-        else if ((bad & CFCTYPE_ARBITRARY))   { strcpy(bad_flag, "ARBITRARY"); }
-        else if ((bad & CFCTYPE_COMPOSITE))   { strcpy(bad_flag, "COMPOSITE"); }
+        if ((bad & CFCTYPE_CONST))              { strcpy(bad_flag, "CONST"); }
+        else if ((bad & CFCTYPE_NULLABLE))      { strcpy(bad_flag, "NULLABLE"); }
+        else if ((bad & CFCTYPE_INCREMENTED))   { strcpy(bad_flag, "INCREMENTED"); }
+        else if ((bad & CFCTYPE_DECREMENTED))   { strcpy(bad_flag, "DECREMENTED"); }
+        else if ((bad & CFCTYPE_OBJECT))        { strcpy(bad_flag, "OBJECT"); }
+        else if ((bad & CFCTYPE_PRIMITIVE))     { strcpy(bad_flag, "PRIMITIVE"); }
+        else if ((bad & CFCTYPE_INTEGER))       { strcpy(bad_flag, "INTEGER"); }
+        else if ((bad & CFCTYPE_FLOATING))      { strcpy(bad_flag, "FLOATING"); }
+        else if ((bad & CFCTYPE_CFISH_OBJ))     { strcpy(bad_flag, "CFISH_OBJ"); }
+        else if ((bad & CFCTYPE_CFISH_STRING))  { strcpy(bad_flag, "CFISH_STRING"); }
+        else if ((bad & CFCTYPE_CFISH_BLOB))    { strcpy(bad_flag, "CFISH_BLOB"); }
+        else if ((bad & CFCTYPE_CFISH_INTEGER)) { strcpy(bad_flag, "CFISH_INTEGER"); }
+        else if ((bad & CFCTYPE_CFISH_FLOAT))   { strcpy(bad_flag, "CFISH_FLOAT"); }
+        else if ((bad & CFCTYPE_CFISH_BOOLEAN)) { strcpy(bad_flag, "CFISH_BOOLEAN"); }
+        else if ((bad & CFCTYPE_CFISH_VECTOR))  { strcpy(bad_flag, "CFISH_VECTOR"); }
+        else if ((bad & CFCTYPE_CFISH_HASH))    { strcpy(bad_flag, "CFISH_HASH"); }
+        else if ((bad & CFCTYPE_VA_LIST))       { strcpy(bad_flag, "VA_LIST"); }
+        else if ((bad & CFCTYPE_ARBITRARY))     { strcpy(bad_flag, "ARBITRARY"); }
+        else if ((bad & CFCTYPE_COMPOSITE))     { strcpy(bad_flag, "COMPOSITE"); }
         else {
             CFCUtil_die("Unknown flags: %d", bad);
         }
@@ -182,12 +189,36 @@ CFCType_new_object(int flags, CFCParcel *parcel, const char *specifier,
 
     // Add flags.
     flags |= CFCTYPE_OBJECT;
-    if (strcmp(specifier, "String") == 0
-        || strcmp(specifier, "cfish_String") == 0
-       ) {
-        // Determine whether this type is a string type.
-        flags |= CFCTYPE_STRING_TYPE;
+    static struct {
+        char *sym;
+        char *full_sym;
+        int   flag;
+    } cfish_types[] = {
+        {"Obj",     "cfish_Obj",     CFCTYPE_CFISH_OBJ},
+        {"String",  "cfish_String",  CFCTYPE_CFISH_STRING},
+        {"Blob",    "cfish_Blob",    CFCTYPE_CFISH_BLOB},
+        {"Integer", "cfish_Integer", CFCTYPE_CFISH_INTEGER},
+        {"Float",   "cfish_Float",   CFCTYPE_CFISH_FLOAT},
+        {"Boolean", "cfish_Boolean", CFCTYPE_CFISH_BOOLEAN},
+        {"Vector",  "cfish_Vector",  CFCTYPE_CFISH_VECTOR},
+        {"Hash",    "cfish_Hash",    CFCTYPE_CFISH_HASH}
+    };
+    int count_cfish_types = sizeof(cfish_types) / sizeof(cfish_types[0]);
+    int acceptable_flags = CFCTYPE_OBJECT
+                           | CFCTYPE_CONST
+                           | CFCTYPE_NULLABLE
+                           | CFCTYPE_INCREMENTED
+                           | CFCTYPE_DECREMENTED;
+    for (int i = 0; i < count_cfish_types; i++) {
+        if (strcmp(specifier, cfish_types[i].sym) == 0
+            || strcmp(specifier, cfish_types[i].full_sym) == 0
+           ) {
+            flags |= cfish_types[i].flag;
+            acceptable_flags |= cfish_types[i].flag;
+            break;
+        }
     }
+    S_check_flags(flags, acceptable_flags, "Object");
 
     // Validate specifier.
     if (!isalpha(*specifier)) {
@@ -204,14 +235,6 @@ CFCType_new_object(int flags, CFCParcel *parcel, const char *specifier,
         CFCUtil_die("Invalid specifier: '%s'", specifier);
     }
 
-    int acceptable_flags = CFCTYPE_OBJECT
-                           | CFCTYPE_STRING_TYPE
-                           | CFCTYPE_CONST
-                           | CFCTYPE_NULLABLE
-                           | CFCTYPE_INCREMENTED
-                           | CFCTYPE_DECREMENTED;
-    S_check_flags(flags, acceptable_flags, "Object");
-
     return CFCType_new(flags, parcel, specifier, 1);
 }
 
@@ -481,9 +504,50 @@ CFCType_is_floating(CFCType *self) {
     return !!(self->flags & CFCTYPE_FLOATING);
 }
 
+
+int
+CFCType_cfish_obj(CFCType *self) {
+    return !!(self->flags & CFCTYPE_CFISH_OBJ);
+}
+
 int
 CFCType_is_string_type(CFCType *self) {
-    return !!(self->flags & CFCTYPE_STRING_TYPE);
+    return !!(self->flags & CFCTYPE_CFISH_STRING);
+}
+
+int
+CFCType_cfish_string(CFCType *self) {
+    return !!(self->flags & CFCTYPE_CFISH_STRING);
+}
+
+int
+CFCType_cfish_blob(CFCType *self) {
+    return !!(self->flags & CFCTYPE_CFISH_BLOB);
+}
+
+int
+CFCType_cfish_integer(CFCType *self) {
+    return !!(self->flags & CFCTYPE_CFISH_INTEGER);
+}
+
+int
+CFCType_cfish_float(CFCType *self) {
+    return !!(self->flags & CFCTYPE_CFISH_FLOAT);
+}
+
+int
+CFCType_cfish_boolean(CFCType *self) {
+    return !!(self->flags & CFCTYPE_CFISH_BOOLEAN);
+}
+
+int
+CFCType_cfish_vector(CFCType *self) {
+    return !!(self->flags & CFCTYPE_CFISH_VECTOR);
+}
+
+int
+CFCType_cfish_hash(CFCType *self) {
+    return !!(self->flags & CFCTYPE_CFISH_HASH);
 }
 
 int

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/978a011e/compiler/src/CFCType.h
----------------------------------------------------------------------
diff --git a/compiler/src/CFCType.h b/compiler/src/CFCType.h
index f351eba..31800fc 100644
--- a/compiler/src/CFCType.h
+++ b/compiler/src/CFCType.h
@@ -28,19 +28,26 @@ typedef struct CFCType CFCType;
 struct CFCClass;
 struct CFCParcel;
 
-#define CFCTYPE_CONST       0x00000001
-#define CFCTYPE_NULLABLE    0x00000002
-#define CFCTYPE_VOID        0x00000004
-#define CFCTYPE_INCREMENTED 0x00000008
-#define CFCTYPE_DECREMENTED 0x00000010
-#define CFCTYPE_OBJECT      0x00000020
-#define CFCTYPE_PRIMITIVE   0x00000040
-#define CFCTYPE_INTEGER     0x00000080
-#define CFCTYPE_FLOATING    0x00000100
-#define CFCTYPE_STRING_TYPE 0x00000200
-#define CFCTYPE_VA_LIST     0x00000400
-#define CFCTYPE_ARBITRARY   0x00000800
-#define CFCTYPE_COMPOSITE   0x00001000
+#define CFCTYPE_CONST         (1 << 0)
+#define CFCTYPE_NULLABLE      (1 << 1)
+#define CFCTYPE_VOID          (1 << 2)
+#define CFCTYPE_INCREMENTED   (1 << 3)
+#define CFCTYPE_DECREMENTED   (1 << 4)
+#define CFCTYPE_OBJECT        (1 << 5)
+#define CFCTYPE_PRIMITIVE     (1 << 6)
+#define CFCTYPE_INTEGER       (1 << 7)
+#define CFCTYPE_FLOATING      (1 << 8)
+#define CFCTYPE_CFISH_OBJ     (1 << 9)
+#define CFCTYPE_CFISH_STRING  (1 << 10)
+#define CFCTYPE_CFISH_BLOB    (1 << 12)
+#define CFCTYPE_CFISH_INTEGER (1 << 13)
+#define CFCTYPE_CFISH_FLOAT   (1 << 14)
+#define CFCTYPE_CFISH_BOOLEAN (1 << 15)
+#define CFCTYPE_CFISH_VECTOR  (1 << 16)
+#define CFCTYPE_CFISH_HASH    (1 << 17)
+#define CFCTYPE_VA_LIST       (1 << 18)
+#define CFCTYPE_ARBITRARY     (1 << 19)
+#define CFCTYPE_COMPOSITE     (1 << 20)
 
 /** Generic constructor.
  *
@@ -104,8 +111,9 @@ CFCType_new_float(int flags, const char *specifier);
  *
  * The Parcel's prefix will be prepended to the specifier by new_object().
  *
- * @param flags Allowed flags: OBJECT, STRING_TYPE, CONST, NULLABLE,
- * INCREMENTED, DECREMENTED.
+ * @param flags Allowed flags: OBJECT, CFISH_OBJ, CFISH_STRING, CFISH_BLOB,
+ * CFISH_INTEGER, CFISH_FLOAT, CFISH_BOOLEAN, CFISH_VECTOR, CFISH_HASH, CONST,
+ * NULLABLE, INCREMENTED, DECREMENTED.
  * @param parcel A Clownfish::CFC::Model::Parcel.
  * @param specifier Required.  Must follow the rules for
  * Clownfish::CFC::Model::Class class name components.
@@ -255,12 +263,51 @@ CFCType_is_integer(CFCType *self);
 int
 CFCType_is_floating(CFCType *self);
 
-/** Returns true if $type represents a Clownfish type which holds unicode
- * strings.
+/** Returns true if the type is Clownfish::Obj.
+ */
+int
+CFCType_cfish_obj(CFCType *self);
+
+/** Returns true if the type is Clownfish::String.
  */
 int
 CFCType_is_string_type(CFCType *self);
 
+/** Returns true if the type is Clownfish::String.
+ */
+int
+CFCType_cfish_string(CFCType *self);
+
+/** Returns true if the type is Clownfish::Blob.
+ */
+int
+CFCType_cfish_blob(CFCType *self);
+
+/** Returns true if the type is Clownfish::Integer.
+ */
+int
+CFCType_cfish_integer(CFCType *self);
+
+/** Returns true if the type is Clownfish::Float.
+ */
+int
+CFCType_cfish_float(CFCType *self);
+
+/** Returns true if the type is Clownfish::Boolean
+ */
+int
+CFCType_cfish_boolean(CFCType *self);
+
+/** Returns true if the type is Clownfish::Vector.
+ */
+int
+CFCType_cfish_vector(CFCType *self);
+
+/** Returns true if the type is Clownfish::Hash.
+ */
+int
+CFCType_cfish_hash(CFCType *self);
+
 int
 CFCType_is_va_list(CFCType *self);
 


[2/7] lucy-clownfish git commit: Wrap any Clownfish object from Go.

Posted by ma...@apache.org.
Wrap any Clownfish object from Go.

Produce a Go func for each Clownfish class, which, given a Clownfish
C-space object pointer, wraps it in the appropriate Go struct yet
returns a clownfish.Obj.  Register these funcs in the `clownfish` Go
package and make them accessible via `clownfish.WRAPAny`.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/56e4ab9c
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/56e4ab9c
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/56e4ab9c

Branch: refs/heads/master
Commit: 56e4ab9c21f1b5a9db1cea38ca58cca8539f1a1f
Parents: 978a011
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Mon Jul 27 19:49:59 2015 -0700
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Fri Jul 31 11:05:37 2015 -0700

----------------------------------------------------------------------
 compiler/src/CFCGo.c              | 29 ++++++++++++++++++++-
 compiler/src/CFCGoClass.c         | 23 +++++++++++++++--
 compiler/src/CFCGoClass.h         |  3 +++
 runtime/go/clownfish/clownfish.go | 46 ++++++++++++++++++++++++++++++++++
 4 files changed, 98 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/56e4ab9c/compiler/src/CFCGo.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCGo.c b/compiler/src/CFCGo.c
index 3b3af2e..a5812a3 100644
--- a/compiler/src/CFCGo.c
+++ b/compiler/src/CFCGo.c
@@ -223,17 +223,21 @@ S_gen_init_code(CFCGo *self, CFCParcel *parcel) {
     const char pattern[] =
         "func init() {\n"
         "    C.%sbootstrap_parcel()\n"
+        "    initWRAP()\n"
         "}\n";
     return CFCUtil_sprintf(pattern, prefix);
 }
 
 static char*
 S_gen_autogen_go(CFCGo *self, CFCParcel *parcel) {
+    const char *clownfish_dot = CFCParcel_is_cfish(parcel)
+                                ? "" : "clownfish.";
     CFCGoClass **registry = CFCGoClass_registry();
     char *type_decs   = CFCUtil_strdup("");
     char *boilerplate = CFCUtil_strdup("");
     char *ctors       = CFCUtil_strdup("");
     char *meth_defs   = CFCUtil_strdup("");
+    char *wrap_funcs  = CFCUtil_strdup("");
 
     for (int i = 0; registry[i] != NULL; i++) {
         CFCGoClass *class_binding = registry[i];
@@ -258,6 +262,22 @@ S_gen_autogen_go(CFCGo *self, CFCParcel *parcel) {
         char *glue = CFCGoClass_gen_meth_glue(class_binding);
         meth_defs = CFCUtil_cat(meth_defs, glue, "\n", NULL);
         FREEMEM(glue);
+
+        char *wrap_func = CFCGoClass_gen_wrap_func_reg(class_binding);
+        wrap_funcs = CFCUtil_cat(wrap_funcs, wrap_func, NULL);
+        FREEMEM(wrap_func);
+    }
+
+    if (strlen(wrap_funcs)) {
+        char pattern[] =
+            "\tnewEntries := map[unsafe.Pointer]%sWrapFunc{\n%s"
+            "\t}\n"
+            "\t%sRegisterWrapFuncs(newEntries)\n"
+            ;
+        char *temp = CFCUtil_sprintf(pattern, clownfish_dot, wrap_funcs,
+                                     clownfish_dot);
+        FREEMEM(wrap_funcs);
+        wrap_funcs = temp;
     }
 
     char pattern[] =
@@ -269,6 +289,11 @@ S_gen_autogen_go(CFCGo *self, CFCParcel *parcel) {
         "\n"
         "%s\n"
         "\n"
+        "// Register WRAP functions.\n"
+        "func initWRAP() {\n"
+        "%s"
+        "}\n"
+        "\n"
         "// Constructors.\n"
         "\n"
         "%s\n"
@@ -279,8 +304,10 @@ S_gen_autogen_go(CFCGo *self, CFCParcel *parcel) {
         "\n"
         ;
     char *content
-        = CFCUtil_sprintf(pattern, type_decs, boilerplate, ctors, meth_defs);
+        = CFCUtil_sprintf(pattern, type_decs, boilerplate, wrap_funcs,
+                          ctors, meth_defs);
 
+    FREEMEM(wrap_funcs);
     FREEMEM(meth_defs);
     FREEMEM(ctors);
     FREEMEM(boilerplate);

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/56e4ab9c/compiler/src/CFCGoClass.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCGoClass.c b/compiler/src/CFCGoClass.c
index d02aeb8..4392b61 100644
--- a/compiler/src/CFCGoClass.c
+++ b/compiler/src/CFCGoClass.c
@@ -250,18 +250,24 @@ CFCGoClass_boilerplate_funcs(CFCGoClass *self) {
     else if (CFCClass_inert(self->client)) {
         content = CFCUtil_strdup("");
     } else {
+        const char *clownfish_dot = CFCParcel_is_cfish(self->parcel)
+                                    ? "" : "clownfish.";
         const char *short_struct = CFCClass_get_struct_sym(self->client);
-        const char *full_struct  = CFCClass_full_struct_sym(self->client);
         char pattern[] =
             "func WRAP%s(ptr unsafe.Pointer) %s {\n"
             "\tobj := &%sIMP{}\n"
             "\tobj.INITOBJ(ptr)\n"
             "\treturn obj\n"
             "}\n"
+            "\n"
+            "func WRAP%sASOBJ(ptr unsafe.Pointer) %sObj {\n"
+            "\treturn WRAP%s(ptr)\n"
+            "}\n"
             ;
 
         content = CFCUtil_sprintf(pattern, short_struct, short_struct,
-                                  short_struct, full_struct, short_struct);
+                                  short_struct, short_struct, clownfish_dot,
+                                  short_struct);
     }
     return content;
 }
@@ -368,6 +374,19 @@ CFCGoClass_gen_meth_glue(CFCGoClass *self) {
     return meth_defs;
 }
 
+char*
+CFCGoClass_gen_wrap_func_reg(CFCGoClass *self) {
+    if (CFCClass_inert(self->client)) {
+        return CFCUtil_strdup("");
+    }
+    char pattern[] =
+        "\t\tunsafe.Pointer(C.%s): WRAP%sASOBJ,\n";
+
+    const char *short_struct = CFCClass_get_struct_sym(self->client);
+    const char *class_var = CFCClass_full_class_var(self->client);
+    return CFCUtil_sprintf(pattern, class_var, short_struct);
+}
+
 void
 CFCGoClass_spec_method(CFCGoClass *self, const char *name, const char *sig) {
     CFCUTIL_NULL_CHECK(sig);

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/56e4ab9c/compiler/src/CFCGoClass.h
----------------------------------------------------------------------
diff --git a/compiler/src/CFCGoClass.h b/compiler/src/CFCGoClass.h
index 6a2db34..4e2cacf 100644
--- a/compiler/src/CFCGoClass.h
+++ b/compiler/src/CFCGoClass.h
@@ -76,6 +76,9 @@ CFCGoClass_gen_ctors(CFCGoClass *self);
 char*
 CFCGoClass_gen_meth_glue(CFCGoClass *self);
 
+char*
+CFCGoClass_gen_wrap_func_reg(CFCGoClass *self);
+
 void
 CFCGoClass_spec_method(CFCGoClass *self, const char *name, const char *sig);
 

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/56e4ab9c/runtime/go/clownfish/clownfish.go
----------------------------------------------------------------------
diff --git a/runtime/go/clownfish/clownfish.go b/runtime/go/clownfish/clownfish.go
index 2c5f40e..0fb7e32 100644
--- a/runtime/go/clownfish/clownfish.go
+++ b/runtime/go/clownfish/clownfish.go
@@ -62,10 +62,56 @@ GoCfish_RunRoutine(CFISH_Err_Attempt_t routine, void *context) {
 import "C"
 import "runtime"
 import "unsafe"
+import "fmt"
+import "sync"
+
+const (
+	maxUint = ^uint(0)
+	minUint = 0
+	maxInt  = int(^uint(0) >> 1)
+	minInt  = -(maxInt - 1)
+)
+
+type WrapFunc func(unsafe.Pointer)Obj
+var wrapRegMutex sync.Mutex
+var wrapReg *map[unsafe.Pointer]WrapFunc
 
 func init() {
 	C.GoCfish_glue_exported_symbols()
 	C.cfish_bootstrap_parcel()
+	initWRAP()
+}
+
+func RegisterWrapFuncs(newEntries map[unsafe.Pointer]WrapFunc) {
+	wrapRegMutex.Lock()
+	newSize := len(newEntries)
+	if wrapReg != nil {
+		newSize += len(*wrapReg)
+	}
+	newReg := make(map[unsafe.Pointer]WrapFunc, newSize)
+	if wrapReg != nil {
+		for k, v := range *wrapReg {
+			newReg[k] = v
+		}
+	}
+	for k, v := range newEntries {
+		newReg[k] = v
+	}
+	wrapReg = &newReg
+	wrapRegMutex.Unlock()
+}
+
+func WRAPAny(ptr unsafe.Pointer) Obj {
+	if ptr == nil {
+		return nil
+	}
+	class := C.cfish_Obj_get_class((*C.cfish_Obj)(ptr))
+	wrapFunc := (*wrapReg)[unsafe.Pointer(class)]
+	if wrapFunc == nil {
+		className := CFStringToGo(unsafe.Pointer(C.CFISH_Class_Get_Name((*C.cfish_Class)(class))))
+		panic(fmt.Sprintf("Failed to find WRAP function for %s", className))
+	}
+	return wrapFunc(ptr)
 }
 
 type ObjIMP struct {


[7/7] lucy-clownfish git commit: Merge branch 'CLOWNFISH-56-go-composites'

Posted by ma...@apache.org.
Merge branch 'CLOWNFISH-56-go-composites'

Perform the following conversions in Go/Clownfish glue code:

*   Blob: `[]byte`
*   Vector: `[]interface{}`
*   Hash: `map[string]interface{}`

This closes #28.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/1fc8c00a
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/1fc8c00a
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/1fc8c00a

Branch: refs/heads/master
Commit: 1fc8c00a18eee056b0a008f76bcc6289c7d3920a
Parents: 570984e a665132
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Mon Aug 3 14:44:47 2015 -0700
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Mon Aug 3 14:44:47 2015 -0700

----------------------------------------------------------------------
 compiler/src/CFCGo.c                   |  29 +-
 compiler/src/CFCGoClass.c              |  23 +-
 compiler/src/CFCGoClass.h              |   3 +
 compiler/src/CFCGoFunc.c               |  79 +++--
 compiler/src/CFCGoTypeMap.c            |   9 +
 compiler/src/CFCType.c                 | 116 ++++++--
 compiler/src/CFCType.h                 |  81 +++--
 runtime/go/build.go                    |   8 +
 runtime/go/clownfish/clownfish.go      | 445 +++++++++++++++++++++++++++-
 runtime/go/clownfish/clownfish_test.go | 236 ++++++++++++++-
 10 files changed, 943 insertions(+), 86 deletions(-)
----------------------------------------------------------------------



[4/7] lucy-clownfish git commit: Map Vector to Go `[]interface{}`.

Posted by ma...@apache.org.
Map Vector to Go `[]interface{}`.

Convert Vector to slice of empty interface for both input (func
parameters) and output (return types).


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/04a57319
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/04a57319
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/04a57319

Branch: refs/heads/master
Commit: 04a573198d2467a87c293833669a00e6f5e2e16b
Parents: 55d40ac
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Thu Jul 30 16:23:20 2015 -0700
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Fri Jul 31 11:06:27 2015 -0700

----------------------------------------------------------------------
 compiler/src/CFCGoFunc.c          | 65 +++++++++++++++++-----------------
 compiler/src/CFCGoTypeMap.c       |  3 ++
 runtime/go/build.go               |  4 +++
 runtime/go/clownfish/clownfish.go |  8 +++++
 4 files changed, 48 insertions(+), 32 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/04a57319/compiler/src/CFCGoFunc.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCGoFunc.c b/compiler/src/CFCGoFunc.c
index 060e62b..64f2255 100644
--- a/compiler/src/CFCGoFunc.c
+++ b/compiler/src/CFCGoFunc.c
@@ -55,6 +55,8 @@ CFCGoFunc_go_meth_name(const char *orig) {
 static char*
 S_prep_start(CFCParcel *parcel, const char *name, CFCClass *invoker,
              CFCParamList *param_list, CFCType *return_type, int targ) {
+    const char *clownfish_dot = CFCParcel_is_cfish(parcel)
+                                ? "" : "clownfish.";
     CFCVariable **param_vars = CFCParamList_get_variables(param_list);
     char *invocant;
     char go_name[GO_NAME_BUF_SIZE];
@@ -85,22 +87,21 @@ S_prep_start(CFCParcel *parcel, const char *name, CFCClass *invoker,
 
         // Convert certain types and defer their destruction until after the
         // Clownfish call returns.
-        if (CFCType_is_string_type(type)) {
-            const char *cf_prefix = CFCParcel_is_cfish(parcel)
-                                    ? "" : "clownfish.";
-            char pattern[] =
-                "%s\t%sCF := C.cfish_Str_new_steal_utf8("
-                "C.CString(%s), C.size_t(len(%s)))\n"
-                ;
-            char *temp = CFCUtil_sprintf(pattern, converted, go_name, go_name,
-                                         go_name, go_name);
-            FREEMEM(converted);
-            converted = temp;
-            if (!CFCType_decremented(type)) {
-                converted = CFCUtil_cat(converted,
-                    "\tdefer C.cfish_dec_refcount(unsafe.Pointer(", go_name,
-                    "CF))\n", NULL);
-            }
+        char *convertible = NULL;
+        if      (CFCType_cfish_string(type))  { convertible = "String"; }
+        else if (CFCType_cfish_vector(type))  { convertible = "Vector"; }
+        else                                  { continue; }
+        char pattern[] =
+            "\t%sCF := (*C.cfish_%s)(%sGoTo%s(%s))\n";
+        char *conversion = CFCUtil_sprintf(pattern, go_name, convertible,
+                                           clownfish_dot, convertible,
+                                           go_name);
+        converted = CFCUtil_cat(converted, conversion, NULL);
+        FREEMEM(conversion);
+        if (CFCType_decremented(type)) {
+            converted = CFCUtil_cat(converted,
+                                    "\tdefer C.cfish_decref(unsafe.Pointer(",
+                                    go_name, "CF))\n", NULL);
         }
     }
 
@@ -170,8 +171,9 @@ S_prep_cfargs(CFCParcel *parcel, CFCClass *invoker,
             cfargs = CFCUtil_cat(cfargs, "C.", CFCType_get_specifier(type),
                                  "(", go_name, ")", NULL);
         }
-        else if (CFCType_is_string_type(type)
-                 // Don't convert a clownfish.String invocant.
+        else if ((CFCType_is_string_type(type)
+                  || CFCType_cfish_vector(type))
+                 // Don't convert an invocant.
                  && (targ != IS_METHOD || i != 0)
                 ) {
             cfargs = CFCUtil_cat(cfargs, go_name, "CF", NULL);
@@ -208,7 +210,12 @@ CFCGoFunc_ctor_cfargs(CFCParcel *parcel, CFCParamList *param_list) {
 char*
 CFCGoFunc_return_statement(CFCParcel *parcel, CFCType *return_type,
                            const char *cf_retval) {
+    const char *clownfish_dot = CFCParcel_is_cfish(parcel)
+                                ? "" : "clownfish.";
+    const char *maybe_decref = CFCType_incremented(return_type)
+        ? "\tdefer C.cfish_decref(unsafe.Pointer(retvalCF))\n" : "";
     char *statement = NULL;
+
     if (CFCType_is_void(return_type)) {
         return CFCUtil_strdup("");
     }
@@ -223,20 +230,14 @@ CFCGoFunc_return_statement(CFCParcel *parcel, CFCType *return_type,
             statement = CFCUtil_sprintf("\treturn %s(retvalCF)\n", ret_type_str);
         }
         else if (CFCType_is_string_type(return_type)) {
-            const char *clownfish_dot = CFCParcel_is_cfish(parcel)
-                                        ? "" : "clownfish.";
-            if (CFCType_incremented(return_type)) {
-                char pattern[] =
-                    "\tdefer C.cfish_dec_refcount(unsafe.Pointer(retvalCF))\n"
-                    "\treturn %sCFStringToGo(unsafe.Pointer(retvalCF))\n"
-                    ;
-                statement = CFCUtil_sprintf(pattern, clownfish_dot);
-            }
-            else {
-                char pattern[] =
-                    "\treturn %sCFStringToGo(unsafe.Pointer(retvalCF))\n";
-                statement = CFCUtil_sprintf(pattern, clownfish_dot);
-            }
+            char pattern[] =
+                "%s\treturn %sCFStringToGo(unsafe.Pointer(retvalCF))\n";
+            statement = CFCUtil_sprintf(pattern, maybe_decref, clownfish_dot);
+        }
+        else if (CFCType_cfish_vector(return_type)) {
+            char pattern[] =
+                "%s\treturn %sVectorToGo(unsafe.Pointer(retvalCF))\n";
+            statement = CFCUtil_sprintf(pattern, maybe_decref, clownfish_dot);
         }
         else if (CFCType_is_object(return_type)) {
             char *go_type_name = CFCGoTypeMap_go_type_name(return_type, parcel);

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/04a57319/compiler/src/CFCGoTypeMap.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCGoTypeMap.c b/compiler/src/CFCGoTypeMap.c
index 7ddbf08..15e42a7 100644
--- a/compiler/src/CFCGoTypeMap.c
+++ b/compiler/src/CFCGoTypeMap.c
@@ -96,6 +96,9 @@ CFCGoTypeMap_go_type_name(CFCType *type, CFCParcel *current_parcel) {
     if (CFCType_is_string_type(type)) {
         return CFCUtil_strdup("string");
     }
+    else if (CFCType_cfish_vector(type)) {
+        return CFCUtil_strdup("[]interface{}");
+    }
     else if (CFCType_is_object(type)) {
         // Divide the specifier into prefix and struct name.
         const char *specifier  = CFCType_get_specifier(type);

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/04a57319/runtime/go/build.go
----------------------------------------------------------------------
diff --git a/runtime/go/build.go b/runtime/go/build.go
index 186eebd..fd96fd1 100644
--- a/runtime/go/build.go
+++ b/runtime/go/build.go
@@ -148,6 +148,10 @@ func specMethods(parcel *cfc.Parcel) {
 	stringBinding.SpecMethod("Code_Point_From", "CodePointFrom(uintptr) rune")
 	stringBinding.SpecMethod("Swap_Chars", "SwapChars(rune, rune) string")
 	stringBinding.Register()
+
+	vecBinding := cfc.NewGoClass(parcel, "Clownfish::Vector")
+	vecBinding.SetSuppressCtor(true)
+	vecBinding.Register()
 }
 
 func prep() {

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/04a57319/runtime/go/clownfish/clownfish.go
----------------------------------------------------------------------
diff --git a/runtime/go/clownfish/clownfish.go b/runtime/go/clownfish/clownfish.go
index 21eb567..eefd831 100644
--- a/runtime/go/clownfish/clownfish.go
+++ b/runtime/go/clownfish/clownfish.go
@@ -130,6 +130,14 @@ func NewString(goString string) String {
 	return WRAPString(unsafe.Pointer(cfObj))
 }
 
+func NewVector(size int) Vector {
+	if (size < 0 || uint64(size) > ^uint64(0)) {
+		panic(NewErr(fmt.Sprintf("Param 'size' out of range: %d", size)))
+	}
+	cfObj := C.cfish_Vec_new(C.size_t(size))
+	return WRAPVector(unsafe.Pointer(cfObj))
+}
+
 func (o *ObjIMP) INITOBJ(ptr unsafe.Pointer) {
 	o.ref = uintptr(ptr)
 	runtime.SetFinalizer(o, ClearRef)


[3/7] lucy-clownfish git commit: Add GoToClownfish/ToGo conversion routines.

Posted by ma...@apache.org.
Add GoToClownfish/ToGo conversion routines.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/55d40ac6
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/55d40ac6
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/55d40ac6

Branch: refs/heads/master
Commit: 55d40ac67b7a076050c15e652d51f4a5dfc937a6
Parents: 56e4ab9
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Fri Jul 24 14:23:44 2015 -0700
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Fri Jul 31 11:05:42 2015 -0700

----------------------------------------------------------------------
 runtime/go/clownfish/clownfish.go      | 383 +++++++++++++++++++++++++++-
 runtime/go/clownfish/clownfish_test.go | 236 ++++++++++++++++-
 2 files changed, 611 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/55d40ac6/runtime/go/clownfish/clownfish.go
----------------------------------------------------------------------
diff --git a/runtime/go/clownfish/clownfish.go b/runtime/go/clownfish/clownfish.go
index 0fb7e32..21eb567 100644
--- a/runtime/go/clownfish/clownfish.go
+++ b/runtime/go/clownfish/clownfish.go
@@ -18,6 +18,8 @@ package clownfish
 
 /*
 
+#include <limits.h>
+
 #include "charmony.h"
 
 #include "Clownfish/Obj.h"
@@ -26,7 +28,9 @@ package clownfish
 #include "Clownfish/String.h"
 #include "Clownfish/Blob.h"
 #include "Clownfish/Hash.h"
+#include "Clownfish/HashIterator.h"
 #include "Clownfish/Vector.h"
+#include "Clownfish/Num.h"
 #include "Clownfish/Boolean.h"
 #include "Clownfish/Util/Memory.h"
 #include "Clownfish/Method.h"
@@ -63,6 +67,7 @@ import "C"
 import "runtime"
 import "unsafe"
 import "fmt"
+import "math"
 import "sync"
 
 const (
@@ -139,7 +144,315 @@ func (o *ObjIMP) TOPTR() uintptr {
 	return o.ref
 }
 
+func certifyCF(value interface{}, class *C.cfish_Class) {
+	cfObj, ok := value.(Obj)
+	if ok {
+		if C.cfish_Obj_is_a((*C.cfish_Obj)(unsafe.Pointer(cfObj.TOPTR())), class) {
+			return
+		}
+	}
+	className := StringToGo(unsafe.Pointer(C.CFISH_Class_Get_Name(class)))
+	panic(NewErr(fmt.Sprintf("Can't convert a %T to %s", value, className)))
+}
+
+// Convert a Go type into an incremented Clownfish object.  If the supplied
+// object is a Clownfish object wrapped in a Go struct, extract the Clownfish
+// object and incref it before returning its address.
+func GoToClownfish(value interface{}, class unsafe.Pointer, nullable bool) unsafe.Pointer {
+	klass := (*C.cfish_Class)(class)
+
+	// Check for nil values.
+	if value == nil {
+		if nullable {
+			return nil
+		} else if class != nil {
+			className := StringToGo(unsafe.Pointer(C.CFISH_Class_Get_Name(klass)))
+			panic(NewErr("Cannot be nil, must be a valid " + className))
+		} else {
+			panic(NewErr("Cannot be nil"))
+		}
+	}
+
+	// Default to accepting any type.
+	if klass == nil {
+		klass = C.CFISH_OBJ
+	}
+
+	// Convert the value according to its type if possible.
+	var converted unsafe.Pointer
+	switch v := value.(type) {
+	case string:
+		if klass == C.CFISH_STRING || klass == C.CFISH_OBJ {
+			converted = GoToString(value)
+		}
+	case []byte:
+		if klass == C.CFISH_BLOB || klass == C.CFISH_OBJ {
+			converted = GoToBlob(value)
+		}
+	case int:
+		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+			converted = GoToInteger(value)
+		}
+	case uint:
+		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+			converted = GoToInteger(value)
+		}
+	case uintptr:
+		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+			converted = GoToInteger(value)
+		}
+	case int64:
+		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+			converted = GoToInteger(value)
+		}
+	case int32:
+		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+			converted = GoToInteger(value)
+		}
+	case int16:
+		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+			converted = GoToInteger(value)
+		}
+	case int8:
+		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+			converted = GoToInteger(value)
+		}
+	case uint64:
+		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+			converted = GoToInteger(value)
+		}
+	case uint32:
+		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+			converted = GoToInteger(value)
+		}
+	case uint16:
+		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+			converted = GoToInteger(value)
+		}
+	case uint8:
+		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+			converted = GoToInteger(value)
+		}
+	case float32:
+		if klass == C.CFISH_FLOAT || klass == C.CFISH_OBJ {
+			converted = GoToFloat(value)
+		}
+	case float64:
+		if klass == C.CFISH_FLOAT || klass == C.CFISH_OBJ {
+			converted = GoToFloat(value)
+		}
+	case []interface{}:
+		if klass == C.CFISH_VECTOR || klass == C.CFISH_OBJ {
+			converted = GoToVector(value)
+		}
+	case map[string]interface{}:
+		if klass == C.CFISH_HASH || klass == C.CFISH_OBJ {
+			converted = GoToHash(value)
+		}
+	case Obj:
+		converted = unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+	}
+
+	// Confirm that we got what we were looking for and return.
+	if converted != nil {
+		if C.cfish_Obj_is_a((*C.cfish_Obj)(converted), klass) {
+			return unsafe.Pointer(C.cfish_incref(converted))
+		}
+	}
+
+	// Report a conversion error.
+	className := StringToGo(unsafe.Pointer(C.CFISH_Class_Get_Name(klass)))
+	panic(NewErr(fmt.Sprintf("Can't convert a %T to %s", value, className)))
+}
+
+func GoToString(value interface{}) unsafe.Pointer {
+	switch v := value.(type) {
+	case string:
+		size := len(v)
+		str := C.CString(v)
+		return unsafe.Pointer(C.cfish_Str_new_steal_utf8(str, C.size_t(size)))
+	case Obj:
+		certifyCF(v, C.CFISH_STRING)
+		return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+	default:
+		mess := fmt.Sprintf("Can't convert %T to clownfish.String", v)
+		panic(NewErr(mess))
+	}
+}
+
+func GoToBlob(value interface{}) unsafe.Pointer {
+	switch v := value.(type) {
+	case []byte:
+		size := C.size_t(len(v))
+		var buf *C.char = nil
+		if size > 0 {
+			buf = ((*C.char)(unsafe.Pointer(&v[0])))
+		}
+		return unsafe.Pointer(C.cfish_Blob_new(buf, size))
+	case Obj:
+		certifyCF(v, C.CFISH_BLOB)
+		return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+	default:
+		mess := fmt.Sprintf("Can't convert %T to clownfish.Blob", v)
+		panic(NewErr(mess))
+	}
+}
+
+func GoToInteger(value interface{}) unsafe.Pointer {
+	switch v := value.(type) {
+	case int:
+		return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+	case uint:
+		if v > math.MaxInt64 {
+			mess := fmt.Sprintf("uint value too large: %v", v)
+			panic(NewErr(mess))
+		}
+		return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+	case uintptr:
+		if v > math.MaxInt64 {
+			mess := fmt.Sprintf("uintptr value too large: %v", v)
+			panic(NewErr(mess))
+		}
+		return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+	case uint64:
+		if v > math.MaxInt64 {
+			mess := fmt.Sprintf("uint64 value too large: %v", v)
+			panic(NewErr(mess))
+		}
+		return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+	case uint32:
+		return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+	case uint16:
+		return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+	case uint8:
+		return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+	case int64:
+		return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+	case int32:
+		return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+	case int16:
+		return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+	case int8:
+		return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+	case Obj:
+		certifyCF(v, C.CFISH_INTEGER)
+		return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+	default:
+		mess := fmt.Sprintf("Can't convert %T to clownfish.Integer", v)
+		panic(NewErr(mess))
+	}
+}
+
+func GoToFloat(value interface{}) unsafe.Pointer {
+	switch v := value.(type) {
+	case float32:
+		return unsafe.Pointer(C.cfish_Float_new(C.double(v)))
+	case float64:
+		return unsafe.Pointer(C.cfish_Float_new(C.double(v)))
+	case Obj:
+		certifyCF(v, C.CFISH_FLOAT)
+		return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+	default:
+		mess := fmt.Sprintf("Can't convert %T to clownfish.Float", v)
+		panic(NewErr(mess))
+	}
+}
+
+func GoToBoolean(value interface{}) unsafe.Pointer {
+	switch v := value.(type) {
+	case bool:
+		if v {
+			return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(C.CFISH_TRUE)))
+		} else {
+			return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(C.CFISH_FALSE)))
+		}
+	case Obj:
+		certifyCF(v, C.CFISH_BOOLEAN)
+		return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+	default:
+		mess := fmt.Sprintf("Can't convert %T to clownfish.Boolean", v)
+		panic(NewErr(mess))
+	}
+}
+
+func GoToVector(value interface{}) unsafe.Pointer {
+	switch v := value.(type) {
+	case []interface{}:
+		size := len(v)
+		vec := C.cfish_Vec_new(C.size_t(size))
+		for i := 0; i < size; i++ {
+			elem := GoToClownfish(v[i], nil, true)
+			C.CFISH_Vec_Store(vec, C.size_t(i), (*C.cfish_Obj)(elem))
+		}
+		return unsafe.Pointer(vec)
+	case Obj:
+		certifyCF(v, C.CFISH_VECTOR)
+		return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+	default:
+		mess := fmt.Sprintf("Can't convert %T to clownfish.Vector", v)
+		panic(NewErr(mess))
+	}
+}
+
+func GoToHash(value interface{}) unsafe.Pointer {
+	switch v := value.(type) {
+	case map[string]interface{}:
+		size := len(v)
+		hash := C.cfish_Hash_new(C.size_t(size))
+		for key, val := range v {
+			newVal := GoToClownfish(val, nil, true)
+			keySize := len(key)
+			keyStr := C.CString(key)
+		    cfKey := C.cfish_Str_new_steal_utf8(keyStr, C.size_t(keySize))
+			defer C.cfish_dec_refcount(unsafe.Pointer(cfKey))
+			C.CFISH_Hash_Store(hash, cfKey, (*C.cfish_Obj)(newVal))
+		}
+		return unsafe.Pointer(hash)
+	case Obj:
+		certifyCF(v, C.CFISH_HASH)
+		return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+	default:
+		mess := fmt.Sprintf("Can't convert %T to clownfish.Hash", v)
+		panic(NewErr(mess))
+	}
+}
+
+func ToGo(ptr unsafe.Pointer) interface{} {
+	if ptr == nil {
+		return nil
+	}
+	class := C.cfish_Obj_get_class((*C.cfish_Obj)(ptr))
+	if class == C.CFISH_STRING {
+		return CFStringToGo(ptr)
+	} else if class == C.CFISH_BLOB {
+		return BlobToGo(ptr)
+	} else if class == C.CFISH_VECTOR {
+		return VectorToGo(ptr)
+	} else if class == C.CFISH_HASH {
+		return HashToGo(ptr)
+	} else if class == C.CFISH_BOOLEAN {
+		if ptr == unsafe.Pointer(C.CFISH_TRUE) {
+			return true
+		} else {
+			return false
+		}
+	} else if class == C.CFISH_INTEGER {
+		val := C.CFISH_Int_Get_Value((*C.cfish_Integer)(ptr))
+		return int64(val)
+	} else if class == C.CFISH_FLOAT {
+		val := C.CFISH_Float_Get_Value((*C.cfish_Float)(ptr))
+		return float64(val)
+	} else {
+		// Don't convert to a native Go type, but wrap in a Go struct.
+		return WRAPAny(ptr)
+	}
+}
+
 func CFStringToGo(ptr unsafe.Pointer) string {
+	return StringToGo(ptr)
+}
+
+func StringToGo(ptr unsafe.Pointer) string {
 	cfString := (*C.cfish_String)(ptr)
 	if cfString == nil {
 		return ""
@@ -149,13 +462,77 @@ func CFStringToGo(ptr unsafe.Pointer) string {
 		defer C.cfish_dec_refcount(unsafe.Pointer(cfString))
 	}
 	data := C.CFISH_Str_Get_Ptr8(cfString)
-	size := C.int(C.CFISH_Str_Get_Size(cfString))
-	return C.GoStringN(data, size)
+	size := C.CFISH_Str_Get_Size(cfString)
+	if size > C.size_t(C.INT_MAX) {
+		panic(fmt.Sprintf("Overflow: %d > %d", size, C.INT_MAX))
+	}
+	return C.GoStringN(data, C.int(size))
+}
+
+func BlobToGo(ptr unsafe.Pointer) []byte {
+	blob := (*C.cfish_Blob)(ptr)
+	if blob == nil {
+		return nil
+	}
+	class := C.cfish_Obj_get_class((*C.cfish_Obj)(ptr))
+	if class != C.CFISH_BLOB {
+		mess := "Not a Blob: " + StringToGo(unsafe.Pointer(C.CFISH_Class_Get_Name(class)))
+		panic(NewErr(mess))
+	}
+	data := C.CFISH_Blob_Get_Buf(blob)
+	size := C.CFISH_Blob_Get_Size(blob)
+	if size > C.size_t(C.INT_MAX) {
+		panic(fmt.Sprintf("Overflow: %d > %d", size, C.INT_MAX))
+	}
+	return C.GoBytes(unsafe.Pointer(data), C.int(size))
+}
+
+func VectorToGo(ptr unsafe.Pointer) []interface{} {
+	vec := (*C.cfish_Vector)(ptr)
+	if vec == nil {
+		return nil
+	}
+	class := C.cfish_Obj_get_class((*C.cfish_Obj)(ptr))
+	if class != C.CFISH_VECTOR {
+		mess := "Not a Vector: " + StringToGo(unsafe.Pointer(C.CFISH_Class_Get_Name(class)))
+		panic(NewErr(mess))
+	}
+	size := C.CFISH_Vec_Get_Size(vec)
+	if size > C.size_t(maxInt) {
+		panic(fmt.Sprintf("Overflow: %d > %d", size, maxInt))
+	}
+	slice := make([]interface{}, int(size))
+	for i := 0; i < int(size); i++ {
+		slice[i] = ToGo(unsafe.Pointer(C.CFISH_Vec_Fetch(vec, C.size_t(i))))
+	}
+	return slice
+}
+
+func HashToGo(ptr unsafe.Pointer) map[string]interface{} {
+	hash := (*C.cfish_Hash)(ptr)
+	if hash == nil {
+		return nil
+	}
+	class := C.cfish_Obj_get_class((*C.cfish_Obj)(ptr))
+	if class != C.CFISH_HASH {
+		mess := "Not a Hash: " + StringToGo(unsafe.Pointer(C.CFISH_Class_Get_Name(class)))
+		panic(NewErr(mess))
+	}
+	size := C.CFISH_Hash_Get_Size(hash)
+	m := make(map[string]interface{}, int(size))
+	iter := C.cfish_HashIter_new(hash)
+	defer C.cfish_dec_refcount(unsafe.Pointer(iter))
+	for C.CFISH_HashIter_Next(iter) {
+		key := C.CFISH_HashIter_Get_Key(iter)
+		val := C.CFISH_HashIter_Get_Value(iter)
+		m[StringToGo(unsafe.Pointer(key))] = ToGo(unsafe.Pointer(val))
+	}
+	return m
 }
 
 func (e *ErrIMP) Error() string {
 	mess := C.CFISH_Err_Get_Mess((*C.cfish_Err)(unsafe.Pointer(e.ref)))
-	return CFStringToGo(unsafe.Pointer(mess))
+	return StringToGo(unsafe.Pointer(mess))
 }
 
 //export GoCfish_PanicErr_internal

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/55d40ac6/runtime/go/clownfish/clownfish_test.go
----------------------------------------------------------------------
diff --git a/runtime/go/clownfish/clownfish_test.go b/runtime/go/clownfish/clownfish_test.go
index 6103f24..6b31527 100644
--- a/runtime/go/clownfish/clownfish_test.go
+++ b/runtime/go/clownfish/clownfish_test.go
@@ -18,11 +18,237 @@ package clownfish
 
 import "testing"
 import "unsafe"
+import "reflect"
+import "math"
 
-func TestStuff(t *testing.T) {
-	cfString := NewString("foo")
-	goString := CFStringToGo(unsafe.Pointer(cfString.TOPTR()))
-	if goString != "foo" {
-		t.Error("Round-tripping strings failed")
+func deepCheck(t *testing.T, got, expected interface{}) {
+	if !reflect.DeepEqual(got, expected) {
+		t.Errorf("Expected %v got %v", expected, got)
 	}
 }
+
+func TestStringToGo(t *testing.T) {
+	strings := []string{"foo", "", "z\u0000z"}
+	for _, val := range strings {
+		got := StringToGo(unsafe.Pointer(NewString(val).TOPTR()))
+		deepCheck(t, got, val)
+	}
+}
+
+func TestBlobToGo(t *testing.T) {
+	strings := []string{"foo", "", "z\u0000z"}
+	for _, str := range strings {
+		expected := []byte(str)
+		got := BlobToGo(unsafe.Pointer(NewBlob(expected).TOPTR()))
+		deepCheck(t, got, expected)
+	}
+}
+
+func TestIntegerToGo(t *testing.T) {
+	values := []int64{math.MaxInt64, math.MinInt64, 0, 1, -1}
+	for _, val := range values {
+		got := ToGo(unsafe.Pointer(NewInteger(val).TOPTR()))
+		deepCheck(t, got, val)
+	}
+}
+
+func TestFloatToGo(t *testing.T) {
+	values := []float64{math.MaxFloat64, math.SmallestNonzeroFloat64,
+		0.0, -0.0, 0.5, -0.5, math.Inf(1), math.Inf(-1)}
+	for _, val := range values {
+		got := ToGo(unsafe.Pointer(NewFloat(val).TOPTR()))
+		deepCheck(t, got, val)
+	}
+	notNum := ToGo(unsafe.Pointer(NewFloat(math.NaN()).TOPTR()))
+	if !math.IsNaN(notNum.(float64)) {
+		t.Error("Didn't convert NaN cleanly")
+	}
+}
+
+func TestBoolToGo(t *testing.T) {
+	values := []bool{true, false}
+	for _, val := range values {
+		got := ToGo(unsafe.Pointer(NewBoolean(val).TOPTR()))
+		deepCheck(t, got, val)
+	}
+}
+
+func TestVectorToGo(t *testing.T) {
+	vec := NewVector(3)
+	vec.Push(NewString("foo"))
+	vec.Push(NewInteger(42))
+	//vec.Push(nil)
+	inner := NewVector(1)
+	inner.Push(NewString("bar"))
+	vec.Push(inner)
+	expected := []interface{}{
+		"foo",
+		int64(42),
+		//nil,
+		[]interface{}{"bar"},
+	}
+	got := VectorToGo(unsafe.Pointer(vec.TOPTR()))
+	deepCheck(t, got, expected)
+}
+
+func TestHashToGo(t *testing.T) {
+	hash := NewHash(0)
+	hash.Store("str", NewString("foo"))
+	hash.Store("bool", NewBoolean(false))
+	hash.Store("float", NewFloat(-0.5))
+	hash.Store("int", NewInteger(42))
+	//hash.Store("nil", nil)
+	vec := NewVector(1)
+	vec.Push(NewString("bar"))
+	hash.Store("vector", vec)
+	got := HashToGo(unsafe.Pointer(hash.TOPTR()))
+
+	expected := map[string]interface{}{
+		"str":   "foo",
+		"bool":  false,
+		"float": -0.5,
+		"int":   int64(42),
+		//"nil": nil,
+		"vector": []interface{}{"bar"},
+	}
+	deepCheck(t, got, expected)
+}
+
+func TestNilToGo(t *testing.T) {
+	got := ToGo(unsafe.Pointer(uintptr(0)))
+	if got != nil {
+		t.Errorf("Expected nil, got %v", got)
+	}
+}
+
+func TestGoToNil(t *testing.T) {
+	if GoToClownfish(nil, nil, true) != nil {
+		t.Error("Convert nullable nil successfully")
+	}
+}
+
+func TestGoToNilNotNullable(t *testing.T) {
+	defer func() { recover() }()
+	GoToClownfish(nil, nil, false)                   // should panic
+	t.Error("Non-nullable nil should trigger error") // should be unreachable
+}
+
+func TestGoToString(t *testing.T) {
+	strings := []string{"foo", "", "z\u0000z"}
+	for _, val := range strings {
+		got := WRAPAny(GoToString(val))
+		if _, ok := got.(String); !ok {
+			t.Errorf("Not a String, but a %T", got)
+		}
+		if ToGo(unsafe.Pointer(got.TOPTR())).(string) != val {
+			t.Error("Round trip failed")
+		}
+	}
+}
+
+func TestGoToBlob(t *testing.T) {
+	strings := []string{"foo", "", "z\u0000z"}
+	for _, str := range strings {
+		val := []byte(str)
+		got := WRAPAny(GoToBlob(val))
+		if _, ok := got.(Blob); !ok {
+			t.Errorf("Not a Blob, but a %T", got)
+		}
+		if !reflect.DeepEqual(ToGo(unsafe.Pointer(got.TOPTR())), val) {
+			t.Error("Round trip failed")
+		}
+	}
+}
+
+func checkIntConv(t *testing.T, got Obj) {
+	if _, ok := got.(Integer); !ok {
+		t.Errorf("Not an Integer, but a %T", got)
+	}
+}
+
+func TestGoToInteger(t *testing.T) {
+	checkIntConv(t, WRAPAny(GoToClownfish(int(42), nil, true)))
+	checkIntConv(t, WRAPAny(GoToClownfish(uint(42), nil, true)))
+	checkIntConv(t, WRAPAny(GoToClownfish(int64(42), nil, true)))
+	checkIntConv(t, WRAPAny(GoToClownfish(int32(42), nil, true)))
+	checkIntConv(t, WRAPAny(GoToClownfish(int16(42), nil, true)))
+	checkIntConv(t, WRAPAny(GoToClownfish(int8(42), nil, true)))
+	checkIntConv(t, WRAPAny(GoToClownfish(uint64(42), nil, true)))
+	checkIntConv(t, WRAPAny(GoToClownfish(uint32(42), nil, true)))
+	checkIntConv(t, WRAPAny(GoToClownfish(uint16(42), nil, true)))
+	checkIntConv(t, WRAPAny(GoToClownfish(uint8(42), nil, true)))
+	checkIntConv(t, WRAPAny(GoToClownfish(byte(42), nil, true)))
+	checkIntConv(t, WRAPAny(GoToClownfish(rune(42), nil, true)))
+}
+
+func TestGoToIntegerRangeError(t *testing.T) {
+	defer func() { recover() }()
+	GoToClownfish(uint64(math.MaxUint64), nil, true)
+	t.Error("Truncation didn't cause error")
+}
+
+func TestGoToFloat(t *testing.T) {
+	var got Obj
+
+	values := []float64{math.MaxFloat64, math.SmallestNonzeroFloat64,
+		0.0, -0.0, 0.5, -0.5, math.Inf(1), math.Inf(-1)}
+	for _, val := range values {
+		got := WRAPAny(GoToFloat(val))
+		if _, ok := got.(Float); !ok {
+			t.Errorf("Not a Float, but a %T", got)
+		}
+		if !reflect.DeepEqual(ToGo(unsafe.Pointer(got.TOPTR())), val) {
+			t.Error("Round trip failed")
+		}
+	}
+
+	// NaN
+	got = WRAPAny(GoToFloat(math.NaN()))
+	if !math.IsNaN(ToGo(unsafe.Pointer(got.TOPTR())).(float64)) {
+		t.Error("Didn't convert NaN cleanly")
+	}
+
+	// float32
+	expected := float32(0.5)
+	got = WRAPAny(GoToClownfish(expected, nil, false))
+	deepCheck(t, got.(Float).GetValue(), float64(expected))
+}
+
+func TestGoToBoolean(t *testing.T) {
+	values := []bool{true, false}
+	for _, val := range values {
+		got := WRAPAny(GoToBoolean(val))
+		if _, ok := got.(Boolean); !ok {
+			t.Errorf("Not a Boolean, but a %T", got)
+		}
+		if !reflect.DeepEqual(ToGo(unsafe.Pointer(got.TOPTR())), val) {
+			t.Error("Round trip failed")
+		}
+	}
+}
+
+func TestGoToHash(t *testing.T) {
+	expected := map[string]interface{}{
+		"foo": int64(1),
+		"bar": []interface{}{},
+	}
+	got := WRAPAny(GoToHash(expected))
+	if _, ok := got.(Hash); !ok {
+		t.Errorf("Not a Hash, but a %T", got)
+	}
+	deepCheck(t, ToGo(unsafe.Pointer(got.TOPTR())), expected)
+}
+
+func TestGoToVector(t *testing.T) {
+	expected := []interface{}{
+		"foo",
+		"bar",
+		[]interface{}{},
+		int64(-1),
+	}
+	got := WRAPAny(GoToVector(expected))
+	if _, ok := got.(Vector); !ok {
+		t.Errorf("Not a Vector, but a %T", got)
+	}
+	deepCheck(t, ToGo(unsafe.Pointer(got.TOPTR())), expected)
+}


[5/7] lucy-clownfish git commit: Map Blob to Go `[]byte`.

Posted by ma...@apache.org.
Map Blob to Go `[]byte`.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/df024469
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/df024469
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/df024469

Branch: refs/heads/master
Commit: df0244695968125e31b2c2825cfb72ebf0c5951c
Parents: 04a5731
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Thu Jul 30 18:10:35 2015 -0700
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Fri Jul 31 11:06:32 2015 -0700

----------------------------------------------------------------------
 compiler/src/CFCGoFunc.c    | 7 +++++++
 compiler/src/CFCGoTypeMap.c | 3 +++
 2 files changed, 10 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/df024469/compiler/src/CFCGoFunc.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCGoFunc.c b/compiler/src/CFCGoFunc.c
index 64f2255..80a6e8d 100644
--- a/compiler/src/CFCGoFunc.c
+++ b/compiler/src/CFCGoFunc.c
@@ -90,6 +90,7 @@ S_prep_start(CFCParcel *parcel, const char *name, CFCClass *invoker,
         char *convertible = NULL;
         if      (CFCType_cfish_string(type))  { convertible = "String"; }
         else if (CFCType_cfish_vector(type))  { convertible = "Vector"; }
+        else if (CFCType_cfish_blob(type))    { convertible = "Blob"; }
         else                                  { continue; }
         char pattern[] =
             "\t%sCF := (*C.cfish_%s)(%sGoTo%s(%s))\n";
@@ -172,6 +173,7 @@ S_prep_cfargs(CFCParcel *parcel, CFCClass *invoker,
                                  "(", go_name, ")", NULL);
         }
         else if ((CFCType_is_string_type(type)
+                  || CFCType_cfish_blob(type)
                   || CFCType_cfish_vector(type))
                  // Don't convert an invocant.
                  && (targ != IS_METHOD || i != 0)
@@ -234,6 +236,11 @@ CFCGoFunc_return_statement(CFCParcel *parcel, CFCType *return_type,
                 "%s\treturn %sCFStringToGo(unsafe.Pointer(retvalCF))\n";
             statement = CFCUtil_sprintf(pattern, maybe_decref, clownfish_dot);
         }
+        else if (CFCType_cfish_blob(return_type)) {
+            char pattern[] =
+                "%s\treturn %sBlobToGo(unsafe.Pointer(retvalCF))\n";
+            statement = CFCUtil_sprintf(pattern, maybe_decref, clownfish_dot);
+        }
         else if (CFCType_cfish_vector(return_type)) {
             char pattern[] =
                 "%s\treturn %sVectorToGo(unsafe.Pointer(retvalCF))\n";

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/df024469/compiler/src/CFCGoTypeMap.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCGoTypeMap.c b/compiler/src/CFCGoTypeMap.c
index 15e42a7..afd5345 100644
--- a/compiler/src/CFCGoTypeMap.c
+++ b/compiler/src/CFCGoTypeMap.c
@@ -96,6 +96,9 @@ CFCGoTypeMap_go_type_name(CFCType *type, CFCParcel *current_parcel) {
     if (CFCType_is_string_type(type)) {
         return CFCUtil_strdup("string");
     }
+    else if (CFCType_cfish_blob(type)) {
+        return CFCUtil_strdup("[]byte");
+    }
     else if (CFCType_cfish_vector(type)) {
         return CFCUtil_strdup("[]interface{}");
     }


[6/7] lucy-clownfish git commit: Map Hash to `map[string]interface{}`.

Posted by ma...@apache.org.
Map Hash to `map[string]interface{}`.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/a665132d
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/a665132d
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/a665132d

Branch: refs/heads/master
Commit: a665132d860925f4de24cd0182a0021a44a6a6ed
Parents: df02446
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Thu Jul 30 18:20:06 2015 -0700
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Fri Jul 31 11:06:32 2015 -0700

----------------------------------------------------------------------
 compiler/src/CFCGoFunc.c          | 9 ++++++++-
 compiler/src/CFCGoTypeMap.c       | 3 +++
 runtime/go/build.go               | 4 ++++
 runtime/go/clownfish/clownfish.go | 8 ++++++++
 4 files changed, 23 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/a665132d/compiler/src/CFCGoFunc.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCGoFunc.c b/compiler/src/CFCGoFunc.c
index 80a6e8d..2670034 100644
--- a/compiler/src/CFCGoFunc.c
+++ b/compiler/src/CFCGoFunc.c
@@ -91,6 +91,7 @@ S_prep_start(CFCParcel *parcel, const char *name, CFCClass *invoker,
         if      (CFCType_cfish_string(type))  { convertible = "String"; }
         else if (CFCType_cfish_vector(type))  { convertible = "Vector"; }
         else if (CFCType_cfish_blob(type))    { convertible = "Blob"; }
+        else if (CFCType_cfish_hash(type))    { convertible = "Hash"; }
         else                                  { continue; }
         char pattern[] =
             "\t%sCF := (*C.cfish_%s)(%sGoTo%s(%s))\n";
@@ -174,7 +175,8 @@ S_prep_cfargs(CFCParcel *parcel, CFCClass *invoker,
         }
         else if ((CFCType_is_string_type(type)
                   || CFCType_cfish_blob(type)
-                  || CFCType_cfish_vector(type))
+                  || CFCType_cfish_vector(type)
+                  || CFCType_cfish_hash(type))
                  // Don't convert an invocant.
                  && (targ != IS_METHOD || i != 0)
                 ) {
@@ -246,6 +248,11 @@ CFCGoFunc_return_statement(CFCParcel *parcel, CFCType *return_type,
                 "%s\treturn %sVectorToGo(unsafe.Pointer(retvalCF))\n";
             statement = CFCUtil_sprintf(pattern, maybe_decref, clownfish_dot);
         }
+        else if (CFCType_cfish_hash(return_type)) {
+            char pattern[] =
+                "%s\treturn %sHashToGo(unsafe.Pointer(retvalCF))\n";
+            statement = CFCUtil_sprintf(pattern, maybe_decref, clownfish_dot);
+        }
         else if (CFCType_is_object(return_type)) {
             char *go_type_name = CFCGoTypeMap_go_type_name(return_type, parcel);
             char *struct_name  = go_type_name;

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/a665132d/compiler/src/CFCGoTypeMap.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCGoTypeMap.c b/compiler/src/CFCGoTypeMap.c
index afd5345..fbee678 100644
--- a/compiler/src/CFCGoTypeMap.c
+++ b/compiler/src/CFCGoTypeMap.c
@@ -102,6 +102,9 @@ CFCGoTypeMap_go_type_name(CFCType *type, CFCParcel *current_parcel) {
     else if (CFCType_cfish_vector(type)) {
         return CFCUtil_strdup("[]interface{}");
     }
+    else if (CFCType_cfish_hash(type)) {
+        return CFCUtil_strdup("map[string]interface{}");
+    }
     else if (CFCType_is_object(type)) {
         // Divide the specifier into prefix and struct name.
         const char *specifier  = CFCType_get_specifier(type);

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/a665132d/runtime/go/build.go
----------------------------------------------------------------------
diff --git a/runtime/go/build.go b/runtime/go/build.go
index fd96fd1..71a8c6b 100644
--- a/runtime/go/build.go
+++ b/runtime/go/build.go
@@ -152,6 +152,10 @@ func specMethods(parcel *cfc.Parcel) {
 	vecBinding := cfc.NewGoClass(parcel, "Clownfish::Vector")
 	vecBinding.SetSuppressCtor(true)
 	vecBinding.Register()
+
+	hashBinding := cfc.NewGoClass(parcel, "Clownfish::Hash")
+	hashBinding.SetSuppressCtor(true)
+	hashBinding.Register()
 }
 
 func prep() {

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/a665132d/runtime/go/clownfish/clownfish.go
----------------------------------------------------------------------
diff --git a/runtime/go/clownfish/clownfish.go b/runtime/go/clownfish/clownfish.go
index eefd831..d5e3190 100644
--- a/runtime/go/clownfish/clownfish.go
+++ b/runtime/go/clownfish/clownfish.go
@@ -138,6 +138,14 @@ func NewVector(size int) Vector {
 	return WRAPVector(unsafe.Pointer(cfObj))
 }
 
+func NewHash(size int) Hash {
+	if (size < 0 || uint64(size) > ^uint64(0)) {
+		panic(NewErr(fmt.Sprintf("Param 'size' out of range: %d", size)))
+	}
+	cfObj := C.cfish_Hash_new(C.size_t(size))
+	return WRAPHash(unsafe.Pointer(cfObj))
+}
+
 func (o *ObjIMP) INITOBJ(ptr unsafe.Pointer) {
 	o.ref = uintptr(ptr)
 	runtime.SetFinalizer(o, ClearRef)