アジョブジ星通信

進捗が出た頃に更新されるブログ。

取り急ぎ古いNuGetでもインストールできるパッケージをつくる

CoreTweet 0.7.0.339 を NuGet 2.x 系を使う環境(Visual Studio 2013 でアップデート入れてない or MonoDevelop/Xamarin Studio 6.0.x以下)でインストールしようとすると、こんな感じのエラーが出ることは知ってましたがリリースしてしまいました、が思ったより影響大きかった(エゴサ調べ)ので、なんとかしたお話。

f:id:azyobuzin:20160811000149p:plain

原因

0.7.0.339 の nuspec ファイルの依存パッケージが記述されている部分がこちら。

<dependencies>
    <group targetFramework="net35">
        <dependency id="Newtonsoft.Json" version="9.0.1" />
    </group>
    <group targetFramework="portable-win81+wpa81">
        <dependency id="Newtonsoft.Json" version="9.0.1" />
    </group>
    <group targetFramework="portable-net45+dnxcore50+win8+wpa81+MonoAndroid+xamarinios+MonoTouch">
        <dependency id="Newtonsoft.Json" version="9.0.1" />
    </group>
    <group targetFramework="monoandroid">
        <dependency id="Newtonsoft.Json" version="9.0.1" />
    </group>
    <group targetFramework="monotouch">
        <dependency id="Newtonsoft.Json" version="9.0.1" />
    </group>
    <group targetFramework="xamarinios">
        <dependency id="Newtonsoft.Json" version="9.0.1" />
    </group>
    <group targetFramework="netstandard1.1">
        <dependency id="Newtonsoft.Json" version="9.0.1" />
        <dependency id="System.Dynamic.Runtime" version="4.0.11" />
        <dependency id="System.Globalization" version="4.0.11" />
        <dependency id="System.Linq" version="4.1.0" />
        <dependency id="System.Linq.Expressions" version="4.1.0" />
        <dependency id="System.Net.Http" version="4.1.0" />
        <dependency id="System.Reflection.Extensions" version="4.0.1" />
        <dependency id="System.Runtime.Extensions" version="4.1.0" />
        <dependency id="System.Text.RegularExpressions" version="4.1.0" />
        <dependency id="System.Threading" version="4.0.11" />
    </group>
    <group targetFramework="netcoreapp1.0">
        <dependency id="Newtonsoft.Json" version="9.0.1" />
        <dependency id="System.Dynamic.Runtime" version="4.0.11" />
        <dependency id="System.Globalization" version="4.0.11" />
        <dependency id="System.IO.FileSystem" version="4.0.1" />
        <dependency id="System.Linq" version="4.1.0" />
        <dependency id="System.Linq.Expressions" version="4.1.0" />
        <dependency id="System.Net.Http" version="4.1.0" />
        <dependency id="System.Reflection.Extensions" version="4.0.1" />
        <dependency id="System.Runtime.Extensions" version="4.1.0" />
        <dependency id="System.Security.Cryptography.Algorithms" version="4.2.0" />
        <dependency id="System.Text.RegularExpressions" version="4.1.0" />
        <dependency id="System.Threading" version="4.0.11" />
    </group>
</dependencies>

この中で、 NuGet 2.x 系で対応していないフレームワークは netstandard1.1 と netcoreapp1.0 の2つです。さて、 NuGet 内部では対応していないフレームワークはまとめて「Unsupported 0.0」として扱われます。つまりこういう状況。

<dependencies>
    <group targetFramework="net35">
        <dependency id="Newtonsoft.Json" version="9.0.1" />
    </group>
    <group targetFramework="portable-win81+wpa81">
        <dependency id="Newtonsoft.Json" version="9.0.1" />
    </group>
    <group targetFramework="portable-net45+dnxcore50+win8+wpa81+MonoAndroid+xamarinios+MonoTouch">
        <dependency id="Newtonsoft.Json" version="9.0.1" />
    </group>
    <group targetFramework="monoandroid">
        <dependency id="Newtonsoft.Json" version="9.0.1" />
    </group>
    <group targetFramework="monotouch">
        <dependency id="Newtonsoft.Json" version="9.0.1" />
    </group>
    <group targetFramework="xamarinios">
        <dependency id="Newtonsoft.Json" version="9.0.1" />
    </group>
    <group targetFramework="Unsupported,Version=0.0">
        <dependency id="Newtonsoft.Json" version="9.0.1" />
        <dependency id="System.Dynamic.Runtime" version="4.0.11" />
        <dependency id="System.Globalization" version="4.0.11" />
        <dependency id="System.Linq" version="4.1.0" />
        <dependency id="System.Linq.Expressions" version="4.1.0" />
        <dependency id="System.Net.Http" version="4.1.0" />
        <dependency id="System.Reflection.Extensions" version="4.0.1" />
        <dependency id="System.Runtime.Extensions" version="4.1.0" />
        <dependency id="System.Text.RegularExpressions" version="4.1.0" />
        <dependency id="System.Threading" version="4.0.11" />

        <dependency id="Newtonsoft.Json" version="9.0.1" />
        <dependency id="System.Dynamic.Runtime" version="4.0.11" />
        <dependency id="System.Globalization" version="4.0.11" />
        <dependency id="System.IO.FileSystem" version="4.0.1" />
        <dependency id="System.Linq" version="4.1.0" />
        <dependency id="System.Linq.Expressions" version="4.1.0" />
        <dependency id="System.Net.Http" version="4.1.0" />
        <dependency id="System.Reflection.Extensions" version="4.0.1" />
        <dependency id="System.Runtime.Extensions" version="4.1.0" />
        <dependency id="System.Security.Cryptography.Algorithms" version="4.2.0" />
        <dependency id="System.Text.RegularExpressions" version="4.1.0" />
        <dependency id="System.Threading" version="4.0.11" />
    </group>
</dependencies>

こうすると Unsupported で2個同じパッケージが含まれているので、検証で殺されて、スクリーンショットのエラーメッセージが出るようになるわけです。

この問題は、 netstandard, netcoreapp に対応した 2.11 で解決しています。

どう対処する?

A. 諦めるか、古い NuGet が非対応のフレームワークを1つまでにする

とりあえず CoreTweet 0.7.1.345 では依存パッケージの宣言から netcoreapp を消し去りました(消し去るために少しコードもいじりましたが)。

というわけで、なかなか厳しい状況なので、早く NuGet 3 系が広まって欲しい。 Xamarin Studio 6.1 はよ。

System.Stringのコンストラクタを許すな

というわけで始まりました深夜の CoreCLR ソースコードリーディングのお時間。司会は早くこの記事を書き終えてアニメを見たいazyobuzinがお送りいたします。

普通のコンストラクタ

コンストラクタは、名前「.ctor」、戻り値の型 void で定義されるインスタンスメソッドと考えることができます。そしてオペコード newobj でコンストラクタが指定されると、そのクラスのインスタンスが作成され、第0引数に入れられコンストラクタが呼び出されます。つまり、「メモリ確保 → コンストラクタ呼び出し」という順番になります。

String のコンストラクタ

mscorlib の String クラスのコンストラクタは、すべて [MethodImplAttribute(MethodImplOptions.InternalCall)] が付与されていています(ILでは internalcall 属性, 通称 FCall)。 FCall は ecalllist.hマッピングされた関数を呼び出します。

マネージドメソッドとして定義されたコンストラクタ

では、 String のコンストラクタを呼び出した時に、実際に呼び出される関数を見ていきましょう。まずは FCDynamicSig で定義されているこの5つ。

FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ArrChar_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorCharArrayManaged)
FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ArrChar_Int_Int_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorCharArrayStartLengthManaged)
FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrChar_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorCharPtrManaged)
FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrChar_Int_Int_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorCharPtrStartLengthManaged)
FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_Char_Int_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorCharCountManaged)

https://github.com/dotnet/coreclr/blob/release/1.0.0/src/vm/ecalllist.h#L214-L218

これらは最後に「Managed」がついてることからわかるように、マネージドメソッドを呼び出します。最初の「CtorCharArrayManaged」に対応するメソッドを探すと String クラスに CtorCharArray メソッドがありました。(ecall.h, ecall.cpp を読むと、 String 型のメンバーを舐めているのがわかります。)

CtorCharArray はインスタンスメソッドで、 String を返します。……は?とりあえず、この戻り値が newobj の実行結果となるようです。

アンマネージドのほう

C++で書かれたメソッドを呼び出すものは FCFuncElementSig で定義されています。

FCFuncElementSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrSByt_RetVoid, COMString::StringInitCharPtr)
FCFuncElementSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrSByt_Int_Int_RetVoid, COMString::StringInitCharPtrPartial)

https://github.com/dotnet/coreclr/blob/v1.0.0/src/vm/ecalllist.h#L219-L220

COMString クラスのメンバーの実装は stringnative.cpp にあります。

StringInitCharPtr を見てみると

_ASSERTE(stringThis == 0);      // This is the constructor

なんてコードがありますね。つまり this として null が渡されているようです。ということはマネージドのほうも this は null なのだと思われます。

戻り値はオブジェクトの参照、つまり String です。

まとめると

コンストラクタを internalcall にすると、 FCall で this が null なインスタンスメソッドとして呼び出され、戻り値が生成されたインスタンスとして扱われます。

Decimal のコンストラクタ

他にもコンストラクタが internalcall なものはないかと探していたら Decimal の float, double を引数にとるコンストラクタが internalcall でした。 decimal.cpp の InitSingle と InitDouble が呼び出されるメソッドです。今度は戻り値が void ですね。値型だと戻り値は不要のようです。そもそも値型のコンストラクタは newobj のほかに、普通に call で呼び出すこともあります(コンパイラ依存案件)し、そういうものなのでしょうか。よくわからん。

Unicode正規化を実装する (4) クイックチェック

バックナンバー

  1. Unicode正規化を実装する (1) UCDにふれる - アジョブジ星通信
  2. Unicode正規化を実装する (2) 正規分解・互換分解 - アジョブジ星通信
  3. Unicode正規化を実装する (3) 正規合成 - アジョブジ星通信

本当は正規化の高速化全般について書きたかったのですが、 UAX #15 に「トライ木使うといいんじゃね?」とか書いてあるんですけど、どういう木構造にしたらいいのかさっぱりわからず無事死亡しました。強い方、よろしくお願いします。

というわけで今回は、クイックチェックプロパティを使った、正規化済み判定と合成の高速化について説明していこうと思います。

続きを読む

Unicode正規化を実装する (3) 正規合成

バックナンバー

  1. Unicode正規化を実装する (1) UCDにふれる - アジョブジ星通信
  2. Unicode正規化を実装する (2) 正規分解・互換分解 - アジョブジ星通信

PCの死亡を言い訳に3ヶ月空いてしまいましたが、その間に Unicode 9.0.0 がリリースされたようです。サンプルリポジトリに入っている UCD のコピーを 9.0.0 にアップデートしました。 NormalizationTest.txt のテストケースは前回のサンプルコードのまま問題なくクリアしています。

さて、今回は合成です。合成は分解テーブルのキーと値が逆のテーブルを作って、ひたすらルックアップしていく作業になります。ただし、合成できる文字には細かい規定があるので気をつけましょう。

前回同様、サンプルと見比べながら読み進めてもらえると良いです。結構ファイルが分散しているので、「定義へ移動」が使える Visual Studio を使うと読みやすいかと思います。

続きを読む

ツイートの新構造対応のメモ

これです。

CoreTweet 0.6.3API の変更には対応したのでその使い方について説明を書いておこうと思います。

1. とりあえず tweet_mode=extended をつけておく

新構造のデータを受信するには tweet_mode=extended を指定する必要があります。 CoreTweet でこれをやるには、各 API 呼び出しで、 tweet_mode 引数に TweetMode.extended を指定します。

サンプルコード

t.Statuses.HomeTimeline(tweet_mode: TweetMode.extended); // TweetMode 列挙体を使用
t.Statuses.HomeTimeline(tweet_mode => "extended"); // string で

で、このパラメータを指定すると、 text ではなく full_text フィールドにツイートの中身が入ってくるようになります。 CoreTweet では FullText プロパティになっています。

続きを読む

Unicode正規化を実装する (2) 正規分解・互換分解

前回: Unicode正規化を実装する (1) UCDにふれる - アジョブジ星通信

さて、前回 UnicodeData.txt の読み方をやりましたので、これを使って実際に正規分解・互換分解を実装してみましょう。

サンプルコードと見比べながら説明を読んでいただければと思います。

続きを読む