[Bug:trunk] require_relative depends on current directory

e$B1sF#$G$9!#e(B

e$B%=!<%9%U%!%$%k$rAjBP%Q%9$G;XDj$7$F5/F0$7$?$H$-!“e(Brequire_relative
e$B$,e(B
e$B%+%l%s%H%G%#%l%/%H%j$K0MB8$9$k$h$&$G$9$,!”$3$l$O;EMM$G$7$g$&$+!#e(B

$ 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

e$BAjBP%Q%9$G5/F0$9$k$H!"e(Brequire_relative

e$B;~$N%+%l%s%H%G%#%l%/%H%j$r8+$ke(B
$ cd /tmp/
$ ruby19 t1.rb
“/tmp/foo/t2”

e$B%U%k%Q%9$G5/F0$9$l$PLdBj$J$$e(B

$ ruby19 /tmp/t1.rb
“/tmp/t2”

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

e$B$"$H!"e(Brequire_relative e$B$Ne(B rdoc
e$B$,$J$$5$$,$9$k$N$G$9$,!"e(Bprelude.rb e$B$Ke(B
e$B=q$1$P$$$$$s$G$7$g$&$+!#e(B

2010e$BG/e(B1e$B7ne(B9e$BF|e(B23:15 Yusuke ENDOH [email protected]:

e$B%=!<%9%U%!%$%k$rAjBP%Q%9$G;XDj$7$F5/F0$7$?$H$-!“e(Brequire_relative e$B$,e(B
e$B%+%l%s%H%G%#%l%/%H%j$K0MB8$9$k$h$&$G$9$,!”$3$l$O;EMM$G$7$g$&$+!#e(B

e$B0U?^$7$F$=$&$J$C$F$$$k$o$1$G$O$"$j$^$;$s!#e(B

e$B%=!<%9%U%!%$%k$rFI$s$@$H$-$N%+%l%s%H%G%#%l%/%H%j$C$F!“e(B
e$B$I$3$+$K5-O?$7$F$”$j$^$7$?$C$1!)e(B

e$B%A%1%C%He(B #2581 e$B$,99?7$5$l$^$7$?!#e(B (by Yusuke E.)

e$BM%@hEYe(B Normale$B$+$ie(BUrgente$B$KJQ99e(B

e$B1sF#$G$9!#e(B

e$B%=!<%9%U%!%$%k$rAjBP%Q%9$G;XDj$7$F5/F0$7$?$H$-!“e(Brequire_relative e$B$,e(B
e$B%+%l%s%H%G%#%l%/%H%j$K0MB8$9$k$h$&$G$9$,!”$3$l$O;EMM$G$7$g$&$+!#e(B

e$B$3$N7o!"$I$&$J$C$F$^$9$G$7$g$&$+!#e(B
File.realpath e$B$,;H$o$l$k$h$&$K$J$C$?1F6A$GB?>/H/8=$O$7$K$/$/e(B
e$B$J$C$?$h$&$G$9$,!"K<AE*$K$O2r7h$7$F$$$J$$$H$$$&G’<1$G$9!#e(B

1.9.2 e$B$G$Oe(B $: e$B$+$ie(B “.” e$B$,>C$($k$?$ae(B require_relative
e$B$N<{MW$,e(B
e$BA}$($k$3$H$,M=A[$5$l$^$9!#$=$N$?$a!“$3$NLdBj$O$o$j$H=EBg$@$He(B
e$B;W$C$F$$$^$9!#$H$j$”$($:M%@hEY$re(B urgent e$B$K$7$F$*$-$^$9!#e(B

e$B$3$N%a%=%C%I$,e(B prelude.rb e$B$GDj5A$5$l$F$$$?$j!“e(Bcaller
e$B$r;H$C$?e(B
e$BIT0B$J<BAu$K$J$C$F$$$?$j$9$k$N$OM}M3$,$”$k$s$G$7$g$&$+!#e(B


Yusuke E. [email protected]

http://redmine.ruby-lang.org/issues/show/2581

2010e$BG/e(B1e$B7ne(B9e$BF|e(B23:15 Yusuke ENDOH [email protected]:

$ cat /tmp/t2.rb
$ cd /tmp/
$ ruby19 t1.rb
“/tmp/foo/t2”

e$B$*$&$A$Ne(BLinuxe$B%^%7%s$G$O0c$&%(%i!<$,=P$^$9e(B

% 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)

e$B$J$<$+!"e(Bchdire$B8e$Ne(Brequire_relativee$B$NCf$G4V0c$C$?%G%#%l%/%H%j$Ge(Bt1.rbe$B$r3+$3$&$H$9$k$h$&$G$9!#e(B

e$B$J$<$+!"e(Bchdire$B8e$Ne(Brequire_relativee$B$NCf$G4V0c$C$?%G%#%l%/%H%j$Ge(Bt1.rbe$B$r3+$3$&$H$9$k$h$&$G$9!#e(B

e$BEz$($OA4It%=!<%9$K$"$j$^$7$?e(B e$B"+$"$?$j$^$($G$9$,e(B

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

29e$B9TL$Ge(Bcallere$B$r;H$C$F8F$S=P$7$b$H%U%!%$%ke(B t1.rb
e$B$r<hF@$7$F!"e(B35e$B9TL$Ge(BFile.realpath()e$B$re(B
e$B;H$C$F@dBP%Q%9$KJQ49$7$h$&$H$7$F$$$^$9$,!“e(Bchdire$B$O$3$N%”%k%4%j%:%`$N7j$r$D$$$F$7$^$$$^$9!#e(B

e$B#1!%$=$b$=$be(Brealpath()e$B$O%+%l%s%H%Q%9$r85$KAjBP%Q%9$r@dBP%Q%9$KJd40$9$k4X?t$J$N$Ge(B
e$B;HMQ<T$,4|BT$7$F$$$k!"%9%/%j%W%H%U%!%$%k$+$i$NAjBP0LCV$G$O$J$$e(B
e$B#2!%e(BFile.realpath()e$B$OB8:_$7$J$$%Q%9$r0z?t$KM?$($k$HNc30Ej$2$k!#$h$C$F!“e(B
e$B%-%c%C%A$7$J$1$l$P!”%9%/%j%W%H$,=*N;$7$F$7$^$&!#e(B

e$B$h$/CN$i$J$$$N$G$9$,!"FbItE*$K$OFs=Ee(Brequiree$BKI;MQ$K@dBP%Q%9$G%m!<%I:Q$%U%!%$%k>pJse(B
e$B;}$C$F$$$k5$$,$9$k$N$Ge(Brequire_relativee$B$rAH$9~$$K$9$l$P2r7h!)e(B

2010e$BG/e(B3e$B7ne(B7e$BF|e(B0:30 Yusuke E. [email protected]:

e$B%=!<%9%U%!%$%k$rAjBP%Q%9$G;XDj$7$F5/F0$7$?$H$-!“e(Brequire_relative e$B$,e(B
e$B%+%l%s%H%G%#%l%/%H%j$K0MB8$9$k$h$&$G$9$,!”$3$l$O;EMM$G$7$g$&$+!#e(B

e$B$3$N7o!"$I$&$J$C$F$^$9$G$7$g$&$+!#e(B

e$B$H$/$K$I$&$K$b!#e(B

e$B$I$&$K$+$9$k$K$O5/F0;~$N%+%l%s%H%G%#%l%/%H%j$r$I$3$+$K5-O?$7$F$*$/$s$G$7$g$&$M!#e(B
e$B$=$l$K$OL>A0$,I,MW$+$J!#e(B

File.realpath
e$B$r3HD%$7$F!"%+%l%s%H%G%#%l%/%H%j$NBe$o$j$K;H$&%G%#%l%/%H%j$re(B
e$B;XDj$G$-$k$h$&$K$9$k$N$O2DG=$G$9!#e(B

1.9.2 e$B$G$Oe(B $: e$B$+$ie(B “.” e$B$,>C$($k$?$ae(B require_relative e$B$N<{MW$,e(B
e$BA}$($k$3$H$,M=A[$5$l$^$9!#$=$N$?$a!“$3$NLdBj$O$o$j$H=EBg$@$He(B
e$B;W$C$F$$$^$9!#$H$j$”$($:M%@hEY$re(B urgent e$B$K$7$F$*$-$^$9!#e(B

e$B%9%/%j%W%H@hF,$K=q$/$V$s$K$OF0$/$7$J$!!#e(B

e$B$3$N%a%=%C%I$,e(B prelude.rb e$B$GDj5A$5$l$F$$$?$j!“e(Bcaller e$B$r;H$C$?e(B
e$BIT0B$J<BAu$K$J$C$F$$$?$j$9$k$N$OM}M3$,$”$k$s$G$7$g$&$+!#e(B

e$B$@$l$be(B C e$B$G<BAu$7$F$$$J$$$+$ie(B?

1.9.2 e$B$G$Oe(B $: e$B$+$ie(B “.” e$B$,>C$($k$?$ae(B require_relative e$B$N<{MW$,e(B
e$BA}$($k$3$H$,M=A[$5$l$^$9!#$=$N$?$a!"$3$NLdBj$O$o$j$H=EBg$@$He(B
e$B;W$C$F$$$^$9!#$H$j$"$($:M%@hEY$re(B urgent e$B$K$7$F$*$-$^$9!#e(B

e$B%9%/%j%W%H@hF,$K=q$/$V$s$K$OF0$/$7$J$!!#e(B

e$B$$$^$^$G$N5DO@$G!">r7o$,L@$i$+$K$J$C$?$H;W$C$F$*$j!"$+$D$=$N>r7oe(B
e$B!Je(Brequire_relativee$BA0$K%9%/%j%W%H$,e(Bchdire$B$r8F$V!K$O!"$"$^$jIaDL$N;H$$J}$G$Oe(B
e$B$J$5$=$&$@$H;W$C$F$$$^$9!#e(B

e$BD>$9$Y$-%P%0$@$H$O;W$$$^$9$,!"e(B1.9.2e$B$N%j%j!<%9$K1F6A$rM?$($k$[$I$N?<9oEY$H$Oe(B
e$B;W$($J$$$N$G!"M%@hEY$re(BNormale$B$KLa$9$3$H$rDs0F$7$^$9!#e(B

e$B1sF#$G$9!#e(B

2010e$BG/e(B3e$B7ne(B7e$BF|e(B21:17 KOSAKI Motohiro
[email protected]:

1.9.2 e$B$G$Oe(B $: e$B$+$ie(B “.” e$B$,>C$($k$?$ae(B require_relative e$B$N<{MW$,e(B
e$BA}$($k$3$H$,M=A[$5$l$^$9!#$=$N$?$a!“$3$NLdBj$O$o$j$H=EBg$@$He(B
e$B;W$C$F$$$^$9!#$H$j$”$($:M%@hEY$re(B urgent e$B$K$7$F$*$-$^$9!#e(B

e$B%9%/%j%W%H@hF,$K=q$/$V$s$K$OF0$/$7$J$!!#e(B

e$B$$$^$^$G$N5DO@$G!“>r7o$,L@$i$+$K$J$C$?$H;W$C$F$*$j!”$+$D$=$N>r7oe(B
e$B!Je(Brequire_relativee$BA0$K%9%/%j%W%H$,e(Bchdire$B$r8F$V!K$O!“$”$^$jIaDL$N;H$$J}$G$Oe(B
e$B$J$5$=$&$@$H;W$C$F$$$^$9!#e(B

e$B>r7o$O!"e(B

  • e$BAjBP%Q%9$G5/F0$7$?%9%/%j%W%H$NCf$Ge(B
  • Dir.chdir e$B$7$?8e$Ke(B
  • require_relative e$B$r8F$Ve(B

e$B$G$9$h$M!#e(B

Ruby e$B$G$O!“%U%!%$%k@hF,$G$J$/!”%3!<%ICf$GI,MW$K$J$C$F;O$a$Fe(B
require e$B$re(B
e$B8F$V$H$$$&%3!<%I$,7k9=$“$j$^$9!#e(Blib/
e$B0J2<$K$b$?$/$5$s8+$D$+$j$^$9!#e(B
(e$BI8=`E:IU%i%$%V%i%j$N%3!<%I$O>e5-$N>r7o$K$”$o$J$$$+$iLdBj$J$$$1$l$Ie(B)

e$BF1$8%N%j$Ge(B require_relative
e$B$r;H$o$l$k$3$H$OA[A|$KFq$/$J$$$G$9!#e(B

e$BD>$9$Y$-%P%0$@$H$O;W$$$^$9$,!"e(B1.9.2e$B$N%j%j!<%9$K1F6A$rM?$($k$[$I$N?<9oEY$H$Oe(B
e$B;W$($J$$$N$G!"M%@hEY$re(BNormale$B$KLa$9$3$H$rDs0F$7$^$9!#e(B

$: e$B$+$ie(B “.” e$B$,>C$5$l$?M}M3$,@5<0$K8x3+$5$l$F$$$J$$e(B
(e$B%;%-%e%j%F%#4X78$Ne(B
e$BBP:v$G$“$k$?$a8x3+$7$?$/$J$$!)e(B)
e$B$N$G$9$,!”$3$NLdBj$O$=$NBP:v$rBfL5$7$Ke(B
e$B$7$F$7$^$&$o$1$G$O$J$$$G$9$+!)e(B
e$B$J$$$H$$$&$3$H$G$"$l$P!“e(BNormal e$B$KLa$7$?$$$H;W$$$^$9!#e(Bakr
e$B$5$s$,=$@5$Ke(B
e$B>h$j5$$G$J$$$H$o$+$C$?$N$G!”$*$=$i$/Bg>fIW$J$N$+$J$H;W$$$^$9$,!#e(B

e$B1sF#$G$9!#e(B

2010e$BG/e(B3e$B7ne(B7e$BF|e(B22:26 Tanaka A. [email protected]:

e$B$^$!D>$9$N$OL>A00J30Fq$7$/$O$J$/$F!"e(B

snip

e$B$H$+$G$9$+$M!#e(B

e$B$“$”$J$k$[$I!"$3$l$@$1$G$$$$$s$G$9$M!#e(B
rb_iseq_t e$B$K@dBP%Q%9$r:$;$J$-$c$J$i$s$H;W$$9~$s$G$^$7$?!#e(B

Dir::INITIAL_WORKING_DIRECTORY e$B$H$$$&$N$,Dj5A$5$l$k$N$G!"e(B
e$B$3$NL>A0$,5$$KF~$i$J$$?M$,=P$F$/$k$HFq$7$$$H$3$m$G$9$,!#e(B

e$B;d$O9=$o$J$$$H;W$$$^$9$,!"$I$&$G$7$g$&$M!#e(B
define_method e$B$r;H$C$F$h$1$l$P!"M>J,$JDj?t$b$J$7$G:Q$`5$$b$7$^$9!#e(B

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

e$B;n$7$F$$$?$i!"0J2<$N$h$&$K$O$^$j$^$7$?!#e(B

$ ./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 `’

e$B$&!<$s!"$3$l$O$7$g$&$,$J$$$+!#e(B

2010e$BG/e(B3e$B7ne(B7e$BF|e(B22:06 Yusuke ENDOH [email protected]:

Ruby e$B$G$O!“%U%!%$%k@hF,$G$J$/!”%3!<%ICf$GI,MW$K$J$C$F;O$a$Fe(B require e$B$re(B
e$B8F$V$H$$$&%3!<%I$,7k9=$“$j$^$9!#e(Blib/ e$B0J2<$K$b$?$/$5$s8+$D$+$j$^$9!#e(B
(e$BI8=`E:IU%i%$%V%i%j$N%3!<%I$O>e5-$N>r7o$K$”$o$J$$$+$iLdBj$J$$$1$l$Ie(B)

e$BF1$8%N%j$Ge(B require_relative e$B$r;H$o$l$k$3$H$OA[A|$KFq$/$J$$$G$9!#e(B

e$B$^$!D>$9$N$OL>A00J30Fq$7$/$O$J$/$F!"e(B

% svn diff --diff-cmd diff -x ‘-u -p’
Index: prelude.rb

— prelude.rb (revision 26840)
+++ prelude.rb (working copy)
@@ -23,6 +23,13 @@ class Thread
end
end

+Dir::INITIAL_WORKING_DIRECTORY =

  • begin
  • Dir.getwd
  • rescue Errno::EACCES, Errno::ENOENT
  • nil
  • end

module Kernel
module_function
def require_relative(relative_feature)
@@ -32,7 +39,7 @@ module Kernel
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,
    Dir::INITIAL_WORKING_DIRECTORY)), relative_feature)
    require absolute_feature
    end
    end

e$B$H$+$G$9$+$M!#e(B

Dir::INITIAL_WORKING_DIRECTORY e$B$H$$$&$N$,Dj5A$5$l$k$N$G!"e(B
e$B$3$NL>A0$,5$$KF~$i$J$$?M$,=P$F$/$k$HFq$7$$$H$3$m$G$9$,!#e(B

2010e$BG/e(B3e$B7ne(B7e$BF|e(B22:48 Yusuke ENDOH [email protected]:

e$B;n$7$F$$$?$i!"0J2<$N$h$&$K$O$^$j$^$7$?!#e(B

$ ./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 `’

e$B$&!<$s!"$3$l$O$7$g$&$,$J$$$+!#e(B

-e e$B$OFq$7$$$G$9$M!#e(B

e$B0J2<$N$h$&$J%*%W%7%g%s$H$7$F$Ne(B -e e$B$H!"e(B

% ruby -e ‘p caller(0)’
[“-e:1:in `'”]

e$B0J2<$N$h$&$J%U%!%$%kL>$H$7$F$Ne(B -e e$B$r6hJL$G$-$J$$$H;W$&$N$G!#e(B

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

e$B%A%1%C%He(B #2581 e$B$,99?7$5$l$^$7$?!#e(B (by Yusuke E.)

e$B%9%F!<%?%9e(B Assignede$B$+$ie(BClosede$B$KJQ99e(B
e$B?JD=e(B % 0e$B$+$ie(B100e$B$KJQ99e(B

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.


http://redmine.ruby-lang.org/issues/show/2581

e$B1sF#$G$9!#e(B

2010e$BG/e(B3e$B7ne(B8e$BF|e(B13:22 Tanaka A. [email protected]:

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

e$B$$H$J$7$/e(B C
e$B$G<BAu$7$F$_$^$7$?!#%U%!%$%k$+$i%m!<%I$7$?>l9g$K8B$j!"e(B
rb_iseq_t e$B$K@dBP%Q%9$r:$;$F$
$-$^$9!#e(B
e$B$3$N%A%1%C%H$N:G=i$NNc$,2r7h$7!"e(B-e
e$B$NNc$b0J2<$N$h$&$K$J$j$^$9!#e(B

$ ./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

e$BH?BP$,$J$5$=$&$J$i%3%_%C%H$7$h$&$H;W$$$^$9!#e(B

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(“<top (required)>”), fname,
    Qfalse);
  • iseq = rb_iseq_new_top(node, rb_str_new2(“<top (required)>”), 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)
{

e$B$"$-$i$a5$L#$J$N$G$3$C$=$j8@$$$^$9$,!"e(B$: e$B$+$ie(B “.”
e$B$r>J$/$h$&$J;EMMe(B
e$BJQ99$re(B 1.9 e$B7ONsCf$G9T$&$N$OHr$1$k$Y$-$@$H;W$C$F$$$^$9!#e(B