読者です 読者をやめる 読者になる 読者になる

by shigemk2

当面は技術的なことしか書かない

オープンソースアプリケーションのアーキテクチャ読書会(6) #read_aosa

勉強会 Eclipse

前回のラブライブ!

f:id:shigemk2:20150110141649j:plain

グアムにいましたので不参加です。

プラグインを組み込める設計になっております。

  • IBMの開発したコードがある程度入っている
  • 初期はIBM関係、あとはいろんな会社が開発に参画している
  • 柔軟な開発ができるようなIDE
  • Javaでできている
  • バージョンを指定する方式
  • メニューはxmlで定義しつつ、実際の挙動はJavaで制御している

サンプル

package com.example.helloworld.actions;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
import org.eclipse.jface.dialogs.MessageDialog;
public class ExampleAction implements IWorkbenchWindowActionDelegate {
  private IWorkbenchWindow window;

  public ExampleAction() {
  }

  public void run(IAction action) {
    MessageDialog.openInformation(
      window.getShell(),
      "org.eclipse.helloworld",
      "Hello, Eclipse architecture world");
  }

  public void selectionChanged(IAction action, ISelection selection) {
  }

  public void dispose() {
  }

  public void init(IWorkbenchWindow window) {
    this.window = window;
  }
}

初期のGUIはAWT 最大公約数的な実装しか出来ないので、簡単なやつしか作れない

http://www.tohoho-web.com/java/awt.htm

次にSwing…ではなく、SWTを使うようにしている(当時はSwingは重くて開発者の印象は最悪であった)

ある程度のベースを提供しつつ、細かいところは自前で実装する Swing風のLnF

http://ja.wikipedia.org/wiki/Standard_Widget_Toolkit

コンパイラを自前で用意する

  • JDT
  • インクリメンタルコンパイラ

http://www.fpga-net.jp/fpga/word/incremental_compile.html

ちょっとの修正で全部をコンパイルするのは時間がかかりすぎる(マシンリソース的に)ので、コンパイルを分割する

修正をすぐにコンパイルする

自前ではないコンパイラだと全部コンパイルしてしまう

プラグイン開発環境

プラグインを作るためにはXMLとJavaが必要で、プラグインを作成するのにもIDEがあった

以上、おさらい

7.2 Eclipse 3.0:ランタイム、RCP そしてロボットたち

3.0以前

  • Eclipseのコンポーネントモデルはプラグインで構成→双方向でやりとり
  • クラスローダーを自前で実装したい
  • 拡張と拡張ポイント
  • XMLが広まったタイミングとJavaが広まった時期が一緒だったからJavaとXMLはワンセットみたいなところがある
  • XMLは人間にとって読みづらいからYAMLとかJSONとか使うようになってきた(ただしXMLは機械には読みやすい)

XML

パーサーは比較的簡単に書けるけど最近はあんまり書かないねっっていう話 http://ja.wikipedia.org/wiki/Simple_API_for_XML

<a>
  <abc>1</abc>
  <hoge>2</hoge>
</a>

JSON

a = {
  "abc": 1,
  "hoge": 2
}

http://ja.wikipedia.org/wiki/Diode-transistor_logic

Equinoxプロジェクト

Eclipse 3.0をを作るためのプロジェクト名 Equinox

Yosemite的なコードネーム

いくいのっくす いくいにくすとは関係ない

インキュベータープロジェクト(ベータ版みたいな意味合い) http://incubator.apache.org/projects/

f:id:shigemk2:20150110141515j:plain

JMXやJakarta AvalonそしてOSGi いろいろ使えそうなやつを検討した

OSGi

コンポーネントモデルの標準化 OSGiは使いやすい

http://ja.wikipedia.org/wiki/OSGi

動的読み込みのニーズ

フラグメント(OSや国の言葉を問わない実装をするときに必要)

Equinoxの実装は、OSGiの仕様のリファレンス実装とされている

フラグメントってなんだよ。ってなったけど、たぶんEclipse特有の言葉 http://www.atmarkit.co.jp/fjava/rensai3/eclipsepgnpro01/eclipsepgnpro01_3.html

(粒度が違ったので困った)

互換性の確保が必要だったので、既存プラグインも使えるようにした

プラグイン=バンドル

3.0以前も以後も、メタデータはplugin.xmlに記述するようにした。

org.eclipse.uiには依存関係が書いてあるっぽい。バンドルなのでバージョン管理ができるようだ。

[3.3.0,4.0.0) は以上とか未満とかの書き方

Eclipse 3.1

そもそもバンドルとは

バンドルの必須実行環境(BREE) bundle required execution environment

3.2.0以上4.0.0未満

パッケージはJavaの言語機能の概念 公開APIはあるけど、使いドコロは限定されている

f:id:shigemk2:20150110141726p:plain

  • installed (インストール済)
  • resolved (依存関係解決済み)
  • active (アクティベート)
  • stopping (リソースの後始末)
  • uninstalled (アンインストール)

APIの変更を通知する仕組み + バンドルにバージョンをつける仕組み

OSGiに採用されている「サービス」という仕組みを利用することでバンドル間をより疎結合にできる

疎結合とは (loose coupling) そけつごう: - IT用語辞典バイナリ

フレームワークが、そのサービスの実装をサービスレジストリから取得する。

バンドルの依存関係を直接書くのではなく、複数組み合わせる。

Java のクラスファイルには main メソッドがあるように、Eclipse にも起動用のアプリケーションが定義されている。

Eclipse はさまざまなアプリケーションを提供している。スタンドアロンのヘルプサーバーを起動するアプリケーションもあれば、Ant のタスクや JUnit のテストを実行するアプリケーションもある。

org.eclipse.ui.ide.workbenchはあってもなくてもよくって、実体org.eclipse.ui.ide.application

リッチクライアント・プラットフォーム (RCP)

Rich Client Platform

まったく予想しない使い方をする人たちも想定して作られている。

Eclipse のそもそもの狙いは、IDEを作ったり拡張したりするための基盤とツールを提供することだった。

このあたりはEmacsやらVimやらのいわゆるエディタとなんとなく似ている。

基盤となるプラットフォームの API は安定していて、長期間にわたるサポートが保証されている

リッチクライアント − @IT リッチクライアント用語事典

Eclipse RCP − @IT リッチクライアント用語事典

リッチな見た目のアプリケーションをベースに、開発者は開発に集中できる。

f:id:shigemk2:20150110143509p:plain

自前のやつとのトレードオフ

Eclipse 3.4

アプリケーションを新しいバージョンに簡単に更新できて、新たな機能が使えるようになるというのは、あって当然の仕組みだと考えられている。

Firefoxが良い例。新しいバージョンが出ていることを通知してくれている。

f:id:shigemk2:20150110143957p:plain

Eclipse アプリケーションは、単にフィーチャーやバンドルの寄せ集めだけでできているわけではない。

更新マネージャーはフィーチャーだけを扱うので結構不便です。なので、更新マネージャーに代わるナニカを用意する必要があった。

p2 の概念

IUは原語だとInstall Unitsなので、情報ユニットではなくインストール単位ではなかろうか。

Equinox p2 によって Eclipse プラグインの頭痛を解消する

関心の分離を実現するために、インストールされる内容とそのメタデータを完全に分離した。p2 のリポジトリには、メタデータのリポジトリと生成物のリポジトリの両方が含まれる。

プロファイル (標準化) - Wikipedia

f:id:shigemk2:20150110144753p:plain

プロファイルは、インストールされた IU の一覧だ。たとえば、Eclipse SDK は現在インストールされている内容を記したプロファイルを持っている。

プロファイル=インストールしたやつ、OS、インストールした場所などが含まれる

A profile is a list of IUs in your install. For instance, your Eclipse SDK has a profile that describes your current install. From within Eclipse, you can request an update to a newer version of the build which will create a new profile with a different set of IUs. A profile also provides a list of properties associated with the installation, such as the operating system, windowing system, and architecture parameters. Profiles also store the installation directory and the location. Profiles are held by a profile registry, which can store multiple profiles. The director is responsible for invoking provisioning operations. It works with the planner and the engine. The planner examines the existing profile, and determines the operations that must occur to transform the install into its new state. The engine is responsible for carrying out the actual provisioning operations and installing the new artifacts on disk. Touchpoints are part of the engine that work with the runtime implementation of the system being installed. For instance, for the Eclipse SDK, there is an Eclipse touchpoint which knows how to install bundles. For a Linux system where Eclipse is installed from RPM binaries, the engine would deal with an RPM touchpoint. Also, p2 can perform installs in-process or outside in a separate process, such as a build.

ここの段落、難しいです。

プロファイルは設定セットと訳されたりする。高度なデプロイ。

プロビジョニングとは 【 provisioning 】 - 意味/解説/説明/定義 : IT用語辞典

タッチポイント - MarketingPedia (マーケティング用語集Wiki)

新しいプロビジョニングシステムである p2 には、いろんなメリットがあった。Eclipse がインストールする生成物は、リリースのたびに更新される。

p2 リポジトリの生成物やメタデータを扱える方法を提供した。

バージョン管理的なことをしている。バグ報告が来たらチェックアウトしてやり直す。(変な設定をしたときにバグが出るとか)

ベストエフォート型とは 【 best effort 】 - 意味/解説/説明/定義 : IT用語辞典

昔はzipを手動でダウンロードして展開していたからコンフリクトが起きていてもそれに気づかない可能性があった。

今はp2を使ってそういったコンフリクトを検知して回避している。

7.4 Eclipse 4.0

アーキテクチャは常に、それで適切かどうかを評価し続けないといけない。

  • 新しいテクノロジーを取り込むことができるか?
  • コミュニティの成長を促せるか?
  • 新しい参加者にとって魅力的なものだろうか?

これらの質問に対し、今(というか当時)のEclipseはイエスとは言えない。改善の余地はあるけどこのままだと改善出来ないから、以下を目標にして開発をすすめることにした。

  • Eclipse のプログラミングモデルを単純化すること
  • 新たなコミッターを獲得しやすいよう魅力的にすること
  • ウェブベースのテクノロジーの利点をいかしたプラットフォームをオープンなアーキテクチャとして提供すること

2010/7 4.0の最初のリリース→アーリーアダプターのフィードバックを得たいという狙い

だいたいこんな感じのモデル。

f:id:shigemk2:20150110153701p:plain

クラウドサービスプラットフォーム Cosminexus:Eclipse用語「ワークベンチ」「パースペクティブ」「エディター」「ビュー」「ワークスペース」について:ソフトウェア:日立

  • 4.0からCSSが使えるようになった。
  • モデルを追加するとそれに合わせたコードが生成されるようになった

テーマなんかをCSSで変えてる

guari/eclipse-ui-theme · GitHub

  • プロデューサー
  • コンシューマー
  • ブローカー

f:id:shigemk2:20150110154649p:plain

疎結合なコンポーネント群を目指す

Eclipse 4.x における依存性の注入は、カスタムフレームワークを使って実現する

f:id:shigemk2:20150110154915p:plain

Eclipse 4.0 の目標の一つは、利用者向けの API をシンプルにして、共通サービスを実装しやすくすることだった。

モデルとビューが分離していて、あとは階層構造が勝手に解釈してくれるやつ。

インテント - Android 開発入門

アノテーションヘル

アノテーション - KuniWiki

7.5 結論

ストリームって何=ストリームじゃなくってブランチ的な意味合いじゃないの

the Eclipse 4.x stream was created.

  • いろいろな意見を取り入れてきた
  • APIを単純化してきた
  • 期日はきちんと守るようにしてきた
  • コミュニティが活発になるようにしてきた

このあたりがEclipseが長く愛顧されている原因なのだろう

LLVM

LLVM は包括的なプロジェクトで、各種の低レベルツールコンポーネント (アセンブラやコンパイラ、デバッガなど) が密接に結びついている。

LLVM - Wikipedia

Clang - Wikipedia

コンパイラを作るためのインフラ=LLVM

clangやSwiftはLLVMを使っている。

LLVM は独特の機能を提供するし、すばらしいツール (たとえば Clang コンパイラ 2。これは C/C++/Objective-C のコンパイラで、GCC に比べていろんな利点がある) が含まれていることでも有名だが、LLVM がその他のコンパイラと一線を画す最大の要因は、その内部アーキテクチャだ。

最終的に吐くのはネイティブコード。

その頃のオープンソースなプログラミング言語の実装は、特化型のツールとして設計されていることが多く、たいていは一枚岩の大きな実行ファイルだった。たとえば、GCC などの静的なコンパイラの中からパーサーだけを再利用して静的解析やリファクタリングを施そうとしても、それはとても難しかったのだ。スクリプト言語の中にはそのランタイムを別のアプリケーションに組み込めるように なっているものも多かったが、そのランタイムだって一枚岩なひと固まりのコードで、それをインクルードするかしないかというだけのことだった。一部だけを再利用する手段はなくて、言語をまたがって実装を共有するプロジェクトなどほとんどなかった。

JVMみたく、ScalaやClojureなどそれをベースに使いまわすといったことが出来なかった。

Javaに独自拡張を入れたらSunともめたのでC#を作った。

静的コンパイラ or 実行時コンパイラの二者択一しかなかった。けど、LLVMは両方できるようにしている。

メモリ上にネイティブを持っているのがJIT。

LLVMがだんだん普及してきている(その代表例がOpenCL)

OpenCL - Wikipedia

GPUを変えたらプログラムが動かなくなるのはしんどい。

11.1 古典的なコンパイラの設計

昔ながらの静的コンパイラの構成

  • フロントエンド(ソースコード解析)
  • オプティマイザ(変換によるコードの実行時間の短縮)
  • バックエンド(ターゲット上の命令セットにマップ)

抽象構文木 構文をオブジェクトにする。

抽象構文木

ソースコードを抽象構文木にしてオブジェクトにするのがフロントエンド。

Yacc - Wikipedia

吐いたコードが動いているけどYaccが動いているわけではない。

この設計の実装

コンパイラで複数のソース言語やターゲットアーキテクチャをサポートするようになったときに効いてくるのである。

オプティマイザやバックエンドは既存のものを流用できる。

プロプライエタリ・ソフトウェア - Wikipedia

3フェーズ設計にしておいて求めるスキルを分割することで、参入障壁を低くする。

分野ごとに分けておけば、参加できる人を増やすことができる。

分けておけば、スキルセットがモヒカンな人でも参入しやすい。

11.2 既存の言語の実装

3フェーズコンパイラの利点を完璧に理解している人は少ないので、この設計をもとにコードの共有をやっている人は少ない。

一応共有できている例もあるが、(LLJVMのように)パフォーマンスが二の次になっている例もある。

davidar/lljvm · GitHub

LLJVMでCをコンパイルすると、コンパイル速度も遅いし例外処理の実装も非効率的だしデバッグ機能も貧弱になる。

(CofeeScriptなどが例)

tuples - GCC Wiki

モノリシックとは - OSS用語 Weblio辞書

GCCの一部だけを切り出してライブラリとして再利用できない問題 →LLVMはできる

  • 密結合なところ
  • RMSにやる気がない
  • JVMはコンパイラでどうにかすればなんでも渡せる

11.3 LLVM のコード表現: LLVM IR

最も重要な部分 LLVM Intermediate Representation (IR)

典型的な.llコード

define i32 @add1(i32 %a, i32 %b) {
entry:
  %tmp1 = add i32 %a, %b
  ret i32 %tmp1
}

define i32 @add2(i32 %a, i32 %b) {
entry:
  %tmp1 = icmp eq i32 %a, 0
  br i1 %tmp1, label %done, label %recurse

recurse:
  %tmp2 = sub i32 %a, 1
  %tmp3 = add i32 %b, 1
  %tmp4 = call i32 @add2(i32 %tmp2, i32 %tmp3)
  ret i32 %tmp4

done:
  ret i32 %b
}
  • %は変数
  • アセンブラとC言語の中間
  • サンプルプログラムは再帰で足し算を実装しているっぽい

ペアノの公理 - Wikipedia

unsigned add1(unsigned a, unsigned b) {
  return a+b;
}
// 非効率的な方法での足し算
unsigned add2(unsigned a, unsigned b) {
  if (a == 0) return b;
  return add2(a-1, b+1);
}
  • 3アドレス方式
  • 結果を別のレジスタに生成
  • 多くの RISC 命令セットとは異なり、LLVM はシンプルな型システムによる強い型付け

2 アドレス形式 (X86 など、入力レジスタを破壊してしまう方式) や 1 アドレスマシン (オペランドだけを指定してアキュムレータを操作したり、スタックマシン上でスタックの先頭を操作したりするもの) とは対象的だ。

オプティマイザは特定のソース言語やターゲットマシンの制約を受けない

LLVM IR の最適化

中間表現 - Wikipedia

最適化はこの三段階の構造。

  • 変換できそうなパターンを探す。
  • その変換をしても安全か、そして動きが変わらないかを確かめる。
  • 変換を施し、コードを更新する。

アセンブラの例

...
%example1 = sub i32 %a, %a
...
%example2 = sub i32 %b, 0
...
%tmp = mul i32 %c, 2
%example3 = sub i32 %tmp, %c

オプティマイザみたいなのを自前で実装しようとしたときにこういうふうに書けば作れるよっていう話。

for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ++I)
if (Value *V = SimplifyInstruction(I))
I->replaceAllUsesWith(V);

11.4 LLVM での 3 フェーズ設計の実装

LLVM IR は完全なコード表現である

LLVM IR にはファーストクラスのテキスト表現形式がある GCC のフロントエンドを書きたければ、GIMPLE だけではなく GCC のツリーデータ構造も知らないといけない