status-go/vendor/gopkg.in/olebedev/go-duktape.v3/debugger.go

304 lines
8.1 KiB
Go

package duktape
/*
#cgo !windows CFLAGS: -std=c99 -O3 -Wall -fomit-frame-pointer -fstrict-aliasing
#cgo windows CFLAGS: -O3 -Wall -fomit-frame-pointer -fstrict-aliasing
#cgo linux LDFLAGS: -lm
#cgo freebsd LDFLAGS: -lm
#cgo openbsd LDFLAGS: -lm
#include "duktape.h"
#include <stdbool.h>
#include <stdio.h>
extern duk_size_t goDebugReadFunction(void *uData, char *buffer, duk_size_t length);
extern duk_size_t goDebugWriteFunction(void *uData, char *buffer, duk_size_t length);
extern duk_size_t goDebugPeekFunction(void *uData);
extern void goDebugReadFlushFunction(void *uData);
extern void goDebugWriteFlushFunction(void *uData);
extern duk_idx_t goDebugRequestFunction(duk_context *ctx, void *uData, duk_idx_t nvalues);
extern void goDebugDetachedFunction(duk_context *ctx, void *uData);
static void _duk_debugger_attach(duk_context *ctx, bool peek, bool readFlush, bool writeFlush, bool request, void *uData) {
duk_debugger_attach(
ctx,
goDebugReadFunction,
((duk_size_t (*)(void*, const char*, duk_size_t)) goDebugWriteFunction),
peek ? goDebugPeekFunction : NULL,
readFlush ? goDebugReadFlushFunction : NULL,
writeFlush ? goDebugWriteFlushFunction : NULL,
request ? goDebugRequestFunction : NULL,
goDebugDetachedFunction,
uData
);
}
*/
import "C"
import (
"errors"
"fmt"
"reflect"
"sync"
"unsafe"
)
type DebugReadFunc = func(uData unsafe.Pointer, buffer []byte) uint
type DebugWriteFunc = func(uData unsafe.Pointer, buffer []byte) uint
type DebugPeekFunc = func(uData unsafe.Pointer) uint
type DebugReadFlushFunc = func(uData unsafe.Pointer)
type DebugWriteFlushFunc = func(uData unsafe.Pointer)
type DebugRequestFunc = func(ctx *Context, uData unsafe.Pointer, nValues int) int
type DebugDetachedFunc = func(ctx *Context, uData unsafe.Pointer)
type DebugNotifyFunc = func(ctx *Context) int
var DukDebuggerMaxAttachments = 64 // arbitrary number of max 64 debugger attachments :)
type attachment struct {
readFunc DebugReadFunc
writeFunc DebugWriteFunc
peekFunc DebugPeekFunc
readFlushFunc DebugReadFlushFunc
writeFlushFunc DebugWriteFlushFunc
requestFunc DebugRequestFunc
detachedFunc DebugDetachedFunc
uData unsafe.Pointer
}
type Debugger struct {
m sync.Mutex
attachments []*attachment
}
var creationMutex = sync.Mutex{}
var debugger *Debugger
// Returns the Duktape debugger instance which can be attached to
// multiple Duktape contexts using the Attach method
func DukDebugger() *Debugger {
if debugger != nil {
return debugger
}
creationMutex.Lock()
defer creationMutex.Unlock()
if debugger != nil {
return debugger
}
debugger = &Debugger{
m: sync.Mutex{},
attachments: make([]*attachment, DukDebuggerMaxAttachments),
}
return debugger
}
func (d *Debugger) newAttachment(readFunc DebugReadFunc,
writeFunc DebugWriteFunc,
peekFunc DebugPeekFunc,
readFlushFunc DebugReadFlushFunc,
writeFlushFunc DebugWriteFlushFunc,
requestFunc DebugRequestFunc,
detachedFunc DebugDetachedFunc,
uData interface{}) (int, error) {
d.m.Lock()
defer d.m.Unlock()
for i := 0; i < len(d.attachments); i++ {
if d.attachments[i] == nil {
d.attachments[i] = &attachment{
readFunc: readFunc,
writeFunc: writeFunc,
peekFunc: peekFunc,
readFlushFunc: readFlushFunc,
writeFlushFunc: writeFlushFunc,
requestFunc: requestFunc,
detachedFunc: detachedFunc,
uData: unsafe.Pointer(&uData),
}
return i, nil
}
}
return -1, errors.New("no more attachment slots available")
}
func (d *Debugger) removeAttachment(slot int) (*attachment, error) {
d.m.Lock()
defer d.m.Unlock()
if slot < 0 || slot >= len(d.attachments) {
return nil, errors.New("illegal attachment requested")
}
attachment := d.attachments[slot]
if attachment == nil {
return nil, errors.New("no attachment registered for requested attachment slot")
}
d.attachments[slot] = nil
return attachment, nil
}
func (d *Debugger) getAttachment(slot int) (*attachment, error) {
d.m.Lock()
defer d.m.Unlock()
if slot < 0 || slot >= len(d.attachments) {
return nil, errors.New(fmt.Sprintf("illegal attachment requested: %d", slot))
}
attachment := d.attachments[slot]
if attachment == nil {
return nil, errors.New("no attachment registered for requested attachment slot")
}
return attachment, nil
}
// See: http://duktape.org/api.html#duk_debugger_attach
//
// All parameters are optional, except for readFunc, writeFunc.
func (d *Debugger) Attach(ctx *Context,
readFunc DebugReadFunc,
writeFunc DebugWriteFunc,
peekFunc DebugPeekFunc,
readFlushFunc DebugReadFlushFunc,
writeFlushFunc DebugWriteFlushFunc,
requestFunc DebugRequestFunc,
detachedFunc DebugDetachedFunc,
uData interface{}) error {
if readFunc == nil {
return errors.New("readFunc cannot be nil")
}
if writeFunc == nil {
return errors.New("writeFunc cannot be nil")
}
slot, err := d.newAttachment(
readFunc,
writeFunc,
peekFunc,
readFlushFunc,
writeFlushFunc,
requestFunc,
detachedFunc,
uData,
)
if err != nil {
return err
}
var peek, readFlush, writeFlush, request C.bool = peekFunc != nil,
readFlushFunc != nil, writeFlushFunc != nil, requestFunc != nil
dData := slotToPtr(slot)
C._duk_debugger_attach(
ctx.duk_context,
peek,
readFlush,
writeFlush,
request,
dData,
)
return nil
}
// See: http://duktape.org/api.html#duk_debugger_detach
func (d *Debugger) Detach(ctx *Context) {
C.duk_debugger_detach(ctx.duk_context)
}
// See: http://duktape.org/api.html#duk_debugger_cooperate
func (d *Debugger) Cooperate(ctx *Context) {
C.duk_debugger_cooperate(ctx.duk_context)
}
// See: http://duktape.org/api.html#duk_debugger_pause
func (d *Debugger) Pause(ctx *Context) {
C.duk_debugger_pause(ctx.duk_context)
}
// See: http://duktape.org/api.html#duk_debugger_notify
func (d *Debugger) Notify(ctx *Context, notifyFunc DebugNotifyFunc) int {
nvalues := notifyFunc(ctx)
return (int)(C.duk_debugger_notify(ctx.duk_context, (C.duk_idx_t)(nvalues)))
}
//export goDebugReadFunction
func goDebugReadFunction(dData unsafe.Pointer, buffer *C.char, length C.duk_size_t) C.duk_size_t {
a := ptrToAttachment(dData)
b := ptrToSlice(buffer, length)
return (C.duk_size_t)(a.readFunc(a.uData, b))
}
//export goDebugWriteFunction
func goDebugWriteFunction(dData unsafe.Pointer, buffer *C.char, length C.duk_size_t) C.duk_size_t {
a := ptrToAttachment(dData)
b := ptrToSlice(buffer, length)
return (C.duk_size_t)(a.writeFunc(a.uData, b))
}
//export goDebugPeekFunction
func goDebugPeekFunction(dData unsafe.Pointer) C.duk_size_t {
a := ptrToAttachment(dData)
return (C.duk_size_t)(a.peekFunc(a.uData))
}
//export goDebugReadFlushFunction
func goDebugReadFlushFunction(dData unsafe.Pointer) {
a := ptrToAttachment(dData)
a.readFlushFunc(a.uData)
}
//export goDebugWriteFlushFunction
func goDebugWriteFlushFunction(dData unsafe.Pointer) {
a := ptrToAttachment(dData)
a.writeFlushFunc(a.uData)
}
//export goDebugRequestFunction
func goDebugRequestFunction(ctx *C.duk_context, dData unsafe.Pointer, nvalues C.duk_idx_t) C.duk_idx_t {
a := ptrToAttachment(dData)
d := contextFromPointer(ctx)
d.transmute(unsafe.Pointer(ctx))
return (C.duk_idx_t)(a.requestFunc(d, a.uData, int(nvalues)))
}
//export goDebugDetachedFunction
func goDebugDetachedFunction(ctx *C.duk_context, dData unsafe.Pointer) {
s := ptrToSlot(dData)
debugger = DukDebugger()
a, err := debugger.removeAttachment(s)
if err != nil {
panic(err)
}
defer C.free(dData)
d := contextFromPointer(ctx)
d.transmute(unsafe.Pointer(ctx))
if a.detachedFunc != nil {
a.detachedFunc(d, a.uData)
}
}
func ptrToSlice(buffer *C.char, length C.duk_size_t) []byte {
ptr := uintptr(unsafe.Pointer(buffer))
l := int(length)
header := reflect.SliceHeader{Data: ptr, Len: l, Cap: l}
return *(*[]byte)(unsafe.Pointer(&header))
}
func ptrToSlot(dData unsafe.Pointer) int {
return int(*(*int32)(dData))
}
func slotToPtr(slot int) unsafe.Pointer {
s := uint8(slot)
dData := C.malloc(C.size_t(unsafe.Sizeof(s)))
*(*C.uint8_t)(dData) = C.uint8_t(s)
return dData
}
func ptrToAttachment(dData unsafe.Pointer) *attachment {
slot := ptrToSlot(dData)
debugger := DukDebugger()
attachment, err := debugger.getAttachment(slot)
if err != nil {
panic(err)
}
return attachment
}