Commit 4c847f36 authored by Dylan Trotter's avatar Dylan Trotter Committed by GitHub

Proper native types are returned from __native__ for simple typedefs

Previously Go typedefs for simple types (e.g. `type myInt int`) would return the underlying base type (e.g. `int`) from __native__. They now correctly convert to the typedef when used in a Go context.
parent 92c85b5b
......@@ -114,6 +114,7 @@ var builtinTypes = map[*Type]*builtinTypeInfo{
MethodType: {init: initMethodType},
ModuleType: {init: initModuleType},
NameErrorType: {global: true},
nativeBoolMetaclassType: {init: initNativeBoolMetaclassType},
nativeFuncType: {init: initNativeFuncType},
nativeMetaclassType: {init: initNativeMetaclassType},
nativeSliceType: {init: initNativeSliceType},
......
......@@ -25,10 +25,11 @@ import (
)
var (
nativeFuncType = newSimpleType("func", nativeType)
nativeMetaclassType = newBasisType("nativetype", reflect.TypeOf(nativeMetaclass{}), toNativeMetaclassUnsafe, TypeType)
nativeSliceType = newSimpleType("slice", nativeType)
nativeType = newBasisType("native", reflect.TypeOf(native{}), toNativeUnsafe, ObjectType)
nativeBoolMetaclassType = newBasisType("nativebooltype", reflect.TypeOf(nativeBoolMetaclass{}), toNativeBoolMetaclassUnsafe, nativeMetaclassType)
nativeFuncType = newSimpleType("func", nativeType)
nativeMetaclassType = newBasisType("nativetype", reflect.TypeOf(nativeMetaclass{}), toNativeMetaclassUnsafe, TypeType)
nativeSliceType = newSimpleType("slice", nativeType)
nativeType = newBasisType("native", reflect.TypeOf(native{}), toNativeUnsafe, ObjectType)
// Prepopulate the builtin primitive types so that WrapNative calls on
// these kinds of values resolve directly to primitive Python types.
nativeTypes = map[reflect.Type]*Type{
......@@ -64,8 +65,8 @@ func toNativeMetaclassUnsafe(o *Object) *nativeMetaclass {
return (*nativeMetaclass)(o.toPointer())
}
func newNativeType(rtype reflect.Type, base *Type) *nativeMetaclass {
return &nativeMetaclass{
func newNativeType(rtype reflect.Type, base *Type) *Type {
t := &nativeMetaclass{
Type{
Object: Object{typ: nativeMetaclassType},
name: nativeTypeName(rtype),
......@@ -75,6 +76,23 @@ func newNativeType(rtype reflect.Type, base *Type) *nativeMetaclass {
},
rtype,
}
if !base.isSubclass(nativeType) {
t.slots.Native = &nativeSlot{nativeTypedefNative}
}
return &t.Type
}
func nativeTypedefNative(f *Frame, o *Object) (reflect.Value, *BaseException) {
// The __native__ slot for primitive base classes (e.g. int) returns
// the corresponding primitive Go type. For typedef'd primitive types
// (e.g. type devNull int) we should return the subtype, not the
// primitive type. So first call the primitive type's __native__ and
// then convert it to the appropriate subtype.
val, raised := o.typ.bases[0].slots.Native.Fn(f, o)
if raised != nil {
return reflect.Value{}, raised
}
return val.Convert(toNativeMetaclassUnsafe(o.typ.ToObject()).rtype), nil
}
func nativeMetaclassNew(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
......@@ -89,6 +107,65 @@ func initNativeMetaclassType(dict map[string]*Object) {
dict["new"] = newBuiltinFunction("new", nativeMetaclassNew).ToObject()
}
type nativeBoolMetaclass struct {
nativeMetaclass
trueValue *Object
falseValue *Object
}
func toNativeBoolMetaclassUnsafe(o *Object) *nativeBoolMetaclass {
return (*nativeBoolMetaclass)(o.toPointer())
}
func newNativeBoolType(rtype reflect.Type) *Type {
t := &nativeBoolMetaclass{
nativeMetaclass: nativeMetaclass{
Type{
Object: Object{typ: nativeBoolMetaclassType},
name: nativeTypeName(rtype),
basis: BoolType.basis,
bases: []*Type{BoolType},
flags: typeFlagDefault &^ (typeFlagInstantiable | typeFlagBasetype),
},
rtype,
},
}
t.trueValue = (&Int{Object{typ: &t.nativeMetaclass.Type}, 1}).ToObject()
t.falseValue = (&Int{Object{typ: &t.nativeMetaclass.Type}, 0}).ToObject()
t.slots.Native = &nativeSlot{nativeBoolNative}
t.slots.New = &newSlot{nativeBoolNew}
return &t.nativeMetaclass.Type
}
func nativeBoolNative(f *Frame, o *Object) (reflect.Value, *BaseException) {
val := reflect.ValueOf(toIntUnsafe(o).IsTrue())
return val.Convert(toNativeMetaclassUnsafe(o.typ.ToObject()).rtype), nil
}
func nativeBoolNew(f *Frame, t *Type, args Args, kwargs KWArgs) (*Object, *BaseException) {
meta := toNativeBoolMetaclassUnsafe(t.ToObject())
argc := len(args)
if argc == 0 {
return meta.falseValue, nil
}
if argc != 1 {
return nil, f.RaiseType(TypeErrorType, fmt.Sprintf("%s() takes at most 1 argument (%d given)", t.Name(), argc))
}
ret, raised := IsTrue(f, args[0])
if raised != nil {
return nil, raised
}
if ret {
return meta.trueValue, nil
}
return meta.falseValue, nil
}
func initNativeBoolMetaclassType(dict map[string]*Object) {
nativeBoolMetaclassType.flags &^= typeFlagInstantiable | typeFlagBasetype
dict["new"] = newBuiltinFunction("new", nativeMetaclassNew).ToObject()
}
type native struct {
Object
value reflect.Value
......@@ -314,8 +391,6 @@ func getNativeType(rtype reflect.Type) *Type {
// object.
base := nativeType
switch rtype.Kind() {
case reflect.Bool:
base = BoolType
case reflect.Float32, reflect.Float64:
base = FloatType
case reflect.Func:
......@@ -337,9 +412,11 @@ func getNativeType(rtype reflect.Type) *Type {
d[meth.Name] = newNativeMethod(meth.Name, meth.Func)
}
}
t = &newNativeType(rtype, base).Type
if rtype.Kind() == reflect.Bool {
t = newNativeBoolType(rtype)
} else {
t = newNativeType(rtype, base)
}
derefed := rtype
for derefed.Kind() == reflect.Ptr {
derefed = derefed.Elem()
......@@ -351,7 +428,6 @@ func getNativeType(rtype reflect.Type) *Type {
}
}
t.dict = newStringDict(d)
// This cannot fail since we're defining simple classes.
if err := prepareType(t); err != "" {
logFatal(err)
......
......@@ -24,7 +24,7 @@ import (
func TestNativeMetaclassNew(t *testing.T) {
var i int16
intType := &newNativeType(reflect.TypeOf(i), IntType).Type
intType := newNativeType(reflect.TypeOf(i), IntType)
fun := wrapFuncForTest(func(f *Frame, args ...*Object) *BaseException {
newFunc, raised := GetAttr(f, intType.ToObject(), NewStr("new"), nil)
if raised != nil {
......@@ -142,6 +142,39 @@ func TestNativeFuncStrRepr(t *testing.T) {
}
}
func TestNativeNew(t *testing.T) {
fun := wrapFuncForTest(func(f *Frame, t *Type, args ...*Object) (*Tuple, *BaseException) {
o, raised := t.Call(f, args, nil)
if raised != nil {
return nil, raised
}
return newTestTuple(o, o.Type()), nil
})
type testBool bool
testBoolType := getNativeType(reflect.TypeOf(testBool(false)))
type testFloat float32
testFloatType := getNativeType(reflect.TypeOf(testFloat(0)))
type testString string
testStringType := getNativeType(reflect.TypeOf(testString("")))
cases := []invokeTestCase{
{args: wrapArgs(testBoolType), want: newTestTuple(false, testBoolType).ToObject()},
{args: wrapArgs(testBoolType, ""), want: newTestTuple(false, testBoolType).ToObject()},
{args: wrapArgs(testBoolType, 123), want: newTestTuple(true, testBoolType).ToObject()},
{args: wrapArgs(testBoolType, "foo", "bar"), wantExc: mustCreateException(TypeErrorType, "testBool() takes at most 1 argument (2 given)")},
{args: wrapArgs(testFloatType), want: newTestTuple(0.0, testFloatType).ToObject()},
{args: wrapArgs(testFloatType, 3.14), want: newTestTuple(3.14, testFloatType).ToObject()},
{args: wrapArgs(testFloatType, "foo", "bar"), wantExc: mustCreateException(TypeErrorType, "'__new__' of 'float' requires 0 or 1 arguments")},
{args: wrapArgs(testStringType), want: newTestTuple("", testStringType).ToObject()},
{args: wrapArgs(testStringType, "foo"), want: newTestTuple("foo", testStringType).ToObject()},
{args: wrapArgs(testStringType, "foo", "bar"), wantExc: mustCreateException(TypeErrorType, "str() takes at most 1 argument (2 given)")},
}
for _, cas := range cases {
if err := runInvokeTestCase(fun, &cas); err != "" {
t.Error(err)
}
}
}
func TestNativeSliceIter(t *testing.T) {
fun := wrapFuncForTest(func(f *Frame, slice interface{}) (*Object, *BaseException) {
o, raised := WrapNative(f, reflect.ValueOf(slice))
......@@ -389,6 +422,29 @@ func TestMaybeConvertValue(t *testing.T) {
}
}
func TestNativeTypedefNative(t *testing.T) {
fun := wrapFuncForTest(func(f *Frame, o *Object, wantType reflect.Type) (bool, *BaseException) {
val, raised := ToNative(f, o)
if raised != nil {
return false, raised
}
return val.Type() == wantType, nil
})
type testBool bool
testBoolRType := reflect.TypeOf(testBool(false))
type testInt int
testIntRType := reflect.TypeOf(testInt(0))
cases := []invokeTestCase{
{args: wrapArgs(mustNotRaise(getNativeType(testBoolRType).Call(NewRootFrame(), wrapArgs(true), nil)), testBoolRType), want: True.ToObject()},
{args: wrapArgs(mustNotRaise(getNativeType(testIntRType).Call(NewRootFrame(), wrapArgs(123), nil)), testIntRType), want: True.ToObject()},
}
for _, cas := range cases {
if err := runInvokeTestCase(fun, &cas); err != "" {
t.Error(err)
}
}
}
func TestNativeTypeName(t *testing.T) {
type fooStruct struct{}
cases := []struct {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment