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 +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +25 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/lib/rubinius/compiler.rb +11 -0
- data/lib/rubinius/compiler/compiled_file.rb +338 -0
- data/lib/rubinius/compiler/compiler.rb +377 -0
- data/lib/rubinius/compiler/evaluator.rb +334 -0
- data/lib/rubinius/compiler/generator.rb +646 -0
- data/lib/rubinius/compiler/generator_methods.rb +802 -0
- data/lib/rubinius/compiler/iseq.rb +146 -0
- data/lib/rubinius/compiler/locals.rb +147 -0
- data/lib/rubinius/compiler/opcodes.rb +150 -0
- data/lib/rubinius/compiler/printers.rb +115 -0
- data/lib/rubinius/compiler/runtime.rb +72 -0
- data/lib/rubinius/compiler/stages.rb +254 -0
- data/lib/rubinius/compiler/version.rb +5 -0
- data/rubinius-compiler.gemspec +22 -0
- metadata +91 -0
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
data/Gemfile
ADDED
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
|