mirah 0.0.4-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 (141) hide show
  1. data/History.txt +15 -0
  2. data/README.txt +51 -0
  3. data/Rakefile +86 -0
  4. data/bin/duby +10 -0
  5. data/bin/dubyc +10 -0
  6. data/bin/dubyp +10 -0
  7. data/bin/jrubyp +36 -0
  8. data/bin/mirah +9 -0
  9. data/bin/mirah.cmd +1 -0
  10. data/bin/mirahc +9 -0
  11. data/bin/mirahc.cmd +1 -0
  12. data/bin/mirahp +9 -0
  13. data/bin/mirahp.cmd +1 -0
  14. data/examples/ant/example-build.xml +7 -0
  15. data/examples/appengine/Rakefile +19 -0
  16. data/examples/appengine/Readme +29 -0
  17. data/examples/appengine/src/org/mirah/MirahApp.mirah +57 -0
  18. data/examples/appengine/src/org/mirah/list.dhtml +15 -0
  19. data/examples/appengine/war/WEB-INF/lib/dubydatastore.jar +0 -0
  20. data/examples/bintrees.mirah +66 -0
  21. data/examples/construction.mirah +8 -0
  22. data/examples/dynamic.mirah +17 -0
  23. data/examples/edb.mirah +3 -0
  24. data/examples/fib.mirah +16 -0
  25. data/examples/fields.mirah +22 -0
  26. data/examples/fractal.mirah +55 -0
  27. data/examples/java_thing.mirah +13 -0
  28. data/examples/plugins/appengine/Rakefile +55 -0
  29. data/examples/plugins/appengine/lib/com/google/appengine/ext/duby/db/datastore.rb +375 -0
  30. data/examples/plugins/appengine/src/com/google/appengine/ext/duby/db/Model.duby +336 -0
  31. data/examples/plugins/appengine/test/com/google/appengine/ext/duby/db/ModelTest.duby +113 -0
  32. data/examples/simple_class.mirah +12 -0
  33. data/examples/sort_closure.mirah +7 -0
  34. data/examples/swing.mirah +20 -0
  35. data/examples/tak.mirah +15 -0
  36. data/examples/test.edb +9 -0
  37. data/examples/wiki/Rakefile +18 -0
  38. data/examples/wiki/src/org/mirah/wiki/MirahWiki.duby +324 -0
  39. data/examples/wiki/src/org/mirah/wiki/edit.eduby.html +42 -0
  40. data/examples/wiki/src/org/mirah/wiki/error.eduby.html +2 -0
  41. data/examples/wiki/src/org/mirah/wiki/layout.eduby.html +69 -0
  42. data/examples/wiki/src/org/mirah/wiki/parser.eduby.html +7 -0
  43. data/examples/wiki/src/org/mirah/wiki/view.eduby.html +15 -0
  44. data/examples/wiki/war/WEB-INF/classes/test/HeredocContext.class +0 -0
  45. data/examples/wiki/war/WEB-INF/classes/test/MirahParser.class +0 -0
  46. data/examples/wiki/war/WEB-INF/lib/appengine-api.jar +0 -0
  47. data/examples/wiki/war/WEB-INF/lib/dubydatastore.jar +0 -0
  48. data/examples/wiki/war/WEB-INF/lib/jmeta-runtime.jar +0 -0
  49. data/examples/wiki/war/WEB-INF/lib/pegdown-stubs.jar +0 -0
  50. data/examples/wiki/war/WEB-INF/pegdown.jar +0 -0
  51. data/examples/wiki/war/app.yaml +21 -0
  52. data/examples/wiki/war/public/favicon.ico +0 -0
  53. data/examples/wiki/war/public/images/appengine_duby.png +0 -0
  54. data/examples/wiki/war/public/images/back.gif +0 -0
  55. data/examples/wiki/war/public/images/dir.gif +0 -0
  56. data/examples/wiki/war/public/images/file.gif +0 -0
  57. data/examples/wiki/war/public/javascripts/prettify.js +61 -0
  58. data/examples/wiki/war/public/robots.txt +0 -0
  59. data/examples/wiki/war/public/stylesheets/main.css +156 -0
  60. data/examples/wiki/war/public/stylesheets/prettify.css +1 -0
  61. data/examples/wiki/war/public/stylesheets/sh_style.css +66 -0
  62. data/examples/wiki/war/public/stylesheets/source.css +21 -0
  63. data/examples/wiki/war/public/wmd/images/bg-fill.png +0 -0
  64. data/examples/wiki/war/public/wmd/images/bg.png +0 -0
  65. data/examples/wiki/war/public/wmd/images/blockquote.png +0 -0
  66. data/examples/wiki/war/public/wmd/images/bold.png +0 -0
  67. data/examples/wiki/war/public/wmd/images/code.png +0 -0
  68. data/examples/wiki/war/public/wmd/images/h1.png +0 -0
  69. data/examples/wiki/war/public/wmd/images/hr.png +0 -0
  70. data/examples/wiki/war/public/wmd/images/img.png +0 -0
  71. data/examples/wiki/war/public/wmd/images/italic.png +0 -0
  72. data/examples/wiki/war/public/wmd/images/link.png +0 -0
  73. data/examples/wiki/war/public/wmd/images/ol.png +0 -0
  74. data/examples/wiki/war/public/wmd/images/redo.png +0 -0
  75. data/examples/wiki/war/public/wmd/images/separator.png +0 -0
  76. data/examples/wiki/war/public/wmd/images/ul.png +0 -0
  77. data/examples/wiki/war/public/wmd/images/undo.png +0 -0
  78. data/examples/wiki/war/public/wmd/images/wmd-on.png +0 -0
  79. data/examples/wiki/war/public/wmd/images/wmd.png +0 -0
  80. data/examples/wiki/war/public/wmd/showdown.js +421 -0
  81. data/examples/wiki/war/public/wmd/wmd-base.js +1799 -0
  82. data/examples/wiki/war/public/wmd/wmd-plus.js +311 -0
  83. data/examples/wiki/war/public/wmd/wmd.js +73 -0
  84. data/javalib/JRubyParser.jar +0 -0
  85. data/javalib/dynalang-invoke-0.1.jar +0 -0
  86. data/javalib/mirah-bootstrap.jar +0 -0
  87. data/javalib/mirah-parser.jar +0 -0
  88. data/lib/duby.rb +2 -0
  89. data/lib/mirah.rb +338 -0
  90. data/lib/mirah/appengine_tasks.rb +146 -0
  91. data/lib/mirah/ast.rb +615 -0
  92. data/lib/mirah/ast/call.rb +307 -0
  93. data/lib/mirah/ast/class.rb +311 -0
  94. data/lib/mirah/ast/flow.rb +364 -0
  95. data/lib/mirah/ast/intrinsics.rb +470 -0
  96. data/lib/mirah/ast/literal.rb +154 -0
  97. data/lib/mirah/ast/local.rb +89 -0
  98. data/lib/mirah/ast/method.rb +360 -0
  99. data/lib/mirah/ast/scope.rb +208 -0
  100. data/lib/mirah/ast/structure.rb +226 -0
  101. data/lib/mirah/ast/type.rb +130 -0
  102. data/lib/mirah/compiler.rb +341 -0
  103. data/lib/mirah/env.rb +33 -0
  104. data/lib/mirah/jvm/base.rb +258 -0
  105. data/lib/mirah/jvm/compiler.rb +885 -0
  106. data/lib/mirah/jvm/method_lookup.rb +203 -0
  107. data/lib/mirah/jvm/source_compiler.rb +737 -0
  108. data/lib/mirah/jvm/source_generator/builder.rb +444 -0
  109. data/lib/mirah/jvm/source_generator/loops.rb +110 -0
  110. data/lib/mirah/jvm/source_generator/precompile.rb +188 -0
  111. data/lib/mirah/jvm/source_generator/typer.rb +11 -0
  112. data/lib/mirah/jvm/typer.rb +151 -0
  113. data/lib/mirah/jvm/types.rb +416 -0
  114. data/lib/mirah/jvm/types/basic_types.rb +33 -0
  115. data/lib/mirah/jvm/types/boolean.rb +17 -0
  116. data/lib/mirah/jvm/types/enumerable.rb +65 -0
  117. data/lib/mirah/jvm/types/extensions.rb +86 -0
  118. data/lib/mirah/jvm/types/factory.rb +186 -0
  119. data/lib/mirah/jvm/types/floats.rb +86 -0
  120. data/lib/mirah/jvm/types/integers.rb +171 -0
  121. data/lib/mirah/jvm/types/intrinsics.rb +376 -0
  122. data/lib/mirah/jvm/types/literals.rb +74 -0
  123. data/lib/mirah/jvm/types/methods.rb +614 -0
  124. data/lib/mirah/jvm/types/number.rb +143 -0
  125. data/lib/mirah/nbcompiler.rb +29 -0
  126. data/lib/mirah/plugin/edb.rb +29 -0
  127. data/lib/mirah/plugin/gwt.rb +173 -0
  128. data/lib/mirah/plugin/java.rb +55 -0
  129. data/lib/mirah/transform.rb +266 -0
  130. data/lib/mirah/transform2.rb +728 -0
  131. data/lib/mirah/typer.rb +407 -0
  132. data/lib/mirah_task.rb +107 -0
  133. data/test/test_ast.rb +359 -0
  134. data/test/test_compilation.rb +112 -0
  135. data/test/test_env.rb +42 -0
  136. data/test/test_gwt.rb +58 -0
  137. data/test/test_java_typer.rb +183 -0
  138. data/test/test_javac_compiler.rb +63 -0
  139. data/test/test_jvm_compiler.rb +2607 -0
  140. data/test/test_typer.rb +221 -0
  141. metadata +235 -0
@@ -0,0 +1,885 @@
1
+ require 'mirah'
2
+ require 'mirah/jvm/base'
3
+ require 'mirah/jvm/method_lookup'
4
+ require 'mirah/jvm/types'
5
+ require 'mirah/typer'
6
+ require 'mirah/plugin/java'
7
+ require 'bitescript'
8
+
9
+ module Duby
10
+ module AST
11
+ class FunctionalCall
12
+ attr_accessor :target
13
+ end
14
+
15
+ class Super
16
+ attr_accessor :target
17
+ end
18
+ end
19
+
20
+ module Compiler
21
+ class JVM < JVMCompilerBase
22
+ java_import java.lang.System
23
+ java_import java.io.PrintStream
24
+ include Duby::JVM::MethodLookup
25
+ Types = Duby::JVM::Types
26
+
27
+ class << self
28
+ attr_accessor :verbose
29
+
30
+ def log(message)
31
+ puts "* [#{name}] #{message}" if JVM.verbose
32
+ end
33
+
34
+ def classname_from_filename(filename)
35
+ basename = File.basename(filename).sub(/\.(duby|mirah)$/, '')
36
+ basename.split(/_/).map{|x| x[0...1].upcase + x[1..-1]}.join
37
+ end
38
+ end
39
+
40
+ module JVMLogger
41
+ def log(message); JVM.log(message); end
42
+ end
43
+
44
+ class ImplicitSelf
45
+ attr_reader :inferred_type
46
+
47
+ def initialize(type)
48
+ @inferred_type = type
49
+ end
50
+
51
+ def compile(compiler, expression)
52
+ compiler.compile_self if expression
53
+ end
54
+ end
55
+
56
+ def initialize
57
+ super
58
+ BiteScript.bytecode_version = BiteScript::JAVA1_5
59
+ @jump_scope = []
60
+ end
61
+
62
+ def file_builder(filename)
63
+ builder = BiteScript::FileBuilder.new(filename)
64
+ AST.type_factory.define_types(builder)
65
+ builder
66
+ end
67
+
68
+ def output_type
69
+ "classes"
70
+ end
71
+
72
+ def push_jump_scope(node)
73
+ raise "Not a node" unless Duby::AST::Node === node
74
+ begin
75
+ @jump_scope << node
76
+ yield
77
+ ensure
78
+ @jump_scope.pop
79
+ end
80
+ end
81
+
82
+ def find_ensures(before)
83
+ found = []
84
+ @jump_scope.reverse_each do |scope|
85
+ if Duby::AST::Ensure === scope
86
+ found << scope
87
+ end
88
+ break if before === scope
89
+ end
90
+ found
91
+ end
92
+
93
+ def begin_main
94
+ # declare argv variable
95
+ @method.local('argv', AST.type(nil, 'string', true))
96
+ end
97
+
98
+ def finish_main
99
+ @method.returnvoid
100
+ end
101
+
102
+ def prepare_binding(scope)
103
+ if scope.has_binding?
104
+ type = scope.binding_type
105
+ @binding = @bindings[type]
106
+ @method.new type
107
+ @method.dup
108
+ @method.invokespecial type, "<init>", [@method.void]
109
+ type.store(@method, @method.local('$binding', type))
110
+ end
111
+ begin
112
+ yield
113
+ ensure
114
+ if scope.has_binding?
115
+ @binding.stop
116
+ @binding = nil
117
+ end
118
+ end
119
+ end
120
+
121
+ def define_method(node)
122
+ push_jump_scope(node) do
123
+ base_define_method(node, true) do |method, arg_types|
124
+ return if @class.interface?
125
+
126
+ log "Starting new #{node.static? ? 'static ' : ''}method #{node.name}(#{arg_types})"
127
+ args = node.arguments.args
128
+ method_body(method, args, node, node.signature[:return])
129
+ log "Method #{node.name}(#{arg_types}) complete!"
130
+ end
131
+ end
132
+ end
133
+
134
+ def define_optarg_chain(name, arg, return_type,
135
+ args_for_opt, arg_types_for_opt)
136
+ # declare all args so they get their values
137
+ @method.aload(0) unless @static
138
+ args_for_opt.each do |req_arg|
139
+ req_arg.inferred_type.load(@method, @method.local(req_arg.name, req_arg.inferred_type))
140
+ end
141
+ arg.value.compile(self, true)
142
+
143
+ # invoke the next one in the chain
144
+ if @static
145
+ @method.invokestatic(@class, name.to_s, [return_type] + arg_types_for_opt + [arg.inferred_type])
146
+ else
147
+ @method.invokevirtual(@class, name.to_s, [return_type] + arg_types_for_opt + [arg.inferred_type])
148
+ end
149
+
150
+ return_type.return(@method)
151
+ end
152
+
153
+ def constructor(node)
154
+ push_jump_scope(node) do
155
+ super(node, true) do |method, args|
156
+ method_body(method, args, node, Types::Void) do
157
+ method.aload 0
158
+ if node.delegate_args
159
+ if node.calls_super
160
+ delegate_class = @type.superclass
161
+ else
162
+ delegate_class = @type
163
+ end
164
+ delegate_types = node.delegate_args.map do |arg|
165
+ arg.inferred_type
166
+ end
167
+ constructor = delegate_class.constructor(*delegate_types)
168
+ node.delegate_args.each do |arg|
169
+ arg.compile(self, true)
170
+ end
171
+ method.invokespecial(
172
+ delegate_class, "<init>",
173
+ [@method.void, *constructor.argument_types])
174
+ else
175
+ method.invokespecial @class.superclass, "<init>", [@method.void]
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
181
+
182
+ def method_body(method, args, node, return_type)
183
+ body = node.body
184
+ with(:method => method,
185
+ :declared_locals => {}) do
186
+
187
+ method.start
188
+
189
+ scope = node.static_scope
190
+
191
+ # declare all args so they get their values
192
+ if args
193
+ args.each {|arg| declare_local(scope, arg.name, arg.inferred_type)}
194
+ end
195
+ declare_locals(scope)
196
+
197
+ yield if block_given?
198
+
199
+ prepare_binding(node) do
200
+ expression = return_type != Types::Void
201
+ body.compile(self, expression) if body
202
+ end
203
+
204
+ return_type.return(@method)
205
+
206
+ @method.stop
207
+ end
208
+ end
209
+
210
+ def define_closure(class_def, expression)
211
+ compiler = ClosureCompiler.new(@file, @type, self)
212
+ compiler.define_class(class_def, expression)
213
+ end
214
+
215
+ def branch(iff, expression)
216
+ elselabel = @method.label
217
+ donelabel = @method.label
218
+
219
+ # this is ugly...need a better way to abstract the idea of compiling a
220
+ # conditional branch while still fitting into JVM opcodes
221
+ predicate = iff.condition.predicate
222
+ if iff.body || expression
223
+ jump_if_not(predicate, elselabel)
224
+
225
+ if iff.body
226
+ iff.body.compile(self, expression)
227
+ elsif expression
228
+ iff.inferred_type.init_value(@method)
229
+ end
230
+
231
+ @method.goto(donelabel)
232
+ else
233
+ jump_if(predicate, donelabel)
234
+ end
235
+
236
+ elselabel.set!
237
+
238
+ if iff.else
239
+ iff.else.compile(self, expression)
240
+ elsif expression
241
+ iff.inferred_type.init_value(@method)
242
+ end
243
+
244
+ donelabel.set!
245
+ end
246
+
247
+ def loop(loop, expression)
248
+ push_jump_scope(loop) do
249
+ with(:break_label => @method.label,
250
+ :redo_label => @method.label,
251
+ :next_label => @method.label) do
252
+ predicate = loop.condition.predicate
253
+
254
+ loop.init.compile(self, false) if loop.init?
255
+
256
+ pre_label = @redo_label
257
+
258
+ if loop.check_first
259
+ @next_label.set! unless loop.post?
260
+ if loop.negative
261
+ # if condition, exit
262
+ jump_if(predicate, @break_label)
263
+ else
264
+ # if not condition, exit
265
+ jump_if_not(predicate, @break_label)
266
+ end
267
+ end
268
+
269
+ if loop.pre?
270
+ pre_label = method.label
271
+ pre_label.set!
272
+ loop.pre.compile(self, false)
273
+ end
274
+
275
+
276
+ @redo_label.set!
277
+ loop.body.compile(self, false) if loop.body
278
+
279
+ if loop.check_first && !loop.post?
280
+ @method.goto(@next_label)
281
+ else
282
+ @next_label.set!
283
+ loop.post.compile(self, false) if loop.post?
284
+ if loop.negative
285
+ # if not condition, continue
286
+ jump_if_not(predicate, pre_label)
287
+ else
288
+ # if condition, continue
289
+ jump_if(predicate, pre_label)
290
+ end
291
+ end
292
+
293
+ @break_label.set!
294
+
295
+ # loops always evaluate to null
296
+ @method.aconst_null if expression
297
+ end
298
+ end
299
+ end
300
+
301
+ def break(node)
302
+ error("break outside of loop", node) unless @break_label
303
+ handle_ensures(find_ensures(Duby::AST::Loop))
304
+ @method.goto(@break_label)
305
+ end
306
+
307
+ def next(node)
308
+ error("next outside of loop", node) unless @next_label
309
+ handle_ensures(find_ensures(Duby::AST::Loop))
310
+ @method.goto(@next_label)
311
+ end
312
+
313
+ def redo(node)
314
+ error("redo outside of loop", node) unless @redo_label
315
+ handle_ensures(find_ensures(Duby::AST::Loop))
316
+ @method.goto(@redo_label)
317
+ end
318
+
319
+ def jump_if(predicate, target)
320
+ unless predicate.inferred_type == Types::Boolean
321
+ raise "Expected boolean, found #{predicate.inferred_type}"
322
+ end
323
+ if Duby::AST::Call === predicate
324
+ method = extract_method(predicate)
325
+ if method.respond_to? :jump_if
326
+ method.jump_if(self, predicate, target)
327
+ return
328
+ end
329
+ end
330
+ predicate.compile(self, true)
331
+ @method.ifne(target)
332
+ end
333
+
334
+ def jump_if_not(predicate, target)
335
+ unless predicate.inferred_type == Types::Boolean
336
+ raise "Expected boolean, found #{predicate.inferred_type}"
337
+ end
338
+ if Duby::AST::Call === predicate
339
+ method = extract_method(predicate)
340
+ if method.respond_to? :jump_if_not
341
+ method.jump_if_not(self, predicate, target)
342
+ return
343
+ end
344
+ end
345
+ predicate.compile(self, true)
346
+ @method.ifeq(target)
347
+ end
348
+
349
+ def extract_method(call)
350
+ target = call.target.inferred_type!
351
+ params = call.parameters.map do |param|
352
+ param.inferred_type!
353
+ end
354
+ target.get_method(call.name, params)
355
+ end
356
+
357
+ def call(call, expression)
358
+ return cast(call, expression) if call.cast?
359
+ method = extract_method(call)
360
+ if method
361
+ method.call(self, call, expression)
362
+ else
363
+ raise "Missing method #{target}.#{call.name}(#{params.join ', '})"
364
+ end
365
+ end
366
+
367
+ def self_call(fcall, expression)
368
+ return cast(fcall, expression) if fcall.cast?
369
+ type = fcall.scope.static_scope.self_type
370
+ type = type.meta if (@static && type == @type)
371
+ fcall.target = ImplicitSelf.new(type)
372
+
373
+ params = fcall.parameters.map do |param|
374
+ param.inferred_type
375
+ end
376
+ method = type.get_method(fcall.name, params)
377
+ unless method
378
+ target = static ? @class.name : 'self'
379
+
380
+ raise NameError, "No method %s.%s(%s)" %
381
+ [target, fcall.name, params.join(', ')]
382
+ end
383
+ method.call(self, fcall, expression)
384
+ end
385
+
386
+ def super_call(sup, expression)
387
+ type = @type.superclass
388
+ sup.target = ImplicitSelf.new(type)
389
+
390
+ params = sup.parameters.map do |param|
391
+ param.inferred_type
392
+ end
393
+ method = type.get_method(sup.name, params)
394
+ unless method
395
+
396
+ raise NameError, "No method %s.%s(%s)" %
397
+ [type, sup.name, params.join(', ')]
398
+ end
399
+ method.call_special(self, sup, expression)
400
+ end
401
+
402
+ def cast(fcall, expression)
403
+ # casting operation, not a call
404
+ castee = fcall.parameters[0]
405
+
406
+ # TODO move errors to inference phase
407
+ source_type_name = castee.inferred_type.name
408
+ target_type_name = fcall.inferred_type.name
409
+ if castee.inferred_type.primitive?
410
+ if fcall.inferred_type.primitive?
411
+ if source_type_name == 'boolean' && target_type_name != "boolean"
412
+ raise TypeError.new "not a boolean type: #{castee.inferred_type}"
413
+ end
414
+ # ok
415
+ primitive = true
416
+ else
417
+ raise TypeError.new "Cannot cast #{castee.inferred_type} to #{fcall.inferred_type}: not a reference type."
418
+ end
419
+ elsif fcall.inferred_type.primitive?
420
+ raise TypeError.new "not a primitive type: #{castee.inferred_type}"
421
+ else
422
+ # ok
423
+ primitive = false
424
+ end
425
+
426
+ castee.compile(self, expression)
427
+ if expression
428
+ if primitive
429
+ source_type_name = 'int' if %w[byte short char].include? source_type_name
430
+ if (source_type_name != 'int') && (%w[byte short char].include? target_type_name)
431
+ target_type_name = 'int'
432
+ end
433
+
434
+ if source_type_name != target_type_name
435
+ if RUBY_VERSION == "1.9"
436
+ @method.send "#{source_type_name[0]}2#{target_type_name[0]}"
437
+ else
438
+ @method.send "#{source_type_name[0].chr}2#{target_type_name[0].chr}"
439
+ end
440
+ end
441
+ else
442
+ if (source_type_name != target_type_name ||
443
+ castee.inferred_type.array? != fcall.inferred_type.array?)
444
+ @method.checkcast fcall.inferred_type
445
+ end
446
+ end
447
+ end
448
+ end
449
+
450
+ def body(body, expression)
451
+ # last element is an expression only if the body is an expression
452
+ super(body, expression) do |last|
453
+ compile(last, expression)
454
+ end
455
+ end
456
+
457
+ def local(scope, name, type)
458
+ type.load(@method, @method.local(scoped_local_name(name, scope), type))
459
+ end
460
+
461
+ def local_assign(scope, name, type, expression, value)
462
+ declare_local(scope, name, type)
463
+
464
+ value.compile(self, true)
465
+
466
+ # if expression, dup the value we're assigning
467
+ @method.dup if expression
468
+
469
+ type.store(@method, @method.local(scoped_local_name(name, scope), type))
470
+ end
471
+
472
+ def declared_locals
473
+ @declared_locals ||= {}
474
+ end
475
+
476
+ def annotate(builder, annotations)
477
+ annotations.each do |annotation|
478
+ type = annotation.type
479
+ type = type.jvm_type if type.respond_to?(:jvm_type)
480
+ builder.annotate(type, annotation.runtime?) do |visitor|
481
+ annotation.values.each do |name, value|
482
+ annotation_value(visitor, name, value)
483
+ end
484
+ end
485
+ end
486
+ end
487
+
488
+ def annotation_value(builder, name, value)
489
+ case value
490
+ when Duby::AST::Annotation
491
+ type = value.type
492
+ type = type.jvm_type if type.respond_to?(:jvm_type)
493
+ builder.annotation(name, type) do |child|
494
+ value.values.each do |name, value|
495
+ annotation_value(child, name, value)
496
+ end
497
+ end
498
+ when ::Array
499
+ builder.array(name) do |array|
500
+ value.each do |item|
501
+ annotation_value(array, nil, item)
502
+ end
503
+ end
504
+ else
505
+ builder.value(name, value)
506
+ end
507
+ end
508
+
509
+ def declared?(scope, name)
510
+ declared_locals.include?(scoped_local_name(name, scope))
511
+ end
512
+
513
+ def declare_local(scope, name, type)
514
+ # TODO confirm types are compatible
515
+ name = scoped_local_name(name, scope)
516
+ unless declared_locals[name]
517
+ declared_locals[name] = type
518
+ index = @method.local(name, type)
519
+ end
520
+ end
521
+
522
+ def local_declare(scope, name, type)
523
+ end
524
+
525
+ def declare_locals(scope)
526
+ scope.locals.each do |name|
527
+ unless scope.captured?(name) || declared?(scope, name)
528
+ type = scope.local_type(name)
529
+ declare_local(scope, name, type)
530
+ type.init_value(@method)
531
+ type.store(@method, @method.local(scoped_local_name(name, scope), type))
532
+ end
533
+ end
534
+ end
535
+
536
+ def get_binding(type)
537
+ @bindings[type]
538
+ end
539
+
540
+ def declared_captures(binding=nil)
541
+ @captured_locals[binding || @binding]
542
+ end
543
+
544
+ def captured_local_declare(scope, name, type)
545
+ unless declared_captures[name]
546
+ declared_captures[name] = type
547
+ # default should be fine, but I don't think bitescript supports it.
548
+ @binding.protected_field(name, type)
549
+ end
550
+ end
551
+
552
+ def captured_local(scope, name, type)
553
+ captured_local_declare(scope, name, type)
554
+ binding_reference
555
+ @method.getfield(scope.binding_type, name, type)
556
+ end
557
+
558
+ def captured_local_assign(node, expression)
559
+ scope, name, type = node.containing_scope, node.name, node.inferred_type
560
+ captured_local_declare(scope, name, type)
561
+ binding_reference
562
+ node.value.compile(self, true)
563
+ @method.dup_x2 if expression
564
+ @method.putfield(scope.binding_type, name, type)
565
+ end
566
+
567
+ def field(name, type, annotations, static_field)
568
+ name = name[1..-1] if name =~ /^@/
569
+
570
+ real_type = declared_fields[name] || type
571
+
572
+ declare_field(name, real_type, annotations, static_field)
573
+
574
+ # load self object unless static
575
+ method.aload 0 unless static || static_field
576
+
577
+ if static || static_field
578
+ @method.getstatic(@class, name, type)
579
+ else
580
+ @method.getfield(@class, name, type)
581
+ end
582
+ end
583
+
584
+ def declared_fields
585
+ @declared_fields ||= {}
586
+ @declared_fields[@class] ||= {}
587
+ end
588
+
589
+ def declare_field(name, type, annotations, static_field)
590
+ # TODO confirm types are compatible
591
+ unless declared_fields[name]
592
+ declared_fields[name] = type
593
+ field = if static || static_field
594
+ @class.private_static_field name, type
595
+ else
596
+ @class.private_field name, type
597
+ end
598
+ annotate(field, annotations)
599
+ end
600
+ end
601
+
602
+ def field_declare(name, type, annotations, static_field)
603
+ name = name[1..-1] if name =~ /^@/
604
+ declare_field(name, type, annotations, static_field)
605
+ end
606
+
607
+ def field_assign(name, type, expression, value, annotations, static_field)
608
+ name = name[1..-1] if name =~ /^@/
609
+
610
+ real_type = declared_fields[name] || type
611
+
612
+ declare_field(name, real_type, annotations, static_field)
613
+
614
+ method.aload 0 unless static || static_field
615
+ value.compile(self, true)
616
+ if expression
617
+ instruction = 'dup'
618
+ instruction << '2' if type.wide?
619
+ instruction << '_x1' unless static || static_field
620
+ method.send instruction
621
+ end
622
+
623
+ if static || static_field
624
+ @method.putstatic(@class, name, real_type)
625
+ else
626
+ @method.putfield(@class, name, real_type)
627
+ end
628
+ end
629
+
630
+ def string(value)
631
+ @method.ldc(value)
632
+ end
633
+
634
+ def build_string(nodes, expression)
635
+ if expression
636
+ # could probably be more efficient with non-default constructor
637
+ builder_class = Duby::AST.type(nil, 'java.lang.StringBuilder')
638
+ @method.new builder_class
639
+ @method.dup
640
+ @method.invokespecial builder_class, "<init>", [@method.void]
641
+
642
+ nodes.each do |node|
643
+ node.compile(self, true)
644
+ method = find_method(builder_class, "append", [node.inferred_type], false)
645
+ if method
646
+ @method.invokevirtual builder_class, "append", [method.return_type, *method.argument_types]
647
+ else
648
+ log "Could not find a match for #{java::lang::StringBuilder}.append(#{node.inferred_type})"
649
+ fail "Could not compile"
650
+ end
651
+ end
652
+
653
+ # convert to string
654
+ @method.invokevirtual java::lang::StringBuilder.java_class, "toString", [@method.string]
655
+ else
656
+ nodes.each do |node|
657
+ node.compile(self, false)
658
+ end
659
+ end
660
+ end
661
+
662
+ def to_string(body, expression)
663
+ if expression
664
+ body.compile(self, true)
665
+ body.inferred_type.box(@method) if body.inferred_type.primitive?
666
+ null = method.label
667
+ done = method.label
668
+ method.dup
669
+ method.ifnull(null)
670
+ @method.invokevirtual @method.object, "toString", [@method.string]
671
+ @method.goto(done)
672
+ null.set!
673
+ method.pop
674
+ method.ldc("null")
675
+ done.set!
676
+ else
677
+ body.compile(self, false)
678
+ end
679
+ end
680
+
681
+ def boolean(value)
682
+ value ? @method.iconst_1 : @method.iconst_0
683
+ end
684
+
685
+ def regexp(value, flags = 0)
686
+ # TODO: translate flags to Java-appropriate values
687
+ @method.ldc(value)
688
+ @method.invokestatic java::util::regex::Pattern, "compile", [java::util::regex::Pattern, @method.string]
689
+ end
690
+
691
+ def array(node, expression)
692
+ if expression
693
+ # create basic arraylist
694
+ @method.new java::util::ArrayList
695
+ @method.dup
696
+ @method.ldc_int node.children ? node.children.size : 0
697
+ @method.invokespecial java::util::ArrayList, "<init>", [@method.void, @method.int]
698
+
699
+ # elements, as expressions
700
+ # TODO: ensure they're all reference types!
701
+ node.children.each do |n|
702
+ @method.dup
703
+ n.compile(self, true)
704
+ # TODO this feels like it should be in the node.compile itself
705
+ if n.inferred_type.primitive?
706
+ n.inferred_type.box(@method)
707
+ end
708
+ @method.invokeinterface java::util::List, "add", [@method.boolean, @method.object]
709
+ @method.pop
710
+ end
711
+
712
+ # make it unmodifiable
713
+ @method.invokestatic java::util::Collections, "unmodifiableList", [java::util::List, java::util::List]
714
+ else
715
+ # elements, as non-expressions
716
+ # TODO: ensure they're all reference types!
717
+ node.children.each do |n|
718
+ n.compile(self, true)
719
+ # TODO this feels like it should be in the node.compile itself
720
+ if n.inferred_type.primitive?
721
+ n.inferred_type.box(@method)
722
+ end
723
+ end
724
+ end
725
+ end
726
+
727
+ def null
728
+ @method.aconst_null
729
+ end
730
+
731
+ def binding_reference
732
+ @method.aload(@method.local('$binding'))
733
+ end
734
+
735
+ def real_self
736
+ method.aload(0)
737
+ end
738
+
739
+ def line(num)
740
+ @method.line(num) if @method
741
+ end
742
+
743
+ def print(print_node)
744
+ @method.getstatic System, "out", PrintStream
745
+ print_node.parameters.each {|param| param.compile(self, true)}
746
+ params = print_node.parameters.map {|param| param.inferred_type.jvm_type}
747
+ method_name = print_node.println ? "println" : "print"
748
+ method = find_method(PrintStream.java_class, method_name, params, false)
749
+ if (method)
750
+ @method.invokevirtual(
751
+ PrintStream,
752
+ method_name,
753
+ [method.return_type, *method.parameter_types])
754
+ else
755
+ log "Could not find a match for #{PrintStream}.#{method_name}(#{params})"
756
+ fail "Could not compile"
757
+ end
758
+ end
759
+
760
+ def return(return_node)
761
+ return_node.value.compile(self, true) if return_node.value
762
+ handle_ensures(find_ensures(Duby::AST::MethodDefinition))
763
+ return_node.inferred_type.return(@method)
764
+ end
765
+
766
+ def _raise(exception)
767
+ exception.compile(self, true)
768
+ @method.athrow
769
+ end
770
+
771
+ def rescue(rescue_node, expression)
772
+ start = @method.label.set!
773
+ body_end = @method.label
774
+ done = @method.label
775
+ rescue_node.body.compile(self, expression)
776
+ body_end.set!
777
+ @method.goto(done)
778
+ rescue_node.clauses.each do |clause|
779
+ target = @method.label.set!
780
+ if clause.name
781
+ @method.astore(declare_local(clause.static_scope, clause.name, clause.type))
782
+ else
783
+ @method.pop
784
+ end
785
+ declare_locals(clause.static_scope)
786
+ clause.body.compile(self, expression)
787
+ @method.goto(done)
788
+ clause.types.each do |type|
789
+ @method.trycatch(start, body_end, target, type)
790
+ end
791
+ end
792
+ done.set!
793
+ end
794
+
795
+ def handle_ensures(nodes)
796
+ nodes.each do |ensure_node|
797
+ ensure_node.clause.compile(self, false)
798
+ end
799
+ end
800
+
801
+ def ensure(node, expression)
802
+ node.state = @method.label # Save the ensure target for JumpNodes
803
+ start = @method.label.set!
804
+ body_end = @method.label
805
+ done = @method.label
806
+ push_jump_scope(node) do
807
+ node.body.compile(self, expression) # First compile the body
808
+ end
809
+ body_end.set!
810
+ handle_ensures([node]) # run the ensure clause
811
+ @method.goto(done) # and continue on after the exception handler
812
+ target = @method.label.set! # Finally, create the exception handler
813
+ @method.trycatch(start, body_end, target, nil)
814
+ handle_ensures([node])
815
+ @method.athrow
816
+ done.set!
817
+ end
818
+
819
+ def empty_array(type, size)
820
+ size.compile(self, true)
821
+ type.newarray(@method)
822
+ end
823
+
824
+ def bootstrap_dynamic
825
+ # hacky, I know
826
+ unless defined? @class.bootstrapped
827
+ def @class.bootstrapped; true; end
828
+ method = @class.build_method("<clinit>", :public, :static, [], Java::void)
829
+ method.start
830
+ method.ldc org.mirah.DynalangBootstrap
831
+ method.ldc "bootstrap"
832
+ method.invokestatic java.dyn.Linkage, "registerBootstrapMethod", [method.void, java.lang.Class, method.string]
833
+ method.returnvoid
834
+ method.stop
835
+ end
836
+ end
837
+
838
+ class ClosureCompiler < Duby::Compiler::JVM
839
+ def initialize(file, type, parent)
840
+ @file = file
841
+ @type = type
842
+ @jump_scope = []
843
+ @parent = parent
844
+ end
845
+
846
+ def prepare_binding(scope)
847
+ if scope.has_binding?
848
+ type = scope.binding_type
849
+ @binding = @parent.get_binding(type)
850
+ @method.aload 0
851
+ @method.getfield(@class, 'binding', @binding)
852
+ type.store(@method, @method.local('$binding', type))
853
+ end
854
+ begin
855
+ yield
856
+ ensure
857
+ if scope.has_binding?
858
+ @binding = nil
859
+ end
860
+ end
861
+ end
862
+
863
+ def declared_captures
864
+ @parent.declared_captures(@binding)
865
+ end
866
+ end
867
+ end
868
+ end
869
+ end
870
+
871
+ if __FILE__ == $0
872
+ Duby::Typer.verbose = true
873
+ Duby::AST.verbose = true
874
+ Duby::Compiler::JVM.verbose = true
875
+ ast = Duby::AST.parse(File.read(ARGV[0]))
876
+
877
+ typer = Duby::Typer::Simple.new(:script)
878
+ ast.infer(typer)
879
+ typer.resolve(true)
880
+
881
+ compiler = Duby::Compiler::JVM.new(ARGV[0])
882
+ compiler.compile(ast)
883
+
884
+ compiler.generate
885
+ end