mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			281 lines
		
	
	
	
		
			7.7 KiB
		
	
	
	
		
			Cheetah
		
	
	
	
	
	
			
		
		
	
	
			281 lines
		
	
	
	
		
			7.7 KiB
		
	
	
	
		
			Cheetah
		
	
	
	
	
	
| <%
 | |
| # 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_relative '../tool/ruby_vm/helpers/c_escape'
 | |
| 
 | |
| class Prelude
 | |
|   include RubyVM::CEscape
 | |
|   LINE_LIMIT = 509 # by C89
 | |
| 
 | |
|   def prelude_base(filename)
 | |
|     filename.chomp(".rb")
 | |
|   end
 | |
|   def prelude_name(filename)
 | |
|     "<internal:" + prelude_base(filename) + ">"
 | |
|   end
 | |
| 
 | |
|   def initialize(output, preludes, vpath)
 | |
|     @output = output
 | |
|     @have_sublib = false
 | |
|     @vpath = vpath
 | |
|     @prelude_count = 0
 | |
|     @builtin_count = 0
 | |
|     @preludes = {}
 | |
|     @mains = preludes.map do |filename|
 | |
|       if prelude = filename.end_with?("golf_prelude.rb")
 | |
|         @prelude_count += 1
 | |
|       else
 | |
|         @builtin_count += 1
 | |
|       end
 | |
|       translate(filename, (filename unless prelude))[0]
 | |
|     end
 | |
|     @preludes.delete_if {|_, (_, _, lines, sub)| sub && lines.empty?}
 | |
|   end
 | |
| 
 | |
|   def translate(filename, sub = false)
 | |
|     idx = @preludes[filename]
 | |
|     return idx if idx
 | |
|     lines = []
 | |
|     start_line = nil
 | |
|     lineno = 0
 | |
|     result = [@preludes.size, @vpath.strip(filename), lines, sub]
 | |
|     @vpath.foreach(filename) do |line|
 | |
|       line.force_encoding("ASCII-8BIT") if line.respond_to?(:force_encoding)
 | |
|       line.rstrip!
 | |
|       lineno += 1
 | |
|       @preludes[filename] ||= result
 | |
|       comment = ($1 || '' if line.sub!(/(?:^|\s+)\#(?:$|[#\s](.*))/, ''))
 | |
|       if !line.empty? or start_line
 | |
|         line << "\n"
 | |
|         start_line ||= lineno
 | |
|       end
 | |
|       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 << (start_line || 1)
 | |
|     result
 | |
|   end
 | |
| end
 | |
| Prelude.new(output, 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 "internal.h"
 | |
| #include "internal/warnings.h"
 | |
| #include "iseq.h"
 | |
| #include "ruby/ruby.h"
 | |
| #include "vm_core.h"
 | |
| 
 | |
| COMPILER_WARNING_PUSH
 | |
| #if __has_warning("-Wstring-concatenation")
 | |
| COMPILER_WARNING_IGNORED(-Wstring-concatenation)
 | |
| #endif
 | |
| % preludes = @preludes.values.sort
 | |
| % preludes.each {|i, prelude, lines, sub|
 | |
| 
 | |
| %   name = prelude_name(*prelude)
 | |
| static const char prelude_name<%=i%><%=%>[] = <%=rstring2cstr(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 <%=rstring2cstr(prelude)%>
 | |
| %   lines.each_with_index {|(line, comment), n|
 | |
| %     if size + line.size >= Prelude::LINE_LIMIT
 | |
| %       size = 0
 | |
| ,
 | |
| #line <%=n+1%> <%=rstring2cstr(prelude)%>
 | |
| %     end
 | |
| %     size += line.size
 | |
| <%=rstring2cstr(line)%><%if comment%><%=commentify(comment)%><%end%>
 | |
| %   }
 | |
| #line <%=_erbout.count("\n")+2%> "<%=@output%>"
 | |
| };
 | |
| % }
 | |
| 
 | |
| COMPILER_WARNING_POP
 | |
| 
 | |
| % 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_NAME(n) rb_usascii_str_new_static(prelude_name##n, sizeof(prelude_name##n)-1)
 | |
| #define PRELUDE_CODE(n) rb_utf8_str_new_static(prelude_code##n.L0, sizeof(prelude_code##n))
 | |
| 
 | |
| static rb_ast_t *
 | |
| prelude_ast(VALUE name, VALUE code, int line)
 | |
| {
 | |
|     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());
 | |
|     }
 | |
|     return ast;
 | |
| }
 | |
| 
 | |
| % end
 | |
| % if @builtin_count > 0
 | |
| #define PRELUDE_AST(n, name_str, start_line) \
 | |
|     (((sizeof(prelude_name<%='##'%><%=%>n) - prefix_len - 2) == namelen) && \
 | |
|      (strncmp(prelude_name<%='##'%><%=%>n + prefix_len, feature_name, namelen) == 0) ? \
 | |
|      prelude_ast((name_str) = PRELUDE_NAME(n), PRELUDE_CODE(n), start_line) : 0)
 | |
| 
 | |
| rb_ast_t *
 | |
| rb_builtin_ast(const char *feature_name, VALUE *name_str)
 | |
| {
 | |
|     const size_t prefix_len = rb_strlen_lit("<internal:");
 | |
|     size_t namelen = strlen(feature_name);
 | |
|     rb_ast_t *ast = 0;
 | |
| 
 | |
| %   @preludes.each_value do |i, prelude, lines, sub, start_line|
 | |
| %     if sub and sub != true
 | |
|     if ((ast = PRELUDE_AST(<%=i%><%=%>, *name_str, <%=start_line%>)) != 0) return ast;
 | |
| %     end
 | |
| %   end
 | |
|     return ast;
 | |
| }
 | |
| 
 | |
| % end
 | |
| % if @prelude_count > 0
 | |
| COMPILER_WARNING_PUSH
 | |
| #if GCC_VERSION_SINCE(4, 2, 0)
 | |
| COMPILER_WARNING_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; */
 | |
|         FALSE,/* 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 = prelude_ast(name, code, line);
 | |
|     rb_iseq_eval(rb_iseq_new_with_opt(&ast->body, name, name, Qnil, INT2FIX(line),
 | |
|                                       NULL, 0, ISEQ_TYPE_TOP, &optimization));
 | |
|     rb_ast_dispose(ast);
 | |
| }
 | |
| COMPILER_WARNING_POP
 | |
| 
 | |
| % 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);
 | |
|     int start_line;
 | |
| 
 | |
|     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, start_line|
 | |
| %     if sub == true
 | |
|       case <%=i%><%=%>:
 | |
|         code = PRELUDE_CODE(<%=i%><%=%>);
 | |
|         name = PRELUDE_NAME(<%=i%><%=%>);
 | |
|         start_line = <%=start_line%>;
 | |
|         break;
 | |
| %     end
 | |
| %   end
 | |
|       default:
 | |
|         return Qfalse;
 | |
|     }
 | |
|     prelude_eval(code, name, start_line);
 | |
|     return Qtrue;
 | |
| }
 | |
| 
 | |
| % end
 | |
| %end
 | |
| % init_name = @output && @output[/\w+(?=_prelude.c\b)/] || 'prelude'
 | |
| void
 | |
| Init_<%=init_name%><%=%>(void)
 | |
| {
 | |
| %unless @prelude_count.zero?
 | |
| % if @have_sublib
 | |
|     struct prelude_env memo;
 | |
|     ID name = rb_intern("TMP_RUBY_PREFIX");
 | |
|     VALUE prelude = Data_Wrap_Struct(rb_cObject, 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, start_line|
 | |
| %   next if sub
 | |
|     prelude_eval(PRELUDE_CODE(<%=i%><%=%>), PRELUDE_NAME(<%=i%><%=%>), <%=start_line%>);
 | |
| % end
 | |
| 
 | |
| #if 0
 | |
| % preludes.length.times {|i|
 | |
|     printf("%.*s", (int)sizeof(prelude_code<%=i%><%=%>), prelude_code<%=i%><%=%>.L0);
 | |
| % }
 | |
| #endif
 | |
| %end
 | |
| }
 | |
| <%end -%>
 | 
