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.
- data/Changelog +27 -0
- data/README +79 -0
- data/Rakefile +24 -0
- data/TODO +14 -0
- data/VERSION +1 -0
- data/benchmarks/bench.rb +129 -0
- data/benchmarks/concretize_test.rb +26 -0
- data/benchmarks/extconf.rb +10 -0
- data/benchmarks/tak_rb.rb +7 -0
- data/benchmarks/tak_so.rb +7 -0
- data/benchmarks/tak_source.rb +16 -0
- data/bin/rb2cx +162 -0
- data/doc/eval2c.txt +246 -0
- data/doc/gen_html.rb +26 -0
- data/doc/html_template +10 -0
- data/doc/index.txt +169 -0
- data/doc/limitations.txt +529 -0
- data/doc/optimizations.txt +185 -0
- data/doc/rb2cx.txt +130 -0
- data/doc/style.css +27 -0
- data/lib/concretizer.rb +3 -0
- data/lib/ruby2cext/c_function.rb +617 -0
- data/lib/ruby2cext/common_node_comp.rb +1412 -0
- data/lib/ruby2cext/compiler.rb +311 -0
- data/lib/ruby2cext/concretize.rb +269 -0
- data/lib/ruby2cext/error.rb +15 -0
- data/lib/ruby2cext/eval2c.rb +126 -0
- data/lib/ruby2cext/parser.rb +36 -0
- data/lib/ruby2cext/plugin.rb +24 -0
- data/lib/ruby2cext/plugins/builtin_methods.rb +817 -0
- data/lib/ruby2cext/plugins/cache_call.rb +293 -0
- data/lib/ruby2cext/plugins/case_optimize.rb +102 -0
- data/lib/ruby2cext/plugins/const_cache.rb +36 -0
- data/lib/ruby2cext/plugins/direct_self_call.rb +70 -0
- data/lib/ruby2cext/plugins/inline_builtin.rb +797 -0
- data/lib/ruby2cext/plugins/inline_methods.rb +68 -0
- data/lib/ruby2cext/plugins/ivar_cache.rb +147 -0
- data/lib/ruby2cext/plugins/require_include.rb +69 -0
- data/lib/ruby2cext/plugins/util.rb +154 -0
- data/lib/ruby2cext/plugins/warnings.rb +121 -0
- data/lib/ruby2cext/scopes.rb +225 -0
- data/lib/ruby2cext/str_to_c_strlit.rb +12 -0
- data/lib/ruby2cext/tools.rb +80 -0
- data/lib/ruby2cext/version.rb +22 -0
- data/results +68 -0
- data/setup.rb +1585 -0
- data/stuff/builtin_methods.rb +69 -0
- data/stuff/builtin_methods_test.rb +37 -0
- data/test/bootstrap.rb +10 -0
- data/test/causes_crash_all_opts.rb +1165 -0
- data/test/eval2c/test_eval2c.rb +37 -0
- data/test/temp_17.rb +16 -0
- data/test/temp_18.rb +8 -0
- data/test/temp_19.rb +8 -0
- data/test/temp_2.rb +7 -0
- data/test/temp_20.rb +5 -0
- data/test/temp_21.rb +161 -0
- data/test/temp_22.rb +7 -0
- data/test/temp_23.rb +7 -0
- data/test/temp_24.rb +219 -0
- data/test/temp_25.rb +7 -0
- data/test/temp_26.rb +11 -0
- data/test/temp_27.rb +11 -0
- data/test/temp_28.rb +9 -0
- data/test/temp_29.rb +9 -0
- data/test/temp_3.rb +0 -0
- data/test/temp_30.rb +0 -0
- data/test/temp_31.rb +10 -0
- data/test/temp_32.rb +10 -0
- data/test/temp_33.rb +15 -0
- data/test/temp_34.rb +15 -0
- data/test/temp_35.rb +7 -0
- data/test/temp_36.rb +7 -0
- data/test/temp_37.rb +10 -0
- data/test/temp_38.rb +10 -0
- data/test/temp_39.rb +0 -0
- data/test/temp_4.rb +7 -0
- data/test/temp_40.rb +50 -0
- data/test/temp_41.rb +50 -0
- data/test/temp_42.rb +8 -0
- data/test/temp_43.rb +8 -0
- data/test/temp_44.rb +0 -0
- data/test/temp_48.rb +7 -0
- data/test/temp_49.rb +7 -0
- data/test/temp_5.rb +7 -0
- data/test/temp_59.rb +7 -0
- data/test/temp_6.rb +7 -0
- data/test/temp_60.rb +7 -0
- data/test/temp_68.rb +239 -0
- data/test/temp_7.rb +7 -0
- data/test/temp_70.rb +7 -0
- data/test/temp_71.rb +7 -0
- data/test/temp_72.rb +13 -0
- data/test/temp_73.rb +7 -0
- data/test/temp_74.rb +7 -0
- data/test/temp_76.rb +7 -0
- data/test/temp_77.rb +13 -0
- data/test/temp_79.rb +7 -0
- data/test/temp_8.rb +14 -0
- data/test/temp_81.rb +14 -0
- data/test/temp_83.rb +0 -0
- data/test/temp_84.rb +7 -0
- data/test/temp_85.rb +7 -0
- data/test/temp_86.rb +14 -0
- data/test/temp_87.rb +7 -0
- data/test/temp_88.rb +7 -0
- data/test/temp_89.rb +7 -0
- data/test/temp_9.rb +14 -0
- data/test/temp_90.rb +0 -0
- data/test/temp_91.rb +7 -0
- data/test/temp_92.rb +7 -0
- data/test/temp_93.rb +7 -0
- data/test/temp_94.rb +7 -0
- data/test/temp_95.rb +7 -0
- data/test/temp_96.rb +0 -0
- data/test/temp_97.rb +0 -0
- data/test/temp_98.rb +7 -0
- data/test/temp_99.rb +7 -0
- data/test/test_concretize.rb +132 -0
- data/test/test_concretize_all.rb +15 -0
- data/test/test_crystalize_block.rb +73 -0
- data/test/test_files/test.rb +615 -0
- data/test/test_files/vmode_test.rb +73 -0
- data/test/test_files/warn_test.rb +35 -0
- data/test/test_syntax.rb +25 -0
- 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
|
+
|