@@ -252,45 +252,64 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_op
252252 free_alloca (shiftlist , use_heap );
253253}
254254
255+ /* Returns true if every instance of ce1 (or a subclass) is provably an instance
256+ * of the target class. target_ce is the resolved class entry for the target
257+ * when available; it may be NULL for a class declared in the same compilation
258+ * unit that is not yet linked, in which case we fall back to matching
259+ * target_lc against ce1's own declared parent and interfaces. */
255260static bool safe_instanceof (
256261 const zend_class_entry * ce1 ,
257- const zend_class_entry * ce2 ,
262+ const zend_class_entry * target_ce ,
263+ const zend_string * target_lc ,
258264 const zend_script * script ,
259265 const zend_op_array * op_array
260266) {
261- if (ce1 == ce2 ) {
267+ if (target_ce && ce1 == target_ce ) {
268+ return true;
269+ }
270+ if (zend_string_equals_ci (ce1 -> name , target_lc )) {
262271 return true;
263272 }
264273 if (ce1 -> ce_flags & ZEND_ACC_LINKED ) {
265- return instanceof_function (ce1 , ce2 );
274+ return target_ce && instanceof_function (ce1 , target_ce );
266275 }
267276
268- /* TODO Handle unlinked parents ike in unlinked_instanceof()? */
269-
270- if (ce1 -> num_interfaces ) {
271- uint32_t i ;
277+ for (uint32_t i = 0 ; i < ce1 -> num_interfaces ; i ++ ) {
278+ const zend_class_entry * iface ;
272279 if (ce1 -> ce_flags & ZEND_ACC_RESOLVED_INTERFACES ) {
273280 /* Unlike the normal instanceof_function(), we have to perform a recursive
274281 * check here, as the parent interfaces might not have been fully copied yet. */
275- for (i = 0 ; i < ce1 -> num_interfaces ; i ++ ) {
276- if (safe_instanceof (ce1 -> interfaces [i ], ce2 , script , op_array )) {
277- return true;
278- }
279- }
282+ iface = ce1 -> interfaces [i ];
280283 } else {
281- for (i = 0 ; i < ce1 -> num_interfaces ; i ++ ) {
282- const zend_class_entry * ce = zend_optimizer_get_class_entry (script , op_array , ce1 -> interface_names [i ].lc_name );
283- if (!ce ) {
284- continue ;
285- }
286- /* Avoid recursing if class implements itself. */
287- if (ce == ce1 ) {
288- continue ;
289- }
290- if (safe_instanceof (ce , ce2 , script , op_array )) {
291- return true;
292- }
284+ if (zend_string_equals_ci (ce1 -> interface_names [i ].lc_name , target_lc )) {
285+ return true;
293286 }
287+
288+ iface = zend_optimizer_get_class_entry (script , op_array , ce1 -> interface_names [i ].lc_name );
289+ }
290+
291+ /* Skip if unresolvable/not-yet-copied, or the class implements itself. */
292+ if (!iface || iface == ce1 ) {
293+ continue ;
294+ }
295+
296+ if (safe_instanceof (iface , target_ce , target_lc , script , op_array )) {
297+ return true;
298+ }
299+ }
300+
301+ /* ce1 is unlinked here (the linked case returned above), so the
302+ * parent/parent_name union holds the as-yet-unresolved parent name. */
303+ if (ce1 -> parent_name ) {
304+ if (zend_string_equals_ci (ce1 -> parent_name , target_lc )) {
305+ return true;
306+ }
307+
308+ zend_string * parent_lc = zend_string_tolower (ce1 -> parent_name );
309+ const zend_class_entry * parent = zend_optimizer_get_class_entry (script , op_array , parent_lc );
310+ zend_string_release (parent_lc );
311+ if (parent && parent != ce1 && safe_instanceof (parent , target_ce , target_lc , script , op_array )) {
312+ return true;
294313 }
295314 }
296315
@@ -313,8 +332,8 @@ static inline bool can_elide_list_type(
313332 if (ZEND_TYPE_HAS_NAME (* single_type )) {
314333 zend_string * lcname = zend_string_tolower (ZEND_TYPE_NAME (* single_type ));
315334 const zend_class_entry * ce = zend_optimizer_get_class_entry (script , op_array , lcname );
335+ bool result = use_info -> ce && safe_instanceof (use_info -> ce , ce , lcname , script , op_array );
316336 zend_string_release (lcname );
317- bool result = ce && safe_instanceof (use_info -> ce , ce , script , op_array );
318337 if (result == !is_intersection ) {
319338 return result ;
320339 }
0 commit comments