mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	Fix false LocalJumpError when branch coverage is enabled
`throw TAG_BREAK` instruction makes a jump only if the continuation of catch of TAG_BREAK exactly matches the instruction immediately following the "send" instruction that is currently being executed. Otherwise, it seems to determine break from proc-closure. Branch coverage may insert some recording instructions after "send" instruction, which broke the conditions for TAG_BREAK to work properly. This change forces to set the continuation of catch of TAG_BREAK immediately after "send" (or "invokesuper") instruction. [Bug #18991]
This commit is contained in:
		
							parent
							
								
									f7db1affd1
								
							
						
					
					
						commit
						4a7d6c2852
					
				
				
				Notes:
				
					git
				
				2022-11-08 05:37:26 +00:00 
				
			
			
			
		
		
					 2 changed files with 38 additions and 1 deletions
				
			
		
							
								
								
									
										25
									
								
								compile.c
									
										
									
									
									
								
							
							
						
						
									
										25
									
								
								compile.c
									
										
									
									
									
								
							|  | @ -7449,7 +7449,30 @@ compile_iter(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in | |||
|                            ISEQ_TYPE_BLOCK, line); | ||||
|         CHECK(COMPILE(ret, "iter caller", node->nd_iter)); | ||||
|     } | ||||
|     ADD_LABEL(ret, retry_end_l); | ||||
| 
 | ||||
|     { | ||||
|         // We need to put the label "retry_end_l" immediately after the last "send" instruction.
 | ||||
|         // This because vm_throw checks if the break cont is equal to the index of next insn of the "send".
 | ||||
|         // (Otherwise, it is considered "break from proc-closure". See "TAG_BREAK" handling in "vm_throw_start".)
 | ||||
|         //
 | ||||
|         // Normally, "send" instruction is at the last.
 | ||||
|         // However, qcall under branch coverage measurement adds some instructions after the "send".
 | ||||
|         //
 | ||||
|         // Note that "invokesuper" appears instead of "send".
 | ||||
|         INSN *iobj; | ||||
|         LINK_ELEMENT *last_elem = LAST_ELEMENT(ret); | ||||
|         iobj = IS_INSN(last_elem) ? (INSN*) last_elem : (INSN*) get_prev_insn((INSN*) last_elem); | ||||
|         while (INSN_OF(iobj) != BIN(send) && INSN_OF(iobj) != BIN(invokesuper)) { | ||||
|             iobj = (INSN*) get_prev_insn(iobj); | ||||
|         } | ||||
|         ELEM_INSERT_NEXT(&iobj->link, (LINK_ELEMENT*) retry_end_l); | ||||
| 
 | ||||
|         // LINK_ANCHOR has a pointer to the last element, but ELEM_INSERT_NEXT does not update it
 | ||||
|         // even if we add an insn to the last of LINK_ANCHOR. So this updates it manually.
 | ||||
|         if (&iobj->link == LAST_ELEMENT(ret)) { | ||||
|             ret->last = (LINK_ELEMENT*) retry_end_l; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (popped) { | ||||
|         ADD_INSN(ret, line_node, pop); | ||||
|  |  | |||
|  | @ -964,4 +964,18 @@ class TestCoverage < Test::Unit::TestCase | |||
|       p :NG | ||||
|     end; | ||||
|   end | ||||
| 
 | ||||
|   def test_tag_break_with_branch_coverage | ||||
|     result = { | ||||
|       :branches => { | ||||
|         [:"&.", 0, 1, 0, 1, 6] => { | ||||
|           [:then, 1, 1, 0, 1, 6] => 1, | ||||
|           [:else, 2, 1, 0, 1, 6] => 0, | ||||
|         }, | ||||
|       }, | ||||
|     } | ||||
|     assert_coverage(<<~"end;", { branches: true }, result) | ||||
|       1&.tap do break end | ||||
|     end; | ||||
|   end | ||||
| end | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Yusuke Endoh
						Yusuke Endoh