@@ -501,8 +501,39 @@ void phpdbg_free_watch_element(phpdbg_watch_element *element);
501501void phpdbg_remove_watchpoint (phpdbg_watchpoint_t * watch );
502502void phpdbg_watch_parent_ht (phpdbg_watch_element * element );
503503
504- phpdbg_watch_element * phpdbg_add_watch_element (phpdbg_watchpoint_t * watch , phpdbg_watch_element * element ) {
504+ static bool phpdbg_watch_element_collides (void * addr , phpdbg_watchtype type , phpdbg_watch_element * element ) {
505+ phpdbg_watch_element * old ;
506+ phpdbg_btree_result * res = phpdbg_btree_find (& PHPDBG_G (watchpoint_tree ), (zend_ulong ) addr );
507+
508+ if (res ) {
509+ phpdbg_watchpoint_t * watch = res -> ptr ;
510+ if (watch -> type == type && (old = zend_hash_find_ptr (& watch -> elements , element -> str )) && old != element ) {
511+ return true;
512+ }
513+ }
514+
515+ return (old = zend_hash_find_ptr (& PHPDBG_G (watch_recreation ), element -> str )) && old != element ;
516+ }
517+
518+ static bool phpdbg_ht_watch_element_collides (zval * zv , phpdbg_watch_element * element ) {
519+ HashTable * ht = HT_FROM_ZVP (zv );
520+
521+ if (!ht ) {
522+ return false;
523+ }
524+
525+ return phpdbg_watch_element_collides (HT_WATCH_OFFSET + (char * ) ht , WATCH_ON_HASHTABLE , element );
526+ }
527+
528+ static bool phpdbg_bucket_watch_element_collides (zval * zv , phpdbg_watch_element * element ) {
529+ return phpdbg_watch_element_collides ((Bucket * ) zv , WATCH_ON_BUCKET , element );
530+ }
531+
532+ phpdbg_watch_element * phpdbg_add_watch_element (phpdbg_watchpoint_t * watch , phpdbg_watch_element * element , bool * is_new ) {
505533 phpdbg_btree_result * res ;
534+ if (is_new ) {
535+ * is_new = true;
536+ }
506537 if ((res = phpdbg_btree_find (& PHPDBG_G (watchpoint_tree ), (zend_ulong ) watch -> addr .ptr )) == NULL ) {
507538 phpdbg_watchpoint_t * mem = emalloc (sizeof (* mem ));
508539 * mem = * watch ;
@@ -520,6 +551,9 @@ phpdbg_watch_element *phpdbg_add_watch_element(phpdbg_watchpoint_t *watch, phpdb
520551 if (element != old_element ) {
521552 phpdbg_free_watch_element (element );
522553 }
554+ if (is_new ) {
555+ * is_new = false;
556+ }
523557 return old_element ;
524558 }
525559 }
@@ -534,25 +568,35 @@ phpdbg_watch_element *phpdbg_add_watch_element(phpdbg_watchpoint_t *watch, phpdb
534568 return element ;
535569}
536570
537- phpdbg_watch_element * phpdbg_add_bucket_watch_element (Bucket * bucket , phpdbg_watch_element * element ) {
571+ phpdbg_watch_element * phpdbg_add_bucket_watch_element (Bucket * bucket , phpdbg_watch_element * element , bool * is_new ) {
538572 phpdbg_watchpoint_t watch ;
539573 phpdbg_set_bucket_watchpoint (bucket , & watch );
540- element = phpdbg_add_watch_element (& watch , element );
541- phpdbg_watch_parent_ht (element );
542- return element ;
574+ bool added_new ;
575+ phpdbg_watch_element * added = phpdbg_add_watch_element (& watch , element , & added_new );
576+ if (added_new ) {
577+ phpdbg_watch_parent_ht (added );
578+ }
579+ if (is_new ) {
580+ * is_new = added_new ;
581+ }
582+ return added ;
543583}
544584
545- phpdbg_watch_element * phpdbg_add_ht_watch_element (zval * zv , phpdbg_watch_element * element ) {
585+ phpdbg_watch_element * phpdbg_add_ht_watch_element (zval * zv , phpdbg_watch_element * element , bool * is_new ) {
546586 phpdbg_watchpoint_t watch ;
547587 HashTable * ht = HT_FROM_ZVP (zv );
548588
589+ if (is_new ) {
590+ * is_new = false;
591+ }
592+
549593 if (!ht ) {
550594 return NULL ;
551595 }
552596
553597 element -> flags |= Z_TYPE_P (zv ) == IS_ARRAY ? PHPDBG_WATCH_ARRAY : PHPDBG_WATCH_OBJECT ;
554598 phpdbg_set_ht_watchpoint (ht , & watch );
555- return phpdbg_add_watch_element (& watch , element );
599+ return phpdbg_add_watch_element (& watch , element , is_new );
556600}
557601
558602bool phpdbg_is_recursively_watched (void * ptr , phpdbg_watch_element * element ) {
@@ -588,8 +632,15 @@ void phpdbg_add_recursive_watch_from_ht(phpdbg_watch_element *element, zend_long
588632 child -> parent = element ;
589633 child -> child = NULL ;
590634 child -> parent_container = HT_WATCH_HT (element -> watch );
591- zend_hash_add_ptr (& element -> child_container , child -> str , child );
592- phpdbg_add_bucket_watch_element ((Bucket * ) zv , child );
635+ if (phpdbg_bucket_watch_element_collides (zv , child )) {
636+ phpdbg_free_watch_element (child );
637+ return ;
638+ }
639+ bool added_new ;
640+ phpdbg_add_bucket_watch_element ((Bucket * ) zv , child , & added_new );
641+ if (added_new ) {
642+ zend_hash_add_ptr (& element -> child_container , child -> str , child );
643+ }
593644}
594645
595646void phpdbg_recurse_watch_element (phpdbg_watch_element * element ) {
@@ -627,8 +678,13 @@ void phpdbg_recurse_watch_element(phpdbg_watch_element *element) {
627678 child -> child = NULL ;
628679 element -> child = child ;
629680 }
681+ if (phpdbg_ht_watch_element_collides (zv , child )) {
682+ phpdbg_free_watch_element (child );
683+ element -> child = NULL ;
684+ return ;
685+ }
630686 zend_hash_init (& child -> child_container , 8 , NULL , NULL , 0 );
631- phpdbg_add_ht_watch_element (zv , child );
687+ phpdbg_add_ht_watch_element (zv , child , NULL );
632688 } else if (zend_hash_num_elements (& element -> child_container ) == 0 ) {
633689 zend_string * str ;
634690 zend_long idx ;
@@ -727,7 +783,10 @@ bool phpdbg_try_re_adding_watch_element(zval *parent, phpdbg_watch_element *elem
727783 phpdbg_print_watch_diff (WATCH_ON_HASHTABLE , element -> str , oldPtr , htPtr );
728784 }
729785
730- phpdbg_add_ht_watch_element (parent , element );
786+ if ((element -> flags & PHPDBG_WATCH_RECURSIVE ) && phpdbg_ht_watch_element_collides (parent , element )) {
787+ return false;
788+ }
789+ element = phpdbg_add_ht_watch_element (parent , element , NULL );
731790 } else if ((zv = zend_symtable_find (ht , element -> name_in_parent ))) {
732791 if (element -> flags & PHPDBG_WATCH_IMPLICIT ) {
733792 zval * next = zv ;
@@ -746,9 +805,11 @@ bool phpdbg_try_re_adding_watch_element(zval *parent, phpdbg_watch_element *elem
746805 phpdbg_print_watch_diff (WATCH_ON_ZVAL , element -> str , & element -> backup .zv , zv );
747806 }
748807
808+ if ((element -> flags & PHPDBG_WATCH_RECURSIVE ) && phpdbg_bucket_watch_element_collides (zv , element )) {
809+ return false;
810+ }
749811 element -> parent_container = ht ;
750- phpdbg_add_bucket_watch_element ((Bucket * ) zv , element );
751- phpdbg_watch_parent_ht (element );
812+ element = phpdbg_add_bucket_watch_element ((Bucket * ) zv , element , NULL );
752813 } else {
753814 return false;
754815 }
@@ -1248,14 +1309,23 @@ void phpdbg_list_watchpoints(void) {
12481309 } ZEND_HASH_FOREACH_END ();
12491310}
12501311
1251- static int phpdbg_create_simple_watchpoint (zval * zv , phpdbg_watch_element * element ) {
1252- element -> flags = PHPDBG_WATCH_SIMPLE ;
1253- phpdbg_add_bucket_watch_element ((Bucket * ) zv , element );
1312+ #define PHPDBG_WATCH_DUPLICATE 1
1313+
1314+ static int phpdbg_create_simple_watchpoint (zval * zv , phpdbg_watch_element * * element ) {
1315+ phpdbg_watch_element * added ;
1316+ (* element )-> flags = PHPDBG_WATCH_SIMPLE ;
1317+ bool added_new ;
1318+ added = phpdbg_add_bucket_watch_element ((Bucket * ) zv , * element , & added_new );
1319+ if (!added_new ) {
1320+ * element = added ;
1321+ return PHPDBG_WATCH_DUPLICATE ;
1322+ }
12541323 return SUCCESS ;
12551324}
12561325
1257- static int phpdbg_create_array_watchpoint (zval * zv , phpdbg_watch_element * element ) {
1258- phpdbg_watch_element * new ;
1326+ static int phpdbg_create_array_watchpoint (zval * zv , phpdbg_watch_element * * element_ptr ) {
1327+ phpdbg_watch_element * element = * element_ptr ;
1328+ phpdbg_watch_element * new , * added ;
12591329 zend_string * str ;
12601330 zval * orig_zv = zv ;
12611331
@@ -1264,30 +1334,48 @@ static int phpdbg_create_array_watchpoint(zval *zv, phpdbg_watch_element *elemen
12641334 return FAILURE ;
12651335 }
12661336
1267- new = ecalloc (1 , sizeof (phpdbg_watch_element ));
1268-
12691337 str = strpprintf (0 , "%.*s[]" , (int ) ZSTR_LEN (element -> str ), ZSTR_VAL (element -> str ));
12701338 zend_string_release (element -> str );
12711339 element -> str = str ;
12721340 element -> flags = PHPDBG_WATCH_IMPLICIT ;
1273- phpdbg_add_bucket_watch_element ((Bucket * ) orig_zv , element );
1274- element -> child = new ;
1341+ bool added_new ;
1342+ added = phpdbg_add_bucket_watch_element ((Bucket * ) orig_zv , element , & added_new );
1343+ if (!added_new ) {
1344+ * element_ptr = added ;
1345+ return PHPDBG_WATCH_DUPLICATE ;
1346+ }
1347+ element = added ;
12751348
1349+ new = ecalloc (1 , sizeof (phpdbg_watch_element ));
12761350 new -> flags = PHPDBG_WATCH_SIMPLE ;
12771351 new -> str = zend_string_copy (str );
12781352 new -> parent = element ;
1279- phpdbg_add_ht_watch_element (zv , new );
1353+ added = phpdbg_add_ht_watch_element (zv , new , & added_new );
1354+ if (added != NULL && !added_new ) {
1355+ phpdbg_clean_watch_element (element );
1356+ phpdbg_free_watch_element (element );
1357+ * element_ptr = added ;
1358+ return PHPDBG_WATCH_DUPLICATE ;
1359+ }
1360+ element -> child = new ;
1361+ * element_ptr = element ;
12801362 return SUCCESS ;
12811363}
12821364
1283- static int phpdbg_create_recursive_watchpoint (zval * zv , phpdbg_watch_element * element ) {
1284- element -> flags = PHPDBG_WATCH_RECURSIVE | PHPDBG_WATCH_RECURSIVE_ROOT ;
1285- element -> child = NULL ;
1286- phpdbg_add_bucket_watch_element ((Bucket * ) zv , element );
1365+ static int phpdbg_create_recursive_watchpoint (zval * zv , phpdbg_watch_element * * element ) {
1366+ phpdbg_watch_element * added ;
1367+ (* element )-> flags = PHPDBG_WATCH_RECURSIVE | PHPDBG_WATCH_RECURSIVE_ROOT ;
1368+ (* element )-> child = NULL ;
1369+ bool added_new ;
1370+ added = phpdbg_add_bucket_watch_element ((Bucket * ) zv , * element , & added_new );
1371+ if (!added_new ) {
1372+ * element = added ;
1373+ return PHPDBG_WATCH_DUPLICATE ;
1374+ }
12871375 return SUCCESS ;
12881376}
12891377
1290- typedef struct { int (* callback )(zval * zv , phpdbg_watch_element * ); zend_string * str ; } phpdbg_watch_parse_struct ;
1378+ typedef struct { int (* callback )(zval * zv , phpdbg_watch_element * * ); zend_string * str ; } phpdbg_watch_parse_struct ;
12911379
12921380static int phpdbg_watchpoint_parse_wrapper (char * name , size_t namelen , char * key , size_t keylen , HashTable * parent , zval * zv , phpdbg_watch_parse_struct * info ) {
12931381 int ret ;
@@ -1298,13 +1386,25 @@ static int phpdbg_watchpoint_parse_wrapper(char *name, size_t namelen, char *key
12981386 element -> parent = PHPDBG_G (watch_tmp );
12991387 element -> child = NULL ;
13001388
1301- ret = info -> callback (zv , element );
1389+ ret = info -> callback (zv , & element );
13021390
13031391 efree (name );
13041392 efree (key );
13051393
1306- if (ret != SUCCESS ) {
1394+ if (ret == FAILURE ) {
13071395 phpdbg_remove_watch_element (element );
1396+ } else if (ret == PHPDBG_WATCH_DUPLICATE ) {
1397+ phpdbg_notice ("%.*s is already being watched" , (int ) ZSTR_LEN (element -> str ), ZSTR_VAL (element -> str ));
1398+ while (PHPDBG_G (watch_tmp ) && PHPDBG_G (watch_tmp )-> child == NULL ) {
1399+ phpdbg_watch_element * parent = PHPDBG_G (watch_tmp )-> parent ;
1400+ phpdbg_clean_watch_element (PHPDBG_G (watch_tmp ));
1401+ phpdbg_free_watch_element (PHPDBG_G (watch_tmp ));
1402+ if (parent ) {
1403+ parent -> child = NULL ;
1404+ }
1405+ PHPDBG_G (watch_tmp ) = parent ;
1406+ }
1407+ ret = SUCCESS ;
13081408 } else {
13091409 if (PHPDBG_G (watch_tmp )) {
13101410 PHPDBG_G (watch_tmp )-> child = element ;
@@ -1346,7 +1446,7 @@ static int phpdbg_watchpoint_parse_step(char *name, size_t namelen, char *key, s
13461446 element -> name_in_parent = zend_string_init (key , keylen , 0 );
13471447 element -> parent_container = parent ;
13481448 element -> parent = PHPDBG_G (watch_tmp );
1349- element = phpdbg_add_bucket_watch_element ((Bucket * ) zv , element );
1449+ element = phpdbg_add_bucket_watch_element ((Bucket * ) zv , element , NULL );
13501450
13511451 efree (name );
13521452 efree (key );
@@ -1359,7 +1459,7 @@ static int phpdbg_watchpoint_parse_step(char *name, size_t namelen, char *key, s
13591459 return SUCCESS ;
13601460}
13611461
1362- static int phpdbg_watchpoint_parse_symtables (char * input , size_t len , int (* callback )(zval * , phpdbg_watch_element * )) {
1462+ static int phpdbg_watchpoint_parse_symtables (char * input , size_t len , int (* callback )(zval * , phpdbg_watch_element * * )) {
13631463 zend_class_entry * scope = zend_get_executed_scope ();
13641464 phpdbg_watch_parse_struct info ;
13651465 int ret ;
0 commit comments