Why is the behaviour of begin-next-end different in ruby and jruby?

199 Views Asked by At

Compare the following scenarios (they are the same, but the outcome is different):

First I'll do it on ruby (cruby)

~> irb
irb(main):001:0> begin
irb(main):002:1* begin
irb(main):003:2* puts 1
irb(main):004:2> next
irb(main):005:2> end
irb(main):006:1> puts 2
irb(main):007:1> end
SyntaxError: (irb):4: Can't escape from eval with next

Now same thing on jruby:

~> jirb
irb(main):001:0> begin
irb(main):002:1* begin
irb(main):003:2* puts 1
irb(main):004:2> next
irb(main):005:2> end
irb(main):006:1> puts 2
irb(main):007:1> end
1
=> nil

Why doesn't this fail on jruby like it does on cruby? Is this a jruby bug?

2

There are 2 best solutions below

0
On BEST ANSWER

I filed this as Bug #13064.

I tested your code in a variety of versions of YARV, as well as on the latest versions of JRuby, MRuby, and Rubinius:

  • YARV 2.2.0 (the build shipping with macOS)

    # ruby -v -W -e 'begin; begin puts 1; next end; puts 2 end'
    ruby 2.0.0p648 (2015-12-16 revision 53162) [universal.x86_64-darwin16]
    -e:1: warning: statement not reached
    -e:1: Invalid next
    -e: compile error (SyntaxError)
    
    # irb -f -d -w -W
    irb(main):001:0> p RUBY_VERSION
    "2.0.0"
    => "2.0.0"
    irb(main):002:0> p RUBY_ENGINE
    "ruby"
    => "ruby"
    irb(main):003:0> 
    irb(main):004:0* begin
    irb(main):005:1*   begin
    irb(main):006:2*     puts 1
    irb(main):007:2>     next
    irb(main):008:2>   end
    irb(main):009:1>   puts 2
    irb(main):010:1> end
    (irb):9: warning: statement not reached
    Exception `SyntaxError' at /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/irb/workspace.rb:86 - (irb):7: Can't escape from eval with next
    Exception `SyntaxError' at /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/irb/workspace.rb:86 - (irb):7: Can't escape from eval with next
    SyntaxError: (irb):7: Can't escape from eval with next
        from (irb)
    irb(main):011:0> 
    irb(main):012:0* exit
    
  • YARV 2.3.1 (the version JRuby claims to be compatible with at the moment)

    # ruby -v -W -e 'begin; begin puts 1; next end; puts 2 end'
    ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin16]
    -e:1: warning: statement not reached
    -e: -e:1: Invalid next (SyntaxError)
    
    # irb -f -d -w -W
    irb(main):001:0> p RUBY_VERSION
    "2.3.1"
    => "2.3.1"
    irb(main):002:0> p RUBY_ENGINE
    "ruby"
    => "ruby"
    irb(main):003:0> 
    irb(main):004:0* begin
    irb(main):005:1*   begin
    irb(main):006:2*     puts 1
    irb(main):007:2>     next
    irb(main):008:2>   end
    irb(main):009:1>   puts 2
    irb(main):010:1> end
    (irb):9: warning: statement not reached
    Exception `SyntaxError' at /Users/joerg/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/workspace.rb:87 - (irb):7: Can't escape from eval with next
    Exception `SyntaxError' at /Users/joerg/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/workspace.rb:87 - (irb):7: Can't escape from eval with next
    Exception `SyntaxError' at /Users/joerg/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/workspace.rb:87 - (irb):7: Can't escape from eval with next
    SyntaxError: (irb):7: Can't escape from eval with next
        from (irb)
    irb(main):011:0> 
    irb(main):012:0* exit
    
  • YARV 2.3.3

    # ruby -v -W -e 'begin; begin puts 1; next end; puts 2 end'
    ruby 2.3.3p222 (2016-11-21 revision 56859) [x86_64-darwin16]
    -e:1: warning: statement not reached
    -e: -e:1: Invalid next (SyntaxError)
    
    # irb -f -d -w -W
    irb(main):001:0> p RUBY_VERSION
    "2.3.3"
    => "2.3.3"
    irb(main):002:0> p RUBY_ENGINE
    "ruby"
    => "ruby"
    irb(main):003:0> 
    irb(main):004:0* begin
    irb(main):005:1*   begin
    irb(main):006:2*     puts 1
    irb(main):007:2>     next
    irb(main):008:2>   end
    irb(main):009:1>   puts 2
    irb(main):010:1> end
    (irb):9: warning: statement not reached
    Exception `SyntaxError' at /Users/joerg/.rbenv/versions/2.3.3/lib/ruby/2.3.0/irb/workspace.rb:87 - (irb):7: Can't escape from eval with next
    Exception `SyntaxError' at /Users/joerg/.rbenv/versions/2.3.3/lib/ruby/2.3.0/irb/workspace.rb:87 - (irb):7: Can't escape from eval with next
    Exception `SyntaxError' at /Users/joerg/.rbenv/versions/2.3.3/lib/ruby/2.3.0/irb/workspace.rb:87 - (irb):7: Can't escape from eval with next
    SyntaxError: (irb):7: Can't escape from eval with next
        from (irb)
    irb(main):011:0> 
    irb(main):012:0* exit
    
  • YARV 2.4.0-preview3:

    # ruby -v -W -e 'begin; begin puts 1; next end; puts 2 end'
    ruby 2.4.0preview3 (2016-11-07 trunk 56661) [x86_64-darwin16]
    -e:1: warning: statement not reached
    -e: -e:1: Invalid next (SyntaxError)
    
    # irb -f -d -w -W
    irb(main):001:0> p RUBY_VERSION
    "2.4.0"
    => "2.4.0"
    irb(main):002:0> p RUBY_ENGINE
    "ruby"
    => "ruby"
    irb(main):003:0> 
    irb(main):004:0* begin
    irb(main):005:1*   begin
    irb(main):006:2*     puts 1
    irb(main):007:2>     next
    irb(main):008:2>   end
    irb(main):009:1>   puts 2
    irb(main):010:1> end
    (irb):9: warning: statement not reached
    Exception `SyntaxError' at /Users/joerg/.rbenv/versions/2.4.0-preview3/lib/ruby/2.4.0/irb/workspace.rb:87 - (irb):7: Can't escape from eval with next
    SyntaxError: (irb):7: Can't escape from eval with next
        from (irb)
    irb(main):011:0> 
    irb(main):012:0* exit
    
  • YARV 2.4.0-dev (current SVN trunk as of yesterday):

    # ruby -v -W -e 'begin; begin puts 1; next end; puts 2 end'
    ruby 2.4.0dev (2016-12-22 trunk 57151) [x86_64-darwin16]
    -e:1: warning: statement not reached
    -e: -e:1: Invalid next (SyntaxError)
    
    # irb -f -d -w -W
    irb(main):001:0> p RUBY_VERSION
    "2.4.0"
    => "2.4.0"
    irb(main):002:0> p RUBY_ENGINE
    "ruby"
    => "ruby"
    irb(main):003:0> 
    irb(main):004:0* begin
    irb(main):005:1*   begin
    irb(main):006:2*     puts 1
    irb(main):007:2>     next
    irb(main):008:2>   end
    irb(main):009:1>   puts 2
    irb(main):010:1> end
    (irb):9: warning: statement not reached
    Exception `SyntaxError' at /Users/joerg/.rbenv/versions/2.4.0-dev/lib/ruby/2.4.0/irb/workspace.rb:87 - (irb):7: Can't escape from eval with next
    SyntaxError: (irb):7: Can't escape from eval with next
        from (irb)
    irb(main):011:0> 
    irb(main):012:0* exit
    
  • Rubinius 3.69

    # rbx -v -W -e 'begin; begin puts 1; next end; puts 2 end'
    rubinius 3.69 (2.3.1 a57071c6 2016-11-17 3.8.1) [x86_64-darwin15.6.0]
    1
                    main # Rubinius::Loader at core/loader.rb:860
                   evals # Rubinius::Loader at core/loader.rb:646
                    eval # Kernel(Rubinius::Loader) at core/kernel.rb:1130
        call_on_instance # Rubinius::BlockEnvironment at core/block_environment.rb:147
       { } in __script__ # Object at -e:1
              jump_error . Rubinius at core/rubinius.rb:279
    
    invalid context for 'next' (LocalJumpError)
    
    An exception occurred evaluating command line code
    
    # irb
    irb(main):001:0> p RUBY_VERSION
    "2.3.1"
    => "2.3.1"
    irb(main):002:0> p RUBY_ENGINE
    "rbx"
    => "rbx"
    irb(main):003:0> 
    irb(main):004:0* begin
    irb(main):005:1*   begin
    irb(main):006:2*     puts 1
    irb(main):007:2>     next
    irb(main):008:2>   end
    irb(main):009:1>   puts 2
    irb(main):010:1> end
    1
    LocalJumpError: invalid context for 'next'
        from core/rubinius.rb:279:in `jump_error'
        from (irb):7
        from core/block_environment.rb:147:in `call_on_instance'
        from core/kernel.rb:1130:in `eval'
        from core/kernel.rb:585:in `loop'
        from core/proc.rb:20:in `call'
        from core/kernel.rb:1067:in `catch'
        from core/throw_catch.rb:8:in `register'
        from core/kernel.rb:1066:in `catch'
        from core/proc.rb:20:in `call'
        from core/kernel.rb:1067:in `catch'
        from core/throw_catch.rb:8:in `register'
        from core/kernel.rb:1066:in `catch'
        from core/code_loader.rb:505:in `load_script'
        from core/code_loader.rb:590:in `load_script'
        from core/loader.rb:679:in `script'
        from core/loader.rb:861:in `main'irb(main):011:0> 
    irb(main):012:0* exit
    
  • JRuby 9.1.6.0 (the latest release)

    # jruby -v -W -e 'begin; begin puts 1; next end; puts 2 end'
    jruby 9.1.6.0 (2.3.1) 2016-11-09 0150a76 Java HotSpot(TM) 64-Bit Server VM 25.102-b14 on 1.8.0_102-b14 +jit [darwin-x86_64]
    1
    LocalJumpError: unexpected next
      <main> at -e:1
    
    # irb -f -d -w -W
    irb(main):001:0> p RUBY_VERSION
    "2.3.1"
    => "2.3.1"
    irb(main):002:0> p RUBY_ENGINE
    "jruby"
    => "jruby"
    irb(main):003:0> 
    irb(main):004:0* begin
    irb(main):005:1*   begin
    irb(main):006:2*     puts 1
    irb(main):007:2>     next
    irb(main):008:2>   end
    irb(main):009:1>   puts 2
    irb(main):010:1> end
    1
    => nil
    irb(main):011:0> 
    irb(main):012:0* exit
    
  • MRuby 1.2.0 (the minimal ISO compliant Ruby implementation written by matz himself)

    # mruby -v -e 'begin; begin puts 1; next end; puts 2 end'
    mruby 1.2.0 (2015-11-17) 
    00001 NODE_SCOPE:
    00001   NODE_BEGIN:
    00001     NODE_BEGIN:
    00001       NODE_BEGIN:
    00001         NODE_CALL:
    00001           NODE_SELF
    00001           method='puts' (383)
    00001           args:
    00001             NODE_INT 1 base 10
    00001         NODE_NEXT:
    00001       NODE_CALL:
    00001         NODE_SELF
    00001         method='puts' (383)
    00001         args:
    00001           NODE_INT 2 base 10
    irep 0x7fe0e3c1b630 nregs=4 nlocals=1 pools=1 syms=1 reps=0
    file: -e
        1 000 OP_LOADSELF   R1      
        1 001 OP_LOADI  R2  1   
        1 002 OP_SEND   R1  :puts   1
        1 003 OP_ERR    "unexpected next"
        1 004 OP_LOADSELF   R1      
        1 005 OP_LOADI  R2  2   
        1 006 OP_SEND   R1  :puts   1
        1 007 OP_STOP
    
    1
    trace:
        [0] -e:1
    LocalJumpError: unexpected next
    
    # irb -v
    mruby 1.2.0 (2015-11-17) 
    mirb - Embeddable Interactive Ruby Shell
    
    > p RUBY_VERSION
    00001 NODE_SCOPE:
    00001   NODE_BEGIN:
    00001     NODE_CALL:
    00001       NODE_SELF
    00001       method='p' (384)
    00001       args:
    00001         NODE_CONST RUBY_VERSION
    irep 0x7fceeac05220 nregs=4 nlocals=1 pools=0 syms=2 reps=0
    file: (mirb)
        1 000 OP_LOADSELF   R1      
        1 001 OP_GETCONST   R2  :RUBY_VERSION
        1 002 OP_SEND   R1  :p  1
        1 003 OP_STOP
    
    "1.9"
     => "1.9"
    > p RUBY_ENGINE
    00002 NODE_SCOPE:
    00002   NODE_BEGIN:
    00002     NODE_CALL:
    00002       NODE_SELF
    00002       method='p' (384)
    00002       args:
    00002         NODE_CONST RUBY_ENGINE
    irep 0x7fceeae05cf0 nregs=4 nlocals=1 pools=0 syms=2 reps=0
    file: (mirb)
        2 000 OP_LOADSELF   R1      
        2 001 OP_GETCONST   R2  :RUBY_ENGINE
        2 002 OP_SEND   R1  :p  1
        2 003 OP_STOP
    
    "mruby"
     => "mruby"
    > 
    00004 NODE_SCOPE:
    00004   NODE_BEGIN:
    irep 0x7fceeac06a50 nregs=2 nlocals=1 pools=0 syms=0 reps=0
    file: (mirb)
        4 000 OP_LOADNIL    R1      
        4 001 OP_STOP
    
     => nil
    > begin
    00005 NODE_NIL
    *   begin
    00007 NODE_NIL
    *     puts 1
    00009 NODE_NIL
    *     next
    00011 NODE_NIL
    *   end
    00013 NODE_NIL
    *   puts 2
    00015 NODE_NIL
    * end
    00012 NODE_SCOPE:
    00012   NODE_BEGIN:
    00012     NODE_BEGIN:
    00012       NODE_BEGIN:
    00012         NODE_CALL:
    00012           NODE_SELF
    00012           method='puts' (383)
    00012           args:
    00012             NODE_INT 1 base 10
    00013         NODE_NEXT:
    00015       NODE_CALL:
    00015         NODE_SELF
    00015         method='puts' (383)
    00015         args:
    00015           NODE_INT 2 base 10
    irep 0x7fceeae01130 nregs=4 nlocals=1 pools=1 syms=1 reps=0
    file: (mirb)
       12 000 OP_LOADSELF   R1      
       12 001 OP_LOADI  R2  1   
       12 002 OP_SEND   R1  :puts   1
       13 003 OP_ERR    "unexpected next"
       15 004 OP_LOADSELF   R1      
       15 005 OP_LOADI  R2  2   
       15 006 OP_SEND   R1  :puts   1
       15 007 OP_STOP
    
    1
    LocalJumpError: unexpected next
    > 
    00012 NODE_SCOPE:
    00012   NODE_BEGIN:
    irep 0x7fceeae078f0 nregs=2 nlocals=1 pools=0 syms=0 reps=0
    file: (mirb)
       12 000 OP_LOADNIL    R1      
       12 001 OP_STOP
    
     => nil
    > exit
    

What is most interesting is that MRuby, JRuby and Rubinius actually agree on the behavior, but differ from YARV. Either YARV or all the other ones are wrong. I cannot say which ones, though.

0
On

Around the Ruby 1.9 timeframe, MRI started to detect some of these illegal jump cases at parse time rather than raising LocalJumpError. JRuby, MRuby, and Rubinius likely still leave the jump in place and let it fail later on when invoked rather than raising a Syntax error right away.

It might be worth filing a bug with JRuby. Our parser is basically a port of MRIs so we should be able to raise the same error. Barring that, we can perform this analysis at compile time and raise the error then.