Use PHP objects instead of resources to wrap pointers

Pointer to member is currently still wrapped as a resource.
This commit is contained in:
Olly Betts 2021-04-21 15:40:35 +12:00
commit c705ef8f32
9 changed files with 107 additions and 118 deletions

View file

@ -24,7 +24,7 @@ check::equal(5,intp_value($tr),"5==$tr");
# Check the voidhandle call, first with null
$handle=NULL;
voidhandle($handle);
check::resource($handle,"_p_void",'$handle is not _p_void');
check::equal(get_class($handle),"SWIG\\_p_void",'$handle is not _p_void');
$handledata=handle($handle);
check::equal($handledata,"Here it is","\$handledata != \"Here it is\"");

View file

@ -2,7 +2,10 @@
require "tests.php";
require "callback.php";
// In 2.0.6 and earlier, the constant was misnamed.
if (gettype(callback::FOO_I_Cb_Ptr) !== 'resource') die("callback::FOO_I_Cb_Ptr not a resource\n");
check::equal(gettype(callback::FOO_I_Cb_Ptr), 'object', "callback::FOO_I_Cb_Ptr not an object");
check::equal(get_class(callback::FOO_I_Cb_Ptr), 'SWIG\_p_f_int__int', "callback::FOO_I_Cb_Ptr not expected class");
check::done();

View file

@ -5,7 +5,7 @@ require "grouping.php";
check::functions(array("test1","test2","do_unary","negate"));
check::equal(5,test1(5),"5==test1(5)");
check::resource(test2(7),"_p_int","_p_int==test2(7)");
check::equal(get_class(test2(7)),"SWIG\\_p_int","test2(7) is _p_int");
check::globals(array('test3'));
//check::equal(37,test3_get(),'37==test3_get()');

View file

@ -17,6 +17,10 @@ class check {
if (! is_array($extra)) {
$df=array_flip(get_declared_classes());
foreach($_original_classes as $class) unset($df[$class]);
// Filter out pointer wrappers such as SWIG/_p_int.
foreach(array_keys($df) as $class) {
if (preg_match('/^SWIG\\\\/', $class)) unset($df[$class]);
}
$extra=array_keys($df);
}
return $extra;
@ -202,10 +206,6 @@ class check {
return TRUE;
}
static function resource($a,$b,$message) {
return check::equal(get_resource_type($a), $b, $message);
}
static function isnull($a,$message) {
return check::equal($a,NULL,$message);
}

View file

@ -95,7 +95,7 @@ if (!dcast) {
Type *dobj = dynamic_cast<Type *>($1);
if (dobj) {
dcast = 1;
SWIG_SetZval(return_value, $needNewFlow, $owner, SWIG_as_voidptr(dobj), $descriptor(Type *));
SWIG_SetZval(return_value, $needNewFlow-0, $owner, SWIG_as_voidptr(dobj), $descriptor(Type *));
}
}%enddef
@ -104,6 +104,6 @@ if (!dcast) {
int dcast = 0;
%formacro(%_factory_dispatch, Types)
if (!dcast) {
SWIG_SetZval(return_value, $needNewFlow, $owner, SWIG_as_voidptr($1), $descriptor);
SWIG_SetZval(return_value, $needNewFlow-0, $owner, SWIG_as_voidptr($1), $descriptor);
}
}%enddef

View file

@ -383,7 +383,7 @@
SWIGTYPE &,
SWIGTYPE &&
%{
SWIG_SetZval($input, $needNewFlow, $owner, (void *)&$1, $1_descriptor);
SWIG_SetPointerZval($input, (void *)&$1, $1_descriptor, $owner);
%}
%typemap(out, fragment="swig_php_init_member_ptr") SWIGTYPE (CLASS::*)
@ -422,7 +422,7 @@
%typemap(directorin) SWIGTYPE
%{
SWIG_SetZval($input, $needNewFlow, 1, SWIG_as_voidptr(new $1_ltype((const $1_ltype &)$1)), $&1_descriptor);
SWIG_SetPointerZval($input, SWIG_as_voidptr(new $1_ltype((const $1_ltype &)$1)), $&1_descriptor, 1);
%}
%typemap(out) void "";

View file

@ -21,5 +21,6 @@ static int swig_member_ptr = 0;
%}
%fragment("swig_php_init_member_ptr", "init", fragment="swig_php_init_member_ptr2") %{
// FIXME: Make this a class instead
swig_member_ptr = zend_register_list_destructors_ex(swig_member_ptr_dtor, NULL, SWIG_MEMBER_PTR, module_number);
%}

View file

@ -94,32 +94,21 @@ SWIG_SetPointerZval(zval *z, void *ptr, swig_type_info *type, int newobject) {
ZVAL_NULL(z);
return;
}
if (type->clientdata) {
if ((newobject & 2) == 0) {
int resource_type = *(int *)(type->clientdata);
if (resource_type == 0) {
zend_error(E_ERROR, "Type: %s failed to register with zend", type->name);
} else {
/* Register the pointer as a resource. */
swig_object_wrapper *value=(swig_object_wrapper *)emalloc(sizeof(swig_object_wrapper));
value->ptr = ptr;
value->newobject = (newobject & 1);
value->type = type;
ZVAL_RES(z, zend_register_resource(value, resource_type));
}
} else {
/* This code path is currently only used by directorin typemaps. */
zend_class_entry *ce = (zend_class_entry*)(type->clientdata);
zend_object *obj = ce->create_object(ce);
swig_object_wrapper *value = php_fetch_object(obj);
value->ptr = ptr;
value->newobject = (newobject & 1);
value->type = type;
ZVAL_OBJ(z, obj);
}
if (!type->clientdata) {
zend_error(E_ERROR, "Type: %s not registered with zend", type->name);
return;
}
zend_error(E_ERROR, "Type: %s not registered with zend",type->name);
{
zend_class_entry *ce = (zend_class_entry*)(type->clientdata);
zend_object *obj = ce->create_object(ce);
swig_object_wrapper *value = php_fetch_object(obj);
value->ptr = ptr;
value->newobject = (newobject & 1);
value->type = type;
ZVAL_OBJ(z, obj);
}
}
/* This pointer conversion routine takes the native pointer p (along with
@ -127,13 +116,10 @@ SWIG_SetPointerZval(zval *z, void *ptr, swig_type_info *type, int newobject) {
according to ty. The resultant pointer is returned, or NULL is returned
if the pointer can't be cast.
Sadly PHP has no API to find a type name from a type id, only from an
instance of a resource of the type id, so we have to pass type_name as well.
This is called by SWIG_ConvertPtr which gets the type name from the
swig_object_wrapper or resource type. */
swig_object_wrapper. */
static void *
SWIG_ConvertResourceData(void * p, const char *type_name, swig_type_info *ty) {
SWIG_ConvertPtrData(void * p, const char *type_name, swig_type_info *ty) {
swig_cast_info *tc;
void *result = 0;
@ -157,8 +143,7 @@ SWIG_ConvertResourceData(void * p, const char *type_name, swig_type_info *ty) {
return result;
}
/* We allow passing of a RESOURCE wrapping a non-class pointer or an OBJECT
wrapping a pointer to an object. */
/* We wrap C/C++ pointers as PHP objects. */
static int
SWIG_ConvertPtr(zval *z, void **ptr, swig_type_info *ty, int flags) {
if (z == NULL) {
@ -172,24 +157,7 @@ SWIG_ConvertPtr(zval *z, void **ptr, swig_type_info *ty, int flags) {
if (flags & SWIG_POINTER_DISOWN) {
value->newobject = 0;
}
*ptr = SWIG_ConvertResourceData(value->ptr, value->type->name, ty);
return (*ptr == NULL ? SWIG_ERROR : SWIG_OK);
}
case IS_RESOURCE: {
swig_object_wrapper *value;
void *p;
const char *type_name;
if (Z_RES_TYPE_P(z) == -1) return -1;
value = (swig_object_wrapper *) Z_RES_VAL_P(z);
if (flags & SWIG_POINTER_DISOWN) {
value->newobject = 0;
}
p = value->ptr;
type_name=zend_rsrc_list_get_rsrc_type(Z_RES_P(z));
*ptr = SWIG_ConvertResourceData(p, type_name, ty);
*ptr = SWIG_ConvertPtrData(value->ptr, value->type->name, ty);
return (*ptr == NULL ? SWIG_ERROR : SWIG_OK);
}
case IS_NULL:
@ -202,18 +170,13 @@ SWIG_ConvertPtr(zval *z, void **ptr, swig_type_info *ty, int flags) {
static void
SWIG_SetZval(zval *zv, int newFlow, int newobject, void *ptr, swig_type_info *type) {
if (!ptr) {
ZVAL_NULL(zv);
return;
}
if (newFlow) {
swig_object_wrapper *obj;
if (newFlow == 1) {
zend_class_entry *ce = (zend_class_entry*)type->clientdata;
ZVAL_OBJ(zv, ce->create_object(ce));
if (newFlow > 1) {
if (!ptr) {
ZVAL_NULL(zv);
return;
}
obj = SWIG_Z_FETCH_OBJ_P(zv);
swig_object_wrapper * obj = SWIG_Z_FETCH_OBJ_P(zv);
obj->ptr = ptr;
obj->newobject = newobject;
obj->type = type;

View file

@ -95,7 +95,7 @@ static String *fake_class_name() {
*/
static Hash *arginfo_used;
/* Track non-class pointer types that get wrapped as resources */
/* Track non-class pointer types we need to to wrap */
static Hash *zend_types = 0;
static int shadow = 1;
@ -131,30 +131,31 @@ static void print_creation_free_wrapper(Node *n) {
Printf(s, "/* Garbage Collection Method for class %s */\n",class_name);
Printf(s, "void %s_free_storage(zend_object *object) {\n",class_name);
Printf(s, " swig_object_wrapper *obj = 0;\n\n");
Printf(s, " if(!object)\n\t return;\n\n");
Printf(s, " obj = php_fetch_object(object);\n\n");
Printf(s, " swig_object_wrapper *obj = 0;\n");
Printf(s, " if (!object)\n");
Printf(s, " return;\n");
Printf(s, " obj = php_fetch_object(object);\n");
// expand %delete typemap?
// expand %delete typemap instead of SWIG_remove?
if (Getattr(n, "has_destructor")) {
Printf(s, " if(obj->newobject)\n");
Printf(s, " if (obj->newobject)\n");
Printf(s, " SWIG_remove((%s *)obj->ptr);\n", Getattr(n, "classtype"));
}
Printf(s, " zend_object_std_dtor(&obj->std);\n}\n\n\n");
Printf(s, " zend_object_std_dtor(&obj->std);\n");
Printf(s, "}\n\n");
Printf(s, "/* Object Creation Method for class %s */\n",class_name);
Printf(s, "zend_object * %s_object_new(zend_class_entry *ce) {\n",class_name);
Printf(s, " swig_object_wrapper *obj = (swig_object_wrapper*)zend_object_alloc(sizeof(swig_object_wrapper), ce);\n");
Printf(s, " zend_object_std_init(&obj->std, ce);\n");
Printf(s, " object_properties_init(&obj->std, ce);\n");
Printf(s, " %s_object_handlers.offset = XtOffsetOf(swig_object_wrapper, std);\n",class_name);
Printf(s, " %s_object_handlers.free_obj = %s_free_storage;\n",class_name,class_name);
Printf(s, " %s_object_handlers.dtor_obj = zend_objects_destroy_object;\n",class_name);
Printf(s, " %s_object_handlers.offset = XtOffsetOf(swig_object_wrapper, std);\n", class_name);
Printf(s, " %s_object_handlers.free_obj = %s_free_storage;\n", class_name, class_name);
Printf(s, " obj->std.handlers = &%s_object_handlers;\n obj->newobject = 1;\n return &obj->std;\n}\n\n\n",class_name);
}
static void SwigPHP_emit_resource_registrations() {
static void SwigPHP_emit_pointer_type_registrations() {
if (!zend_types)
return;
@ -162,25 +163,65 @@ static void SwigPHP_emit_resource_registrations() {
if (!ki.key)
return;
// Write out custom destructor function
const char *rsrc_dtor_name = "_swig_default_rsrc_destroy";
Printf(s_wrappers, "static ZEND_RSRC_DTOR_FUNC(%s) {\n", rsrc_dtor_name);
Printf(s_wrappers, " efree(res->ptr);\n");
Printf(s_wrappers, "}\n");
Printf(s_wrappers, "/* class object handlers for pointer wrappers */\n");
Printf(s_wrappers, "static zend_object_handlers swig_ptr_object_handlers;\n\n");
Printf(s_wrappers, "/* Object Creation Method for pointer wrapping class */\n");
Printf(s_wrappers, "static zend_object * swig_ptr_object_new(zend_class_entry *ce) {\n");
Printf(s_wrappers, " swig_object_wrapper *obj = (swig_object_wrapper*)zend_object_alloc(sizeof(swig_object_wrapper), ce);\n");
Printf(s_wrappers, " zend_object_std_init(&obj->std, ce);\n");
Printf(s_wrappers, " object_properties_init(&obj->std, ce);\n");
Printf(s_wrappers, " obj->std.handlers = &swig_ptr_object_handlers;\n");
Printf(s_wrappers, " obj->newobject = 0;\n");
Printf(s_wrappers, " return &obj->std;\n");
Printf(s_wrappers, "}\n\n");
Printf(s_wrappers, "/* Implement __toString equivalent, since that worked for the old-style resource wrapped pointers. */\n");
Append(s_wrappers, "#if PHP_MAJOR_VERSION < 8\n");
Printf(s_wrappers, "static int swig_ptr_cast_object(zval *z, zval *retval, int type) {\n");
Append(s_wrappers, "#else\n");
Printf(s_wrappers, "static int swig_ptr_cast_object(zend_object *zobj, zval *retval, int type) {\n");
Append(s_wrappers, "#endif\n");
Printf(s_wrappers, " if (type == IS_STRING) {\n");
Printf(s_wrappers, " char buf[80];\n");
Append(s_wrappers, "#if PHP_MAJOR_VERSION < 8\n");
Printf(s_wrappers, " swig_object_wrapper *obj = SWIG_Z_FETCH_OBJ_P(z);\n");
Append(s_wrappers, "#else\n");
Printf(s_wrappers, " swig_object_wrapper *obj = php_fetch_object(zobj);\n");
Append(s_wrappers, "#endif\n");
Printv(s_wrappers, " sprintf(buf, \"SWIGPointer(%p,owned=%d)\", obj->ptr, obj->newobject);\n", NIL);
Printf(s_wrappers, " ZVAL_STRING(retval, buf);\n");
Printf(s_wrappers, " return SUCCESS;\n");
Printf(s_wrappers, " }\n");
Printf(s_wrappers, " return FAILURE;\n");
Printf(s_wrappers, "}\n\n");
Printf(s_wrappers, "static zend_function_entry swig_ptr_class_functions[] = {\n");
Printf(s_wrappers, " ZEND_FE_END\n");
Printf(s_wrappers, "};\n\n");
Printf(s_oinit, "\n /* Register classes to represent non-class pointer types */\n");
Printf(s_oinit, " memcpy(&swig_ptr_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));\n");
Printf(s_oinit, " swig_ptr_object_handlers.offset = XtOffsetOf(swig_object_wrapper, std);\n");
Printf(s_oinit, " swig_ptr_object_handlers.cast_object = swig_ptr_cast_object;\n");
Printf(s_oinit, "\n /* Register resource destructors for non-class pointer types */\n");
while (ki.key) {
String *type = ki.key;
// declare le_swig<mangled> to store php registration
Printf(s_vdecl, "static int le_swig%s=0; /* handle for %s */\n", type, type);
if (!s_creation) {
s_creation = NewStringEmpty();
}
// register with php
Printf(s_oinit, " le_swig%s=zend_register_list_destructors_ex"
"(%s, NULL, SWIGTYPE%s->name, module_number);\n", type, rsrc_dtor_name, type);
Printf(s_creation, "/* class entry for pointer to %s */\n", type);
Printf(s_creation, "zend_class_entry *SWIGTYPE_%s_ce;\n\n", type);
// store php type in class struct
Printf(s_oinit, " SWIG_TypeClientData(SWIGTYPE%s,&le_swig%s);\n", type, type);
Printf(s_oinit, "{\n");
Printf(s_oinit, " zend_class_entry SWIGTYPE_%s_internal_ce;\n", type);
Printf(s_oinit, " INIT_CLASS_ENTRY(SWIGTYPE_%s_internal_ce, \"%s\\\\%s\", swig_ptr_class_functions);\n", type, "SWIG", type);
Printf(s_oinit, " SWIGTYPE_%s_ce = zend_register_internal_class(&SWIGTYPE_%s_internal_ce);\n", type, type);
Printf(s_oinit, " SWIGTYPE_%s_ce->create_object = swig_ptr_object_new;\n", type);
Printf(s_oinit, " SWIG_TypeClientData(SWIGTYPE%s,SWIGTYPE_%s_ce);\n", type, type);
Printf(s_oinit, "}\n\n");
ki = Next(ki);
}
@ -450,7 +491,7 @@ public:
/* Emit all of the code */
Language::top(n);
SwigPHP_emit_resource_registrations();
SwigPHP_emit_pointer_type_registrations();
if (s_creation) {
Dump(s_creation, s_header);
Delete(s_creation);
@ -1284,7 +1325,6 @@ public:
if ((tm = Getattr(p, "tmap:in"))) {
Replaceall(tm, "$input", source);
Replaceall(tm, "$needNewFlow", paramType_valid ? (is_class_wrapped(paramType_class) ? "1" : "0") : "0");
Setattr(p, "emit:input", source);
Printf(f->code, "%s\n", tm);
if (i == 0 && Getattr(p, "self")) {
@ -1359,29 +1399,16 @@ public:
}
Setattr(n, "wrapper:method:name", wname);
String *retType_class = NULL;
bool retType_valid = is_class(d);
bool valid_wrapped_class = false;
bool constructorRenameOverload = false;
if (retType_valid) {
retType_class = get_class_name(d);
Chop(retType_class);
valid_wrapped_class = is_class_wrapped(retType_class);
}
if (constructor && Cmp(class_name, Getattr(n, "constructorHandler:sym:name")) != 0) {
constructorRenameOverload = true;
}
bool php_constructor = (constructor && Cmp(class_name, Getattr(n, "constructorHandler:sym:name")) == 0);
/* emit function call */
String *actioncode = emit_action(n);
if ((tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode))) {
Replaceall(tm, "$input", Swig_cresult_name());
Replaceall(tm, "$result", constructor ? (constructorRenameOverload ? "return_value" : "ZEND_THIS") : "return_value");
Replaceall(tm, "$result", php_constructor ? "ZEND_THIS" : "return_value");
Replaceall(tm, "$owner", newobject ? "1" : "0");
Replaceall(tm, "$needNewFlow", retType_valid ? (constructor ? (constructorRenameOverload ? "1" : "2") : (valid_wrapped_class ? "1" : "0")) : "0");
Replaceall(tm, "$needNewFlow", php_constructor && is_class(d) ? "2" : "0");
Printf(f->code, "%s\n", tm);
} else {
Swig_warning(WARN_TYPEMAP_OUT_UNDEF, input_file, line_number, "Unable to use return type %s in function %s.\n", SwigType_str(d, 0), name);
@ -2019,11 +2046,6 @@ public:
if ((tm = Getattr(p, "tmap:directorin")) != 0) {
String *parse = Getattr(p, "tmap:directorin:parse");
if (!parse) {
if (is_class(Getattr(p, "type"))) {
Replaceall(tm, "$needNewFlow", "1");
} else {
Replaceall(tm, "$needNewFlow", "0");
}
String *input = NewStringf("&args[%d]", idx++);
Setattr(p, "emit:directorinput", input);
Replaceall(tm, "$input", input);