Skip to content

Commit

Permalink
No more pointers to CGO! Go 1.6!
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt Peterson committed Aug 19, 2016
1 parent 9cc0ed3 commit c7c80f7
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 42 deletions.
102 changes: 82 additions & 20 deletions class.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ const (
TPFLAGS_BASETYPE = uint32(C.Py_TPFLAGS_BASETYPE)
)

var goObjMap = make(map[*C.PyObject]Object)

// A Class struct instance is used to define a Python class that has been
// implemented in Go.
//
Expand Down Expand Up @@ -97,7 +99,7 @@ type Class struct {
New func(*Type, *Tuple, *Dict) (Object, error)
}

var otyp = reflect.TypeOf(new(Object)).Elem()
var otyp = reflect.TypeOf((*Object)(nil)).Elem()

//export goClassCallMethod
func goClassCallMethod(obj, unused unsafe.Pointer) unsafe.Pointer {
Expand Down Expand Up @@ -209,8 +211,17 @@ func goClassGetProp(obj, closure unsafe.Pointer) unsafe.Pointer {

//export goClassObjGet
func goClassObjGet(obj unsafe.Pointer, idx int) unsafe.Pointer {
goobj, ok := goObjMap[(*C.PyObject)(obj)]
if !ok {
// t := newType((*C.PyObject)(unsafe.Pointer(pyType)))
panic(fmt.Sprintf("TODO: not sure what to do? %v", idx))
}

goobjR := reflect.ValueOf(goobj)

field := getField(idx)
item := unsafe.Pointer(uintptr(obj) + field.Offset)
// item := unsafe.Pointer(uintptr(obj) + field.Offset)
item := unsafe.Pointer(goobjR.Pointer() + field.Offset)

var o Object

Expand All @@ -226,8 +237,17 @@ func goClassObjGet(obj unsafe.Pointer, idx int) unsafe.Pointer {

//export goClassObjSet
func goClassObjSet(obj unsafe.Pointer, idx int, obj2 unsafe.Pointer) int {
goobj, ok := goObjMap[(*C.PyObject)(obj)]
if !ok {
// t := newType((*C.PyObject)(unsafe.Pointer(pyType)))
panic(fmt.Sprintf("TODO: not sure what to do? %v", idx))
}

goobjR := reflect.ValueOf(goobj)

field := getField(idx)
item := unsafe.Pointer(uintptr(obj) + field.Offset)
// item := unsafe.Pointer(uintptr(obj) + field.Offset)
item := unsafe.Pointer(goobjR.Pointer() + field.Offset)

// This is the new value we are being asked to set
value := newObject((*C.PyObject)(obj2))
Expand All @@ -245,7 +265,7 @@ func goClassObjSet(obj unsafe.Pointer, idx int, obj2 unsafe.Pointer) int {
}

vt := reflect.TypeOf(value)
ov := reflect.NewAt(field.Type, unsafe.Pointer(item)).Elem()
ov := reflect.NewAt(field.Type, item).Elem()

// If the value is assignable to the field, then we do it, with the same
// refcount dance as above.
Expand All @@ -265,8 +285,17 @@ func goClassObjSet(obj unsafe.Pointer, idx int, obj2 unsafe.Pointer) int {

//export goClassNatGet
func goClassNatGet(obj unsafe.Pointer, idx int) unsafe.Pointer {
goobj, ok := goObjMap[(*C.PyObject)(obj)]
if !ok {
// t := newType((*C.PyObject)(unsafe.Pointer(pyType)))
panic(fmt.Sprintf("TODO: not sure what to do? %v", idx))
}

goobjR := reflect.ValueOf(goobj)

field := getField(idx)
item := unsafe.Pointer(uintptr(obj) + field.Offset)
// item := unsafe.Pointer(uintptr(obj) + field.Offset)
item := unsafe.Pointer(goobjR.Pointer() + field.Offset)

switch field.Type.Kind() {
case reflect.Int:
Expand All @@ -280,8 +309,17 @@ func goClassNatGet(obj unsafe.Pointer, idx int) unsafe.Pointer {

//export goClassNatSet
func goClassNatSet(obj unsafe.Pointer, idx int, obj2 unsafe.Pointer) int {
goobj, ok := goObjMap[(*C.PyObject)(obj)]
if !ok {
// t := newType((*C.PyObject)(unsafe.Pointer(pyType)))
panic(fmt.Sprintf("TODO: not sure what to do? %v", idx))
}

goobjR := reflect.ValueOf(goobj)

field := getField(idx)
item := unsafe.Pointer(uintptr(obj) + field.Offset)
// item := unsafe.Pointer(uintptr(obj) + field.Offset)
item := unsafe.Pointer(goobjR.Pointer() + field.Offset)

// This is the new value we are being asked to set
value := newObject((*C.PyObject)(obj2))
Expand Down Expand Up @@ -313,19 +351,31 @@ func goClassTraverse(obj, visit, arg unsafe.Pointer) int {
return -1
}

goobj, ok := goObjMap[(*C.PyObject)(obj)]
if !ok {
t := newType((*C.PyObject)(unsafe.Pointer(pyType)))
panic(fmt.Sprintf("TODO: not sure what to do? %s", t))
}

st := reflect.TypeOf(class.Pointer).Elem()
goobjT := reflect.TypeOf(goobj)
if goobjT.Elem() != st {
panic(fmt.Sprintf("Types don't match! class:%s, val:%s", st, goobjT.Elem()))
}

for i := 0; i < st.NumField(); i++ {
field := st.Field(i)
if !field.Type.AssignableTo(otyp) {
goobjR := reflect.ValueOf(goobj)

for i := 0; i < goobjR.NumField(); i++ {
field := goobjR.Field(i)
if !field.Type().AssignableTo(otyp) {
continue
}
v := unsafe.Pointer(uintptr(obj) + field.Offset)
// v := unsafe.Pointer(uintptr(obj) + field.Offset)
var o Object
if field.Type == otyp {
o = *(*Object)(v)
if field.Type() == otyp {
o = field.Interface().(Object)
} else {
o = *(**AbstractObject)(v)
o = field.Interface().(*AbstractObject)
}
ret := C.doVisit(c(o), visit, arg)
if ret != 0 {
Expand All @@ -348,21 +398,33 @@ func goClassClear(obj unsafe.Pointer) int {
return -1
}

goobj, ok := goObjMap[(*C.PyObject)(obj)]
if !ok {
t := newType((*C.PyObject)(unsafe.Pointer(pyType)))
panic(fmt.Sprintf("TODO: not sure what to do? %s", t))
}

st := reflect.TypeOf(class.Pointer).Elem()
goobjT := reflect.TypeOf(goobj)
if goobjT.Elem() != st {
panic(fmt.Sprintf("Types don't match! class:%s, val:%s", st, goobjT.Elem()))
}

goobjR := reflect.ValueOf(goobj)

for i := 0; i < st.NumField(); i++ {
field := st.Field(i)
if !field.Type.AssignableTo(otyp) {
for i := 0; i < goobjR.NumField(); i++ {
field := goobjR.Field(i)
if !field.Type().AssignableTo(otyp) {
continue
}
v := unsafe.Pointer(uintptr(obj) + field.Offset)
if field.Type == otyp {
o := (*Object)(v)
// v := unsafe.Pointer(uintptr(obj) + field.Offset)
if field.Type() == otyp {
o := field.Addr().Interface().(*Object)
tmp := *o
*o = nil
Decref(tmp)
} else {
o := (**AbstractObject)(v)
o := field.Addr().Interface().(**AbstractObject)
tmp := *o
*o = nil
Decref(tmp)
Expand Down
1 change: 0 additions & 1 deletion dict.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ func (d *Dict) SetItem(key, val Object) error {
// with a *String with the value of "key" will be used as the key). If "key" is
// not hashable, then a TypeError will be returned.
func (d *Dict) SetItemString(key string, val Object) error {
fmt.Printf("SetItemString: [%s] = %T\n", key, val)
s := C.CString(key)
defer C.free(unsafe.Pointer(s))
ret := C.PyDict_SetItemString(c(d), s, c(val))
Expand Down
34 changes: 21 additions & 13 deletions memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ import "sync"
// Some sizes that we need for various calculations
const (
upSize = unsafe.Sizeof(unsafe.Pointer(nil))
headSize = unsafe.Sizeof(C.PyGC_Head{})
baseSize = unsafe.Sizeof(C.PyObject{})
headSize = C.sizeof_PyGC_Head
baseSize = C.sizeof_PyObject
)

// We need to keep track of things that we have allocated, and the proxies that
// we have created, memLock must be locked whilst using these variables.
var (
memLock sync.Mutex
allocated = make(map[uintptr][]unsafe.Pointer)
memLock sync.Mutex
// allocated = make(map[uintptr][]unsafe.Pointer)
gcProxies = make(map[uintptr]*C.PyObject)
)

Expand All @@ -46,17 +46,19 @@ func goGcMalloc(size uintptr) *C.PyObject {
// We need to move the original tracked entry to be indexed by the offset
// address from fromGc
px := uintptr(unsafe.Pointer(p))
gx := uintptr(unsafe.Pointer(g))
allocated[px] = allocated[gx]
delete(allocated, gx)
// gx := uintptr(unsafe.Pointer(g))
// TODO!
// allocated[px] = allocated[gx]
// delete(allocated, gx)

// We can't access the internals of the GC Module to manipulate the
// generation counts, so we have to use a proxy object instead. We just
// create a bare minimum object, initialise it, and then store it away to be
// cleaned up later.
proxy := C._PyObject_GC_Malloc(C.size_t(baseSize))
if proxy == nil {
delete(allocated, px)
// TODO!
// delete(allocated, px)
return nil
}
C.GoPyObject_INIT(proxy, c(BaseType))
Expand All @@ -71,13 +73,18 @@ func _goMalloc(size uintptr) *C.PyObject {
// runtime will mark the memory as not containing pointers, and won't use it
// to pin other Go allocations in memory - in which case we might as well
// just stick with the Python allocator.
n := (size + upSize - 1) / upSize
s := make([]unsafe.Pointer, n)
p := unsafe.Pointer(&s[0])
// n := (size + upSize - 1) / upSize
// s := make([]unsafe.Pointer, n)
// p := unsafe.Pointer(&s[0])

// Go 1.6: Cannot return pointers to Go memory anymore!
// Instead we just keep a key into the [something] map and use that
// to get Go memory
p := C.malloc(C.size_t(size))

// We need to keep a reference to the allocation, so that the Go runtime
// doesn't free the memory before we are finished with it.
allocated[uintptr(p)] = s
// allocated[uintptr(p)] = s

return (*C.PyObject)(p)
}
Expand Down Expand Up @@ -134,7 +141,8 @@ func goGenericFree(o unsafe.Pointer) {

// Remove the entry from allocated, to let the Go runtime reclaim the memory
// in the next GC run.
delete(allocated, uintptr(o))
// TODO!
// delete(allocated, uintptr(o))

// We need to also delete the proxy, so that the Python GC counts get
// updated as appropriate.
Expand Down
7 changes: 1 addition & 6 deletions module.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@ package py
// static inline void decref(PyObject *obj) { Py_DECREF(obj); }
import "C"

import (
"fmt"
"unsafe"
)
import "unsafe"

type Module struct {
AbstractObject
Expand Down Expand Up @@ -142,8 +139,6 @@ func (mod *Module) AddObject(name string, obj Object) error {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))

fmt.Printf("AddObject: [%s] = %T\n", name, obj)

ret := C.PyModule_AddObject(c(mod), cname, c(obj))
if ret < 0 {
return exception()
Expand Down
18 changes: 16 additions & 2 deletions object.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ package py
// static inline int exceptionCheck(PyObject *obj) {
// return PyExceptionClass_Check(obj);
// }
// static inline PyObject *mycgocheck(PyObject *obj) {
// return obj;
// }
import "C"

import (
Expand Down Expand Up @@ -65,7 +68,12 @@ func c(obj Object) *C.PyObject {
if b.o == nil {
panic(fmt.Sprintf("nil! %T", obj))
}
return obj.Base().o
defer func() {
if r := recover(); r != nil {
panic(fmt.Sprintf("Panic in c() for %T: %v", obj, r))
}
}()
return C.mycgocheck(obj.Base().o)
}

func stringify(obj Object) string {
Expand Down Expand Up @@ -101,7 +109,9 @@ func obj2Class(c *Class, obj *C.PyObject) (Object, bool) {
// vp := reflect.NewAt(reflect.TypeOf(c.Pointer), unsafe.Pointer(&obj))
vp := reflect.New(reflect.TypeOf(c.Pointer).Elem())
o, ok := vp.Interface().(Object)
o.Base().o = obj
if ok {
o.Base().o = obj
}
return o, ok
}

Expand All @@ -114,6 +124,10 @@ func newObject(obj *C.PyObject) Object {
return None
}

if goobj, ok := goObjMap[obj]; ok {
return goobj
}

pyType := (*C.PyTypeObject)(obj.ob_type)
class, ok := getType(pyType)
if ok {
Expand Down

0 comments on commit c7c80f7

Please sign in to comment.