先週行われたSpringOne2GXの基調講演で,2番目に登壇したGuillaume Laforge氏が,Groovy 2.4.xと2.5の計画について講演を行った。最も注目されるのは,従来のクラス読み込みトリックに代わってAST(Abstract Syntax Tree)クラスリーダを新たに導入したことによる,コンパイラのパフォーマンス向上だろう。
Groovyコンパイラは起動時,スクリプトからトークンのストリームを生成して,それをCST(Concrete Syntax Tree)にコンパイルする。このCSTは,コンパイラでは実際には利用できないため,ノード階層を持ったAST(Abstract Syntax Tree)に変換される。CSTからASTへのプロセスは変更可能であり,AST変換としてプラグインできる構造に設計されている。Groovyの資料には,“AST変換を利用して,コンパイルプロセスにフックすることが可能です。ASTを修正した上でコンパイルプロセスを継続して,通常のバイトコードを生成することができます。ランタイムメタプログラミングに比較すると,変換結果をクラスファイルとして(つまりバイトコードで)確認することができるというメリットがあります”,と説明されている。さらにこの方法(実行時メタプログラミングとは異なるコンパイル時メタプログラミング)には,実行時にパフォーマンス上のコストが発生しないという面もある。
GroovyでASTを使用した例としては,シングルトン変換,遅延評価を行う@Lazy,流れるようなインターフェース(fluent interface)を使用したAPI構築のための@Builder,ミックスイン変換,不変ASTマクロ,Newify変換などがある。
Laforge氏はInfoQに対して,今回のコンパイラの変更が,IntelliJ IDEAの開発者のひとりであるPeter Gromov氏のコントリビューションであることを教えてくれた。JetBrainでは,同社IDEのGroovyコンパイルを高速化するために,Groovyコンパイラのコンパイルプロセスを改善する方法を探していた。それというのも彼ら自身が,コンパイルに多くの時間を取られ過ぎていたからだ。これが,その時のプル要求である。
一般的に,コンパイル時においては,コンパイル済みのコードやJDKなどに依存するGroovyコードを処理する上で,それらのクラスを読み込んで,そのクラス構造やメソッドシグネチャなどを参照する必要があります。
これまではJava自体の持つクラスローダを使っていましたが,クラスローダがクラスを初期化するために,それに関連する欠点(静的イニシャライザを実行することによる副作用と実行時間)がありました。
それに代えて今回,このコントリビューションを統合して,ASMバイトコードライブラリを使ってバイトコードを直接読み取るようにすることで,より効率的で副作用のない処理が可能になります。
本記事の執筆時点では,具体的な数値は示されていないが,“IntelliJ IDEA内での処理に加えて,Groovyコードベース自体のコンパイルでも明らかな改善が認められた”と,Laforge氏は述べている。
コンパイラの変更に加えて,クラス内のマップコンストラクタ生成を支援する機能として,@MapConstructor変換が新たに開発された。この機能はASTの好例でもあり,次のようなコードが可能になる(以下の例は,現在の2.5スナップショットをベースとしている)。
import groovy.transform.*
@TupleConstructor
class Person {
String first, last
}
@CompileStatic // optional
@ToString(includeSuperProperties=true)
@MapConstructor(pre={ super(args?.first, args?.last); args = args ?: [:] }, post = { first = first?.toUpperCase() })
class Author extends Person {
String bookName
}
assert new Author(first: 'Dierk', last: 'Koenig', bookName: 'ReGinA').toString() == 'Author(ReGinA, DIERK, Koenig)'
assert new Author().toString() == 'Author(null, null, null)'
この例では,コンパイラに対して,クラスに必要なコンストラクタメソッドを追加するAST変換の実行を指示している。生成されるコードは次のようなものになる。
public Author(java.util.Map args) {
super(args?.first, args?.last)
args = args ? args : [:]
if (args.containsKey('bookName')) {
this.bookName = args['bookName']
}
first = first?.toUpperCase()
}
Groovy言語の変更としてはその他に,@Canonical変換のメタ変換化や,変換パラメータのプロパティ検証などがある。
基調講演ではその他,GrailsフレームワークのリーダであるGraeme Rocher氏による,Grails 3.1以降の計画に関する概説があった。Web APIプロファイルとRESTアプリケーションが改善されたGrails 3.1 GAは年内にリリースで,計画中のAngularサポートに加えて,Emberなど単一ページアプリケーションフレームワークについても検討がされている。
それら以外では,“2016年には,Grailsをさらに拡張することも考えています。Nettyなどを使用し,非同期技術と非ブロッキング技術を採用することで,マイクロサービス開発のためのプロファイルを実装することで,サーブレットコンテナを越えるものになる可能性があります”,と氏は説明している。その他の計画としては,GrailsのORM実装であるGORMを拡張して,非同期非ブロッキングAPIをサポートする予定などがある。例えば,MongoDB 3.0はドライバによる非同期サポートがあり,PostgressやMySQLなどのリレーショナルデータベースでも非ブロッキングアクセスAPIが開発されている。これらについても,GORMでサポートされる予定だ。