rubinius-compiler 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,334 @@
|
|
1
|
+
# -*- encoding: us-ascii -*-
|
2
|
+
|
3
|
+
##
|
4
|
+
# Used for the Rubinius::asm Compiler hook.
|
5
|
+
|
6
|
+
module Rubinius::ToolSet.current::TS
|
7
|
+
module AST
|
8
|
+
class Node
|
9
|
+
end
|
10
|
+
|
11
|
+
class ClosedScope < Node
|
12
|
+
end
|
13
|
+
|
14
|
+
class VariableAccess < Node
|
15
|
+
end
|
16
|
+
|
17
|
+
class VariableAssignment < Node
|
18
|
+
end
|
19
|
+
|
20
|
+
class Evaluator
|
21
|
+
attr_accessor :self
|
22
|
+
|
23
|
+
def initialize(generator, names, arguments)
|
24
|
+
@self = generator
|
25
|
+
@locals = {}
|
26
|
+
|
27
|
+
names.each_with_index do |name, index|
|
28
|
+
set_local name, arguments[index]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def execute(node)
|
33
|
+
node.execute self
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_local(name)
|
37
|
+
@locals[name]
|
38
|
+
end
|
39
|
+
|
40
|
+
def set_local(name, value)
|
41
|
+
@locals[name] = value
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Container < ClosedScope
|
46
|
+
def execute(e)
|
47
|
+
@body.execute(e)
|
48
|
+
return true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class TrueLiteral < Node
|
53
|
+
def execute(e)
|
54
|
+
true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class FalseLiteral < Node
|
59
|
+
def execute(e)
|
60
|
+
false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class NilLiteral < Node
|
65
|
+
def execute(e)
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Self < Node
|
71
|
+
def execute(e)
|
72
|
+
e.self
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class And < Node
|
77
|
+
def execute(e)
|
78
|
+
@left.execute(e) and @right.execute(e)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class Or < And
|
83
|
+
def execute(e)
|
84
|
+
@left.execute(e) or @right.execute(e)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class Not < Node
|
89
|
+
def execute(e)
|
90
|
+
not @child.execute(e)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class Negate < Node
|
95
|
+
def execute(e)
|
96
|
+
-@child.execute(e)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class NumberLiteral < Node
|
101
|
+
def execute(e)
|
102
|
+
@value
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class Literal < Node
|
107
|
+
def execute(e)
|
108
|
+
@value
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class RegexLiteral < Node
|
113
|
+
def execute(e)
|
114
|
+
::Regexp.new(@source, @options)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class StringLiteral < Node
|
119
|
+
def execute(e)
|
120
|
+
@string.dup
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class DynamicString < StringLiteral
|
125
|
+
def execute(e)
|
126
|
+
str = @string.dup
|
127
|
+
@body.each do |x|
|
128
|
+
str << x.execute(e)
|
129
|
+
end
|
130
|
+
|
131
|
+
str
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
class DynamicRegex < DynamicString
|
136
|
+
def execute(e)
|
137
|
+
::Regexp.new super(e)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
class DynamicOnceRegex < DynamicRegex
|
142
|
+
def execute(e)
|
143
|
+
@value ||= super(e)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
class If < Node
|
148
|
+
def execute(e)
|
149
|
+
if @condition.execute(e)
|
150
|
+
@then.execute(e) if @then
|
151
|
+
else
|
152
|
+
@else.execute(e) if @else
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
class While < Node
|
158
|
+
def execute(e)
|
159
|
+
if @check_first
|
160
|
+
while @condition.execute(e)
|
161
|
+
@body.execute(e)
|
162
|
+
end
|
163
|
+
else
|
164
|
+
begin
|
165
|
+
@body.execute(e)
|
166
|
+
end while @condition.execute(e)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
class Until < While
|
172
|
+
def execute(e)
|
173
|
+
if @check_first
|
174
|
+
until @condition.execute(e)
|
175
|
+
@body.execute(e)
|
176
|
+
end
|
177
|
+
else
|
178
|
+
begin
|
179
|
+
@body.execute(e)
|
180
|
+
end until @condition.execute(e)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
class Block < Node
|
186
|
+
def execute(e)
|
187
|
+
val = nil
|
188
|
+
@array.each do |x|
|
189
|
+
val = x.execute(e)
|
190
|
+
end
|
191
|
+
|
192
|
+
return val
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
class LocalVariableAccess < VariableAccess
|
197
|
+
def execute(e)
|
198
|
+
e.get_local @name
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
class LocalVariableAssignment < VariableAssignment
|
203
|
+
def execute(e)
|
204
|
+
e.set_local @name, @value.execute(e)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
class ArrayLiteral < Node
|
209
|
+
def execute(e)
|
210
|
+
@body.map { |x| x.execute(e) }
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
class EmptyArray < Node
|
215
|
+
def execute(e)
|
216
|
+
[]
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
class HashLiteral < Node
|
221
|
+
def execute(e)
|
222
|
+
args = @array.map { |x| x.execute(e) }
|
223
|
+
Hash[*args]
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
class SymbolLiteral < Node
|
228
|
+
def execute(e)
|
229
|
+
@value
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
class InstanceVariableAccess < VariableAccess
|
234
|
+
def execute(e)
|
235
|
+
e.self.instance_variable_get @name
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
class InstanceVariableAssignment < VariableAssignment
|
240
|
+
def execute(e)
|
241
|
+
e.self.instance_variable_set @name, @value.execute(e)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
class GlobalVariableAccess < VariableAccess
|
246
|
+
def execute(e)
|
247
|
+
::Rubinius::Globals[@name]
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
class GlobalVariableAssignment < VariableAssignment
|
252
|
+
def execute(e)
|
253
|
+
::Rubinius::Globals[@name] = @value.execute(e)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
class ConstantAccess < Node
|
258
|
+
def execute(e)
|
259
|
+
Object.const_get @name
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
class ScopedConstant < Node
|
264
|
+
def execute(e)
|
265
|
+
parent = @parent.execute(e)
|
266
|
+
parent.const_get @name
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
class ToplevelConstant < Node
|
271
|
+
def execute(e)
|
272
|
+
Object.const_get @name
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
class Send < Node
|
277
|
+
def execute_receiver(e)
|
278
|
+
if @receiver.kind_of? Self
|
279
|
+
e.self
|
280
|
+
else
|
281
|
+
@receiver.execute(e)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
def execute(e)
|
286
|
+
receiver = execute_receiver(e)
|
287
|
+
|
288
|
+
receiver.__send__ @name
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
class SendWithArguments < Send
|
293
|
+
def execute(e)
|
294
|
+
arguments = @arguments.execute(e)
|
295
|
+
receiver = execute_receiver(e)
|
296
|
+
|
297
|
+
receiver.__send__ @name, *arguments
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
class ActualArguments < Node
|
302
|
+
def execute(e)
|
303
|
+
array = @array.map { |x| x.execute(e) }
|
304
|
+
array << @splat.execute if @splat.kind_of? SplatValue
|
305
|
+
array
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
class Yield < SendWithArguments
|
310
|
+
def execute(e)
|
311
|
+
# TODO
|
312
|
+
e.block.call(*@arguments.execute(e))
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
class ExecuteString < StringLiteral
|
317
|
+
def execute(e)
|
318
|
+
`#{@string.execute(e)}`
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
class ToString < Node
|
323
|
+
def execute(e)
|
324
|
+
@child.execute(e).to_s
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
class DynamicExecuteString < DynamicString
|
329
|
+
def execute(e)
|
330
|
+
`#{super(e)}`
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
@@ -0,0 +1,646 @@
|
|
1
|
+
# -*- encoding: us-ascii -*-
|
2
|
+
|
3
|
+
module Rubinius::ToolSet.current::TS
|
4
|
+
class Generator
|
5
|
+
include GeneratorMethods
|
6
|
+
|
7
|
+
##
|
8
|
+
# Jump label for the branch instructions. The use scenarios for labels:
|
9
|
+
# 1. Used and then set
|
10
|
+
# g.gif label
|
11
|
+
# ...
|
12
|
+
# label.set!
|
13
|
+
# 2. Set and then used
|
14
|
+
# label.set!
|
15
|
+
# ...
|
16
|
+
# g.git label
|
17
|
+
# 3. 1, 2
|
18
|
+
#
|
19
|
+
# Many labels are only used once. This class employs two small
|
20
|
+
# optimizations. First, for the case where a label is used once then set,
|
21
|
+
# the label merely records the point it was used and updates that location
|
22
|
+
# to the concrete IP when the label is set. In the case where the label is
|
23
|
+
# used multiple times, it records each location and updates them to an IP
|
24
|
+
# when the label is set. In both cases, once the label is set, each use
|
25
|
+
# after that updates the instruction stream with a concrete IP at the
|
26
|
+
# point the label is used. This avoids the need to ever record all the
|
27
|
+
# labels or search through the stream later to change symbolic labels into
|
28
|
+
# concrete IP's.
|
29
|
+
|
30
|
+
class Label
|
31
|
+
attr_accessor :position
|
32
|
+
attr_reader :used, :basic_block
|
33
|
+
alias_method :used?, :used
|
34
|
+
|
35
|
+
def initialize(generator)
|
36
|
+
@generator = generator
|
37
|
+
@basic_block = generator.new_basic_block
|
38
|
+
@position = nil
|
39
|
+
@used = false
|
40
|
+
@location = nil
|
41
|
+
@locations = nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def set!
|
45
|
+
@position = @generator.ip
|
46
|
+
if @locations
|
47
|
+
@locations.each { |x| @generator.stream[x] = @position }
|
48
|
+
elsif @location
|
49
|
+
@generator.stream[@location] = @position
|
50
|
+
end
|
51
|
+
|
52
|
+
@generator.current_block.left = @basic_block
|
53
|
+
@generator.current_block.close
|
54
|
+
@generator.current_block = @basic_block
|
55
|
+
@basic_block.open
|
56
|
+
end
|
57
|
+
|
58
|
+
def used_at(ip)
|
59
|
+
if @position
|
60
|
+
@generator.stream[ip] = @position
|
61
|
+
elsif !@location
|
62
|
+
@location = ip
|
63
|
+
elsif @locations
|
64
|
+
@locations << ip
|
65
|
+
else
|
66
|
+
@locations = [@location, ip]
|
67
|
+
end
|
68
|
+
@used = true
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class BasicBlock
|
73
|
+
attr_accessor :left
|
74
|
+
attr_accessor :right
|
75
|
+
|
76
|
+
def initialize(generator)
|
77
|
+
@generator = generator
|
78
|
+
@ip = generator.ip
|
79
|
+
@enter_size = nil
|
80
|
+
@max_size = 0
|
81
|
+
@min_size = 0
|
82
|
+
@exit_ip = 0
|
83
|
+
@exit_size = nil
|
84
|
+
@stack = 0
|
85
|
+
@left = nil
|
86
|
+
@right = nil
|
87
|
+
@visited = false
|
88
|
+
@closed = false
|
89
|
+
end
|
90
|
+
|
91
|
+
def add_stack(read, write)
|
92
|
+
read_change = @stack - read
|
93
|
+
@min_size = read_change if read_change < @min_size
|
94
|
+
|
95
|
+
@stack += (write - read)
|
96
|
+
|
97
|
+
@max_size = @stack if @stack > @max_size
|
98
|
+
end
|
99
|
+
|
100
|
+
def open
|
101
|
+
@ip = @generator.ip
|
102
|
+
end
|
103
|
+
|
104
|
+
def close(record_exit=false)
|
105
|
+
@closed = true
|
106
|
+
|
107
|
+
if record_exit
|
108
|
+
@exit_size = @stack
|
109
|
+
@exit_ip = @generator.ip - 1
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def location(ip=nil)
|
114
|
+
ip ||= @ip
|
115
|
+
line = @generator.ip_to_line(ip)
|
116
|
+
"#{@generator.name}: line: #{line}, IP: #{ip}"
|
117
|
+
end
|
118
|
+
|
119
|
+
SEPARATOR_SIZE = 40
|
120
|
+
|
121
|
+
def invalid(message)
|
122
|
+
if $DEBUG
|
123
|
+
puts message
|
124
|
+
name = @generator.name.inspect
|
125
|
+
size = (SEPARATOR_SIZE - name.size - 2) / 2
|
126
|
+
size = 1 if size <= 0
|
127
|
+
puts "\n#{"=" * size} #{name} #{"=" * (size + name.size % 2)}"
|
128
|
+
|
129
|
+
literals = @generator.literals
|
130
|
+
names = @generator.local_names
|
131
|
+
stream = @generator.stream
|
132
|
+
i = 0
|
133
|
+
n = stream.size
|
134
|
+
stack = 0
|
135
|
+
|
136
|
+
while i < n
|
137
|
+
insn = Rubinius::InstructionSet[stream[i]]
|
138
|
+
printf "[%3d] %04d %-28s" % [stack, i, insn.opcode.inspect]
|
139
|
+
|
140
|
+
args = stream[i+1, insn.size-1]
|
141
|
+
if insn.size > 1
|
142
|
+
insn.args.each_with_index do |kind, index|
|
143
|
+
arg = args[index]
|
144
|
+
case kind
|
145
|
+
when :literal
|
146
|
+
printf "%s " % literals[arg].inspect
|
147
|
+
when :local
|
148
|
+
printf "%s " % (names ? names[arg] : arg)
|
149
|
+
else
|
150
|
+
printf "%d " % arg
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
puts
|
156
|
+
|
157
|
+
if insn.variable_stack?
|
158
|
+
use = insn.stack_consumed
|
159
|
+
if use.kind_of? Array
|
160
|
+
use = args[use[1] - 1] + use[0]
|
161
|
+
end
|
162
|
+
|
163
|
+
pro = insn.stack_produced
|
164
|
+
if pro.kind_of? Array
|
165
|
+
pro = (args[pro[1] - 1] * pro[2]) + pro[0]
|
166
|
+
end
|
167
|
+
|
168
|
+
stack += pro - use
|
169
|
+
else
|
170
|
+
stack += insn.stack_difference
|
171
|
+
end
|
172
|
+
|
173
|
+
i += insn.size
|
174
|
+
end
|
175
|
+
|
176
|
+
puts "-" * SEPARATOR_SIZE
|
177
|
+
end
|
178
|
+
|
179
|
+
raise CompileError, message
|
180
|
+
end
|
181
|
+
|
182
|
+
def visited?
|
183
|
+
@visited
|
184
|
+
end
|
185
|
+
|
186
|
+
def validate_stack
|
187
|
+
@enter_size = 0
|
188
|
+
|
189
|
+
stack = [self]
|
190
|
+
until stack.empty?
|
191
|
+
bb = stack.shift
|
192
|
+
bb.flow_stack_size stack
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def flow_stack_size(stack)
|
197
|
+
unless @visited
|
198
|
+
@visited = true
|
199
|
+
|
200
|
+
@generator.accumulate_stack(@enter_size + @max_size)
|
201
|
+
|
202
|
+
net_size = @enter_size + @stack
|
203
|
+
|
204
|
+
if net_size < 0
|
205
|
+
invalid "net stack underflow in block starting at #{location}"
|
206
|
+
end
|
207
|
+
|
208
|
+
if @enter_size + @min_size < 0
|
209
|
+
invalid "minimum stack underflow in block starting at #{location}"
|
210
|
+
end
|
211
|
+
|
212
|
+
if @exit_size and @enter_size + @exit_size < 1
|
213
|
+
invalid "exit stack underflow in block starting at #{location(@exit_ip)}"
|
214
|
+
end
|
215
|
+
|
216
|
+
if @left
|
217
|
+
@left.check_stack net_size
|
218
|
+
stack.push @left unless @left.visited?
|
219
|
+
end
|
220
|
+
|
221
|
+
if @right
|
222
|
+
@right.check_stack net_size
|
223
|
+
stack.push @right unless @right.visited?
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def check_stack(stack_size)
|
229
|
+
if @enter_size
|
230
|
+
unless stack_size == @enter_size
|
231
|
+
invalid "unbalanced stack at #{location}: #{stack_size} != #{@enter_size}"
|
232
|
+
end
|
233
|
+
else
|
234
|
+
if not @closed
|
235
|
+
invalid "control fails to exit properly at #{location}"
|
236
|
+
end
|
237
|
+
|
238
|
+
@enter_size = stack_size
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def initialize
|
244
|
+
@stream = []
|
245
|
+
@literals_map = Hash.new { |h,k| h[k] = add_literal(k) }
|
246
|
+
@literals = []
|
247
|
+
@ip = 0
|
248
|
+
@modstack = []
|
249
|
+
@break = nil
|
250
|
+
@redo = nil
|
251
|
+
@next = nil
|
252
|
+
@retry = nil
|
253
|
+
@last_line = nil
|
254
|
+
@file = nil
|
255
|
+
@lines = []
|
256
|
+
@primitive = nil
|
257
|
+
@instruction = nil
|
258
|
+
@for_block = nil
|
259
|
+
@for_module_body = nil
|
260
|
+
|
261
|
+
@required_args = 0
|
262
|
+
@post_args = 0
|
263
|
+
@total_args = 0
|
264
|
+
|
265
|
+
@detected_args = 0
|
266
|
+
@detected_locals = 0
|
267
|
+
|
268
|
+
@splat_index = nil
|
269
|
+
@local_names = nil
|
270
|
+
@block_index = nil
|
271
|
+
@local_count = 0
|
272
|
+
|
273
|
+
@state = []
|
274
|
+
@generators = []
|
275
|
+
|
276
|
+
@stack_locals = 0
|
277
|
+
|
278
|
+
@enter_block = new_basic_block
|
279
|
+
@current_block = @enter_block
|
280
|
+
@max_stack = 0
|
281
|
+
end
|
282
|
+
|
283
|
+
attr_reader :ip, :stream, :iseq, :literals
|
284
|
+
attr_accessor :break, :redo, :next, :retry, :file, :name,
|
285
|
+
:required_args, :post_args, :total_args, :splat_index,
|
286
|
+
:local_count, :local_names, :primitive, :for_block, :for_module_body,
|
287
|
+
:current_block, :detected_args, :detected_locals,
|
288
|
+
:block_index
|
289
|
+
|
290
|
+
def execute(node)
|
291
|
+
node.bytecode self
|
292
|
+
end
|
293
|
+
|
294
|
+
alias_method :run, :execute
|
295
|
+
|
296
|
+
# Formalizers
|
297
|
+
|
298
|
+
def encode
|
299
|
+
@iseq = Rubinius::InstructionSequence.new @stream.to_tuple
|
300
|
+
|
301
|
+
begin
|
302
|
+
# Validate the stack and calculate the max depth
|
303
|
+
@enter_block.validate_stack
|
304
|
+
rescue Exception => e
|
305
|
+
if $DEBUG
|
306
|
+
puts "Error computing stack for #{@name}: #{e.message} (#{e.class})"
|
307
|
+
end
|
308
|
+
raise e
|
309
|
+
end
|
310
|
+
|
311
|
+
@generators.each { |x| @literals[x].encode }
|
312
|
+
end
|
313
|
+
|
314
|
+
def package(klass)
|
315
|
+
@generators.each { |x| @literals[x] = @literals[x].package klass }
|
316
|
+
|
317
|
+
code = klass.new
|
318
|
+
code.iseq = @iseq
|
319
|
+
code.literals = @literals.to_tuple
|
320
|
+
code.lines = @lines.to_tuple
|
321
|
+
|
322
|
+
code.required_args = @required_args
|
323
|
+
code.post_args = @post_args
|
324
|
+
code.total_args = @total_args
|
325
|
+
code.splat = @splat_index
|
326
|
+
code.block_index = @block_index
|
327
|
+
code.local_count = @local_count
|
328
|
+
code.local_names = @local_names.to_tuple if @local_names
|
329
|
+
|
330
|
+
code.stack_size = max_stack_size
|
331
|
+
code.file = @file
|
332
|
+
code.name = @name
|
333
|
+
code.primitive = @primitive
|
334
|
+
|
335
|
+
if @for_block
|
336
|
+
code.add_metadata :for_block, true
|
337
|
+
end
|
338
|
+
|
339
|
+
if @for_module_body
|
340
|
+
code.add_metadata :for_module_body, true
|
341
|
+
end
|
342
|
+
|
343
|
+
code
|
344
|
+
end
|
345
|
+
|
346
|
+
def use_detected
|
347
|
+
if @required_args < @detected_args
|
348
|
+
@required_args = @detected_args
|
349
|
+
end
|
350
|
+
|
351
|
+
if @total_args < @detected_args
|
352
|
+
@total_args = @detected_args
|
353
|
+
end
|
354
|
+
|
355
|
+
if @local_count < @detected_locals
|
356
|
+
@local_count = @detected_locals
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
# Commands (these don't generate data in the stream)
|
361
|
+
|
362
|
+
def state
|
363
|
+
@state.last
|
364
|
+
end
|
365
|
+
|
366
|
+
def push_state(scope)
|
367
|
+
@state << AST::State.new(scope)
|
368
|
+
end
|
369
|
+
|
370
|
+
def pop_state
|
371
|
+
@state.pop
|
372
|
+
end
|
373
|
+
|
374
|
+
def push_modifiers
|
375
|
+
@modstack << [@break, @redo, @next, @retry]
|
376
|
+
end
|
377
|
+
|
378
|
+
def pop_modifiers
|
379
|
+
@break, @redo, @next, @retry = @modstack.pop
|
380
|
+
end
|
381
|
+
|
382
|
+
def definition_line(line)
|
383
|
+
unless @stream.empty?
|
384
|
+
raise Exception, "only use #definition_line first"
|
385
|
+
end
|
386
|
+
|
387
|
+
@lines << -1
|
388
|
+
@lines << line
|
389
|
+
|
390
|
+
@last_line = line
|
391
|
+
end
|
392
|
+
|
393
|
+
def set_line(line)
|
394
|
+
raise Exception, "source code line cannot be nil" unless line
|
395
|
+
|
396
|
+
if !@last_line
|
397
|
+
@lines << @ip
|
398
|
+
@lines << line
|
399
|
+
@last_line = line
|
400
|
+
elsif line != @last_line
|
401
|
+
if @lines[-2] == @ip
|
402
|
+
@lines[-1] = line
|
403
|
+
else
|
404
|
+
@lines << @ip
|
405
|
+
@lines << line
|
406
|
+
end
|
407
|
+
|
408
|
+
@last_line = line
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
def line
|
413
|
+
@last_line
|
414
|
+
end
|
415
|
+
|
416
|
+
def ip_to_line(ip)
|
417
|
+
total = @lines.size - 2
|
418
|
+
i = 0
|
419
|
+
|
420
|
+
while i < total
|
421
|
+
if ip >= @lines[i] and ip <= @lines[i+2]
|
422
|
+
return @lines[i+1]
|
423
|
+
end
|
424
|
+
|
425
|
+
i += 2
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
def close
|
430
|
+
if @lines.empty?
|
431
|
+
msg = "closing a method definition with no line info: #{file}:#{line}"
|
432
|
+
raise Exception, msg
|
433
|
+
end
|
434
|
+
|
435
|
+
@lines << @ip
|
436
|
+
end
|
437
|
+
|
438
|
+
def send_primitive(name)
|
439
|
+
@primitive = name
|
440
|
+
end
|
441
|
+
|
442
|
+
def new_label
|
443
|
+
Label.new(self)
|
444
|
+
end
|
445
|
+
|
446
|
+
# Aliases
|
447
|
+
|
448
|
+
alias_method :dup, :dup_top
|
449
|
+
alias_method :git, :goto_if_true
|
450
|
+
alias_method :gif, :goto_if_false
|
451
|
+
alias_method :swap, :swap_stack
|
452
|
+
|
453
|
+
# Helpers
|
454
|
+
|
455
|
+
def new_basic_block
|
456
|
+
BasicBlock.new self
|
457
|
+
end
|
458
|
+
|
459
|
+
def accumulate_stack(size)
|
460
|
+
@max_stack = size if size > @max_stack
|
461
|
+
end
|
462
|
+
|
463
|
+
def max_stack_size
|
464
|
+
size = @max_stack + @local_count + @stack_locals
|
465
|
+
size += 1 if @for_block
|
466
|
+
size
|
467
|
+
end
|
468
|
+
|
469
|
+
def new_stack_local
|
470
|
+
idx = @stack_locals
|
471
|
+
@stack_locals += 1
|
472
|
+
return idx
|
473
|
+
end
|
474
|
+
|
475
|
+
def push(what)
|
476
|
+
case what
|
477
|
+
when :true
|
478
|
+
push_true
|
479
|
+
when :false
|
480
|
+
push_false
|
481
|
+
when :self
|
482
|
+
push_self
|
483
|
+
when :nil
|
484
|
+
push_nil
|
485
|
+
when Integer
|
486
|
+
push_int what
|
487
|
+
else
|
488
|
+
raise CompileError, "Unknown push argument '#{what.inspect}'"
|
489
|
+
end
|
490
|
+
end
|
491
|
+
|
492
|
+
def push_generator(generator)
|
493
|
+
index = push_literal generator
|
494
|
+
@generators << index
|
495
|
+
index
|
496
|
+
end
|
497
|
+
|
498
|
+
# Find the index for the specified literal, or create a new slot if the
|
499
|
+
# literal has not been encountered previously.
|
500
|
+
def find_literal(literal)
|
501
|
+
@literals_map[literal]
|
502
|
+
end
|
503
|
+
|
504
|
+
# Add literal exists to allow RegexLiteral's to create a new regex literal
|
505
|
+
# object at run-time. All other literals should be added via find_literal,
|
506
|
+
# which re-use an existing matching literal if one exists.
|
507
|
+
def add_literal(literal)
|
508
|
+
index = @literals.size
|
509
|
+
@literals << literal
|
510
|
+
return index
|
511
|
+
end
|
512
|
+
|
513
|
+
# Pushes the specified literal value into the literal's tuple
|
514
|
+
def push_literal(literal)
|
515
|
+
index = find_literal literal
|
516
|
+
emit_push_literal index
|
517
|
+
return index
|
518
|
+
end
|
519
|
+
|
520
|
+
# Puts +what+ is the literals tuple without trying to see if
|
521
|
+
# something that is like +what+ is already there.
|
522
|
+
def push_unique_literal(literal)
|
523
|
+
index = add_literal literal
|
524
|
+
emit_push_literal index
|
525
|
+
return index
|
526
|
+
end
|
527
|
+
|
528
|
+
# Pushes the literal value on the stack into the specified position in the
|
529
|
+
# literals tuple. Most timees, push_literal should be used instead; this
|
530
|
+
# method exists to support RegexLiteral, where the compiled literal value
|
531
|
+
# (a Regex object) does not exist until runtime.
|
532
|
+
def push_literal_at(index)
|
533
|
+
emit_push_literal index
|
534
|
+
return index
|
535
|
+
end
|
536
|
+
|
537
|
+
# The push_const instruction itself is unused right now. The instruction
|
538
|
+
# parser does not emit a GeneratorMethods#push_const. This method/opcode
|
539
|
+
# was used in the compiler before the push_const_fast instruction. Rather
|
540
|
+
# than changing the compiler code, this helper was used.
|
541
|
+
def push_const(name)
|
542
|
+
push_const_fast find_literal(name)
|
543
|
+
end
|
544
|
+
|
545
|
+
# The find_const instruction itself is unused right now. The instruction
|
546
|
+
# parser does not emit a GeneratorMethods#find_const. This method/opcode
|
547
|
+
# was used in the compiler before the find_const_fast instruction. Rather
|
548
|
+
# than changing the compiler code, this helper was used.
|
549
|
+
def find_const(name)
|
550
|
+
find_const_fast find_literal(name)
|
551
|
+
end
|
552
|
+
|
553
|
+
def push_local(idx)
|
554
|
+
if @detected_locals <= idx
|
555
|
+
@detected_locals = idx + 1
|
556
|
+
end
|
557
|
+
|
558
|
+
super
|
559
|
+
end
|
560
|
+
|
561
|
+
def set_local(idx)
|
562
|
+
if @detected_locals <= idx
|
563
|
+
@detected_locals = idx + 1
|
564
|
+
end
|
565
|
+
|
566
|
+
super
|
567
|
+
end
|
568
|
+
|
569
|
+
# Minor meta operations that can be used to detect
|
570
|
+
# the number of method arguments needed
|
571
|
+
def push_arg(idx)
|
572
|
+
push_local(idx)
|
573
|
+
@detected_args = @detected_locals
|
574
|
+
end
|
575
|
+
|
576
|
+
def set_arg(idx)
|
577
|
+
set_local(idx)
|
578
|
+
@detected_args = @detected_locals
|
579
|
+
end
|
580
|
+
|
581
|
+
def last_match(mode, which)
|
582
|
+
push_int Integer(mode)
|
583
|
+
push_int Integer(which)
|
584
|
+
invoke_primitive :regexp_last_match_result, 2
|
585
|
+
end
|
586
|
+
|
587
|
+
def send(meth, count, priv=false)
|
588
|
+
allow_private if priv
|
589
|
+
|
590
|
+
unless count.kind_of? Fixnum
|
591
|
+
raise CompileError, "count must be a number"
|
592
|
+
end
|
593
|
+
|
594
|
+
idx = find_literal(meth)
|
595
|
+
|
596
|
+
# Don't use send_method, it's only for when the syntax
|
597
|
+
# specified no arguments and no parens.
|
598
|
+
send_stack idx, count
|
599
|
+
end
|
600
|
+
|
601
|
+
# Do a private send to self with no arguments specified, ie, a vcall
|
602
|
+
# style send.
|
603
|
+
def send_vcall(meth)
|
604
|
+
idx = find_literal(meth)
|
605
|
+
send_method idx
|
606
|
+
end
|
607
|
+
|
608
|
+
def send_with_block(meth, count, priv=false)
|
609
|
+
allow_private if priv
|
610
|
+
|
611
|
+
unless count.kind_of? Fixnum
|
612
|
+
raise CompileError, "count must be a number"
|
613
|
+
end
|
614
|
+
|
615
|
+
idx = find_literal(meth)
|
616
|
+
|
617
|
+
send_stack_with_block idx, count
|
618
|
+
end
|
619
|
+
|
620
|
+
def send_with_splat(meth, args, priv=false, concat=false)
|
621
|
+
val = 0
|
622
|
+
val |= Rubinius::InstructionSet::CALL_FLAG_CONCAT if concat
|
623
|
+
set_call_flags val unless val == 0
|
624
|
+
|
625
|
+
allow_private if priv
|
626
|
+
|
627
|
+
idx = find_literal(meth)
|
628
|
+
send_stack_with_splat idx, args
|
629
|
+
end
|
630
|
+
|
631
|
+
def send_super(meth, args, splat=false)
|
632
|
+
idx = find_literal(meth)
|
633
|
+
|
634
|
+
if splat
|
635
|
+
send_super_stack_with_splat idx, args
|
636
|
+
else
|
637
|
+
send_super_stack_with_block idx, args
|
638
|
+
end
|
639
|
+
end
|
640
|
+
|
641
|
+
def meta_to_s(name=:to_s, priv=true)
|
642
|
+
allow_private if priv
|
643
|
+
super find_literal(name)
|
644
|
+
end
|
645
|
+
end
|
646
|
+
end
|