Інтерпретація Дженкінса декількох об'єктних декларацій в одному рядку


9

Це не питання, а скоріше застереження: я намагався заощадити трохи місця і оголосив свої змінні в деклараційному конвеєрі Дженкінса так:

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

все працювало як шарм. Просто хотів цим поділитися. Можливо, це допоможе комусь врятувати розчарування.

Відповіді:


12

Трубопроводи Дженкінса виконують код Groovy у стилі продовження, використовуючи інтерпретатор groovy-cps . Це не ванільна 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[]{"", " ", " ", ""}));
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.