Merge pull request #19 from deuill/feature/go16-compatibility

Compatibility fixes for Go 1.6
This commit is contained in:
Alex Palaistras 2016-02-27 22:36:34 +00:00
commit d9b0735a69
17 changed files with 303 additions and 241 deletions

View File

@ -11,7 +11,7 @@
#include "value.h"
#include "context.h"
engine_context *context_new(void *ctx) {
engine_context *context_new() {
engine_context *context;
// Initialize context.
@ -21,7 +21,6 @@ engine_context *context_new(void *ctx) {
return NULL;
}
context->ctx = ctx;
SG(server_context) = context;
// Initialize request lifecycle.

View File

@ -9,7 +9,6 @@ package engine
//
// #include <stdlib.h>
// #include <main/php.h>
// #include "receiver.h"
// #include "context.h"
import "C"
@ -31,9 +30,8 @@ type Context struct {
// Header represents the HTTP headers set by current PHP context.
Header http.Header
context *C.struct__engine_context
values []*Value
receivers map[string]*Receiver
context *C.struct__engine_context
values []*Value
}
// Bind allows for binding Go values into the current execution context under
@ -55,34 +53,6 @@ func (c *Context) Bind(name string, val interface{}) error {
return nil
}
// Define registers a PHP class for the name passed, using function fn as
// constructor for individual object instances as needed by the PHP context.
//
// The class name registered is assumed to be unique for the active engine.
//
// The constructor function accepts a slice of arguments, as passed by the PHP
// context, and should return a method receiver instance, or nil on error (in
// which case, an exception is thrown on the PHP object constructor).
func (c *Context) Define(name string, fn func(args []interface{}) interface{}) error {
if _, exists := c.receivers[name]; exists {
return fmt.Errorf("Failed to define duplicate receiver '%s'", name)
}
rcvr := &Receiver{
name: name,
create: fn,
objects: make([]*ReceiverObject, 0),
}
n := C.CString(name)
defer C.free(unsafe.Pointer(n))
C.receiver_define(n, unsafe.Pointer(rcvr))
c.receivers[name] = rcvr
return nil
}
// Exec executes a PHP script pointed to by filename in the current execution
// context, and returns an error, if any. Output produced by the script is
// written to the context's pre-defined io.Writer instance.
@ -129,12 +99,6 @@ func (c *Context) Destroy() {
return
}
for _, r := range c.receivers {
r.Destroy()
}
c.receivers = nil
for _, v := range c.values {
v.Destroy()
}

View File

@ -31,28 +31,6 @@ func TestContextNew(t *testing.T) {
c.Destroy()
}
func TestContextDefine(t *testing.T) {
ctor := func(args []interface{}) interface{} {
return nil
}
c, _ := e.NewContext()
if err := c.Define("TestDefine", ctor); err != nil {
t.Errorf("Context.Define(): %s", err)
}
if len(c.receivers) != 1 {
t.Errorf("Context.Define(): `Context.receivers` length is %d, should be 1", len(c.receivers))
}
if err := c.Define("TestDefine", ctor); err == nil {
t.Errorf("Context.Define(): Incorrectly defined duplicate receiver")
}
c.Destroy()
}
var execTests = []struct {
name string
script string

View File

@ -33,7 +33,7 @@ const char engine_ini_defaults[] = {
static ENGINE_UB_WRITE(str, len) {
engine_context *context = SG(server_context);
int written = engineWriteOut(context->ctx, (void *) str, len);
int written = engineWriteOut(context, (void *) str, len);
if (written != len) {
php_handle_aborted_connection();
}
@ -48,7 +48,7 @@ static int engine_header_handler(sapi_header_struct *sapi_header, sapi_header_op
case SAPI_HEADER_REPLACE:
case SAPI_HEADER_ADD:
case SAPI_HEADER_DELETE:
engineSetHeader(context->ctx, op, (void *) sapi_header->header, sapi_header->header_len);
engineSetHeader(context, op, (void *) sapi_header->header, sapi_header->header_len);
break;
}
@ -70,7 +70,7 @@ static void engine_register_variables(zval *track_vars_array) {
static void engine_log_message(char *str) {
engine_context *context = SG(server_context);
engineWriteLog(context->ctx, (void *) str, strlen(str));
engineWriteLog(context, (void *) str, strlen(str));
}
static sapi_module_struct engine_module = {

View File

@ -11,6 +11,7 @@ package engine
//
// #include <stdlib.h>
// #include <main/php.h>
// #include "receiver.h"
// #include "context.h"
// #include "engine.h"
import "C"
@ -25,53 +26,96 @@ import (
// Engine represents the core PHP engine bindings.
type Engine struct {
engine *C.struct__php_engine
contexts []*Context
engine *C.struct__php_engine
contexts map[*C.struct__engine_context]*Context
receivers map[string]*Receiver
}
// This contains a reference to the active engine, if any.
var engine *Engine
// New initializes a PHP engine instance on which contexts can be executed. It
// corresponds to PHP's MINIT (module init) phase.
func New() (*Engine, error) {
if engine != nil {
return nil, fmt.Errorf("Cannot activate multiple engine instances")
}
ptr, err := C.engine_init()
if err != nil {
return nil, fmt.Errorf("PHP engine failed to initialize")
}
e := &Engine{
engine: ptr,
contexts: make([]*Context, 0),
engine = &Engine{
engine: ptr,
contexts: make(map[*C.struct__engine_context]*Context),
receivers: make(map[string]*Receiver),
}
return e, nil
return engine, nil
}
// NewContext creates a new execution context for the active engine and returns
// an error if the execution context failed to initialize at any point. This
// corresponds to PHP's RINIT (request init) phase.
func (e *Engine) NewContext() (*Context, error) {
ctx := &Context{
Header: make(http.Header),
values: make([]*Value, 0),
receivers: make(map[string]*Receiver),
}
ptr, err := C.context_new(unsafe.Pointer(ctx))
ptr, err := C.context_new()
if err != nil {
return nil, fmt.Errorf("Failed to initialize context for PHP engine")
}
ctx.context = ptr
e.contexts = append(e.contexts, ctx)
ctx := &Context{
Header: make(http.Header),
context: ptr,
values: make([]*Value, 0),
}
// Store reference to context, using pointer as key.
e.contexts[ptr] = ctx
return ctx, nil
}
// Define registers a PHP class for the name passed, using function fn as
// constructor for individual object instances as needed by the PHP context.
//
// The class name registered is assumed to be unique for the active engine.
//
// The constructor function accepts a slice of arguments, as passed by the PHP
// context, and should return a method receiver instance, or nil on error (in
// which case, an exception is thrown on the PHP object constructor).
func (e *Engine) Define(name string, fn func(args []interface{}) interface{}) error {
if _, exists := e.receivers[name]; exists {
return fmt.Errorf("Failed to define duplicate receiver '%s'", name)
}
rcvr := &Receiver{
name: name,
create: fn,
objects: make(map[*C.struct__engine_receiver]*ReceiverObject),
}
n := C.CString(name)
defer C.free(unsafe.Pointer(n))
C.receiver_define(n)
e.receivers[name] = rcvr
return nil
}
// Destroy shuts down and frees any resources related to the PHP engine bindings.
func (e *Engine) Destroy() {
if e.engine == nil {
return
}
for _, r := range e.receivers {
r.Destroy()
}
e.receivers = nil
for _, c := range e.contexts {
c.Destroy()
}
@ -80,6 +124,8 @@ func (e *Engine) Destroy() {
C.engine_shutdown(e.engine)
e.engine = nil
engine = nil
}
func write(w io.Writer, buffer unsafe.Pointer, length C.uint) C.int {
@ -97,22 +143,28 @@ func write(w io.Writer, buffer unsafe.Pointer, length C.uint) C.int {
}
//export engineWriteOut
func engineWriteOut(ctxptr, buffer unsafe.Pointer, length C.uint) C.int {
c := (*Context)(ctxptr)
func engineWriteOut(ctx *C.struct__engine_context, buffer unsafe.Pointer, length C.uint) C.int {
if engine == nil || engine.contexts[ctx] == nil {
return -1
}
return write(c.Output, buffer, length)
return write(engine.contexts[ctx].Output, buffer, length)
}
//export engineWriteLog
func engineWriteLog(ctxptr unsafe.Pointer, buffer unsafe.Pointer, length C.uint) C.int {
c := (*Context)(ctxptr)
func engineWriteLog(ctx *C.struct__engine_context, buffer unsafe.Pointer, length C.uint) C.int {
if engine == nil || engine.contexts[ctx] == nil {
return -1
}
return write(c.Log, buffer, length)
return write(engine.contexts[ctx].Log, buffer, length)
}
//export engineSetHeader
func engineSetHeader(ctxptr unsafe.Pointer, operation C.uint, buffer unsafe.Pointer, length C.uint) {
c := (*Context)(ctxptr)
func engineSetHeader(ctx *C.struct__engine_context, operation C.uint, buffer unsafe.Pointer, length C.uint) {
if engine == nil || engine.contexts[ctx] == nil {
return
}
header := (string)(C.GoBytes(buffer, C.int(length)))
split := strings.SplitN(header, ":", 2)
@ -124,15 +176,106 @@ func engineSetHeader(ctxptr unsafe.Pointer, operation C.uint, buffer unsafe.Poin
switch operation {
case 0: // Replace header.
if len(split) == 2 && split[1] != "" {
c.Header.Set(split[0], split[1])
engine.contexts[ctx].Header.Set(split[0], split[1])
}
case 1: // Append header.
if len(split) == 2 && split[1] != "" {
c.Header.Add(split[0], split[1])
engine.contexts[ctx].Header.Add(split[0], split[1])
}
case 2: // Delete header.
if split[0] != "" {
c.Header.Del(split[0])
engine.contexts[ctx].Header.Del(split[0])
}
}
}
//export engineReceiverNew
func engineReceiverNew(rcvr *C.struct__engine_receiver, args unsafe.Pointer) C.int {
n := C.GoString(C.receiver_get_name(rcvr))
if engine == nil || engine.receivers[n] == nil {
return 1
}
va, err := NewValueFromPtr(args)
if err != nil {
return 1
}
defer va.Destroy()
obj, err := engine.receivers[n].NewObject(va.Slice())
if err != nil {
return 1
}
engine.receivers[n].objects[rcvr] = obj
return 0
}
//export engineReceiverGet
func engineReceiverGet(rcvr *C.struct__engine_receiver, name *C.char) unsafe.Pointer {
n := C.GoString(C.receiver_get_name(rcvr))
if engine == nil || engine.receivers[n].objects[rcvr] == nil {
return nil
}
val, err := engine.receivers[n].objects[rcvr].Get(C.GoString(name))
if err != nil {
return nil
}
return val.Ptr()
}
//export engineReceiverSet
func engineReceiverSet(rcvr *C.struct__engine_receiver, name *C.char, val unsafe.Pointer) {
n := C.GoString(C.receiver_get_name(rcvr))
if engine == nil || engine.receivers[n].objects[rcvr] == nil {
return
}
v, err := NewValueFromPtr(val)
if err != nil {
return
}
engine.receivers[n].objects[rcvr].Set(C.GoString(name), v.Interface())
}
//export engineReceiverExists
func engineReceiverExists(rcvr *C.struct__engine_receiver, name *C.char) C.int {
n := C.GoString(C.receiver_get_name(rcvr))
if engine == nil || engine.receivers[n].objects[rcvr] == nil {
return 0
}
if engine.receivers[n].objects[rcvr].Exists(C.GoString(name)) {
return 1
}
return 0
}
//export engineReceiverCall
func engineReceiverCall(rcvr *C.struct__engine_receiver, name *C.char, args unsafe.Pointer) unsafe.Pointer {
n := C.GoString(C.receiver_get_name(rcvr))
if engine == nil || engine.receivers[n].objects[rcvr] == nil {
return nil
}
// Process input arguments.
va, err := NewValueFromPtr(args)
if err != nil {
return nil
}
defer va.Destroy()
val := engine.receivers[n].objects[rcvr].Call(C.GoString(name), va.Slice())
if val == nil {
return nil
}
return val.Ptr()
}

View File

@ -44,7 +44,7 @@ func TestEngineNew(t *testing.T) {
t.Fatalf("New(): %s", err)
}
if e.engine == nil || e.contexts == nil {
if e.engine == nil || e.contexts == nil || e.receivers == nil {
t.Fatalf("New(): Struct fields are `nil` but no error returned")
}
}
@ -60,10 +60,28 @@ func TestEngineNewContext(t *testing.T) {
}
}
func TestEngineDefine(t *testing.T) {
ctor := func(args []interface{}) interface{} {
return nil
}
if err := e.Define("TestDefine", ctor); err != nil {
t.Errorf("Engine.Define(): %s", err)
}
if len(e.receivers) != 1 {
t.Errorf("Engine.Define(): `Engine.receivers` length is %d, should be 1", len(e.receivers))
}
if err := e.Define("TestDefine", ctor); err == nil {
t.Errorf("Engine.Define(): Incorrectly defined duplicate receiver")
}
}
func TestEngineDestroy(t *testing.T) {
e.Destroy()
if e.engine != nil || e.contexts != nil {
if e.engine != nil || e.contexts != nil || e.receivers != nil {
t.Errorf("Engine.Destroy(): Did not set internal fields to `nil`")
}

View File

@ -5,16 +5,15 @@
#ifndef __CONTEXT_H__
#define __CONTEXT_H__
#include "_context.h"
typedef struct _engine_context {
void *ctx;
} engine_context;
engine_context *context_new(void *ctx);
engine_context *context_new();
void context_exec(engine_context *context, char *filename);
void *context_eval(engine_context *context, char *script);
void context_bind(engine_context *context, char *name, void *value);
void context_destroy(engine_context *context);
#include "_context.h"
#endif

View File

@ -5,12 +5,12 @@
#ifndef __ENGINE_H__
#define __ENGINE_H__
#include "_engine.h"
typedef struct _php_engine {
} php_engine;
php_engine *engine_init(void);
void engine_shutdown(php_engine *engine);
#include "_engine.h"
#endif

View File

@ -59,4 +59,9 @@ static inline zval *RECEIVER_RETVAL() {
h.get_class_entry = std->get_class_entry; \
} while (0)
// Return class name for method receiver.
static inline char *receiver_get_name(engine_receiver *rcvr) {
return rcvr->obj.ce->name;
}
#endif

View File

@ -15,10 +15,6 @@
ZVAL_NULL(v); \
} while (0)
#define VALUE_FREE(v) do { \
zval_dtor(v); \
} while (0)
#define HASH_GET_CURRENT_KEY(h, k, i) zend_hash_get_current_key(h, k, i, 0)
#define HASH_SET_CURRENT_KEY(h, v) do { \
zval *t; \
@ -52,4 +48,10 @@
return v; \
} while (0)
// Destroy and free engine value.
static inline void value_destroy(engine_value *val) {
zval_dtor(val->internal);
free(val);
}
#endif

View File

@ -50,4 +50,9 @@
h.free_obj = receiver_free; \
} while (0)
// Return class name for method receiver.
static inline char *receiver_get_name(engine_receiver *rcvr) {
return rcvr->obj.ce->name->val;
}
#endif

View File

@ -15,11 +15,6 @@
ZVAL_NULL(v); \
} while (0)
#define VALUE_FREE(v) do { \
zval_dtor(v); \
free(v); \
} while (0)
#define HASH_GET_CURRENT_KEY(h, k, i) zend_hash_get_current_key(h, k, i)
#define HASH_SET_CURRENT_KEY(h, v) do { \
zval t; \
@ -54,4 +49,11 @@
return v; \
} while (0)
// Destroy and free engine value.
static inline void value_destroy(engine_value *val) {
zval_dtor(val->internal);
free(val->internal);
free(val);
}
#endif

View File

@ -5,21 +5,13 @@
#ifndef __RECEIVER_H__
#define __RECEIVER_H__
#include "_receiver.h"
#define RECEIVER_POINTER(ce, name) \
(void *) Z_LVAL_P(zend_read_static_property(ce, name, sizeof(name) - 1, 1))
#define RECEIVER_POINTER_SET(ce, name, ptr) \
zend_declare_property_long(ce, name, sizeof(name) - 1, (long int) ptr, \
ZEND_ACC_STATIC | ZEND_ACC_PRIVATE)
typedef struct _engine_receiver {
zend_object obj;
void *rcvr;
} engine_receiver;
void receiver_define(char *name, void *rcvr);
void receiver_define(char *name);
void receiver_destroy(char *name);
#include "_receiver.h"
#endif

View File

@ -5,8 +5,6 @@
#ifndef __VALUE_H__
#define __VALUE_H__
#include "_value.h"
typedef struct _engine_value {
zval *internal;
int kind;
@ -28,14 +26,8 @@ static inline void value_copy(zval *dst, zval *src) {
zval_copy_ctor(dst);
}
static inline void value_destroy(engine_value *val) {
VALUE_FREE(val->internal);
free(val);
}
engine_value *value_new();
int value_kind(engine_value *val);
void value_destroy(engine_value *val);
void value_set_null(engine_value *val);
void value_set_long(engine_value *val, long int num);
@ -63,4 +55,6 @@ engine_value *value_array_next_get(engine_value *arr);
engine_value *value_array_index_get(engine_value *arr, unsigned long idx);
engine_value *value_array_key_get(engine_value *arr, char *key);
#include "_value.h"
#endif

View File

@ -18,7 +18,7 @@ static zval *RECEIVER_GET(zval *object, zval *member) {
engine_receiver *this = RECEIVER_THIS(object);
zval *val = RECEIVER_RETVAL();
engine_value *result = receiverGet(this->rcvr, Z_STRVAL_P(member));
engine_value *result = engineReceiverGet(this, Z_STRVAL_P(member));
if (result == NULL) {
ZVAL_NULL(val);
return val;
@ -33,14 +33,14 @@ static zval *RECEIVER_GET(zval *object, zval *member) {
// Set field for method receiver.
static void RECEIVER_SET(zval *object, zval *member, zval *value) {
engine_receiver *this = RECEIVER_THIS(object);
receiverSet(this->rcvr, Z_STRVAL_P(member), (void *) value);
engineReceiverSet(this, Z_STRVAL_P(member), (void *) value);
}
// Check if field exists for method receiver.
static int RECEIVER_EXISTS(zval *object, zval *member, int check) {
engine_receiver *this = RECEIVER_THIS(object);
if (!receiverExists(this->rcvr, Z_STRVAL_P(member))) {
if (!engineReceiverExists(this, Z_STRVAL_P(member))) {
// Value does not exist.
return 0;
} else if (check == 2) {
@ -49,7 +49,7 @@ static int RECEIVER_EXISTS(zval *object, zval *member, int check) {
}
int result = 0;
engine_value *val = receiverGet(this->rcvr, Z_STRVAL_P(member));
engine_value *val = engineReceiverGet(this, Z_STRVAL_P(member));
if (check == 1) {
// Value exists and is "truthy".
@ -78,7 +78,7 @@ static int RECEIVER_METHOD_CALL(method) {
if (zend_copy_parameters_array(ZEND_NUM_ARGS(), &args) == FAILURE) {
RETVAL_NULL();
} else {
engine_value *result = receiverCall(this->rcvr, name, (void *) &args);
engine_value *result = engineReceiverCall(this, name, (void *) &args);
if (result == NULL) {
RETVAL_NULL();
} else {
@ -102,10 +102,8 @@ static void receiver_new(INTERNAL_FUNCTION_PARAMETERS) {
zend_throw_exception(NULL, "Could not parse parameters for method receiver", 0);
} else {
// Create receiver instance. Throws an exception if creation fails.
void *ctor = RECEIVER_POINTER(this->obj.ce, "__rcvr__");
this->rcvr = receiverNew(ctor, (void *) &args);
if (this->rcvr == NULL) {
int result = engineReceiverNew(this, (void *) &args);
if (result != 0) {
zend_throw_exception(NULL, "Failed to instantiate method receiver", 0);
}
}
@ -191,8 +189,8 @@ static RECEIVER_INIT(zend_class_entry *class_type) {
RECEIVER_OBJECT_CREATE(this, class_type);
}
// Define class with unique name, using `rcvr` as the method receiver prototype.
void receiver_define(char *name, void *rcvr) {
// Define class with unique name.
void receiver_define(char *name) {
zend_class_entry tmp;
INIT_CLASS_ENTRY_EX(tmp, name, strlen(name), NULL);
@ -203,12 +201,9 @@ void receiver_define(char *name, void *rcvr) {
// Set standard handlers for receiver.
RECEIVER_HANDLERS_SET(receiver_handlers);
// Method receiver is stored as internal class property.
RECEIVER_POINTER_SET(this, "__rcvr__", rcvr);
}
void receiver_destroy(char *name) {
name = php_strtolower(name, strlen(name));
RECEIVER_DESTROY(name);
}
}

View File

@ -13,63 +13,31 @@ package engine
import "C"
import (
"fmt"
"reflect"
"unsafe"
)
// ReceiverObject represents an object instance of a pre-defined method receiver.
type ReceiverObject struct {
instance interface{}
values map[string]reflect.Value
methods map[string]reflect.Value
}
// Receiver represents a method receiver.
type Receiver struct {
name string
create func(args []interface{}) interface{}
objects []*ReceiverObject
objects map[*C.struct__engine_receiver]*ReceiverObject
}
// Destroy removes references to the generated PHP class for this receiver and
// frees any memory used by object instances.
func (r *Receiver) Destroy() {
if r.create == nil {
return
}
n := C.CString(r.name)
defer C.free(unsafe.Pointer(n))
C.receiver_destroy(n)
r.create = nil
r.objects = nil
}
//export receiverNew
func receiverNew(rcvr unsafe.Pointer, args unsafe.Pointer) unsafe.Pointer {
r := (*Receiver)(rcvr)
va, err := NewValueFromPtr(args)
if err != nil {
return nil
}
defer va.Destroy()
// NewObject instantiates a new method receiver object, using the Receiver's
// create function and passing in a slice of values as a parameter.
func (r *Receiver) NewObject(args []interface{}) (*ReceiverObject, error) {
obj := &ReceiverObject{
instance: r.create(va.Slice()),
instance: r.create(args),
values: make(map[string]reflect.Value),
methods: make(map[string]reflect.Value),
}
if obj.instance == nil {
return nil
return nil, fmt.Errorf("Failed to instantiate method receiver")
}
r.objects = append(r.objects, obj)
v := reflect.ValueOf(obj.instance)
vi := reflect.Indirect(v)
@ -93,93 +61,95 @@ func receiverNew(rcvr unsafe.Pointer, args unsafe.Pointer) unsafe.Pointer {
}
}
return unsafe.Pointer(obj)
return obj, nil
}
//export receiverGet
func receiverGet(obj unsafe.Pointer, name *C.char) unsafe.Pointer {
o := (*ReceiverObject)(obj)
n := C.GoString(name)
if _, exists := o.values[n]; !exists || !o.values[n].CanInterface() {
return nil
// Destroy removes references to the generated PHP class for this receiver and
// frees any memory used by object instances.
func (r *Receiver) Destroy() {
if r.create == nil {
return
}
result, err := NewValue(o.values[n].Interface())
n := C.CString(r.name)
defer C.free(unsafe.Pointer(n))
C.receiver_destroy(n)
r.create = nil
r.objects = nil
}
// ReceiverObject represents an object instance of a pre-defined method receiver.
type ReceiverObject struct {
instance interface{}
values map[string]reflect.Value
methods map[string]reflect.Value
}
// Get returns a named internal property of the receiver object instance, or an
// error if the property does not exist or is not addressable.
func (o *ReceiverObject) Get(name string) (*Value, error) {
if _, exists := o.values[name]; !exists || !o.values[name].CanInterface() {
return nil, fmt.Errorf("Value '%s' does not exist or is not addressable", name)
}
val, err := NewValue(o.values[name].Interface())
if err != nil {
return nil
return nil, err
}
return result.Ptr()
return val, nil
}
//export receiverSet
func receiverSet(obj unsafe.Pointer, name *C.char, val unsafe.Pointer) {
o := (*ReceiverObject)(obj)
n := C.GoString(name)
// Set assigns value to named internal property. If the named property does not
// exist or cannot be set, the method does nothing.
func (o *ReceiverObject) Set(name string, val interface{}) {
// Do not attempt to set non-existing or unset-able field.
if _, exists := o.values[n]; !exists || !o.values[n].CanSet() {
if _, exists := o.values[name]; !exists || !o.values[name].CanSet() {
return
}
v, err := NewValueFromPtr(val)
if err != nil {
return
}
o.values[n].Set(reflect.ValueOf(v.Interface()))
o.values[name].Set(reflect.ValueOf(val))
}
//export receiverExists
func receiverExists(obj unsafe.Pointer, name *C.char) C.int {
o := (*ReceiverObject)(obj)
n := C.GoString(name)
if _, exists := o.values[n]; !exists {
return 0
// Exists checks if named internal property exists and returns true, or false if
// property does not exist.
func (o *ReceiverObject) Exists(name string) bool {
if _, exists := o.values[name]; !exists {
return false
}
return 1
return true
}
//export receiverCall
func receiverCall(obj unsafe.Pointer, name *C.char, args unsafe.Pointer) unsafe.Pointer {
o := (*ReceiverObject)(obj)
n := C.GoString(name)
if _, exists := o.methods[n]; !exists {
return nil
}
// Process input arguments.
va, err := NewValueFromPtr(args)
if err != nil {
// Call executes a method receiver's named internal method, passing a slice of
// values as arguments to the method. If the method fails to execute or returns
// no value, nil is returned, otherwise a Value instance is returned.
func (o *ReceiverObject) Call(name string, args []interface{}) *Value {
if _, exists := o.methods[name]; !exists {
return nil
}
in := make([]reflect.Value, 0)
for _, v := range va.Slice() {
for _, v := range args {
in = append(in, reflect.ValueOf(v))
}
va.Destroy()
// Call receiver method.
var result interface{}
ret := o.methods[n].Call(in)
val := o.methods[name].Call(in)
// Process results, returning a single value if result slice contains a single
// element, otherwise returns a slice of values.
if len(ret) > 1 {
t := make([]interface{}, len(ret))
for i, v := range ret {
if len(val) > 1 {
t := make([]interface{}, len(val))
for i, v := range val {
t[i] = v.Interface()
}
result = t
} else if len(ret) == 1 {
result = ret[0].Interface()
} else if len(val) == 1 {
result = val[0].Interface()
} else {
return nil
}
@ -189,5 +159,5 @@ func receiverCall(obj unsafe.Pointer, name *C.char, args unsafe.Pointer) unsafe.
return nil
}
return v.Ptr()
return v
}

View File

@ -125,12 +125,12 @@ func TestReceiverDefine(t *testing.T) {
c, _ := e.NewContext()
c.Output = &w
if err := c.Define("TestReceiver", newTestReceiver); err != nil {
if err := e.Define("TestReceiver", newTestReceiver); err != nil {
t.Fatalf("Engine.Define(): Failed to define method receiver: %s", err)
}
// Attempting to define a receiver twice should fail.
if err := c.Define("TestReceiver", newTestReceiver); err == nil {
if err := e.Define("TestReceiver", newTestReceiver); err == nil {
t.Fatalf("Engine.Define(): Defining duplicate receiver should fail")
}
@ -156,11 +156,7 @@ func TestReceiverDestroy(t *testing.T) {
c, _ := e.NewContext()
defer c.Destroy()
if err := c.Define("TestReceiver", newTestReceiver); err != nil {
t.Fatalf("Engine.Define(): Failed to define method receiver: %s", err)
}
r := c.receivers["TestReceiver"]
r := e.receivers["TestReceiver"]
if r == nil {
t.Fatalf("Receiver.Destroy(): Could not find defined receiver")
}