コード注入
ここで言うコード注入とは、スクリプトファイルの冒頭などに処理系自身がコードを挿入し、ちょっとした機能を追加すること。
例えば.luaファイルそれぞれの冒頭に local __MODULE__ = {ファイルパス}
というコードを挿入しておくと、__MODULE__
という変数名で自身のファイルパスを取得できるようになる。
ファイル作成者が毎回ボイラープレートを書かなくて済むので便利。
応用例は以下のように、require
関数を再定義して相対パスによるモジュール読み込みを可能にするなど。
// ストレージからluaスクリプトを取得し、冒頭に「自身のファイルパス」を保持するコードを付加する。
std::string get_code_injected(const std::filesystem::path &file_path) {
std::string code = /* ストレージからluaファイルを読み込む */
std::string cwd = file_path.parent_path().string();
std::string header =
"local __CWD__ = '" + cwd + "';" +
"local __original_require = require;"
"local function require(module)"
" __CWD_global__ = __CWD__"
" return __original_require(module)"
"end;";
return header + code;
}
// package.searchersにも独自関数を追加し、その中で`__CWD_global__`を参照する。
// `__CWD_global__`からの相対パスでファイルを検索すればよい。
工夫
ここからはSolライブラリ(C++内でLuaの実行環境を簡単に作れる)を使用時の、具体的な注意点をまとめる。
(sol2 v3.3.0)
挿入コードはminifyして1行に圧縮する
Luaは改行文字を特別扱いすることはない。
あらゆるLuaのコードは改行を含まないように書き直すことができる。
local a = 9
print(a)
このコードは、実は以下のように書き直せる。
local a = 9 print(a)
-- または
local a = 9;print(a)
トークンが適切に区切れてさえいればいいらしい。セミコロンも使えるが、必須ではないのがC言語と違って面白いところだ。
さて、この事実を使うことで、挿入するコードを1行に圧縮することができる。
Solはスクリプトの実行中にエラーに当たると、エラーの発生行番号を表示してくれるのだが、コードを複数行挿入しているとその行番号が元々のファイル内容とズレてしまう。
このズレを防止するのが、挿入コードを1行に圧縮する意義である。
chunkname
引数の活用
sol::state::script()
関数やsol::state::load()
関数は、Luaコードを文字列として与えることでそれを実行してくれる。
しかしこのままだと、このコード自体には「ファイル名」などという概念が存在しないので、エラーが発生したときも何だかよく分からない表示が出てくる。
これに対処するために、第2引数chunkname
として追加の「実質的なファイルパス」等の情報を教えておくことができる。これでエラー発生時に“ファイルパス”を表示してくれるようになる。
lua.script(code, "@scripts/hoge.lua")
この時さらに注意点として、ファイルパスには先頭に「@
」を付けておかないと、またもや少しおかしな表示となってしまう。
何もしない |
chunkname 指定(@なし) |
chunkname 指定(@あり) |
---|---|---|
ファイル名は存在しないので、コード内容がそのまま表示されている。 |
[string "パス"] のような表示になってしまう。 |
ファイルパスと行番号が綺麗に表示される。 |
まとめ
- Lua実行環境自身がスクリプト冒頭などにコードを挿入しておくことで、様々な機能追加が可能
- 挿入コードはminifyして1行にしておくことで、エラー行通知と実際のファイル行の齟齬を無くせる
- Solの
chunkname
引数にファイルパスを与えるときは、先頭に「@
」を追加すべし
コード注入自体、些かハッキーなテクニックであるかもしれないが、こうした書き方にも耐えうる拡張性を持っていることがLua言語の強みでもある。
この記事を書くきっかけの話。当初はエラー行がズレてしまう現象に悩まされていたのだが、挿入コードを無理矢理1行に圧縮できることに気付いて目が覚めたような気持ちになった。