crystalizer 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. data/Changelog +27 -0
  2. data/README +79 -0
  3. data/Rakefile +24 -0
  4. data/TODO +14 -0
  5. data/VERSION +1 -0
  6. data/benchmarks/bench.rb +129 -0
  7. data/benchmarks/concretize_test.rb +26 -0
  8. data/benchmarks/extconf.rb +10 -0
  9. data/benchmarks/tak_rb.rb +7 -0
  10. data/benchmarks/tak_so.rb +7 -0
  11. data/benchmarks/tak_source.rb +16 -0
  12. data/bin/rb2cx +162 -0
  13. data/doc/eval2c.txt +246 -0
  14. data/doc/gen_html.rb +26 -0
  15. data/doc/html_template +10 -0
  16. data/doc/index.txt +169 -0
  17. data/doc/limitations.txt +529 -0
  18. data/doc/optimizations.txt +185 -0
  19. data/doc/rb2cx.txt +130 -0
  20. data/doc/style.css +27 -0
  21. data/lib/concretizer.rb +3 -0
  22. data/lib/ruby2cext/c_function.rb +617 -0
  23. data/lib/ruby2cext/common_node_comp.rb +1412 -0
  24. data/lib/ruby2cext/compiler.rb +311 -0
  25. data/lib/ruby2cext/concretize.rb +269 -0
  26. data/lib/ruby2cext/error.rb +15 -0
  27. data/lib/ruby2cext/eval2c.rb +126 -0
  28. data/lib/ruby2cext/parser.rb +36 -0
  29. data/lib/ruby2cext/plugin.rb +24 -0
  30. data/lib/ruby2cext/plugins/builtin_methods.rb +817 -0
  31. data/lib/ruby2cext/plugins/cache_call.rb +293 -0
  32. data/lib/ruby2cext/plugins/case_optimize.rb +102 -0
  33. data/lib/ruby2cext/plugins/const_cache.rb +36 -0
  34. data/lib/ruby2cext/plugins/direct_self_call.rb +70 -0
  35. data/lib/ruby2cext/plugins/inline_builtin.rb +797 -0
  36. data/lib/ruby2cext/plugins/inline_methods.rb +68 -0
  37. data/lib/ruby2cext/plugins/ivar_cache.rb +147 -0
  38. data/lib/ruby2cext/plugins/require_include.rb +69 -0
  39. data/lib/ruby2cext/plugins/util.rb +154 -0
  40. data/lib/ruby2cext/plugins/warnings.rb +121 -0
  41. data/lib/ruby2cext/scopes.rb +225 -0
  42. data/lib/ruby2cext/str_to_c_strlit.rb +12 -0
  43. data/lib/ruby2cext/tools.rb +80 -0
  44. data/lib/ruby2cext/version.rb +22 -0
  45. data/results +68 -0
  46. data/setup.rb +1585 -0
  47. data/stuff/builtin_methods.rb +69 -0
  48. data/stuff/builtin_methods_test.rb +37 -0
  49. data/test/bootstrap.rb +10 -0
  50. data/test/causes_crash_all_opts.rb +1165 -0
  51. data/test/eval2c/test_eval2c.rb +37 -0
  52. data/test/temp_17.rb +16 -0
  53. data/test/temp_18.rb +8 -0
  54. data/test/temp_19.rb +8 -0
  55. data/test/temp_2.rb +7 -0
  56. data/test/temp_20.rb +5 -0
  57. data/test/temp_21.rb +161 -0
  58. data/test/temp_22.rb +7 -0
  59. data/test/temp_23.rb +7 -0
  60. data/test/temp_24.rb +219 -0
  61. data/test/temp_25.rb +7 -0
  62. data/test/temp_26.rb +11 -0
  63. data/test/temp_27.rb +11 -0
  64. data/test/temp_28.rb +9 -0
  65. data/test/temp_29.rb +9 -0
  66. data/test/temp_3.rb +0 -0
  67. data/test/temp_30.rb +0 -0
  68. data/test/temp_31.rb +10 -0
  69. data/test/temp_32.rb +10 -0
  70. data/test/temp_33.rb +15 -0
  71. data/test/temp_34.rb +15 -0
  72. data/test/temp_35.rb +7 -0
  73. data/test/temp_36.rb +7 -0
  74. data/test/temp_37.rb +10 -0
  75. data/test/temp_38.rb +10 -0
  76. data/test/temp_39.rb +0 -0
  77. data/test/temp_4.rb +7 -0
  78. data/test/temp_40.rb +50 -0
  79. data/test/temp_41.rb +50 -0
  80. data/test/temp_42.rb +8 -0
  81. data/test/temp_43.rb +8 -0
  82. data/test/temp_44.rb +0 -0
  83. data/test/temp_48.rb +7 -0
  84. data/test/temp_49.rb +7 -0
  85. data/test/temp_5.rb +7 -0
  86. data/test/temp_59.rb +7 -0
  87. data/test/temp_6.rb +7 -0
  88. data/test/temp_60.rb +7 -0
  89. data/test/temp_68.rb +239 -0
  90. data/test/temp_7.rb +7 -0
  91. data/test/temp_70.rb +7 -0
  92. data/test/temp_71.rb +7 -0
  93. data/test/temp_72.rb +13 -0
  94. data/test/temp_73.rb +7 -0
  95. data/test/temp_74.rb +7 -0
  96. data/test/temp_76.rb +7 -0
  97. data/test/temp_77.rb +13 -0
  98. data/test/temp_79.rb +7 -0
  99. data/test/temp_8.rb +14 -0
  100. data/test/temp_81.rb +14 -0
  101. data/test/temp_83.rb +0 -0
  102. data/test/temp_84.rb +7 -0
  103. data/test/temp_85.rb +7 -0
  104. data/test/temp_86.rb +14 -0
  105. data/test/temp_87.rb +7 -0
  106. data/test/temp_88.rb +7 -0
  107. data/test/temp_89.rb +7 -0
  108. data/test/temp_9.rb +14 -0
  109. data/test/temp_90.rb +0 -0
  110. data/test/temp_91.rb +7 -0
  111. data/test/temp_92.rb +7 -0
  112. data/test/temp_93.rb +7 -0
  113. data/test/temp_94.rb +7 -0
  114. data/test/temp_95.rb +7 -0
  115. data/test/temp_96.rb +0 -0
  116. data/test/temp_97.rb +0 -0
  117. data/test/temp_98.rb +7 -0
  118. data/test/temp_99.rb +7 -0
  119. data/test/test_concretize.rb +132 -0
  120. data/test/test_concretize_all.rb +15 -0
  121. data/test/test_crystalize_block.rb +73 -0
  122. data/test/test_files/test.rb +615 -0
  123. data/test/test_files/vmode_test.rb +73 -0
  124. data/test/test_files/warn_test.rb +35 -0
  125. data/test/test_syntax.rb +25 -0
  126. metadata +268 -0
@@ -0,0 +1,311 @@
1
+
2
+ require "rubynode"
3
+ require "rbconfig"
4
+ require 'sane'
5
+ require 'logger'
6
+ require_rel '.'
7
+ require "rubynode"
8
+ require "rbconfig"
9
+ require "ruby2cext/parser"
10
+ require "ruby2cext/error"
11
+ require "ruby2cext/tools"
12
+ require "ruby2cext/c_function"
13
+ require "ruby2cext/version"
14
+ require_rel 'plugins'
15
+ require 'sane'
16
+
17
+ module Ruby2CExtension
18
+
19
+ class Compiler
20
+
21
+ attr_reader :name, :logger, :plugins
22
+
23
+ def initialize(name, logger = nil)
24
+ @name = name
25
+ @logger = logger
26
+ @funs = []
27
+ @funs_reuseable = {}
28
+ @toplevel_funs = []
29
+ @sym_man = Tools::SymbolManager.new
30
+ @global_man = Tools::GlobalManager.new
31
+ @uniq_names = Tools::UniqueNames.new
32
+ @helpers = {}
33
+ @plugins = []
34
+ @preprocessors = {}
35
+ end
36
+
37
+ # plugins is {:optimizations => :all || [:name1, :name2]}, also {:warnings => true} or something
38
+ # logger if require 'logger'; Logger.new
39
+ # include_paths = [] # dirs
40
+ # only_c -- pass true if you just want the source, not compiled
41
+ def Compiler.compile_file(file_name, plugins, include_paths, only_c, logger)
42
+ bn = File.basename(file_name)
43
+ unless bn =~ /\A(.*)\.rb\w?\z/
44
+ raise "#{file_name} is no ruby file"
45
+ end
46
+ name = $1;
47
+ unless name =~ /\A\w+\z/
48
+ raise "'#{name}' is not a valid extension name"
49
+ end
50
+ file_name = File.join(File.dirname(file_name), bn)
51
+
52
+ logger.info("reading #{file_name}")
53
+ source_str = IO.read(file_name)
54
+
55
+ logger.info("translating #{file_name} to C")
56
+ c = Compiler.new(name, logger)
57
+ unless include_paths.empty?
58
+ plugins = plugins.merge({:require_include => [include_paths, [file_name]]})
59
+ end
60
+ logger.debug("plugins = #{plugins.inspect}")
61
+ c.add_plugins(plugins)
62
+ logger.debug("plugins used: #{c.plugins.map { |pi| pi.class }.inspect}")
63
+ c.add_rb_file(source_str, file_name)
64
+ c_code = c.to_c_code
65
+
66
+ c_file_name = File.join(File.dirname(file_name), "#{name}.c")
67
+ logger.info("writing #{c_file_name}")
68
+ File.open(c_file_name, "w") { |f| f.puts(c_code) }
69
+
70
+ if only_c
71
+ c_code
72
+ else
73
+ logger.info("compiling #{c_file_name}")
74
+ Compiler.compile_c_file_to_dllib(c_file_name, logger)
75
+ end
76
+ end
77
+
78
+
79
+
80
+
81
+ def to_c_code(time_stamp = Time.now)
82
+ plugins_global = @plugins.map { |plugin| plugin.global_c_code }
83
+ plugins_init = @plugins.map { |plugin| plugin.init_c_code }
84
+ res = [
85
+ "/* generated by #{FULL_VERSION_STRING} on #{time_stamp} */",
86
+ "#include <ruby.h>",
87
+ "#include <node.h>",
88
+ "#include <env.h>",
89
+ "#include <st.h>",
90
+ "extern VALUE ruby_top_self;",
91
+ "static VALUE org_ruby_top_self;",
92
+ @sym_man.to_c_code,
93
+ @global_man.to_c_code,
94
+ ]
95
+ res.concat(@helpers.keys.sort)
96
+ res.concat(plugins_global)
97
+ res.concat(@funs)
98
+ res << "void Init_#{@name}() {"
99
+ res << "org_ruby_top_self = ruby_top_self;"
100
+ # just to be sure
101
+ res << "rb_global_variable(&org_ruby_top_self);"
102
+ res << "init_syms();"
103
+ res << "init_globals();"
104
+ res << "NODE *cref = rb_node_newnode(NODE_CREF, rb_cObject, 0, 0);"
105
+ res.concat(plugins_init)
106
+ @toplevel_funs.each { |f| res << "#{f}(ruby_top_self, cref);" }
107
+ res << "}"
108
+ res.join("\n").split("\n").map { |l| l.strip }.reject { |l| l.empty? }.join("\n")
109
+ end
110
+
111
+ def add_toplevel(function_name)
112
+ @toplevel_funs << function_name
113
+ end
114
+
115
+ # non destructive: node_tree will not be changed
116
+ def compile_toplevel_function(node_tree, private_vmode = true)
117
+ CFunction::ToplevelScope.compile(self, node_tree, private_vmode)
118
+ end
119
+
120
+ NODE_TRANSFORM_OPTIONS = {:include_node => true, :keep_newline_nodes => true}
121
+
122
+ def rb_file_to_toplevel_functions(source_str, file_name)
123
+ res = []
124
+ hash = Parser.parse_string(source_str, file_name)
125
+ # add all BEGIN blocks, if available
126
+ if (beg_tree = hash[:begin])
127
+ beg_tree = beg_tree.transform(NODE_TRANSFORM_OPTIONS)
128
+ if beg_tree.first == :block
129
+ beg_tree.last.each { |s| res << compile_toplevel_function(s, false) }
130
+ else
131
+ res << compile_toplevel_function(beg_tree, false)
132
+ end
133
+ end
134
+ # add toplevel scope
135
+ if (tree = hash[:tree])
136
+ res << compile_toplevel_function(tree.transform(NODE_TRANSFORM_OPTIONS))
137
+ end
138
+ res
139
+ end
140
+
141
+ def add_rb_file(source_str, file_name)
142
+ rb_file_to_toplevel_functions(source_str, file_name).each { |fn|
143
+ add_toplevel(fn)
144
+ }
145
+ end
146
+
147
+ # uniq name
148
+ def un(str)
149
+ @uniq_names.get(str)
150
+ end
151
+ def sym(sym)
152
+ @sym_man.get(sym)
153
+ end
154
+ def global_const(str, register_gc = true)
155
+ @global_man.get(str, true, register_gc)
156
+ end
157
+ def global_var(str)
158
+ @global_man.get(str, false, true)
159
+ end
160
+
161
+ def log(str, warning = false)
162
+ if logger
163
+ if warning
164
+ logger.warn(str)
165
+ else
166
+ logger.info(str)
167
+ end
168
+ end
169
+ end
170
+
171
+ def add_helper(str)
172
+ @helpers[str] ||= true
173
+ end
174
+
175
+ def add_fun(code, base_name)
176
+ unless (name = @funs_reuseable[code])
177
+ name = un(base_name)
178
+ lines = code.split("\n")
179
+ unless lines.shift =~ /^\s*static / # first line needs static
180
+ raise Ruby2CExtError::Bug, "trying to add a non static function"
181
+ end
182
+ if lines.grep(/^\s*static /).empty? # only reuseably without static variables
183
+ @funs_reuseable[code] = name
184
+ end
185
+ unless code.sub!("FUNNAME", name)
186
+ raise Ruby2CExtError::Bug, "trying to add a function without FUNNAME"
187
+ end
188
+ @funs << code
189
+ end
190
+ name
191
+ end
192
+
193
+ def add_plugin(plugin_class, *args)
194
+ @plugins << plugin_class.new(self, *args)
195
+ end
196
+
197
+ def add_plugins(options)
198
+ if options[:warnings]
199
+ add_plugin(Plugins::Warnings)
200
+ end
201
+ if (opt = options[:optimizations])
202
+ if opt == :all
203
+ opt = {
204
+ :const_cache=>true,
205
+ :case_optimize=>true,
206
+ # :direct_self_call=>true, # also buggy...
207
+ # :inline_builtin=>true, # causes a bug [just itself, too]
208
+ :cache_call=>true,
209
+ :builtin_methods=>true,
210
+ :inline_methods=>true,
211
+ :ivar_cache=>true
212
+ }
213
+ end
214
+ if opt[:const_cache]
215
+ add_plugin(Plugins::ConstCache)
216
+ end
217
+ if opt[:case_optimize]
218
+ add_plugin(Plugins::CaseOptimize)
219
+ end
220
+ if opt[:direct_self_call]
221
+ add_plugin(Plugins::DirectSelfCall)
222
+ end
223
+ if opt[:inline_builtin]
224
+ add_plugin(Plugins::InlineBuiltin)
225
+ end
226
+ if opt[:inline_methods]
227
+ add_plugin(Plugins::InlineMethods)
228
+ end
229
+ if opt[:cache_call]
230
+ add_plugin(Plugins::CacheCall)
231
+ end
232
+ if (builtins = opt[:builtin_methods])
233
+ if Array === builtins
234
+ builtins = builtins.map { |b| b.to_s.to_sym } # allow symbols, strings and the actual classes to work
235
+ else
236
+ builtins = Plugins::BuiltinMethods::SUPPORTED_BUILTINS
237
+ end
238
+ add_plugin(Plugins::BuiltinMethods, builtins)
239
+ end
240
+ if opt[:ivar_cache]
241
+ add_plugin(Plugins::IVarCache)
242
+ end
243
+ end
244
+ if (ri_args = options[:require_include])
245
+ unless Array === ri_args.first
246
+ ri_args = [ri_args] # to allow just an array of include paths to also work
247
+ end
248
+ add_plugin(Plugins::RequireInclude, *ri_args)
249
+ end
250
+ end
251
+
252
+ # preprocessors can be added by plugins. preprocessors are procs that
253
+ # take two arguments: the current cfun and the node (tree) to
254
+ # preprocess (which will have type node_type)
255
+ #
256
+ # The proc can either return a (modified) node (tree) or string. If a
257
+ # node (tree) is returned then that will be translated as usual, if a
258
+ # string is returned, that string will be the result
259
+ #
260
+ # Example, a preprocessor that replaces 23 with 42:
261
+ # add_preprocessor(:lit) { |cfun, node|
262
+ # node.last[:lit] == 23 ? [:lit, {:lit=>42}] : node
263
+ # }
264
+ #
265
+ # Another way to do the same:
266
+ # add_preprocessor(:lit) { |cfun, node|
267
+ # node.last[:lit] == 23 ? cfun.comp_lit(:lit=>42) : node
268
+ # }
269
+ #
270
+ # If multiple preprocessors are added for the same node type then they
271
+ # will be called after each other with the result of the previous one
272
+ # unless it is a string, then the following preprocessors are ignored
273
+ def add_preprocessor(node_type, &pp_proc)
274
+ (@preprocessors[node_type] ||= []) << pp_proc
275
+ end
276
+
277
+ def preprocessors_for(node_type)
278
+ @preprocessors[node_type]
279
+ end
280
+
281
+ conf = ::Config::CONFIG
282
+ cflags = [conf["CCDLFLAGS"], conf["CFLAGS"], conf["ARCH_FLAG"]].join(" ")
283
+ COMPILE_COMMAND = "#{conf["LDSHARED"]} #{cflags} -I . -I #{conf["archdir"]} " # added -c
284
+ DLEXT = conf["DLEXT"]
285
+
286
+ # compiles a C file using the compiler from rbconfig
287
+ def self.compile_c_file_to_dllib(c_file_name, logger = nil)
288
+ conf = ::Config::CONFIG
289
+ unless c_file_name =~ /\.c\z/
290
+ raise Ruby2CExtError, "#{c_file_name} is no C file"
291
+ end
292
+ dl_name = c_file_name.sub(/c\z/, DLEXT)
293
+ cmd = "#{COMPILE_COMMAND} -o #{dl_name} #{c_file_name}"
294
+ if RUBY_PLATFORM =~ /mswin32/
295
+ cmd << " -link /INCREMENTAL:no /EXPORT:Init_#{File.basename(c_file_name, ".c")}"
296
+ end
297
+ if RUBY_PLATFORM =~ /mingw/
298
+ cmd << " #{ conf['DLDFLAGS'] } #{ conf['SOLIBS'] } "
299
+ cmd << " -L#{ conf['libdir'] } #{ conf["LIBRUBYARG_SHARED"] } "
300
+ end
301
+ logger.info(cmd) if logger
302
+
303
+ unless system(cmd) # run it
304
+ raise Ruby2CExtError, "error while executing '#{cmd}' #{`cmd`}"
305
+ end
306
+ dl_name
307
+ end
308
+
309
+ end
310
+
311
+ end
@@ -0,0 +1,269 @@
1
+ require 'rubygems'
2
+ require 'ruby2ruby'
3
+ require 'parse_tree'
4
+ require 'thread' # mutex
5
+ require 'sane'
6
+ # LTODO singleton methods, class methods, [procs?]
7
+
8
+ module Ruby2CExtension
9
+
10
+ class Concretize
11
+
12
+ def Concretize.source_for klass, method_name
13
+ raw = @@pt.parse_tree_for_method(klass, method_name)
14
+ begin
15
+ processed = @@pt.process(raw)
16
+ rescue
17
+ return nil
18
+ end
19
+ code = @@r2r.process( processed )
20
+ if code.include?('&block') || code.include?('binding')
21
+ # eval?
22
+ # for now, try to avoid the tripsy case of ruby2cext not yielding arrays right yet...
23
+ # TODO add some test cases for it in 'broken.rb' or something
24
+ # LTODO fix it :)
25
+
26
+ # TODO test it with rdoc...yeah. A real test sniff
27
+ # compare with 1.9...
28
+ nil
29
+ else
30
+ code
31
+ end
32
+ end
33
+
34
+
35
+ begin
36
+ @@good_codes ||= YAML.load File.read('known_good_codes')
37
+ rescue Exception
38
+ @@good_codes = {}
39
+ end
40
+
41
+ at_exit {
42
+ puts 'saving'
43
+ File.write 'known_good_codes', YAML.dump(@@good_codes)
44
+ }
45
+ @@count = 0
46
+ @@mutex = Mutex.new
47
+ @@r2r = Ruby2Ruby.new
48
+ @@pt = ParseTree.new
49
+
50
+ # returns rb code, or c code, or true on successfull load
51
+ # returns nil if want_rb_afterward && had to compile
52
+ # which is ok
53
+ def Concretize.c_ify! klass, method_name, want_rb_afterward = false, want_just_c = false
54
+ count = @@mutex.synchronize { @@count += 1 }
55
+ rb = "temp_#{count}.rb"
56
+ ruby_code = source_for klass, method_name
57
+
58
+ # TODO it probably catches railsy class style poorly [re-use my other desc_method?]
59
+ return nil unless ruby_code
60
+
61
+
62
+ assert klass.class.in?( [Class, Module]) # sanity check
63
+ comment = "# concretize temp file: autogenerated: #{ Time.now }\n"
64
+ if klass.class == Module
65
+ klass_string = "module #{klass}" # modules don't descend
66
+ else
67
+ nearest_ancestor = klass.ancestors[1..-1].find{|ancestor| ancestor.class == Class}
68
+ if nearest_ancestor
69
+ klass_string = "class #{klass} < #{nearest_ancestor}"
70
+ else
71
+ # Object, I suppose
72
+ klass_string = "class #{klass}"
73
+ end
74
+ end
75
+
76
+ ruby_code_wrapped = comment + klass_string + "\npublic\n" + ruby_code + "\nend\n"
77
+ @@log.debug ruby_code_wrapped
78
+ if @@good_codes[ruby_code] && want_rb_afterward
79
+ @@log.debug 'cache hit'
80
+ @@cache_hits+= 1
81
+ return ruby_code_wrapped
82
+ else
83
+ @@log.debug 'cache miss' + @@good_codes.length.to_s
84
+ end
85
+
86
+ # sometimes ruby2ruby unpacks them wrong, so test for that
87
+ File.open(rb, 'w') do |file|
88
+ file.write ruby_code_wrapped
89
+ end
90
+ output = `ruby -c #{rb} 2>&1`
91
+ if($?.exitstatus != 0)
92
+ # unparsable ruby was generated...hmm...
93
+ @@log.warn "got bad code generation", klass, method_name, ruby_code
94
+ File.delete rb
95
+ return nil # LTODO re-parse it [other parser?] make sure reparsing matches...
96
+ else
97
+ # see if it passes, below, too
98
+ end
99
+
100
+
101
+ if want_just_c
102
+ return Concretize.compile_string(ruby_code_wrapped, want_just_c)
103
+ else
104
+ success = Concretize.compile_string(ruby_code_wrapped) rescue nil # can fail, like ...ensure; return nil
105
+ if success
106
+ @@good_codes[ruby_code] = true
107
+
108
+ if want_rb_afterward
109
+ return ruby_code_wrapped # no new [helpful] ruby code here, but pass it out anyway..
110
+ end
111
+ end
112
+ success
113
+ end
114
+ end
115
+
116
+ @@cache_hits = 0
117
+ def self.cache_hits
118
+ @@cache_hits #ltodo attr_reader?
119
+ end
120
+ def self.good_codes
121
+ @@good_codes
122
+ end
123
+
124
+ @@log = Logger.new (STDOUT)
125
+ private
126
+ # returns the c code, or the .so filename if it was compiled and run
127
+
128
+
129
+ def Concretize.compile_string(ruby_string, want_just_c = false)
130
+ file_name = "temp_#{@@count += 1}" # has to be .rb for some reason
131
+ File.write file_name + '.rb', ruby_string
132
+
133
+ Compiler.compile_file(file_name + '.rb', {:optimizations => :all}, [], false, @@log)
134
+ #LTODO delete all temp files -- except we can't delete .so files once loaded...
135
+ c_file = file_name + '.c'
136
+ so_file = file_name + '.so'
137
+
138
+ if(want_just_c)
139
+ return File.read(c_file)
140
+ end
141
+ # LTODO make it multi process friendly, too :)
142
+ require so_file # return just the name? huh?
143
+ end
144
+
145
+ public
146
+
147
+ @@already_cified = {}
148
+ # pass in a class name instnace
149
+ # like c_ify_class! ClassName
150
+ # currently only cifys the singleton methods...
151
+ # add_to_string is either nil or a string.
152
+ # returns whether that class and all ancestors had something ruby-y that it successfully converted to C
153
+ def Concretize.c_ify_class! klass, add_to_string = nil, skip_ancestors = false
154
+
155
+ local_add_to_string = ''
156
+
157
+ Ruby2CExtension::Plugins::DirectSelfCall.allow_public_methods # we're concretizing, so public methods are ok
158
+ # TODO test that this actually does something to the C code :)
159
+ success = false
160
+ # LTODO class methods, singleton methods...sure! :)
161
+ ancestors = [klass]
162
+
163
+ if(!skip_ancestors)
164
+ ancestors = klass.ancestors
165
+ end
166
+
167
+ for klass in ancestors # ltodo reverse...
168
+ if @@already_cified[klass]
169
+ # for now we keep all classes as real classes [not really concretize anything...we just c-ify everything aggressively]
170
+ # when that is fixed be careful to get the inheritance wrong if two ancestors define the same method
171
+ next
172
+ end
173
+ # TODO optionally take out all private checks :)
174
+ for method_name in klass.instance_methods(false)
175
+ string = Concretize.c_ify!(klass, method_name, true)
176
+ if(string)
177
+ local_add_to_string << " " << string
178
+ success = true
179
+ end
180
+
181
+ end
182
+
183
+ if add_to_string
184
+ add_to_string << local_add_to_string
185
+ else
186
+ Concretize.compile_string(local_add_to_string)
187
+ end
188
+
189
+ @@already_cified[klass] = true
190
+
191
+ print klass.to_s + " has "
192
+ if(!success)
193
+ print "no "
194
+ end
195
+ puts "ruby methods"
196
+ end
197
+
198
+ return success
199
+ end
200
+
201
+ # turn all classes' ruby methods into their C equivalents
202
+ # deemed unstable as of yet :(
203
+ def Concretize.concretize_all! classes_to_do = nil, all_together = true
204
+ @@already_cified = {} # in case things have changed...maybe?
205
+ if !classes_to_do
206
+ classes_to_do = []
207
+ ObjectSpace.each_object(Class){|c| classes_to_do << c}
208
+ end
209
+
210
+ all_successful = []
211
+ if all_together
212
+ all_ruby_codes = ''
213
+ else
214
+ all_ruby_codes = nil
215
+ end
216
+
217
+ classes_to_do.each{|klass|
218
+ all_successful << klass if Concretize.c_ify_class!(klass, all_ruby_codes)
219
+ }
220
+ # puts 'none found' if all_ruby_codes == '' this is expected currently anytime after the first run...
221
+ Concretize.compile_string(all_ruby_codes)
222
+ all_successful
223
+ end
224
+ end
225
+
226
+ require 'event_hook' # 1.8 only for now [sigh]
227
+ class Tracer < EventHook
228
+ class << self
229
+ def all_classes
230
+ @all_classes.keys
231
+ end
232
+ def start
233
+ @all_classes = {}
234
+ start_hook
235
+ end
236
+ def process(*args)
237
+ # like [16, Tracer, :get_line, Tracer]
238
+ # not sure what [1] is versus [3]
239
+ @all_classes[args[3]]= true
240
+ end
241
+ end
242
+ end
243
+
244
+ def Concretize.crystalize_after_first_time_through
245
+ raise unless block_given?
246
+ Tracer.start
247
+ yield
248
+ Tracer.stop_hook
249
+ raise unless Tracer.all_classes.length > 0
250
+ puts 'crystalizing', Tracer.all_classes.inspect
251
+ Concretize.concretize_all! Tracer.all_classes
252
+ end
253
+ end
254
+
255
+ class Class
256
+ # somewhat of a misnomer TODO rdoc
257
+ def concretize!
258
+ Ruby2CExtension::Concretize.c_ify_class! self
259
+ end
260
+ end
261
+
262
+ class Object
263
+ def concretize_ruby!
264
+ Ruby2CExtension::Concretize.concretize_all!
265
+ end
266
+ end
267
+
268
+
269
+