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 }