rubinius-compiler 1.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4f7a4f90b65396c5b6a411d0dba6d2c27fac75c0
4
+ data.tar.gz: 31e27d1a8b667740b398c6456b37dd1e8e3b694f
5
+ SHA512:
6
+ metadata.gz: e0278293e994de4801017219e759a19763e45454775231657a7df08ca28498fe56cece97f306f011138954041e590512fc8d85b18914eac9c79f8555748f7c9e
7
+ data.tar.gz: 0ed9518ee5010a0834630b6fb91b85b013515ce1eb2ded814962492aceb24533640aba5788f341b3f35b23eacaf538ab433d2c207dc6f17a55f84a4e6d93cbfc
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rubinius-compiler.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2013, Brian Shirai
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+ 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ 3. Neither the name of the library nor the names of its contributors may be
13
+ used to endorse or promote products derived from this software without
14
+ specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT,
20
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21
+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23
+ OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
25
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Rubinius::Compiler
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rubinius-compiler'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rubinius-compiler
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,11 @@
1
+ require "rubinius/compiler/compiler"
2
+ require "rubinius/compiler/stages"
3
+ require "rubinius/compiler/locals"
4
+ require "rubinius/compiler/generator_methods"
5
+ require "rubinius/compiler/generator"
6
+ require "rubinius/compiler/iseq"
7
+ require "rubinius/compiler/opcodes"
8
+ require "rubinius/compiler/compiled_file"
9
+ require "rubinius/compiler/evaluator"
10
+ require "rubinius/compiler/printers"
11
+ require "rubinius/compiler/runtime"
@@ -0,0 +1,338 @@
1
+ # -*- encoding: us-ascii -*-
2
+
3
+ module Rubinius::ToolSet.current::TS
4
+ ##
5
+ # A decode for the .rbc file format.
6
+
7
+ class CompiledFile
8
+ ##
9
+ # Create a CompiledFile with +magic+ bytes, +signature+, and +version+.
10
+ # The optional +stream+ is used to lazy load the body.
11
+
12
+ def initialize(magic, signature, version, stream=nil)
13
+ @magic = magic
14
+ @signature = signature
15
+ @version = version
16
+ @stream = stream
17
+ @data = nil
18
+ end
19
+
20
+ attr_reader :magic
21
+ attr_reader :signature
22
+ attr_reader :version
23
+ attr_reader :stream
24
+
25
+ ##
26
+ # From a stream-like object +stream+ load the data in and return a
27
+ # CompiledFile object.
28
+
29
+ def self.load(stream)
30
+ raise IOError, "attempted to load nil stream" unless stream
31
+
32
+ magic = stream.gets.strip
33
+ signature = Integer(stream.gets.strip)
34
+ version = Integer(stream.gets.strip)
35
+
36
+ return new(magic, signature, version, stream)
37
+ end
38
+
39
+ ##
40
+ # Writes the CompiledFile +code+ to +file+.
41
+ def self.dump(code, file, signature, version)
42
+ File.open(file, "wb") do |f|
43
+ new("!RBIX", signature, version).encode_to(f, code)
44
+ end
45
+ rescue SystemCallError
46
+ # just skip writing the compiled file if we don't have permissions
47
+ end
48
+
49
+ ##
50
+ # Encode the contets of this CompiledFile object to +stream+ with
51
+ # a body of +body+. Body use marshalled using CompiledFile::Marshal
52
+
53
+ def encode_to(stream, body)
54
+ stream.puts @magic
55
+ stream.puts @signature.to_s
56
+ stream.puts @version.to_s
57
+
58
+ mar = CompiledFile::Marshal.new
59
+ stream << mar.marshal(body)
60
+ end
61
+
62
+ ##
63
+ # Return the body object by unmarshaling the data
64
+
65
+ def body
66
+ return @data if @data
67
+
68
+ mar = CompiledFile::Marshal.new
69
+ @data = mar.unmarshal(stream)
70
+ end
71
+
72
+ ##
73
+ # A class used to convert an CompiledCode to and from
74
+ # a String.
75
+
76
+ class Marshal
77
+
78
+ ##
79
+ # Read all data from +stream+ and invoke unmarshal_data
80
+
81
+ def unmarshal(stream)
82
+ if stream.kind_of? String
83
+ str = stream
84
+ else
85
+ str = stream.read
86
+ end
87
+
88
+ @start = 0
89
+ @size = str.size
90
+ @data = str.data
91
+
92
+ unmarshal_data
93
+ end
94
+
95
+ ##
96
+ # Process a stream object +stream+ as as marshalled data and
97
+ # return an object representation of it.
98
+
99
+ def unmarshal_data
100
+ kind = next_type
101
+ case kind
102
+ when 116 # ?t
103
+ return true
104
+ when 102 # ?f
105
+ return false
106
+ when 110 # ?n
107
+ return nil
108
+ when 73 # ?I
109
+ return next_string.to_i(16)
110
+ when 100 # ?d
111
+ str = next_string.chop
112
+
113
+ # handle the special NaN, Infinity and -Infinity differently
114
+ if str[0] == ?\ # leading space
115
+ x = str.to_f
116
+ e = str[-5..-1].to_i
117
+
118
+ # This is necessary because (2**1024).to_f yields Infinity
119
+ if e == 1024
120
+ return x * 2 ** 512 * 2 ** 512
121
+ else
122
+ return x * 2 ** e
123
+ end
124
+ else
125
+ case str.downcase
126
+ when "infinity"
127
+ return 1.0 / 0.0
128
+ when "-infinity"
129
+ return -1.0 / 0.0
130
+ when "nan"
131
+ return 0.0 / 0.0
132
+ else
133
+ raise TypeError, "Invalid Float format: #{str}"
134
+ end
135
+ end
136
+ when 115 # ?s
137
+ enc = unmarshal_data
138
+ count = next_string.to_i
139
+ str = next_bytes count
140
+ str.force_encoding enc if enc and defined?(Encoding)
141
+ return str
142
+ when 120 # ?x
143
+ enc = unmarshal_data
144
+ count = next_string.to_i
145
+ str = next_bytes count
146
+ str.force_encoding enc if enc and defined?(Encoding)
147
+ return str.to_sym
148
+ when 99 # ?c
149
+ count = next_string.to_i
150
+ str = next_bytes count
151
+ return str.split("::").inject(Object) { |a,n| a.const_get(n) }
152
+ when 112 # ?p
153
+ count = next_string.to_i
154
+ obj = Rubinius::Tuple.new(count)
155
+ i = 0
156
+ while i < count
157
+ obj[i] = unmarshal_data
158
+ i += 1
159
+ end
160
+ return obj
161
+ when 105 # ?i
162
+ count = next_string.to_i
163
+ seq = Rubinius::InstructionSequence.new(count)
164
+ i = 0
165
+ while i < count
166
+ seq[i] = next_string.to_i
167
+ i += 1
168
+ end
169
+ return seq
170
+ when 69 # ?E
171
+ count = next_string.to_i
172
+ name = next_bytes count
173
+ return Encoding.find(name) if defined?(Encoding)
174
+ when 77 # ?M
175
+ version = next_string.to_i
176
+ if version != 1
177
+ raise "Unknown CompiledCode version #{version}"
178
+ end
179
+ code = Rubinius::CompiledCode.new
180
+ code.metadata = unmarshal_data
181
+ code.primitive = unmarshal_data
182
+ code.name = unmarshal_data
183
+ code.iseq = unmarshal_data
184
+ code.stack_size = unmarshal_data
185
+ code.local_count = unmarshal_data
186
+ code.required_args = unmarshal_data
187
+ code.post_args = unmarshal_data
188
+ code.total_args = unmarshal_data
189
+ code.splat = unmarshal_data
190
+ code.literals = unmarshal_data
191
+ code.lines = unmarshal_data
192
+ code.file = unmarshal_data
193
+ code.local_names = unmarshal_data
194
+ return code
195
+ else
196
+ raise "Unknown type '#{kind.chr}'"
197
+ end
198
+ end
199
+
200
+ private :unmarshal_data
201
+
202
+ ##
203
+ # Returns the next character in _@data_ as a Fixnum.
204
+ #--
205
+ # The current format uses a one-character type indicator
206
+ # followed by a newline. If that format changes, this
207
+ # will break and we'll fix it.
208
+ #++
209
+ def next_type
210
+ chr = @data[@start]
211
+ @start += 2
212
+ chr
213
+ end
214
+
215
+ private :next_type
216
+
217
+ ##
218
+ # Returns the next string in _@data_ including the trailing
219
+ # "\n" character.
220
+ def next_string
221
+ count = @data.locate "\n", @start, @size
222
+ count = @size unless count
223
+ str = String.from_bytearray @data, @start, count - @start
224
+ @start = count
225
+ str
226
+ end
227
+
228
+ private :next_string
229
+
230
+ ##
231
+ # Returns the next _count_ bytes in _@data_, skipping the
232
+ # trailing "\n" character.
233
+ def next_bytes(count)
234
+ str = String.from_bytearray @data, @start, count
235
+ @start += count + 1
236
+ str
237
+ end
238
+
239
+ private :next_bytes
240
+
241
+ ##
242
+ # For object +val+, return a String represetation.
243
+
244
+ def marshal(val)
245
+ case val
246
+ when TrueClass
247
+ "t\n"
248
+ when FalseClass
249
+ "f\n"
250
+ when NilClass
251
+ "n\n"
252
+ when Fixnum, Bignum
253
+ "I\n#{val.to_s(16)}\n"
254
+ when String
255
+ if defined?(Encoding)
256
+ # We manually construct the Encoding data to avoid recursion
257
+ # marshaling an Encoding name as a String.
258
+ name = val.encoding.name
259
+ enc_name = "E\n#{name.bytesize}\n#{name}\n"
260
+ else
261
+ # The kernel code is all US-ASCII. When building melbourne for 1.8
262
+ # Ruby, we fake a bunch of encoding stuff so force US-ASCII here.
263
+ enc_name = "E\n8\nUS-ASCII\n"
264
+ end
265
+
266
+ "s\n#{enc_name}#{val.bytesize}\n#{val}\n"
267
+ when Symbol
268
+ s = val.to_s
269
+ if defined?(Encoding)
270
+ # We manually construct the Encoding data to avoid recursion
271
+ # marshaling an Encoding name as a String.
272
+ name = s.encoding.name
273
+ enc_name = "E\n#{name.bytesize}\n#{name}\n"
274
+ else
275
+ # The kernel code is all US-ASCII. When building melbourne for 1.8
276
+ # Ruby, we fake a bunch of encoding stuff so force US-ASCII here.
277
+ enc_name = "E\n8\nUS-ASCII\n"
278
+ end
279
+
280
+ "x\n#{enc_name}#{s.bytesize}\n#{s}\n"
281
+ when Rubinius::Tuple
282
+ str = "p\n#{val.size}\n"
283
+ val.each do |ele|
284
+ data = marshal(ele)
285
+ data.force_encoding(str.encoding) if defined?(Encoding)
286
+ str.append data
287
+ end
288
+ str
289
+ when Float
290
+ str = "d\n"
291
+ if val.infinite?
292
+ str.append "-" if val < 0.0
293
+ str.append "Infinity"
294
+ elsif val.nan?
295
+ str.append "NaN"
296
+ else
297
+ str.append " %+.54f %5d" % Math.frexp(val)
298
+ end
299
+ str.append "\n"
300
+ when Rubinius::InstructionSequence
301
+ str = "i\n#{val.size}\n"
302
+ val.opcodes.each do |op|
303
+ unless op.kind_of?(Fixnum)
304
+ raise TypeError, "InstructionSequence contains non Fixnum: #{op.inspect}"
305
+ end
306
+ str.append "#{op}\n"
307
+ end
308
+ str
309
+ when Rubinius::CompiledCode
310
+ str = "M\n1\n"
311
+ str.append marshal(val.metadata)
312
+ str.append marshal(val.primitive)
313
+ str.append marshal(val.name)
314
+ str.append marshal(val.iseq)
315
+ str.append marshal(val.stack_size)
316
+ str.append marshal(val.local_count)
317
+ str.append marshal(val.required_args)
318
+ str.append marshal(val.post_args)
319
+ str.append marshal(val.total_args)
320
+ str.append marshal(val.splat)
321
+ str.append marshal(val.literals)
322
+ str.append marshal(val.lines)
323
+ str.append marshal(val.file)
324
+ str.append marshal(val.local_names)
325
+ str
326
+ else
327
+ if val.respond_to? :rbx_marshal_constant
328
+ name = StringValue(val.rbx_marshal_constant)
329
+ "c\n#{name.size}\n#{name}\n"
330
+ else
331
+ raise ArgumentError, "Unknown type #{val.class}: #{val.inspect}"
332
+ end
333
+ end
334
+ end
335
+ end
336
+ end
337
+ end
338
+
@@ -0,0 +1,377 @@
1
+ # -*- encoding: us-ascii -*-
2
+
3
+ module Rubinius::ToolSet.current::TS
4
+
5
+ class CompileError < RuntimeError
6
+ end
7
+
8
+ class Compiler
9
+ attr_accessor :parser, :generator, :encoder, :packager, :writer
10
+
11
+ def self.compiler_error(msg, orig)
12
+ if defined?(RUBY_ENGINE) and RUBY_ENGINE == "rbx"
13
+ raise CompileError, msg, orig
14
+ else
15
+ orig.message.replace("#{orig.message} - #{msg}")
16
+ raise orig
17
+ end
18
+ end
19
+
20
+ if RBC_DB = Rubinius::Config['rbc.db']
21
+ def self.compiled_name(file)
22
+ full = "#{File.expand_path(file)}#{Rubinius::RUBY_LIB_VERSION}"
23
+ hash = Rubinius.invoke_primitive :sha1_hash, full
24
+ dir = hash[0,2]
25
+
26
+ "#{RBC_DB}/#{dir}/#{hash}"
27
+ end
28
+ else
29
+ def self.compiled_cache_writable?(db, dir)
30
+ File.owned?(db) or File.owned?(dir)
31
+ end
32
+
33
+ def self.compiled_name(file)
34
+ name = File.expand_path file
35
+
36
+ # If the file we are compiling is in a subdirectory of the current
37
+ # working directory when Rubinius is started, try to cache the
38
+ # compiled code in <cwd>/.rbx if:
39
+ #
40
+ # 1. <cwd>/.rbx exists and is owned by the process user
41
+ # OR
42
+ # 2. if <cwd> is owned by the process user
43
+ #
44
+ # Otherwise, try to cache the compiled code in ~/.rbx if:
45
+ #
46
+ # 1. ~/.rbx exists and is owned by the process user
47
+ # OR
48
+ # 2. ~/ is owned by the process user
49
+
50
+ unless @db
51
+ dir = Rubinius::OS_STARTUP_DIR
52
+ db = "#{dir}/.rbx"
53
+ unless name.prefix?(dir) and compiled_cache_writable?(db, dir)
54
+ # Yes, this retarded shit is necessary because people actually
55
+ # run under fucked environments with no HOME set.
56
+ return unless ENV["HOME"]
57
+
58
+ dir = File.expand_path "~/"
59
+ db = "#{dir}/.rbx"
60
+ return unless compiled_cache_writable?(db, dir)
61
+ end
62
+ @db = db
63
+ end
64
+
65
+ full = "#{name}#{Rubinius::RUBY_LIB_VERSION}"
66
+ hash = Rubinius.invoke_primitive :sha1_hash, full
67
+
68
+ "#{@db}/#{hash[0, 2]}/#{hash}"
69
+ end
70
+ end
71
+
72
+ def self.compile(file, output=nil, line=1, transforms=:default)
73
+ compiler = new :file, :compiled_file
74
+
75
+ parser = compiler.parser
76
+ parser.root AST::Script
77
+
78
+ if transforms.kind_of? Array
79
+ transforms.each { |t| parser.enable_category t }
80
+ else
81
+ parser.enable_category transforms
82
+ end
83
+
84
+ parser.input file, line
85
+
86
+ writer = compiler.writer
87
+ writer.version = Rubinius::RUBY_LIB_VERSION
88
+ writer.name = output ? output : compiled_name(file)
89
+
90
+ begin
91
+ compiler.run
92
+ rescue SyntaxError => e
93
+ raise e
94
+ rescue Exception => e
95
+ compiler_error "Error trying to compile #{file}", e
96
+ end
97
+
98
+ end
99
+
100
+ # Match old compiler's signature
101
+ def self.compile_file_old(file, flags=nil)
102
+ compile_file file, 1
103
+ end
104
+
105
+ def self.compile_file(file, line=1)
106
+ compiler = new :file, :compiled_code
107
+
108
+ parser = compiler.parser
109
+ parser.root AST::Script
110
+ parser.default_transforms
111
+ parser.input file, line
112
+
113
+ begin
114
+ compiler.run
115
+ rescue Exception => e
116
+ compiler_error "Error trying to compile #{file}", e
117
+ end
118
+ end
119
+
120
+ def self.compile_string(string, file="(eval)", line=1)
121
+ compiler = new :string, :compiled_code
122
+
123
+ parser = compiler.parser
124
+ parser.root AST::Script
125
+ parser.default_transforms
126
+ parser.input string, file, line
127
+
128
+ compiler.run
129
+ end
130
+
131
+ class LRUCache
132
+ class Entry
133
+ attr_reader :hits, :key
134
+ attr_accessor :value, :next_entry, :prev_entry
135
+
136
+ def initialize(key, value)
137
+ @key = key
138
+ @value = value
139
+ @hits = 0
140
+ @next_entry = nil
141
+ @prev_entry = nil
142
+ end
143
+
144
+ def insert_after(entry)
145
+ nxt = entry.next_entry
146
+
147
+ @prev_entry = entry
148
+ @next_entry = nxt
149
+
150
+ entry.next_entry = self
151
+ nxt.prev_entry = self if nxt
152
+ end
153
+
154
+ def insert_before(entry)
155
+ prev = entry.prev_entry
156
+
157
+ @prev_entry = prev
158
+ @next_entry = entry
159
+
160
+ entry.prev_entry = self
161
+ prev.next_entry = self if prev
162
+ end
163
+
164
+ def detach!
165
+ @next_entry.prev_entry = @prev_entry if @next_entry
166
+ @prev_entry.next_entry = @next_entry if @prev_entry
167
+
168
+ @next_entry = nil
169
+ @prev_entry = nil
170
+ end
171
+
172
+ def become_first!
173
+ @prev_entry = nil
174
+ end
175
+
176
+ def inc!
177
+ @hits += 1
178
+ end
179
+ end
180
+
181
+ def initialize(total)
182
+ @cache = {}
183
+ @total = total
184
+ @current = 0
185
+
186
+ @head = Entry.new(nil, nil)
187
+ @tail = Entry.new(nil, nil)
188
+
189
+ @tail.insert_after(@head)
190
+
191
+ @misses = 0
192
+ end
193
+
194
+ attr_reader :current, :misses
195
+
196
+ def clear!
197
+ Rubinius.synchronize(self) do
198
+ @cache = {}
199
+ @current = 0
200
+
201
+ @head = Entry.new(nil, nil, -1)
202
+ @tail = Entry.new(nil, nil, -2)
203
+
204
+ @tail.insert_after(@head)
205
+ end
206
+ end
207
+
208
+ def explain
209
+ entry = @head.next_entry
210
+ while entry != @tail
211
+ str, layout = entry.key
212
+ puts "hits: #{entry.hits}"
213
+ puts "layout: #{layout.inspect}"
214
+ puts "<STRING>"
215
+ puts str
216
+ puts "</STRING>"
217
+
218
+ entry = entry.next_entry
219
+ end
220
+ end
221
+
222
+ def retrieve(key)
223
+ Rubinius.synchronize(self) do
224
+ if entry = @cache[key]
225
+ entry.inc!
226
+
227
+ entry.detach!
228
+ entry.insert_before @tail
229
+
230
+ return entry.value
231
+ end
232
+
233
+ @misses += 1
234
+
235
+ nil
236
+ end
237
+ end
238
+
239
+ def set(key, value)
240
+ Rubinius.synchronize(self) do
241
+ if entry = @cache[key]
242
+ entry.value = value
243
+
244
+ entry.detach!
245
+ entry.insert_before @tail
246
+
247
+ return value
248
+ end
249
+
250
+ if @current == @total
251
+ entry = @head.next_entry
252
+
253
+ entry.detach!
254
+
255
+ @cache.delete entry.key
256
+ else
257
+ @current += 1
258
+ end
259
+
260
+ entry = Entry.new(key, value)
261
+
262
+ entry.insert_before @tail
263
+
264
+ @cache[key] = entry
265
+ end
266
+ end
267
+ end
268
+
269
+ total = Rubinius::Config['eval.cache']
270
+
271
+ case total
272
+ when Fixnum
273
+ if total == 0
274
+ @eval_cache = nil
275
+ else
276
+ @eval_cache = LRUCache.new(total)
277
+ end
278
+ when false
279
+ @eval_cache = nil
280
+ else
281
+ @eval_cache = LRUCache.new(50)
282
+ end
283
+
284
+ def self.eval_cache
285
+ @eval_cache
286
+ end
287
+
288
+ def self.compile_eval(string, variable_scope, file="(eval)", line=1)
289
+ if ec = @eval_cache
290
+ layout = variable_scope.local_layout
291
+ if code = ec.retrieve([string, layout, line])
292
+ return code
293
+ end
294
+ end
295
+
296
+ compiler = new :eval, :compiled_code
297
+
298
+ parser = compiler.parser
299
+ parser.root AST::EvalExpression
300
+ parser.default_transforms
301
+ parser.input string, file, line
302
+
303
+ compiler.generator.variable_scope = variable_scope
304
+
305
+ code = compiler.run
306
+
307
+ code.add_metadata :for_eval, true
308
+
309
+ if ec and parser.should_cache?
310
+ ec.set([string.dup, layout, line], code)
311
+ end
312
+
313
+ return code
314
+ end
315
+
316
+ def self.construct_block(string, binding, file="(eval)", line=1)
317
+ code = compile_eval string, binding.variables, file, line
318
+
319
+ code.scope = binding.constant_scope
320
+ code.name = binding.variables.method.name
321
+
322
+ # This has to be setup so __FILE__ works in eval.
323
+ script = Rubinius::CompiledCode::Script.new(code, file, true)
324
+ script.eval_source = string
325
+
326
+ code.scope.script = script
327
+
328
+ be = Rubinius::BlockEnvironment.new
329
+ be.under_context binding.variables, code
330
+
331
+ # Pass the BlockEnvironment this binding was created from
332
+ # down into the new BlockEnvironment we just created.
333
+ # This indicates the "declaration trace" to the stack trace
334
+ # mechanisms, which can be different from the "call trace"
335
+ # in the case of, say: eval("caller", a_proc_instance)
336
+ if binding.from_proc?
337
+ be.proc_environment = binding.proc_environment
338
+ end
339
+
340
+ return be
341
+ end
342
+
343
+ def self.compile_test_bytecode(string, transforms)
344
+ compiler = new :string, :bytecode
345
+
346
+ parser = compiler.parser
347
+ parser.root AST::Snippet
348
+ parser.input string
349
+ transforms.each { |x| parser.enable_transform x }
350
+
351
+ compiler.generator.processor Rubinius::TestGenerator
352
+
353
+ compiler.run
354
+ end
355
+
356
+ def self.compile_test_bytecode_19(string, transforms)
357
+ compiler = new :string, :bytecode
358
+
359
+ parser = compiler.parser
360
+ parser.root AST::Snippet
361
+ parser.input string
362
+ transforms.each { |x| parser.enable_transform x }
363
+
364
+ compiler.generator.processor TestGenerator
365
+
366
+ compiler.run
367
+ end
368
+
369
+ def initialize(from, to)
370
+ @start = Stages[from].new self, to
371
+ end
372
+
373
+ def run
374
+ @start.run
375
+ end
376
+ end
377
+ end