monami-ya.mrb での Sandbox サポート

たぶん会社の公式サイトに乗るべき情報のような気もしますが. まだ master ブランチどころか develop ブランチにも入っていないので,メモとして,こちらに書いておきます.

mruby の機器組み込み向け fork である monami-ya.mrb に,mrbgems の sandbox 機能を追加しました. 何故この機能が必要なのか,という話は後日するとして,どう使うかということを記しておきます. master ブランチに入るまでに,API 等の変更があるかもしれません.

また,mruby 本体に取り込まれるかどうかは,解りません.

機能概要

前提知識

本家 mruby では,mrbgems による機能拡張がサポートされています. これは,monami-ya.mrb でも同様です.

mruby は,複数の実行環境を持てます. 実行環境は mrb_state という構造体が代表します.

mrb_state は,mrb_open() の呼び出しによって作成されます. mrb_open() の実行時には,build_config.rb で静的に指定した全ての mrbgems が, mrb_state で使用するものとして初期化されます.

sandbox が提供する機能

mrb_state ごとに,利用する mrbgems を限定できます. 限定する mrbgems は,静的に指定します.

実装

実例として,monami-ya-mrb/mruby-sqlite3 と monami-ya-mrb/mruby-bin-sqlite3 のみを含む sandbox を挙げます.

monami-ya-mrb/mruby-sqlite3 は monami-ya-mrb/mruby-bin-sqlite3 に依存しています.

build_config.rb への記述

build_config.rb に,sandbox メソッドを記述できるようになりました.

1
2
3
4
5
6
7
MRuby::CrossBuild.new('jsp-bfin') do |conf|
...
  conf.sandbox('sqlite') do
    gem :github => 'monami-ya-mrb/mruby-sqlite3'
    gem :github => 'monami-ya-mrb/mruby-bin-sqlite3'
  end
...

この例では ‘sqilte’ という名前の sandbox を指定しています. ブロック内の gem は,従来のと同じです.

このような記述があるとき,minirake を実行すると,build/jsp-bfin/mrbgems/gem_init.c には,従来に加えていくつかの定義が生成されます.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void
mrb_init_sqlite_sandbox(mrb_state *mrb) {
  GENERATED_TMP_mrb_mruby_bin_sqlite3_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_sqlite3_gem_init(mrb);
}

void
mrb_final_sqlite_sandbox(mrb_state *mrb) {
  GENERATED_TMP_mrb_mruby_bin_sqlite3_gem_final(mrb);
  GENERATED_TMP_mrb_mruby_sqlite3_gem_final(mrb);
}

struct mrb_sandbox_inib mrb_sandbox_inib_array[] = {
  {
    mrb_init_sqlite_sandbox,
    mrb_final_sqlite_sandbox,
  },
  { NULL, NULL }
};

さらに, build/jsp-bfin/mrb_sandbox_id.h というヘッダファイルが生成されます.

1
2
3
4
5
6
7
/*
 * IMPORTANT:
 *   This file was generated!
 *   All manual changes will get lost.
 */

#define MRB_SANDBOX_SQLITE (1u)

依存性チェック

もし, mruby-sqlite3 のみを指定し,依存性のある mruby-bin-sqlite3 を 含め忘れたとします.

1
2
3
4
5
6
MRuby::CrossBuild.new('jsp-bfin') do |conf|
...
  conf.sandbox('sqlite') do
    gem :github => 'monami-ya-mrb/mruby-sqlite3'
  end
...

このような依存性の破れを含む記述で minirake を実行した場合には,エラーとなります.

1
2
3
4
(in /Users/monaka/git/monami-ya.mrb/monami-ya.mrb)
rake aborted!
GEM mruby-bin-sqlite3 not found in the sandbox `sqlite'
rakefile:27:in `load'

実行時の sandbox 生成

API として mrb_open_sandbox() および mrb_open_sandbox_allocf() が追加になりました.

1
2
mrb_state* mrb_open_sandbox(unsigned int sandbox_id);
mrb_state* mrb_open_sandbox_allocf(mrb_allocf, uintptr_t ud, unsigned int sandbox_id);

引数 sandbox_id は,mrb_sandbox_id.h にある定義を与えます.

1
  mrb = mrb_open_sandbox(MRB_SANDBOX_SQLITE);

sandbox_id が 0 のときは,mrb_open() を呼んだ時と同じになります. すなわち,登録されている全ての mrbgems が初期化されます.

どの sandbox を指定したかは,mrb_state に保持されます. そのため,mrb_state を破棄する際には,単に mrb_close() を呼び出してください.