Project1
标题: eval()中异常的正确位置? [打印本页]
作者: 叶子 时间: 2012-9-23 14:15
标题: eval()中异常的正确位置?
本帖最后由 叶子 于 2012-9-26 06:50 编辑
在Ace环境下,eval()里面抛出的异常通常都无法定位到正确的脚本与行号。测试脚本如下
def foo
bar(5)
end
def bar
end
begin
eval('foo')
rescue Exception=>e
p e
p e.backtrace
end
`PAUSE`
def foo
bar(5)
end
def bar
end
begin
eval('foo')
rescue Exception=>e
p e
p e.backtrace
end
`PAUSE`
使用Ace运行结果:
#<ArgumentError: wrong number of arguments (1 for 0)>
["{0151}:9:in `eval'", "(eval):1:in `<main>'", "{0151}:9:in `eval'", "{0151}:9:in `<main>'", "ruby:in `eval'"]
使用ruby 1.9.3运行结果:
#<ArgumentError: wrong number of arguments (1 for 0)>
["eval_test.rb:9:in `eval'", "eval_test.rb:2:in `foo'", "(eval):1:in `<main>'", "eval_test.rb:9:in `eval'", "eval_test.rb:9:in `<main>'"]
可见ruby 1.9.3的stacktrace的第二个是出错的正确位置,而Ace缺失这个信息。
事件脚本党应该深有体会,因为事件运行脚本command_355就是eval(script)
所以有Ace下的获得异常正确位置的解决办法吗?
[line]1[/line]
经过艰苦的讨论,得出一个不完美的解决方案
def wrap_trace_capture(script)
"
begin
#{script}
rescue Exception=>e
true_backtrace = Marshal.load(Marshal.dump(e.backtrace))
e.define_singleton_method(:correct_backtrace) do
e.backtrace.replace(true_backtrace)
end
raise e
end
"
end
begin
eval(wrap_trace_capture('foo'))
rescue Exception=>e
p e
p e.backtrace
e.correct_backtrace if e.respond_to?(:correct_backtrace)
raise e
end
def wrap_trace_capture(script)
"
begin
#{script}
rescue Exception=>e
true_backtrace = Marshal.load(Marshal.dump(e.backtrace))
e.define_singleton_method(:correct_backtrace) do
e.backtrace.replace(true_backtrace)
end
raise e
end
"
end
begin
eval(wrap_trace_capture('foo'))
rescue Exception=>e
p e
p e.backtrace
e.correct_backtrace if e.respond_to?(:correct_backtrace)
raise e
end
有任何改进/建议么?
[line]1[/line]
0回复,似乎没什么人在意这个颇消耗开发时间的问题..
放个即插即用版本好了
#==============================================================================
# ■ EvalStackFix
#------------------------------------------------------------------------------
# 修正eval中抛出的异常的backtrace不正确,进而导致脚本编辑器跳转不正确的问题
# 使用方法:
# 直接插入脚本编辑器
# 放置位置应尽量靠近Main
# 注意事项:
# 本代码仅适用于RPG Maker Ace。相同的机制可应用于其他版本
#==============================================================================
module EvalStackFix
module_function
#--------------------------------------------------------------------------
# ● 包装eval内容,截取正确的backtrace
#--------------------------------------------------------------------------
def wrap(script)
"begin
#{script}
# 上面一行没有缩进是因为script可能包含=begin关键字,而=begin前面不能有空格
rescue Exception=>e
true_backtrace = Marshal.load(Marshal.dump(e.backtrace)) # deep clone
e.define_singleton_method(:correct_backtrace) do
e.backtrace.replace(true_backtrace)
end
raise e
end"
end
end
alias rgss_main_no_eval_stack_fix rgss_main
def rgss_main(&proc)
begin
rgss_main_no_eval_stack_fix(&proc)
rescue Exception=>e
p e
e.correct_backtrace if e.respond_to?(:correct_backtrace)
p e.backtrace
raise e
end
end
alias eval_no_stack_fix eval
def eval(script, *args)
eval_no_stack_fix(EvalStackFix.wrap(script), *args)
end
#==============================================================================
# ■ EvalStackFix
#------------------------------------------------------------------------------
# 修正eval中抛出的异常的backtrace不正确,进而导致脚本编辑器跳转不正确的问题
# 使用方法:
# 直接插入脚本编辑器
# 放置位置应尽量靠近Main
# 注意事项:
# 本代码仅适用于RPG Maker Ace。相同的机制可应用于其他版本
#==============================================================================
module EvalStackFix
module_function
#--------------------------------------------------------------------------
# ● 包装eval内容,截取正确的backtrace
#--------------------------------------------------------------------------
def wrap(script)
"begin
#{script}
# 上面一行没有缩进是因为script可能包含=begin关键字,而=begin前面不能有空格
rescue Exception=>e
true_backtrace = Marshal.load(Marshal.dump(e.backtrace)) # deep clone
e.define_singleton_method(:correct_backtrace) do
e.backtrace.replace(true_backtrace)
end
raise e
end"
end
end
alias rgss_main_no_eval_stack_fix rgss_main
def rgss_main(&proc)
begin
rgss_main_no_eval_stack_fix(&proc)
rescue Exception=>e
p e
e.correct_backtrace if e.respond_to?(:correct_backtrace)
p e.backtrace
raise e
end
end
alias eval_no_stack_fix eval
def eval(script, *args)
eval_no_stack_fix(EvalStackFix.wrap(script), *args)
end
作者: 晴兰 时间: 2012-9-27 00:58
提示: 作者被禁止或删除 内容自动屏蔽
作者: 叶子 时间: 2012-9-27 02:16
晴兰 发表于 2012-9-27 00:58
def foo
bar(5)
end
的确,加了binding就可以让backtrace第一个不被替换成eval了
为啥会这样呢?eval默认就是在现在的binding内执行了
从源代码vm_eval.c#1032来看,应该是跟base_block有关
if (scope != Qnil) {
if (rb_obj_is_kind_of(scope, rb_cBinding)) {
GetBindingPtr(scope, bind);
envval = bind->env;
if (strcmp(file, "(eval)") == 0 && bind->path != Qnil) {
file = RSTRING_PTR(bind->path);
line = bind->first_lineno;
}
}
else {
rb_raise(rb_eTypeError,
"wrong argument type %s (expected Binding)",
rb_obj_classname(scope));
}
GetEnvPtr(envval, env);
base_block = &env->block;
}
else {
rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
if (cfp != 0) {
block = *RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp);
base_block = █
base_block->self = self;
base_block->iseq = cfp->iseq; /* TODO */
}
else {
rb_raise(rb_eRuntimeError, "Can't eval on top of Fiber or Thread");
}
}
if (scope != Qnil) {
if (rb_obj_is_kind_of(scope, rb_cBinding)) {
GetBindingPtr(scope, bind);
envval = bind->env;
if (strcmp(file, "(eval)") == 0 && bind->path != Qnil) {
file = RSTRING_PTR(bind->path);
line = bind->first_lineno;
}
}
else {
rb_raise(rb_eTypeError,
"wrong argument type %s (expected Binding)",
rb_obj_classname(scope));
}
GetEnvPtr(envval, env);
base_block = &env->block;
}
else {
rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
if (cfp != 0) {
block = *RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp);
base_block = █
base_block->self = self;
base_block->iseq = cfp->iseq; /* TODO */
}
else {
rb_raise(rb_eRuntimeError, "Can't eval on top of Fiber or Thread");
}
}
欢迎光临 Project1 (https://rpg.blue/) |
Powered by Discuz! X3.1 |