mirah 0.1.2-java → 0.1.3-java
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 +4 -4
- data/History.txt +225 -0
- data/Rakefile +108 -315
- data/TODO.md +100 -0
- data/bin/bundler +16 -0
- data/bin/rake +16 -0
- data/dist/mirahc.jar +0 -0
- data/examples/appengine/Readme +0 -1
- data/examples/literals.mirah +17 -0
- data/examples/macros/string_each_char.mirah +1 -1
- data/lib/mirah.rb +11 -21
- data/lib/mirah/transform/transformer.rb +1 -2
- data/lib/mirah/util/class_loader.rb +1 -1
- data/lib/mirah/util/logging.rb +0 -63
- data/lib/mirah/util/process_errors.rb +1 -0
- data/lib/mirah/version.rb +1 -1
- data/{examples/simple_class.mirah~ → test/artifacts/jar_test.rb} +7 -11
- data/{lib/mirah/commands.rb → test/artifacts/jruby_test.rb} +8 -5
- data/test/core/typer_test.rb +29 -11
- data/test/core/util/argument_processor_test.rb +24 -23
- data/test/core/util/class_loader_test.rb +7 -4
- data/test/core/util/{compilation_state_test.rb → jvm_version_test.rb} +20 -16
- data/test/fixtures/org/foo/ImplicitClassRetAnno.java +4 -0
- data/test/fixtures/org/foo/IntAnno.java +9 -0
- data/test/jvm/annotations_test.rb +11 -11
- data/test/jvm/blocks_test.rb +16 -12
- data/test/jvm/constructors_test.rb +8 -8
- data/test/jvm/enumerable_test.rb +48 -24
- data/test/jvm/generics_test.rb +3 -7
- data/test/jvm/import_test.rb +14 -0
- data/test/jvm/interface_test.rb +9 -24
- data/test/jvm/jvm_commands_test.rb +22 -4
- data/test/jvm/jvm_compiler_test.rb +124 -79
- data/test/jvm/list_extensions_test.rb +1 -1
- data/test/jvm/macros_test.rb +67 -14
- data/test/jvm/main_method_test.rb +1 -1
- data/test/jvm/new_backend_test_helper.rb +100 -3
- data/{lib/mirah/jvm/types/bitescript_ext.rb → test/jvm/static_fields_test.rb} +22 -21
- data/test/mirrors/base_type_test.rb +4 -3
- data/test/mirrors/bytecode_mirror_test.rb +35 -15
- data/test/mirrors/generics_test.rb +14 -5
- data/test/mirrors/member_test.rb +2 -1
- data/test/mirrors/method_lookup_test.rb +18 -6
- data/test/mirrors/mirrors_test.rb +87 -20
- data/test/mirrors/simple_async_mirror_loader_test.rb +7 -3
- data/test/mirrors/simple_mirror_loader_test.rb +5 -5
- data/test/test_helper.rb +25 -1
- metadata +18 -78
- data/bin/mirahp +0 -27
- data/bin/mirahp.cmd +0 -16
- data/examples/Fib.class +0 -0
- data/javalib/mirah-bootstrap.jar +0 -0
- data/javalib/mirah-builtins.jar +0 -0
- data/javalib/mirah-compiler.jar +0 -0
- data/javalib/mirah-mirrors.jar +0 -0
- data/javalib/mirah-newast-transitional.jar +0 -0
- data/javalib/mirah-parser.jar +0 -0
- data/javalib/mirah-util.jar +0 -0
- data/lib/mirah/ast.rb +0 -43
- data/lib/mirah/ast/scope.rb +0 -262
- data/lib/mirah/commands/base.rb +0 -59
- data/lib/mirah/commands/compile.rb +0 -39
- data/lib/mirah/commands/parse.rb +0 -36
- data/lib/mirah/commands/run.rb +0 -78
- data/lib/mirah/generator.rb +0 -150
- data/lib/mirah/jvm/compiler.rb +0 -50
- data/lib/mirah/jvm/compiler/base.rb +0 -421
- data/lib/mirah/jvm/compiler/jvm_bytecode.rb +0 -1194
- data/lib/mirah/jvm/method_lookup.rb +0 -307
- data/lib/mirah/jvm/types.rb +0 -45
- data/lib/mirah/jvm/types/array_type.rb +0 -60
- data/lib/mirah/jvm/types/ast_ext.rb +0 -31
- data/lib/mirah/jvm/types/basic_types.rb +0 -41
- data/lib/mirah/jvm/types/block_type.rb +0 -15
- data/lib/mirah/jvm/types/boolean.rb +0 -70
- data/lib/mirah/jvm/types/enumerable.rb +0 -80
- data/lib/mirah/jvm/types/extensions.rb +0 -110
- data/lib/mirah/jvm/types/factory.rb +0 -830
- data/lib/mirah/jvm/types/floats.rb +0 -99
- data/lib/mirah/jvm/types/generic_type.rb +0 -72
- data/lib/mirah/jvm/types/implicit_nil_type.rb +0 -29
- data/lib/mirah/jvm/types/integers.rb +0 -131
- data/lib/mirah/jvm/types/interface_definition.rb +0 -20
- data/lib/mirah/jvm/types/intrinsics.rb +0 -385
- data/lib/mirah/jvm/types/literals.rb +0 -89
- data/lib/mirah/jvm/types/meta_type.rb +0 -54
- data/lib/mirah/jvm/types/methods.rb +0 -946
- data/lib/mirah/jvm/types/null_type.rb +0 -39
- data/lib/mirah/jvm/types/number.rb +0 -184
- data/lib/mirah/jvm/types/primitive_type.rb +0 -76
- data/lib/mirah/jvm/types/source_mirror.rb +0 -274
- data/lib/mirah/jvm/types/type.rb +0 -311
- data/lib/mirah/jvm/types/type_definition.rb +0 -72
- data/lib/mirah/jvm/types/void_type.rb +0 -19
- data/lib/mirah/util/compilation_state.rb +0 -60
- data/test/core/commands_test.rb +0 -89
- data/test/core/generator_test.rb +0 -26
- data/test/fixtures/org/foo/LowerCaseInnerClass$inner.class +0 -0
- data/test/fixtures/org/foo/LowerCaseInnerClass.class +0 -0
- data/test/jvm/bytecode_test_helper.rb +0 -193
- data/test/jvm/factory_test.rb +0 -28
- data/test/jvm/java_typer_test.rb +0 -283
|
@@ -1,1194 +0,0 @@
|
|
|
1
|
-
require 'mirah/jvm/types/ast_ext'
|
|
2
|
-
|
|
3
|
-
module Mirah
|
|
4
|
-
module JVM
|
|
5
|
-
module Compiler
|
|
6
|
-
class JVMBytecode < Base
|
|
7
|
-
java_import java.lang.System
|
|
8
|
-
java_import java.io.PrintStream
|
|
9
|
-
include Mirah::JVM::MethodLookup
|
|
10
|
-
include Mirah::Logging::Logged
|
|
11
|
-
Types = Mirah::JVM::Types
|
|
12
|
-
java_import 'mirah.lang.ast.Node'
|
|
13
|
-
java_import 'mirah.lang.ast.Array'
|
|
14
|
-
java_import 'mirah.lang.ast.Annotation'
|
|
15
|
-
java_import 'mirah.lang.ast.MethodDefinition'
|
|
16
|
-
java_import 'mirah.lang.ast.ConstructorDefinition'
|
|
17
|
-
java_import 'mirah.lang.ast.Ensure'
|
|
18
|
-
java_import 'mirah.lang.ast.Call'
|
|
19
|
-
java_import 'mirah.lang.ast.Loop'
|
|
20
|
-
java_import 'mirah.lang.ast.FunctionalCall'
|
|
21
|
-
java_import 'mirah.lang.ast.Super'
|
|
22
|
-
java_import 'mirah.lang.ast.ZSuper'
|
|
23
|
-
java_import 'mirah.lang.ast.ImplicitSelf'
|
|
24
|
-
java_import 'mirah.lang.ast.NodeList'
|
|
25
|
-
java_import 'mirah.lang.ast.SimpleString'
|
|
26
|
-
java_import 'mirah.lang.ast.StringConcat'
|
|
27
|
-
java_import 'org.mirah.typer.TypeFuture'
|
|
28
|
-
|
|
29
|
-
class FunctionalCall
|
|
30
|
-
attr_accessor :target
|
|
31
|
-
end
|
|
32
|
-
class Super
|
|
33
|
-
attr_accessor :target, :name
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
class << self
|
|
37
|
-
attr_accessor :verbose
|
|
38
|
-
|
|
39
|
-
def classname_from_filename(filename)
|
|
40
|
-
basename = File.basename(filename).sub(/\.(duby|mirah)$/, '')
|
|
41
|
-
basename.split(/[_-]/).map{|x| x[0...1].upcase + x[1..-1]}.join
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def initialize(config, scoper, typer)
|
|
46
|
-
super
|
|
47
|
-
@jump_scope = []
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def logger_name
|
|
51
|
-
"org.mirah.ruby.JVM.Compiler.JVMBytecode"
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def file_builder(filename)
|
|
55
|
-
builder = BiteScript::FileBuilder.new(filename)
|
|
56
|
-
builder.to_widen do |_a, _b|
|
|
57
|
-
a = @typer.type_system.get_type(_a.tr('/', '.'))
|
|
58
|
-
b = @typer.type_system.get_type(_b.tr('/', '.'))
|
|
59
|
-
a_ancestors = []
|
|
60
|
-
while a
|
|
61
|
-
a_ancestors << a.name
|
|
62
|
-
a = a.superclass
|
|
63
|
-
end
|
|
64
|
-
b_ancestors = []
|
|
65
|
-
while b
|
|
66
|
-
b_ancestors << b.name
|
|
67
|
-
b = b.superclass
|
|
68
|
-
end
|
|
69
|
-
intersection = (a_ancestors & b_ancestors)
|
|
70
|
-
if intersection.size == 0
|
|
71
|
-
puts "#{_a} => #{a}, #{_b} => #{b}"
|
|
72
|
-
puts "#{a_ancestors.inspect} & #{b_ancestors.inspect} = []"
|
|
73
|
-
'java/lang/Object'
|
|
74
|
-
else
|
|
75
|
-
intersection[0].gsub('.', '/')
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
@typer.type_system.define_types(builder)
|
|
79
|
-
builder
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def output_type
|
|
83
|
-
"classes"
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def push_jump_scope(node)
|
|
87
|
-
raise "Not a node" unless Node === node
|
|
88
|
-
begin
|
|
89
|
-
@jump_scope << node
|
|
90
|
-
yield
|
|
91
|
-
ensure
|
|
92
|
-
@jump_scope.pop
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def find_ensures(before)
|
|
97
|
-
found = []
|
|
98
|
-
@jump_scope.reverse_each do |scope|
|
|
99
|
-
if Ensure === scope
|
|
100
|
-
found << scope
|
|
101
|
-
end
|
|
102
|
-
break if before === scope
|
|
103
|
-
end
|
|
104
|
-
found
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
def begin_main
|
|
108
|
-
# declare argv variable
|
|
109
|
-
@method.local('argv', @typer.type_system.type(nil, 'string', true))
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
def finish_main
|
|
113
|
-
@method.returnvoid
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
def prepare_binding(node)
|
|
117
|
-
scope = introduced_scope(node)
|
|
118
|
-
if scope.has_binding?
|
|
119
|
-
type = scope.binding_type
|
|
120
|
-
@binding = @bindings[type]
|
|
121
|
-
@method.new type
|
|
122
|
-
@method.dup
|
|
123
|
-
@method.invokespecial type, "<init>", [@method.void]
|
|
124
|
-
if node.respond_to? :arguments
|
|
125
|
-
node.arguments.required.each do |param|
|
|
126
|
-
name = param.name.identifier
|
|
127
|
-
param_type = inferred_type(param)
|
|
128
|
-
if scope.captured?(param.name.identifier)
|
|
129
|
-
@method.dup
|
|
130
|
-
param_type.load(@method, @method.local(name, param_type))
|
|
131
|
-
@method.putfield(type, name, param_type)
|
|
132
|
-
end
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
type.store(@method, @method.local('$binding', type))
|
|
136
|
-
end
|
|
137
|
-
begin
|
|
138
|
-
yield
|
|
139
|
-
ensure
|
|
140
|
-
if scope.has_binding?
|
|
141
|
-
@binding.stop
|
|
142
|
-
@binding = nil
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
def visitMethodDefinition(node, expression)
|
|
148
|
-
push_jump_scope(node) do
|
|
149
|
-
base_define_method(node) do |method, arg_types|
|
|
150
|
-
return if @class.interface? || node.annotated_abstract?
|
|
151
|
-
is_static = self.static || node.kind_of?(StaticMethodDefinition)
|
|
152
|
-
|
|
153
|
-
log "Starting new #{is_static ? 'static ' : ''}method #{node.name.identifier}(#{arg_types})"
|
|
154
|
-
args = visit(node.arguments, true)
|
|
155
|
-
method_body(method, args, node, inferred_type(node).returnType)
|
|
156
|
-
log "Method #{node.name.identifier}(#{arg_types}) complete!"
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
def define_optarg_chain(name, arg, return_type,
|
|
162
|
-
args_for_opt, arg_types_for_opt)
|
|
163
|
-
# declare all args so they get their values
|
|
164
|
-
@method.aload(0) unless @static
|
|
165
|
-
args_for_opt.each do |req_arg|
|
|
166
|
-
inferred_type(req_arg).load(@method, @method.local(req_arg.name.identifier, inferred_type(req_arg)))
|
|
167
|
-
end
|
|
168
|
-
visit(arg.value, true)
|
|
169
|
-
|
|
170
|
-
# invoke the next one in the chain
|
|
171
|
-
if @static
|
|
172
|
-
@method.invokestatic(@class, name.to_s, [return_type] + arg_types_for_opt + [inferred_type(arg)])
|
|
173
|
-
else
|
|
174
|
-
@method.invokevirtual(@class, name.to_s, [return_type] + arg_types_for_opt + [inferred_type(arg)])
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
return_type.return(@method)
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
def visitConstructorDefinition(node, expression)
|
|
181
|
-
push_jump_scope(node) do
|
|
182
|
-
super(node, true) do |method, args|
|
|
183
|
-
method_body(method, args, node, @typer.type_system.type(nil, 'void')) do
|
|
184
|
-
method.aload 0
|
|
185
|
-
scope = introduced_scope(node)
|
|
186
|
-
if node.body.size > 0 &&
|
|
187
|
-
(node.body(0).kind_of?(Super) || node.body(0).kind_of?(ZSuper))
|
|
188
|
-
super_node = node.body(0)
|
|
189
|
-
delegate_class = @type.superclass
|
|
190
|
-
delegate_types = []
|
|
191
|
-
if super_node.kind_of?(ZSuper)
|
|
192
|
-
[node.arguments.required,
|
|
193
|
-
node.arguments.optional,
|
|
194
|
-
node.arguments.required2
|
|
195
|
-
].each do |args|
|
|
196
|
-
args.each do |arg|
|
|
197
|
-
arg_type = inferred_type(arg)
|
|
198
|
-
delegate_types << arg_type
|
|
199
|
-
local(scope, arg.name.identifier, arg_type)
|
|
200
|
-
end
|
|
201
|
-
end
|
|
202
|
-
else
|
|
203
|
-
super_node.parameters.each do |param|
|
|
204
|
-
param_type = inferred_type(param)
|
|
205
|
-
delegate_types << param_type
|
|
206
|
-
visit(param, true)
|
|
207
|
-
end
|
|
208
|
-
end
|
|
209
|
-
constructor = delegate_class.constructor(*delegate_types)
|
|
210
|
-
method.invokespecial(
|
|
211
|
-
delegate_class, "<init>",
|
|
212
|
-
[@method.void, *constructor.argument_types])
|
|
213
|
-
else
|
|
214
|
-
unless (node.body.size > 0 &&
|
|
215
|
-
node.body(0).kind_of?(FunctionalCall) &&
|
|
216
|
-
node.body(0).name.identifier == 'initialize')
|
|
217
|
-
method.invokespecial @class.superclass, "<init>", [@method.void]
|
|
218
|
-
end
|
|
219
|
-
end
|
|
220
|
-
end
|
|
221
|
-
end
|
|
222
|
-
end
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
def method_body(method, args, node, return_type)
|
|
226
|
-
body = node.body
|
|
227
|
-
with(:method => method,
|
|
228
|
-
:declared_locals => {}) do
|
|
229
|
-
|
|
230
|
-
method.start
|
|
231
|
-
|
|
232
|
-
scope = introduced_scope(node)
|
|
233
|
-
|
|
234
|
-
# declare all args so they get their values
|
|
235
|
-
if args
|
|
236
|
-
args.each {|arg| declare_local(scope, arg.name.identifier, inferred_type(arg))}
|
|
237
|
-
end
|
|
238
|
-
declare_locals(scope)
|
|
239
|
-
|
|
240
|
-
yield if block_given?
|
|
241
|
-
|
|
242
|
-
prepare_binding(node) do
|
|
243
|
-
expression = return_type.name != 'void'
|
|
244
|
-
if body
|
|
245
|
-
if expression
|
|
246
|
-
body_type = inferred_type(body)
|
|
247
|
-
unless return_type.assignableFrom(body_type)
|
|
248
|
-
error("Invalid return type #{body_type.name}, expected #{return_type.name}", body)
|
|
249
|
-
end
|
|
250
|
-
end
|
|
251
|
-
visit(body, expression)
|
|
252
|
-
convert_value inferred_type(body), return_type if expression
|
|
253
|
-
end
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
return_type.return(@method) unless return_type.name == ':unreachable'
|
|
257
|
-
|
|
258
|
-
@method.stop
|
|
259
|
-
end
|
|
260
|
-
end
|
|
261
|
-
|
|
262
|
-
def visitClosureDefinition(class_def, expression)
|
|
263
|
-
compiler = ClosureCompiler.new(@config, @file, @type, self, @scoper, @typer)
|
|
264
|
-
compiler.visitClassDefinition(class_def, expression)
|
|
265
|
-
end
|
|
266
|
-
|
|
267
|
-
def visitInterfaceDeclaration(class_def, expression)
|
|
268
|
-
visitClassDefinition(class_def, expression)
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
def visitIf(iff, expression)
|
|
272
|
-
elselabel = @method.label
|
|
273
|
-
donelabel = @method.label
|
|
274
|
-
|
|
275
|
-
# this is ugly...need a better way to abstract the idea of compiling a
|
|
276
|
-
# conditional branch while still fitting into JVM opcodes
|
|
277
|
-
predicate = iff.condition
|
|
278
|
-
body = iff.body
|
|
279
|
-
elseBody = iff.elseBody
|
|
280
|
-
if body.is_a?(NodeList) && body.size == 0
|
|
281
|
-
body = nil
|
|
282
|
-
end
|
|
283
|
-
if elseBody.is_a?(NodeList) && elseBody.size == 0
|
|
284
|
-
elseBody = nil
|
|
285
|
-
end
|
|
286
|
-
if body || expression
|
|
287
|
-
jump_if_not(predicate, elselabel)
|
|
288
|
-
|
|
289
|
-
if body
|
|
290
|
-
visit(body, expression)
|
|
291
|
-
elsif expression
|
|
292
|
-
inferred_type(iff).init_value(@method)
|
|
293
|
-
end
|
|
294
|
-
|
|
295
|
-
@method.goto(donelabel)
|
|
296
|
-
else
|
|
297
|
-
jump_if(predicate, donelabel)
|
|
298
|
-
end
|
|
299
|
-
|
|
300
|
-
elselabel.set!
|
|
301
|
-
|
|
302
|
-
if elseBody
|
|
303
|
-
visit(elseBody, expression)
|
|
304
|
-
elsif expression
|
|
305
|
-
inferred_type(iff).init_value(@method)
|
|
306
|
-
end
|
|
307
|
-
|
|
308
|
-
donelabel.set!
|
|
309
|
-
end
|
|
310
|
-
|
|
311
|
-
def visitLoop(loop, expression)
|
|
312
|
-
push_jump_scope(loop) do
|
|
313
|
-
with(:break_label => @method.label,
|
|
314
|
-
:redo_label => @method.label,
|
|
315
|
-
:next_label => @method.label) do
|
|
316
|
-
predicate = loop.condition
|
|
317
|
-
|
|
318
|
-
visit(loop.init, false)
|
|
319
|
-
|
|
320
|
-
pre_label = @redo_label
|
|
321
|
-
|
|
322
|
-
unless loop.skipFirstCheck
|
|
323
|
-
@next_label.set! unless loop.post_size > 0
|
|
324
|
-
if loop.negative
|
|
325
|
-
# if condition, exit
|
|
326
|
-
jump_if(predicate, @break_label)
|
|
327
|
-
else
|
|
328
|
-
# if not condition, exit
|
|
329
|
-
jump_if_not(predicate, @break_label)
|
|
330
|
-
end
|
|
331
|
-
end
|
|
332
|
-
|
|
333
|
-
if loop.pre_size > 0
|
|
334
|
-
pre_label = method.label
|
|
335
|
-
pre_label.set!
|
|
336
|
-
visit(loop.pre, false)
|
|
337
|
-
end
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
@redo_label.set!
|
|
341
|
-
visit(loop.body, false) if loop.body
|
|
342
|
-
|
|
343
|
-
if loop.skipFirstCheck || loop.post_size > 0
|
|
344
|
-
@next_label.set!
|
|
345
|
-
visit(loop.post, false)
|
|
346
|
-
if loop.negative
|
|
347
|
-
# if not condition, continue
|
|
348
|
-
jump_if_not(predicate, pre_label)
|
|
349
|
-
else
|
|
350
|
-
# if condition, continue
|
|
351
|
-
jump_if(predicate, pre_label)
|
|
352
|
-
end
|
|
353
|
-
else
|
|
354
|
-
@method.goto(@next_label)
|
|
355
|
-
end
|
|
356
|
-
|
|
357
|
-
@break_label.set!
|
|
358
|
-
|
|
359
|
-
# loops always evaluate to null
|
|
360
|
-
@method.aconst_null if expression
|
|
361
|
-
end
|
|
362
|
-
end
|
|
363
|
-
end
|
|
364
|
-
|
|
365
|
-
def visitBreak(node, expression)
|
|
366
|
-
error("break outside of loop", node) unless @break_label
|
|
367
|
-
handle_ensures(find_ensures(Loop))
|
|
368
|
-
set_position node.position
|
|
369
|
-
@method.goto(@break_label)
|
|
370
|
-
end
|
|
371
|
-
|
|
372
|
-
def visitNext(node, expression)
|
|
373
|
-
error("next outside of loop", node) unless @next_label
|
|
374
|
-
handle_ensures(find_ensures(Loop))
|
|
375
|
-
set_position node.position
|
|
376
|
-
@method.goto(@next_label)
|
|
377
|
-
end
|
|
378
|
-
|
|
379
|
-
def visitRedo(node, expression)
|
|
380
|
-
error("redo outside of loop", node) unless @redo_label
|
|
381
|
-
handle_ensures(find_ensures(Loop))
|
|
382
|
-
set_position node.position
|
|
383
|
-
@method.goto(@redo_label)
|
|
384
|
-
end
|
|
385
|
-
|
|
386
|
-
def jump_if(predicate, target)
|
|
387
|
-
type = inferred_type(predicate)
|
|
388
|
-
if type.primitive?
|
|
389
|
-
raise "Expected boolean, found #{type}" unless type.name == 'boolean'
|
|
390
|
-
end
|
|
391
|
-
if Call === predicate
|
|
392
|
-
method = extract_method(predicate)
|
|
393
|
-
if method.respond_to? :jump_if
|
|
394
|
-
method.jump_if(self, predicate, target)
|
|
395
|
-
return
|
|
396
|
-
end
|
|
397
|
-
end
|
|
398
|
-
visit(predicate, true)
|
|
399
|
-
if type.primitive?
|
|
400
|
-
@method.ifne(target)
|
|
401
|
-
else
|
|
402
|
-
@method.ifnonnull(target)
|
|
403
|
-
end
|
|
404
|
-
end
|
|
405
|
-
|
|
406
|
-
def jump_if_not(predicate, target)
|
|
407
|
-
type = inferred_type(predicate)
|
|
408
|
-
if type.primitive?
|
|
409
|
-
raise "Expected boolean, found #{type}" unless type.name == 'boolean'
|
|
410
|
-
end
|
|
411
|
-
if Call === predicate
|
|
412
|
-
method = extract_method(predicate)
|
|
413
|
-
if method.respond_to? :jump_if_not
|
|
414
|
-
method.jump_if_not(self, predicate, target)
|
|
415
|
-
return
|
|
416
|
-
end
|
|
417
|
-
end
|
|
418
|
-
visit(predicate, true)
|
|
419
|
-
if type.primitive?
|
|
420
|
-
@method.ifeq(target)
|
|
421
|
-
else
|
|
422
|
-
@method.ifnull(target)
|
|
423
|
-
end
|
|
424
|
-
end
|
|
425
|
-
|
|
426
|
-
def extract_method(call)
|
|
427
|
-
target = inferred_type(call.target)
|
|
428
|
-
params = call.parameters.map do |param|
|
|
429
|
-
inferred_type(param)
|
|
430
|
-
end
|
|
431
|
-
target.get_method(call.name.identifier, params)
|
|
432
|
-
end
|
|
433
|
-
|
|
434
|
-
def visitAttrAssign(call, expression)
|
|
435
|
-
target = inferred_type(call.target)
|
|
436
|
-
value_type = inferred_type(call.value)
|
|
437
|
-
setter = "#{call.name.identifier}_set"
|
|
438
|
-
method = target.get_method(setter, [value_type])
|
|
439
|
-
if method
|
|
440
|
-
method.call(self, call, expression, [call.value])
|
|
441
|
-
else
|
|
442
|
-
target = inferred_type(call.target)
|
|
443
|
-
raise "Missing method #{target.full_name}.#{setter}(#{value_type.full_name})"
|
|
444
|
-
end
|
|
445
|
-
end
|
|
446
|
-
|
|
447
|
-
def visitCall(call, expression)
|
|
448
|
-
method = extract_method(call)
|
|
449
|
-
if method
|
|
450
|
-
method.call(self, call, expression)
|
|
451
|
-
else
|
|
452
|
-
params = call.parameters.map do |param|
|
|
453
|
-
inferred_type(param)
|
|
454
|
-
end
|
|
455
|
-
target = inferred_type(call.target)
|
|
456
|
-
raise "Missing method #{target}.#{call.name.identifier}(#{params.join ', '})"
|
|
457
|
-
end
|
|
458
|
-
end
|
|
459
|
-
|
|
460
|
-
def visitVarargsArray type, parameters
|
|
461
|
-
@method.push_int parameters.size
|
|
462
|
-
@method.anewarray type.component_type
|
|
463
|
-
parameters.each.with_index do |value, i|
|
|
464
|
-
@method.dup
|
|
465
|
-
@method.push_int i
|
|
466
|
-
visit(value, true)
|
|
467
|
-
convert_value inferred_type(value), type.component_type
|
|
468
|
-
@method.aastore
|
|
469
|
-
end
|
|
470
|
-
end
|
|
471
|
-
|
|
472
|
-
def visitFunctionalCall(fcall, expression)
|
|
473
|
-
scope = get_scope(fcall)
|
|
474
|
-
type = get_scope(fcall).self_type.resolve
|
|
475
|
-
type = type.meta if (@static && type == @type)
|
|
476
|
-
fcall.target = ImplicitSelf.new
|
|
477
|
-
fcall.target.parent = fcall
|
|
478
|
-
@typer.infer(fcall.target)
|
|
479
|
-
|
|
480
|
-
params = fcall.parameters.map do |param|
|
|
481
|
-
inferred_type(param)
|
|
482
|
-
end
|
|
483
|
-
name = fcall.name.identifier
|
|
484
|
-
chained_constructor = false
|
|
485
|
-
if name == 'initialize'
|
|
486
|
-
if scope.context.kind_of?(ConstructorDefinition) &&
|
|
487
|
-
scope.context.body(0) == fcall
|
|
488
|
-
name = '<init>'
|
|
489
|
-
chained_constructor = true
|
|
490
|
-
end
|
|
491
|
-
end
|
|
492
|
-
|
|
493
|
-
method = type.get_method(name, params)
|
|
494
|
-
unless method
|
|
495
|
-
target = static ? @class.name : 'self'
|
|
496
|
-
|
|
497
|
-
raise NameError, "No method %s.%s(%s)" %
|
|
498
|
-
[target, fcall.name.identifier, params.join(', ')]
|
|
499
|
-
end
|
|
500
|
-
if chained_constructor
|
|
501
|
-
method.call(self, fcall, expression, nil, true)
|
|
502
|
-
else
|
|
503
|
-
method.call(self, fcall, expression)
|
|
504
|
-
end
|
|
505
|
-
end
|
|
506
|
-
|
|
507
|
-
def visitSuper(sup, expression)
|
|
508
|
-
mdef = sup.findAncestor(MethodDefinition.java_class)
|
|
509
|
-
# FIXME Horrible hack
|
|
510
|
-
return if mdef.kind_of?(ConstructorDefinition)
|
|
511
|
-
type = @type.superclass
|
|
512
|
-
super_type = @typer.type_system.getSuperClass(get_scope(sup).self_type)
|
|
513
|
-
@typer.infer(sup.target)
|
|
514
|
-
|
|
515
|
-
sup.name = mdef.name.identifier
|
|
516
|
-
|
|
517
|
-
# TODO ZSuper
|
|
518
|
-
params = sup.parameters.map do |param|
|
|
519
|
-
inferred_type(param)
|
|
520
|
-
end
|
|
521
|
-
method = type.get_method(sup.name, params)
|
|
522
|
-
unless method
|
|
523
|
-
raise NameError, "No method %s.%s(%s)" %
|
|
524
|
-
[type, sup.name, params.join(', ')]
|
|
525
|
-
end
|
|
526
|
-
method.call_special(self, ImplicitSelf.new, type, sup.parameters, expression)
|
|
527
|
-
end
|
|
528
|
-
|
|
529
|
-
def visitCast(fcall, expression)
|
|
530
|
-
# casting operation, not a call
|
|
531
|
-
castee = fcall.value
|
|
532
|
-
|
|
533
|
-
# TODO move errors to inference phase
|
|
534
|
-
source_type_name = inferred_type(castee).name
|
|
535
|
-
target_type_name = inferred_type(fcall).name
|
|
536
|
-
if inferred_type(castee).primitive?
|
|
537
|
-
if inferred_type(fcall).primitive?
|
|
538
|
-
if source_type_name == 'boolean' && target_type_name != "boolean"
|
|
539
|
-
raise TypeError.new "not a boolean type: #{inferred_type(castee)}"
|
|
540
|
-
end
|
|
541
|
-
# ok
|
|
542
|
-
primitive = true
|
|
543
|
-
else
|
|
544
|
-
raise TypeError.new "Cannot cast #{inferred_type(castee)} to #{inferred_type(fcall)}: not a reference type."
|
|
545
|
-
end
|
|
546
|
-
elsif inferred_type(fcall).primitive?
|
|
547
|
-
raise TypeError.new "not a primitive type: #{inferred_type(castee)}"
|
|
548
|
-
else
|
|
549
|
-
# ok
|
|
550
|
-
primitive = false
|
|
551
|
-
end
|
|
552
|
-
|
|
553
|
-
visit(castee, expression)
|
|
554
|
-
if expression
|
|
555
|
-
if primitive
|
|
556
|
-
source_type_name = 'int' if %w[byte short char].include? source_type_name
|
|
557
|
-
if (source_type_name != 'int') && (%w[byte short char].include? target_type_name)
|
|
558
|
-
target_type_name = 'int'
|
|
559
|
-
end
|
|
560
|
-
|
|
561
|
-
if source_type_name != target_type_name
|
|
562
|
-
if RUBY_VERSION == "1.9"
|
|
563
|
-
@method.send "#{source_type_name[0]}2#{target_type_name[0]}"
|
|
564
|
-
else
|
|
565
|
-
@method.send "#{source_type_name[0].chr}2#{target_type_name[0].chr}"
|
|
566
|
-
end
|
|
567
|
-
end
|
|
568
|
-
else
|
|
569
|
-
if (source_type_name != target_type_name ||
|
|
570
|
-
inferred_type(castee).array? != inferred_type(fcall).array?)
|
|
571
|
-
@method.checkcast inferred_type(fcall)
|
|
572
|
-
end
|
|
573
|
-
end
|
|
574
|
-
end
|
|
575
|
-
end
|
|
576
|
-
|
|
577
|
-
def visitNodeList(body, expression)
|
|
578
|
-
# last element is an expression only if the body is an expression
|
|
579
|
-
super(body, expression) do |last|
|
|
580
|
-
if last
|
|
581
|
-
visit(last, expression)
|
|
582
|
-
elsif expression
|
|
583
|
-
inferred_type(body).init_value(method)
|
|
584
|
-
end
|
|
585
|
-
end
|
|
586
|
-
end
|
|
587
|
-
|
|
588
|
-
def declared_locals
|
|
589
|
-
@declared_locals ||= {}
|
|
590
|
-
end
|
|
591
|
-
|
|
592
|
-
def annotate(builder, annotations)
|
|
593
|
-
annotations.each do |annotation|
|
|
594
|
-
next if annotation.type.typeref.name.start_with?('org.mirah.jvm.')
|
|
595
|
-
type = inferred_type(annotation)
|
|
596
|
-
mirror = type.jvm_type
|
|
597
|
-
if mirror.respond_to?(:getDeclaredAnnotation)
|
|
598
|
-
retention = mirror.getDeclaredAnnotation('java.lang.annotation.Retention')
|
|
599
|
-
else
|
|
600
|
-
raise "Unsupported annotation #{mirror} (#{mirror.class})"
|
|
601
|
-
end
|
|
602
|
-
next if retention && retention.value.name == 'SOURCE'
|
|
603
|
-
runtime_retention = (retention && retention.value.name == 'RUNTIME')
|
|
604
|
-
builder.annotate(mirror, runtime_retention) do |visitor|
|
|
605
|
-
annotation.values.each do |entry|
|
|
606
|
-
annotation_value(get_scope(annotation), type, visitor,
|
|
607
|
-
entry.key.identifier, entry.value)
|
|
608
|
-
end
|
|
609
|
-
end
|
|
610
|
-
end
|
|
611
|
-
end
|
|
612
|
-
|
|
613
|
-
def annotation_value(scope, type, builder, name, value)
|
|
614
|
-
if name
|
|
615
|
-
value_type = type.unmeta.java_method(name).return_type
|
|
616
|
-
if value_type.array?
|
|
617
|
-
unless value.kind_of?(Array)
|
|
618
|
-
raise "#{type.name}.#{name} should be an Array, got #{value.class}"
|
|
619
|
-
end
|
|
620
|
-
builder.array(name) do |child|
|
|
621
|
-
value.values.each do |item|
|
|
622
|
-
annotation_value(scope, value_type.component_type, child, nil, item)
|
|
623
|
-
end
|
|
624
|
-
end
|
|
625
|
-
return
|
|
626
|
-
end
|
|
627
|
-
else
|
|
628
|
-
value_type = type
|
|
629
|
-
end
|
|
630
|
-
primitive_classes = {
|
|
631
|
-
'Z' => java.lang.Boolean,
|
|
632
|
-
'B' => java.lang.Byte,
|
|
633
|
-
'C' => java.lang.Character,
|
|
634
|
-
'S' => java.lang.Short,
|
|
635
|
-
'I' => java.lang.Integer,
|
|
636
|
-
'J' => java.lang.Long,
|
|
637
|
-
'F' => java.lang.Float,
|
|
638
|
-
'D' => java.lang.Double,
|
|
639
|
-
}
|
|
640
|
-
descriptor = BiteScript::Signature::class_id(value_type)
|
|
641
|
-
case descriptor
|
|
642
|
-
when 'Ljava/lang/String;'
|
|
643
|
-
string_value = if value.kind_of?(StringConcat)
|
|
644
|
-
value.strings.map {|x| x.identifier}.join
|
|
645
|
-
else
|
|
646
|
-
value.identifier
|
|
647
|
-
end
|
|
648
|
-
builder.visit(name, string_value)
|
|
649
|
-
when 'Ljava/lang/Class;'
|
|
650
|
-
mirror = @typer.type_system.type(scope, value.typeref.name)
|
|
651
|
-
klass = if value.typeref.isArray
|
|
652
|
-
BiteScript::ASM::Type.get("[#{mirror.type.descriptor}")
|
|
653
|
-
else
|
|
654
|
-
mirror
|
|
655
|
-
end
|
|
656
|
-
builder.visit(name, klass)
|
|
657
|
-
when *primitive_classes.keys
|
|
658
|
-
klass = primitive_classes[descriptor]
|
|
659
|
-
builder.visit(name, klass.new(value.value))
|
|
660
|
-
else
|
|
661
|
-
if value_type.jvm_type.enum?
|
|
662
|
-
builder.enum(name, value_type, value.identifier)
|
|
663
|
-
elsif value_type.jvm_type.annotation?
|
|
664
|
-
subtype = inferred_type(value)
|
|
665
|
-
mirror = subtype.jvm_type
|
|
666
|
-
builder.annotation(name, mirror) do |child|
|
|
667
|
-
value.values.each do |entry|
|
|
668
|
-
annotation_value(scope, subtype, child, entry.key.identifier, entry.value)
|
|
669
|
-
end
|
|
670
|
-
end
|
|
671
|
-
else
|
|
672
|
-
raise "Unsupported annotation #{descriptor} #{name} = #{value.class}"
|
|
673
|
-
end
|
|
674
|
-
end
|
|
675
|
-
end
|
|
676
|
-
|
|
677
|
-
def declared?(scope, name)
|
|
678
|
-
declared_locals.include?(scoped_local_name(name, scope))
|
|
679
|
-
end
|
|
680
|
-
|
|
681
|
-
def declare_local(scope, name, type)
|
|
682
|
-
# TODO confirm types are compatible
|
|
683
|
-
name = scoped_local_name(name, scope)
|
|
684
|
-
unless declared_locals[name]
|
|
685
|
-
declared_locals[name] = type
|
|
686
|
-
index = @method.local(name, type)
|
|
687
|
-
end
|
|
688
|
-
end
|
|
689
|
-
|
|
690
|
-
def declare_locals(scope)
|
|
691
|
-
scope.locals.each do |name|
|
|
692
|
-
unless scope.captured?(name) || declared?(scope, name)
|
|
693
|
-
type = scope.local_type(name)
|
|
694
|
-
type = type.resolve if type.kind_of?(TypeFuture)
|
|
695
|
-
declare_local(scope, name, type)
|
|
696
|
-
type.init_value(@method)
|
|
697
|
-
type.store(@method, @method.local(scoped_local_name(name, scope), type))
|
|
698
|
-
end
|
|
699
|
-
end
|
|
700
|
-
end
|
|
701
|
-
|
|
702
|
-
def get_binding(type)
|
|
703
|
-
@bindings[type]
|
|
704
|
-
end
|
|
705
|
-
|
|
706
|
-
def declared_captures(binding=nil)
|
|
707
|
-
@captured_locals[binding || @binding]
|
|
708
|
-
end
|
|
709
|
-
|
|
710
|
-
def visitLocalDeclaration(local, expression)
|
|
711
|
-
scope = get_scope(local)
|
|
712
|
-
if scope.has_binding? && scope.captured?(local.name.identifier)
|
|
713
|
-
captured_local_declare(scope, local.name.identifier, inferred_type(local))
|
|
714
|
-
end
|
|
715
|
-
end
|
|
716
|
-
|
|
717
|
-
def captured_local_declare(scope, name, type)
|
|
718
|
-
unless declared_captures[name]
|
|
719
|
-
declared_captures[name] = type
|
|
720
|
-
# default should be fine, but I don't think bitescript supports it.
|
|
721
|
-
@binding.protected_field(name, type)
|
|
722
|
-
end
|
|
723
|
-
end
|
|
724
|
-
|
|
725
|
-
def visitLocalAccess(local, expression)
|
|
726
|
-
if expression
|
|
727
|
-
set_position(local.position)
|
|
728
|
-
scope = get_scope(local)
|
|
729
|
-
if scope.has_binding? && scope.captured?(local.name.identifier)
|
|
730
|
-
captured_local(scope, local.name.identifier, inferred_type(local))
|
|
731
|
-
else
|
|
732
|
-
local(containing_scope(local), local.name.identifier, inferred_type(local))
|
|
733
|
-
end
|
|
734
|
-
end
|
|
735
|
-
end
|
|
736
|
-
|
|
737
|
-
def local(scope, name, type)
|
|
738
|
-
type.load(@method, @method.local(scoped_local_name(name, scope), type))
|
|
739
|
-
end
|
|
740
|
-
|
|
741
|
-
def captured_local(scope, name, type)
|
|
742
|
-
captured_local_declare(scope, name, type)
|
|
743
|
-
binding_reference
|
|
744
|
-
@method.getfield(scope.binding_type, name, type)
|
|
745
|
-
end
|
|
746
|
-
|
|
747
|
-
def visitLocalAssignment(local, expression)
|
|
748
|
-
scope = get_scope(local)
|
|
749
|
-
if scope.has_binding? && scope.captured?(local.name.identifier)
|
|
750
|
-
captured_local_assign(local, expression)
|
|
751
|
-
else
|
|
752
|
-
local_assign(local, expression)
|
|
753
|
-
end
|
|
754
|
-
end
|
|
755
|
-
|
|
756
|
-
def local_assign(local, expression)
|
|
757
|
-
name = local.name.identifier
|
|
758
|
-
type = inferred_type(local)
|
|
759
|
-
scope = containing_scope(local)
|
|
760
|
-
declare_local(scope, name, type)
|
|
761
|
-
|
|
762
|
-
visit(local.value, true)
|
|
763
|
-
convert_value inferred_type(local.value), type
|
|
764
|
-
|
|
765
|
-
# if expression, dup the value we're assigning
|
|
766
|
-
if expression
|
|
767
|
-
dup_value type
|
|
768
|
-
end
|
|
769
|
-
|
|
770
|
-
set_position(local.position)
|
|
771
|
-
type.store(@method, @method.local(scoped_local_name(name, scope), type))
|
|
772
|
-
end
|
|
773
|
-
|
|
774
|
-
def captured_local_assign(node, expression)
|
|
775
|
-
scope, name = containing_scope(node), node.name.identifier
|
|
776
|
-
type = get_scope(node).local_type(name).resolve
|
|
777
|
-
captured_local_declare(scope, name, type)
|
|
778
|
-
binding_reference
|
|
779
|
-
visit(node.value, true)
|
|
780
|
-
convert_value(inferred_type(node.value), type)
|
|
781
|
-
|
|
782
|
-
# if expression, dup the value we're assigning
|
|
783
|
-
if expression
|
|
784
|
-
dup_x2_value type
|
|
785
|
-
end
|
|
786
|
-
set_position(node.position)
|
|
787
|
-
@method.putfield(scope.binding_type, name, type)
|
|
788
|
-
end
|
|
789
|
-
|
|
790
|
-
def visitFieldAccess(field, expression)
|
|
791
|
-
return nil unless expression
|
|
792
|
-
name = field.name.identifier
|
|
793
|
-
|
|
794
|
-
real_type = declared_fields[name] || inferred_type(field)
|
|
795
|
-
declare_field(name, real_type, [], field.isStatic)
|
|
796
|
-
|
|
797
|
-
set_position(field.position)
|
|
798
|
-
# load self object unless static
|
|
799
|
-
method.aload 0 unless static || field.isStatic
|
|
800
|
-
|
|
801
|
-
if static || field.isStatic
|
|
802
|
-
@method.getstatic(@class, name, inferred_type(field))
|
|
803
|
-
else
|
|
804
|
-
@method.getfield(@class, name, inferred_type(field))
|
|
805
|
-
end
|
|
806
|
-
end
|
|
807
|
-
|
|
808
|
-
def declared_fields
|
|
809
|
-
@declared_fields ||= {}
|
|
810
|
-
@declared_fields[@class] ||= {}
|
|
811
|
-
end
|
|
812
|
-
|
|
813
|
-
def declare_field(name, type, annotations, static_field)
|
|
814
|
-
# TODO confirm types are compatible
|
|
815
|
-
unless declared_fields[name]
|
|
816
|
-
declared_fields[name] = type
|
|
817
|
-
field = if static || static_field
|
|
818
|
-
@class.private_static_field name, type
|
|
819
|
-
else
|
|
820
|
-
@class.private_field name, type
|
|
821
|
-
end
|
|
822
|
-
annotate(field, annotations)
|
|
823
|
-
end
|
|
824
|
-
end
|
|
825
|
-
|
|
826
|
-
def visitFieldDeclaration(decl, expression)
|
|
827
|
-
declare_field(decl.name.identifier, inferred_type(decl), decl.annotations, decl.isStatic)
|
|
828
|
-
end
|
|
829
|
-
|
|
830
|
-
def visitFieldAssign(field, expression)
|
|
831
|
-
name = field.name.identifier
|
|
832
|
-
|
|
833
|
-
real_type = declared_fields[name] || inferred_type(field)
|
|
834
|
-
|
|
835
|
-
declare_field(name, real_type, field.annotations, field.isStatic)
|
|
836
|
-
|
|
837
|
-
method.aload 0 unless static || field.isStatic
|
|
838
|
-
visit(field.value, true)
|
|
839
|
-
if expression
|
|
840
|
-
instruction = 'dup'
|
|
841
|
-
instruction << '2' if real_type.wide?
|
|
842
|
-
instruction << '_x1' unless static || field.isStatic
|
|
843
|
-
method.send instruction
|
|
844
|
-
end
|
|
845
|
-
set_position(field.position)
|
|
846
|
-
if static || field.isStatic
|
|
847
|
-
@method.putstatic(@class, name, real_type)
|
|
848
|
-
else
|
|
849
|
-
@method.putfield(@class, name, real_type)
|
|
850
|
-
end
|
|
851
|
-
end
|
|
852
|
-
|
|
853
|
-
def visitSimpleString(string, expression)
|
|
854
|
-
set_position(string.position)
|
|
855
|
-
@method.ldc(string.value) if expression
|
|
856
|
-
end
|
|
857
|
-
|
|
858
|
-
def visitStringConcat(strcat, expression)
|
|
859
|
-
set_position(strcat.position)
|
|
860
|
-
if expression
|
|
861
|
-
# could probably be more efficient with non-default constructor
|
|
862
|
-
builder_class = @typer.type_system.type(nil, 'java.lang.StringBuilder')
|
|
863
|
-
@method.new builder_class
|
|
864
|
-
@method.dup
|
|
865
|
-
@method.invokespecial builder_class, "<init>", [@method.void]
|
|
866
|
-
|
|
867
|
-
strcat.strings.each do |node|
|
|
868
|
-
visit(node, true)
|
|
869
|
-
method = find_method(builder_class, "append", [inferred_type(node)], nil, false)
|
|
870
|
-
if method
|
|
871
|
-
@method.invokevirtual builder_class, "append", [method.return_type, *method.argument_types]
|
|
872
|
-
else
|
|
873
|
-
log "Could not find a match for #{java::lang::StringBuilder}.append(#{inferred_type(node)})"
|
|
874
|
-
fail "Could not compile"
|
|
875
|
-
end
|
|
876
|
-
end
|
|
877
|
-
|
|
878
|
-
# convert to string
|
|
879
|
-
set_position(strcat.position)
|
|
880
|
-
@method.invokevirtual java::lang::StringBuilder.java_class, "toString", [@method.string]
|
|
881
|
-
else
|
|
882
|
-
strcat.strings.each do |node|
|
|
883
|
-
visit(node, false)
|
|
884
|
-
end
|
|
885
|
-
end
|
|
886
|
-
end
|
|
887
|
-
|
|
888
|
-
def visitStringEval(node, expression)
|
|
889
|
-
if expression
|
|
890
|
-
visit(node.value, true)
|
|
891
|
-
set_position(node.position)
|
|
892
|
-
inferred_type(node.value).box(@method) if inferred_type(node.value).primitive?
|
|
893
|
-
null = method.label
|
|
894
|
-
done = method.label
|
|
895
|
-
method.dup
|
|
896
|
-
method.ifnull(null)
|
|
897
|
-
@method.invokevirtual @method.object, "toString", [@method.string]
|
|
898
|
-
@method.goto(done)
|
|
899
|
-
null.set!
|
|
900
|
-
method.pop
|
|
901
|
-
method.ldc("null")
|
|
902
|
-
done.set!
|
|
903
|
-
else
|
|
904
|
-
visit(node.value, false)
|
|
905
|
-
end
|
|
906
|
-
end
|
|
907
|
-
|
|
908
|
-
def visitBoolean(node, expression)
|
|
909
|
-
if expression
|
|
910
|
-
set_position(node.position)
|
|
911
|
-
node.value ? @method.iconst_1 : @method.iconst_0
|
|
912
|
-
end
|
|
913
|
-
end
|
|
914
|
-
|
|
915
|
-
def visitRegex(node, expression)
|
|
916
|
-
# TODO: translate flags to Java-appropriate values
|
|
917
|
-
if node.strings_size == 1
|
|
918
|
-
visit(node.strings(0), expression)
|
|
919
|
-
else
|
|
920
|
-
visitStringConcat(node, expression)
|
|
921
|
-
end
|
|
922
|
-
if expression
|
|
923
|
-
set_position(node.position)
|
|
924
|
-
@method.invokestatic java::util::regex::Pattern, "compile", [java::util::regex::Pattern, @method.string]
|
|
925
|
-
end
|
|
926
|
-
end
|
|
927
|
-
|
|
928
|
-
def visitArray(node, expression)
|
|
929
|
-
set_position(node.position)
|
|
930
|
-
if expression
|
|
931
|
-
# create basic arraylist
|
|
932
|
-
@method.new java::util::ArrayList
|
|
933
|
-
@method.dup
|
|
934
|
-
@method.ldc_int node.values_size
|
|
935
|
-
@method.invokespecial java::util::ArrayList, "<init>", [@method.void, @method.int]
|
|
936
|
-
|
|
937
|
-
# elements, as expressions
|
|
938
|
-
# TODO: ensure they're all reference types!
|
|
939
|
-
node.values.each do |n|
|
|
940
|
-
@method.dup
|
|
941
|
-
visit(n, true)
|
|
942
|
-
# TODO this feels like it should be in the node.compile itself
|
|
943
|
-
if inferred_type(n).primitive?
|
|
944
|
-
inferred_type(n).box(@method)
|
|
945
|
-
end
|
|
946
|
-
@method.invokeinterface java::util::List, "add", [@method.boolean, @method.object]
|
|
947
|
-
@method.pop
|
|
948
|
-
end
|
|
949
|
-
else
|
|
950
|
-
# elements, as non-expressions
|
|
951
|
-
# TODO: ensure they're all reference types!
|
|
952
|
-
node.values.each do |n|
|
|
953
|
-
visit(n, true)
|
|
954
|
-
# TODO this feels like it should be in the node.compile itself
|
|
955
|
-
if inferred_type(n).primitive?
|
|
956
|
-
inferred_type(n).box(@method)
|
|
957
|
-
end
|
|
958
|
-
end
|
|
959
|
-
end
|
|
960
|
-
end
|
|
961
|
-
|
|
962
|
-
def visitHash(node, expression)
|
|
963
|
-
set_position(node.position)
|
|
964
|
-
if expression
|
|
965
|
-
# create basic arraylist
|
|
966
|
-
@method.new java::util::HashMap
|
|
967
|
-
@method.dup
|
|
968
|
-
@method.ldc_int [node.size / 0.75, 16].max.to_i
|
|
969
|
-
@method.invokespecial java::util::HashMap, "<init>", [@method.void, @method.int]
|
|
970
|
-
|
|
971
|
-
node.each do |e|
|
|
972
|
-
@method.dup
|
|
973
|
-
[e.key, e.value].each do |n|
|
|
974
|
-
visit(n, true)
|
|
975
|
-
# TODO this feels like it should be in the node.compile itself
|
|
976
|
-
if inferred_type(n).primitive?
|
|
977
|
-
inferred_type(n).box(@method)
|
|
978
|
-
end
|
|
979
|
-
end
|
|
980
|
-
@method.invokeinterface java::util::Map, "put", [@method.object, @method.object, @method.object]
|
|
981
|
-
@method.pop
|
|
982
|
-
end
|
|
983
|
-
else
|
|
984
|
-
# elements, as non-expressions
|
|
985
|
-
node.each do |n|
|
|
986
|
-
visit(n.key, false)
|
|
987
|
-
visit(n.value, false)
|
|
988
|
-
end
|
|
989
|
-
end
|
|
990
|
-
end
|
|
991
|
-
|
|
992
|
-
def visitNot(node, expression)
|
|
993
|
-
visit(node.value, expression)
|
|
994
|
-
if expression
|
|
995
|
-
set_position(node.position)
|
|
996
|
-
type = inferred_type(node.value)
|
|
997
|
-
done = @method.label
|
|
998
|
-
else_label = @method.label
|
|
999
|
-
if type.primitive?
|
|
1000
|
-
@method.ifeq else_label
|
|
1001
|
-
else
|
|
1002
|
-
@method.ifnull else_label
|
|
1003
|
-
end
|
|
1004
|
-
@method.iconst_0
|
|
1005
|
-
@method.goto done
|
|
1006
|
-
else_label.set!
|
|
1007
|
-
@method.iconst_1
|
|
1008
|
-
done.set!
|
|
1009
|
-
end
|
|
1010
|
-
end
|
|
1011
|
-
|
|
1012
|
-
def visitNull(node, expression)
|
|
1013
|
-
if expression
|
|
1014
|
-
set_position(node.position)
|
|
1015
|
-
@method.aconst_null
|
|
1016
|
-
end
|
|
1017
|
-
end
|
|
1018
|
-
|
|
1019
|
-
def visitImplicitNil(node, expression)
|
|
1020
|
-
visitNull(node, expression)
|
|
1021
|
-
end
|
|
1022
|
-
|
|
1023
|
-
def visitBindingReference(node, expression)
|
|
1024
|
-
binding_reference
|
|
1025
|
-
end
|
|
1026
|
-
|
|
1027
|
-
def binding_reference
|
|
1028
|
-
@method.aload(@method.local('$binding'))
|
|
1029
|
-
end
|
|
1030
|
-
|
|
1031
|
-
def real_self
|
|
1032
|
-
method.aload(0)
|
|
1033
|
-
end
|
|
1034
|
-
|
|
1035
|
-
def set_position(position)
|
|
1036
|
-
# TODO support positions from multiple files
|
|
1037
|
-
@method.line(position.start_line) if @method && position
|
|
1038
|
-
end
|
|
1039
|
-
|
|
1040
|
-
def print(print_node)
|
|
1041
|
-
@method.getstatic System, "out", PrintStream
|
|
1042
|
-
print_node.parameters.each {|param| visit(param, true)}
|
|
1043
|
-
params = print_node.parameters.map {|param| inferred_type(param).jvm_type}
|
|
1044
|
-
method_name = print_node.println ? "println" : "print"
|
|
1045
|
-
method = find_method(PrintStream.java_class, method_name, params, nil, false)
|
|
1046
|
-
if (method)
|
|
1047
|
-
@method.invokevirtual(
|
|
1048
|
-
PrintStream,
|
|
1049
|
-
method_name,
|
|
1050
|
-
[method.return_type, *method.parameter_types])
|
|
1051
|
-
else
|
|
1052
|
-
log "Could not find a match for #{PrintStream}.#{method_name}(#{params})"
|
|
1053
|
-
fail "Could not compile"
|
|
1054
|
-
end
|
|
1055
|
-
end
|
|
1056
|
-
|
|
1057
|
-
def visitReturn(return_node, expression)
|
|
1058
|
-
visit(return_node.value, true) if return_node.value
|
|
1059
|
-
handle_ensures(find_ensures(MethodDefinition))
|
|
1060
|
-
set_position return_node.position
|
|
1061
|
-
inferred_type(return_node).return(@method)
|
|
1062
|
-
end
|
|
1063
|
-
|
|
1064
|
-
def visitRaise(node, expression)
|
|
1065
|
-
visit(node.args(0), true)
|
|
1066
|
-
set_position(node.position)
|
|
1067
|
-
@method.athrow
|
|
1068
|
-
end
|
|
1069
|
-
|
|
1070
|
-
def visitRescue(rescue_node, expression)
|
|
1071
|
-
start = @method.label.set!
|
|
1072
|
-
body_end = @method.label
|
|
1073
|
-
done = @method.label
|
|
1074
|
-
no_else_clauses = rescue_node.else_clause.size == 0
|
|
1075
|
-
|
|
1076
|
-
visit(rescue_node.body, expression && no_else_clauses)
|
|
1077
|
-
body_end.set!
|
|
1078
|
-
|
|
1079
|
-
visit(rescue_node.else_clause, expression) unless no_else_clauses
|
|
1080
|
-
return if start.label.offset == body_end.label.offset
|
|
1081
|
-
|
|
1082
|
-
@method.goto(done)
|
|
1083
|
-
rescue_node.clauses.each do |clause|
|
|
1084
|
-
target = @method.label.set!
|
|
1085
|
-
if clause.name
|
|
1086
|
-
types = clause.types.map {|t| inferred_type(t)}
|
|
1087
|
-
widened_type = types.inject {|a, b| a.widen(b)}
|
|
1088
|
-
@method.astore(declare_local(introduced_scope(clause), clause.name.identifier, widened_type))
|
|
1089
|
-
else
|
|
1090
|
-
@method.pop
|
|
1091
|
-
end
|
|
1092
|
-
declare_locals(introduced_scope(clause))
|
|
1093
|
-
visit(clause.body, expression)
|
|
1094
|
-
@method.goto(done)
|
|
1095
|
-
clause.types.each do |type|
|
|
1096
|
-
type = inferred_type(type)
|
|
1097
|
-
@method.trycatch(start, body_end, target, type)
|
|
1098
|
-
end
|
|
1099
|
-
end
|
|
1100
|
-
done.set!
|
|
1101
|
-
end
|
|
1102
|
-
|
|
1103
|
-
def handle_ensures(nodes)
|
|
1104
|
-
nodes.each do |ensure_node|
|
|
1105
|
-
visit(ensure_node.ensureClause, false)
|
|
1106
|
-
end
|
|
1107
|
-
end
|
|
1108
|
-
|
|
1109
|
-
def visitEnsure(node, expression)
|
|
1110
|
-
# TODO this doesn't appear to be used
|
|
1111
|
-
#node.state = @method.label # Save the ensure target for JumpNodes
|
|
1112
|
-
start = @method.label.set!
|
|
1113
|
-
body_end = @method.label
|
|
1114
|
-
done = @method.label
|
|
1115
|
-
push_jump_scope(node) do
|
|
1116
|
-
visit(node.body, expression) # First compile the body
|
|
1117
|
-
end
|
|
1118
|
-
body_end.set!
|
|
1119
|
-
handle_ensures([node]) # run the ensure clause
|
|
1120
|
-
@method.goto(done) # and continue on after the exception handler
|
|
1121
|
-
target = @method.label.set! # Finally, create the exception handler
|
|
1122
|
-
@method.trycatch(start, body_end, target, nil)
|
|
1123
|
-
handle_ensures([node])
|
|
1124
|
-
@method.athrow
|
|
1125
|
-
done.set!
|
|
1126
|
-
end
|
|
1127
|
-
|
|
1128
|
-
def visitEmptyArray(node, expression)
|
|
1129
|
-
if expression
|
|
1130
|
-
visit(node.size, true)
|
|
1131
|
-
type = @typer.type_system.get(@scoper.getScope(node), node.type).resolve
|
|
1132
|
-
type.newarray(@method)
|
|
1133
|
-
end
|
|
1134
|
-
end
|
|
1135
|
-
|
|
1136
|
-
private
|
|
1137
|
-
|
|
1138
|
-
def convert_value in_type, out_type
|
|
1139
|
-
in_type.compile_widen(@method, out_type) if in_type.name != ':unreachable' && in_type.primitive?
|
|
1140
|
-
end
|
|
1141
|
-
|
|
1142
|
-
def dup_value type
|
|
1143
|
-
if type.primitive? && type.wide?
|
|
1144
|
-
@method.dup2
|
|
1145
|
-
else
|
|
1146
|
-
@method.dup
|
|
1147
|
-
end
|
|
1148
|
-
end
|
|
1149
|
-
|
|
1150
|
-
def dup_x2_value type
|
|
1151
|
-
if type.primitive? && type.wide?
|
|
1152
|
-
@method.dup2_x2
|
|
1153
|
-
else
|
|
1154
|
-
@method.dup_x2
|
|
1155
|
-
end
|
|
1156
|
-
end
|
|
1157
|
-
|
|
1158
|
-
class ClosureCompiler < JVMBytecode
|
|
1159
|
-
def initialize(config, file, type, parent, scoper, typer)
|
|
1160
|
-
super(config, scoper, typer)
|
|
1161
|
-
@file = file
|
|
1162
|
-
@type = type
|
|
1163
|
-
@jump_scope = []
|
|
1164
|
-
@parent = parent
|
|
1165
|
-
end
|
|
1166
|
-
|
|
1167
|
-
def prepare_binding(node)
|
|
1168
|
-
scope = introduced_scope(node)
|
|
1169
|
-
if scope.has_binding?
|
|
1170
|
-
type = scope.binding_type
|
|
1171
|
-
@binding = @parent.get_binding(type)
|
|
1172
|
-
@method.aload 0
|
|
1173
|
-
@method.getfield(@class, 'binding', @binding)
|
|
1174
|
-
type.store(@method, @method.local('$binding', type))
|
|
1175
|
-
else
|
|
1176
|
-
log "No binding for #{node} (#{scope.has_binding?} #{scope.parent} #{scope.parent && scope.parent.has_binding?})"
|
|
1177
|
-
end
|
|
1178
|
-
begin
|
|
1179
|
-
yield
|
|
1180
|
-
ensure
|
|
1181
|
-
if scope.has_binding?
|
|
1182
|
-
@binding = nil
|
|
1183
|
-
end
|
|
1184
|
-
end
|
|
1185
|
-
end
|
|
1186
|
-
|
|
1187
|
-
def declared_captures
|
|
1188
|
-
@parent.declared_captures(@binding)
|
|
1189
|
-
end
|
|
1190
|
-
end
|
|
1191
|
-
end
|
|
1192
|
-
end
|
|
1193
|
-
end
|
|
1194
|
-
end
|