php: Encapsulate arginfo generation
Encapsulate the code to generate arginfo in the PHPTypes class. By itself this should result in no functional changes, but it's a step towards being able to delay arginfo generation which I think is necessary to address the incompatible overridden method problem discussed in #2151.
This commit is contained in:
parent
c25d0d707a
commit
6503200684
1 changed files with 147 additions and 144 deletions
|
|
@ -220,32 +220,16 @@ class PHPTypes {
|
|||
|
||||
// Used to clamp the required number of parameters in the arginfo to be
|
||||
// compatible with any parent class version of the method.
|
||||
int required_clamp;
|
||||
int num_required;
|
||||
|
||||
public:
|
||||
PHPTypes(int num_required)
|
||||
: merged_types(NewList()),
|
||||
byref(NULL),
|
||||
required_clamp(num_required) { }
|
||||
|
||||
PHPTypes(const PHPTypes *o)
|
||||
: merged_types(Copy(o->merged_types)),
|
||||
byref(Copy(o->byref)),
|
||||
required_clamp(o->required_clamp) { }
|
||||
|
||||
~PHPTypes() {
|
||||
Delete(merged_types);
|
||||
Delete(byref);
|
||||
int get_byref(int key) const {
|
||||
return byref && key < Len(byref) && Getitem(byref, key) != None;
|
||||
}
|
||||
|
||||
int adjust_num_required(int num_required) {
|
||||
required_clamp = std::min(num_required, required_clamp);
|
||||
return required_clamp;
|
||||
int size() const {
|
||||
return std::max(Len(merged_types), Len(byref));
|
||||
}
|
||||
|
||||
// key is 0 for return type, or >= 1 for parameters numbered from 1
|
||||
void process_phptype(Node *n, int key, const String_or_char *attribute_name);
|
||||
|
||||
String *get_phptype(int key, String *classtypes) {
|
||||
Clear(classtypes);
|
||||
DOH *types = Getitem(merged_types, key);
|
||||
|
|
@ -277,6 +261,29 @@ public:
|
|||
return result;
|
||||
}
|
||||
|
||||
public:
|
||||
PHPTypes(int num_required_)
|
||||
: merged_types(NewList()),
|
||||
byref(NULL),
|
||||
num_required(num_required_) { }
|
||||
|
||||
PHPTypes(const PHPTypes *o)
|
||||
: merged_types(Copy(o->merged_types)),
|
||||
byref(Copy(o->byref)),
|
||||
num_required(o->num_required) { }
|
||||
|
||||
~PHPTypes() {
|
||||
Delete(merged_types);
|
||||
Delete(byref);
|
||||
}
|
||||
|
||||
void adjust_num_required(int num_required_) {
|
||||
num_required = std::min(num_required, num_required_);
|
||||
}
|
||||
|
||||
// key is 0 for return type, or >= 1 for parameters numbered from 1
|
||||
void process_phptype(Node *n, int key, const String_or_char *attribute_name);
|
||||
|
||||
void set_byref(int key) {
|
||||
if (!byref) {
|
||||
byref = NewList();
|
||||
|
|
@ -290,12 +297,124 @@ public:
|
|||
Setitem(byref, key, ""); // Just needs to be something != None.
|
||||
}
|
||||
|
||||
int get_byref(int key) const {
|
||||
return byref && key < Len(byref) && Getitem(byref, key) != None;
|
||||
}
|
||||
void emit_arginfo(String *fname, String *cname, Node *n, String *modes, bool dispatch) {
|
||||
// We want to only emit each different arginfo once, as that reduces the
|
||||
// size of both the generated source code and the compiled extension
|
||||
// module. The parameters at this level are just named arg1, arg2, etc
|
||||
// so the arginfo will be the same for any function with the same number
|
||||
// of parameters and (if present) PHP type declarations for parameters and
|
||||
// return type.
|
||||
//
|
||||
// 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
|
||||
// arginfo_used Hash to see if we've already generated it.
|
||||
|
||||
int size() const {
|
||||
return std::max(Len(merged_types), Len(byref));
|
||||
// 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_phpclasses = NewStringEmpty();
|
||||
if (!Equal(fname, "__construct")) {
|
||||
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
|
||||
// except for directed methods. The point of directors is to allow
|
||||
// subclassing in the target language, and if the wrapped method has
|
||||
// a return type declaration then an overriding method in user code
|
||||
// needs to have a compatible declaration.
|
||||
//
|
||||
// The upshot of this is that enabling return type declarations for
|
||||
// existing bindings would break compatibility with user code written
|
||||
// for an older version. For parameters however the situation is
|
||||
// different because if the parent class declares types for parameters
|
||||
// a subclass overriding the function will be compatible whether it
|
||||
// declares them or not.
|
||||
//
|
||||
// directorNode being present seems to indicate if this method or one
|
||||
// it inherits from is directed, which is what we care about here.
|
||||
// Using (!is_member_director(n)) would get it wrong for testcase
|
||||
// director_frob.
|
||||
out_phptype = get_phptype(0, out_phpclasses);
|
||||
}
|
||||
}
|
||||
|
||||
// ### in arginfo_code will be replaced with the id once that is known.
|
||||
String *arginfo_code = NewStringEmpty();
|
||||
if (out_phptype) {
|
||||
if (Len(out_phpclasses)) {
|
||||
Replace(out_phpclasses, "\\", "\\\\", DOH_REPLACE_ANY);
|
||||
Printf(arginfo_code, "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(swig_arginfo_###, 0, %d, %s, %s)\n", num_required, out_phpclasses, out_phptype);
|
||||
} else {
|
||||
Printf(arginfo_code, "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(swig_arginfo_###, 0, %d, %s)\n", num_required, out_phptype);
|
||||
}
|
||||
} else {
|
||||
Printf(arginfo_code, "ZEND_BEGIN_ARG_INFO_EX(swig_arginfo_###, 0, 0, %d)\n", num_required);
|
||||
}
|
||||
|
||||
int phptypes_size = size();
|
||||
for (int param_count = 1; param_count < phptypes_size; ++param_count) {
|
||||
String *phpclasses = NewStringEmpty();
|
||||
String *phptype = get_phptype(param_count, phpclasses);
|
||||
|
||||
int byref = get_byref(param_count);
|
||||
|
||||
// FIXME: Should we be doing byref for return value as well?
|
||||
|
||||
if (phptype) {
|
||||
if (Len(phpclasses)) {
|
||||
// We need to double any backslashes (which are PHP namespace
|
||||
// separators) in the PHP class names as they get turned into
|
||||
// C strings by the ZEND_ARG_OBJ_TYPE_MASK macro.
|
||||
Replace(phpclasses, "\\", "\\\\", DOH_REPLACE_ANY);
|
||||
Printf(arginfo_code, " ZEND_ARG_OBJ_TYPE_MASK(%d,arg%d,%s,%s,NULL)\n", byref, param_count, phpclasses, phptype);
|
||||
} else {
|
||||
Printf(arginfo_code, " ZEND_ARG_TYPE_MASK(%d,arg%d,%s,NULL)\n", byref, param_count, phptype);
|
||||
}
|
||||
} else {
|
||||
Printf(arginfo_code, " ZEND_ARG_INFO(%d,arg%d)\n", byref, param_count);
|
||||
}
|
||||
}
|
||||
Printf(arginfo_code, "ZEND_END_ARG_INFO()\n");
|
||||
|
||||
String *arginfo_id_new = Getattr(n, "sym:name");
|
||||
String *arginfo_id = Getattr(arginfo_used, arginfo_code);
|
||||
if (arginfo_id) {
|
||||
Printf(s_arginfo, "#define swig_arginfo_%s swig_arginfo_%s\n", arginfo_id_new, arginfo_id);
|
||||
} else {
|
||||
// Not had this arginfo before.
|
||||
Setattr(arginfo_used, arginfo_code, arginfo_id_new);
|
||||
arginfo_code = Copy(arginfo_code);
|
||||
Replace(arginfo_code, "###", arginfo_id_new, DOH_REPLACE_FIRST);
|
||||
Append(s_arginfo, arginfo_code);
|
||||
}
|
||||
Delete(arginfo_code);
|
||||
arginfo_code = NULL;
|
||||
|
||||
String *s = cs_entry;
|
||||
if (!s) s = s_entry;
|
||||
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);
|
||||
} else {
|
||||
if (dispatch) {
|
||||
if (wrap_nonclass_global) {
|
||||
Printf(s, " ZEND_NAMED_FE(%(lower)s,%s,swig_arginfo_%s)\n", Getattr(n, "sym:name"), fname, arginfo_id_new);
|
||||
}
|
||||
|
||||
if (wrap_nonclass_fake_class) {
|
||||
(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);
|
||||
}
|
||||
} else {
|
||||
if (wrap_nonclass_global) {
|
||||
Printf(s, " PHP_FE(%s,swig_arginfo_%s)\n", fname, arginfo_id_new);
|
||||
}
|
||||
|
||||
if (wrap_nonclass_fake_class) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -726,125 +845,9 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
int num_required = phptypes->adjust_num_required(emit_num_required(l));
|
||||
phptypes->adjust_num_required(emit_num_required(l));
|
||||
|
||||
// We want to only emit each different arginfo once, as that reduces the
|
||||
// size of both the generated source code and the compiled extension
|
||||
// module. The parameters at this level are just named arg1, arg2, etc
|
||||
// so the arginfo will be the same for any function with the same number
|
||||
// of parameters and (if present) PHP type declarations for parameters and
|
||||
// return type.
|
||||
//
|
||||
// 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
|
||||
// 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_phpclasses = NewStringEmpty();
|
||||
if (!Equal(fname, "__construct")) {
|
||||
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
|
||||
// except for directed methods. The point of directors is to allow
|
||||
// subclassing in the target language, and if the wrapped method has
|
||||
// a return type declaration then an overriding method in user code
|
||||
// needs to have a compatible declaration.
|
||||
//
|
||||
// The upshot of this is that enabling return type declarations for
|
||||
// existing bindings would break compatibility with user code written
|
||||
// for an older version. For parameters however the situation is
|
||||
// different because if the parent class declares types for parameters
|
||||
// a subclass overriding the function will be compatible whether it
|
||||
// declares them or not.
|
||||
//
|
||||
// directorNode being present seems to indicate if this method or one
|
||||
// it inherits from is directed, which is what we care about here.
|
||||
// Using (!is_member_director(n)) would get it wrong for testcase
|
||||
// director_frob.
|
||||
out_phptype = phptypes->get_phptype(0, out_phpclasses);
|
||||
}
|
||||
}
|
||||
|
||||
// ### in arginfo_code will be replaced with the id once that is known.
|
||||
String *arginfo_code = NewStringEmpty();
|
||||
if (out_phptype) {
|
||||
if (Len(out_phpclasses)) {
|
||||
Replace(out_phpclasses, "\\", "\\\\", DOH_REPLACE_ANY);
|
||||
Printf(arginfo_code, "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(swig_arginfo_###, 0, %d, %s, %s)\n", num_required, out_phpclasses, out_phptype);
|
||||
} else {
|
||||
Printf(arginfo_code, "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(swig_arginfo_###, 0, %d, %s)\n", num_required, out_phptype);
|
||||
}
|
||||
} else {
|
||||
Printf(arginfo_code, "ZEND_BEGIN_ARG_INFO_EX(swig_arginfo_###, 0, 0, %d)\n", num_required);
|
||||
}
|
||||
|
||||
int phptypes_size = phptypes->size();
|
||||
for (int param_count = 1; param_count < phptypes_size; ++param_count) {
|
||||
String *phpclasses = NewStringEmpty();
|
||||
String *phptype = phptypes->get_phptype(param_count, phpclasses);
|
||||
|
||||
int byref = phptypes->get_byref(param_count);
|
||||
|
||||
// FIXME: Should we be doing byref for return value as well?
|
||||
|
||||
if (phptype) {
|
||||
if (Len(phpclasses)) {
|
||||
// We need to double any backslashes (which are PHP namespace
|
||||
// separators) in the PHP class names as they get turned into
|
||||
// C strings by the ZEND_ARG_OBJ_TYPE_MASK macro.
|
||||
Replace(phpclasses, "\\", "\\\\", DOH_REPLACE_ANY);
|
||||
Printf(arginfo_code, " ZEND_ARG_OBJ_TYPE_MASK(%d,arg%d,%s,%s,NULL)\n", byref, param_count, phpclasses, phptype);
|
||||
} else {
|
||||
Printf(arginfo_code, " ZEND_ARG_TYPE_MASK(%d,arg%d,%s,NULL)\n", byref, param_count, phptype);
|
||||
}
|
||||
} else {
|
||||
Printf(arginfo_code, " ZEND_ARG_INFO(%d,arg%d)\n", byref, param_count);
|
||||
}
|
||||
}
|
||||
Printf(arginfo_code, "ZEND_END_ARG_INFO()\n");
|
||||
|
||||
String *arginfo_id_new = Getattr(n, "sym:name");
|
||||
String *arginfo_id = Getattr(arginfo_used, arginfo_code);
|
||||
if (arginfo_id) {
|
||||
Printf(s_arginfo, "#define swig_arginfo_%s swig_arginfo_%s\n", arginfo_id_new, arginfo_id);
|
||||
} else {
|
||||
// Not had this arginfo before.
|
||||
Setattr(arginfo_used, arginfo_code, arginfo_id_new);
|
||||
arginfo_code = Copy(arginfo_code);
|
||||
Replace(arginfo_code, "###", arginfo_id_new, DOH_REPLACE_FIRST);
|
||||
Append(s_arginfo, arginfo_code);
|
||||
}
|
||||
Delete(arginfo_code);
|
||||
arginfo_code = NULL;
|
||||
|
||||
String *s = cs_entry;
|
||||
if (!s) s = s_entry;
|
||||
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);
|
||||
} else {
|
||||
if (dispatch) {
|
||||
if (wrap_nonclass_global) {
|
||||
Printf(s, " ZEND_NAMED_FE(%(lower)s,%s,swig_arginfo_%s)\n", Getattr(n, "sym:name"), fname, arginfo_id_new);
|
||||
}
|
||||
|
||||
if (wrap_nonclass_fake_class) {
|
||||
(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);
|
||||
}
|
||||
} else {
|
||||
if (wrap_nonclass_global) {
|
||||
Printf(s, " PHP_FE(%s,swig_arginfo_%s)\n", fname, arginfo_id_new);
|
||||
}
|
||||
|
||||
if (wrap_nonclass_fake_class) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
phptypes->emit_arginfo(fname, cname, n, modes, dispatch);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue