为什么 begin-next-end 的行为在 ruby 和 jruby 中不同?
Why is the behaviour of begin-next-end different in ruby and jruby?
比较以下场景(相同,但结果不同):
首先我会在 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
现在在 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
为什么这在 jruby 上没有像在 cruby 上那样失败?这是一个 jruby 错误吗?
我将其归档为 Bug #13064。
我在各种版本的 YARV 以及 JRuby、MRuby 和 Rubinius 的最新版本中测试了您的代码:
YARV 2.2.0(随 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(版本 JRuby 目前声称兼容)
# 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(截至昨天的当前 SVN 主干):
# 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(最新版本)
# 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(matz 自己编写的最小 ISO 兼容 Ruby 实现)
# 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
最有趣的是,MRuby、JRuby 和 Rubinius 实际上同意这种行为,但与 YARV 不同。 YARV 或所有其他的都是错误的。不过,我不能说是哪些。
在 Ruby 1.9 时间范围内,MRI 开始在解析时检测其中一些非法跳转情况,而不是引发 LocalJumpError。 JRuby、MRuby 和 Rubinius 可能仍然保留跳转,并让它在稍后调用时失败,而不是立即引发语法错误。
可能值得使用 JRuby 提交错误。我们的解析器基本上是 MRI 的一个端口,因此我们应该能够引发相同的错误。除此之外,我们可以在编译时执行此分析,然后引发错误。
比较以下场景(相同,但结果不同):
首先我会在 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
现在在 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
为什么这在 jruby 上没有像在 cruby 上那样失败?这是一个 jruby 错误吗?
我将其归档为 Bug #13064。
我在各种版本的 YARV 以及 JRuby、MRuby 和 Rubinius 的最新版本中测试了您的代码:
YARV 2.2.0(随 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(版本 JRuby 目前声称兼容)
# 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(截至昨天的当前 SVN 主干):
# 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(最新版本)
# 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(matz 自己编写的最小 ISO 兼容 Ruby 实现)
# 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
最有趣的是,MRuby、JRuby 和 Rubinius 实际上同意这种行为,但与 YARV 不同。 YARV 或所有其他的都是错误的。不过,我不能说是哪些。
在 Ruby 1.9 时间范围内,MRI 开始在解析时检测其中一些非法跳转情况,而不是引发 LocalJumpError。 JRuby、MRuby 和 Rubinius 可能仍然保留跳转,并让它在稍后调用时失败,而不是立即引发语法错误。
可能值得使用 JRuby 提交错误。我们的解析器基本上是 MRI 的一个端口,因此我们应该能够引发相同的错误。除此之外,我们可以在编译时执行此分析,然后引发错误。