You don't have javascript enabled. Good luck! :(

変数展開処理の流れ

最終更新日: 2018/08/04

変数展開記法

hamlet では #{...} という形式で変数展開を行うことができる。

HTML のエスケープなどもこの段階で行われる。

今回はこの処理の流れを追ってみようと思う。

Text -> Html 型への変換部分

今回は Text 型について処理を追うが、その他の型についても大筋の流れは同じである。

【ステップ1】 toHtml (in yesod-core)

#{var} は実際のところ yesod-core パッケージの toHtml 関数を適用している。

つまり、以下の式と同じである。

また yesod-core で定義されている toHtmlblaze-html パッケージの toHtml を再エクスポートしているだけである。

【ステップ2】 toHtml (in blaze-html)

toHtml実装 は以下のとおりである。

toMarkup メソッドは blaze-markup パッケージで定義されている。

【ステップ3】 toMarkup (in blaze-markup)

toMarkup メソッドは ToMarkup のメソッドなので、実装は型によって異なる。

今回は Text 型に着目している。Text 型のインスタンス定義 は以下の通りである。

text 関数は Text.Blaze.Internal モジュールで定義されている。

【ステップ4】 text (in blaze-markup)

text 関数の実装は以下の通り。

ここで出現する TextChoiceString 型の値である。

content 関数もまた、Text.Blaze.Internal モジュールで定義されている。

【ステップ5】 content (in blaze-markup))

content 関数の実装は以下の通り。

ここで出現する ContentMarkupM 型の値である。

つまりここまでの流れとまとめると、以下のようになる

次は、ディスパッチの処理を追っていこう。

ディスパッチ処理

典型的なハンドラは以下のような定義とすることが多いだろう。

ここで注意する点は getHomeR の型は Handler Text ではなく Handler Html だという事実である。

つまり defaultLayout を適用した結果はまだエスケープ処理に移る前段階ということである。

ではどこでエスケープ処理が行われているのだろうか?

それはハンドラが実際に呼び出される場所を特定すれば良い。具体的に言えば YesodDispatch 型クラスがハンドラを実際に呼び出している。

【ステップ1】YesodDispatch 型クラス

ここで少し問題となるのが、YesodDispatch のインスタンスは TH によって自動生成されるという点だ。

TH で生成されるコードの確認方法 を参考に生成されるコードを確認してみよう。

生成されるコードは次のようになる。(読みやすいように、少々整形している)

色々と生成されているが、ここで着目するのは yesodDispatch メソッドである。

その中で yesodRunner getCheck1R env (Just Check1R) req という形で yesodRunner が呼ばれていることがわかる。

【ステップ2】yesodRunner (in yesod-core)

yesodRunner の実装 は以下の通りである。(長いので必要な部分のみ抜き出した)

まずは yesodMiddleware に適用されるが、型が変化しないため、ここではエスケープ処理が行われていないことがわかる。

次に Yesod.Core.Internal.Run モジュールで定義されている runHandler 関数に制御がうつる。

【ステップ3】runHandler

runHadler の実装は以下の通り。(ここでも必要な部分のみを掲載)

次は basicRunHandler が呼ばれる。

【ステップ4】basicRunHandler

basicRunHandler の実装は以下の通り。(ここでも必要な部分のみを掲載)

次に unHandlerFor に適用される。

【ステップ5】unHandlerFor

unHandlerForHandlerFor 型を剥がすための関数である。

実装 は以下のようになっている。

ここで HandlerFor site aIO a に変化する。

つまり先程の res の型が Html だということがわかる。

そのため、次は toTypedContent を見ていく必要がある。

【ステップ6】toTypedContent

toTypedContent は ToTypedContent 型クラスのメソッドである。

Html 型のインスタンス定義 は次のようになっている。

toContent を見てみよう。

【ステップ7】toContent

toContent は ToContent 型クラスのメソッドであり Html 型のインスタンス定義 は以下のようになっている。

ここでやっとレンダリング関数の renderHtmlBuilder が出現した。

レンダリング処理

renderHtmlBuilder は DEPRECATE になっており、renderMarkupBuilder の利用が推奨されている。

【ステップ1】renderMarkupBuilder

renderMarkupBuilder の実装は以下のようになっている。

ここで、この関数に渡される値は Content (Text var) () のような形になっていたことを思い出そう。該当するパターンマッチのみを掲載する。

単純に fromChoiceString を適用するだけのようだ。

【ステップ2】fromChoiceString

fromChoiceString実装は以下のようになっている。

今回関係するのは fromChoiceString (String s) = escapeMarkupEntities s なので escapeMarkupEntities を確認しよう。

【ステップ3】escapeMarkupEntities

escapeMarkupEntities 関数はエクスポートされていないが、実装は以下のようになっている。

つまり、この関数内で実際のエスケープ処理が走っていることがわかる。

まとめ

<, >, &, ", ' の5文字がエスケープされる。