1

问题

给定以下代码,我想在不运行代码的情况下muffinize提取并传递所有参数:

$ask = false
muffinize("Shoop")
muffinize("Woop") if($ask == true)
if($ask == false)
  muffinize("Pants")
end

这是我期望的输出:

Call#:Args
1:"Shoop"
2:"Woop"
3:"Pants"

到目前为止我在做什么

我正在使用Ripper解析源代码以查找调用该方法的位置。然后我在调用该方法的代码行中查找字符范围。从该行中提取方法后,我将使用Parser gem 对其进行分析。完成后,我将发布代码作为答案,但我很想知道是否有更好的方法来做到这一点。我不太了解代码解析,所以此时我有点盲目地四处游荡。

为什么不只使用正则表达式

正则表达式仍会捕获注释代码块。可能还有其他我还没有考虑过的极端情况。

4

2 回答 2

1

通常,很难(阅读:不可能)静态分析用 Ruby 等动态语言编写的程序,即在不实际运行程序的情况下找到所有可能的方法调用或分支。这样做的原因是动态语言,如 Ruby(还有其他语言,如 Python、Perl 或现在的 Java 或 .NET)允许根据正在运行的程序中的数据动态生成方法调用。一个例子是这样

def deliciousness(pastry)
  self.send("#{pastry}ize", "Icing")
end

deliciousness("muffin")

此代码将调用该muffinize方法并"Icing"作为参数传递。但是,由于源代码中没有提到被调用方法的实际名称,因此您无法知道仅使用静态分析器或解析器。您可以任意使该示例更复杂,并且还涉及代码生成和更多间接层。我的观点是,即使您专门介绍了一些其他情况,也不能确定您得到了所有内容。

但是,您可以做的是跟踪您的代码,因为它实际运行(可能通过测试)并找到您的方法的可能调用。您一定要使用静态分析找到所有这些。一旦你使用了任何网络框架,你肯定会因为涉及大量的元编程和代码生成而倒霉。

于 2013-09-12T10:20:46.197 回答
-1

尽管 Holger Just 是完全正确的,但对我来说,我有一个用例,没有人会在没有明确输入的情况下调用该方法。这是一个奇怪的极端案例,但我想无论如何我都会分享我的解决方案。这是我最终使用的代码:

require "parser/current"
require "ripper"

#Get the source code somehow
code = Ripper.lex(source)
callNumber = 0
code.each_with_index do |line, index|
    if(line[1] == :on_ident and line[2][/^muffinize$/] != nil)
        extractedCode = ""
        charEnd = 0
        lineSperated = nil
        charStart = line[0][1]
        lineNumber = line[0][0]
        #Look ahead till you find the first right parenthese
        i = 0
        while(i < code.length-1)
            if(code[index+i] != nil)
                if(code[index+i][1] == :on_rparen)
                    charEnd = code[index+i][0][1]
                    break
                end
            end
            i += 1
        end
        lineSeperated = source.split(/\n/)
        extractedCode = lineSeperated[lineNumber-1]
        extractedCode = extractedCode[charStart,(charEnd-charStart+1)]
        #Use the somewhat crazy Ruby parser gem to interpret the code as the Ruby interpreter would interpret it.
        callArray = Parser::CurrentRuby.parse(extractedCode).to_a
        text = callArray[2].to_a[0].to_s
        callNumber += 1
        puts "#{callNuber}:#{text}"
    end
end
于 2013-09-12T09:04:45.093 回答