Groovyでjava.lang.VerifyErrorが起きた時の対処法

GroovyでJenkinsプラグインを作っていて、ちょっとしたリファクタリングだけのアップデートなのにプラグイン内でエラーをはいてJenkinsプラグインのロードに失敗したことがありました :scream: 

今回はその対処方法です

# tl;dr
コードレベルで差分がなければコンパイラを疑え

# エラー内容

```shell-session
./gradlew server

（略）

Jenkins home directory: /Users/sueyoshi_go/workspace/github.com/jenkinsci/yaml-axis-plugin/work found at: System.getProperty("JENKINS_HOME")
Failed to instantiate Key[type=org.jenkinsci.plugins.yamlaxis.YamlAxis$DescriptorImpl, annotation=[none]]; skipping this component
com.google.inject.ProvisionException: Guice provision errors:

1) Error injecting constructor, java.lang.VerifyError: (class: org/jenkinsci/plugins/yamlaxis/YamlAxis, method: super$1$forEach signature: (Ljava/util/function/Consumer;)V) Illegal use of nonvirtual function call
  at org.jenkinsci.plugins.yamlaxis.YamlAxis$DescriptorImpl.<init>(YamlAxis.groovy:80)

1 error
    at com.google.inject.internal.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:52)
    at com.google.inject.Scopes$1$1.get(Scopes.java:65)
    at hudson.ExtensionFinder$GuiceFinder$FaultTolerantScope$1.get(ExtensionFinder.java:427)
    at com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:41)
    at com.google.inject.internal.InjectorImpl$3$1.call(InjectorImpl.java:1005)
    at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1051)
    at com.google.inject.internal.InjectorImpl$3.get(InjectorImpl.java:1001)
    at hudson.ExtensionFinder$GuiceFinder._find(ExtensionFinder.java:389)
    at hudson.ExtensionFinder$GuiceFinder.find(ExtensionFinder.java:380)
    at hudson.ClassicPluginStrategy.findComponents(ClassicPluginStrategy.java:386)
    at hudson.ExtensionList.load(ExtensionList.java:300)
    at hudson.ExtensionList.ensureLoaded(ExtensionList.java:253)
    at hudson.ExtensionList.getComponents(ExtensionList.java:154)
    at hudson.DescriptorExtensionList.load(DescriptorExtensionList.java:182)
    at hudson.ExtensionList.ensureLoaded(ExtensionList.java:253)
    at hudson.ExtensionList.iterator(ExtensionList.java:143)
    at hudson.model.ComputerSet.<clinit>(ComputerSet.java:441)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at hudson.init.TaskMethodFinder.invoke(TaskMethodFinder.java:106)
    at hudson.init.TaskMethodFinder$TaskImpl.run(TaskMethodFinder.java:176)
    at org.jvnet.hudson.reactor.Reactor.runTask(Reactor.java:282)
    at jenkins.model.Jenkins$7.runTask(Jenkins.java:904)
    at org.jvnet.hudson.reactor.Reactor$2.run(Reactor.java:210)
    at org.jvnet.hudson.reactor.Reactor$Node.run(Reactor.java:117)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.VerifyError: (class: org/jenkinsci/plugins/yamlaxis/YamlAxis, method: super$1$forEach signature: (Ljava/util/function/Consumer;)V) Illegal use of nonvirtual function call
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
    at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
    at java.lang.Class.getMethod0(Class.java:3018)
    at java.lang.Class.getMethod(Class.java:1784)
    at hudson.model.Descriptor.<init>(Descriptor.java:284)
    at hudson.matrix.AxisDescriptor.<init>(AxisDescriptor.java:43)
    at org.jenkinsci.plugins.yamlaxis.YamlAxis$DescriptorImpl.<init>(YamlAxis.groovy)
    at org.jenkinsci.plugins.yamlaxis.YamlAxis$DescriptorImpl$$FastClassByGuice$$9a80bc2f.newInstance(<generated>)
    at com.google.inject.internal.cglib.reflect.$FastConstructor.newInstance(FastConstructor.java:40)
    at com.google.inject.internal.DefaultConstructionProxyFactory$1.newInstance(DefaultConstructionProxyFactory.java:61)
    at com.google.inject.internal.ConstructorInjector.provision(ConstructorInjector.java:108)
    at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:88)
    at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:269)
    at com.google.inject.internal.ProviderToInternalFactoryAdapter$1.call(ProviderToInternalFactoryAdapter.java:46)
    at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1058)
    at com.google.inject.internal.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:40)
    ... 29 more
```

エラー箇所

https://github.com/jenkinsci/yaml-axis-plugin/blob/0.1.1/src/main/groovy/org/jenkinsci/plugins/yamlaxis/YamlAxis.groovy#L80


# 調査
バージョン間の差分を見てもどこが悪いのか分からず。。。（0.1.0が正常に動いていたバージョンで0.1.1がエラーになったバージョン）

https://github.com/jenkinsci/yaml-axis-plugin/compare/0.1.0...0.1.1

ためしに正常に動いていたバージョンのtagをcheckoutしても同様のエラーが発生。

「コードレベルで差分がないならコンパイラが悪いのでは？」ということで調べたらビンゴでした [^1]

[^1]: さらっと書いていますがコンパイラにたどり着くまでに一晩かかってます

# 使ったJDKのバージョン
v0.1.0（正常に動いていたバージョン）をビルドしたJDK

```shell-session
$ java -v
openjdk version "1.7.0-u10-b09"
OpenJDK Runtime Environment (build 1.7.0-u10-b09-20121015)
OpenJDK 64-Bit Server VM (build 23.6-b03, mixed mode)
```

v0.1.1（エラーになったバージョン）をビルドしたJDK

```shell-session
$ java -v
java version "1.8.0_66"
Java(TM) SE Runtime Environment (build 1.8.0_66-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)
```

ちなみにどうしてv0.1.0とv0.1.1でJDKのバージョンが違うのかというと、プラグイン開発のためローカルでJenkinsを動かすのが重いので途中からスペックの高いPC(JDK 8)を使うようにしたためです

最終的にはv0.1.0をビルドしたのと同じPC（JDK 7）でビルドしなおして動作確認とったらエラーが出なくなっているのを確認できたのでリリースしました。

# 教訓
ユニットテストがグリーンだからといって動作確認もせずにリリースしてはいけない :shit:

# 追記
GroovyでJDK 8サポートされたのは2.3からだった

[The Groovy programming language - Groovy 2.3 release notes](http://www.groovy-lang.org/releasenotes/groovy-2.3.html)

Jenkinsとの兼ね合いでGroovyのバージョンは1.8.9に固定しているので [^jenkins]、JDK 8だと正しくビルドできないってのはありそう。

[^jenkins]: [GroovyとGradleでJenkinsプラグインを作る - Qiita](http://qiita.com/sue445/items/e0ea16d9c38d949114eb) 参照