[php] Fix missing parameter type declarations

In some cases of overloaded functions the parameter type information was
missing for some or all parameters.

See #2151
This commit is contained in:
Olly Betts 2022-06-12 09:22:27 +12:00
commit c95df57976
2 changed files with 154 additions and 28 deletions

View file

@ -0,0 +1,141 @@
<?php
require "tests.php";
// New functions
check::functions(array('doublevalue1','doublevalue2','seek','seek2','seek3','seek4','seek5','seek6','seek7','seek8','seek9','seeka','seekb','anonymous','booltest','casts1','casts2','chartest1','chartest2','chartest3','chartest4','chartest5','chartest6','dummy','afunction','reftest1','reftest2','chops','exceptionspec','constructorcall','cfunc1','cfunc2','cfunc3','slightly_off_square'));
// New classes
check::classes(array('TrickyInPython','default_args','EnumClass','DerivedEnumClass','Tree','Foo','MyClass1','MyClass2','Except','Statics','Tricky','Klass','ConstMethods','Pointf','CDA'));
// New vars
check::globals(array('CONST_NUM'));
$ec = new EnumClass();
check::equal($ec->blah(), true, "EnumClass::blah() default arguments don't work");
$de = new DerivedEnumClass();
$de->accelerate();
$de->accelerate(EnumClass::SLOW);
check::equal(Statics::staticMethod(), 60, "Statics::staticMethod()");
check::equal(cfunc1(1), 2.0, "cfunc1(1)");
check::equal(cfunc2(1), 3.0, "cfunc2(1)");
check::equal(cfunc3(1), 4.0, "cfunc3(1)");
$f = new Foo();
$f->newname();
$f->newname(1);
$f->defaulted1();
$f->defaulted2();
check::equal($f->double_if_void_ptr_is_null(2, Null), 4, "\$f->double_if_void_ptr_is_null(2, Null)");
check::equal($f->double_if_void_ptr_is_null(3), 6, "\$f->double_if_void_ptr_is_null(3)");
check::equal($f->double_if_void_ptr_is_null(4, Null), 8, "\$f->double_if_void_ptr_is_null(4, Null)");
check::equal($f->double_if_void_ptr_is_null(5, Null), 10, "\$f->double_if_void_ptr_is_null(5, Null)");
check::equal($f->double_if_void_ptr_is_null(6, Null), 12, "\$f->double_if_void_ptr_is_null(6, Null)");
check::equal($f->double_if_void_ptr_is_null(7), 14, "\$f->double_if_void_ptr_is_null(7)");
try {
$f = new Foo(1);
check::fail("Foo::Foo ignore is not working");
} catch (ArgumentCountError $e) {
}
try {
$f = new Foo(1, 2);
check::fail("Foo::Foo ignore is not working");
} catch (ArgumentCountError $e) {
}
try {
$f = new Foo(1, 2, 3);
check::fail("Foo::Foo ignore is not working");
} catch (ArgumentCountError $e) {
}
try {
$m = $f->meth(1);
check::fail("Foo::meth ignore is not working");
} catch (Error $e) {
}
try {
$m = $f->meth(1, 2);
check::fail("Foo::meth ignore is not working");
} catch (Error $e) {
}
try {
$m = $f->meth(1, 2, 3);
check::fail("Foo::meth ignore is not working");
} catch (Error $e) {
}
check::equal(Klass::inc(100, new Klass(22))->val, 122, "Klass::inc failed");
check::equal(klass::inc(100)->val, 99, "klass::inc failed");
check::equal(klass::inc()->val, 0, "klass::inc failed");
$tricky = new TrickyInPython();
check::equal($tricky->value_m1(10), -1, "trickyvalue_m1 failed");
check::equal($tricky->value_m1(10, 10), 10, "trickyvalue_m1 failed");
check::equal($tricky->value_0xabcdef(10), 0xabcdef, "trickyvalue_0xabcdef failed");
check::equal($tricky->value_0644(10), 420, "trickyvalue_0644 failed");
check::equal($tricky->value_perm(10), 420, "trickyvalue_perm failed");
check::equal($tricky->value_m01(10), -1, "trickyvalue_m01 failed");
check::equal($tricky->booltest2(), True, "booltest2 failed");
check::equal($tricky->max_32bit_int1(), 0x7FFFFFFF, "max_32bit_int1 failed");
check::equal($tricky->min_32bit_int1(), -2147483648, "min_32bit_int1 failed");
check::equal($tricky->max_32bit_int2(), 0x7FFFFFFF, "max_32bit_int2 failed");
$tricky->too_big_32bit_int1();
$tricky->too_small_32bit_int1();
$tricky->too_big_32bit_int2();
$tricky->too_small_32bit_int2();
seek();
seek(10);
check::equal(booltest(), True, "booltest failed");
check::equal(slightly_off_square(10), 102, "slightly_off_square(10)");
check::equal(slightly_off_square(), 291, "slightly_off_square()");
check::equal(chartest1(), "x", "chartest1()");
check::equal(chartest2(), "\0", "chartest2()");
check::equal(chartest3(), "\1", "chartest3()");
check::equal(chartest4(), "\n", "chartest4()");
check::equal(chartest5(), "B", "chartest5()");
check::equal(chartest6(), "C", "chartest6()");
if (PHP_MAJOR_VERSION >= 8) {
// Regression test for bug in initial implementation of PHP type declarations.
$p = (new ReflectionMethod('TrickyInPython', 'value_m1'))->getParameters();
// empty array in buggy version
check::equal(count($p), 2, "Expected 2 parameters");
check::equal((string)$p[0]->getType(), 'int', "Expected int parameter");
check::equal((string)$p[1]->getType(), 'int', "Expected int parameter");
$p = (new ReflectionMethod('EnumClass', 'blah'))->getParameters();
// empty array in buggy version
check::equal(count($p), 2, "Expected 2 parameters");
check::equal((string)$p[0]->getType(), 'int', "Expected int parameter");
check::equal((string)$p[1]->getType(), 'int', "Expected int parameter");
}
check::done();

View file

@ -13,6 +13,7 @@
*/
#include "swigmod.h"
#include <algorithm>
#include <ctype.h>
#include <errno.h>
@ -269,12 +270,19 @@ public:
while (Len(byref) <= key) {
Append(byref, None);
}
// If any overload takes a particular parameter by reference then the
// dispatch function also needs to take that parameter by reference so
// we can just set unconditionally here.
Setitem(byref, key, ""); // Just needs to be something != None.
}
int get_byref(int key) const {
return byref && key < Len(byref) && Getitem(byref, key) != None;
}
int size() const {
return std::max(Len(merged_types), Len(byref));
}
};
static PHPTypes *phptypes = NULL;
@ -759,36 +767,12 @@ public:
Printf(arginfo_code, "ZEND_BEGIN_ARG_INFO_EX(swig_arginfo_###, 0, 0, %d)\n", num_required);
}
if (Getattr(n, "defaultargs")) {
// Include parameters with default values in the arginfo.
l = Getattr(Getattr(n, "defaultargs"), "parms");
}
int param_count = 0;
for (Parm *p = l; p; p = Getattr(p, "tmap:in:next")) {
String *tmap_in_numinputs = Getattr(p, "tmap:in:numinputs");
// tmap:in:numinputs is unset for varargs, which we don't count here.
if (!tmap_in_numinputs || Equal(tmap_in_numinputs, "0")) {
/* Ignored parameter */
continue;
}
++param_count;
int phptypes_size = phptypes->size();
for (int param_count = 1; param_count < phptypes_size; ++param_count) {
String *phpclasses = NewStringEmpty();
String *phptype = NULL;
if (GetFlag(n, "feature:php:type")) {
phptype = phptypes->get_phptype(param_count, phpclasses);
}
String *phptype = phptypes->get_phptype(param_count, phpclasses);
int byref;
if (!dispatch) {
byref = GetFlag(p, "tmap:in:byref");
if (byref) phptypes->set_byref(param_count);
} else {
// If any overload takes a particular parameter by reference then the
// dispatch function also needs to take that parameter by reference.
byref = phptypes->get_byref(param_count);
}
int byref = phptypes->get_byref(param_count);
// FIXME: Should we be doing byref for return value as well?
@ -1356,6 +1340,7 @@ public:
}
phptypes->process_phptype(p, i + 1, "tmap:in:phptype");
if (GetFlag(p, "tmap:in:byref")) phptypes->set_byref(i + 1);
String *source = NewStringf("args[%d]", i);
Replaceall(tm, "$input", source);