ruby/tool/compile_prelude.rb

199 строки
4.9 KiB
Ruby
Исходник Обычный вид История

# This file is interpreted by $(BASERUBY) and miniruby.
# $(BASERUBY) is used for miniprelude.c.
# miniruby is used for prelude.c.
# Since $(BASERUBY) may be older than Ruby 1.9,
# Ruby 1.9 feature should not be used.
require 'erb'
class Prelude
SRCDIR = File.dirname(File.dirname(__FILE__))
$:.unshift(SRCDIR)
C_ESC = {
"\\" => "\\\\",
'"' => '\"',
"\n" => '\n',
}
0x00.upto(0x1f) {|ch| C_ESC[[ch].pack("C")] ||= "\\%03o" % ch }
0x7f.upto(0xff) {|ch| C_ESC[[ch].pack("C")] = "\\%03o" % ch }
C_ESC_PAT = Regexp.union(*C_ESC.keys)
def c_esc(str)
'"' + str.gsub(C_ESC_PAT) { C_ESC[$&] } + '"'
end
def prelude_base(filename)
filename[/\A#{Regexp.quote(SRCDIR)}\/(.*?)(\.rb)?\z/om, 1]
end
def prelude_name(filename)
"<internal:" + prelude_base(filename) + ">"
end
def initialize(preludes)
@mkconf = nil
@have_sublib = false
@need_ruby_prefix = false
@preludes = {}
@mains = preludes.map {|filename| translate(filename)[0]}
@preludes.delete_if {|_, (_, _, lines, sub)| !sub && lines.empty?}
end
def translate(filename, sub = false)
idx = @preludes[filename]
return idx if idx
lines = []
@preludes[filename] = result = [@preludes.size, filename, lines, sub]
File.readlines(filename).each do |line|
line.gsub!(/RbConfig::CONFIG\["(\w+)"\]/) {
key = $1
unless @mkconf
require './rbconfig'
@mkconf = RbConfig::MAKEFILE_CONFIG.merge('prefix'=>'#{TMP_RUBY_PREFIX}')
end
if RbConfig::MAKEFILE_CONFIG.has_key? key
val = RbConfig.expand("$(#{key})", @mkconf)
@need_ruby_prefix ||= /\A\#\{TMP_RUBY_PREFIX\}/ =~ val
c_esc(val)
else
"nil"
end
}
line.sub!(/require\s*\(?\s*(["'])(.*?)\1\)?/) do
orig, path = $&, $2
path = File.join(SRCDIR, path)
if File.exist?(path)
@have_sublib = true
"TMP_RUBY_PREFIX.require(#{translate(path, true)[0]})"
else
orig
end
end
lines << c_esc(line)
end
result
end
def emit(outfile)
@init_name = outfile[/\w+(?=_prelude.c\b)/] || 'prelude'
erb = ERB.new(<<'EOS', nil, '%')
/* -*-c-*-
THIS FILE WAS AUTOGENERATED BY tool/compile_prelude.rb. DO NOT EDIT.
sources: <%= @preludes.map {|n,*| prelude_base(n)}.join(', ') %>
*/
#include "ruby/ruby.h"
#include "internal.h"
#include "vm_core.h"
% preludes = @preludes.values.sort
% preludes.each {|i, prelude, lines, sub|
static const char prelude_name<%=i%>[] = <%=c_esc(prelude_name(*prelude))%>;
static const char prelude_code<%=i%>[] =
% lines.each {|line|
<%=line%>
% }
;
% }
#define PRELUDE_COUNT <%=@have_sublib ? preludes.size : 0%>
% if @have_sublib or @need_ruby_prefix
struct prelude_env {
volatile VALUE prefix_path;
#if PRELUDE_COUNT > 0
char loaded[PRELUDE_COUNT];
#endif
};
static VALUE
prelude_prefix_path(VALUE self)
{
struct prelude_env *ptr = DATA_PTR(self);
return ptr->prefix_path;
}
% end
% unless preludes.empty?
static void
prelude_eval(VALUE code, VALUE name, VALUE line)
{
* iseq.c, vm_eval.c: set th->base_block properly. th->base_block is information for (a) parsing, (b) compiling and (c) setting up the frame to execute the program passed by `eval' method. For example, (1) parser need to know up-level variables to detect it is variable or method without paren. Befor (a), (b) and (c), VM set th->base_block by passed bindng (or previous frame information). After execute (a), (b) and (c), VM should clear th->base_block. However, if (a), (b) or (c) raises an exception, then th->base_block is not cleared. Problem is that the uncleared value th->balo_block is used for irrelevant iseq compilation. It causes SEGV or critical error. I tried to solve this problem: to clear them before exception, but finally I found out that it is difficult to do it (Ruby program can be run in many places). Because of this background, I set th->base_block before compiling iseq and restore it after compiling. Basically, th->base_block is dirty hack (similar to global variable) and this patch is also dirty. * bootstraptest/test_eval.rb: add a test for above. * internal.h: remove unused decl. * iseq.c (rb_iseq_compile_with_option): add base_block parameter. set th->base_block before compation and restore it after compilation. * ruby.c (require_libraries): pass 0 as base_block instead of setting th->base_block * tool/compile_prelude.rb (prelude_eval): apply above changes. * vm.c, vm_eval.c: ditto. * vm_core.h: add comments. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36179 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-22 13:32:56 +04:00
rb_iseq_eval(rb_iseq_compile_with_option(code, name, Qnil, line, 0, Qtrue));
}
% end
% if @have_sublib
static VALUE
prelude_require(VALUE self, VALUE nth)
{
struct prelude_env *ptr = DATA_PTR(self);
VALUE code, name;
int n = FIX2INT(nth);
if (n > PRELUDE_COUNT) return Qfalse;
if (ptr->loaded[n]) return Qfalse;
ptr->loaded[n] = 1;
switch (n) {
% @preludes.each_value do |i, prelude, lines, sub|
% if sub
case <%=i%>:
code = rb_usascii_str_new(prelude_code<%=i%>, sizeof(prelude_code<%=i%>) - 1);
name = rb_usascii_str_new(prelude_name<%=i%>, sizeof(prelude_name<%=i%>) - 1);
break;
% end
% end
default:
return Qfalse;
}
prelude_eval(code, name, INT2FIX(1));
return Qtrue;
}
% end
void
Init_<%=@init_name%>(void)
{
% if @have_sublib or @need_ruby_prefix
struct prelude_env memo;
ID name = rb_intern("TMP_RUBY_PREFIX");
VALUE prelude = Data_Wrap_Struct(rb_cData, 0, 0, &memo);
memo.prefix_path = rb_const_remove(rb_cObject, name);
rb_const_set(rb_cObject, name, prelude);
rb_define_singleton_method(prelude, "to_s", prelude_prefix_path, 0);
% end
% if @have_sublib
memset(memo.loaded, 0, sizeof(memo.loaded));
rb_define_singleton_method(prelude, "require", prelude_require, 1);
% end
% preludes.each do |i, prelude, lines, sub|
% next if sub
prelude_eval(
rb_usascii_str_new(prelude_code<%=i%>, sizeof(prelude_code<%=i%>) - 1),
rb_usascii_str_new(prelude_name<%=i%>, sizeof(prelude_name<%=i%>) - 1),
INT2FIX(1));
% end
% if @have_sublib or @need_ruby_prefix
rb_gc_force_recycle(prelude);
% end
#if 0
% preludes.length.times {|i|
puts(prelude_code<%=i%>);
% }
#endif
}
EOS
tmp = erb.result(binding)
open(outfile, 'w'){|f|
f << tmp
}
end
end
preludes = ARGV.dup
outfile = preludes.pop
Prelude.new(preludes).emit(outfile)