Interprétation Jenkins de plusieurs déclarations d'objets sur une seule ligne

9

Ce n'est pas une question, mais plutôt une mise en garde: j'ai essayé de gagner de l'espace et déclaré mes variables dans le pipeline déclaratif Jenkins comme suit:

int a, b, c

Ensuite, je les ai initialisés comme:

a = b = c = 0

Dans mon code, j'utilise ces entiers comme compteurs dans une boucle for. Mon script échouait encore et encore, certaines des exceptions levées:

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

et je savais avec certitude que ma liste est valide car elle était codée en dur. Alors, j'ai commencé à me demander ce qui se passait avec ces compteurs et quand j'ai appelé getClass () sur eux, Jenkins m'a joyeusement dit qu'ils n'étaient pas des entiers, mais plutôt

org.codehaus.groovy.runtime.NullObject

Après avoir changé le code en

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

tout fonctionnait comme un charme. Je voulais juste partager ça. Peut-être que cela aidera quelqu'un à éviter la frustration.

Scintillait
la source

Réponses:

12

Les pipelines Jenkins exécutent le code Groovy dans le style de passage continu à l'aide de l' interpréteur groovy-cps . Ce n'est pas Groovy vanille que vous pouvez exécuter directement dans l'IDE ou dans Groovy Shell.

Groovy CPS transforme votre code pour prendre en charge le style de passage de continuation et l'expression Groovy correcte comme:

a = b = c = 0

se transforme en quelque chose qui ressemble plus à:

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

Le problème avec cette expression dans l'interpréteur CPS est que l'affectation ne renvoie aucune valeur, et donc la nullvaleur est affectée à la variable b, et la même chose se produit pour la variable a.

Si vous souhaitez approfondir le bloc d'invocations CPS, vous pouvez cloner le projet groovy-cps et écrire un cas de test simple dans la com.cloudbees.groovy.cps.CpsTransformerTestclasse.

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

Ensuite, vous pouvez placer un point d'arrêt sur le println cpset exécuter le débogueur. Lorsque vous ouvrez la fenêtre d'inspection, vous verrez l'image similaire à celle-ci:

entrez la description de l'image ici

En guise de remarque, gardez à l'esprit que le compilateur Groovy transforme également vos affectations de ligne unique lors de la compilation du code en bytecode. Si vous compilez un simple script Groovy comme:

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

println "$a $b $c"

puis vous ouvrez son fichier de classe dans l'EDI pour décompiler le bytecode en équivalent Java, vous verrez quelque chose comme ceci:

//
// 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
la source