diff --git a/runtime/baseexception.go b/runtime/baseexception.go index ac18b6f9..e7b3cdbe 100644 --- a/runtime/baseexception.go +++ b/runtime/baseexception.go @@ -25,7 +25,7 @@ type BaseException struct { } func toBaseExceptionUnsafe(o *Object) *BaseException { - return (*BaseException)(o.toPointer()) + return o.self.(*BaseException) } // ToObject upcasts e to an Object. diff --git a/runtime/bool.go b/runtime/bool.go index 219596d8..465189a0 100644 --- a/runtime/bool.go +++ b/runtime/bool.go @@ -69,3 +69,8 @@ var ( trueStr = NewStr("True").ToObject() falseStr = NewStr("False").ToObject() ) + +func init() { + False.self = False + True.self = True +} diff --git a/runtime/builtin_types.go b/runtime/builtin_types.go index a64caa53..3372549b 100644 --- a/runtime/builtin_types.go +++ b/runtime/builtin_types.go @@ -17,6 +17,7 @@ package grumpy import ( "fmt" "math/big" + "reflect" "unicode" ) @@ -325,7 +326,7 @@ func builtinID(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { if raised := checkFunctionArgs(f, "id", args, ObjectType); raised != nil { return nil, raised } - return NewInt(int(uintptr(args[0].toPointer()))).ToObject(), nil + return NewInt(int(reflect.ValueOf(args[0]).Pointer())).ToObject(), nil } func builtinIsInstance(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { diff --git a/runtime/builtin_types_test.go b/runtime/builtin_types_test.go index 1eaa0cc1..7fac8e19 100644 --- a/runtime/builtin_types_test.go +++ b/runtime/builtin_types_test.go @@ -17,6 +17,7 @@ package grumpy import ( "fmt" "math/big" + "reflect" "testing" ) @@ -102,7 +103,7 @@ func TestBuiltinFuncs(t *testing.T) { {f: "hex", args: wrapArgs(0.1), wantExc: mustCreateException(TypeErrorType, "hex() argument can't be converted to hex")}, {f: "hex", args: wrapArgs(1, 2, 3), wantExc: mustCreateException(TypeErrorType, "'hex' requires 1 arguments")}, {f: "hex", args: wrapArgs(newObject(hexOctType)), want: NewStr("0xhexadecimal").ToObject()}, - {f: "id", args: wrapArgs(foo), want: NewInt(int(uintptr(foo.toPointer()))).ToObject()}, + {f: "id", args: wrapArgs(foo), want: NewInt(int(reflect.ValueOf(foo).Pointer())).ToObject()}, {f: "id", args: wrapArgs(), wantExc: mustCreateException(TypeErrorType, "'id' requires 1 arguments")}, {f: "isinstance", args: wrapArgs(NewInt(42).ToObject(), IntType.ToObject()), want: True.ToObject()}, {f: "isinstance", args: wrapArgs(NewStr("foo").ToObject(), TupleType.ToObject()), want: False.ToObject()}, diff --git a/runtime/code.go b/runtime/code.go index bf75f060..570b5a72 100644 --- a/runtime/code.go +++ b/runtime/code.go @@ -47,6 +47,8 @@ type Code struct { fn func(*Frame, []*Object) (*Object, *BaseException) } +func (c *Code) ToObject() *Object { return &c.Object } + // NewCode creates a new Code object that executes the given fn. func NewCode(name, filename string, args []FunctionArg, flags CodeFlag, fn func(*Frame, []*Object) (*Object, *BaseException)) *Code { argc := len(args) @@ -62,11 +64,13 @@ func NewCode(name, filename string, args []FunctionArg, flags CodeFlag, fn func( logFatal(fmt.Sprintf(format, name, arg.Name)) } } - return &Code{Object{typ: CodeType}, name, filename, argc, minArgc, flags, args, fn} + c := &Code{Object{typ: CodeType}, name, filename, argc, minArgc, flags, args, fn} + c.self = c + return c } func toCodeUnsafe(o *Object) *Code { - return (*Code)(o.toPointer()) + return o.self.(*Code) } // Eval runs the code object c in the context of the given globals. diff --git a/runtime/core.go b/runtime/core.go index 3b78865e..f24fc7e3 100644 --- a/runtime/core.go +++ b/runtime/core.go @@ -914,7 +914,7 @@ func compareRich(f *Frame, op compareOp, v, w *Object) (*Object, *BaseException) // closely resembles the behavior of CPython's default_3way_compare in object.c. func compareDefault(f *Frame, v, w *Object) int { if v.typ == w.typ { - pv, pw := uintptr(v.toPointer()), uintptr(w.toPointer()) + pv, pw := reflect.ValueOf(v).Pointer(), reflect.ValueOf(w).Pointer() if pv < pw { return -1 } @@ -938,7 +938,7 @@ func compareDefault(f *Frame, v, w *Object) int { if v.typ.Name() != w.typ.Name() { return 1 } - if uintptr(v.typ.toPointer()) < uintptr(w.typ.toPointer()) { + if reflect.ValueOf(v.typ).Pointer() < reflect.ValueOf(w.typ).Pointer() { return -1 } return 1 diff --git a/runtime/core_test.go b/runtime/core_test.go index 2b29468c..dfa90201 100644 --- a/runtime/core_test.go +++ b/runtime/core_test.go @@ -157,14 +157,14 @@ func TestBinaryOps(t *testing.T) { func TestCompareDefault(t *testing.T) { o1, o2 := newObject(ObjectType), newObject(ObjectType) // Make sure uintptr(o1) < uintptr(o2). - if uintptr(o1.toPointer()) > uintptr(o2.toPointer()) { + if reflect.ValueOf(o1).Pointer() > reflect.ValueOf(o2).Pointer() { o1, o2 = o2, o1 } // When type names are equal, comparison should fall back to comparing // the pointer values of the types of the objects. fakeObjectType := newTestClass("object", []*Type{ObjectType}, NewDict()) o3, o4 := newObject(fakeObjectType), newObject(ObjectType) - if uintptr(o3.typ.toPointer()) > uintptr(o4.typ.toPointer()) { + if reflect.ValueOf(o3.typ).Pointer() > reflect.ValueOf(o4.typ).Pointer() { o3, o4 = o4, o3 } // An int subtype that equals anything, but doesn't override other @@ -331,7 +331,7 @@ func TestHash(t *testing.T) { cases := []invokeTestCase{ {args: wrapArgs("foo"), want: hashFoo}, {args: wrapArgs(123), want: NewInt(123).ToObject()}, - {args: wrapArgs(o), want: NewInt(int(uintptr(o.toPointer()))).ToObject()}, + {args: wrapArgs(o), want: NewInt(int(reflect.ValueOf(o).Pointer())).ToObject()}, {args: wrapArgs(NewList()), wantExc: mustCreateException(TypeErrorType, "unhashable type: 'list'")}, {args: wrapArgs(NewDict()), wantExc: mustCreateException(TypeErrorType, "unhashable type: 'dict'")}, {args: wrapArgs(newObject(badHash)), wantExc: mustCreateException(TypeErrorType, "an integer is required")}, diff --git a/runtime/descriptor.go b/runtime/descriptor.go index 7ea001a3..92803db5 100644 --- a/runtime/descriptor.go +++ b/runtime/descriptor.go @@ -26,11 +26,13 @@ type Property struct { } func newProperty(get, set, del *Object) *Property { - return &Property{Object{typ: PropertyType}, get, set, del} + p := &Property{Object{typ: PropertyType}, get, set, del} + p.self = p + return p } func toPropertyUnsafe(o *Object) *Property { - return (*Property)(o.toPointer()) + return o.self.(*Property) } // ToObject upcasts p to an Object. diff --git a/runtime/dict.go b/runtime/dict.go index 66f2d7b3..4a99d80c 100644 --- a/runtime/dict.go +++ b/runtime/dict.go @@ -266,7 +266,9 @@ type Dict struct { // NewDict returns an empty Dict. func NewDict() *Dict { - return &Dict{Object: Object{typ: DictType}, table: newDictTable(0)} + d := &Dict{Object: Object{typ: DictType}, table: newDictTable(0)} + d.self = d + return d } func newStringDict(items map[string]*Object) *Dict { @@ -278,11 +280,13 @@ func newStringDict(items map[string]*Object) *Dict { for key, value := range items { table.insertAbsentEntry(&dictEntry{hashString(key), NewStr(key).ToObject(), value}) } - return &Dict{Object: Object{typ: DictType}, table: table} + d := &Dict{Object: Object{typ: DictType}, table: table} + d.self = d + return d } func toDictUnsafe(o *Object) *Dict { - return (*Dict)(o.toPointer()) + return o.self.(*Dict) } // loadTable atomically loads and returns d's underlying dictTable. @@ -741,15 +745,17 @@ type dictItemIterator struct { // newDictItemIterator creates a dictItemIterator object for d. It assumes that // d.mutex is held by the caller. func newDictItemIterator(d *Dict) *dictItemIterator { - return &dictItemIterator{ + i := &dictItemIterator{ Object: Object{typ: dictItemIteratorType}, iter: newDictEntryIterator(d), guard: newDictVersionGuard(d), } + i.self = i + return i } func toDictItemIteratorUnsafe(o *Object) *dictItemIterator { - return (*dictItemIterator)(o.toPointer()) + return o.self.(*dictItemIterator) } func (iter *dictItemIterator) ToObject() *Object { @@ -784,15 +790,17 @@ type dictKeyIterator struct { // newDictKeyIterator creates a dictKeyIterator object for d. It assumes that // d.mutex is held by the caller. func newDictKeyIterator(d *Dict) *dictKeyIterator { - return &dictKeyIterator{ + i := &dictKeyIterator{ Object: Object{typ: dictKeyIteratorType}, iter: newDictEntryIterator(d), guard: newDictVersionGuard(d), } + i.self = i + return i } func toDictKeyIteratorUnsafe(o *Object) *dictKeyIterator { - return (*dictKeyIterator)(o.toPointer()) + return o.self.(*dictKeyIterator) } func (iter *dictKeyIterator) ToObject() *Object { diff --git a/runtime/file.go b/runtime/file.go index a5b1c46c..e5df55db 100644 --- a/runtime/file.go +++ b/runtime/file.go @@ -45,12 +45,13 @@ type File struct { func NewFileFromFD(fd uintptr) *File { // TODO: Use fcntl or something to get the mode of the descriptor. file := &File{Object: Object{typ: FileType}, mode: "?", open: true, file: os.NewFile(fd, "")} + file.self = file file.reader = bufio.NewReader(file.file) return file } func toFileUnsafe(o *Object) *File { - return (*File)(o.toPointer()) + return o.self.(*File) } // ToObject upcasts f to an Object. diff --git a/runtime/float.go b/runtime/float.go index 2cbc728a..97def277 100644 --- a/runtime/float.go +++ b/runtime/float.go @@ -33,11 +33,13 @@ type Float struct { // NewFloat returns a new Float holding the given floating point value. func NewFloat(value float64) *Float { - return &Float{Object{typ: FloatType}, value} + f := &Float{Object{typ: FloatType}, value} + f.self = f + return f } func toFloatUnsafe(o *Object) *Float { - return (*Float)(o.toPointer()) + return o.self.(*Float) } // ToObject upcasts f to an Object. diff --git a/runtime/float_test.go b/runtime/float_test.go index dedc2780..e7c2569e 100644 --- a/runtime/float_test.go +++ b/runtime/float_test.go @@ -161,10 +161,14 @@ func TestFloatIsTrue(t *testing.T) { } func TestFloatNew(t *testing.T) { + setFloatSelf := func(f *Float) *Float { + f.self = f + return f + } floatNew := mustNotRaise(GetAttr(NewRootFrame(), FloatType.ToObject(), NewStr("__new__"), nil)) strictEqType := newTestClassStrictEq("StrictEq", FloatType) subType := newTestClass("SubType", []*Type{FloatType}, newStringDict(map[string]*Object{})) - subTypeObject := (&Float{Object: Object{typ: subType}, value: 3.14}).ToObject() + subTypeObject := setFloatSelf(&Float{Object: Object{typ: subType}, value: 3.14}).ToObject() goodSlotType := newTestClass("GoodSlot", []*Type{ObjectType}, newStringDict(map[string]*Object{ "__float__": newBuiltinFunction("__float__", func(_ *Frame, _ Args, _ KWArgs) (*Object, *BaseException) { return NewFloat(3.14).ToObject(), nil @@ -199,8 +203,8 @@ func TestFloatNew(t *testing.T) { {args: wrapArgs(FloatType, newObject(goodSlotType)), want: NewFloat(3.14).ToObject()}, {args: wrapArgs(FloatType, newObject(badSlotType)), wantExc: mustCreateException(TypeErrorType, "__float__ returned non-float (type object)")}, {args: wrapArgs(FloatType, newObject(slotSubTypeType)), want: subTypeObject}, - {args: wrapArgs(strictEqType, 3.14), want: (&Float{Object{typ: strictEqType}, 3.14}).ToObject()}, - {args: wrapArgs(strictEqType, newObject(goodSlotType)), want: (&Float{Object{typ: strictEqType}, 3.14}).ToObject()}, + {args: wrapArgs(strictEqType, 3.14), want: setFloatSelf(&Float{Object{typ: strictEqType}, 3.14}).ToObject()}, + {args: wrapArgs(strictEqType, newObject(goodSlotType)), want: setFloatSelf(&Float{Object{typ: strictEqType}, 3.14}).ToObject()}, {args: wrapArgs(strictEqType, newObject(badSlotType)), wantExc: mustCreateException(TypeErrorType, "__float__ returned non-float (type object)")}, {args: wrapArgs(), wantExc: mustCreateException(TypeErrorType, "'__new__' requires 1 arguments")}, {args: wrapArgs(IntType), wantExc: mustCreateException(TypeErrorType, "float.__new__(int): int is not a subtype of float")}, diff --git a/runtime/frame.go b/runtime/frame.go index 121ec9a8..e958c2ed 100644 --- a/runtime/frame.go +++ b/runtime/frame.go @@ -49,6 +49,7 @@ func NewRootFrame() *Frame { // newFrame creates a new Frame whose parent frame is back. func newFrame(back *Frame) *Frame { f := &Frame{Object: Object{typ: FrameType}} + f.self = f f.pushFrame(back) return f } @@ -64,7 +65,7 @@ func (f *Frame) pushFrame(back *Frame) { } func toFrameUnsafe(o *Object) *Frame { - return (*Frame)(o.toPointer()) + return o.self.(*Frame) } // Globals returns the globals dict for this frame. diff --git a/runtime/function.go b/runtime/function.go index 66c8afe5..e50a4e1c 100644 --- a/runtime/function.go +++ b/runtime/function.go @@ -95,17 +95,21 @@ type FunctionArg struct { // number of arguments are provided, populating *args and **kwargs if // necessary, etc. func NewFunction(c *Code, globals *Dict) *Function { - return &Function{Object{typ: FunctionType, dict: NewDict()}, nil, c.name, c, globals} + f := &Function{Object{typ: FunctionType, dict: NewDict()}, nil, c.name, c, globals} + f.self = f + return f } // newBuiltinFunction returns a function object with the given name that // invokes fn when called. func newBuiltinFunction(name string, fn Func) *Function { - return &Function{Object: Object{typ: FunctionType, dict: NewDict()}, fn: fn, name: name} + f := &Function{Object: Object{typ: FunctionType, dict: NewDict()}, fn: fn, name: name} + f.self = f + return f } func toFunctionUnsafe(o *Object) *Function { - return (*Function)(o.toPointer()) + return o.self.(*Function) } // ToObject upcasts f to an Object. @@ -150,11 +154,13 @@ type staticMethod struct { } func newStaticMethod(callable *Object) *staticMethod { - return &staticMethod{Object{typ: StaticMethodType}, callable} + s := &staticMethod{Object{typ: StaticMethodType}, callable} + s.self = s + return s } func toStaticMethodUnsafe(o *Object) *staticMethod { - return (*staticMethod)(o.toPointer()) + return o.self.(*staticMethod) } // ToObject upcasts f to an Object. diff --git a/runtime/generator.go b/runtime/generator.go index f8761a0b..c3c2931b 100644 --- a/runtime/generator.go +++ b/runtime/generator.go @@ -44,11 +44,13 @@ type Generator struct { // NewGenerator returns a new Generator object that runs the given Block b. func NewGenerator(f *Frame, fn func(*Object) (*Object, *BaseException)) *Generator { - return &Generator{Object: Object{typ: GeneratorType}, frame: f, fn: fn} + g := &Generator{Object: Object{typ: GeneratorType}, frame: f, fn: fn} + g.self = g + return g } func toGeneratorUnsafe(o *Object) *Generator { - return (*Generator)(o.toPointer()) + return o.self.(*Generator) } func (g *Generator) resume(f *Frame, sendValue *Object) (*Object, *BaseException) { diff --git a/runtime/int.go b/runtime/int.go index 691df0c0..8b04a758 100644 --- a/runtime/int.go +++ b/runtime/int.go @@ -26,9 +26,13 @@ const ( internedIntMax = 300 ) -var ( - internedInts = makeInternedInts() -) +var internedInts [internedIntMax - internedIntMin + 1]Int + +func init() { + for i := internedIntMin; i <= internedIntMax; i++ { + internedInts[i-internedIntMin] = Int{Object{typ: IntType, self: &internedInts[i-internedIntMin]}, i} + } +} // Int represents Python 'int' objects. type Int struct { @@ -41,11 +45,13 @@ func NewInt(value int) *Int { if value >= internedIntMin && value <= internedIntMax { return &internedInts[value-internedIntMin] } - return &Int{Object{typ: IntType}, value} + i := &Int{Object{typ: IntType}, value} + i.self = i + return i } func toIntUnsafe(o *Object) *Int { - return (*Int)(o.toPointer()) + return o.self.(*Int) } // ToObject upcasts i to an Object. @@ -504,11 +510,3 @@ func intFromObject(f *Frame, o *Object) (*Object, *BaseException) { func intToLong(o *Int) *Long { return NewLong(big.NewInt(int64(o.Value()))) } - -func makeInternedInts() [internedIntMax - internedIntMin + 1]Int { - var ints [internedIntMax - internedIntMin + 1]Int - for i := internedIntMin; i <= internedIntMax; i++ { - ints[i-internedIntMin] = Int{Object{typ: IntType}, i} - } - return ints -} diff --git a/runtime/int_test.go b/runtime/int_test.go index ffcfffea..ccbda80c 100644 --- a/runtime/int_test.go +++ b/runtime/int_test.go @@ -113,9 +113,13 @@ func TestIntNew(t *testing.T) { return args[0], nil }).ToObject(), })) + setIntSelf := func(i *Int) *Int { + i.self = i + return i + } strictEqType := newTestClassStrictEq("StrictEq", IntType) subType := newTestClass("SubType", []*Type{IntType}, newStringDict(map[string]*Object{})) - subTypeObject := (&Int{Object: Object{typ: subType}, value: 3}).ToObject() + subTypeObject := setIntSelf(&Int{Object: Object{typ: subType}, value: 3}).ToObject() goodSlotType := newTestClass("GoodSlot", []*Type{ObjectType}, newStringDict(map[string]*Object{ "__int__": newBuiltinFunction("__int__", func(_ *Frame, _ Args, _ KWArgs) (*Object, *BaseException) { return NewInt(3).ToObject(), nil @@ -145,11 +149,11 @@ func TestIntNew(t *testing.T) { {args: wrapArgs(IntType, "102", 0), want: NewInt(102).ToObject()}, {args: wrapArgs(IntType, 42), want: NewInt(42).ToObject()}, {args: wrapArgs(IntType, -3.14), want: NewInt(-3).ToObject()}, - {args: wrapArgs(strictEqType, 42), want: (&Int{Object{typ: strictEqType}, 42}).ToObject()}, + {args: wrapArgs(strictEqType, 42), want: setIntSelf(&Int{Object{typ: strictEqType}, 42}).ToObject()}, {args: wrapArgs(IntType, newObject(goodSlotType)), want: NewInt(3).ToObject()}, {args: wrapArgs(IntType, newObject(badSlotType)), wantExc: mustCreateException(TypeErrorType, "__int__ returned non-int (type object)")}, {args: wrapArgs(IntType, newObject(slotSubTypeType)), want: subTypeObject}, - {args: wrapArgs(strictEqType, newObject(goodSlotType)), want: (&Int{Object{typ: strictEqType}, 3}).ToObject()}, + {args: wrapArgs(strictEqType, newObject(goodSlotType)), want: setIntSelf(&Int{Object{typ: strictEqType}, 3}).ToObject()}, {args: wrapArgs(strictEqType, newObject(badSlotType)), wantExc: mustCreateException(TypeErrorType, "__int__ returned non-int (type object)")}, {args: wrapArgs(IntType, "0xff"), wantExc: mustCreateException(ValueErrorType, "invalid literal for int() with base 10: 0xff")}, {args: wrapArgs(IntType, ""), wantExc: mustCreateException(ValueErrorType, "invalid literal for int() with base 10: ")}, diff --git a/runtime/list.go b/runtime/list.go index 47bb1685..e64a2643 100644 --- a/runtime/list.go +++ b/runtime/list.go @@ -35,6 +35,7 @@ type List struct { // NewList returns a list containing the given elements. func NewList(elems ...*Object) *List { l := &List{Object: Object{typ: ListType}} + l.self = l numElems := len(elems) l.resize(numElems) for i := 0; i < numElems; i++ { @@ -44,7 +45,7 @@ func NewList(elems ...*Object) *List { } func toListUnsafe(o *Object) *List { - return (*List)(o.toPointer()) + return o.self.(*List) } // ToObject upcasts l to an Object. @@ -368,11 +369,12 @@ type listIterator struct { func newListIterator(l *List) *Object { iter := &listIterator{Object: Object{typ: listIteratorType}, list: l} + iter.self = iter return &iter.Object } func toListIteratorUnsafe(o *Object) *listIterator { - return (*listIterator)(o.toPointer()) + return o.self.(*listIterator) } var listIteratorType = newBasisType("listiterator", reflect.TypeOf(listIterator{}), toListIteratorUnsafe, ObjectType) diff --git a/runtime/long.go b/runtime/long.go index 4c2aeb8c..4dad3ee1 100644 --- a/runtime/long.go +++ b/runtime/long.go @@ -43,21 +43,23 @@ type Long struct { // NewLong returns a new Long holding the given value. func NewLong(x *big.Int) *Long { - result := Long{Object: Object{typ: LongType}} + result := &Long{Object: Object{typ: LongType}} + result.self = result result.value.Set(x) - return &result + return result } // NewLongFromBytes returns a new Long holding the given bytes, // interpreted as a big endian unsigned integer. func NewLongFromBytes(b []byte) *Long { - result := Long{Object: Object{typ: LongType}} + result := &Long{Object: Object{typ: LongType}} + result.self = result result.value.SetBytes(b) - return &result + return result } func toLongUnsafe(o *Object) *Long { - return (*Long)(o.toPointer()) + return o.self.(*Long) } // ToObject upcasts l to an Object. @@ -77,10 +79,11 @@ func (l *Long) IsTrue() bool { // Neg returns a new Long that is the negative of l. func (l *Long) Neg() *Long { - result := Long{Object: Object{typ: LongType}} + result := &Long{Object: Object{typ: LongType}} + result.self = result result.value.Set(&l.value) result.value.Neg(&result.value) - return &result + return result } // LongType is the object representing the Python 'long' type. @@ -174,7 +177,8 @@ func longLong(f *Frame, o *Object) (*Object, *BaseException) { if o.typ == LongType { return o, nil } - l := Long{Object: Object{typ: LongType}} + l := &Long{Object: Object{typ: LongType}} + l.self = l l.value.Set(&toLongUnsafe(o).value) return l.ToObject(), nil } @@ -344,7 +348,8 @@ func initLongType(dict map[string]*Object) { } func longCallUnary(fun func(z, x *big.Int), v *Long) *Object { - l := Long{Object: Object{typ: LongType}} + l := &Long{Object: Object{typ: LongType}} + l.self = l fun(&l.value, &v.value) return l.ToObject() } @@ -354,7 +359,8 @@ func longCallUnaryBool(fun func(x *big.Int) bool, v *Long) *Object { } func longCallBinary(fun func(z, x, y *big.Int), v, w *Long) *Object { - l := Long{Object: Object{typ: LongType}} + l := &Long{Object: Object{typ: LongType}} + l.self = l fun(&l.value, &v.value, &w.value) return l.ToObject() } @@ -370,7 +376,8 @@ func longCallShift(fun func(z, x *big.Int, n uint), f *Frame, v, w *Long) (*Obje if w.value.Sign() < 0 { return nil, f.RaiseType(ValueErrorType, "negative shift count") } - l := Long{Object: Object{typ: LongType}} + l := &Long{Object: Object{typ: LongType}} + l.self = l fun(&l.value, &v.value, uint(w.value.Int64())) return l.ToObject(), nil } diff --git a/runtime/long_test.go b/runtime/long_test.go index f4acc89d..680cf7b3 100644 --- a/runtime/long_test.go +++ b/runtime/long_test.go @@ -67,7 +67,8 @@ func TestLongNew(t *testing.T) { })) strictEqType := newTestClassStrictEq("StrictEq", LongType) newStrictEq := func(i *big.Int) *Object { - l := Long{Object: Object{typ: strictEqType}} + l := &Long{Object: Object{typ: strictEqType}} + l.self = l l.value.Set(i) return l.ToObject() } diff --git a/runtime/method.go b/runtime/method.go index f48c0e49..5bf8de6a 100644 --- a/runtime/method.go +++ b/runtime/method.go @@ -31,11 +31,13 @@ type Method struct { // NewMethod returns a method wrapping the given function belonging to class. // When self is None the method is unbound, otherwise it is bound to self. func NewMethod(function *Function, self *Object, class *Type) *Method { - return &Method{Object{typ: MethodType}, function, self, class, function.Name()} + m := &Method{Object{typ: MethodType}, function, self, class, function.Name()} + m.Object.self = m + return m } func toMethodUnsafe(o *Object) *Method { - return (*Method)(o.toPointer()) + return o.self.(*Method) } // ToObject upcasts m to an Object. diff --git a/runtime/module.go b/runtime/module.go index 49aa0520..635d8c33 100644 --- a/runtime/module.go +++ b/runtime/module.go @@ -186,11 +186,13 @@ func newModule(name, filename string) *Module { "__file__": NewStr(filename).ToObject(), "__name__": NewStr(name).ToObject(), }) - return &Module{Object: Object{typ: ModuleType, dict: d}} + m := &Module{Object: Object{typ: ModuleType, dict: d}} + m.self = m + return m } func toModuleUnsafe(o *Object) *Module { - return (*Module)(o.toPointer()) + return o.self.(*Module) } // GetFilename returns the __file__ attribute of m, raising SystemError if it diff --git a/runtime/module_test.go b/runtime/module_test.go index f9f41206..fc3e8498 100644 --- a/runtime/module_test.go +++ b/runtime/module_test.go @@ -20,6 +20,11 @@ import ( "testing" ) +func setModuleSelf(m *Module) *Module { + m.self = m + return m +} + func TestImportModule(t *testing.T) { f := NewRootFrame() invalidModule := newObject(ObjectType) @@ -219,7 +224,7 @@ func TestModuleGetNameAndFilename(t *testing.T) { cases := []invokeTestCase{ {args: wrapArgs(newModule("foo", "foo.py")), want: newTestTuple("foo", "foo.py").ToObject()}, {args: Args{mustNotRaise(ModuleType.Call(NewRootFrame(), wrapArgs("foo"), nil))}, wantExc: mustCreateException(SystemErrorType, "module filename missing")}, - {args: wrapArgs(&Module{Object: Object{typ: ModuleType, dict: NewDict()}}), wantExc: mustCreateException(SystemErrorType, "nameless module")}, + {args: wrapArgs(setModuleSelf(&Module{Object: Object{typ: ModuleType, dict: NewDict()}})), wantExc: mustCreateException(SystemErrorType, "nameless module")}, } for _, cas := range cases { if err := runInvokeTestCase(fun, &cas); err != "" { @@ -262,7 +267,7 @@ func TestModuleStrRepr(t *testing.T) { {args: wrapArgs(newModule("foo", "")), want: NewStr("'>").ToObject()}, {args: wrapArgs(newModule("foo.bar.baz", "")), want: NewStr("'>").ToObject()}, {args: Args{mustNotRaise(ModuleType.Call(NewRootFrame(), wrapArgs("foo"), nil))}, want: NewStr("").ToObject()}, - {args: wrapArgs(&Module{Object: Object{typ: ModuleType, dict: newTestDict("__file__", "foo.py")}}), want: NewStr("").ToObject()}, + {args: wrapArgs(setModuleSelf(&Module{Object: Object{typ: ModuleType, dict: newTestDict("__file__", "foo.py")}})), want: NewStr("").ToObject()}, } for _, cas := range cases { if err := runInvokeTestCase(wrapFuncForTest(ToStr), &cas); err != "" { @@ -380,5 +385,5 @@ func init() { } func newTestModule(name, filename string) *Module { - return &Module{Object: Object{typ: testModuleType, dict: newTestDict("__name__", name, "__file__", filename)}} + return setModuleSelf(&Module{Object: Object{typ: testModuleType, dict: newTestDict("__name__", name, "__file__", filename)}}) } diff --git a/runtime/native.go b/runtime/native.go index 0ee9746f..a31a0784 100644 --- a/runtime/native.go +++ b/runtime/native.go @@ -61,11 +61,11 @@ type nativeMetaclass struct { } func toNativeMetaclassUnsafe(o *Object) *nativeMetaclass { - return (*nativeMetaclass)(o.toPointer()) + return o.self.(*nativeMetaclass) } func newNativeType(rtype reflect.Type, base *Type, d *Dict) *nativeMetaclass { - return &nativeMetaclass{ + n := &nativeMetaclass{ Type{ Object: Object{typ: nativeMetaclassType, dict: d}, name: nativeTypeName(rtype), @@ -75,6 +75,8 @@ func newNativeType(rtype reflect.Type, base *Type, d *Dict) *nativeMetaclass { }, rtype, } + n.self = n + return n } func nativeMetaclassNew(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { @@ -95,7 +97,7 @@ type native struct { } func toNativeUnsafe(o *Object) *native { - return (*native)(o.toPointer()) + return o.self.(*native) } // ToObject upcasts n to an Object. @@ -161,11 +163,12 @@ type sliceIterator struct { func newSliceIterator(slice reflect.Value) *Object { iter := &sliceIterator{Object: Object{typ: sliceIteratorType}, slice: slice, numElems: slice.Len()} + iter.self = iter return &iter.Object } func toSliceIteratorUnsafe(o *Object) *sliceIterator { - return (*sliceIterator)(o.toPointer()) + return o.self.(*sliceIterator) } func sliceIteratorIter(f *Frame, o *Object) (*Object, *BaseException) { @@ -234,7 +237,9 @@ func WrapNative(f *Frame, v reflect.Value) (*Object, *BaseException) { } // TODO: Make native bool subtypes singletons and add support // for __new__ so we can use t.Call() here. - return (&Int{Object{typ: t}, i}).ToObject(), nil + v := &Int{Object{typ: t}, i} + v.self = v + return v.ToObject(), nil case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16: return t.Call(f, Args{NewInt(int(v.Int())).ToObject()}, nil) // Handle potentially large ints separately in case of overflow. @@ -292,7 +297,21 @@ func WrapNative(f *Frame, v reflect.Value) (*Object, *BaseException) { if basis := v.Elem(); basisTypes[basis.Type()] != nil { // We have a basis type that is binary compatible with // Object. - return (*Object)(unsafe.Pointer(basis.UnsafeAddr())), nil + if !v.CanInterface() { + // In the event we got here with an un-exported field, just force + // convert it. + // TODO: Find a better/safer solution. + return (*Object)(unsafe.Pointer(basis.UnsafeAddr())), nil + } + + iface := v.Interface() + if o, ok := iface.(*Object); ok { + return o, nil + } + type toObjecter interface { + ToObject() *Object + } + return iface.(toObjecter).ToObject(), nil } case reflect.Struct: if i, ok := v.Interface().(big.Int); ok { @@ -303,7 +322,9 @@ func WrapNative(f *Frame, v reflect.Value) (*Object, *BaseException) { return None, nil } } - return (&native{Object{typ: t}, v}).ToObject(), nil + n := &native{Object{typ: t}, v} + n.self = n + return n.ToObject(), nil } func getNativeType(rtype reflect.Type) *Type { diff --git a/runtime/native_test.go b/runtime/native_test.go index 1dad5eb7..10d5d1fb 100644 --- a/runtime/native_test.go +++ b/runtime/native_test.go @@ -71,6 +71,7 @@ func TestNativeFuncCall(t *testing.T) { } for _, cas := range cases { n := &native{Object{typ: nativeFuncType}, reflect.ValueOf(cas.fun)} + n.self = n if err := runInvokeTestCase(n.ToObject(), &cas.invokeTestCase); err != "" { t.Error(err) } @@ -176,6 +177,7 @@ func TestWrapNative(t *testing.T) { d := NewDict() i := 0 n := &native{Object{typ: nativeType}, reflect.ValueOf(&i)} + n.self = n cases := []struct { value interface{} want *Object @@ -354,6 +356,7 @@ func TestMaybeConvertValue(t *testing.T) { type fooStruct struct{} foo := &fooStruct{} fooNative := &native{Object{typ: nativeType}, reflect.ValueOf(&foo)} + fooNative.self = fooNative cases := []struct { o *Object expectedRType reflect.Type diff --git a/runtime/object.go b/runtime/object.go index 585c957f..491fa9cf 100644 --- a/runtime/object.go +++ b/runtime/object.go @@ -17,7 +17,6 @@ package grumpy import ( "fmt" "reflect" - "unsafe" ) var ( @@ -36,11 +35,16 @@ var ( } ) +func init() { + ObjectType.self = ObjectType +} + // Object represents Python 'object' objects. type Object struct { typ *Type `attr:"__class__"` dict *Dict `attr:"__dict__"` ref *WeakRef + self interface{} } func newObject(t *Type) *Object { @@ -48,7 +52,18 @@ func newObject(t *Type) *Object { if t != ObjectType { dict = NewDict() } - o := (*Object)(unsafe.Pointer(reflect.New(t.basis).Pointer())) + outside := reflect.New(t.basis).Interface() + type toObjecter interface { + ToObject() *Object + } + + var o *Object + if obj, ok := outside.(*Object); ok { + o = obj + } else { + o = outside.(toObjecter).ToObject() + } + o.self = outside o.typ = t o.dict = dict return o @@ -86,10 +101,6 @@ func (o *Object) Type() *Type { return o.typ } -func (o *Object) toPointer() unsafe.Pointer { - return unsafe.Pointer(o) -} - func (o *Object) isInstance(t *Type) bool { return o.typ.isSubclass(t) } @@ -157,7 +168,7 @@ func objectGetAttribute(f *Frame, o *Object, name *Str) (*Object, *BaseException } func objectHash(f *Frame, o *Object) (*Object, *BaseException) { - return NewInt(int(uintptr(o.toPointer()))).ToObject(), nil + return NewInt(int(reflect.ValueOf(o).Pointer())).ToObject(), nil } func objectNew(f *Frame, t *Type, _ Args, _ KWArgs) (*Object, *BaseException) { diff --git a/runtime/object_test.go b/runtime/object_test.go index 22b6e3cd..b2892c84 100644 --- a/runtime/object_test.go +++ b/runtime/object_test.go @@ -274,6 +274,7 @@ func TestObjectReduce(t *testing.T) { }) fooType := newTestClass("Foo", []*Type{StrType}, NewDict()) fooNoDict := &Str{Object: Object{typ: fooType}, value: "fooNoDict"} + fooNoDict.self = fooNoDict // Calling __reduce_ex__ on a type that overrides __reduce__ should // forward to the call to __reduce__. reduceOverrideType := newTestClass("ReduceOverride", []*Type{ObjectType}, newStringDict(map[string]*Object{ @@ -299,6 +300,7 @@ func TestObjectReduce(t *testing.T) { // subclasses can be reduced. intSubclass := newTestClass("IntSubclass", []*Type{IntType}, NewDict()) intSubclassInst := &Int{Object{typ: intSubclass}, 123} + intSubclassInst.self = intSubclassInst cases := []invokeTestCase{ {args: wrapArgs("__reduce__", 42, Args{}), wantExc: mustCreateException(TypeErrorType, "can't pickle int objects")}, {args: wrapArgs("__reduce__", 42, wrapArgs(2)), want: newTestTuple(42, None, None, None).ToObject()}, @@ -366,6 +368,10 @@ func TestObjectSetAttr(t *testing.T) { } } +type noReprMethodBasis struct{ Object } + +func (n *noReprMethodBasis) ToObject() *Object { return &n.Object } + func TestObjectStrRepr(t *testing.T) { fun := wrapFuncForTest(func(f *Frame, o *Object, wantPattern string) *BaseException { re := regexp.MustCompile(wantPattern) @@ -385,7 +391,6 @@ func TestObjectStrRepr(t *testing.T) { } return nil }) - type noReprMethodBasis struct{ Object } noReprMethodType := newType(TypeType, "noReprMethod", reflect.TypeOf(noReprMethodBasis{}), []*Type{}, NewDict()) noReprMethodType.mro = []*Type{noReprMethodType} fooType := newTestClass("Foo", []*Type{ObjectType}, newTestDict("__module__", "foo.bar")) diff --git a/runtime/range.go b/runtime/range.go index 4c883d5a..92583c56 100644 --- a/runtime/range.go +++ b/runtime/range.go @@ -37,7 +37,7 @@ type enumerate struct { } func toEnumerateUnsafe(o *Object) *enumerate { - return (*enumerate)(o.toPointer()) + return o.self.(*enumerate) } func enumerateIter(f *Frame, o *Object) (*Object, *BaseException) { @@ -88,6 +88,7 @@ func enumerateNew(f *Frame, t *Type, args Args, _ KWArgs) (*Object, *BaseExcepti d = NewDict() } e := &enumerate{Object: Object{typ: t, dict: d}, index: index, iter: iter} + e.self = e return &e.Object, nil } @@ -126,7 +127,7 @@ type rangeIterator struct { } func toRangeIteratorUnsafe(o *Object) *rangeIterator { - return (*rangeIterator)(o.toPointer()) + return o.self.(*rangeIterator) } func rangeIteratorIter(f *Frame, o *Object) (*Object, *BaseException) { @@ -157,12 +158,14 @@ type xrange struct { } func toXRangeUnsafe(o *Object) *xrange { - return (*xrange)(o.toPointer()) + return o.self.(*xrange) } func xrangeIter(f *Frame, o *Object) (*Object, *BaseException) { r := toXRangeUnsafe(o) - return &(&rangeIterator{Object{typ: rangeIteratorType}, r.start, r.stop, r.step}).Object, nil + ri := &rangeIterator{Object{typ: rangeIteratorType}, r.start, r.stop, r.step} + ri.self = ri + return &ri.Object, nil } func xrangeNew(f *Frame, _ *Type, args Args, _ KWArgs) (*Object, *BaseException) { @@ -192,6 +195,7 @@ func xrangeNew(f *Frame, _ *Type, args Args, _ KWArgs) (*Object, *BaseException) return nil, f.RaiseType(OverflowErrorType, errResultTooLarge) } r := &xrange{Object: Object{typ: xrangeType}, start: start, stop: stop, step: step} + r.self = r return &r.Object, nil } diff --git a/runtime/seq.go b/runtime/seq.go index d2da0b8c..8b422354 100644 --- a/runtime/seq.go +++ b/runtime/seq.go @@ -289,11 +289,12 @@ type seqIterator struct { func newSeqIterator(seq *Object) *Object { iter := &seqIterator{Object: Object{typ: seqIteratorType}, seq: seq} + iter.self = iter return &iter.Object } func toSeqIteratorUnsafe(o *Object) *seqIterator { - return (*seqIterator)(o.toPointer()) + return o.self.(*seqIterator) } func seqIteratorIter(f *Frame, o *Object) (*Object, *BaseException) { diff --git a/runtime/set.go b/runtime/set.go index b515b056..0bcfd15d 100644 --- a/runtime/set.go +++ b/runtime/set.go @@ -72,11 +72,13 @@ type Set setBase // NewSet returns an empty Set. func NewSet() *Set { - return &Set{Object{typ: SetType}, NewDict()} + s := &Set{Object{typ: SetType}, NewDict()} + s.self = s + return s } func toSetUnsafe(o *Object) *Set { - return (*Set)(o.toPointer()) + return o.self.(*Set) } // Add inserts key into s. If key already exists then does nothing. @@ -261,7 +263,7 @@ func initSetType(dict map[string]*Object) { type FrozenSet setBase func toFrozenSetUnsafe(o *Object) *FrozenSet { - return (*FrozenSet)(o.toPointer()) + return o.self.(*FrozenSet) } // Contains returns true if key exists in s. diff --git a/runtime/set_test.go b/runtime/set_test.go index 73216899..93a6df9e 100644 --- a/runtime/set_test.go +++ b/runtime/set_test.go @@ -326,5 +326,7 @@ func newTestFrozenSet(elems ...interface{}) *FrozenSet { panic(raised) } } - return &FrozenSet{Object{typ: FrozenSetType}, d} + s := &FrozenSet{Object{typ: FrozenSetType}, d} + s.self = s + return s } diff --git a/runtime/slice.go b/runtime/slice.go index be5af2d4..b926c38d 100644 --- a/runtime/slice.go +++ b/runtime/slice.go @@ -32,7 +32,7 @@ type Slice struct { } func toSliceUnsafe(o *Object) *Slice { - return (*Slice)(o.toPointer()) + return o.self.(*Slice) } // calcSlice returns the three range indices (start, stop, step) and the length diff --git a/runtime/slice_test.go b/runtime/slice_test.go index ed3f2467..e32fa1b1 100644 --- a/runtime/slice_test.go +++ b/runtime/slice_test.go @@ -59,11 +59,15 @@ func TestSliceCompare(t *testing.T) { } func TestSliceNew(t *testing.T) { + setSliceSelf := func(s *Slice) *Slice { + s.self = s + return s + } cases := []invokeTestCase{ {args: nil, wantExc: mustCreateException(TypeErrorType, "'__new__' of 'object' requires 3 arguments")}, - {args: wrapArgs(10), want: (&Slice{Object{typ: SliceType}, nil, NewInt(10).ToObject(), nil}).ToObject()}, - {args: wrapArgs(1.2, "foo"), want: (&Slice{Object{typ: SliceType}, NewFloat(1.2).ToObject(), NewStr("foo").ToObject(), nil}).ToObject()}, - {args: wrapArgs(None, None, true), want: (&Slice{Object{typ: SliceType}, None, None, True.ToObject()}).ToObject()}, + {args: wrapArgs(10), want: setSliceSelf(&Slice{Object{typ: SliceType}, nil, NewInt(10).ToObject(), nil}).ToObject()}, + {args: wrapArgs(1.2, "foo"), want: setSliceSelf(&Slice{Object{typ: SliceType}, NewFloat(1.2).ToObject(), NewStr("foo").ToObject(), nil}).ToObject()}, + {args: wrapArgs(None, None, true), want: setSliceSelf(&Slice{Object{typ: SliceType}, None, None, True.ToObject()}).ToObject()}, {args: wrapArgs(1, 2, 3, 4), wantExc: mustCreateException(TypeErrorType, "'__new__' of 'object' requires 3 arguments")}, } for _, cas := range cases { diff --git a/runtime/str.go b/runtime/str.go index b0c82f6a..0d014923 100644 --- a/runtime/str.go +++ b/runtime/str.go @@ -43,6 +43,7 @@ func InternStr(s string) *Str { str, _ := internedStrs[s] if str == nil { str = &Str{Object: Object{typ: StrType}, value: s, hash: NewInt(hashString(s))} + str.self = str internedStrs[s] = str } return str @@ -60,11 +61,13 @@ func NewStr(value string) *Str { if s := internedStrs[value]; s != nil { return s } - return &Str{Object: Object{typ: StrType}, value: value} + s := &Str{Object: Object{typ: StrType}, value: value} + s.self = s + return s } func toStrUnsafe(o *Object) *Str { - return (*Str)(o.toPointer()) + return o.self.(*Str) } // Decode produces a unicode object from the bytes of s assuming they have the diff --git a/runtime/str_test.go b/runtime/str_test.go index 994e0b27..3dd7aa87 100644 --- a/runtime/str_test.go +++ b/runtime/str_test.go @@ -23,6 +23,7 @@ import ( func TestNewStr(t *testing.T) { expected := &Str{Object: Object{typ: StrType}, value: "foo"} + expected.self = expected s := NewStr("foo") if !reflect.DeepEqual(s, expected) { t.Errorf(`NewStr("foo") = %+v, expected %+v`, *s, *expected) @@ -176,6 +177,10 @@ func TestStrGetItem(t *testing.T) { } func TestStrNew(t *testing.T) { + setStrSelf := func(s *Str) *Str { + s.self = s + return s + } dummy := newObject(ObjectType) dummyStr := NewStr(fmt.Sprintf("", dummy)) fooType := newTestClass("Foo", []*Type{ObjectType}, newStringDict(map[string]*Object{ @@ -186,7 +191,7 @@ func TestStrNew(t *testing.T) { foo := newObject(fooType) strictEqType := newTestClassStrictEq("StrictEq", StrType) subType := newTestClass("SubType", []*Type{StrType}, newStringDict(map[string]*Object{})) - subTypeObject := (&Str{Object: Object{typ: subType}, value: "abc"}).ToObject() + subTypeObject := setStrSelf(&Str{Object: Object{typ: subType}, value: "abc"}).ToObject() goodSlotType := newTestClass("GoodSlot", []*Type{ObjectType}, newStringDict(map[string]*Object{ "__str__": newBuiltinFunction("__str__", func(_ *Frame, _ Args, _ KWArgs) (*Object, *BaseException) { return NewStr("abc").ToObject(), nil @@ -210,11 +215,11 @@ func TestStrNew(t *testing.T) { {args: wrapArgs(StrType.ToObject()), want: NewStr("").ToObject()}, {args: wrapArgs(StrType.ToObject(), NewDict().ToObject()), want: NewStr("{}").ToObject()}, {args: wrapArgs(StrType.ToObject(), dummy), want: dummyStr.ToObject()}, - {args: wrapArgs(strictEqType, "foo"), want: (&Str{Object: Object{typ: strictEqType}, value: "foo"}).ToObject()}, + {args: wrapArgs(strictEqType, "foo"), want: setStrSelf(&Str{Object: Object{typ: strictEqType}, value: "foo"}).ToObject()}, {args: wrapArgs(StrType, newObject(goodSlotType)), want: NewStr("abc").ToObject()}, {args: wrapArgs(StrType, newObject(badSlotType)), wantExc: mustCreateException(TypeErrorType, "__str__ returned non-string (type object)")}, {args: wrapArgs(StrType, newObject(slotSubTypeType)), want: subTypeObject}, - {args: wrapArgs(strictEqType, newObject(goodSlotType)), want: (&Str{Object: Object{typ: strictEqType}, value: "abc"}).ToObject()}, + {args: wrapArgs(strictEqType, newObject(goodSlotType)), want: setStrSelf(&Str{Object: Object{typ: strictEqType}, value: "abc"}).ToObject()}, {args: wrapArgs(strictEqType, newObject(badSlotType)), wantExc: mustCreateException(TypeErrorType, "__str__ returned non-string (type object)")}, } for _, cas := range cases { diff --git a/runtime/super.go b/runtime/super.go index f8b27509..c9a3ed74 100644 --- a/runtime/super.go +++ b/runtime/super.go @@ -30,8 +30,10 @@ type super struct { objType *Type } +func (s *super) ToObject() *Object { return &s.Object } + func toSuperUnsafe(o *Object) *super { - return (*super)(o.toPointer()) + return o.self.(*super) } func superInit(f *Frame, o *Object, args Args, _ KWArgs) (*Object, *BaseException) { diff --git a/runtime/traceback.go b/runtime/traceback.go index 04252aaa..42fe7492 100644 --- a/runtime/traceback.go +++ b/runtime/traceback.go @@ -27,11 +27,13 @@ type Traceback struct { } func newTraceback(f *Frame, next *Traceback) *Traceback { - return &Traceback{Object{typ: TracebackType}, f, next, f.lineno} + t := &Traceback{Object{typ: TracebackType}, f, next, f.lineno} + t.self = t + return t } func toTracebackUnsafe(o *Object) *Traceback { - return (*Traceback)(o.toPointer()) + return o.self.(*Traceback) } // ToObject upcasts f to an Object. diff --git a/runtime/tuple.go b/runtime/tuple.go index f723e82c..637bd4b0 100644 --- a/runtime/tuple.go +++ b/runtime/tuple.go @@ -32,11 +32,13 @@ func NewTuple(elems ...*Object) *Tuple { if len(elems) == 0 { return emptyTuple } - return &Tuple{Object: Object{typ: TupleType}, elems: elems} + t := &Tuple{Object: Object{typ: TupleType}, elems: elems} + t.self = t + return t } func toTupleUnsafe(o *Object) *Tuple { - return (*Tuple)(o.toPointer()) + return o.self.(*Tuple) } // GetItem returns the i'th element of t. Bounds are unchecked and therefore @@ -60,6 +62,10 @@ var TupleType = newBasisType("tuple", reflect.TypeOf(Tuple{}), toTupleUnsafe, Ob var emptyTuple = &Tuple{Object: Object{typ: TupleType}} +func init() { + emptyTuple.self = emptyTuple +} + func tupleAdd(f *Frame, v, w *Object) (*Object, *BaseException) { if !w.isInstance(TupleType) { return NotImplemented, nil diff --git a/runtime/type.go b/runtime/type.go index e1071068..b941a3bc 100644 --- a/runtime/type.go +++ b/runtime/type.go @@ -100,13 +100,15 @@ func newClass(f *Frame, meta *Type, name string, bases []*Type, dict *Dict) (*Ty } func newType(meta *Type, name string, basis reflect.Type, bases []*Type, dict *Dict) *Type { - return &Type{ + t := &Type{ Object: Object{typ: meta, dict: dict}, name: name, basis: basis, bases: bases, flags: typeFlagDefault, } + t.self = t + return t } func newBasisType(name string, basis reflect.Type, basisFunc interface{}, base *Type) *Type { @@ -268,7 +270,14 @@ func mroCalc(t *Type) []*Type { } func toTypeUnsafe(o *Object) *Type { - return (*Type)(o.toPointer()) + if t, ok := o.self.(*Type); ok { + return t + } + // TODO: Check this for sanity... + if n, ok := o.self.(*nativeMetaclass); ok { + return &n.Type + } + return o.self.(*Type) // Make a properly useful panic } // ToObject upcasts t to an Object. @@ -335,6 +344,10 @@ var TypeType = &Type{ slots: typeSlots{Basis: &basisSlot{typeBasisFunc}}, } +func init() { + TypeType.self = TypeType +} + func typeCall(f *Frame, callable *Object, args Args, kwargs KWArgs) (*Object, *BaseException) { t := toTypeUnsafe(callable) newFunc := t.slots.New diff --git a/runtime/type_test.go b/runtime/type_test.go index ad968ee6..6d3b3ce4 100644 --- a/runtime/type_test.go +++ b/runtime/type_test.go @@ -23,7 +23,7 @@ import ( func TestNewClass(t *testing.T) { type strBasisStruct struct{ Str } - strBasisStructFunc := func(o *Object) *strBasisStruct { return (*strBasisStruct)(o.toPointer()) } + strBasisStructFunc := func(o *Object) *strBasisStruct { return o.self.(*strBasisStruct) } fooType := newBasisType("Foo", reflect.TypeOf(strBasisStruct{}), strBasisStructFunc, StrType) defer delete(basisTypes, fooType.basis) fooType.dict = NewDict() @@ -57,9 +57,12 @@ func TestNewClass(t *testing.T) { } } +type basisStruct struct{ Object } + +func (b *basisStruct) ToObject() *Object { return &b.Object } + func TestNewBasisType(t *testing.T) { - type basisStruct struct{ Object } - basisStructFunc := func(o *Object) *basisStruct { return (*basisStruct)(o.toPointer()) } + basisStructFunc := func(o *Object) *basisStruct { return o.self.(*basisStruct) } basis := reflect.TypeOf(basisStruct{}) typ := newBasisType("Foo", basis, basisStructFunc, ObjectType) defer delete(basisTypes, basis) @@ -79,7 +82,7 @@ func TestNewBasisType(t *testing.T) { if name := typ.Name(); name != "Foo" { t.Errorf(`Foo.Name() = %q, want "Foo"`, name) } - foo := (*basisStruct)(newObject(typ).toPointer()) + foo := newObject(typ).self.(*basisStruct) if typ.slots.Basis == nil { t.Error("type's Basis slot is nil") } else if got := typ.slots.Basis.Fn(&foo.Object); got.Type() != basis || got.Addr().Interface().(*basisStruct) != foo { @@ -136,9 +139,9 @@ func TestInvalidBasisType(t *testing.T) { func TestPrepareType(t *testing.T) { type objectBasisStruct struct{ Object } - objectBasisStructFunc := func(o *Object) *objectBasisStruct { return (*objectBasisStruct)(o.toPointer()) } + objectBasisStructFunc := func(o *Object) *objectBasisStruct { return o.self.(*objectBasisStruct) } type strBasisStruct struct{ Str } - strBasisStructFunc := func(o *Object) *strBasisStruct { return (*strBasisStruct)(o.toPointer()) } + strBasisStructFunc := func(o *Object) *strBasisStruct { return o.self.(*strBasisStruct) } cases := []struct { basis reflect.Type basisFunc interface{} @@ -334,6 +337,7 @@ func TestTypeGetAttribute(t *testing.T) { // __metaclass__ = BarMeta // bar = Bar() barType := &Type{Object: Object{typ: barMetaType}, name: "Bar", basis: fooType.basis, bases: []*Type{fooType}} + barType.self = barType barType.dict = newTestDict("bar", "Bar's bar", "foo", 101, "barsetter", setter, "barmetasetter", "NOT setter") bar := newObject(barType) prepareType(barType) diff --git a/runtime/unicode.go b/runtime/unicode.go index e7ba1f87..1e18872b 100644 --- a/runtime/unicode.go +++ b/runtime/unicode.go @@ -42,11 +42,13 @@ func NewUnicode(value string) *Unicode { // NewUnicodeFromRunes returns a new Unicode holding the given runes. func NewUnicodeFromRunes(value []rune) *Unicode { - return &Unicode{Object{typ: UnicodeType}, value} + u := &Unicode{Object{typ: UnicodeType}, value} + u.self = u + return u } func toUnicodeUnsafe(o *Object) *Unicode { - return (*Unicode)(o.toPointer()) + return o.self.(*Unicode) } // Encode translates the runes in s into a str with the given encoding. diff --git a/runtime/unicode_test.go b/runtime/unicode_test.go index ba7d8a29..37d22d0a 100644 --- a/runtime/unicode_test.go +++ b/runtime/unicode_test.go @@ -21,6 +21,11 @@ import ( "unicode" ) +func setUnicodeSelf(u *Unicode) *Unicode { + u.self = u + return u +} + func TestUnicodeNewUnicode(t *testing.T) { cases := []struct { s string @@ -256,7 +261,7 @@ func TestUnicodeNew(t *testing.T) { {args: wrapArgs(UnicodeType, "foo\xffbar", "utf8", "replace"), want: NewUnicode("foo\ufffdbar").ToObject()}, {args: wrapArgs(UnicodeType, "\xff", "utf-8", "noexist"), wantExc: mustCreateException(LookupErrorType, "unknown error handler name 'noexist'")}, {args: wrapArgs(UnicodeType, "\xff", "utf16"), wantExc: mustCreateException(LookupErrorType, "unknown encoding: utf16")}, - {args: wrapArgs(strictEqType, NewUnicode("foo")), want: (&Unicode{Object{typ: strictEqType}, bytes.Runes([]byte("foo"))}).ToObject()}, + {args: wrapArgs(strictEqType, NewUnicode("foo")), want: setUnicodeSelf(&Unicode{Object{typ: strictEqType}, bytes.Runes([]byte("foo"))}).ToObject()}, } for _, cas := range cases { if err := runInvokeMethodTestCase(UnicodeType, "__new__", &cas); err != "" { @@ -274,7 +279,7 @@ func TestUnicodeNewNotSubtype(t *testing.T) { func TestUnicodeNewSubclass(t *testing.T) { fooType := newTestClass("Foo", []*Type{UnicodeType}, NewDict()) - bar := (&Unicode{Object{typ: fooType}, bytes.Runes([]byte("bar"))}).ToObject() + bar := setUnicodeSelf(&Unicode{Object{typ: fooType}, bytes.Runes([]byte("bar"))}).ToObject() fun := wrapFuncForTest(func(f *Frame) *BaseException { got, raised := UnicodeType.Call(f, []*Object{bar}, nil) if raised != nil { diff --git a/runtime/weakref.go b/runtime/weakref.go index 16045be4..57ff618d 100644 --- a/runtime/weakref.go +++ b/runtime/weakref.go @@ -48,7 +48,7 @@ type WeakRef struct { } func toWeakRefUnsafe(o *Object) *WeakRef { - return (*WeakRef)(o.toPointer()) + return o.self.(*WeakRef) } // get returns r's referent, or nil if r is "dead". @@ -124,8 +124,9 @@ func weakRefNew(f *Frame, t *Type, args Args, _ KWArgs) (*Object, *BaseException r = (*WeakRef)(p) break } else { - r = &WeakRef{Object: Object{typ: WeakRefType}, ptr: uintptr(o.toPointer())} - if atomic.CompareAndSwapPointer(addr, nilPtr, r.toPointer()) { + r = &WeakRef{Object: Object{typ: WeakRefType}, ptr: reflect.ValueOf(o).Pointer()} + r.self = r + if atomic.CompareAndSwapPointer(addr, nilPtr, unsafe.Pointer(r)) { runtime.SetFinalizer(o, weakRefFinalizeReferent) break }