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 <[email protected]> Authored: Fri Aug 28 18:49:34 2015 -0700 Committer: Marvin Humphrey <[email protected]> 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) }
