詹金斯解释一行上的多个对象声明


9

这不是问题,而是一个警告性的故事:我试图节省一些空间,并在Jenkins Declarative管道中声明我的变量,如下所示:

int a, b, c

然后,我将它们初始化为:

a = b = c = 0

在我的代码中,我将这些整数用作for循环中的计数器。我的脚本不断失败,引发了一些异常:

java.lang.NullPointerException: Cannot invoke method next() on null object

而且我肯定知道我的清单是经过硬编码的,因此是有效的。因此,我开始想知道这些计数器是怎么回事,当我在它们上调用getClass()时,詹金斯高兴地告诉我它们不是整数,而是

org.codehaus.groovy.runtime.NullObject

将代码更改为

int a = 0
int b = 0
int c = 0

一切都像魅力。只是想分享这个。也许它将帮助某人节省一些挫败感。

Answers:


12

Jenkins管道使用groovy-cps解释器以连续传递样式执行Groovy代码。这不是普通的Groovy,您可以直接在IDE或Groovy Shell中执行。

Groovy CPS转换您的代码以支持延续传递样式和正确的Groovy表达式,例如:

a = b = c = 0

被转换成更像是的东西:

eval(
  var("a"), 
  assign(
    eval(
      var("b"), 
      assign(
        eval(
          var("c"), 
          assign(0)
        )
      )
    )
  )
)

CPS解释器中此表达式的问题在于,赋值不返回任何值,因此该null值被赋给了变量b,并且变量也发生了同样的事情a

如果您想深入了解CPS调用块,则可以克隆groovy-cps项目并在com.cloudbees.groovy.cps.CpsTransformerTest该类中编写一个简单的测试用例。

@Test
void testMultiVariablesInlineCPS() {
    def cps = parseCps('''
int a, b, c
a = b = c = 0
''')
    println cps
}

然后,您可以在处放置一个断点println cps并运行调试器。当您打开检查窗口时,您将看到与此类似的图片:

在此处输入图片说明

附带说明一下,请记住,当将代码编译为字节码时,Groovy编译器还会转换单行分配。如果您编译一个简单的Groovy脚本,例如:

int a, b, c
a = b = c = 0

println "$a $b $c"

然后在IDE中打开其类文件以将字节码反编译为Java等效文件,您将看到类似以下内容:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import groovy.lang.Binding;
import groovy.lang.Script;
import org.codehaus.groovy.runtime.GStringImpl;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.callsite.CallSite;

public class test extends Script {
    public test() {
        CallSite[] var1 = $getCallSiteArray();
    }

    public test(Binding context) {
        CallSite[] var2 = $getCallSiteArray();
        super(context);
    }

    public static void main(String... args) {
        CallSite[] var1 = $getCallSiteArray();
        var1[0].call(InvokerHelper.class, test.class, args);
    }

    public Object run() {
        CallSite[] var1 = $getCallSiteArray();
        int a = 0;
        int b = 0;
        int c = 0;
        byte var5 = 0;
        return var1[1].callCurrent(this, new GStringImpl(new Object[]{Integer.valueOf(var5), Integer.valueOf(var5), Integer.valueOf(var5)}, new String[]{"", " ", " ", ""}));
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.