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
@@ -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
|