From 65032006842f34d43e9c1c893e84e0489e309485 Mon Sep 17 00:00:00 2001 From: Olly Betts Date: Mon, 11 Jul 2022 17:15:34 +1200 Subject: [PATCH] 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. --- Source/Modules/php.cxx | 291 +++++++++++++++++++++-------------------- 1 file changed, 147 insertions(+), 144 deletions(-) diff --git a/Source/Modules/php.cxx b/Source/Modules/php.cxx index 7c7735a65..f79a60a45 100644 --- a/Source/Modules/php.cxx +++ b/Source/Modules/php.cxx @@ -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); } /* ------------------------------------------------------------