Skip to content

Commit 00d9439

Browse files
committed
Groundwork for runtime variance
Still need to collect and then generate ZEND_VERIFY_VARIANCE opcodes
1 parent c318691 commit 00d9439

File tree

8 files changed

+959
-854
lines changed

8 files changed

+959
-854
lines changed

Zend/zend_compile.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6471,6 +6471,8 @@ void zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */
64716471
opline->opcode = ZEND_DECLARE_CLASS;
64726472
}
64736473
}
6474+
6475+
// todo: add to set of classnames that need variance checks iff inheritance is involved
64746476
}
64756477
/* }}} */
64766478

Zend/zend_inheritance.c

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ int _check_inherited_arg_info(
286286
} else {
287287
/* todo: delay to runtime. How? */
288288
/* Don't break bug62441! */
289-
code = 0;
289+
code = -1;
290290
}
291291
} else {
292292
code = 0;
@@ -333,11 +333,7 @@ int _check_inherited_return_type(
333333
return 0;
334334
}
335335

336-
if (!_check_inherited_arg_info(fe, fe_arg_info, proto, proto_arg_info, COVARIANT)) {
337-
return 0;
338-
}
339-
340-
return 1;
336+
return _check_inherited_arg_info(fe, fe_arg_info, proto, proto_arg_info, COVARIANT);
341337
}
342338

343339
static
@@ -365,7 +361,7 @@ int _check_inherited_parameter_type(
365361
}
366362
/* }}} */
367363

368-
static zend_bool zend_do_perform_implementation_check(const zend_function *fe, const zend_function *proto) /* {{{ */
364+
static int zend_do_perform_implementation_check(const zend_function *fe, const zend_function *proto) /* {{{ */
369365
{
370366
uint32_t i, num_args;
371367

@@ -2000,6 +1996,72 @@ ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_class_entry *parent)
20001996
}
20011997
/* }}} */
20021998

1999+
ZEND_API void zend_verify_variance(zend_class_entry *ce) /* {{{ */
2000+
{
2001+
// todo: name? "verify" typically means something else
2002+
// todo: de-duplicate all this
2003+
zend_function *child;
2004+
ZEND_ASSERT(ce->ce_flags & ZEND_ACC_LINKED);
2005+
2006+
ZEND_HASH_FOREACH_PTR(&ce->function_table, child) {
2007+
zend_function *parent = child->common.prototype;
2008+
/* If the parenttype method is private do not enforce a signature */
2009+
if (parent && !(parent ->common.fn_flags & ZEND_ACC_PRIVATE)) {
2010+
uint32_t i, num_args;
2011+
2012+
/* Checks for constructors only if they are declared in an interface,
2013+
* or explicitly marked as abstract
2014+
*/
2015+
if ((ce->constructor == child)
2016+
&& ((parent->common.scope->ce_flags & ZEND_ACC_INTERFACE) == 0
2017+
&& (parent->common.fn_flags & ZEND_ACC_ABSTRACT) == 0))
2018+
{
2019+
continue;
2020+
}
2021+
2022+
/* Check return type compatibility, but only if the prototype already
2023+
* specifies a return type. Adding a new return type is always valid. */
2024+
if (parent->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
2025+
int check = _check_inherited_return_type(
2026+
child, child->common.arg_info - 1,
2027+
parent, parent->common.arg_info - 1);
2028+
if (check < 0) {
2029+
zend_error_noreturn(E_ERROR, "Bad return!");
2030+
}
2031+
}
2032+
2033+
num_args = parent->common.num_args;
2034+
if (parent->common.fn_flags & ZEND_ACC_VARIADIC) {
2035+
num_args++;
2036+
if (child->common.num_args >= parent->common.num_args) {
2037+
num_args = child->common.num_args;
2038+
if (child->common.fn_flags & ZEND_ACC_VARIADIC) {
2039+
num_args++;
2040+
}
2041+
}
2042+
}
2043+
2044+
for (i = 0; i < num_args; i++) {
2045+
zend_arg_info *child_arg_info = &child->common.arg_info[i];
2046+
zend_arg_info *parent_arg_info = (i < parent->common.num_args)
2047+
? &parent->common.arg_info[i]
2048+
: &parent->common.arg_info[parent->common.num_args];
2049+
2050+
int check = _check_inherited_parameter_type(
2051+
child, child_arg_info,
2052+
parent, parent_arg_info);
2053+
2054+
if (check < 0) {
2055+
zend_error_noreturn(E_ERROR, "Bad arg!");
2056+
}
2057+
}
2058+
2059+
}
2060+
} ZEND_HASH_FOREACH_END();
2061+
2062+
}
2063+
/* }}} */
2064+
20032065
/*
20042066
* Local variables:
20052067
* tab-width: 4

Zend/zend_inheritance.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry
2828
ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent_ce);
2929

3030
ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_class_entry *parent_ce);
31+
ZEND_API void zend_verify_variance(zend_class_entry *ce);
3132

3233
void zend_verify_abstract_class(zend_class_entry *ce);
3334
void zend_check_deprecated_constructor(const zend_class_entry *ce);

Zend/zend_vm_def.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6695,6 +6695,22 @@ ZEND_VM_HANDLER(145, ZEND_DECLARE_INHERITED_CLASS_DELAYED, CONST, CONST)
66956695
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
66966696
}
66976697

6698+
ZEND_VM_HANDLER(146, ZEND_VERIFY_VARIANCE, CONST, UNUSED)
6699+
{
6700+
USE_OPLINE
6701+
zend_class_entry *zce;
6702+
6703+
SAVE_OPLINE();
6704+
zce = zend_lookup_class(Z_STR_P(RT_CONSTANT(opline, opline->op1)));
6705+
if (zce) {
6706+
zend_verify_variance(zce);
6707+
} else {
6708+
ZEND_ASSERT(EG(exception));
6709+
HANDLE_EXCEPTION();
6710+
}
6711+
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
6712+
}
6713+
66986714
ZEND_VM_HANDLER(171, ZEND_DECLARE_ANON_CLASS, ANY, ANY, JMP_ADDR)
66996715
{
67006716
zval *zv;

0 commit comments

Comments
 (0)