<% # 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. class Prelude LINE_LIMIT = 509 # by C89 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.chomp(".rb") end def prelude_name(filename) "" end def initialize(init_name, preludes, vpath) @init_name = init_name @have_sublib = false @vpath = vpath @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 = [] result = [@preludes.size, @vpath.strip(filename), lines, sub] @vpath.foreach(filename) do |line| @preludes[filename] ||= result comment = ($1 || '' if line.sub!(/(?:^|\s+)\#(?:$|[#\s](.*))/, '')) if line.size > LINE_LIMIT raise "#{filename}:#{lines.size+1}: too long line" end line.sub!(/require(_relative)?\s*\(?\s*(["'])(.*?)(?:\.rb)?\2\)?/) do orig, rel, path = $&, $2, $3 if rel path = File.join(File.dirname(filename), path) nil while path.gsub!(%r'(\A|/)(?!\.\.?/)[^/]+/\.\.(?:/|\z)', '') end path = translate("#{path}.rb", true) rescue nil if path @have_sublib = true "TMP_RUBY_PREFIX.require(#{path[0]})" else orig end end lines << [line, comment] end result end end Prelude.new(output && output[/\w+(?=_prelude.c\b)/] || 'prelude', ARGV, vpath).instance_eval do -%> /* -*-c-*- THIS FILE WAS AUTOGENERATED BY template/prelude.c.tmpl. DO NOT EDIT. sources: <%= @preludes.map {|n,*| prelude_base(n)}.join(', ') %><%=%> */ %unless @preludes.empty? #include "ruby/ruby.h" #include "internal.h" #include "vm_core.h" #include "iseq.h" % preludes = @preludes.values.sort % preludes.each {|i, prelude, lines, sub| % name = prelude_name(*prelude) static const struct { char L0[<%=name.size%><%=%>]; } prelude_name<%=i%><%=%> = {"<%=c_esc(name)%>"}; static const struct { % size = beg = 0 % lines.each_with_index {|(line, comment), n| % if size + line.size < Prelude::LINE_LIMIT % size += line.size % next % end char L<%=beg%><%=%>[<%=size%><%=%>]; /* <%=beg+1%>..<%=n%> */ % size = line.size % beg = n % } % if size > 0 char L<%=beg%><%=%>[<%=size%><%=%>]; /* <%=beg+1%>..<%=lines.size+1%> */ % end } prelude_code<%=i%><%=%> = { % size = 0 #line 1 "<%=c_esc(prelude)%>" % lines.each_with_index {|(line, comment), n| % if size + line.size >= Prelude::LINE_LIMIT % size = 0 , #line <%=n+1%> "<%=c_esc(prelude)%>" % end % size += line.size "<%=c_esc(line)%>"<%if comment%>/* <%=c_esc(comment)%> */<%end%> % } #line <%=_erbout.count("\n")+2%> "<%=@init_name%>.c" }; % } % if @have_sublib #define PRELUDE_COUNT <%=preludes.size%> 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? #define PRELUDE_STR(n) rb_usascii_str_new_static(prelude_##n.L0, sizeof(prelude_##n)) #if defined __GNUC__ && __GNUC__ >= 5 # pragma GCC diagnostic push # pragma GCC diagnostic error "-Wmissing-field-initializers" #endif static void prelude_eval(VALUE code, VALUE name, int line) { static const rb_compile_option_t optimization = { TRUE, /* int inline_const_cache; */ TRUE, /* int peephole_optimization; */ TRUE, /* int tailcall_optimization; */ TRUE, /* int specialized_instruction; */ TRUE, /* int operands_unification; */ TRUE, /* int instructions_unification; */ TRUE, /* int stack_caching; */ TRUE, /* int frozen_string_literal; */ FALSE, /* int debug_frozen_string_literal; */ FALSE, /* unsigned int coverage_enabled; */ 0, /* int debug_level; */ }; rb_ast_t *ast = rb_parser_compile_string_path(rb_parser_new(), name, code, line); if (!ast->body.root) { rb_ast_dispose(ast); rb_exc_raise(rb_errinfo()); } rb_iseq_eval(rb_iseq_new_with_opt(&ast->body, name, name, Qnil, INT2FIX(line), NULL, ISEQ_TYPE_TOP, &optimization)); rb_ast_dispose(ast); } #if defined __GNUC__ && __GNUC__ >= 5 # pragma GCC diagnostic pop #endif % 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 = PRELUDE_STR(code<%=i%><%=%>); name = PRELUDE_STR(name<%=i%><%=%>); break; % end % end default: return Qfalse; } prelude_eval(code, name, 1); return Qtrue; } % end %end void Init_<%=@init_name%><%=%>(void) { %unless @preludes.empty? % if @have_sublib 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(PRELUDE_STR(code<%=i%><%=%>), PRELUDE_STR(name<%=i%><%=%>), 1); % end % if @have_sublib rb_gc_force_recycle(prelude); % end #if 0 % preludes.length.times {|i| printf("%.*s", (int)sizeof(prelude_code<%=i%><%=%>), prelude_code<%=i%><%=%>.L0); % } #endif %end } <%end -%>