Skip to content

Commit

Permalink
Make cursor visitor abide to the cgo pointer passing rules
Browse files Browse the repository at this point in the history
Solution uses a callback registry map which stores the go function
values. GoClangCursorVisitor now accepts the id of the function
it must call in its arguments, instead of the function pointer
to the go function
Fixes #17
  • Loading branch information
doppioandante committed Sep 4, 2016
1 parent af27874 commit 78d39fd
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 8 deletions.
47 changes: 42 additions & 5 deletions cursor.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package clang
// #include "go-clang.h"
import "C"
import (
"sync"
"unsafe"
)

Expand Down Expand Up @@ -795,24 +796,60 @@ type CursorVisitor func(cursor, parent Cursor) (status ChildVisitResult)
*/
func (c Cursor) Visit(visitor CursorVisitor) bool {
forceEscapeVisitor = &visitor
o := C._go_clang_visit_children(c.c, unsafe.Pointer(&visitor))
id := visitorCallbacks.add(visitor)
defer visitorCallbacks.remove(id)

o := C._go_clang_visit_children(c.c, C.uintptr_t(id))
if o != C.uint(0) {
return false
}
return true
}

type visitorCallbackRegistry struct {
lock sync.Mutex
callbacks map[uintptr]CursorVisitor
generation uintptr
}

var visitorCallbacks = visitorCallbackRegistry{
callbacks: map[uintptr]CursorVisitor{},
}

func (r *visitorCallbackRegistry) add(cb CursorVisitor) uintptr {
r.lock.Lock()
defer r.lock.Unlock()

r.generation++
r.callbacks[r.generation] = cb
return r.generation
}

func (r *visitorCallbackRegistry) remove(id uintptr) {
r.lock.Lock()
defer r.lock.Unlock()

delete(r.callbacks, id)
}

func (r *visitorCallbackRegistry) get(id uintptr) CursorVisitor {
r.lock.Lock()
defer r.lock.Unlock()

return r.callbacks[id]
}

// forceEscapeVisitor is write-only: to force compiler to escape the address
// (else the address can become stale if the goroutine stack needs to grow
// and is forced to move)
// Explained by rsc in https://golang.org/issue/9125
var forceEscapeVisitor *CursorVisitor

//export GoClangCursorVisitor
func GoClangCursorVisitor(cursor, parent C.CXCursor, cfct unsafe.Pointer) (status ChildVisitResult) {
fct := *(*CursorVisitor)(cfct)
o := fct(Cursor{cursor}, Cursor{parent})
return o
func GoClangCursorVisitor(cursor, parent C.CXCursor, callback_id unsafe.Pointer) (status ChildVisitResult) {
id := uintptr(callback_id)
cb := visitorCallbacks.get(id)
return cb(Cursor{cursor}, Cursor{parent})
}

/**
Expand Down
2 changes: 1 addition & 1 deletion go-clang.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ _goclang_get_platform_availability_at(CXPlatformAvailability* array, int idx) {
return array[idx];
}

unsigned _go_clang_visit_children(CXCursor c, void *fct);
unsigned _go_clang_visit_children(CXCursor c, uintptr_t callback_id);

CXPlatformAvailability
_goclang_get_platform_availability_at(CXPlatformAvailability* array, int idx);
Expand Down
4 changes: 2 additions & 2 deletions visitorwrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
#include "go-clang.h"

unsigned
_go_clang_visit_children(CXCursor c, void *fct)
_go_clang_visit_children(CXCursor c, uintptr_t callback_id)
{
return clang_visitChildren(c, (CXCursorVisitor)&GoClangCursorVisitor, fct);
return clang_visitChildren(c, (CXCursorVisitor)&GoClangCursorVisitor, (CXClientData)callback_id);
}

/* EOF */

0 comments on commit 78d39fd

Please sign in to comment.