crystalizer 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+