[php] Emit arginfo after all the code

Another step in preparation for a fix for the incompatible overridden
method problem discussed in #2151.
This commit is contained in:
Olly Betts 2022-07-13 14:21:02 +12:00
commit cfc3888cba

View file

@ -218,6 +218,15 @@ class PHPTypes {
// the dispatch function. If NULL, no parameters are passed by reference. // the dispatch function. If NULL, no parameters are passed by reference.
List *byref; List *byref;
// The id string used in the name of the arginfo for this object.
String *arginfo_id;
// The feature:php:type value: 0, 1 or -1 for "compatibility".
int php_type_flag;
// Does the node for this have directorNode set?
bool has_director_node;
// Used to clamp the required number of parameters in the arginfo to be // Used to clamp the required number of parameters in the arginfo to be
// compatible with any parent class version of the method. // compatible with any parent class version of the method.
int num_required; int num_required;
@ -261,24 +270,51 @@ class PHPTypes {
return result; return result;
} }
void init(Node *n) {
String *php_type_feature = Getattr(n, "feature:php:type");
php_type_flag = 0;
if (php_type_feature != NULL) {
if (Equal(php_type_feature, "1")) {
php_type_flag = 1;
} else if (!Equal(php_type_feature, "0")) {
php_type_flag = -1;
}
}
arginfo_id = Copy(Getattr(n, "sym:name"));
has_director_node = (Getattr(n, "directorNode") != NULL);
}
public: public:
PHPTypes(int num_required_) PHPTypes(Node *n, int num_required_)
: merged_types(NewList()), : merged_types(NewList()),
byref(NULL), byref(NULL),
num_required(num_required_) { } num_required(num_required_) {
init(n);
}
PHPTypes(const PHPTypes *o) PHPTypes(Node *n, const PHPTypes *o)
: merged_types(Copy(o->merged_types)), : merged_types(Copy(o->merged_types)),
byref(Copy(o->byref)), byref(Copy(o->byref)),
num_required(o->num_required) { } num_required(o->num_required) {
init(n);
}
~PHPTypes() { ~PHPTypes() {
Delete(merged_types); Delete(merged_types);
Delete(byref); Delete(byref);
} }
void adjust_num_required(int num_required_) { void adjust(int num_required_, bool php_constructor) {
num_required = std::min(num_required, num_required_); num_required = std::min(num_required, num_required_);
if (php_constructor) {
// Don't add a return type declaration for a PHP __construct method
// (because there it has no return type as far as PHP is concerned).
php_type_flag = 0;
}
}
String *get_arginfo_id() const {
return arginfo_id;
} }
// key is 0 for return type, or >= 1 for parameters numbered from 1 // key is 0 for return type, or >= 1 for parameters numbered from 1
@ -297,7 +333,7 @@ public:
Setitem(byref, key, ""); // Just needs to be something != None. Setitem(byref, key, ""); // Just needs to be something != None.
} }
void emit_arginfo(String *fname, Node *n) { void emit_arginfo() {
// We want to only emit each different arginfo once, as that reduces the // We want to only emit each different arginfo once, as that reduces the
// size of both the generated source code and the compiled extension // size of both the generated source code and the compiled extension
// module. The parameters at this level are just named arg1, arg2, etc // module. The parameters at this level are just named arg1, arg2, etc
@ -308,15 +344,9 @@ public:
// We generate the arginfo we want (taking care to normalise, e.g. the // We generate the arginfo we want (taking care to normalise, e.g. the
// lists of types are unique and in sorted order), then use the // lists of types are unique and in sorted order), then use the
// arginfo_used Hash to see if we've already generated it. // arginfo_used Hash to see if we've already generated it.
// Don't add a return type declaration for a constructor (because there
// is no return type as far as PHP is concerned).
String *out_phptype = NULL; String *out_phptype = NULL;
String *out_phpclasses = NewStringEmpty(); String *out_phpclasses = NewStringEmpty();
if (!Equal(fname, "__construct")) { if (php_type_flag > 0 || (php_type_flag && !has_director_node)) {
String *php_type_flag = GetFlagAttr(n, "feature:php:type");
if (Equal(php_type_flag, "1") ||
(php_type_flag && !Getattr(n, "directorNode"))) {
// We provide a simple way to generate PHP return type declarations // We provide a simple way to generate PHP return type declarations
// except for directed methods. The point of directors is to allow // except for directed methods. The point of directors is to allow
// subclassing in the target language, and if the wrapped method has // subclassing in the target language, and if the wrapped method has
@ -336,7 +366,6 @@ public:
// director_frob. // director_frob.
out_phptype = get_phptype(0, out_phpclasses); out_phptype = get_phptype(0, out_phpclasses);
} }
}
// ### in arginfo_code will be replaced with the id once that is known. // ### in arginfo_code will be replaced with the id once that is known.
String *arginfo_code = NewStringEmpty(); String *arginfo_code = NewStringEmpty();
@ -376,15 +405,14 @@ public:
} }
Printf(arginfo_code, "ZEND_END_ARG_INFO()\n"); Printf(arginfo_code, "ZEND_END_ARG_INFO()\n");
String *arginfo_id_new = Getattr(n, "sym:name"); String *arginfo_id_same = Getattr(arginfo_used, arginfo_code);
String *arginfo_id = Getattr(arginfo_used, arginfo_code); if (arginfo_id_same) {
if (arginfo_id) { Printf(s_arginfo, "#define swig_arginfo_%s swig_arginfo_%s\n", arginfo_id, arginfo_id_same);
Printf(s_arginfo, "#define swig_arginfo_%s swig_arginfo_%s\n", arginfo_id_new, arginfo_id);
} else { } else {
// Not had this arginfo before. // Not had this arginfo before.
Setattr(arginfo_used, arginfo_code, arginfo_id_new); Setattr(arginfo_used, arginfo_code, arginfo_id);
arginfo_code = Copy(arginfo_code); arginfo_code = Copy(arginfo_code);
Replace(arginfo_code, "###", arginfo_id_new, DOH_REPLACE_FIRST); Replace(arginfo_code, "###", arginfo_id, DOH_REPLACE_FIRST);
Append(s_arginfo, arginfo_code); Append(s_arginfo, arginfo_code);
} }
Delete(arginfo_code); Delete(arginfo_code);
@ -394,10 +422,8 @@ public:
static PHPTypes *phptypes = NULL; static PHPTypes *phptypes = NULL;
// Track if the current phptypes is for a non-class function. // php_class + ":" + php_method -> PHPTypes*
static PHPTypes *non_class_phptypes = NULL; // ":" + php_function -> PHPTypes*
// class + ":" + method -> PHPTypes*
static Hash *all_phptypes = NewHash(); static Hash *all_phptypes = NewHash();
// php_class_name -> php_parent_class_name // php_class_name -> php_parent_class_name
@ -599,6 +625,12 @@ public:
/* Emit all of the code */ /* Emit all of the code */
Language::top(n); Language::top(n);
/* Emit all the arginfo */
for (Iterator ki = First(all_phptypes); ki.key; ki = Next(ki)) {
PHPTypes *p = (PHPTypes*)Data(ki.item);
p->emit_arginfo();
}
SwigPHP_emit_pointer_type_registrations(); SwigPHP_emit_pointer_type_registrations();
Dump(s_creation, s_header); Dump(s_creation, s_header);
Delete(s_creation); Delete(s_creation);
@ -808,7 +840,7 @@ public:
wrapperType != staticmembervar && wrapperType != staticmembervar &&
!Equal(fname, "__construct")) { !Equal(fname, "__construct")) {
// Skip the first entry in the parameter list which is the this pointer. // Skip the first entry in the parameter list which is the this pointer.
l = Getattr(l, "tmap:in:next"); if (l) l = Getattr(l, "tmap:in:next");
// FIXME: does this throw the phptype key value off? // FIXME: does this throw the phptype key value off?
} }
} else { } else {
@ -819,33 +851,31 @@ public:
} }
} }
phptypes->adjust_num_required(emit_num_required(l)); phptypes->adjust(emit_num_required(l), Equal(fname, "__construct"));
phptypes->emit_arginfo(fname, n); String *arginfo_id = phptypes->get_arginfo_id();
String *arginfo_id_new = Getattr(n, "sym:name");
String *s = cs_entry; String *s = cs_entry;
if (!s) s = s_entry; if (!s) s = s_entry;
if (cname && Cmp(Getattr(n, "storage"), "friend") != 0) { if (cname && Cmp(Getattr(n, "storage"), "friend") != 0) {
Printf(all_cs_entry, " PHP_ME(%s%s,%s,swig_arginfo_%s,%s)\n", prefix, cname, fname, arginfo_id_new, modes); Printf(all_cs_entry, " PHP_ME(%s%s,%s,swig_arginfo_%s,%s)\n", prefix, cname, fname, arginfo_id, modes);
} else { } else {
if (dispatch) { if (dispatch) {
if (wrap_nonclass_global) { if (wrap_nonclass_global) {
Printf(s, " ZEND_NAMED_FE(%(lower)s,%s,swig_arginfo_%s)\n", Getattr(n, "sym:name"), fname, arginfo_id_new); Printf(s, " ZEND_NAMED_FE(%(lower)s,%s,swig_arginfo_%s)\n", Getattr(n, "sym:name"), fname, arginfo_id);
} }
if (wrap_nonclass_fake_class) { if (wrap_nonclass_fake_class) {
(void)fake_class_name(); (void)fake_class_name();
Printf(fake_cs_entry, " ZEND_NAMED_ME(%(lower)s,%s,swig_arginfo_%s,ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)\n", Getattr(n, "sym:name"), fname, arginfo_id_new); Printf(fake_cs_entry, " ZEND_NAMED_ME(%(lower)s,%s,swig_arginfo_%s,ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)\n", Getattr(n, "sym:name"), fname, arginfo_id);
} }
} else { } else {
if (wrap_nonclass_global) { if (wrap_nonclass_global) {
Printf(s, " PHP_FE(%s,swig_arginfo_%s)\n", fname, arginfo_id_new); Printf(s, " PHP_FE(%s,swig_arginfo_%s)\n", fname, arginfo_id);
} }
if (wrap_nonclass_fake_class) { if (wrap_nonclass_fake_class) {
String *fake_class = fake_class_name(); String *fake_class = fake_class_name();
Printf(fake_cs_entry, " PHP_ME(%s,%s,swig_arginfo_%s,ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)\n", fake_class, fname, arginfo_id_new); Printf(fake_cs_entry, " PHP_ME(%s,%s,swig_arginfo_%s,ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)\n", fake_class, fname, arginfo_id);
} }
} }
} }
@ -870,8 +900,8 @@ public:
bool constructorRenameOverload = false; bool constructorRenameOverload = false;
if (constructor) { if (constructor) {
if (!Equal(class_name, Getattr(n, "constructorHandler:sym:name"))) {
// Renamed constructor - turn into static factory method // Renamed constructor - turn into static factory method
if (Cmp(class_name, Getattr(n, "constructorHandler:sym:name")) != 0) {
constructorRenameOverload = true; constructorRenameOverload = true;
wname = Copy(Getattr(n, "constructorHandler:sym:name")); wname = Copy(Getattr(n, "constructorHandler:sym:name"));
} else { } else {
@ -1158,7 +1188,12 @@ public:
} }
if (constructor) { if (constructor) {
if (!Equal(class_name, Getattr(n, "constructorHandler:sym:name"))) {
// Renamed constructor - turn into static factory method
wname = Copy(Getattr(n, "constructorHandler:sym:name"));
} else {
wname = NewString("__construct"); wname = NewString("__construct");
}
} else if (wrapperType == membervar) { } else if (wrapperType == membervar) {
wname = Copy(Getattr(n, "membervariableHandler:sym:name")); wname = Copy(Getattr(n, "membervariableHandler:sym:name"));
if (is_setter_method(n)) { if (is_setter_method(n)) {
@ -1210,34 +1245,35 @@ public:
if (!Getattr(n, "sym:previousSibling") && !static_getter) { if (!Getattr(n, "sym:previousSibling") && !static_getter) {
// First function of an overloaded group or a function which isn't part // First function of an overloaded group or a function which isn't part
// of a group so reset the phptype information. // of a group so reset the phptype information.
if (non_class_phptypes) {
delete non_class_phptypes;
non_class_phptypes = NULL;
}
phptypes = NULL; phptypes = NULL;
String *key = NewStringf("%s:%s", class_name, wname);
PHPTypes *p = (PHPTypes*)GetVoid(all_phptypes, key);
if (p) {
// We already have an entry - this happens when overloads are created
// by %extend, for instance.
phptypes = p;
Delete(key);
} else {
if (class_name) { if (class_name) {
// See if there's a parent class which implements this method, and if // See if there's a parent class which implements this method, and if
// so copy the PHPTypes of that method as a starting point as we need // so copy the PHPTypes of that method as a starting point as we need
// to be compatible with it (whether it is virtual or not). // to be compatible with it (whether it is virtual or not).
String *parent = class_name; String *parent = class_name;
while ((parent = Getattr(php_parent_class, parent)) != NULL) { while ((parent = Getattr(php_parent_class, parent)) != NULL) {
String *key = NewStringf("%s:%s", parent, wname); String *k = NewStringf("%s:%s", parent, wname);
PHPTypes *p = (PHPTypes*)GetVoid(all_phptypes, key); PHPTypes *p = (PHPTypes*)GetVoid(all_phptypes, k);
Delete(key); Delete(key);
if (p) { if (p) {
phptypes = new PHPTypes(p); phptypes = new PHPTypes(n, p);
break; break;
} }
} }
} }
if (!phptypes) { if (!phptypes) {
phptypes = new PHPTypes(emit_num_required(l)); phptypes = new PHPTypes(n, emit_num_required(l));
} }
if (class_name) { SetVoid(all_phptypes, key, phptypes);
SetVoid(all_phptypes, NewStringf("%s:%s", class_name, wname), phptypes);
} else {
non_class_phptypes = phptypes;
} }
} }