设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
查看: 3067|回复: 2
打印 上一主题 下一主题

[讨论] eval()中异常的正确位置?

[复制链接]

Lv1.梦旅人

梦石
0
星屑
65
在线时间
400 小时
注册时间
2005-10-24
帖子
634
跳转到指定楼层
1
发表于 2012-9-23 14:15:44 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有帐号?注册会员

x
本帖最后由 叶子 于 2012-9-26 06:50 编辑

在Ace环境下,eval()里面抛出的异常通常都无法定位到正确的脚本与行号。测试脚本如下
RUBY 代码复制
  1. def foo
  2. bar(5)
  3. end
  4.  
  5. def bar
  6. end
  7.  
  8. begin
  9.   eval('foo')
  10. rescue Exception=>e
  11.   p e
  12.   p e.backtrace
  13. end
  14.  
  15. `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下的获得异常正确位置的解决办法吗?




经过艰苦的讨论,得出一个不完美的解决方案
RUBY 代码复制
  1. def wrap_trace_capture(script)
  2.   "
  3.   begin
  4.     #{script}
  5.   rescue Exception=>e
  6.     true_backtrace = Marshal.load(Marshal.dump(e.backtrace))
  7.     e.define_singleton_method(:correct_backtrace) do
  8.       e.backtrace.replace(true_backtrace)
  9.     end
  10.     raise e
  11.   end
  12.   "
  13. end
  14.  
  15. begin
  16.   eval(wrap_trace_capture('foo'))
  17. rescue Exception=>e
  18.   p e
  19.   p e.backtrace
  20.   e.correct_backtrace if e.respond_to?(:correct_backtrace)
  21.   raise e
  22. end

有任何改进/建议么?




0回复,似乎没什么人在意这个颇消耗开发时间的问题..
放个即插即用版本好了

RUBY 代码复制
  1. #==============================================================================
  2. # ■ EvalStackFix
  3. #------------------------------------------------------------------------------
  4. #   修正eval中抛出的异常的backtrace不正确,进而导致脚本编辑器跳转不正确的问题
  5. # 使用方法:
  6. #   直接插入脚本编辑器
  7. #   放置位置应尽量靠近Main
  8. # 注意事项:
  9. #   本代码仅适用于RPG Maker Ace。相同的机制可应用于其他版本
  10. #==============================================================================
  11.  
  12. module EvalStackFix
  13.   module_function
  14.   #--------------------------------------------------------------------------
  15.   # ● 包装eval内容,截取正确的backtrace
  16.   #--------------------------------------------------------------------------
  17.   def wrap(script)
  18.     "begin
  19. #{script}
  20.   # 上面一行没有缩进是因为script可能包含=begin关键字,而=begin前面不能有空格
  21. rescue Exception=>e
  22.   true_backtrace = Marshal.load(Marshal.dump(e.backtrace)) # deep clone
  23.   e.define_singleton_method(:correct_backtrace) do
  24.     e.backtrace.replace(true_backtrace)
  25.   end
  26.   raise e
  27. end"
  28.   end
  29. end
  30.  
  31. alias rgss_main_no_eval_stack_fix rgss_main
  32. def rgss_main(&proc)
  33.   begin
  34.     rgss_main_no_eval_stack_fix(&proc)
  35.   rescue Exception=>e
  36.     p e
  37.     e.correct_backtrace if e.respond_to?(:correct_backtrace)
  38.     p e.backtrace
  39.     raise e
  40.   end
  41. end
  42.  
  43. alias eval_no_stack_fix eval
  44. def eval(script, *args)
  45.   eval_no_stack_fix(EvalStackFix.wrap(script), *args)
  46. end
头像被屏蔽

Lv2.观梦者 (禁止发言)

梦石
0
星屑
653
在线时间
3774 小时
注册时间
2011-2-26
帖子
1839

开拓者

2
发表于 2012-9-27 00:58:54 | 只看该作者
提示: 作者被禁止或删除 内容自动屏蔽
签名被屏蔽
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
65
在线时间
400 小时
注册时间
2005-10-24
帖子
634
3
 楼主| 发表于 2012-9-27 02:16:10 | 只看该作者
晴兰 发表于 2012-9-27 00:58
def foo
bar(5)
end

的确,加了binding就可以让backtrace第一个不被替换成eval了
为啥会这样呢?eval默认就是在现在的binding内执行了

从源代码vm_eval.c#1032来看,应该是跟base_block有关
RUBY 代码复制
  1. if (scope != Qnil) {
  2.             if (rb_obj_is_kind_of(scope, rb_cBinding)) {
  3.                 GetBindingPtr(scope, bind);
  4.                 envval = bind->env;
  5.                 if (strcmp(file, "(eval)") == 0 && bind->path != Qnil) {
  6.                     file = RSTRING_PTR(bind->path);
  7.                     line = bind->first_lineno;
  8.                 }
  9.             }
  10.             else {
  11.                 rb_raise(rb_eTypeError,
  12.                          "wrong argument type %s (expected Binding)",
  13.                          rb_obj_classname(scope));
  14.             }
  15.             GetEnvPtr(envval, env);
  16.             base_block = &env->block;
  17.         }
  18.         else {
  19.             rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
  20.  
  21.             if (cfp != 0) {
  22.                 block = *RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp);
  23.                 base_block = &block;
  24.                 base_block->self = self;
  25.                 base_block->iseq = cfp->iseq;        /* TODO */
  26.             }
  27.             else {
  28.                 rb_raise(rb_eRuntimeError, "Can't eval on top of Fiber or Thread");
  29.             }
  30.         }
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-12-1 08:05

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表