Dart1.6の主な変更点

もちろんご存知かとは思いますが昨日8月27日にDartのバージョン1.6が出ました。目玉は「Deferred loading」ということで、来るasync/await実装に向けて非同期処理まわりの改善の第一歩ということでしょうかね？

1.6アップデートの公式ブログ記事は[こちら](http://news.dartlang.org/2014/08/dart-16-adds-support-for-deferred.html)です。以降[リリースノート](https://groups.google.com/a/dartlang.org/forum/#!msg/announce/Ek1EROgTD1Q/SR7lFmYjn20J)の内容についてサンプルコードと一緒に解説していきます。

## ライブラリ読み込みの遅延実行をサポート
今回の目玉である **deferred loading** は外部ライブラリのインポートを必要な時まで遅延させ、実行時のロード時間を短縮させることが可能になる機能です。公式の解説記事は[こちら](https://www.dartlang.org/docs/spec/deferred-loading.html)です。

遅延読み込みされる側のライブラリには何も変更は必要ありません。サンプルではシンプルなhello worldのライブラリを定義しています。

```dart:hello.dart
library deferred_loading_example.hello;

printGreeting() {
  print('Hello World, from the deferred library!');
}
```

読み込み側ではまず、import文に`deferred`を付け、名前付きでインポートします。

```dart:main.dart
import 'package:deferred_loading_example/hello.dart' deferred as hello;
```

次に、ライブラリを使う時に`hello.loadLibrary`を実行します。

```dart:main.dart
import 'package:deferred_loading_example/hello.dart' deferred as hello;

main() {
  hello.loadLibrary().then((_) => hello.printGreeting());
}
```

`loadLibrary()`は`Future`を返すので、読み込みが終わり次第`then`でライブラリを使います。

#### "Deferred loading"のポイント
- Webアプリでもコマンドラインアプリでも使える
- `loadLibrary()`は一度呼ばれるとキャッシュされるので2回目からは速い
- 読み込み前のライブラリのクラスや、定数を使おうとするとに未定義なる
- **dart2jsするとライブラリごとに別のjsファイルにビルドされ、遅延読込される**
- Polymer.dartはまだ対応してないので遅い

以上です。loadLibrary()されるまでは読み込まれませんが、エディタ上では参照されているのでうっかりインスタンス作ったり、定数呼び出したりしないように気をつけましょう。

## Pattern#allMatches()に"start"パラメータ追加
[Pattern](https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/dart-core.Pattern)クラスのメソッド`allMatches`にオプショナル引数としてint型の`start`が追加されました。Patternの主な実装である[RegExp](https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/dart:core.RegExp)クラスを例に違いを示したのが次のコードです。

```dart:pattern_test.dart
  var regex = new RegExp(r"[a-z]+[0-9]+[a-z]+");
  var sample = "abc0123def";

  test("Pattern.allMatch without [start]", () {
    expect(regex.allMatches(sample).isNotEmpty, isTrue);
  });

  test("Pattern.allMatch with [start]", () {
    expect(regex.allMatches(sample, 4).isNotEmpty, isFalse);
  });
```

`start`はマッチ検索のスタートインデックスになり、指定しない場合は0で、先頭からの検索になります。今までなかったのが不思議な引数でしたが、これで正規表現周りの実装が楽になるかもしれません。また、この変更に関しては既存のコードへの影響はありません。

## Durationクラスに負の値関連の機能追加
[Duration](https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/dart-core.Duration)クラスは時間の長さ、期間を表すためのクラスです。1.5以前でも四則計算は可能で、負の値を取りうることがありましたが特にドキュメントに記述はありませんでした。1.6からは積極的に負の値を取る状態を利用するようにしたようで、負の状態であることを取得する`isNegative`や絶対値を取得する`abs()`、符号を反転させる`-`演算子がサポートされました。

```dart:duration_test.dart
  var duration1 = new Duration(hours:1); // 1 hours (short)
  var duration2 = new Duration(hours:2); // 2 hours (long)

  var subtruct = duration1 - duration2; // 1 - 2 hours (-1 hours)

  test("isNegative", () {
    expect(subtruct.isNegative, isTrue);
  });

  test("abs()", () {
    expect(subtruct.abs(), equals(duration1));
  });

  test("inverse", () {
    expect(-subtruct, equals(duration1));
  });
```

数のようにDurationが扱えるようになったので日付の計算なんかに便利になったのかもしれません。これも既存のコードに影響はありません。

## FormatExceptionにsourceとoffset引数追加
まずは1.5のFormatExceptionのソースを御覧ください
https://code.google.com/p/dart/source/browse/branches/1.5/dart/sdk/lib/core/exceptions.dart#42

次に1.6のソースを御覧ください
https://code.google.com/p/dart/source/browse/branches/1.6/dart/sdk/lib/core/exceptions.dart#42

めちゃくちゃにソースが長くなってます。今までは`message`引数しか渡せず、エラー表示が貧相だったFormatExceptionですが、新たに`source`と`offset`が追加され、表現力のあるエラー表示になりました。試しに不正なjson文字列をエンコードさせてみます。

```dart:exception_test.dart
    var wrongJson = "{ key:value }";
    try {
      var decode = JSON.decode(wrongJson);
      expect(0, 1);
    } catch(e, s) {
      print(e);
    }
```

これを実行して出力されるのがこんな感じです。

```:stdout
FormatException: Unexpected character (at character 3)
{ key:value }
  ^
```

例外が発生した文字列と、その発生位置まで出力されて、デバッグしやすくなりました。自分でFormatExceptionを投げるときもこれを利用するようにしましょう。

## Uriクラスのセキュリティ方面のバグ修正と機能追加
[Uri](https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/dart-core.Uri)に、不正なUriの使用を制限する目的のバグ修正がなされました。詳細の原文は[こちら](https://groups.google.com/a/dartlang.org/forum/#!msg/announce/uBzErOz1x8s/_u5-imrtpTUJ)ですが、簡単にまとめると

-  不正なUriをパースしなくなった
-  hasAuthority/hasQuery/hasFragmentが追加された
-  その他パーサーの改善

となっています。が、実際に試した所これでいいのか？という感じです

```dart:uri_test.dart
  var wrongUri = Uri.parse("foo:///?#");
  var correctUri = Uri.parse("http://www.google.com/");

  test("Wrong Uri toString()", () {
    // ~1.5: toString() outputs parsed "foo:/"
    expect(wrongUri.toString(), equals("foo:///?#"));
  });

  test("hasAuthority", () {
    expect(wrongUri.hasAuthority, isTrue);
    expect(correctUri.hasAuthority, isTrue);
  });

  test("hasQuery", () {
    expect(wrongUri.hasQuery, isTrue); // Why is ?# detected as query...
    expect(correctUri.hasQuery, isFalse);
  });

  test("hasFragment", () {
    expect(wrongUri.hasFragment, isTrue); // Why is ?# detected as fragment...
    expect(correctUri.hasFragment, isFalse);
  });
```

`hasQuery`と`hasFragment`に関してはUriの仕様的にこれでいいのか不安なんですが、Uriに詳しい人コメントで教えてください。

また、バグ修正だけでなくUriに新しく`replace`メソッドが追加されています。Uriの各要素を置換して別のUriを作ることが出来ます。文字列操作をしなくてよくなるのでとても便利なメソッドです。

```dart:uri_test.dart
  var origin = Uri.parse("http://hogehoge.net:80/A/B/laco#hoge");
  test("replace", () {
    var replaced = origin.replace(
      host: "lacolaco.net",
      port: "8080",
      path: "B/A/laco",
      fragment: "fuga"
    );
    expect(replaced.toString(), equals(
      "http://lacolaco.net:8080/B/A/laco#fuga"
    ));
  });
```

Uriに関する変更は、 **既存のコードへの影響があります** 。不正なUriを扱っている可能性がある場合は、1.6にアップデート後に見直しが必要になるかもしれません。

## Futureクラスに静的メソッド`doWhile`追加
[Future](https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/dart-async.Future)クラスに`doWhile(Function)`メソッドが追加されました。引数に渡した関数が`False`を返すまで無限にFutureを投げ続けます。

```dart:future_test.dart
    var i = 0;

    Future.doWhile(() {
      print(++i);
      return i != 3;
    }).whenComplete(() {
      print("comp");
    });
```

このコードの出力はこのようになりました。

```stdout
12
3
comp
```

1行目が1と2が並んでいるのは、1回目の処理と2回目以降の処理ではおそらく非同期処理なので改行が入る前に次の`print`が呼ばれるからだと思われますが、よくわかりません。詳しい検証は割愛します。これは既存のコードに影響はありません。

また、asyncパッケージ周りでは`Zone`クラスにいくつかの変更が入っていますが内部的な改善なので割愛します。

## HTTP通信周りのセキュリティ的改善
見出しにこそなっていませんが変更の規模的にはここが一番大きいような気もします。詳しいアナウンスは[こちら](https://groups.google.com/a/dartlang.org/forum/#!msg/announce/hgBChNDlats/Ieu1AUGTeFsJ)です。まだ自分でも把握しきれていないのでおおまかな部分だけ要約すると、

#### HttpServerクラスの変更点
- HttpServerに新しいプロパティ`defaultResponseHeaders`を追加
- HttpHeaderに新しく以下のデフォルト値を設定
 - Content-Type: text/plain; charset=utf-8
 - X-Frame-Options: SAMEORIGIN
 - X-Content-Type-Options: nosniff
 - X-XSS-Protection: 1; mode=block

#### HttpClientクラスの変更点
- `autoUncompress`プロパティを追加。Bodyのデータの圧縮を自動で解凍するかどうかを設定できるようになった。

ものすごく大雑把なので、HttpServer周りは自分でもう少し情報を集めてから個別に記事を作るかもしれません。

## ByteBufferクラスにas***関数大量追加
[ByteBuffer](https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/dart-typed_data.ByteBuffer)クラスに多数の`as****`関数が追加されました。

----
途中から疲れて最後の方は手抜きになりましたがDart 1.6の変更点はこんな感じです。これに加えて実はpubの方に機能の追加があったはずなんですが、何故かリリースノートに載っていません。"pub global"コマンドに関しては後日アナウンスがあり次第多分記事を書きます。