mirah 0.0.4-java

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