From b0dacf989ca8f7512955eee172f021ff082c70b4 Mon Sep 17 00:00:00 2001 From: Brian Atkinson Date: Fri, 6 Jan 2017 15:36:00 -0800 Subject: [PATCH] unsafe: Removes most of the `unsafe` casts. Rather than forcing casts from an *Object to the desired type, we store a "self" reference on the object to allow us to get back to the containing type. This does increase the amount of ram each object takes up (~2 more pointers), the chance of mis-casting code goes down dramatically. There is still a bad cast resulting from values coming out of WrapNative, and this has helped corner that cast. The actual fix is quite involved, but this should prevent similar problems in the future (hopefully). --- runtime/baseexception.go | 2 +- runtime/bool.go | 5 +++++ runtime/builtin_types.go | 3 ++- runtime/builtin_types_test.go | 3 ++- runtime/code.go | 8 ++++++-- runtime/core.go | 4 ++-- runtime/core_test.go | 6 +++--- runtime/descriptor.go | 6 ++++-- runtime/dict.go | 22 +++++++++++++++------- runtime/file.go | 3 ++- runtime/float.go | 6 ++++-- runtime/float_test.go | 10 +++++++--- runtime/frame.go | 3 ++- runtime/function.go | 16 +++++++++++----- runtime/generator.go | 6 ++++-- runtime/int.go | 24 +++++++++++------------- runtime/int_test.go | 10 +++++++--- runtime/list.go | 6 ++++-- runtime/long.go | 29 ++++++++++++++++++----------- runtime/long_test.go | 3 ++- runtime/method.go | 6 ++++-- runtime/module.go | 6 ++++-- runtime/module_test.go | 11 ++++++++--- runtime/native.go | 35 ++++++++++++++++++++++++++++------- runtime/native_test.go | 3 +++ runtime/object.go | 25 ++++++++++++++++++------- runtime/object_test.go | 7 ++++++- runtime/range.go | 12 ++++++++---- runtime/seq.go | 3 ++- runtime/set.go | 8 +++++--- runtime/set_test.go | 4 +++- runtime/slice.go | 2 +- runtime/slice_test.go | 10 +++++++--- runtime/str.go | 7 +++++-- runtime/str_test.go | 11 ++++++++--- runtime/super.go | 4 +++- runtime/traceback.go | 6 ++++-- runtime/tuple.go | 10 ++++++++-- runtime/type.go | 17 +++++++++++++++-- runtime/type_test.go | 16 ++++++++++------ runtime/unicode.go | 6 ++++-- runtime/unicode_test.go | 9 +++++++-- runtime/weakref.go | 7 ++++--- 43 files changed, 277 insertions(+), 123 deletions(-) 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 }