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.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/History.txt +225 -0
  3. data/Rakefile +108 -315
  4. data/TODO.md +100 -0
  5. data/bin/bundler +16 -0
  6. data/bin/rake +16 -0
  7. data/dist/mirahc.jar +0 -0
  8. data/examples/appengine/Readme +0 -1
  9. data/examples/literals.mirah +17 -0
  10. data/examples/macros/string_each_char.mirah +1 -1
  11. data/lib/mirah.rb +11 -21
  12. data/lib/mirah/transform/transformer.rb +1 -2
  13. data/lib/mirah/util/class_loader.rb +1 -1
  14. data/lib/mirah/util/logging.rb +0 -63
  15. data/lib/mirah/util/process_errors.rb +1 -0
  16. data/lib/mirah/version.rb +1 -1
  17. data/{examples/simple_class.mirah~ → test/artifacts/jar_test.rb} +7 -11
  18. data/{lib/mirah/commands.rb → test/artifacts/jruby_test.rb} +8 -5
  19. data/test/core/typer_test.rb +29 -11
  20. data/test/core/util/argument_processor_test.rb +24 -23
  21. data/test/core/util/class_loader_test.rb +7 -4
  22. data/test/core/util/{compilation_state_test.rb → jvm_version_test.rb} +20 -16
  23. data/test/fixtures/org/foo/ImplicitClassRetAnno.java +4 -0
  24. data/test/fixtures/org/foo/IntAnno.java +9 -0
  25. data/test/jvm/annotations_test.rb +11 -11
  26. data/test/jvm/blocks_test.rb +16 -12
  27. data/test/jvm/constructors_test.rb +8 -8
  28. data/test/jvm/enumerable_test.rb +48 -24
  29. data/test/jvm/generics_test.rb +3 -7
  30. data/test/jvm/import_test.rb +14 -0
  31. data/test/jvm/interface_test.rb +9 -24
  32. data/test/jvm/jvm_commands_test.rb +22 -4
  33. data/test/jvm/jvm_compiler_test.rb +124 -79
  34. data/test/jvm/list_extensions_test.rb +1 -1
  35. data/test/jvm/macros_test.rb +67 -14
  36. data/test/jvm/main_method_test.rb +1 -1
  37. data/test/jvm/new_backend_test_helper.rb +100 -3
  38. data/{lib/mirah/jvm/types/bitescript_ext.rb → test/jvm/static_fields_test.rb} +22 -21
  39. data/test/mirrors/base_type_test.rb +4 -3
  40. data/test/mirrors/bytecode_mirror_test.rb +35 -15
  41. data/test/mirrors/generics_test.rb +14 -5
  42. data/test/mirrors/member_test.rb +2 -1
  43. data/test/mirrors/method_lookup_test.rb +18 -6
  44. data/test/mirrors/mirrors_test.rb +87 -20
  45. data/test/mirrors/simple_async_mirror_loader_test.rb +7 -3
  46. data/test/mirrors/simple_mirror_loader_test.rb +5 -5
  47. data/test/test_helper.rb +25 -1
  48. metadata +18 -78
  49. data/bin/mirahp +0 -27
  50. data/bin/mirahp.cmd +0 -16
  51. data/examples/Fib.class +0 -0
  52. data/javalib/mirah-bootstrap.jar +0 -0
  53. data/javalib/mirah-builtins.jar +0 -0
  54. data/javalib/mirah-compiler.jar +0 -0
  55. data/javalib/mirah-mirrors.jar +0 -0
  56. data/javalib/mirah-newast-transitional.jar +0 -0
  57. data/javalib/mirah-parser.jar +0 -0
  58. data/javalib/mirah-util.jar +0 -0
  59. data/lib/mirah/ast.rb +0 -43
  60. data/lib/mirah/ast/scope.rb +0 -262
  61. data/lib/mirah/commands/base.rb +0 -59
  62. data/lib/mirah/commands/compile.rb +0 -39
  63. data/lib/mirah/commands/parse.rb +0 -36
  64. data/lib/mirah/commands/run.rb +0 -78
  65. data/lib/mirah/generator.rb +0 -150
  66. data/lib/mirah/jvm/compiler.rb +0 -50
  67. data/lib/mirah/jvm/compiler/base.rb +0 -421
  68. data/lib/mirah/jvm/compiler/jvm_bytecode.rb +0 -1194
  69. data/lib/mirah/jvm/method_lookup.rb +0 -307
  70. data/lib/mirah/jvm/types.rb +0 -45
  71. data/lib/mirah/jvm/types/array_type.rb +0 -60
  72. data/lib/mirah/jvm/types/ast_ext.rb +0 -31
  73. data/lib/mirah/jvm/types/basic_types.rb +0 -41
  74. data/lib/mirah/jvm/types/block_type.rb +0 -15
  75. data/lib/mirah/jvm/types/boolean.rb +0 -70
  76. data/lib/mirah/jvm/types/enumerable.rb +0 -80
  77. data/lib/mirah/jvm/types/extensions.rb +0 -110
  78. data/lib/mirah/jvm/types/factory.rb +0 -830
  79. data/lib/mirah/jvm/types/floats.rb +0 -99
  80. data/lib/mirah/jvm/types/generic_type.rb +0 -72
  81. data/lib/mirah/jvm/types/implicit_nil_type.rb +0 -29
  82. data/lib/mirah/jvm/types/integers.rb +0 -131
  83. data/lib/mirah/jvm/types/interface_definition.rb +0 -20
  84. data/lib/mirah/jvm/types/intrinsics.rb +0 -385
  85. data/lib/mirah/jvm/types/literals.rb +0 -89
  86. data/lib/mirah/jvm/types/meta_type.rb +0 -54
  87. data/lib/mirah/jvm/types/methods.rb +0 -946
  88. data/lib/mirah/jvm/types/null_type.rb +0 -39
  89. data/lib/mirah/jvm/types/number.rb +0 -184
  90. data/lib/mirah/jvm/types/primitive_type.rb +0 -76
  91. data/lib/mirah/jvm/types/source_mirror.rb +0 -274
  92. data/lib/mirah/jvm/types/type.rb +0 -311
  93. data/lib/mirah/jvm/types/type_definition.rb +0 -72
  94. data/lib/mirah/jvm/types/void_type.rb +0 -19
  95. data/lib/mirah/util/compilation_state.rb +0 -60
  96. data/test/core/commands_test.rb +0 -89
  97. data/test/core/generator_test.rb +0 -26
  98. data/test/fixtures/org/foo/LowerCaseInnerClass$inner.class +0 -0
  99. data/test/fixtures/org/foo/LowerCaseInnerClass.class +0 -0
  100. data/test/jvm/bytecode_test_helper.rb +0 -193
  101. data/test/jvm/factory_test.rb +0 -28
  102. 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