Jenkins Interpretation mehrerer Objektdeklarationen in einer Zeile

9

Dies ist keine Frage, sondern eine warnende Geschichte: Ich habe versucht, Platz zu sparen, und meine Variablen in der Jenkins Declarative-Pipeline wie folgt deklariert:

int a, b, c

Dann habe ich sie initialisiert als:

a = b = c = 0

In meinem Code verwende ich diese Ganzzahlen als Zähler in einer for-Schleife. Mein Skript schlug immer wieder fehl, einige der Ausnahmen wurden ausgelöst:

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

und ich wusste mit Sicherheit, dass meine Liste gültig ist, da sie fest codiert war. Also begann ich mich zu fragen, was mit diesen Zählern los ist, und als ich getClass () auf ihnen aufrief, sagte mir Jenkins glücklich, dass sie keine ganzen Zahlen waren, sondern vielmehr

org.codehaus.groovy.runtime.NullObject

Nach dem Ändern des Codes in

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

alles funktionierte wie ein Zauber. Ich wollte das nur teilen. Vielleicht hilft es jemandem, etwas Frust zu retten.

Funkeln
quelle

Antworten:

12

Jenkins-Pipelines führen Groovy-Code im Continuation-Passing-Stil mit dem Groovy-CPS- Interpreter aus. Dies ist kein Vanilla Groovy, den Sie direkt in der IDE oder in der Groovy Shell ausführen können.

Groovy CPS transformiert Ihren Code, um den Continuation-Passing-Stil und den korrekten Groovy-Ausdruck wie folgt zu unterstützen:

a = b = c = 0

verwandelt sich in etwas, das eher so aussieht:

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

Das Problem mit diesem Ausdruck im CPS-Interpreter besteht darin, dass die Zuweisung keinen nullWert zurückgibt und der Wert daher der Variablen zugewiesen wird b, und dasselbe passiert mit der Variablen a.

Wenn Sie tiefer in den CPS-Aufrufblock eintauchen möchten, können Sie das groovy-cps-Projekt klonen und einen einfachen Testfall in die com.cloudbees.groovy.cps.CpsTransformerTestKlasse schreiben .

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

Dann können Sie einen Haltepunkt am setzen println cpsund den Debugger ausführen. Wenn Sie das Inspektionsfenster öffnen, sehen Sie ein Bild ähnlich dem folgenden:

Geben Sie hier die Bildbeschreibung ein

Beachten Sie als Randnotiz, dass der Groovy-Compiler auch Ihre einzeiligen Zuweisungen transformiert, wenn Sie den Code in den Bytecode kompilieren. Wenn Sie ein einfaches Groovy-Skript wie folgt kompilieren:

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

println "$a $b $c"

Wenn Sie dann die Klassendatei in der IDE öffnen, um den Bytecode in das Java-Äquivalent zu dekompilieren, wird Folgendes angezeigt:

//
// 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[]{"", " ", " ", ""}));
    }
}
Szymon Stepniak
quelle