Interpretazione di Jenkins di più dichiarazioni di oggetti su una riga


9

Questa non è una domanda, ma piuttosto un ammonimento: ho cercato di risparmiare spazio e ho dichiarato le mie variabili nella pipeline dichiarativa di Jenkins in questo modo:

int a, b, c

Quindi, li ho inizializzati come:

a = b = c = 0

Nel mio codice, uso questi numeri interi come contatori in un ciclo for. La mia sceneggiatura continuava a fallire più e più volte, alcune delle eccezioni generate:

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

e sapevo per certo che il mio elenco è valido poiché era codificato. Quindi, ho iniziato a chiedermi cosa stesse succedendo con questi contatori e quando ho chiamato getClass () su di essi, Jenkins mi ha felicemente detto che non erano numeri interi, ma piuttosto

org.codehaus.groovy.runtime.NullObject

Dopo aver modificato il codice in

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

tutto ha funzionato come un fascino. Volevo solo condividere questo. Forse aiuterà qualcuno a risparmiare un po 'di frustrazione.

Risposte:


12

Le pipeline Jenkins eseguono il codice Groovy nello stile di passaggio di continuazione usando l' interprete groovy-cps . Questo non è vaniglia Groovy che puoi eseguire direttamente nell'IDE o in Groovy Shell.

Groovy CPS trasforma il tuo codice per supportare lo stile di passaggio di continuazione e l'espressione Groovy corretta come:

a = b = c = 0

si trasforma in qualcosa che assomiglia di più:

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

Il problema con questa espressione nell'interprete CPS è che l'assegnazione non restituisce alcun valore e quindi il nullvalore viene assegnato alla variabile be la stessa cosa accade alla variabile a.

Se vuoi scavare più a fondo nel blocco delle invocazioni CPS, puoi clonare il progetto groovy-cps e scrivere un semplice test case nella com.cloudbees.groovy.cps.CpsTransformerTestclasse.

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

Quindi puoi mettere un breakpoint in corrispondenza di println cps ed eseguire il debugger. Quando apri la finestra di ispezione, vedrai l'immagine simile a questa:

inserisci qui la descrizione dell'immagine

Come nota a margine, tieni presente che il compilatore Groovy trasforma anche le assegnazioni a riga singola quando compili il codice in bytecode. Se compili un semplice script Groovy come:

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

println "$a $b $c"

e quindi apri il suo file di classe nell'IDE per decompilare il bytecode nell'equivalente Java, vedrai qualcosa del genere:

//
// 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[]{"", " ", " ", ""}));
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.