mirror of https://github.com/deuill/go-php.git
Fix Receiver bindings for Go 1.6
Several API-breaking changes: * Define method and related functionality moved back to Engine package * Exported functions for receivers moved to the engine package * Receiver API cleaned up and made public Receivers no longer store Go pointers in C contexts, and instead use the C engine_receiver pointer as reference in the Go context (similar to what we do for Contexts). Some changes have been made to the C code to accomodate the above.
This commit is contained in:
parent
2a1e45785b
commit
50c36f8a31
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
143
engine/engine.go
143
engine/engine.go
|
@ -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,8 +26,9 @@ import (
|
|||
|
||||
// Engine represents the core PHP engine bindings.
|
||||
type Engine struct {
|
||||
engine *C.struct__php_engine
|
||||
contexts map[*C.struct__engine_context]*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.
|
||||
|
@ -45,8 +47,9 @@ func New() (*Engine, error) {
|
|||
}
|
||||
|
||||
engine = &Engine{
|
||||
engine: ptr,
|
||||
contexts: make(map[*C.struct__engine_context]*Context),
|
||||
engine: ptr,
|
||||
contexts: make(map[*C.struct__engine_context]*Context),
|
||||
receivers: make(map[string]*Receiver),
|
||||
}
|
||||
|
||||
return engine, nil
|
||||
|
@ -62,10 +65,9 @@ func (e *Engine) NewContext() (*Context, error) {
|
|||
}
|
||||
|
||||
ctx := &Context{
|
||||
Header: make(http.Header),
|
||||
context: ptr,
|
||||
values: make([]*Value, 0),
|
||||
receivers: make(map[string]*Receiver),
|
||||
Header: make(http.Header),
|
||||
context: ptr,
|
||||
values: make([]*Value, 0),
|
||||
}
|
||||
|
||||
// Store reference to context, using pointer as key.
|
||||
|
@ -74,12 +76,46 @@ func (e *Engine) NewContext() (*Context, error) {
|
|||
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()
|
||||
}
|
||||
|
@ -152,3 +188,94 @@ func engineSetHeader(ctx *C.struct__engine_context, operation C.uint, buffer uns
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
//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()
|
||||
}
|
||||
|
|
|
@ -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`")
|
||||
}
|
||||
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
#ifndef __CONTEXT_H__
|
||||
#define __CONTEXT_H__
|
||||
|
||||
#include "_context.h"
|
||||
|
||||
typedef struct _engine_context {
|
||||
} engine_context;
|
||||
|
||||
|
@ -16,4 +14,6 @@ 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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue