[macruby-changes] [3125] MacRuby/trunk

source_changes at macosforge.org source_changes at macosforge.org
Wed Dec 16 14:40:50 PST 2009


Revision: 3125
          http://trac.macosforge.org/projects/ruby/changeset/3125
Author:   lsansonetti at apple.com
Date:     2009-12-16 14:40:49 -0800 (Wed, 16 Dec 2009)
Log Message:
-----------
added support for AOT compiled source code objects in #require

Modified Paths:
--------------
    MacRuby/trunk/bin/rubyc
    MacRuby/trunk/load.c
    MacRuby/trunk/vm.cpp
    MacRuby/trunk/vm.h

Modified: MacRuby/trunk/bin/rubyc
===================================================================
--- MacRuby/trunk/bin/rubyc	2009-12-15 06:27:31 UTC (rev 3124)
+++ MacRuby/trunk/bin/rubyc	2009-12-16 22:40:49 UTC (rev 3125)
@@ -15,7 +15,6 @@
   def initialize(argv)
     @mode = :normal
     @archs = []
-    @frameworks = []
     @internal = argv.delete('--internal')
 
     # Parse arguments.
@@ -24,8 +23,8 @@
       opts.on('-c', 'Compile and assemble, but do not link') { @dont_link = true }
       opts.on('-o <file>', 'Place the output into <file>') { |output| @output = output }
       #opts.on('--mode [MODE]', "Select compilation mode (normal or full)") { |mode| @mode = mode.intern }
-      #opts.on('--framework <framework>', 'Link against <framework>') { |path| @frameworks << path }
-      opts.on('--static', "Create a standalone static executable") { @static = true } 
+      opts.on('--static', "Create a standalone static executable") { @static = true }
+      opts.on('--dylib', "Create a dynamic library") { @dylib = true }
       opts.on('-C', 'Compile, assemble and link a loadable object file') { @bundle = true }
       opts.on('-a', '--arch <ARCH>', 'Compile for specified CPU architecture') { |arch| @archs << arch }
       opts.on('-v', '--version', 'Display the version') { puts RUBY_DESCRIPTION; exit 1 }
@@ -74,6 +73,7 @@
     end
     if @dont_link or @bundle
       die "Cannot specify --static when not building an executable" if @static
+      die "Cannot specify -c or -C when building a dynamic library" if @dylib
       die "Cannot specify -c and -C at the same time" if @bundle and @dont_link
       if @files.size > 1 and @output
         die "Cannot specify -o with -c or -C and multiple input files"
@@ -88,18 +88,26 @@
         compile_object(file, @output)
       end
     else
+      die "Cannot specify --static and --dylib at the same time" if @dylib and @static
       objs = @files.map do |file|
+        die "Given input file `#{file} must exist" unless File.exist?(file)
         case File.extname(file)
           when '.rb'
             compile_object(file, nil)
           when '.o'
-            die "Given input file `#{file} must exist" unless File.exist?(file)
-            file
+            [file, find_init_func(file)]
+          when '.dylib'
+            [file, nil]
           else
-            die "Given input file `#{file}' must be either a Ruby source file (.rb) or a Mach-O object file (.o)"
+            die "Given input file `#{file}' must be either a Ruby source file (.rb) or a Mach-O object file (.o) or dynamic library (.dylib)"
         end
       end
-      compile_executable(objs, @output)
+      if @dylib
+	die "-o must be specified when building a dynamic library" unless @output
+        compile_dylib(objs, @output)
+      else
+        compile_executable(objs, @output)
+      end
     end
   end
 
@@ -149,6 +157,7 @@
 
     output ||= File.join(File.dirname(file), base + '.rbo')
 
+    # Generate main file.
     main_txt = <<EOS
 extern "C" {
   void *#{init_func}(void *, void *);
@@ -159,18 +168,43 @@
 }
 EOS
 
+    # Build.
     main = gen_tmpfile('main', 'c')
     File.open(main, 'w') { |io| io.write(main_txt) }
     linkf = @internal ? "-L. -lmacruby" : "-framework MacRuby"
-    archf = @archs.map { |x| "-arch #{x}" }.join(' ')
-    execute("#{@gcxx} \"#{main}\" -dynamic -bundle -undefined suppress -flat_namespace #{archf} #{linkf} \"#{obj}\" -o \"#{output}\"")
+    execute("#{@gcxx} \"#{main}\" -dynamic -bundle -undefined suppress -flat_namespace #{arch_flags} #{linkf} \"#{obj}\" -o \"#{output}\"")
   end
 
+  def compile_dylib(objs_data, output)
+    # Generate main file.
+    main_txt = <<EOS
+extern "C" {
+  void rb_vm_aot_feature_provide(const char *, void *);
+EOS
+    objs_data.each do |obj, init_func|
+      next if init_func == nil
+      main_txt << "void *#{init_func}(void *, void *);\n"
+    end
+    main_txt << <<EOS
+__attribute__((constructor)) static void __init__(void) {
+EOS
+    objs_data.each do |obj, init_func|
+      main_txt << "rb_vm_aot_feature_provide(\"#{feature_name(obj)}\", (void *)#{init_func});\n"
+    end
+    main_txt << "}}"
+
+    # Build.
+    main = gen_tmpfile('main', 'c')
+    File.open(main, 'w') { |io| io.write(main_txt) }
+    linkf = @internal ? "-L. -lmacruby" : "-framework MacRuby"
+    objs = objs_data.map { |obj, f| "\"#{obj}\"" }.join(' ') 
+    execute("#{@gcxx} \"#{main}\" -dynamiclib -dynamic -undefined suppress -flat_namespace #{arch_flags} #{linkf} #{objs} -o \"#{output}\"")
+  end
+
   def compile_executable(objs_data, output)
     output ||= 'a.out'
-    objs = []
-    init_funcs = []
-    objs_data.each { |obj, init_func| objs << obj; init_funcs << init_func }
+    raise if objs_data.empty?
+    die "first object file must be a Ruby source file or object" if objs_data[0][1] == nil
 
     # Generate main file.
     main_txt = <<EOS
@@ -181,11 +215,15 @@
     void ruby_script(const char *);
     void ruby_set_argv(int, char **);
     void rb_vm_init_compiler(void);
+    void rb_vm_aot_feature_provide(const char *, void *);
     void *rb_vm_top_self(void);
     void rb_vm_print_current_exception(void);
     void rb_exit(int);
 EOS
-    init_funcs.each { |x| main_txt << "void *#{x}(void *, void *);\n" }
+    objs_data.each do |obj, init_func|
+      next if init_func == nil
+      main_txt << "void *#{init_func}(void *, void *);\n"
+    end
     main_txt << <<EOS
 }
 
@@ -203,10 +241,14 @@
     rb_vm_init_compiler();
     ruby_script(progname);
     try {
-        void *self = rb_vm_top_self();
 EOS
-    init_funcs.each { |x| main_txt << "#{x}(self, 0);\n" }
+    objs_data[1..-1].each do |obj, init_func|
+      next if init_func == nil
+      main_txt << "rb_vm_aot_feature_provide(\"#{feature_name(obj)}\", (void *)#{init_func});\n"
+    end
     main_txt << <<EOS
+        void *self = rb_vm_top_self();
+        #{objs_data[0][1]}(self, 0);
     }
     catch (...) {
 	rb_vm_print_current_exception();
@@ -216,20 +258,22 @@
 }
 EOS
 
+    # Prepare objects.
+    objs = []
+    objs_data.each { |o, _| objs << o }
+
     # Compile main file.
     main = gen_tmpfile('main', 'mm')
     File.open(main, 'w') { |io| io.write(main_txt) }
     main_o = gen_tmpfile('main', 'o')
-    archf = @archs.map { |x| "-arch #{x}" }.join(' ')
-    execute("#{@gcxx} \"#{main}\" -c #{archf} -o \"#{main_o}\" -fobjc-gc")
+    execute("#{@gcxx} \"#{main}\" -c #{arch_flags} -o \"#{main_o}\" -fobjc-gc")
     objs.unshift(main_o)
 
     # Link all objects into executable.
     linkf = @static ? 
       "-L#{RbConfig::CONFIG['libdir']} #{RbConfig::CONFIG['LIBRUBYARG_STATIC_REALLY']}" :
       "-framework MacRuby -lobjc"
-    line = "#{@gcxx} -o \"#{output}\" #{archf} #{linkf} "
-    @frameworks.each { |f| line << "-framework #{f} " }
+    line = "#{@gcxx} -o \"#{output}\" #{arch_flags} #{linkf} "
     objs.each { |o| line << " \"#{o}\"" }
     execute(line)
   end
@@ -266,6 +310,25 @@
     end
   end
 
+  def arch_flags
+    @archs.map { |x| "-arch #{x}" }.join(' ')
+  end
+
+  def find_init_func(obj)
+    output = `#{@nm} -j "#{obj}"`
+    output.scan(/^_MREP_.*$/).reject { |func|
+      # Ignore non-main functions.
+      func.include?('ruby_scope')
+    }.map { |func|
+      # Ignore the very first character (_).
+      func[1..-1]
+    }[0]
+  end
+
+  def feature_name(obj)
+    obj.sub(/#{File.extname(obj)}$/, '')
+  end
+
   def gen_tmpfile(base, ext)
     file = File.join(@tmpdir, "#{base}.#{ext}")
     @tmpfiles << file

Modified: MacRuby/trunk/load.c
===================================================================
--- MacRuby/trunk/load.c	2009-12-15 06:27:31 UTC (rev 3124)
+++ MacRuby/trunk/load.c	2009-12-16 22:40:49 UTC (rev 3125)
@@ -265,6 +265,12 @@
     VALUE path;
     int type = 0;
 
+    // Immediately, check out if we have an AOT feature for this.
+    if (rb_vm_aot_feature_load(RSTRING_PTR(fname))) {
+	rb_provide_feature(fname);
+	return Qtrue;
+    }
+
     FilePathValue(fname);
     if (search_required(fname, &path, &type)) {
 	if (path == 0) {

Modified: MacRuby/trunk/vm.cpp
===================================================================
--- MacRuby/trunk/vm.cpp	2009-12-15 06:27:31 UTC (rev 3124)
+++ MacRuby/trunk/vm.cpp	2009-12-16 22:40:49 UTC (rev 3125)
@@ -4807,8 +4807,44 @@
     }
 }
 
+// AOT features. These are registered at runtime once an AOT object file
+// is loaded, either directly from an executable's main() function or from
+// a gcc constructor (in case of a dylib).
+//
+// XXX this shared map is not part of RoxorCore because gcc constructors can
+// potentially be called *before* RoxorCore has been initialized. This is
+// definitely not thread-safe, but it shouldn't be a big deal at this point.
+static std::map<std::string, void *> aot_features;
+
 extern "C"
+bool
+rb_vm_aot_feature_load(const char *name)
+{
+    std::string key(name);
+    std::map<std::string, void *>::iterator iter = aot_features.find(name);
+    if (iter == aot_features.end()) {
+	return false;
+    }
+    void *init_func = iter->second;
+    ((void *(*)(void *, void *))init_func)((void *)rb_vm_top_self(), NULL);
+    aot_features.erase(iter);
+    return true;
+}
+
+extern "C"
 void
+rb_vm_aot_feature_provide(const char *name, void *init_func)
+{
+    std::string key(name);
+    std::map<std::string, void *>::iterator iter = aot_features.find(key);
+    if (iter != aot_features.end()) {
+	printf("WARNING: AOT feature '%s' already registered, new one will be ignored. This could happen if you link your executable against dylibs that contain the same Ruby file.\n", name);
+    }
+    aot_features[key] = init_func;
+}
+
+extern "C"
+void
 Init_PostVM(void)
 {
     // Create and register the main thread.

Modified: MacRuby/trunk/vm.h
===================================================================
--- MacRuby/trunk/vm.h	2009-12-15 06:27:31 UTC (rev 3124)
+++ MacRuby/trunk/vm.h	2009-12-16 22:40:49 UTC (rev 3125)
@@ -413,6 +413,8 @@
 bool rb_vm_is_multithreaded(void);
 void rb_vm_set_multithreaded(bool flag);
 
+bool rb_vm_aot_feature_load(const char *name);
+
 static inline VALUE
 rb_robject_allocate_instance(VALUE klass)
 {
@@ -584,11 +586,13 @@
 	// be empty when we exit and we need to call the remaining finalizers.
 	std::vector<rb_vm_finalizer_t *> finalizers;
 
+	// The global lock.
+	pthread_mutex_t gl;
+
 	// State.
 	bool running;
 	bool multithreaded;
 	bool abort_on_exception;
-	pthread_mutex_t gl;
 	VALUE loaded_features;
 	VALUE load_path;
 	VALUE rand_seed;
@@ -803,7 +807,8 @@
 	void invalidate_respond_to_cache(void) {
 	    respond_to_cache.clear();
 	}
-	bool respond_to(VALUE obj, VALUE klass, SEL sel, bool priv, bool check_override);
+	bool respond_to(VALUE obj, VALUE klass, SEL sel, bool priv,
+		bool check_override);
 
     private:
 	bool register_bs_boxed(bs_element_type_t type, void *value);
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20091216/148d291d/attachment-0001.html>


More information about the macruby-changes mailing list