56202


<http://bamch0h.hatenablog.com/entry/2019/06/21/000743>  
上記のブロクにも書いたのですが、rb_load と rb_f_load の挙動の違いについての質問になります。

[質問内容]  
rb_load() を使用して カレントディレクトリにあるファイル(例えば test.rb) をロードするようなC言語拡張を書いたときに、cannot
load such file -- test.rb (LoadError) となりロードできませんでした。Kernel.#load は
rb_f_load() を使用しているようで、load "test.rb" という指定でも問題なくロードできます。

<https://docs.ruby-lang.org/ja/latest/function/rb_load.html> を見る限り、rb_load() は
Kernel.#load の低レベルインターフェースということですが、rb_load() を使用すれば、Kernel.#load
と同様のことができる。ということでもないのでしょうか？

rb_load() と rb_f_load() がある経緯について知れるならば幸いです。


CC BY-SA 4.0
2019-06-28T14:45:43.027
56200
2019-06-28T17:25:18.187
18863
question
ruby
rb_load と rb_f_load の挙動の違いについて


Ruby 2.6.3 のソースコードを調べてみました。Yamanaka さんのブログ記事に記載がある通りに `rb_load()` ->
`file_to_load()` -> `load_failed()` という順序で実行が続いています。

**load.c**  

[code]

    void
    rb_load(VALUE fname, int wrap)
    {
        rb_load_internal(file_to_load(fname), wrap);
    }
    
    static VALUE
    file_to_load(VALUE fname)
    {
        VALUE tmp = rb_find_file(FilePathValue(fname));
        if (!tmp) load_failed(fname);
        return tmp;
    }
    
    static void
    load_failed(VALUE fname)
    {
        rb_load_fail(fname, "cannot load such file");
    }
    
[/code]

問題の `rb_find_file()` ですが、`rb_find_file_safe()` へと実行が続き、load path(`$:` or
`$LOAD_PATH` の内容) から指定ファイルを探索しています。

Ruby 2.6.3 の `rb_find_file_safe()`
関数内ではカレントディレクトリを調べていません。ただし、ファイル名を相対パス指定にする(`./test.rb`)か、load path に `"."`
を追加すればロードされるはずです。

**file.c**

[code]

    static int
    is_explicit_relative(const char *path)
    {
      if (*path++ != '.') return 0;
      if (*path == '.') path++;
      return isdirsep(*path);
    }
    
    VALUE
    rb_find_file(VALUE path)
    {
        return rb_find_file_safe(path, rb_safe_level());
    }
    
    VALUE
    rb_find_file_safe(VALUE path, int safe_level)
    {
                       :
      if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) {
                       :
        if (!rb_file_load_ok(f)) return 0;
        if (!expanded)
          path = copy_path_class(file_expand_path_1(path), path);
        return path;
      }
                       :
      RB_GC_GUARD(load_path) = rb_get_expanded_load_path();
      if (load_path) {
                       :
        for (i = 0; i < RARRAY_LEN(load_path); i++) {
          VALUE str = RARRAY_AREF(load_path, i);
          RB_GC_GUARD(str) = rb_get_path_check(str, safe_level);
          if (RSTRING_LEN(str) > 0) {
            rb_file_expand_path_internal(path, str, 0, 0, tmp);
            f = RSTRING_PTR(tmp);
            if (rb_file_load_ok(f)) goto found;
          }
        }
        rb_str_resize(tmp, 0);
        return 0;
      }
    
[/code]

次に `rb_f_load()` についてですが、`rb_find_file()`
で指定ファイルがロード不可と判定されるため、`rb_file_load_ok()`
で再度チェックを行う事になります。こちらもブログ記事にある通りに、カレントディレクトリにそのファイルが存在して、かつ読み込み可能かどうかをチェックしています。

[code]

    static VALUE
    rb_f_load(int argc, VALUE *argv)
    {
                     :
      path = rb_find_file(fname);
      if (!path) {
        if (!rb_file_load_ok(RSTRING_PTR(fname)))
          load_failed(orig_fname);
        path = fname;
      }
                    :
    
[/code]

したがって、`rb_load()` では読み込みに失敗して、`rb_f_load()` では成功する事になります。


CC BY-SA 4.0
2019-06-28T17:16:48.927
56202
2019-06-28T17:25:18.187
2019-06-28T17:25:18.187
56200
answer