duby 0.0.2-java

Sign up to get free protection for your applications and to get access to all the features.
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