Project1

标题: 为什么eval传入了__FILE__和__LINE__参数就能精确报错位置? [打印本页]

作者: taroxd    时间: 2014-7-5 12:19
标题: 为什么eval传入了__FILE__和__LINE__参数就能精确报错位置?
本帖最后由 taroxd 于 2014-7-5 13:28 编辑

RUBY 代码复制
  1. class RPG::UsableItem::Damage
  2.   #--------------------------------------------------------------------------
  3.   # ● 根据参数执行计算公式
  4.   #--------------------------------------------------------------------------
  5.   def eval(a, b, v)
  6.     value = b.instance_eval Taroxd::Script.process(@formula),
  7.                             __FILE__, __LINE__
  8.     value > 0 ? value * sign : 0
  9.   end
  10. end


↑ 这是我的脚本。Taroxd::Script.process方法总之是对代码做一些修改(这也是我写这个脚本的原因之一;另一个原因是利用受伤者的上下文来eval),不用在意其中的内容。
就当是这样好了 def Taroxd::Script.process(formula); formula; end

在RGSS3内置脚本中,这个方法是被rescue的。也就是出错了会默默的把技能公式的计算结果归为0。我认为这是不好的特性。报错就是要报的准才行。

于是,我第一次尝试 value = b.instance_eval Taroxd::Script.process(@formula) ,并且刻意输入了会报错的技能公式。结果是:报错信息正确且完整,但报错在eval处,打开脚本编辑器,不会跳转到报错的位置
于是我又尝试了上面的代码,结果是:报错信息正确且完整,报错在真正出错的地方,打开脚本编辑器会跳转到报错的位置

所以我想问,这是什么原理?
传入 __FILE__, __LINE__ 能让eval产生什么样的效果?
VA的脚本编辑器的「跳转至报错位置」是怎么样的原理?

就代码而言,我的问题是解决了的。但是我想要弄清楚得以解决的原因。谢谢。
作者: 无脑之人    时间: 2014-7-5 13:12
摔文档1:http://www.cnblogs.com/darkbaby1 ... ming_file_line.html
摔文档2:http://stackoverflow.com/questio ... ean-in-ruby/2496240
摔文档3:http://www.kuqin.com/rubycndocument/man/stdlib_function.html

eval(expr[, binding[, fname[, lineno=1]]])

    把字符串expr当作Ruby程序来运行并返回其结果。若给第二参数传递Proc对象或Binding对象的话,将在生成该对象的环境中对字符串进行计算。请参考binding。

  1.     def foo
  2.       a = 1
  3.       binding
  4.     end

  5.     eval("p a", foo)  # => 1
  6.    
复制代码
若指定了fname 和 lineno的话,将假定字符串位于fname文件lineno行,并且进行编译。这时可以显示栈跟踪(stack trace)等信息。

作者: taroxd    时间: 2014-7-5 13:27
本帖最后由 taroxd 于 2014-7-5 13:51 编辑
无脑之人 发表于 2014-7-5 13:12
摔文档1:http://www.cnblogs.com/darkbaby1 ... ming_file_line.html
...


不对啊我和这例子不一样。

你给的例子是,在class_eval生成动态方法的时候传入了 __FILE__, __LINE__,因此可以追踪报错信息

但是我那个脚本只是单纯的执行啊。为什么传入了__FILE__之后报错的行数就会变掉呢

试验:

RUBY 代码复制
  1. # LINE 1
  2. def raise!
  3.   raise
  4. end
  5.  
  6. # eval 'raise!'
  7. # D:/Documents/workspace/test.rb:6:in `eval': D:/Documents/workspace/test.rb:3:in `raise!':  (RuntimeError)
  8. #     from (eval):1:in `<main>'
  9. #     from D:/Documents/workspace/test.rb:6:in `eval'
  10. #     from D:/Documents/workspace/test.rb:6:in `<main>'
  11.  
  12. # LINE 12
  13. # eval '
  14. # raise', binding
  15. # D:/Documents/workspace/test.rb:16:in `<main>': unhandled exception
  16. #     from D:/Documents/workspace/test.rb:14:in `eval'
  17. #     from D:/Documents/workspace/test.rb:14:in `<main>'
  18.  
  19. # LINE 19
  20. # eval 'raise', binding, __FILE__,
  21. # __LINE__
  22. # D:/Documents/workspace/test.rb:21:in `<main>': unhandled exception
  23. #     from D:/Documents/workspace/test.rb:20:in `eval'
  24. #     from D:/Documents/workspace/test.rb:20:in `<main>'
  25.  
  26. # LINE 26
  27. # instance_eval 'raise!'
  28. # D:/Documents/workspace/test.rb:27:in `instance_eval': D:/Documents/workspace/test.rb:3:in `raise!':  (RuntimeError)
  29. #     from (eval):1:in `<main>'
  30. #     from D:/Documents/workspace/test.rb:27:in `instance_eval'
  31. #     from D:/Documents/workspace/test.rb:27:in `<main>'
  32.  
  33. # LINE 33
  34. # instance_eval 'raise!', __FILE__,
  35. # __LINE__
  36. # D:/Documents/workspace/test.rb:3:in `raise!': unhandled exception
  37. #     from D:/Documents/workspace/test.rb:35:in `<main>'
  38. #     from D:/Documents/workspace/test.rb:34:in `instance_eval'
  39. #     from D:/Documents/workspace/test.rb:34:in `<main>'


总之加个 binding或者__FILE__, Ruby就会变得聪明?
我觉得就像37行报错一样,加了个__LINE__查找堆栈时,eval这个错误的记录就会记在35行。
但是caller_stack的最外层是如何受到 __FILE__,__LINE__的影响的呢?不传入__FILE__,就会报在eval上面;传入了,就会报在raise!上面
@无脑之人  你的解答告诉了我第37行的原理,但我想要的是第36行的报错原理

话说我没有在元编程啦233
还有,为什么RM的代码高亮不支持HERE DOCUMENT啊啊啊,害得我一直用%{}
作者: 晴兰    时间: 2014-7-6 21:51
提示: 作者被禁止或删除 内容自动屏蔽
作者: taroxd    时间: 2014-7-6 21:53
本帖最后由 taroxd 于 2014-7-7 08:13 编辑
晴兰 发表于 2014-7-6 21:51
https://rpg.blue/thread-249703-1-1.html


那么,instance_eval传入__FILE__参数,可以让backtrace第一个不被替换成instance_eval的原因是?

我翻了一翻源码,发现不传入file参数时,C实现中的file参数值为Qundef

顺着函数,我又找到了这样一段:
C 代码复制
  1. if ((fname = file) == Qundef) {
  2.             fname = rb_usascii_str_new_cstr("(eval)");
  3.         }


接下来我就不知道了……




欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1