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:42 UTC

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

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)
+}