日本ソフトウェア科学会第 33 回大会 (2016 年度) 講演論文集

部分動的レコードを活用した型付きテンプレートエンジンの試作
逢坂 美冬

上野 雄大

大堀 淳

Web アプリケーションは一般に，事前に用意されたテンプレートに動的に生成した文字列を埋め込むことで，動的に Web ページを生成する．この操作は型無しの文字列操作であるため，セキュリティ上の脆弱性などの問題を生じる危険がある．この問題に対する一つの解決策として，本発表では，テンプレートを型付きのレコードとみなして型を与え，テンプレート操作の整合性を静的に検査するシステムを提案する．このシステムを実現する上での困難のひ
とつは，テンプレートはプログラムが動的に読み込むデータであり，従ってテンプレートは静的に型付けされたレコードと直接対応しないことである．そこで，本システムでは，テンプレートファイルを部分動的レコードとして読み込み，テンプレートの穴埋め操作を部分動的レコードのフィールド更新操作を介して行う．実際のテンプレート操作は，既存のテンプレートエンジンを用いて行われる．本発表ではさらに，SML#と ClearSilver を用いた本システムの実装の試作を報告し，今後の展望を述べる．て文字列値のみを扱う．一方，テンプレートファイル

1 はじめにの内容はふつう HTML で記述されており，ユーザーWeb アプリケーションは，ユーザから送られてきはテンプレートエンジンの出力が HTML で書かれたたリクエストに基づいて，動的に Web ページの生成Web ページとして正しいことを期待する．この期待を行うアプリケーションである．動的な Web ページを満たすためには，ユーザーは全てのホールに，文脈の生成には，静的に決められた構成を把握した上で，上期待される構造を持つデータ構造を，適切なフォー適宜必要とされる場所に動的に値を埋め込む必要がマットで整形された形で埋め込まなければならない．

ある．一般に，このような操作は，人為的なミスをこの条件を満たすことはユーザーの責任であり，テン避ける目的や，ユーザ自らが記述するコード量を削プレートエンジンは検査を行わない．しかし，ユーザ減する目的から，テンプレートエンジンを用いて行が値を埋め込む際にホール名を誤る状況は，当然起こう．テンプレートエンジンは，初期値を持たない変数りうる．この場合，ホールの埋め込みに失敗し，プロ（ホール）が記述されているテンプレートファイルをグラムは期待する結果を返さない．これを防ぐために読み込み，プログラムが計算した値でホールを埋めるは，静的な検査を行い，値の埋め込みを行う前にホーことで，動的に Web ページを生成する．テンプレー

ル名の不整合を検出する必要がある．

トエンジンは一般に，テンプレートファイルを構造

変数名の整合性を保証する一つの方針は，テンプを持たない文字列と見なし，ホールに埋める値としレートファイルを型付きのレコードとみなし，テンプレート操作を型付きのレコード操作として行うことでPrototyping Typed Template Engine with Partially
Dynamic Record
Mifuyu Osaka, 東 北 大 学 情 報 科 学 研 究 科, Graduate
School of Information Sciences, Tohoku University.
Katsuhiro Ueno, Atsushi Ohori, 東北大学電気通信研究所, Research Institute of Electrical Communication,
Tohoku University.

ある．この基本的な方針は，文献 [4] にて示されている．しかし，その方針を実用言語に組み込むための詳細な検討や実装は行われていない．そこで我々は，この方針に基いた型付きテンプレートエンジンを，ML系関数型言語 SML#上に実現することを目指し，実現のための方針の詳細を検討し，それに基づいた試作とで，自然と解決する．課題 2 は，JSON におけるを行っている．本稿では，その経過を報告する．

null 値を「値がない」という状況を表す構造として用

文献 [4] では，値が埋まっていない状態を，optionいることができるならば，値が埋められていない状況

型の値 NONE で表現する．例えば，name と age の 2を null 値で表現し，これを別な値に置き換える操作

つのホールを持つテンプレートは，以下のレコードでをホールの埋め込み操作とすることで，解決しそうで表現される．

ある．部分動的レコードによる JSON の操作体系 [5]val template = {name = NONE, age = NONE}

では，null 値は専用の型が与えられ他の値とは区別さ: {age: [’a. ’a option],れており，以上の方針とは対応しない．よって，このname: [’a. ’a option]}

方針をとるために，部分動的レコードの枠組みを拡張

テンプレートに対する値の埋め込みは，レコードフィーし，外部データにおける null 値を「値がない」状況ルドの更新演算によって行う．例えば，name フィーを表す構造として取り扱えるようにする必要がある．

ルドへの値の埋め込みのために，レコードフィールド

我々は以上の方針に従い，SML# [6] と ClearSilverの更新演算を含む以下の関数を定義する．

[2] を用いて，部分動的レコードを応用した型付きテ

fun fillName (x as {name = NONE, ...}) =ンプレートエンジンの試作を行った．本稿では，そx # {name = SOME "Taro"}| fillName (x as {name = SOME _, ...}) =raise AlreadyFilled
この関数の型を以下に示す．

fillName : [’a#{name: string option}.
’a -> ’a]の概要を報告する．型付きテンプレートエンジンは，ユーザーに以下の操作を提供する．

1. テンプレートファイルを部分動的レコードとして読み込む関数

2. 読み込んだテンプレートを動的に型チェックし，静的なビューを与える構文

上記型情報から読み取れるように，fillName 関数

3. テンプレートのホールに値を埋め込む構文

は name フィールドのみに対して更新操作を行う．レ4. 全てのホールが埋まったテンプレートを出力す

コード多相性 [3] により，型’a は name フィールドを部分的に含む全てのレコードを表す．

る関数
ユーザは，これらの機能を順番に用いることで，テン以上は ML プログラムに閉じた話であり，テンププレートの読み込みとそれに対する型付けを自然にレートがその中に含まれるのであれば，この方針をと行うことができ，さらには静的な型チェックが行われることができる．しかし，実際にはテンプレートは外る状況下でホールへの埋め込み操作が行われるため，部データであるため，以下の理由から更なる対応が必

不整合を実行前に検出することが可能である．本稿

要である．

では，以上のうち，1 と 2 の試作について報告する．

• 動的に読み込む外部データに対して，静的に型を与えられない．

• 外部データに値が一切または部分的に埋まって3 と 4 については，未実装であるため，現段階での検討状況を報告する．
本稿の構成は以下の通りである．2 節では，型付きいない状況を扱う枠組みと，外部データに部分的テンプレートエンジンの実現方針を述べる．3 節でに値を埋める操作が必要である．

は，SML#によるテンプレートエンジンの試作を報

我々は，これらの課題を部分動的レコード [1] [5] を用告する．4 節では．結論を述べた上で，試作をより実いることで解決する．部分動的レコードは，JSON な用的なものに改善するための課題を述べる．

ど動的に読み込まれ，構造が静的に判明していないレコードデータを型付きで取り扱うための体系である．

2 型付きテンプレートエンジンの実現方針

課題 1 は，テンプレートは動的な構造であるため，静1 節で挙げた 4 つの機能を，SML#の部分動的レ的なレコードの代わりに動的なレコードを用いるこ

コードに基づく JSON 操作体系をもとに，ライブラ

リ関数あるいはコンパイラ組み込みの構文として作現するため，HDF はテンプレートと概念上独立ではる．そのためにはまず，外部データであるテンプレーない．この観点から，テンプレートは埋めるべきホートをプログラムから型付きで操作できるように，部分ルの集合を持ち，HDF はそれが部分的に埋まってい動的レコードによって型を与える必要がある．本節でる状況を表していると考えられる．この HDF の状

は，テンプレートの型付けと読み込みの方針を述べ

況は JSON で表現すると，全てのホール名をフィーる．さらに，値の埋め込みとテンプレートの出力の実ルドラベルとして持ち，フィールド値が部分的に埋現に向けての展望を述べる．

まっていないような JSON オブジェクトに対応する．

JSON において，値が埋まっていないフィールドは，2. 1 テンプレートの型付けと読み込みnull 値で表現する．例えば，前述のテンプレートの例

テンプレートは，初期値を持たない変数（ホール）を JSON で表現すると以下のようになる．

を含むドキュメントである．以下に ClearSilver のテ{"name" : null, "age" : null}

ンプレートの例を示す．

値が埋まっていないことを表す null 値は，任意の<HTML><BODY>型を持つ．この多相的な性質を保った状態で SML#かName : <?cs var:name ?><BR>ら null を扱えるようにするためには，部分動的レコー

Age : <?cs var:age ?>ドの枠組みを拡張する必要がある．ML において値</BODY></HTML>の有無を表現する一般的な方法は，option 型の値を<?cs var:x ?>は，名前 x を持つホールを表す Clear-用いることであるため，外部データの null に対してSilver の記法である．このホールには整数や文字列なML の option 型を対応づけることが自然な解決策でどの基本的なデータ構造が文字列値として埋め込まある．この対応関係を部分動的レコードの枠組みにれる．簡便のため，本稿では階層的なデータ構造な加えることで，null を含む外部データの扱いに対処

ど，ClearSilver が持つ他の機能は取り扱わないものする．とする．

ClearSilver では，このテンプレートに埋め込む以上の検討から，テンプレートの読み込みについて以下の方針が得られる．

データを管理するために，HDF（hierarchical data• ClearSilver のテンプレートと HDF の組に対しformat）と呼ばれるデータセットとその操作関数をて，τ template 型を与える．τ は，テンプレー提供する．HDF の例を以下に示す．

トに対して与えられる部分動的レコードの型で{ある．τ template 型の値には，HDF の表現でname = Taroある部分動的レコードと，ClearSilver を用いる

age = 25

上で必要となる，テンプレートファイルへのハンドルと HDF が含まれる．

}
例からわかる通り，HDF は一般にレコード構造をと• テンプレートを読み込む関数として，import 関る．ClearSilver は，読み込まれたテンプレートと，数を導入する．この関数は，テンプレートファイユーザーが作成した HDF を受け取り，HDF に従っルを読み込み，その内部構造を全てのフィールドてホールを埋めた文字列を出力する．

が null であるような JSON に対応する動的レ以上の枠組みを，部分動的レコードの観点から分析する．文献 [5] では，部分動的レコードの実現として JSON を扱っているため，本稿では JSON を用いコードに変換する．

• インポートしたテンプレートに静的な型を与える以下の構文を導入する．て分析する．ClearSilver では，テンプレートと HDFtemplate e as τ

はともに，レコード状の構造をもつ外部データであ

この式は，テンプレート e に部分動的レコード型る．HDF はテンプレートに埋め込まれるデータを表τ の静的なビューを与える．e の型が τ ′ template

のとき，この式の評価結果は τ template 型を持テンプレートエンジンの出力は，引数のテンプレートつ．この式の意味は，文献 [5] の json 構文と同に含まれる ClearSilver のデータ構造を引数として，様に実現できる．

ClearSilver が提供する C 関数を呼ぶだけで得ることができる．全てのホールが埋まっているかどうかの2. 2 値の埋め込みと結果の出力チェックに関しては， template e as τ 構文でテン

前述の方針でテンプレートに部分動的レコード型

プレートに完全なレコードの型を与え，得られた静的を与えるならば，値の埋め込みと結果の出力についてなレコードに対して各フィールド値が SOME であるこも，部分動的レコードの考え方から自然に導くこととをチェックすることで，安全かつ完全に行うことがができる．これらは現時点では試作に至っていないのできる．

で，現時点での予備的な検討について述べる．
値の埋め込みは，レコードフィールドの更新演算で3 型付きテンプレートエンジンの試作実現できる．ただし，SML#は静的なレコードの更前節に述べた方針に従い，テンプレートの読み込み新演算を提供している一方で，部分動的レコードの更を行う関数とその型付けを行う構文の試作を行った．

新演算を与えていないため，これを追加する必要があ

試作では，SML#コンパイラ 3.1.1 版を拡張した．本る．部分動的レコードの更新演算の追加は，通常のレ節では試作の実装について報告する．

コードの更新演算に基づいて自然に実現することが試作を行う上で，実際のテンプレートの操作をできる．例えば，以下に示すような構文を導入する．

行うために，ClearSilver のライブラリ関数を用いe1 ## {l = e2 }た．ClearSilver は C 言語のライブラリであるため，これは，部分動的レコード e1 の l フィールドを e2 にSML#の C の FFI を用いることで，問題なく利用す置き換えた，新しい部分動的レコードを返す式であることができた．以下に FFI を利用する際に導入しる．その型付け規則を以下に示す．
K, Γ ⊢ e1 : τ dyn

た型の一部を示す．

type CSfile = stringK ⊢ τ :: {{l : τ0 }}type hdf = unit ptr refK, Γ ⊢ e2 : τ0type cs = unit ptr refK, Γ ⊢ e1 ## {l = e2 } : τ dyn
Γ は型環境，K は文献 [3] で示されるカインド割り当てである．これは，e1 が部分動的レコードであることを除けば，通常のレコード更新と同じである．
この構文を用いて，テンプレート更新構文を作成する．

fill e1 # {l = e2 }この構文は，部分動的レコード更新構文を用いて，e1に含まれる部分動的レコードによる HDF 表現を更新すると同時に，e1 内のポインタを更新することでHDF の実態を l = e2 を含む最新のものにする．表
現と実態を同時に更新することで，両者の間の整合性は保たれる．
結果の出力は，以下の型を持つ関数として実現できる．

export : ’a template -> string

CSfile はテンプレートファイルの型，hdf は HDFへのポインタの型，cs はテンプレートのハンドラーへのポインタの型である．以降の試作の説明においても上記型定義を用いるものとする．
本試作では，テンプレートの型として’a template型を導入した．実際のテンプレートの操作を行うことだけを考えた場合，テンプレートのハンドラと HDF のみを管理すれば充分である．しかし，’atemplate 型を与えた上で，HDF の状態を SML#における JSON によって表現し，対応づけるためには’a に対応するデータ構造を’a template 型の実装の中に含める必要がある．そこで，本試作では，テンプレートのハンドラへのポインタと HDF へのポインタに加え，HDF の状態と厳密に対応する JSON オブジェクトを管理する．したがって，テンプレートの型の datatype 定義は以下のとおりである．

# val template = TemplateEngine.import "test.cs";
val template = _ : JSON.void JSON.dyn TemplateEngine.template# val typedName = _template template as {name : string option} JSON.dyn;
val typedName = _ : {name: string option} JSON.dyn TemplateEngine.template# val typedAll = _template typedName as {name : string option, age : int option};val typedAll = _ : {age: int option, name: string option} TemplateEngine.template
図1

テンプレートファイルの読み込みと型チェックの実行例

datatype ’a template =式は，e に含まれる JSON 表現に対して，動的な型

TEMPLATE of ’a * cs * hdf

チェックを行うというものである．そこで， template

テンプレートを読み込む関数である import の型を構文を，e から部分動的レコードを取り出し， json以下に示す．

構文を適用した上で，再度’a template 型の値を構

import : CSfile -> void dyn template

築するマクロとして実装する．マクロ展開後の式を以void dyn は，SML#において，構造が静的に判明し下に示す．ていない部分動的レコードを表す型である．返り値にtemplateAs (fn x => json x as τ ) e

含まれる 2 つのポインタに関しては，ClearSilver の

ここで，templateAs は’a template 型の値を分解ライブラリ関数に対して，テンプレートファイルを引し，再度’a template 型へ戻す関数である．

数として与えることで取得できる．一方で，HDF の前述したテンプレートファイル例を用いた，SML#状態を表す JSON オブジェクトは，テンプレートのの対話型モードにおける本試作の利用例を図 1 に示す．

構造に合わせて import 関数が作成する必要がある．
部分動的レコードを作成する上で必要となるのは，テ4 まとめと今後の課題

ンプレートに記述されているホール名のリストであ

本稿では，SML#によるテンプレートエンジン実る．ClearSilver は，ホール名のみを取得する関数を現のため，テンプレート読み込み関数と動的型チェッユーザーへ提供していない．一方で，テンプレートク構文の実装方針を検討し，その試作を示した．まの構文木をダンプするための関数が提供されている．

た，テンプレート操作を行う上で必要となる，ホール

そこで，本試作では，ダンプ結果を解析することでへの値の埋め込みを行う構文と，結果を出力する関数ホール名を取得する．得られたホール名（ここではに関しては，方針のみを示した．

l1 , ...ln とする）を用いて，以下に示すような JSON
オブジェクトを表す JSON 文字列を生成する．

今後の展開として，いくつかの方向性が考えられる．そのうちの一つは，階層的なテンプレートを扱え{l1 : null, . . ., ln : null}るようにすることである．本来 ClearSilver は，階層JSON 文字列に対して，以下の SML#が提供する関的なデータ構造を扱うことができるが，本試作では簡数を適用する．

便のため，この機能は対象外とした．このような高度

JSON.import : string -> void dyn

な機能への対応も含め，高い実用性をもつ型付きテン

これは，JSON 文字列を部分動的レコードに変換するプレートエンジンの実現を目指す．

ための関数である．import 関数は，得られた部分動謝辞 本研究の一部は JSPS 科研費 25280019「ML的レコードを，2 つのポインタとまとめて void dyn系多相型言語 SML#の実用化技術に関する基礎研究」

template 型のデータとして，ユーザーに返す．

および 15K15964「実用プログラミング言語のためのtemplate e as τ は，コンパイラが持つ型情報である τ を用いて，実行時に動的な型チェックを行う式であるため，構文として導入する必要がある．この系統的言語開発基盤の実現」の助成を受けて実施されたものである．

参 考 文 献[1] Buneman, P. and Ohori, A.: Polymorphism andType Inference in Database Programming, ACM
Trans. Database Syst., Vol. 21, No. 1(1996), pp. 30– 76.
[2] ClearSilver, http://www.clearsilver.net/.
[3] Ohori, A.: A Polymorphic Record Calculus andIts Compilation, ACM Trans. Program. Lang. Syst.,
Vol. 17, No. 6(1995), pp. 844 – 895.
[4] Ohori, A., Ueno, K., Hoshi, K., Nozaki, S., Sato,T., Makabe, T., and Ito, Y.: SML# in Industry: A
Practical ERP System Development, Proceedings ofthe 19th ACM SIGPLAN International Conference
on Functional Programming, ICFP 14, New York,NY, USA, ACM, 2014, pp. 167 – 173.
[5] Ohori, A., Ueno, K., Sasaki, T., and Kikuchi,
D.: A Calculus with Partially Dynamic Records for
Typeful Manipulation of JSON Objects, 30th European Conference on Object-Oriented Programming(ECOOP 2016), Leibniz International Proceedingsin Informatics (LIPIcs), Vol. 56, 2016, pp. 18:1 –18:25.
[6] SML# Compiler,
http:///www.pllab.riec.tohoku.ac.jp/smlsharp/.