gopkg.in/olebedev/go-duktape.v1@v1.0.0-20151008052556-e2ae92f01e4a/duktape.go (about)

     1  package duktape
     2  
     3  /*
     4  #cgo linux LDFLAGS: -lm
     5  #cgo CFLAGS: -DDUK_OPT_DEEP_C_STACK
     6  
     7  # include "duktape.h"
     8  extern duk_ret_t goFunctionCall(duk_context *ctx);
     9  extern void goFinalizeCall(duk_context *ctx);
    10  
    11  */
    12  import "C"
    13  import (
    14  	"errors"
    15  	"fmt"
    16  	"regexp"
    17  	"sync"
    18  	"unsafe"
    19  )
    20  
    21  var reFuncName = regexp.MustCompile("^[a-z_][a-z0-9_]*([A-Z_][a-z0-9_]*)*$")
    22  
    23  const (
    24  	goFunctionPtrProp = "\xff" + "goFunctionPtrProp"
    25  	goContextPtrProp  = "\xff" + "goContextPtrProp"
    26  )
    27  
    28  type Context struct {
    29  	*context
    30  }
    31  
    32  // transmute replaces the value from Context with the value of pointer
    33  func (c *Context) transmute(p unsafe.Pointer) {
    34  	*c = *(*Context)(p)
    35  }
    36  
    37  // this is a pojo containing only the values of the Context
    38  type context struct {
    39  	sync.Mutex
    40  	duk_context unsafe.Pointer
    41  	fnIndex     *functionIndex
    42  	timerIndex  *timerIndex
    43  }
    44  
    45  // New returns plain initialized duktape context object
    46  // See: http://duktape.org/api.html#duk_create_heap_default
    47  func New() *Context {
    48  	return &Context{
    49  		&context{
    50  			duk_context: C.duk_create_heap(nil, nil, nil, nil, nil),
    51  			fnIndex:     newFunctionIndex(),
    52  			timerIndex:  &timerIndex{},
    53  		},
    54  	}
    55  }
    56  
    57  func contextFromPointer(ctx unsafe.Pointer) *Context {
    58  	return &Context{&context{duk_context: ctx}}
    59  }
    60  
    61  // PushGlobalGoFunction push the given function into duktape global object
    62  // Returns non-negative index (relative to stack bottom) of the pushed function
    63  // also returns error if the function name is invalid
    64  func (d *Context) PushGlobalGoFunction(name string, fn func(*Context) int) (int, error) {
    65  	if !reFuncName.MatchString(name) {
    66  		return -1, errors.New("Malformed function name '" + name + "'")
    67  	}
    68  
    69  	d.PushGlobalObject()
    70  	idx := d.PushGoFunction(fn)
    71  	d.PutPropString(-2, name)
    72  	d.Pop()
    73  
    74  	return idx, nil
    75  }
    76  
    77  // PushGoFunction push the given function into duktape stack, returns non-negative
    78  // index (relative to stack bottom) of the pushed function
    79  func (d *Context) PushGoFunction(fn func(*Context) int) int {
    80  	funPtr := d.fnIndex.Add(fn)
    81  	ctxPtr := unsafe.Pointer(d)
    82  
    83  	idx := d.PushCFunction((*[0]byte)(C.goFunctionCall), C.DUK_VARARGS)
    84  	d.PushCFunction((*[0]byte)(C.goFinalizeCall), 1)
    85  	d.PushPointer(funPtr)
    86  	d.PutPropString(-2, goFunctionPtrProp)
    87  	d.PushPointer(ctxPtr)
    88  	d.PutPropString(-2, goContextPtrProp)
    89  	d.SetFinalizer(-2)
    90  
    91  	d.PushPointer(funPtr)
    92  	d.PutPropString(-2, goFunctionPtrProp)
    93  	d.PushPointer(ctxPtr)
    94  	d.PutPropString(-2, goContextPtrProp)
    95  
    96  	return idx
    97  }
    98  
    99  //export goFunctionCall
   100  func goFunctionCall(cCtx unsafe.Pointer) C.duk_ret_t {
   101  	d := contextFromPointer(cCtx)
   102  
   103  	funPtr, ctxPtr := d.getFunctionPtrs()
   104  	d.transmute(ctxPtr)
   105  
   106  	result := d.fnIndex.Get(funPtr)(d)
   107  
   108  	return C.duk_ret_t(result)
   109  }
   110  
   111  //export goFinalizeCall
   112  func goFinalizeCall(cCtx unsafe.Pointer) {
   113  	d := contextFromPointer(cCtx)
   114  
   115  	funPtr, ctxPtr := d.getFunctionPtrs()
   116  	d.transmute(ctxPtr)
   117  
   118  	d.fnIndex.Delete(funPtr)
   119  }
   120  
   121  func (d *Context) getFunctionPtrs() (funPtr, ctxPtr unsafe.Pointer) {
   122  	d.PushCurrentFunction()
   123  	d.GetPropString(-1, goFunctionPtrProp)
   124  	funPtr = d.GetPointer(-1)
   125  
   126  	d.Pop()
   127  
   128  	d.GetPropString(-1, goContextPtrProp)
   129  	ctxPtr = d.GetPointer(-1)
   130  
   131  	d.Pop2()
   132  	return
   133  }
   134  
   135  // Destroy destroy all the references to the functions and freed the pointers
   136  func (d *Context) Destroy() {
   137  	d.fnIndex.Destroy()
   138  }
   139  
   140  type Error struct {
   141  	Type       string
   142  	Message    string
   143  	FileName   string
   144  	LineNumber int
   145  	Stack      string
   146  }
   147  
   148  func (e *Error) Error() string {
   149  	return fmt.Sprintf("%s: %s", e.Type, e.Message)
   150  }
   151  
   152  type Type int
   153  
   154  func (t Type) IsNone() bool      { return t == TypeNone }
   155  func (t Type) IsUndefined() bool { return t == TypeUndefined }
   156  func (t Type) IsNull() bool      { return t == TypeNull }
   157  func (t Type) IsBool() bool      { return t == TypeBoolean }
   158  func (t Type) IsNumber() bool    { return t == TypeNumber }
   159  func (t Type) IsString() bool    { return t == TypeString }
   160  func (t Type) IsObject() bool    { return t == TypeObject }
   161  func (t Type) IsBuffer() bool    { return t == TypeBuffer }
   162  func (t Type) IsPointer() bool   { return t == TypePointer }
   163  func (t Type) IsLightFunc() bool { return t == TypeLightFunc }
   164  
   165  func (t Type) String() string {
   166  	switch t {
   167  	case TypeNone:
   168  		return "None"
   169  	case TypeUndefined:
   170  		return "Undefined"
   171  	case TypeNull:
   172  		return "Null"
   173  	case TypeBoolean:
   174  		return "Boolean"
   175  	case TypeNumber:
   176  		return "Number"
   177  	case TypeString:
   178  		return "String"
   179  	case TypeObject:
   180  		return "Object"
   181  	case TypeBuffer:
   182  		return "Buffer"
   183  	case TypePointer:
   184  		return "Pointer"
   185  	case TypeLightFunc:
   186  		return "LightFunc"
   187  	default:
   188  		return "Unknown"
   189  	}
   190  }
   191  
   192  type functionIndex struct {
   193  	functions map[unsafe.Pointer]func(*Context) int
   194  	sync.Mutex
   195  }
   196  
   197  type timerIndex struct {
   198  	c float64
   199  	sync.Mutex
   200  }
   201  
   202  func (t *timerIndex) get() float64 {
   203  	t.Lock()
   204  	defer t.Unlock()
   205  	t.c++
   206  	return t.c
   207  }
   208  
   209  func newFunctionIndex() *functionIndex {
   210  	return &functionIndex{
   211  		functions: make(map[unsafe.Pointer]func(*Context) int, 0),
   212  	}
   213  }
   214  
   215  func (i *functionIndex) Add(fn func(*Context) int) unsafe.Pointer {
   216  	ptr := C.malloc(1)
   217  
   218  	i.Lock()
   219  	defer i.Unlock()
   220  	i.functions[ptr] = fn
   221  
   222  	return ptr
   223  }
   224  
   225  func (i *functionIndex) Get(ptr unsafe.Pointer) func(*Context) int {
   226  	i.Lock()
   227  	defer i.Unlock()
   228  
   229  	return i.functions[ptr]
   230  }
   231  
   232  func (i *functionIndex) Delete(ptr unsafe.Pointer) {
   233  	i.Lock()
   234  	defer i.Unlock()
   235  
   236  	delete(i.functions, ptr)
   237  	C.free(ptr)
   238  }
   239  
   240  func (i *functionIndex) Destroy() {
   241  	i.Lock()
   242  	defer i.Unlock()
   243  
   244  	for ptr, _ := range i.functions {
   245  		delete(i.functions, ptr)
   246  		C.free(ptr)
   247  	}
   248  }