[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:
parent
ea193a98f3
commit
cfc3888cba
1 changed files with 115 additions and 79 deletions
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue