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/09/11 23:13:57 UTC

[1/7] lucy-clownfish git commit: Don't export certain conversion helpers.

Repository: lucy-clownfish
Updated Branches:
  refs/heads/master 6f4f79e20 -> 957772687


Don't export certain conversion helpers.

Export `GoToClownfish`, but not `goToString`, `goToHash`, etc.


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

Branch: refs/heads/master
Commit: 029d9786233e56d9f3c75d70b7b8bf5cb8e67096
Parents: 6f4f79e
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Wed Aug 19 18:50:41 2015 -0700
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Fri Sep 4 14:26:57 2015 -0700

----------------------------------------------------------------------
 runtime/go/clownfish/clownfish.go      | 54 ++++++++++++++---------------
 runtime/go/clownfish/clownfish_test.go | 14 ++++----
 2 files changed, 34 insertions(+), 34 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/029d9786/runtime/go/clownfish/clownfish.go
----------------------------------------------------------------------
diff --git a/runtime/go/clownfish/clownfish.go b/runtime/go/clownfish/clownfish.go
index bae8b3d..5fd7c64 100644
--- a/runtime/go/clownfish/clownfish.go
+++ b/runtime/go/clownfish/clownfish.go
@@ -130,7 +130,7 @@ func GetClass(o Obj) Class {
 }
 
 func FetchClass(className string) Class {
-	nameCF := (*C.cfish_String)(GoToString(className))
+	nameCF := (*C.cfish_String)(goToString(className))
 	defer C.cfish_decref(unsafe.Pointer(nameCF))
 	class := C.cfish_Class_fetch_class(nameCF)
 	return WRAPClass(unsafe.Pointer(class))
@@ -155,7 +155,7 @@ func (c *ClassIMP) MakeObj() Obj {
 }
 
 func NewMethod(name string, callbackFunc unsafe.Pointer, offset uint32) Method {
-	nameCF := (*C.cfish_String)(GoToString(name))
+	nameCF := (*C.cfish_String)(goToString(name))
 	defer C.cfish_decref(unsafe.Pointer(nameCF))
 	methCF := C.cfish_Method_new(nameCF, C.cfish_method_t(callbackFunc),
 		C.uint32_t(offset));
@@ -268,75 +268,75 @@ func GoToClownfish(value interface{}, class unsafe.Pointer, nullable bool) unsaf
 	switch v := value.(type) {
 	case string:
 		if klass == C.CFISH_STRING || klass == C.CFISH_OBJ {
-			converted = GoToString(value)
+			converted = goToString(value)
 		}
 	case []byte:
 		if klass == C.CFISH_BLOB || klass == C.CFISH_OBJ {
-			converted = GoToBlob(value)
+			converted = goToBlob(value)
 		}
 	case int:
 		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
-			converted = GoToInteger(value)
+			converted = goToInteger(value)
 		}
 	case uint:
 		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
-			converted = GoToInteger(value)
+			converted = goToInteger(value)
 		}
 	case uintptr:
 		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
-			converted = GoToInteger(value)
+			converted = goToInteger(value)
 		}
 	case int64:
 		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
-			converted = GoToInteger(value)
+			converted = goToInteger(value)
 		}
 	case int32:
 		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
-			converted = GoToInteger(value)
+			converted = goToInteger(value)
 		}
 	case int16:
 		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
-			converted = GoToInteger(value)
+			converted = goToInteger(value)
 		}
 	case int8:
 		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
-			converted = GoToInteger(value)
+			converted = goToInteger(value)
 		}
 	case uint64:
 		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
-			converted = GoToInteger(value)
+			converted = goToInteger(value)
 		}
 	case uint32:
 		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
-			converted = GoToInteger(value)
+			converted = goToInteger(value)
 		}
 	case uint16:
 		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
-			converted = GoToInteger(value)
+			converted = goToInteger(value)
 		}
 	case uint8:
 		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
-			converted = GoToInteger(value)
+			converted = goToInteger(value)
 		}
 	case float32:
 		if klass == C.CFISH_FLOAT || klass == C.CFISH_OBJ {
-			converted = GoToFloat(value)
+			converted = goToFloat(value)
 		}
 	case float64:
 		if klass == C.CFISH_FLOAT || klass == C.CFISH_OBJ {
-			converted = GoToFloat(value)
+			converted = goToFloat(value)
 		}
 	case bool:
 		if klass == C.CFISH_BOOLEAN || klass == C.CFISH_OBJ {
-			converted = GoToBoolean(value)
+			converted = goToBoolean(value)
 		}
 	case []interface{}:
 		if klass == C.CFISH_VECTOR || klass == C.CFISH_OBJ {
-			converted = GoToVector(value)
+			converted = goToVector(value)
 		}
 	case map[string]interface{}:
 		if klass == C.CFISH_HASH || klass == C.CFISH_OBJ {
-			converted = GoToHash(value)
+			converted = goToHash(value)
 		}
 	case Obj:
 		converted = unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
@@ -365,7 +365,7 @@ func UnwrapClownfish(value Obj, name string, nullable bool) unsafe.Pointer {
 	return unsafe.Pointer(value.TOPTR())
 }
 
-func GoToString(value interface{}) unsafe.Pointer {
+func goToString(value interface{}) unsafe.Pointer {
 	switch v := value.(type) {
 	case string:
 		size := len(v)
@@ -380,7 +380,7 @@ func GoToString(value interface{}) unsafe.Pointer {
 	}
 }
 
-func GoToBlob(value interface{}) unsafe.Pointer {
+func goToBlob(value interface{}) unsafe.Pointer {
 	switch v := value.(type) {
 	case []byte:
 		size := C.size_t(len(v))
@@ -398,7 +398,7 @@ func GoToBlob(value interface{}) unsafe.Pointer {
 	}
 }
 
-func GoToInteger(value interface{}) unsafe.Pointer {
+func goToInteger(value interface{}) unsafe.Pointer {
 	switch v := value.(type) {
 	case int:
 		return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
@@ -443,7 +443,7 @@ func GoToInteger(value interface{}) unsafe.Pointer {
 	}
 }
 
-func GoToFloat(value interface{}) unsafe.Pointer {
+func goToFloat(value interface{}) unsafe.Pointer {
 	switch v := value.(type) {
 	case float32:
 		return unsafe.Pointer(C.cfish_Float_new(C.double(v)))
@@ -458,7 +458,7 @@ func GoToFloat(value interface{}) unsafe.Pointer {
 	}
 }
 
-func GoToBoolean(value interface{}) unsafe.Pointer {
+func goToBoolean(value interface{}) unsafe.Pointer {
 	switch v := value.(type) {
 	case bool:
 		if v {
@@ -475,7 +475,7 @@ func GoToBoolean(value interface{}) unsafe.Pointer {
 	}
 }
 
-func GoToVector(value interface{}) unsafe.Pointer {
+func goToVector(value interface{}) unsafe.Pointer {
 	switch v := value.(type) {
 	case []interface{}:
 		size := len(v)
@@ -494,7 +494,7 @@ func GoToVector(value interface{}) unsafe.Pointer {
 	}
 }
 
-func GoToHash(value interface{}) unsafe.Pointer {
+func goToHash(value interface{}) unsafe.Pointer {
 	switch v := value.(type) {
 	case map[string]interface{}:
 		size := len(v)

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/029d9786/runtime/go/clownfish/clownfish_test.go
----------------------------------------------------------------------
diff --git a/runtime/go/clownfish/clownfish_test.go b/runtime/go/clownfish/clownfish_test.go
index 6b31527..69e9660 100644
--- a/runtime/go/clownfish/clownfish_test.go
+++ b/runtime/go/clownfish/clownfish_test.go
@@ -136,7 +136,7 @@ func TestGoToNilNotNullable(t *testing.T) {
 func TestGoToString(t *testing.T) {
 	strings := []string{"foo", "", "z\u0000z"}
 	for _, val := range strings {
-		got := WRAPAny(GoToString(val))
+		got := WRAPAny(goToString(val))
 		if _, ok := got.(String); !ok {
 			t.Errorf("Not a String, but a %T", got)
 		}
@@ -150,7 +150,7 @@ func TestGoToBlob(t *testing.T) {
 	strings := []string{"foo", "", "z\u0000z"}
 	for _, str := range strings {
 		val := []byte(str)
-		got := WRAPAny(GoToBlob(val))
+		got := WRAPAny(goToBlob(val))
 		if _, ok := got.(Blob); !ok {
 			t.Errorf("Not a Blob, but a %T", got)
 		}
@@ -193,7 +193,7 @@ func TestGoToFloat(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 := WRAPAny(GoToFloat(val))
+		got := WRAPAny(goToFloat(val))
 		if _, ok := got.(Float); !ok {
 			t.Errorf("Not a Float, but a %T", got)
 		}
@@ -203,7 +203,7 @@ func TestGoToFloat(t *testing.T) {
 	}
 
 	// NaN
-	got = WRAPAny(GoToFloat(math.NaN()))
+	got = WRAPAny(goToFloat(math.NaN()))
 	if !math.IsNaN(ToGo(unsafe.Pointer(got.TOPTR())).(float64)) {
 		t.Error("Didn't convert NaN cleanly")
 	}
@@ -217,7 +217,7 @@ func TestGoToFloat(t *testing.T) {
 func TestGoToBoolean(t *testing.T) {
 	values := []bool{true, false}
 	for _, val := range values {
-		got := WRAPAny(GoToBoolean(val))
+		got := WRAPAny(goToBoolean(val))
 		if _, ok := got.(Boolean); !ok {
 			t.Errorf("Not a Boolean, but a %T", got)
 		}
@@ -232,7 +232,7 @@ func TestGoToHash(t *testing.T) {
 		"foo": int64(1),
 		"bar": []interface{}{},
 	}
-	got := WRAPAny(GoToHash(expected))
+	got := WRAPAny(goToHash(expected))
 	if _, ok := got.(Hash); !ok {
 		t.Errorf("Not a Hash, but a %T", got)
 	}
@@ -246,7 +246,7 @@ func TestGoToVector(t *testing.T) {
 		[]interface{}{},
 		int64(-1),
 	}
-	got := WRAPAny(GoToVector(expected))
+	got := WRAPAny(goToVector(expected))
 	if _, ok := got.(Vector); !ok {
 		t.Errorf("Not a Vector, but a %T", got)
 	}


[6/7] lucy-clownfish git commit: Fix refcounting error in Go arg conversion.

Posted by ma...@apache.org.
Fix refcounting error in Go arg conversion.

Fix reversed logic for applying decrefs.

*   If the arg is decremented but only unwrapped, add a decref.
*   If the arg is not decremented but is converted using GoToClownfish
    (which returns an incremented value), add a decref.

Then, remove the spurious extra incref in GoToClownfish (a memory leak
in many cases) which had balanced out the extra decref.


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

Branch: refs/heads/master
Commit: b0049db2a1c0c4c8046855bd18075961f33b0f13
Parents: bd9f043
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Fri Aug 28 19:25:24 2015 -0700
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Sep 8 19:09:25 2015 -0700

----------------------------------------------------------------------
 compiler/src/CFCGoFunc.c          |  2 +-
 runtime/go/clownfish/clownfish.go | 27 ++++++++-------------------
 2 files changed, 9 insertions(+), 20 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/b0049db2/compiler/src/CFCGoFunc.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCGoFunc.c b/compiler/src/CFCGoFunc.c
index 0528c11..1d94f04 100644
--- a/compiler/src/CFCGoFunc.c
+++ b/compiler/src/CFCGoFunc.c
@@ -170,7 +170,7 @@ S_prep_start(CFCParcel *parcel, const char *name, CFCClass *invoker,
                                            nullable ? "true" : "false");
         converted = CFCUtil_cat(converted, conversion, NULL);
         FREEMEM(conversion);
-        if (CFCType_decremented(type)) {
+        if (!CFCType_decremented(type)) {
             converted = CFCUtil_cat(converted,
                                     "\tdefer C.cfish_decref(unsafe.Pointer(",
                                     go_name, "CF))\n", NULL);

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/b0049db2/runtime/go/clownfish/clownfish.go
----------------------------------------------------------------------
diff --git a/runtime/go/clownfish/clownfish.go b/runtime/go/clownfish/clownfish.go
index 140f1f2..2cb012f 100644
--- a/runtime/go/clownfish/clownfish.go
+++ b/runtime/go/clownfish/clownfish.go
@@ -260,55 +260,44 @@ func GoToClownfish(value interface{}, class unsafe.Pointer, nullable bool) unsaf
 	}
 
 	// 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, nullable)
+			return goToString(value, nullable)
 		}
 	case []byte:
 		if klass == C.CFISH_BLOB || klass == C.CFISH_OBJ {
-			converted = goToBlob(value, nullable)
+			return goToBlob(value, nullable)
 		}
 	case int, uint, uintptr, int64, int32, int16, int8, uint64, uint32, uint16, uint8:
 		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
-			converted = goToInteger(value, nullable)
+			return goToInteger(value, nullable)
 		}
 	case float32, float64:
 		if klass == C.CFISH_FLOAT || klass == C.CFISH_OBJ {
-			converted = goToFloat(value, nullable)
+			return goToFloat(value, nullable)
 		}
 	case bool:
 		if klass == C.CFISH_BOOLEAN || klass == C.CFISH_OBJ {
-			converted = goToBoolean(value, nullable)
+			return goToBoolean(value, nullable)
 		}
 	case []interface{}:
 		if klass == C.CFISH_VECTOR || klass == C.CFISH_OBJ {
-			converted = goToVector(value, nullable)
+			return goToVector(value, nullable)
 		}
 	case map[string]interface{}:
 		if klass == C.CFISH_HASH || klass == C.CFISH_OBJ {
-			converted = goToHash(value, nullable)
+			return goToHash(value, nullable)
 		}
 	case Obj:
 		certifyCF(value, klass, nullable)
-		converted = unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+		return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
 	case nil:
 		if nullable {
 			return nil
 		}
 	}
 
-	if converted == nil {
-		if nullable {
-			return nil
-		}
-	} else {
-		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)))


[7/7] lucy-clownfish git commit: Merge branch 'CLOWNFISH-59-nil-check'

Posted by ma...@apache.org.
Merge branch 'CLOWNFISH-59-nil-check'

Nil checking Go interface types can be subtle:

* http://play.golang.org/p/-pjThjJfxr
* http://golang.org/doc/faq#nil_error

The branch corrects nil-checking mistakes and continues tightening up
the Go/Clownfish glue code.

This fixes #37.


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

Branch: refs/heads/master
Commit: 95777268700e055caa5f1f1c9f9ca01a82b75975
Parents: 6f4f79e b0049db
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Fri Sep 11 14:09:34 2015 -0700
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Fri Sep 11 14:11:25 2015 -0700

----------------------------------------------------------------------
 compiler/src/CFCGoFunc.c               |  36 +++--
 runtime/go/clownfish/clownfish.go      | 212 +++++++++++-----------------
 runtime/go/clownfish/clownfish_test.go |  14 +-
 runtime/go/clownfish/err_test.go       |   6 +-
 runtime/go/clownfish/hash_test.go      |  14 +-
 5 files changed, 121 insertions(+), 161 deletions(-)
----------------------------------------------------------------------



[2/7] lucy-clownfish git commit: Compress duplicate cases in type switch.

Posted by ma...@apache.org.
Compress duplicate cases in type switch.


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

Branch: refs/heads/master
Commit: 699a22b58c26729eb8bcea2c8c2d625fc8f9ba0c
Parents: 029d978
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Wed Aug 19 19:10:00 2015 -0700
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Fri Sep 4 14:26:58 2015 -0700

----------------------------------------------------------------------
 runtime/go/clownfish/clownfish.go | 48 ++--------------------------------
 1 file changed, 2 insertions(+), 46 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/699a22b5/runtime/go/clownfish/clownfish.go
----------------------------------------------------------------------
diff --git a/runtime/go/clownfish/clownfish.go b/runtime/go/clownfish/clownfish.go
index 5fd7c64..b5a9401 100644
--- a/runtime/go/clownfish/clownfish.go
+++ b/runtime/go/clownfish/clownfish.go
@@ -274,55 +274,11 @@ func GoToClownfish(value interface{}, class unsafe.Pointer, nullable bool) unsaf
 		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:
+	case int, uint, uintptr, int64, int32, int16, int8, uint64, uint32, uint16, uint8:
 		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:
+	case float32, float64:
 		if klass == C.CFISH_FLOAT || klass == C.CFISH_OBJ {
 			converted = goToFloat(value)
 		}


[5/7] lucy-clownfish git commit: Perform nil-checking on concrete types.

Posted by ma...@apache.org.
Perform nil-checking on concrete types.

Nil-checking on interface types only returns true if both the type and
value slots in the interface tuple are 0.  Therefore, it is necessary to
push nil-checking down into code which knows the concrete type.


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

Branch: refs/heads/master
Commit: bd9f043b3a4ba006cc407b4a414827a2879e0b1f
Parents: 180d963
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Fri Aug 28 18:49:34 2015 -0700
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Sep 8 19:09:25 2015 -0700

----------------------------------------------------------------------
 runtime/go/clownfish/clownfish.go      | 120 ++++++++++++++--------------
 runtime/go/clownfish/clownfish_test.go |  14 ++--
 2 files changed, 65 insertions(+), 69 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/bd9f043b/runtime/go/clownfish/clownfish.go
----------------------------------------------------------------------
diff --git a/runtime/go/clownfish/clownfish.go b/runtime/go/clownfish/clownfish.go
index b31ed5f..140f1f2 100644
--- a/runtime/go/clownfish/clownfish.go
+++ b/runtime/go/clownfish/clownfish.go
@@ -131,7 +131,7 @@ func GetClass(o Obj) Class {
 }
 
 func FetchClass(className string) Class {
-	nameCF := (*C.cfish_String)(goToString(className))
+	nameCF := (*C.cfish_String)(goToString(className, false))
 	defer C.cfish_decref(unsafe.Pointer(nameCF))
 	class := C.cfish_Class_fetch_class(nameCF)
 	return WRAPClass(unsafe.Pointer(class))
@@ -156,7 +156,7 @@ func (c *ClassIMP) MakeObj() Obj {
 }
 
 func NewMethod(name string, callbackFunc unsafe.Pointer, offset uint32) Method {
-	nameCF := (*C.cfish_String)(goToString(name))
+	nameCF := (*C.cfish_String)(goToString(name, false))
 	defer C.cfish_decref(unsafe.Pointer(nameCF))
 	methCF := C.cfish_Method_new(nameCF, C.cfish_method_t(callbackFunc),
 		C.uint32_t(offset));
@@ -230,10 +230,17 @@ func (o *ObjIMP)Clone() Obj {
 	return WRAPAny(unsafe.Pointer(dupe)).(Obj)
 }
 
-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) {
+func certifyCF(value interface{}, class *C.cfish_Class, nullable bool) {
+	if nullable && value == nil {
+		return
+	}
+	if cfObj, ok := value.(Obj); ok {
+		o := (*C.cfish_Obj)(unsafe.Pointer(cfObj.TOPTR()))
+		if o == nil {
+			if nullable {
+				return
+			}
+		} else if C.cfish_Obj_is_a(o, class) {
 			return
 		}
 	}
@@ -247,18 +254,6 @@ func certifyCF(value interface{}, class *C.cfish_Class) {
 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
@@ -269,38 +264,46 @@ func GoToClownfish(value interface{}, class unsafe.Pointer, nullable bool) unsaf
 	switch v := value.(type) {
 	case string:
 		if klass == C.CFISH_STRING || klass == C.CFISH_OBJ {
-			converted = goToString(value)
+			converted = goToString(value, nullable)
 		}
 	case []byte:
 		if klass == C.CFISH_BLOB || klass == C.CFISH_OBJ {
-			converted = goToBlob(value)
+			converted = goToBlob(value, nullable)
 		}
 	case int, uint, uintptr, int64, int32, int16, int8, uint64, uint32, uint16, uint8:
 		if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
-			converted = goToInteger(value)
+			converted = goToInteger(value, nullable)
 		}
 	case float32, float64:
 		if klass == C.CFISH_FLOAT || klass == C.CFISH_OBJ {
-			converted = goToFloat(value)
+			converted = goToFloat(value, nullable)
 		}
 	case bool:
 		if klass == C.CFISH_BOOLEAN || klass == C.CFISH_OBJ {
-			converted = goToBoolean(value)
+			converted = goToBoolean(value, nullable)
 		}
 	case []interface{}:
 		if klass == C.CFISH_VECTOR || klass == C.CFISH_OBJ {
-			converted = goToVector(value)
+			converted = goToVector(value, nullable)
 		}
 	case map[string]interface{}:
 		if klass == C.CFISH_HASH || klass == C.CFISH_OBJ {
-			converted = goToHash(value)
+			converted = goToHash(value, nullable)
 		}
 	case Obj:
+		certifyCF(value, klass, nullable)
 		converted = unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+	case nil:
+		if nullable {
+			return nil
+		}
 	}
 
-	// Confirm that we got what we were looking for and return.
-	if converted != nil {
+	if converted == nil {
+		if nullable {
+			return nil
+		}
+	} else {
 		if C.cfish_Obj_is_a((*C.cfish_Obj)(converted), klass) {
 			return unsafe.Pointer(C.cfish_incref(converted))
 		}
@@ -330,22 +333,21 @@ func Unwrap(value Obj, name string) unsafe.Pointer {
 	return ptr
 }
 
-func goToString(value interface{}) unsafe.Pointer {
+func goToString(value interface{}, nullable bool) 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)
+		certifyCF(v, C.CFISH_STRING, nullable)
 		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))
 	}
+	mess := fmt.Sprintf("Can't convert %T to clownfish.String", value)
+	panic(NewErr(mess))
 }
 
-func goToBlob(value interface{}) unsafe.Pointer {
+func goToBlob(value interface{}, nullable bool) unsafe.Pointer {
 	switch v := value.(type) {
 	case []byte:
 		size := C.size_t(len(v))
@@ -355,15 +357,14 @@ func goToBlob(value interface{}) unsafe.Pointer {
 		}
 		return unsafe.Pointer(C.cfish_Blob_new(buf, size))
 	case Obj:
-		certifyCF(v, C.CFISH_BLOB)
+		certifyCF(v, C.CFISH_BLOB, nullable)
 		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))
 	}
+	mess := fmt.Sprintf("Can't convert %T to clownfish.Blob", value)
+	panic(NewErr(mess))
 }
 
-func goToInteger(value interface{}) unsafe.Pointer {
+func goToInteger(value interface{}, nullable bool) unsafe.Pointer {
 	switch v := value.(type) {
 	case int:
 		return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
@@ -400,30 +401,28 @@ func goToInteger(value interface{}) unsafe.Pointer {
 	case int8:
 		return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
 	case Obj:
-		certifyCF(v, C.CFISH_INTEGER)
+		certifyCF(v, C.CFISH_INTEGER, nullable)
 		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))
 	}
+	mess := fmt.Sprintf("Can't convert %T to clownfish.Integer", value)
+	panic(NewErr(mess))
 }
 
-func goToFloat(value interface{}) unsafe.Pointer {
+func goToFloat(value interface{}, nullable bool) 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)
+		certifyCF(v, C.CFISH_FLOAT, nullable)
 		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))
 	}
+	mess := fmt.Sprintf("Can't convert %T to clownfish.Float", value)
+	panic(NewErr(mess))
 }
 
-func goToBoolean(value interface{}) unsafe.Pointer {
+func goToBoolean(value interface{}, nullable bool) unsafe.Pointer {
 	switch v := value.(type) {
 	case bool:
 		if v {
@@ -432,15 +431,14 @@ func goToBoolean(value interface{}) unsafe.Pointer {
 			return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(C.CFISH_FALSE)))
 		}
 	case Obj:
-		certifyCF(v, C.CFISH_BOOLEAN)
+		certifyCF(v, C.CFISH_BOOLEAN, nullable)
 		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))
 	}
+	mess := fmt.Sprintf("Can't convert %T to clownfish.Boolean", value)
+	panic(NewErr(mess))
 }
 
-func goToVector(value interface{}) unsafe.Pointer {
+func goToVector(value interface{}, nullable bool) unsafe.Pointer {
 	switch v := value.(type) {
 	case []interface{}:
 		size := len(v)
@@ -451,15 +449,14 @@ func goToVector(value interface{}) unsafe.Pointer {
 		}
 		return unsafe.Pointer(vec)
 	case Obj:
-		certifyCF(v, C.CFISH_VECTOR)
+		certifyCF(v, C.CFISH_VECTOR, nullable)
 		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))
 	}
+	mess := fmt.Sprintf("Can't convert %T to clownfish.Vector", value)
+	panic(NewErr(mess))
 }
 
-func goToHash(value interface{}) unsafe.Pointer {
+func goToHash(value interface{}, nullable bool) unsafe.Pointer {
 	switch v := value.(type) {
 	case map[string]interface{}:
 		size := len(v)
@@ -474,12 +471,11 @@ func goToHash(value interface{}) unsafe.Pointer {
 		}
 		return unsafe.Pointer(hash)
 	case Obj:
-		certifyCF(v, C.CFISH_HASH)
+		certifyCF(v, C.CFISH_HASH, nullable)
 		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))
 	}
+	mess := fmt.Sprintf("Can't convert %T to clownfish.Hash", value)
+	panic(NewErr(mess))
 }
 
 func ToGo(ptr unsafe.Pointer) interface{} {

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/bd9f043b/runtime/go/clownfish/clownfish_test.go
----------------------------------------------------------------------
diff --git a/runtime/go/clownfish/clownfish_test.go b/runtime/go/clownfish/clownfish_test.go
index 69e9660..074b58b 100644
--- a/runtime/go/clownfish/clownfish_test.go
+++ b/runtime/go/clownfish/clownfish_test.go
@@ -136,7 +136,7 @@ func TestGoToNilNotNullable(t *testing.T) {
 func TestGoToString(t *testing.T) {
 	strings := []string{"foo", "", "z\u0000z"}
 	for _, val := range strings {
-		got := WRAPAny(goToString(val))
+		got := WRAPAny(goToString(val, false))
 		if _, ok := got.(String); !ok {
 			t.Errorf("Not a String, but a %T", got)
 		}
@@ -150,7 +150,7 @@ func TestGoToBlob(t *testing.T) {
 	strings := []string{"foo", "", "z\u0000z"}
 	for _, str := range strings {
 		val := []byte(str)
-		got := WRAPAny(goToBlob(val))
+		got := WRAPAny(goToBlob(val, false))
 		if _, ok := got.(Blob); !ok {
 			t.Errorf("Not a Blob, but a %T", got)
 		}
@@ -193,7 +193,7 @@ func TestGoToFloat(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 := WRAPAny(goToFloat(val))
+		got := WRAPAny(goToFloat(val, false))
 		if _, ok := got.(Float); !ok {
 			t.Errorf("Not a Float, but a %T", got)
 		}
@@ -203,7 +203,7 @@ func TestGoToFloat(t *testing.T) {
 	}
 
 	// NaN
-	got = WRAPAny(goToFloat(math.NaN()))
+	got = WRAPAny(goToFloat(math.NaN(), false))
 	if !math.IsNaN(ToGo(unsafe.Pointer(got.TOPTR())).(float64)) {
 		t.Error("Didn't convert NaN cleanly")
 	}
@@ -217,7 +217,7 @@ func TestGoToFloat(t *testing.T) {
 func TestGoToBoolean(t *testing.T) {
 	values := []bool{true, false}
 	for _, val := range values {
-		got := WRAPAny(goToBoolean(val))
+		got := WRAPAny(goToBoolean(val, false))
 		if _, ok := got.(Boolean); !ok {
 			t.Errorf("Not a Boolean, but a %T", got)
 		}
@@ -232,7 +232,7 @@ func TestGoToHash(t *testing.T) {
 		"foo": int64(1),
 		"bar": []interface{}{},
 	}
-	got := WRAPAny(goToHash(expected))
+	got := WRAPAny(goToHash(expected, false))
 	if _, ok := got.(Hash); !ok {
 		t.Errorf("Not a Hash, but a %T", got)
 	}
@@ -246,7 +246,7 @@ func TestGoToVector(t *testing.T) {
 		[]interface{}{},
 		int64(-1),
 	}
-	got := WRAPAny(goToVector(expected))
+	got := WRAPAny(goToVector(expected, false))
 	if _, ok := got.(Vector); !ok {
 		t.Errorf("Not a Vector, but a %T", got)
 	}


[3/7] lucy-clownfish git commit: Improve quoting in test messages.

Posted by ma...@apache.org.
Improve quoting in test messages.


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

Branch: refs/heads/master
Commit: 180d963b64cba359cb55b44dbe49613e4dc2a76d
Parents: 7988b5f
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Fri Aug 28 18:11:13 2015 -0700
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Sep 8 19:09:24 2015 -0700

----------------------------------------------------------------------
 runtime/go/clownfish/err_test.go  |  6 +++---
 runtime/go/clownfish/hash_test.go | 14 +++++++-------
 2 files changed, 10 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/180d963b/runtime/go/clownfish/err_test.go
----------------------------------------------------------------------
diff --git a/runtime/go/clownfish/err_test.go b/runtime/go/clownfish/err_test.go
index a7abff5..b439ab7 100644
--- a/runtime/go/clownfish/err_test.go
+++ b/runtime/go/clownfish/err_test.go
@@ -44,7 +44,7 @@ func TestErrGetMess(t *testing.T) {
 	err := NewErr("foo")
 	expected := "foo"
 	if got := err.GetMess(); got != expected {
-		t.Errorf("Expected %v, got %v", expected, got)
+		t.Errorf("Expected '%v', got '%v'", expected, got)
 	}
 }
 
@@ -53,7 +53,7 @@ func TestErrCatMess(t *testing.T) {
 	err.CatMess("bar")
 	expected := "foobar"
 	if got := err.GetMess(); got != expected {
-		t.Errorf("Expected %v, got %v", expected, got)
+		t.Errorf("Expected '%v', got '%v'", expected, got)
 	}
 }
 
@@ -61,6 +61,6 @@ func TestErrToString(t *testing.T) {
 	err := NewErr("foo")
 	expected := "foo"
 	if got := err.ToString(); got != expected {
-		t.Errorf("Expected %v, got %v", expected, got)
+		t.Errorf("Expected '%v', got '%v'", expected, got)
 	}
 }

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/180d963b/runtime/go/clownfish/hash_test.go
----------------------------------------------------------------------
diff --git a/runtime/go/clownfish/hash_test.go b/runtime/go/clownfish/hash_test.go
index c2970e0..26acf49 100644
--- a/runtime/go/clownfish/hash_test.go
+++ b/runtime/go/clownfish/hash_test.go
@@ -24,11 +24,11 @@ func TestHashStoreFetch(t *testing.T) {
 	hash := NewHash(0)
 	hash.Store("foo", "bar")
 	if got, ok := hash.Fetch("foo").(string); !ok || got != "bar" {
-		t.Errorf("Expected \"bar\", got %v", got)
+		t.Errorf("Expected 'bar', got '%v'", got)
 	}
 	hash.Store("nada", nil)
 	if got := hash.Fetch("nada"); got != nil {
-		t.Errorf("Expected nil, got %v", got)
+		t.Errorf("Expected nil, got '%v'", got)
 	}
 }
 
@@ -40,7 +40,7 @@ func TestHashDelete(t *testing.T) {
 		t.Errorf("Delete failed (size %d)", size)
 	}
 	if val, ok := got.(string); !ok || val != "bar" {
-		t.Errorf("Delete returned unexpected value: %v")
+		t.Errorf("Delete returned unexpected value: '%v'", val)
 	}
 }
 
@@ -76,7 +76,7 @@ func TestHashKeys(t *testing.T) {
 	sort.Strings(keys)
 	expected := []string{"a", "b"}
 	if !reflect.DeepEqual(keys, expected) {
-		t.Errorf("Expected %v, got %v", expected, keys)
+		t.Errorf("Expected '%v', got '%v'", expected, keys)
 	}
 }
 
@@ -92,7 +92,7 @@ func TestHashValues(t *testing.T) {
 	sort.Strings(vals)
 	expected := []string{"a", "b"}
 	if !reflect.DeepEqual(vals, expected) {
-		t.Errorf("Expected %v, got %v", expected, vals)
+		t.Errorf("Expected '%v', got '%v'", expected, vals)
 	}
 }
 
@@ -146,10 +146,10 @@ func TestHashIterator(t *testing.T) {
 		t.Error("Next() should proceed")
 	}
 	if key := iter.GetKey(); key != "a" {
-		t.Error("Expected \"a\", got %v", key)
+		t.Error("Expected 'a', got '%v'", key)
 	}
 	if val, ok := iter.GetValue().(string); !ok || val != "foo" {
-		t.Error("Expected \"a\", got %v", val)
+		t.Error("Expected 'a', got '%v'", val)
 	}
 	if iter.Next() {
 		t.Error("Next() should return false when iteration complete")


[4/7] lucy-clownfish git commit: Introduce `Unwrap`, `UnwrapNullable`.

Posted by ma...@apache.org.
Introduce `Unwrap`, `UnwrapNullable`.

Replace `UnwrapClownfish` with new functions that perform valid
nil-checking.  For nullable parameters, perform better error checking in
Go in order to throw better errors.


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

Branch: refs/heads/master
Commit: 7988b5ffd869afe613ab7becc3805a219b2f5c2f
Parents: 699a22b
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Thu Aug 27 19:39:19 2015 -0700
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Sep 8 19:09:24 2015 -0700

----------------------------------------------------------------------
 compiler/src/CFCGoFunc.c          | 34 +++++++++++++++++----------
 runtime/go/clownfish/clownfish.go | 43 ++++++++++++++++++++--------------
 2 files changed, 48 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/7988b5ff/compiler/src/CFCGoFunc.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCGoFunc.c b/compiler/src/CFCGoFunc.c
index 34d3d48..0528c11 100644
--- a/compiler/src/CFCGoFunc.c
+++ b/compiler/src/CFCGoFunc.c
@@ -109,11 +109,11 @@ S_prep_start(CFCParcel *parcel, const char *name, CFCClass *invoker,
         // be nullable if it has a default value of "NULL".  (Since Go does
         // not support default values for method parameters, this is the only
         // default value we care about.)
-        const char *nullable = CFCType_nullable(type) ? "true" : "false";
+        int nullable = CFCType_nullable(type);
         if (default_values[i] != NULL
             && strcmp(default_values[i], "NULL") == 0
            ) {
-            nullable = "true";
+            nullable = true;
         }
 
         const char *class_var = NULL;
@@ -136,20 +136,29 @@ S_prep_start(CFCParcel *parcel, const char *name, CFCClass *invoker,
 
         if (class_var == NULL || (targ == IS_METHOD && i == 0)) {
             // Just unwrap -- don't convert.
-            char *pattern;
-            if (CFCType_decremented(type)) {
-                pattern =
-                    "\t%sCF := (*C.%s)(unsafe.Pointer(C.cfish_incref(%sUnwrapClownfish(%s, \"%s\", %s))))\n";
+            char *unwrapped;
+            if (nullable) {
+                unwrapped = CFCUtil_sprintf("%sUnwrapNullable(%s)",
+                                            clownfish_dot, go_name);
             }
             else {
-                pattern =
-                    "\t%sCF := (*C.%s)(%sUnwrapClownfish(%s, \"%s\", %s))\n";
+                unwrapped = CFCUtil_sprintf("%sUnwrap(%s, \"%s\")",
+                                            clownfish_dot, go_name, go_name);
+            }
+
+            if (CFCType_decremented(type)) {
+                char *pattern = "unsafe.Pointer(C.cfish_incref(%s))";
+                char *temp = CFCUtil_sprintf(pattern, unwrapped);
+                FREEMEM(unwrapped);
+                unwrapped = temp;
             }
-            char *conversion = CFCUtil_sprintf(pattern, go_name, struct_name,
-                                               clownfish_dot, go_name,
-                                               go_name, nullable);
+
+            char *conversion
+                = CFCUtil_sprintf("\t%sCF := (*C.%s)(%s)\n", go_name,
+                                  struct_name, unwrapped);
             converted = CFCUtil_cat(converted, conversion, NULL);
             FREEMEM(conversion);
+            FREEMEM(unwrapped);
             continue;
         }
 
@@ -157,7 +166,8 @@ S_prep_start(CFCParcel *parcel, const char *name, CFCClass *invoker,
             "\t%sCF := (*C.%s)(%sGoToClownfish(%s, unsafe.Pointer(C.%s), %s))\n";
         char *conversion = CFCUtil_sprintf(pattern, go_name, struct_name,
                                            clownfish_dot, go_name,
-                                           class_var, nullable);
+                                           class_var,
+                                           nullable ? "true" : "false");
         converted = CFCUtil_cat(converted, conversion, NULL);
         FREEMEM(conversion);
         if (CFCType_decremented(type)) {

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/7988b5ff/runtime/go/clownfish/clownfish.go
----------------------------------------------------------------------
diff --git a/runtime/go/clownfish/clownfish.go b/runtime/go/clownfish/clownfish.go
index b5a9401..b31ed5f 100644
--- a/runtime/go/clownfish/clownfish.go
+++ b/runtime/go/clownfish/clownfish.go
@@ -66,6 +66,7 @@ GoCfish_RunRoutine(CFISH_Err_Attempt_t routine, void *context) {
 import "C"
 import "runtime"
 import "unsafe"
+import "reflect"
 import "fmt"
 import "math"
 import "sync"
@@ -124,7 +125,7 @@ type ObjIMP struct {
 }
 
 func GetClass(o Obj) Class {
-	objCF := (*C.cfish_Obj)(unsafe.Pointer(o.TOPTR()))
+	objCF := (*C.cfish_Obj)(Unwrap(o, "o"))
 	classCF := C.cfish_Obj_get_class(objCF)
 	return WRAPClass(unsafe.Pointer(classCF))
 }
@@ -137,7 +138,7 @@ func FetchClass(className string) Class {
 }
 
 func (c *ClassIMP) GetMethods() []Method {
-	self := (*C.cfish_Class)(unsafe.Pointer(c.TOPTR()))
+	self := (*C.cfish_Class)(Unwrap(c, "c"))
 	methsVec := C.CFISH_Class_Get_Methods(self)
 	size := C.CFISH_Vec_Get_Size(methsVec)
 	meths := make([]Method, 0, int(size))
@@ -149,7 +150,7 @@ func (c *ClassIMP) GetMethods() []Method {
 }
 
 func (c *ClassIMP) MakeObj() Obj {
-	self := (*C.cfish_Class)(unsafe.Pointer(c.TOPTR()))
+	self := (*C.cfish_Class)(Unwrap(c, "c"))
 	retvalCF := C.CFISH_Class_Make_Obj_IMP(self)
 	return WRAPAny(unsafe.Pointer(retvalCF))
 }
@@ -170,7 +171,7 @@ func NewString(goString string) String {
 }
 
 func NewStringIterator(str String, offset uintptr) StringIterator {
-	strCF := (*C.cfish_String)(unsafe.Pointer(str.TOPTR()))
+	strCF := (*C.cfish_String)(Unwrap(str, "str"))
 	iter := C.cfish_StrIter_new(strCF, C.size_t(offset))
 	return WRAPStringIterator(unsafe.Pointer(iter))
 }
@@ -192,13 +193,13 @@ func NewHash(size int) Hash {
 }
 
 func NewHashIterator(hash Hash) HashIterator {
-	hashCF := (*C.cfish_Hash)(unsafe.Pointer(hash.TOPTR()))
+	hashCF := (*C.cfish_Hash)(Unwrap(hash, "hash"))
 	cfObj := C.cfish_HashIter_new(hashCF)
 	return WRAPHashIterator(unsafe.Pointer(cfObj))
 }
 
 func (h *HashIMP) Keys() []string {
-	self := (*C.cfish_Hash)(unsafe.Pointer(h.TOPTR()))
+	self := (*C.cfish_Hash)(Unwrap(h, "h"))
 	keysCF := C.CFISH_Hash_Keys(self)
 	numKeys := C.CFISH_Vec_Get_Size(keysCF)
 	keys := make([]string, 0, int(numKeys))
@@ -224,7 +225,7 @@ func (o *ObjIMP) TOPTR() uintptr {
 }
 
 func (o *ObjIMP)Clone() Obj {
-	self := (*C.cfish_Obj)(unsafe.Pointer(o.TOPTR()))
+	self := (*C.cfish_Obj)(Unwrap(o, "o"))
 	dupe := C.CFISH_Obj_Clone(self)
 	return WRAPAny(unsafe.Pointer(dupe)).(Obj)
 }
@@ -310,17 +311,25 @@ func GoToClownfish(value interface{}, class unsafe.Pointer, nullable bool) unsaf
 	panic(NewErr(fmt.Sprintf("Can't convert a %T to %s", value, className)))
 }
 
-func UnwrapClownfish(value Obj, name string, nullable bool) unsafe.Pointer {
+func UnwrapNullable(value Obj) unsafe.Pointer {
 	if value == nil {
-		if nullable {
-			return nil
-		} else {
-			panic(NewErr(fmt.Sprintf("%s cannot be nil", name)))
-		}
+		return nil
+	}
+	val := reflect.ValueOf(value)
+	if val.IsNil() {
+		return nil
 	}
 	return unsafe.Pointer(value.TOPTR())
 }
 
+func Unwrap(value Obj, name string) unsafe.Pointer {
+	ptr := UnwrapNullable(value)
+	if ptr == nil {
+		panic(NewErr(fmt.Sprintf("%s cannot be nil", name)))
+	}
+	return ptr
+}
+
 func goToString(value interface{}) unsafe.Pointer {
 	switch v := value.(type) {
 	case string:
@@ -628,19 +637,19 @@ func TrapErr(routine func()) (trapped error) {
 }
 
 func (s *StringIMP) CodePointAt(tick uintptr) rune {
-	self := ((*C.cfish_String)(unsafe.Pointer(s.TOPTR())))
+	self := ((*C.cfish_String)(Unwrap(s, "s")))
 	retvalCF := C.CFISH_Str_Code_Point_At(self, C.size_t(tick))
 	return rune(retvalCF)
 }
 
 func (s *StringIMP) CodePointFrom(tick uintptr) rune {
-	self := ((*C.cfish_String)(unsafe.Pointer(s.TOPTR())))
+	self := ((*C.cfish_String)(Unwrap(s, "s")))
 	retvalCF := C.CFISH_Str_Code_Point_From(self, C.size_t(tick))
 	return rune(retvalCF)
 }
 
 func (s *StringIMP) SwapChars(match, replacement rune) string {
-	self := ((*C.cfish_String)(unsafe.Pointer(s.TOPTR())))
+	self := ((*C.cfish_String)(Unwrap(s, "s")))
 	retvalCF := C.CFISH_Str_Swap_Chars(self, C.int32_t(match), C.int32_t(replacement))
 	defer C.cfish_dec_refcount(unsafe.Pointer(retvalCF))
 	return CFStringToGo(unsafe.Pointer(retvalCF))
@@ -665,6 +674,6 @@ func NewBlob(content []byte) Blob {
 }
 
 func (b *BlobIMP) GetBuf() uintptr {
-	self := (*C.cfish_Blob)(unsafe.Pointer(b.TOPTR()))
+	self := (*C.cfish_Blob)(Unwrap(b, "b"))
 	return uintptr(unsafe.Pointer(C.CFISH_Blob_Get_Buf(self)))
 }