Project

General

Profile

Bug #2581

require_relative depends on current directory

Added by mame (Yusuke Endoh) over 10 years ago. Updated about 9 years ago.

Status:
Closed
Priority:
Normal
Target version:
ruby -v:
1.9.2
Backport:
[ruby-dev:40004]

Description

=begin
遠藤です。

ソースファイルを相対パスで指定して起動したとき、require_relative が
カレントディレクトリに依存するようですが、これは仕様でしょうか。

$ cat /tmp/t1.rb
Dir.chdir("foo")
require_relative "t2"
p foo

$ cat /tmp/t2.rb
def foo
"/tmp/t2"
end

$ cat /tmp/foo/t2.rb
def foo
"/tmp/foo/t2"
end

# 相対パスで起動すると、require_relative 時のカレントディレクトリを見る
$ cd /tmp/
$ ruby19 t1.rb
"/tmp/foo/t2"

# フルパスで起動すれば問題ない
$ ruby19 /tmp/t1.rb
"/tmp/t2"

$ ruby19 -v
ruby 1.9.2dev (2010-01-09 trunk 26260) [i686-linux]

あと、require_relative の rdoc がない気がするのですが、prelude.rb に
書けばいいんでしょうか。

--
Yusuke ENDOH mame@tsg.ne.jp
=end

#1

Updated by naruse (Yui NARUSE) over 10 years ago

  • Category set to lib
  • Status changed from Open to Assigned
  • Assignee set to akr (Akira Tanaka)
  • Priority changed from 3 to Normal
  • Target version set to 1.9.2
  • ruby -v set to 1.9.2

=begin

=end

#2

Updated by mame (Yusuke Endoh) over 10 years ago

  • Priority changed from Normal to 6

=begin
遠藤です。

ソースファイルを相対パスで指定して起動したとき、require_relative が
カレントディレクトリに依存するようですが、これは仕様でしょうか。

この件、どうなってますでしょうか。
File.realpath が使われるようになった影響で多少発現はしにくく
なったようですが、本質的には解決していないという認識です。

1.9.2 では $: から "." が消えるため require_relative の需要が
増えることが予想されます。そのため、この問題はわりと重大だと
思っています。とりあえず優先度を urgent にしておきます。

このメソッドが prelude.rb で定義されていたり、caller を使った
不安な実装になっていたりするのは理由があるんでしょうか。

--
Yusuke Endoh mame@tsg.ne.jp
=end

#3

Updated by kosaki (Motohiro KOSAKI) over 10 years ago

=begin
2010年1月9日23:15 Yusuke ENDOH mame@tsg.ne.jp:

遠藤です。

ソースファイルを相対パスで指定して起動したとき、require_relative が
カレントディレクトリに依存するようですが、これは仕様でしょうか。

$ cat /tmp/t1.rb
Dir.chdir("foo")
require_relative "t2"
p foo

$ cat /tmp/t2.rb
def foo
"/tmp/t2"
end

$ cat /tmp/foo/t2.rb
def foo
"/tmp/foo/t2"
end

相対パスで起動すると、require_relative 時のカレントディレクトリを見る

$ cd /tmp/
$ ruby19 t1.rb
"/tmp/foo/t2"

おうちのLinuxマシンでは違うエラーが出ます

% ruby t1.rb
internal:prelude:35:in realpath': No such file or directory -
/home/kosaki/linux/ruby/t/require_relative/foo/t1.rb (Errno::ENOENT)
from <internal:prelude>:35:in
require_relative'
from t1.rb:2:in `'

% strace ruby t1.rb
(snip)
chdir("foo") = 0
getcwd("/home/kosaki/linux/ruby/t/require_relative/foo", 200) = 47
lstat("/home", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("/home/kosaki", {st_mode=S_IFDIR|0710, st_size=4096, ...}) = 0
lstat("/home/kosaki/linux", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
lstat("/home/kosaki/linux/ruby", {st_mode=S_IFDIR|0755, st_size=12288, ...}) = 0
lstat("/home/kosaki/linux/ruby/t", {st_mode=S_IFDIR|0755,
st_size=4096, ...}) = 0
lstat("/home/kosaki/linux/ruby/t/require_relative",
{st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("/home/kosaki/linux/ruby/t/require_relative/foo",
{st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("/home/kosaki/linux/ruby/t/require_relative/foo/t1.rb",
0x7fff5b3f4e20) = -1 ENOENT (No such file or directory)

なぜか、chdir後のrequire_relativeの中で間違ったディレクトリでt1.rbを開こうとするようです。

=end

#4

Updated by kosaki (Motohiro KOSAKI) over 10 years ago

=begin

なぜか、chdir後のrequire_relativeの中で間違ったディレクトリでt1.rbを開こうとするようです。

答えは全部ソースにありました ←あたりまえですが

26: module Kernel
27: module_function
28: def require_relative(relative_feature)
29: c = caller.first
30: e = c.rindex(/:\d+:in /)
31: file = $`
32: if /\A((.*))/ =~ file # eval, etc.
33: raise LoadError, "require_relative is called in #{$1}"
34: end
35: absolute_feature =
File.join(File.dirname(File.realpath(file)), relative_feature)
36: require absolute_feature
37: end
38: end

29行目でcallerを使って呼び出しもとファイル t1.rb を取得して、35行目でFile.realpath()を
使って絶対パスに変換しようとしていますが、chdirはこのアルゴリズムの穴をついてしまいます。

1.そもそもrealpath()はカレントパスを元に相対パスを絶対パスに補完する関数なので
使用者が期待している、スクリプトファイルからの相対位置ではない
2.File.realpath()は存在しないパスを引数に与えると例外投げる。よって、
キャッチしなければ、スクリプトが終了してしまう。

よく知らないのですが、内部的には二重require防止用に絶対パスでロード済みファイル情報
持っている気がするのでrequire_relativeを組み込みにすれば解決?

=end

#5

Updated by kosaki (Motohiro KOSAKI) over 10 years ago

=begin

1.9.2 では $: から "." が消えるため require_relative の需要が
増えることが予想されます。そのため、この問題はわりと重大だと
思っています。とりあえず優先度を urgent にしておきます。

スクリプト先頭に書くぶんには動くしなぁ。

いままでの議論で、条件が明らかになったと思っており、かつその条件
(require_relative前にスクリプトがchdirを呼ぶ)は、あまり普通の使い方では
なさそうだと思っています。

直すべきバグだとは思いますが、1.9.2のリリースに影響を与えるほどの深刻度とは
思えないので、優先度をNormalに戻すことを提案します。

=end

#6

Updated by mame (Yusuke Endoh) over 10 years ago

=begin
遠藤です。

2010年3月7日21:17 KOSAKI Motohiro kosaki.motohiro@gmail.com:

1.9.2 では $: から "." が消えるため require_relative の需要が
増えることが予想されます。そのため、この問題はわりと重大だと
思っています。とりあえず優先度を urgent にしておきます。

スクリプト先頭に書くぶんには動くしなぁ。

いままでの議論で、条件が明らかになったと思っており、かつその条件
(require_relative前にスクリプトがchdirを呼ぶ)は、あまり普通の使い方では
なさそうだと思っています。

条件は、

  • 相対パスで起動したスクリプトの中で
  • Dir.chdir した後に
  • require_relative を呼ぶ

ですよね。

Ruby では、ファイル先頭でなく、コード中で必要になって始めて require を
呼ぶというコードが結構あります。lib/ 以下にもたくさん見つかります。
(標準添付ライブラリのコードは上記の条件にあわないから問題ないけれど)

同じノリで require_relative を使われることは想像に難くないです。

直すべきバグだとは思いますが、1.9.2のリリースに影響を与えるほどの深刻度とは
思えないので、優先度をNormalに戻すことを提案します。

$: から "." が消された理由が正式に公開されていない (セキュリティ関係の
対策であるため公開したくない?) のですが、この問題はその対策を台無しに
してしまうわけではないですか?
ないということであれば、Normal に戻したいと思います。akr さんが修正に
乗り気でないとわかったので、おそらく大丈夫なのかなと思いますが。

--
Yusuke ENDOH mame@tsg.ne.jp

=end

#7

Updated by mame (Yusuke Endoh) over 10 years ago

=begin
遠藤です。

2010年3月7日22:26 Tanaka Akira akr@fsij.org:

まぁ直すのは名前以外難しくはなくて、

snip

とかですかね。

ああなるほど、これだけでいいんですね。
rb_iseq_t に絶対パスを載せなきゃならんと思い込んでました。

Dir::INITIAL_WORKING_DIRECTORY というのが定義されるので、
この名前が気に入らない人が出てくると難しいところですが。

私は構わないと思いますが、どうでしょうね。
define_method を使ってよければ、余分な定数もなしで済む気もします。

diff --git a/prelude.rb b/prelude.rb
index 679e831..4aecf27 100644
--- a/prelude.rb
+++ b/prelude.rb
@@ -25,14 +25,20 @@ end

module Kernel
module_function

  • def require_relative(relative_feature)
  • initial_wd =
  • begin
  • Dir.getwd
  • rescue Exception
  • nil
  • end
  • define_method(:require_relative) do |relative_feature| c = caller.first e = c.rindex(/:\d+:in /) file = $` if /\A((.*))/ =~ file # eval, etc. raise LoadError, "require_relative is called in #{$1}" end
  • absolute_feature = File.join(File.dirname(File.realpath(file)), relative_feature)
  • absolute_feature = File.join(File.dirname(File.realpath(file, initial_wd)), relative_feature) require absolute_feature end end

試していたら、以下のようにはまりました。

$ ./ruby -e 'require_relative "lib/cgi"'
internal:prelude:35:in realpath': No such file or directory -
/home/mame/work/ruby/-e (Errno::ENOENT)
from <internal:prelude>:35:in
require_relative'
from -e:1:in `'

うーん、これはしょうがないか。

--
Yusuke ENDOH mame@tsg.ne.jp

=end

#8

Updated by mame (Yusuke Endoh) over 10 years ago

=begin
遠藤です。

2010年3月8日13:22 Tanaka Akira akr@fsij.org:

-e は難しいですね。

以下のようなオプションとしての -e と、

% ruby -e 'p caller(0)'
["-e:1:in `'"]

以下のようなファイル名としての -e を区別できないと思うので。

% cat ./-e
p caller(0)
% ruby -- -e
["-e:1:in `'"]

おとなしく C で実装してみました。ファイルからロードした場合に限り、
rb_iseq_t に絶対パスを載せておきます。
このチケットの最初の例が解決し、-e の例も以下のようになります。

$ ./ruby -e 'require_relative "lib/cgi"'
-e:1:in require_relative': cannot infer basepath (LoadError)
from -e:1:in
'

$ cat ./-e
require_relative "lib/cgi"

$ ./ruby -- -e

反対がなさそうならコミットしようと思います。

diff --git a/compile.c b/compile.c
index ad57ca2..f37fbd8 100644
--- a/compile.c
+++ b/compile.c
@@ -169,6 +169,9 @@ PRINTF_ARGS(void ruby_debug_printf(const char*, ...), 1, 2);
#define iseq_filename(iseq) \
(((rb_iseq_t*)DATA_PTR(iseq))->filename)

+#define iseq_filepath(iseq) \

  • (((rb_iseq_t*)DATA_PTR(iseq))->filepath) + #define NEW_ISEQVAL(node, name, type, line_no) \ new_child_iseq(iseq, node, name, 0, type, line_no)

@@ -917,7 +920,7 @@ new_child_iseq(rb_iseq_t *iseq, NODE *node,
VALUE ret;

  debugs("[new_child_iseq]> ---------------------------------------\n");
  • ret = rb_iseq_new_with_opt(node, name, iseq_filename(iseq->self), INT2FIX(line_no),
  • ret = rb_iseq_new_with_opt(node, name, iseq_filename(iseq->self), iseq_filepath(iseq->self), INT2FIX(line_no), parent, type, iseq->compile_data->option); debugs("[new_child_iseq]< ---------------------------------------\n"); iseq_add_mark_object(iseq, ret); diff --git a/file.c b/file.c index 50516ad..f381f55 100644 --- a/file.c +++ b/file.c @@ -3165,8 +3165,8 @@ realpath_rec(long *prefixlenp, VALUE *resolvedp, char *unresolved, VALUE loopche } }

-static VALUE
-realpath_internal(VALUE basedir, VALUE path, int strict)
+VALUE
+rb_realpath_internal(VALUE basedir, VALUE path, int strict)
{
long prefixlen;
VALUE resolved;
@@ -3244,7 +3244,7 @@ rb_file_s_realpath(int argc, VALUE *argv, VALUE klass)
{
VALUE path, basedir;
rb_scan_args(argc, argv, "11", &path, &basedir);

  • return realpath_internal(basedir, path, 1);
  • return rb_realpath_internal(basedir, path, 1); }

/*
@@ -3264,7 +3264,7 @@ rb_file_s_realdirpath(int argc, VALUE *argv, VALUE klass)
{
VALUE path, basedir;
rb_scan_args(argc, argv, "11", &path, &basedir);

  • return realpath_internal(basedir, path, 0);
  • return rb_realpath_internal(basedir, path, 0); }

static size_t
@@ -3390,7 +3390,7 @@ rb_file_s_basename(int argc, VALUE *argv)

  • File.dirname("/home/gumby/work/ruby.rb") #=> "/home/gumby/work" */

-static VALUE
+VALUE
rb_file_s_dirname(VALUE klass, VALUE fname)
{
const char *name, *root, *p;
diff --git a/iseq.c b/iseq.c
index 80364ca..4007913 100644
--- a/iseq.c
+++ b/iseq.c
@@ -98,6 +98,7 @@ iseq_mark(void *ptr)
RUBY_MARK_UNLESS_NULL(iseq->mark_ary);
RUBY_MARK_UNLESS_NULL(iseq->name);
RUBY_MARK_UNLESS_NULL(iseq->filename);

  • RUBY_MARK_UNLESS_NULL(iseq->filepath); RUBY_MARK_UNLESS_NULL((VALUE)iseq->cref_stack); RUBY_MARK_UNLESS_NULL(iseq->klass); RUBY_MARK_UNLESS_NULL(iseq->coverage); @@ -207,9 +208,11 @@ set_relation(rb_iseq_t *iseq, const VALUE parent) } }

+VALUE rb_realpath_internal(VALUE basedir, VALUE path, int strict);
+
static VALUE
prepare_iseq_build(rb_iseq_t *iseq,

  • VALUE name, VALUE filename, VALUE line_no,
  •     VALUE name, VALUE filename, VALUE filepath, VALUE line_no,
       VALUE parent, VALUE type, VALUE block_opt,
       const rb_compile_option_t *option)
    

    {
    @@ -218,6 +221,7 @@ prepare_iseq_build(rb_iseq_t *iseq,

    iseq->name = name;
    iseq->filename = filename;

  • iseq->filepath = filepath == Qnil ? Qnil :
    rb_realpath_internal(Qnil, filepath, 1);
    iseq->line_no = line_no;
    iseq->defined_method_id = 0;
    iseq->mark_ary = rb_ary_tmp_new(3);
    @@ -361,31 +365,31 @@ make_compile_option_value(rb_compile_option_t *option)
    }

VALUE
-rb_iseq_new(NODE *node, VALUE name, VALUE filename,
+rb_iseq_new(NODE *node, VALUE name, VALUE filename, VALUE filepath,
VALUE parent, VALUE type)
{

  • return rb_iseq_new_with_opt(node, name, filename, INT2FIX(0), parent, type,
  • return rb_iseq_new_with_opt(node, name, filename, filepath, INT2FIX(0), parent, type, &COMPILE_OPTION_DEFAULT); }

VALUE
-rb_iseq_new_top(NODE *node, VALUE name, VALUE filename, VALUE parent)
+rb_iseq_new_top(NODE *node, VALUE name, VALUE filename, VALUE
filepath, VALUE parent)
{

  • return rb_iseq_new_with_opt(node, name, filename, INT2FIX(0), parent, ISEQ_TYPE_TOP,
  • return rb_iseq_new_with_opt(node, name, filename, filepath, INT2FIX(0), parent, ISEQ_TYPE_TOP, &COMPILE_OPTION_DEFAULT); }

VALUE
-rb_iseq_new_main(NODE *node, VALUE filename)
+rb_iseq_new_main(NODE *node, VALUE filename, VALUE filepath)
{
rb_thread_t *th = GET_THREAD();
VALUE parent = th->base_block->iseq->self;

  • return rb_iseq_new_with_opt(node, rb_str_new2(""), filename, INT2FIX(0),
  • return rb_iseq_new_with_opt(node, rb_str_new2(""), filename, filepath, INT2FIX(0), parent, ISEQ_TYPE_MAIN, &COMPILE_OPTION_DEFAULT); }

static VALUE
-rb_iseq_new_with_bopt_and_opt(NODE *node, VALUE name, VALUE filename,
VALUE line_no,
+rb_iseq_new_with_bopt_and_opt(NODE *node, VALUE name, VALUE filename,
VALUE filepath, VALUE line_no,
VALUE parent, VALUE type, VALUE bopt,
const rb_compile_option_t *option)
{
@@ -395,28 +399,28 @@ rb_iseq_new_with_bopt_and_opt(NODE *node, VALUE
name, VALUE filename, VALUE line
GetISeqPtr(self, iseq);
iseq->self = self;

  • prepare_iseq_build(iseq, name, filename, line_no, parent, type, bopt, option);
  • prepare_iseq_build(iseq, name, filename, filepath, line_no, parent, type, bopt, option); rb_iseq_compile_node(self, node); cleanup_iseq_build(iseq); return self; }

VALUE
-rb_iseq_new_with_opt(NODE node, VALUE name, VALUE filename, VALUE line_no,
+rb_iseq_new_with_opt(NODE *node, VALUE name, VALUE filename, VALUE
filepath, VALUE line_no,
VALUE parent, VALUE type,
const rb_compile_option_t *option)
{
/
TODO: argument check */

  • return rb_iseq_new_with_bopt_and_opt(node, name, filename, line_no, parent, type,
  • return rb_iseq_new_with_bopt_and_opt(node, name, filename, filepath, line_no, parent, type, Qfalse, option); }

VALUE
-rb_iseq_new_with_bopt(NODE node, VALUE name, VALUE filename, VALUE line_no,
+rb_iseq_new_with_bopt(NODE *node, VALUE name, VALUE filename, VALUE
filepath, VALUE line_no,
VALUE parent, VALUE type, VALUE bopt)
{
/
TODO: argument check */

  • return rb_iseq_new_with_bopt_and_opt(node, name, filename, line_no, parent, type,
  • return rb_iseq_new_with_bopt_and_opt(node, name, filename, filepath, line_no, parent, type, bopt, &COMPILE_OPTION_DEFAULT); }

@@ -430,7 +434,7 @@ iseq_load(VALUE self, VALUE data, VALUE parent, VALUE opt)
VALUE iseqval = iseq_alloc(self);

  VALUE magic, version1, version2, format_type, misc;
  • VALUE name, filename, line_no;
  • VALUE name, filename, filepath, line_no;
    VALUE type, body, locals, args, exception;

    VALUE iseq_type;
    @@ -454,6 +458,7 @@ iseq_load(VALUE self, VALUE data, VALUE parent, VALUE opt)

    name = CHECK_STRING(rb_ary_entry(data, i++));
    filename = CHECK_STRING(rb_ary_entry(data, i++));

  • filepath = CHECK_STRING(rb_ary_entry(data, i++));
    line_no = CHECK_INTEGER(rb_ary_entry(data, i++));

    type = CHECK_SYMBOL(rb_ary_entry(data, i++));
    @@ -496,7 +501,7 @@ iseq_load(VALUE self, VALUE data, VALUE parent, VALUE opt)
    }

    make_compile_option(&option, opt);

  • prepare_iseq_build(iseq, name, filename, line_no,

  • prepare_iseq_build(iseq, name, filename, filepath, line_no,
    parent, iseq_type, 0, &option);

    rb_iseq_build_from_ary(iseq, locals, args, exception, body);
    @@ -533,7 +538,7 @@ parse_string(VALUE str, const char *file, int line)
    }

VALUE
-rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE line, VALUE opt)
+rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE filepath,
VALUE line, VALUE opt)
{
rb_compile_option_t option;
const char *fn = StringValueCStr(file);
@@ -544,11 +549,11 @@ rb_iseq_compile_with_option(VALUE src, VALUE
file, VALUE line, VALUE opt)

  if (th->base_block && th->base_block->iseq) {
return rb_iseq_new_with_opt(node, th->base_block->iseq->name,
  • file, line, th->base_block->iseq->self,
  • file, filepath, line, th->base_block->iseq->self, ISEQ_TYPE_EVAL, &option); } else {
  • return rb_iseq_new_with_opt(node, rb_str_new2(""), file, line, Qfalse,
  • return rb_iseq_new_with_opt(node, rb_str_new2(""), file, filepath, line, Qfalse, ISEQ_TYPE_TOP, &option); } } @@ -556,21 +561,21 @@ rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE line, VALUE opt) VALUE rb_iseq_compile(VALUE src, VALUE file, VALUE line) {
  • return rb_iseq_compile_with_option(src, file, line, Qnil);
  • return rb_iseq_compile_with_option(src, file, Qnil, line, Qnil); }

static VALUE
iseq_s_compile(int argc, VALUE *argv, VALUE self)
{

  • VALUE src, file = Qnil, line = INT2FIX(1), opt = Qnil;
  • VALUE src, file = Qnil, path = Qnil, line = INT2FIX(1), opt = Qnil;

    rb_secure(1);

  • rb_scan_args(argc, argv, "13", &src, &file, &line, &opt);

  • rb_scan_args(argc, argv, "13", &src, &file, &path, &line, &opt);
    if (NIL_P(file)) file = rb_str_new2("");
    if (NIL_P(line)) line = INT2FIX(1);

  • return rb_iseq_compile_with_option(src, file, line, opt);

  • return rb_iseq_compile_with_option(src, file, path, line, opt);
    }

static VALUE
@@ -593,7 +598,7 @@ iseq_s_compile_file(int argc, VALUE *argv, VALUE self)
parser = rb_parser_new();
node = rb_parser_compile_file(parser, fname, f, NUM2INT(line));
make_compile_option(&option, opt);

  • return rb_iseq_new_with_opt(node, rb_str_new2(""), file, line, Qfalse,
  • return rb_iseq_new_with_opt(node, rb_str_new2(""), file, file, line, Qfalse, ISEQ_TYPE_TOP, &option); }

@@ -1311,7 +1316,7 @@ iseq_data_to_ary(rb_iseq_t *iseq)

  /*
   * [:magic, :major_version, :minor_version, :format_type, :misc,
  • * :name, :filename, :line_no, :type, :locals, :args,
  • * :name, :filename, :filepath, :line_no, :type, :locals, :args,
    • :catch_table, :bytecode] */ rb_ary_push(val, rb_str_new2("YARVInstructionSequence/SimpleDataFormat")); @@ -1321,6 +1326,7 @@ iseq_data_to_ary(rb_iseq_t *iseq) rb_ary_push(val, misc); rb_ary_push(val, iseq->name); rb_ary_push(val, iseq->filename);
  • rb_ary_push(val, iseq->filepath); rb_ary_push(val, iseq->line_no); rb_ary_push(val, type); rb_ary_push(val, locals); diff --git a/load.c b/load.c index 7551b6b..c532544 100644 --- a/load.c +++ b/load.c @@ -288,7 +288,7 @@ rb_load_internal(VALUE fname, int wrap) th->mild_compile_error++; node = (NODE *)rb_load_file(RSTRING_PTR(fname)); loaded = TRUE;
  • iseq = rb_iseq_new_top(node, rb_str_new2(""), fname, Qfalse);
  • iseq = rb_iseq_new_top(node, rb_str_new2(""), fname, fname, Qfalse); th->mild_compile_error--; rb_iseq_eval(iseq); } @@ -439,6 +439,19 @@ rb_f_require(VALUE obj, VALUE fname) return rb_require_safe(fname, rb_safe_level()); }

+VALUE
+rb_f_require_relative(VALUE obj, VALUE fname)
+{

  • VALUE rb_current_realfilepath(void);
  • VALUE rb_file_s_dirname(VALUE klass, VALUE fname);
  • VALUE base = rb_current_realfilepath();
  • if (NIL_P(base)) {
  • rb_raise(rb_eLoadError, "cannot infer basepath");
  • }
  • base = rb_file_s_dirname(rb_cFile, base);
  • return rb_require_safe(rb_file_expand_path(fname, base), rb_safe_level());
    +}
    +
    static int
    search_required(VALUE fname, volatile VALUE *path, int safe_level)
    {
    @@ -734,6 +747,7 @@ Init_load()

    rb_define_global_function("load", rb_f_load, -1);
    rb_define_global_function("require", rb_f_require, 1);

  • rb_define_global_function("require_relative", rb_f_require_relative, 1);
    rb_define_method(rb_cModule, "autoload", rb_mod_autoload, 2);
    rb_define_method(rb_cModule, "autoload?", rb_mod_autoload_p, 1);
    rb_define_global_function("autoload", rb_f_autoload, 2);
    diff --git a/prelude.rb b/prelude.rb
    index 679e831..1d084df 100644
    --- a/prelude.rb
    +++ b/prelude.rb
    @@ -22,17 +22,3 @@ class Thread
    }
    end

    end

    -module Kernel

  • module_function

  • def require_relative(relative_feature)

  • c = caller.first

  • e = c.rindex(/:\d+:in /)

  • file = $`

  • if /\A((.*))/ =~ file # eval, etc.

  •  raise LoadError, "require_relative is called in #{$1}"
    
  • end

  • absolute_feature = File.join(File.dirname(File.realpath(file)),
    relative_feature)

  • require absolute_feature

  • end
    -end
    diff --git a/ruby.c b/ruby.c
    index 5367720..a7353a9 100644
    --- a/ruby.c
    +++ b/ruby.c
    @@ -1445,7 +1445,9 @@ process_options(int argc, char **argv, struct
    cmdline_options *opt)
    }

    PREPARE_PARSE_MAIN({

  • iseq = rb_iseq_new_main(tree, opt->script_name);

  • VALUE path = Qnil;

  • if (!opt->e_script && strcmp(opt->script, "-")) path = opt->script_name;

  • iseq = rb_iseq_new_main(tree, opt->script_name, path);
    });

    if (opt->dump & DUMP_BIT(insns)) {
    diff --git a/vm.c b/vm.c
    index 96a04bf..678fcb8 100644
    --- a/vm.c
    +++ b/vm.c
    @@ -1441,7 +1441,7 @@ rb_vm_call_cfunc(VALUE recv, VALUE
    (*func)(VALUE), VALUE arg,
    {
    rb_thread_t *th = GET_THREAD();
    const rb_control_frame_t *reg_cfp = th->cfp;

  • volatile VALUE iseqval = rb_iseq_new(0, filename, filename, 0,
    ISEQ_TYPE_TOP);

  • volatile VALUE iseqval = rb_iseq_new(0, filename, filename,
    filename, 0, ISEQ_TYPE_TOP);
    VALUE val;

    vm_push_frame(th, DATA_PTR(iseqval), VM_FRAME_MAGIC_TOP,
    @@ -2052,7 +2052,7 @@ Init_VM(void)
    rb_vm_t *vm = ruby_current_vm;
    rb_thread_t *th = GET_THREAD();
    VALUE filename = rb_str_new2("");

  • volatile VALUE iseqval = rb_iseq_new(0, filename, filename, 0, ISEQ_TYPE_TOP);

  • volatile VALUE iseqval = rb_iseq_new(0, filename, filename, Qnil, 0,
    ISEQ_TYPE_TOP);
    volatile VALUE th_self;
    rb_iseq_t *iseq;

diff --git a/vm_core.h b/vm_core.h
index 126f48e..7bf199b 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -155,6 +155,7 @@ struct rb_iseq_struct {
VALUE type; /* instruction sequence type /
VALUE name; /
String: iseq name /
VALUE filename; /
file information where this sequence from */

  • VALUE filepath; /* real file path or nil / VALUE *iseq; / iseq (insn number and openrads) / VALUE *iseq_encoded; / encoded iseq */ unsigned long iseq_size; @@ -464,11 +465,11 @@ typedef struct rb_thread_struct } rb_thread_t;

/* iseq.c /
-VALUE rb_iseq_new(NODE
, VALUE, VALUE, VALUE, VALUE);
-VALUE rb_iseq_new_top(NODE node, VALUE name, VALUE filename, VALUE parent);
-VALUE rb_iseq_new_main(NODE *node, VALUE filename);
-VALUE rb_iseq_new_with_bopt(NODE
, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE);
-VALUE rb_iseq_new_with_opt(NODE*, VALUE, VALUE, VALUE, VALUE, VALUE,
const rb_compile_option_t*);
+VALUE rb_iseq_new(NODE*, VALUE, VALUE, VALUE, VALUE, VALUE);
+VALUE rb_iseq_new_top(NODE node, VALUE name, VALUE filename, VALUE
filepath, VALUE parent);
+VALUE rb_iseq_new_main(NODE *node, VALUE filename, VALUE filepath);
+VALUE rb_iseq_new_with_bopt(NODE
, VALUE, VALUE, VALUE, VALUE, VALUE,
VALUE, VALUE);
+VALUE rb_iseq_new_with_opt(NODE*, VALUE, VALUE, VALUE, VALUE, VALUE,
VALUE, const rb_compile_option_t*);
VALUE rb_iseq_compile(VALUE src, VALUE file, VALUE line);
VALUE rb_iseq_disasm(VALUE self);
int rb_iseq_disasm_insn(VALUE str, VALUE *iseqval, size_t pos,
rb_iseq_t *iseq, VALUE child);
diff --git a/vm_eval.c b/vm_eval.c
index 2e011c7..0ce4b8f 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -1709,6 +1709,16 @@ rb_f_block_given_p(void)
}
}

+VALUE
+rb_current_realfilepath(void)
+{

  • rb_thread_t *th = GET_THREAD();
  • rb_control_frame_t *cfp = th->cfp;
  • cfp = vm_get_ruby_level_caller_cfp(th, RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp));
  • if (cfp != 0) return cfp->iseq->filepath;
  • return Qnil; +} + void Init_vm_eval(void) {

あきらめ気味なのでこっそり言いますが、$: から "." を省くような仕様
変更を 1.9 系列中で行うのは避けるべきだと思っています。

--
Yusuke ENDOH mame@tsg.ne.jp

=end

#9

Updated by mame (Yusuke Endoh) over 10 years ago

  • Status changed from Assigned to Closed
  • % Done changed from 0 to 100

=begin
This issue was solved with changeset r26959.
Yusuke, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.

=end

Also available in: Atom PDF