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 # Check the voidhandle call, first with null
$handle=NULL; $handle=NULL;
voidhandle($handle); 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); $handledata=handle($handle);
check::equal($handledata,"Here it is","\$handledata != \"Here it is\""); check::equal($handledata,"Here it is","\$handledata != \"Here it is\"");

View file

@ -2,7 +2,10 @@
require "tests.php"; require "tests.php";
require "callback.php"; require "callback.php";
// In 2.0.6 and earlier, the constant was misnamed. // 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(); check::done();

View file

@ -5,7 +5,7 @@ require "grouping.php";
check::functions(array("test1","test2","do_unary","negate")); check::functions(array("test1","test2","do_unary","negate"));
check::equal(5,test1(5),"5==test1(5)"); 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::globals(array('test3'));
//check::equal(37,test3_get(),'37==test3_get()'); //check::equal(37,test3_get(),'37==test3_get()');

View file

@ -17,6 +17,10 @@ class check {
if (! is_array($extra)) { if (! is_array($extra)) {
$df=array_flip(get_declared_classes()); $df=array_flip(get_declared_classes());
foreach($_original_classes as $class) unset($df[$class]); 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); $extra=array_keys($df);
} }
return $extra; return $extra;
@ -202,10 +206,6 @@ class check {
return TRUE; return TRUE;
} }
static function resource($a,$b,$message) {
return check::equal(get_resource_type($a), $b, $message);
}
static function isnull($a,$message) { static function isnull($a,$message) {
return check::equal($a,NULL,$message); return check::equal($a,NULL,$message);
} }

View file

@ -95,7 +95,7 @@ if (!dcast) {
Type *dobj = dynamic_cast<Type *>($1); Type *dobj = dynamic_cast<Type *>($1);
if (dobj) { if (dobj) {
dcast = 1; 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 }%enddef
@ -104,6 +104,6 @@ if (!dcast) {
int dcast = 0; int dcast = 0;
%formacro(%_factory_dispatch, Types) %formacro(%_factory_dispatch, Types)
if (!dcast) { 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 }%enddef

View file

@ -383,7 +383,7 @@
SWIGTYPE &, SWIGTYPE &,
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::*) %typemap(out, fragment="swig_php_init_member_ptr") SWIGTYPE (CLASS::*)
@ -422,7 +422,7 @@
%typemap(directorin) SWIGTYPE %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 ""; %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") %{ %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); 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); ZVAL_NULL(z);
return; return;
} }
if (type->clientdata) {
if ((newobject & 2) == 0) { if (!type->clientdata) {
int resource_type = *(int *)(type->clientdata); zend_error(E_ERROR, "Type: %s not registered with zend", type->name);
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);
}
return; 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 /* 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 according to ty. The resultant pointer is returned, or NULL is returned
if the pointer can't be cast. 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 This is called by SWIG_ConvertPtr which gets the type name from the
swig_object_wrapper or resource type. */ swig_object_wrapper. */
static void * 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; swig_cast_info *tc;
void *result = 0; void *result = 0;
@ -157,8 +143,7 @@ SWIG_ConvertResourceData(void * p, const char *type_name, swig_type_info *ty) {
return result; return result;
} }
/* We allow passing of a RESOURCE wrapping a non-class pointer or an OBJECT /* We wrap C/C++ pointers as PHP objects. */
wrapping a pointer to an object. */
static int static int
SWIG_ConvertPtr(zval *z, void **ptr, swig_type_info *ty, int flags) { SWIG_ConvertPtr(zval *z, void **ptr, swig_type_info *ty, int flags) {
if (z == NULL) { if (z == NULL) {
@ -172,24 +157,7 @@ SWIG_ConvertPtr(zval *z, void **ptr, swig_type_info *ty, int flags) {
if (flags & SWIG_POINTER_DISOWN) { if (flags & SWIG_POINTER_DISOWN) {
value->newobject = 0; value->newobject = 0;
} }
*ptr = SWIG_ConvertResourceData(value->ptr, value->type->name, ty); *ptr = SWIG_ConvertPtrData(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);
return (*ptr == NULL ? SWIG_ERROR : SWIG_OK); return (*ptr == NULL ? SWIG_ERROR : SWIG_OK);
} }
case IS_NULL: case IS_NULL:
@ -202,18 +170,13 @@ SWIG_ConvertPtr(zval *z, void **ptr, swig_type_info *ty, int flags) {
static void static void
SWIG_SetZval(zval *zv, int newFlow, int newobject, void *ptr, swig_type_info *type) { SWIG_SetZval(zval *zv, int newFlow, int newobject, void *ptr, swig_type_info *type) {
if (!ptr) { if (newFlow > 1) {
ZVAL_NULL(zv); if (!ptr) {
return; 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));
} }
obj = SWIG_Z_FETCH_OBJ_P(zv);
swig_object_wrapper * obj = SWIG_Z_FETCH_OBJ_P(zv);
obj->ptr = ptr; obj->ptr = ptr;
obj->newobject = newobject; obj->newobject = newobject;
obj->type = type; obj->type = type;

View file

@ -95,7 +95,7 @@ static String *fake_class_name() {
*/ */
static Hash *arginfo_used; 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 Hash *zend_types = 0;
static int shadow = 1; 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, "/* Garbage Collection Method for class %s */\n",class_name);
Printf(s, "void %s_free_storage(zend_object *object) {\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, " swig_object_wrapper *obj = 0;\n");
Printf(s, " if(!object)\n\t return;\n\n"); Printf(s, " if (!object)\n");
Printf(s, " obj = php_fetch_object(object);\n\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")) { 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, " 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, "/* 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, "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, " 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, " zend_object_std_init(&obj->std, ce);\n");
Printf(s, " object_properties_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.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.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, " obj->std.handlers = &%s_object_handlers;\n obj->newobject = 1;\n return &obj->std;\n}\n\n\n",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) if (!zend_types)
return; return;
@ -162,25 +163,65 @@ static void SwigPHP_emit_resource_registrations() {
if (!ki.key) if (!ki.key)
return; return;
// Write out custom destructor function Printf(s_wrappers, "/* class object handlers for pointer wrappers */\n");
const char *rsrc_dtor_name = "_swig_default_rsrc_destroy"; Printf(s_wrappers, "static zend_object_handlers swig_ptr_object_handlers;\n\n");
Printf(s_wrappers, "static ZEND_RSRC_DTOR_FUNC(%s) {\n", rsrc_dtor_name);
Printf(s_wrappers, " efree(res->ptr);\n"); Printf(s_wrappers, "/* Object Creation Method for pointer wrapping class */\n");
Printf(s_wrappers, "}\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) { while (ki.key) {
String *type = ki.key; String *type = ki.key;
// declare le_swig<mangled> to store php registration if (!s_creation) {
Printf(s_vdecl, "static int le_swig%s=0; /* handle for %s */\n", type, type); s_creation = NewStringEmpty();
}
// register with php Printf(s_creation, "/* class entry for pointer to %s */\n", type);
Printf(s_oinit, " le_swig%s=zend_register_list_destructors_ex" Printf(s_creation, "zend_class_entry *SWIGTYPE_%s_ce;\n\n", type);
"(%s, NULL, SWIGTYPE%s->name, module_number);\n", type, rsrc_dtor_name, type);
// store php type in class struct Printf(s_oinit, "{\n");
Printf(s_oinit, " SWIG_TypeClientData(SWIGTYPE%s,&le_swig%s);\n", type, type); 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); ki = Next(ki);
} }
@ -450,7 +491,7 @@ public:
/* Emit all of the code */ /* Emit all of the code */
Language::top(n); Language::top(n);
SwigPHP_emit_resource_registrations(); SwigPHP_emit_pointer_type_registrations();
if (s_creation) { if (s_creation) {
Dump(s_creation, s_header); Dump(s_creation, s_header);
Delete(s_creation); Delete(s_creation);
@ -1284,7 +1325,6 @@ public:
if ((tm = Getattr(p, "tmap:in"))) { if ((tm = Getattr(p, "tmap:in"))) {
Replaceall(tm, "$input", source); Replaceall(tm, "$input", source);
Replaceall(tm, "$needNewFlow", paramType_valid ? (is_class_wrapped(paramType_class) ? "1" : "0") : "0");
Setattr(p, "emit:input", source); Setattr(p, "emit:input", source);
Printf(f->code, "%s\n", tm); Printf(f->code, "%s\n", tm);
if (i == 0 && Getattr(p, "self")) { if (i == 0 && Getattr(p, "self")) {
@ -1359,29 +1399,16 @@ public:
} }
Setattr(n, "wrapper:method:name", wname); Setattr(n, "wrapper:method:name", wname);
String *retType_class = NULL; bool php_constructor = (constructor && Cmp(class_name, Getattr(n, "constructorHandler:sym:name")) == 0);
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;
}
/* emit function call */ /* emit function call */
String *actioncode = emit_action(n); String *actioncode = emit_action(n);
if ((tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode))) { if ((tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode))) {
Replaceall(tm, "$input", Swig_cresult_name()); 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, "$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); Printf(f->code, "%s\n", tm);
} else { } 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); 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) { if ((tm = Getattr(p, "tmap:directorin")) != 0) {
String *parse = Getattr(p, "tmap:directorin:parse"); String *parse = Getattr(p, "tmap:directorin:parse");
if (!parse) { if (!parse) {
if (is_class(Getattr(p, "type"))) {
Replaceall(tm, "$needNewFlow", "1");
} else {
Replaceall(tm, "$needNewFlow", "0");
}
String *input = NewStringf("&args[%d]", idx++); String *input = NewStringf("&args[%d]", idx++);
Setattr(p, "emit:directorinput", input); Setattr(p, "emit:directorinput", input);
Replaceall(tm, "$input", input); Replaceall(tm, "$input", input);