duby 0.0.2-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 (77) hide show
  1. data/History.txt +8 -0
  2. data/README.txt +39 -0
  3. data/Rakefile +13 -0
  4. data/bin/duby +9 -0
  5. data/bin/dubyc +9 -0
  6. data/bin/dubyp +9 -0
  7. data/examples/README +16 -0
  8. data/examples/appengine/Rakefile +72 -0
  9. data/examples/appengine/Readme +27 -0
  10. data/examples/appengine/config.ru +7 -0
  11. data/examples/appengine/lib/duby/plugin/datastore.rb +171 -0
  12. data/examples/appengine/src/com/google/appengine/ext/duby/db/Model.duby +132 -0
  13. data/examples/appengine/src/com/ribrdb/DubyApp.duby +28 -0
  14. data/examples/appengine/src/com/ribrdb/list.dhtml +15 -0
  15. data/examples/construction.duby +8 -0
  16. data/examples/edb.duby +3 -0
  17. data/examples/fib.duby +24 -0
  18. data/examples/fields.duby +22 -0
  19. data/examples/fractal.duby +57 -0
  20. data/examples/java_thing.duby +13 -0
  21. data/examples/simple_class.duby +12 -0
  22. data/examples/swing.duby +20 -0
  23. data/examples/tak.duby +15 -0
  24. data/examples/test.edb +9 -0
  25. data/javalib/JRubyParser.jar +0 -0
  26. data/lib/duby.rb +168 -0
  27. data/lib/duby/ast.rb +386 -0
  28. data/lib/duby/ast/call.rb +145 -0
  29. data/lib/duby/ast/class.rb +154 -0
  30. data/lib/duby/ast/flow.rb +332 -0
  31. data/lib/duby/ast/intrinsics.rb +56 -0
  32. data/lib/duby/ast/literal.rb +97 -0
  33. data/lib/duby/ast/local.rb +92 -0
  34. data/lib/duby/ast/method.rb +244 -0
  35. data/lib/duby/ast/structure.rb +62 -0
  36. data/lib/duby/ast/type.rb +93 -0
  37. data/lib/duby/c/compiler.rb +134 -0
  38. data/lib/duby/compiler.rb +282 -0
  39. data/lib/duby/jvm/compiler.rb +766 -0
  40. data/lib/duby/jvm/method_lookup.rb +193 -0
  41. data/lib/duby/jvm/source_compiler.rb +605 -0
  42. data/lib/duby/jvm/source_generator/builder.rb +387 -0
  43. data/lib/duby/jvm/source_generator/loops.rb +110 -0
  44. data/lib/duby/jvm/source_generator/precompile.rb +170 -0
  45. data/lib/duby/jvm/source_generator/typer.rb +11 -0
  46. data/lib/duby/jvm/typer.rb +131 -0
  47. data/lib/duby/jvm/types.rb +331 -0
  48. data/lib/duby/jvm/types/basic_types.rb +19 -0
  49. data/lib/duby/jvm/types/boolean.rb +11 -0
  50. data/lib/duby/jvm/types/enumerable.rb +63 -0
  51. data/lib/duby/jvm/types/factory.rb +155 -0
  52. data/lib/duby/jvm/types/floats.rb +70 -0
  53. data/lib/duby/jvm/types/integers.rb +110 -0
  54. data/lib/duby/jvm/types/intrinsics.rb +230 -0
  55. data/lib/duby/jvm/types/literals.rb +82 -0
  56. data/lib/duby/jvm/types/methods.rb +381 -0
  57. data/lib/duby/jvm/types/number.rb +92 -0
  58. data/lib/duby/nbcompiler.rb +29 -0
  59. data/lib/duby/old/compiler_old.rb +845 -0
  60. data/lib/duby/old/declaration.rb +72 -0
  61. data/lib/duby/old/mapper.rb +72 -0
  62. data/lib/duby/old/signature.rb +52 -0
  63. data/lib/duby/old/typer_old.rb +163 -0
  64. data/lib/duby/plugin/edb.rb +25 -0
  65. data/lib/duby/plugin/java.rb +42 -0
  66. data/lib/duby/plugin/math.rb +84 -0
  67. data/lib/duby/transform.rb +1028 -0
  68. data/lib/duby/typer.rb +369 -0
  69. data/test/TestUser.class +0 -0
  70. data/test/test_ast.rb +391 -0
  71. data/test/test_compilation.rb +98 -0
  72. data/test/test_java_typer.rb +199 -0
  73. data/test/test_javac_compiler.rb +58 -0
  74. data/test/test_jvm_compiler.rb +1770 -0
  75. data/test/test_math_plugin.rb +87 -0
  76. data/test/test_typer.rb +246 -0
  77. metadata +156 -0
@@ -0,0 +1,766 @@
1
+ require 'duby'
2
+ require 'duby/jvm/method_lookup'
3
+ require 'duby/jvm/types'
4
+ require 'duby/typer'
5
+ require 'duby/plugin/java'
6
+ require 'bitescript'
7
+
8
+ module Duby
9
+ module AST
10
+ class FunctionalCall
11
+ attr_accessor :target
12
+ end
13
+
14
+ class Super
15
+ attr_accessor :target
16
+ end
17
+ end
18
+
19
+ module Compiler
20
+ class JVM
21
+ java_import java.lang.System
22
+ java_import java.io.PrintStream
23
+ include Duby::JVM::MethodLookup
24
+ Types = Duby::JVM::Types
25
+
26
+ class << self
27
+ attr_accessor :verbose
28
+
29
+ def log(message)
30
+ puts "* [#{name}] #{message}" if JVM.verbose
31
+ end
32
+ end
33
+
34
+ module JVMLogger
35
+ def log(message); JVM.log(message); end
36
+ end
37
+ include JVMLogger
38
+
39
+ class ImplicitSelf
40
+ attr_reader :inferred_type
41
+
42
+ def initialize(type)
43
+ @inferred_type = type
44
+ end
45
+
46
+ def compile(compiler, expression)
47
+ compiler.compile_self if expression
48
+ end
49
+ end
50
+
51
+ attr_accessor :filename, :src, :method, :static, :class
52
+
53
+ def initialize(filename)
54
+ @filename = File.basename(filename)
55
+ @src = ""
56
+ @static = true
57
+ classname = File.basename(filename, '.duby')
58
+ BiteScript.bytecode_version = BiteScript::JAVA1_5
59
+ @file = BiteScript::FileBuilder.new(@filename)
60
+ AST.type_factory.define_types(@file)
61
+ @type = AST::type(classname)
62
+ @jump_scope = []
63
+ end
64
+
65
+ def compile(ast, expression = false)
66
+ ast.compile(self, expression)
67
+ log "Compilation successful!"
68
+ end
69
+
70
+ def toplevel_class
71
+ @class = @type.define(@file)
72
+ end
73
+
74
+ def push_jump_scope(node)
75
+ raise "Not a node" unless Duby::AST::Node === node
76
+ begin
77
+ @jump_scope << node
78
+ yield
79
+ ensure
80
+ @jump_scope.pop
81
+ end
82
+ end
83
+
84
+ def find_ensures(before)
85
+ found = []
86
+ @jump_scope.reverse_each do |scope|
87
+ if Duby::AST::Ensure === scope
88
+ found << scope
89
+ end
90
+ break if scope === before
91
+ end
92
+ found
93
+ end
94
+
95
+ def define_main(body)
96
+ if body.class != AST::ClassDefinition
97
+ @class = @type.define(@file)
98
+ with :method => @class.main do
99
+ log "Starting main method"
100
+
101
+ @method.start
102
+
103
+ # declare argv variable
104
+ @method.local('argv', AST.type('string', true))
105
+
106
+ body.compile(self, false)
107
+
108
+ @method.returnvoid
109
+ @method.stop
110
+ end
111
+ @class.stop
112
+ else
113
+ body.compile(self, false)
114
+ end
115
+
116
+ log "Main method complete!"
117
+ end
118
+
119
+ def define_method(node)
120
+ push_jump_scope(node) do
121
+ name, signature, args = node.name, node.signature, node.arguments.args
122
+ arg_types = if args
123
+ args.map { |arg| arg.inferred_type }
124
+ else
125
+ []
126
+ end
127
+ return_type = signature[:return]
128
+ exceptions = signature[:throws]
129
+
130
+ with :static => @static || node.static? do
131
+ if @static
132
+ method = @class.public_static_method(name.to_s, exceptions, return_type, *arg_types)
133
+ else
134
+ method = @class.public_method(name.to_s, exceptions, return_type, *arg_types)
135
+ end
136
+
137
+ annotate(method, node.annotations)
138
+
139
+ return if @class.interface?
140
+
141
+ log "Starting new method #{name}(#{arg_types})"
142
+ method_body(method, args, node.body, signature[:return])
143
+
144
+ arg_types_for_opt = []
145
+ args_for_opt = []
146
+ if args
147
+ args.each do |arg|
148
+ if AST::OptionalArgument === arg
149
+ if @static
150
+ method = @class.public_static_method(name.to_s, exceptions, return_type, *arg_types_for_opt)
151
+ else
152
+ method = @class.public_method(name.to_s, exceptions, return_type, *arg_types_for_opt)
153
+ end
154
+
155
+ with :method => method do
156
+ log "Starting new method #{name}(#{arg_types_for_opt})"
157
+
158
+ @method.start unless started
159
+
160
+ # declare all args so they get their values
161
+
162
+ expression = signature[:return] != Types::Void
163
+
164
+ @method.aload(0) unless @static
165
+ args_for_opt.each {|req_arg| @method.local(req_arg.name, req_arg.inferred_type)}
166
+ arg.children[0].compile(self, true)
167
+
168
+ # invoke the next one in the chain
169
+ if @static
170
+ @method.invokestatic(@class, name.to_s, [return_type] + arg_types_for_opt + [arg.inferred_type])
171
+ else
172
+ @method.invokevirtual(@class, name.to_s, [return_type] + arg_types_for_opt + [arg.inferred_type])
173
+ end
174
+
175
+ signature[:return].return(@method)
176
+
177
+ @method.stop
178
+ end
179
+ end
180
+ arg_types_for_opt << arg.inferred_type
181
+ args_for_opt << arg
182
+ end
183
+ end
184
+ end
185
+
186
+ log "Method #{name}(#{arg_types}) complete!" end
187
+ end
188
+
189
+ def constructor(node)
190
+ push_jump_scope(node) do
191
+ args = node.arguments.args || []
192
+ arg_types = args.map { |arg| arg.inferred_type }
193
+ exceptions = node.signature[:throws]
194
+ method = @class.public_constructor(exceptions, *arg_types)
195
+ annotate(method, node.annotations)
196
+ method_body(method, args, node.body, Types::Void) do
197
+ method.aload 0
198
+ if node.delegate_args
199
+ if node.calls_super
200
+ delegate_class = @type.superclass
201
+ else
202
+ delegate_class = @type
203
+ end
204
+ delegate_types = node.delegate_args.map {|arg| arg.inferred_type}
205
+ constructor = delegate_class.constructor(*delegate_types)
206
+ node.delegate_args.each do |arg|
207
+ arg.compile(self, true)
208
+ end
209
+ method.invokespecial(
210
+ delegate_class, "<init>",
211
+ [@method.void, *constructor.argument_types])
212
+ else
213
+ method.invokespecial @class.superclass, "<init>", [@method.void]
214
+ end
215
+ end
216
+ end
217
+ end
218
+
219
+ def method_body(method, args, body, return_type)
220
+ with :method => method do
221
+
222
+ method.start
223
+
224
+ # declare all args so they get their values
225
+ if args
226
+ args.each {|arg| @method.local(arg.name, arg.inferred_type)}
227
+ end
228
+
229
+ yield if block_given?
230
+
231
+ expression = return_type != Types::Void
232
+ body.compile(self, expression) if body
233
+
234
+ return_type.return(@method)
235
+
236
+ @method.stop
237
+ end
238
+ end
239
+
240
+ def define_class(class_def, expression)
241
+ with(:type => class_def.inferred_type,
242
+ :class => class_def.inferred_type.define(@file),
243
+ :static => false) do
244
+ annotate(@class, class_def.annotations)
245
+ class_def.body.compile(self, false) if class_def.body
246
+ @class.stop
247
+ end
248
+ end
249
+
250
+ def declare_argument(name, type)
251
+ # declare local vars for arguments here
252
+ end
253
+
254
+ def branch(iff, expression)
255
+ elselabel = @method.label
256
+ donelabel = @method.label
257
+
258
+ # this is ugly...need a better way to abstract the idea of compiling a
259
+ # conditional branch while still fitting into JVM opcodes
260
+ predicate = iff.condition.predicate
261
+ if iff.body || expression
262
+ jump_if_not(predicate, elselabel)
263
+
264
+ if iff.body
265
+ iff.body.compile(self, expression)
266
+ elsif expression
267
+ iff.inferred_type.init_value(@method)
268
+ end
269
+
270
+ @method.goto(donelabel)
271
+ else
272
+ jump_if(predicate, donelabel)
273
+ end
274
+
275
+ elselabel.set!
276
+
277
+ if iff.else
278
+ iff.else.compile(self, expression)
279
+ elsif expression
280
+ iff.inferred_type.init_value(@method)
281
+ end
282
+
283
+ donelabel.set!
284
+ end
285
+
286
+ def loop(loop, expression)
287
+ push_jump_scope(loop) do
288
+ with(:break_label => @method.label,
289
+ :redo_label => @method.label,
290
+ :next_label => @method.label) do
291
+ predicate = loop.condition.predicate
292
+
293
+ loop.init.compile(self, false) if loop.init?
294
+
295
+ pre_label = @redo_label
296
+
297
+ if loop.check_first
298
+ @next_label.set! unless loop.post?
299
+ if loop.negative
300
+ # if condition, exit
301
+ jump_if(predicate, @break_label)
302
+ else
303
+ # if not condition, exit
304
+ jump_if_not(predicate, @break_label)
305
+ end
306
+ end
307
+
308
+ if loop.pre?
309
+ pre_label = method.label
310
+ pre_label.set!
311
+ loop.pre.compile(self, false)
312
+ end
313
+
314
+
315
+ @redo_label.set!
316
+ loop.body.compile(self, false)
317
+
318
+ if loop.check_first && !loop.post?
319
+ @method.goto(@next_label)
320
+ else
321
+ @next_label.set!
322
+ loop.post.compile(self, false) if loop.post?
323
+ if loop.negative
324
+ # if not condition, continue
325
+ jump_if_not(predicate, pre_label)
326
+ else
327
+ # if condition, continue
328
+ jump_if(predicate, pre_label)
329
+ end
330
+ end
331
+
332
+ @break_label.set!
333
+
334
+ # loops always evaluate to null
335
+ @method.aconst_null if expression
336
+ end
337
+ end
338
+ end
339
+
340
+ def break(node)
341
+ handle_ensures(find_ensures(Duby::AST::Loop))
342
+ @method.goto(@break_label)
343
+ end
344
+
345
+ def next(node)
346
+ handle_ensures(find_ensures(Duby::AST::Loop))
347
+ @method.goto(@next_label)
348
+ end
349
+
350
+ def redo(node)
351
+ handle_ensures(find_ensures(Duby::AST::Loop))
352
+ @method.goto(@redo_label)
353
+ end
354
+
355
+ def jump_if(predicate, target)
356
+ raise "Expected boolean, found #{predicate.inferred_type}" unless predicate.inferred_type == Types::Boolean
357
+ predicate.compile(self, true)
358
+ @method.ifne(target)
359
+ end
360
+
361
+ def jump_if_not(predicate, target)
362
+ raise "Expected boolean, found #{predicate.inferred_type}" unless predicate.inferred_type == Types::Boolean
363
+ predicate.compile(self, true)
364
+ @method.ifeq(target)
365
+ end
366
+
367
+ def call(call, expression)
368
+ target = call.target.inferred_type
369
+ params = call.parameters.map do |param|
370
+ param.inferred_type
371
+ end
372
+ method = target.get_method(call.name, params)
373
+ if method
374
+ method.call(self, call, expression)
375
+ else
376
+ raise "Missing method #{target}.#{call.name}(#{params.join ', '})"
377
+ end
378
+ end
379
+
380
+ def self_call(fcall, expression)
381
+ return cast(fcall, expression) if fcall.cast?
382
+ type = @type
383
+ type = type.meta if @static
384
+ fcall.target = ImplicitSelf.new(type)
385
+
386
+ params = fcall.parameters.map do |param|
387
+ param.inferred_type
388
+ end
389
+ method = type.get_method(fcall.name, params)
390
+ unless method
391
+ target = static ? @class.name : 'self'
392
+
393
+ raise NameError, "No method %s.%s(%s)" %
394
+ [target, fcall.name, params.join(', ')]
395
+ end
396
+ method.call(self, fcall, expression)
397
+ end
398
+
399
+ def super_call(sup, expression)
400
+ type = @type.superclass
401
+ sup.target = ImplicitSelf.new(type)
402
+
403
+ params = sup.parameters.map do |param|
404
+ param.inferred_type
405
+ end
406
+ method = type.get_method(sup.name, params)
407
+ unless method
408
+
409
+ raise NameError, "No method %s.%s(%s)" %
410
+ [type, sup.name, params.join(', ')]
411
+ end
412
+ method.call_special(self, sup, expression)
413
+ end
414
+
415
+ def cast(fcall, expression)
416
+ # casting operation, not a call
417
+ castee = fcall.parameters[0]
418
+
419
+ # TODO move errors to inference phase
420
+ source_type_name = castee.inferred_type.name
421
+ target_type_name = fcall.inferred_type.name
422
+ case source_type_name
423
+ when "byte", "short", "char", "int", "long", "float", "double"
424
+ case target_type_name
425
+ when "byte", "short", "char", "int", "long", "float", "double"
426
+ # ok
427
+ primitive = true
428
+ else
429
+ raise TypeError.new "not a reference type: #{castee.inferred_type}"
430
+ end
431
+ when "boolean"
432
+ if target_type_name != "boolean"
433
+ raise TypeError.new "not a boolean type: #{castee.inferred_type}"
434
+ end
435
+ primitive = true
436
+ else
437
+ case target_type_name
438
+ when "byte", "short", "char", "int", "long", "float", "double"
439
+ raise TypeError.new "not a primitive type: #{castee.inferred_type}"
440
+ else
441
+ # ok
442
+ primitive = false
443
+ end
444
+ end
445
+
446
+ castee.compile(self, expression)
447
+ if expression
448
+ if primitive
449
+ source_type_name = 'int' if %w[byte short char].include? source_type_name
450
+ if (source_type_name != 'int') && (%w[byte short char].include? target_type_name)
451
+ target_type_name = 'int'
452
+ end
453
+
454
+ if source_type_name != target_type_name
455
+ if RUBY_VERSION == "1.9"
456
+ @method.send "#{source_type_name[0]}2#{target_type_name[0]}"
457
+ else
458
+ @method.send "#{source_type_name[0].chr}2#{target_type_name[0].chr}"
459
+ end
460
+ end
461
+ else
462
+ if source_type_name != target_type_name
463
+ @method.checkcast fcall.inferred_type
464
+ end
465
+ end
466
+ end
467
+ end
468
+
469
+ def body(body, expression)
470
+ # all except the last element in a body of code is treated as a statement
471
+ i, last = 0, body.children.size - 1
472
+ while i < last
473
+ body.children[i].compile(self, false)
474
+ i += 1
475
+ end
476
+ # last element is an expression only if the body is an expression
477
+ body.children[last].compile(self, expression)
478
+ end
479
+
480
+ def local(name, type)
481
+ type.load(@method, @method.local(name, type))
482
+ end
483
+
484
+ def local_assign(name, type, expression, value)
485
+ declare_local(name, type)
486
+
487
+ value.compile(self, true)
488
+
489
+ # if expression, dup the value we're assigning
490
+ @method.dup if expression
491
+
492
+ type.store(@method, @method.local(name, type))
493
+ end
494
+
495
+ def declared_locals
496
+ @declared_locals ||= {}
497
+ end
498
+
499
+ def annotate(builder, annotations)
500
+ annotations.each do |annotation|
501
+ builder.annotate(annotation.type.jvm_type) do |visitor|
502
+ # todo values
503
+ end
504
+ end
505
+ end
506
+
507
+ def declare_local(name, type)
508
+ # TODO confirm types are compatible
509
+ unless declared_locals[name]
510
+ declared_locals[name] = type
511
+ index = @method.local(name, type)
512
+ end
513
+ end
514
+
515
+ def local_declare(name, type)
516
+ declare_local(name, type)
517
+ type.init_value(@method)
518
+ type.store(@method, @method.local(name, type))
519
+ end
520
+
521
+ def field(name, type, annotations)
522
+ name = name[1..-1]
523
+
524
+ real_type = declared_fields[name] || type
525
+
526
+ declare_field(name, real_type, annotations)
527
+
528
+ # load self object unless static
529
+ method.aload 0 unless static
530
+
531
+ if static
532
+ @method.getstatic(@class, name, type)
533
+ else
534
+ @method.getfield(@class, name, type)
535
+ end
536
+ end
537
+
538
+ def declared_fields
539
+ @declared_fields ||= {}
540
+ @declared_fields[@class] ||= {}
541
+ end
542
+
543
+ def declare_field(name, type, annotations)
544
+ # TODO confirm types are compatible
545
+ unless declared_fields[name]
546
+ declared_fields[name] = type
547
+ field = if static
548
+ @class.private_static_field name, type
549
+ else
550
+ @class.private_field name, type
551
+ end
552
+ annotate(field, annotations)
553
+ end
554
+ end
555
+
556
+ def field_declare(name, type, annotations)
557
+ name = name[1..-1]
558
+ declare_field(name, type, annotations)
559
+ end
560
+
561
+ def field_assign(name, type, expression, value, annotations)
562
+ name = name[1..-1]
563
+
564
+ real_type = declared_fields[name] || type
565
+
566
+ declare_field(name, real_type, annotations)
567
+
568
+ method.aload 0 unless static
569
+ value.compile(self, true)
570
+ if expression
571
+ instruction = 'dup'
572
+ instruction << '2' if type.wide?
573
+ instruction << '_x1' unless static
574
+ method.send instruction
575
+ end
576
+
577
+ if static
578
+ @method.putstatic(@class, name, real_type)
579
+ else
580
+ @method.putfield(@class, name, real_type)
581
+ end
582
+ end
583
+
584
+ def string(value)
585
+ @method.ldc(value)
586
+ end
587
+
588
+ def boolean(value)
589
+ value ? @method.iconst_1 : @method.iconst_0
590
+ end
591
+
592
+ def array(node, expression)
593
+ if expression
594
+ # create basic arraylist
595
+ @method.new java::util::ArrayList
596
+ @method.dup
597
+ @method.ldc_int node.children ? node.children.size : 0
598
+ @method.invokespecial java::util::ArrayList, "<init>", [@method.void, @method.int]
599
+
600
+ # elements, as expressions
601
+ # TODO: ensure they're all reference types!
602
+ node.children.each do |node|
603
+ @method.dup
604
+ node.compile(self, true)
605
+ @method.invokeinterface java::util::List, "add", [@method.boolean, @method.object]
606
+ @method.pop
607
+ end
608
+
609
+ # make it unmodifiable
610
+ @method.invokestatic java::util::Collections, "unmodifiableList", [java::util::List, java::util::List]
611
+ else
612
+ # elements, as non-expressions
613
+ # TODO: ensure they're all reference types!
614
+ node.children.each do |node|
615
+ node.compile(self, false)
616
+ end
617
+ end
618
+ end
619
+
620
+ def null
621
+ @method.aconst_null
622
+ end
623
+
624
+ def compile_self
625
+ method.aload(0)
626
+ end
627
+
628
+ def newline
629
+ # TODO: line numbering
630
+ end
631
+
632
+ def line(num)
633
+ @method.line(num) if @method
634
+ end
635
+
636
+ def generate
637
+ log "Generating classes..."
638
+ @file.generate do |filename, builder|
639
+ log " #{builder.class_name}"
640
+ if block_given?
641
+ yield filename, builder
642
+ else
643
+ File.open(filename, 'w') {|f| f.write(builder.generate)}
644
+ end
645
+ end
646
+ log "...done!"
647
+ end
648
+
649
+ def import(short, long)
650
+ end
651
+
652
+ def print(print_node)
653
+ @method.getstatic System, "out", PrintStream
654
+ print_node.parameters.each {|param| param.compile(self, true)}
655
+ params = print_node.parameters.map {|param| param.inferred_type.jvm_type}
656
+ method_name = print_node.println ? "println" : "print"
657
+ method = find_method(PrintStream.java_class, method_name, params, false)
658
+ if (method)
659
+ @method.invokevirtual(
660
+ PrintStream,
661
+ method_name,
662
+ [method.return_type, *method.parameter_types])
663
+ else
664
+ log "Could not find a match for #{PrintStream}.#{method_name}(#{params})"
665
+ fail "Could not compile"
666
+ end
667
+ end
668
+
669
+ def return(return_node)
670
+ return_node.value.compile(self, true)
671
+ handle_ensures(find_ensures(Duby::AST::MethodDefinition))
672
+ return_node.inferred_type.return(@method)
673
+ end
674
+
675
+ def _raise(exception)
676
+ exception.compile(self, true)
677
+ @method.athrow
678
+ end
679
+
680
+ def rescue(rescue_node, expression)
681
+ start = @method.label.set!
682
+ body_end = @method.label
683
+ done = @method.label
684
+ rescue_node.body.compile(self, expression)
685
+ body_end.set!
686
+ @method.goto(done)
687
+ rescue_node.clauses.each do |clause|
688
+ target = @method.label.set!
689
+ if clause.name
690
+ @method.astore(@method.push_local(clause.name, clause.type))
691
+ else
692
+ @method.pop
693
+ end
694
+ clause.body.compile(self, expression)
695
+ @method.pop_local(clause.name) if clause.name
696
+ @method.goto(done)
697
+ clause.types.each do |type|
698
+ @method.trycatch(start, body_end, target, type)
699
+ end
700
+ end
701
+ done.set!
702
+ end
703
+
704
+ def handle_ensures(nodes)
705
+ nodes.each do |ensure_node|
706
+ ensure_node.clause.compile(self, false)
707
+ end
708
+ end
709
+
710
+ def ensure(node, expression)
711
+ node.state = @method.label # Save the ensure target for JumpNodes
712
+ start = @method.label.set!
713
+ body_end = @method.label
714
+ done = @method.label
715
+ push_jump_scope(node) do
716
+ node.body.compile(self, expression) # First compile the body
717
+ end
718
+ body_end.set!
719
+ handle_ensures([node]) # run the ensure clause
720
+ @method.goto(done) # and continue on after the exception handler
721
+ target = @method.label.set! # Finally, create the exception handler
722
+ @method.trycatch(start, body_end, target, nil)
723
+ handle_ensures([node])
724
+ @method.athrow
725
+ done.set!
726
+ end
727
+
728
+ def empty_array(type, size)
729
+ size.compile(self, true)
730
+ type.newarray(@method)
731
+ end
732
+
733
+ def with(vars)
734
+ orig_values = {}
735
+ begin
736
+ vars.each do |name, new_value|
737
+ name = "@#{name}"
738
+ orig_values[name] = instance_variable_get name
739
+ instance_variable_set name, new_value
740
+ end
741
+ yield
742
+ ensure
743
+ orig_values.each do |name, value|
744
+ instance_variable_set name, value
745
+ end
746
+ end
747
+ end
748
+ end
749
+ end
750
+ end
751
+
752
+ if __FILE__ == $0
753
+ Duby::Typer.verbose = true
754
+ Duby::AST.verbose = true
755
+ Duby::Compiler::JVM.verbose = true
756
+ ast = Duby::AST.parse(File.read(ARGV[0]))
757
+
758
+ typer = Duby::Typer::Simple.new(:script)
759
+ ast.infer(typer)
760
+ typer.resolve(true)
761
+
762
+ compiler = Duby::Compiler::JVM.new(ARGV[0])
763
+ compiler.compile(ast)
764
+
765
+ compiler.generate
766
+ end