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,203 @@
1
+ module Duby
2
+ module JVM
3
+ module MethodLookup
4
+ # dummy log; it's expected the inclusion target will have it
5
+ def log(msg); end
6
+
7
+ def find_method(mapped_type, name, mapped_params, meta)
8
+ raise ArgumentError if mapped_params.any? {|p| p.nil?}
9
+ if name == 'new'
10
+ if meta
11
+ name = "<init>"
12
+ constructor = true
13
+ else
14
+ constructor = false
15
+ end
16
+ end
17
+
18
+ begin
19
+ if constructor
20
+ method = mapped_type.constructor(*mapped_params)
21
+ else
22
+ method = mapped_type.java_method(name, *mapped_params)
23
+ end
24
+ rescue NameError
25
+ # exact args failed, do a deeper search
26
+ log "No exact match for #{mapped_type.name}.#{name}(#{mapped_params.map(&:name).join ', '})"
27
+
28
+ method = find_jls(mapped_type, name, mapped_params, meta, constructor)
29
+
30
+ unless method
31
+ log "Failed to locate method #{mapped_type.name}.#{name}(#{mapped_params.map(&:name).join ', '})"
32
+ return nil
33
+ end
34
+ end
35
+
36
+ log "Found method #{method.declaring_class.name}.#{name}(#{method.argument_types.map(&:name).join ', '}) from #{mapped_type.name}"
37
+ return method
38
+ end
39
+
40
+ def find_jls(mapped_type, name, mapped_params, meta, constructor)
41
+ if constructor
42
+ by_name = mapped_type.unmeta.declared_constructors
43
+ elsif meta
44
+ by_name = mapped_type.declared_class_methods(name)
45
+ else
46
+ by_name = []
47
+ cls = mapped_type
48
+ while cls
49
+ by_name += cls.declared_instance_methods(name)
50
+ cls = cls.superclass
51
+ end
52
+ end
53
+ # filter by arity
54
+ by_name_and_arity = by_name.select {|m| m.argument_types.size == mapped_params.size}
55
+
56
+ phase1_methods = phase1(mapped_params, by_name_and_arity)
57
+
58
+ if phase1_methods.size > 1
59
+ method_list = phase1_methods.map do |m|
60
+
61
+ "#{m.name}(#{m.parameter_types.map(&:name).join(', ')})"
62
+ end.join("\n")
63
+ raise "Ambiguous targets invoking #{mapped_type}.#{name}:\n#{method_list}"
64
+ end
65
+
66
+ phase1_methods[0] ||
67
+ phase2(mapped_params, by_name) ||
68
+ phase3(mapped_params, by_name) ||
69
+ field_lookup(mapped_params, mapped_type, meta, name) ||
70
+ inner_class(mapped_params, mapped_type, meta, name)
71
+ end
72
+
73
+ def phase1(mapped_params, potentials)
74
+ log "Beginning JLS phase 1 search with params (#{mapped_params.map(&:name)})"
75
+
76
+ # cycle through methods looking for more specific matches; gather matches of equal specificity
77
+ methods = potentials.inject([]) do |currents, potential|
78
+ method_params = potential.argument_types
79
+ raise "Bad arguments for method #{potential.declaring_class}.#{potential.name}" unless method_params.all?
80
+
81
+ # exact match always wins; duplicates not possible
82
+ if each_is_exact(mapped_params, method_params)
83
+ return [potential]
84
+ end
85
+
86
+ # otherwise, check for potential match and compare to current
87
+ # TODO: missing ambiguity check; picks last method of equal specificity
88
+ if each_is_exact_or_subtype_or_convertible(mapped_params, method_params)
89
+ if currents.size > 0
90
+ if is_more_specific?(potential.argument_types, currents[0].argument_types)
91
+ # potential is better, dump all currents
92
+ currents = [potential]
93
+ elsif is_more_specific?(currents[0].argument_types, potential.argument_types)
94
+ # currents are better, try next potential
95
+ #next
96
+ else
97
+ # equal specificity, append to currents
98
+ currents << potential
99
+ end
100
+ else
101
+ # no previous matches, use potential
102
+ currents = [potential]
103
+ end
104
+ end
105
+
106
+ currents
107
+ end
108
+
109
+ methods
110
+ end
111
+
112
+ def is_more_specific?(potential, current)
113
+ each_is_exact_or_subtype_or_convertible(potential, current)
114
+ end
115
+
116
+ def phase2(mapped_params, potentials)
117
+ nil
118
+ end
119
+
120
+ def phase3(mapped_params, potentials)
121
+ nil
122
+ end
123
+
124
+ def field_lookup(mapped_params, mapped_type, meta, name)
125
+ log("Attempting #{meta ? 'static' : 'instance'} field lookup for '#{name}' on class #{mapped_type}")
126
+ # if we get to this point, the potentials do not match, so we ignore them
127
+
128
+
129
+ # search for a field of the given name
130
+ if name =~ /_set$/
131
+ # setter
132
+ setter = true
133
+ name = name[0..-5]
134
+ field = mapped_type.field_setter(name)
135
+ else
136
+ # getter
137
+ setter = false
138
+
139
+ # field accesses don't take arguments
140
+ return if mapped_params.size > 0
141
+ field = mapped_type.field_getter(name)
142
+ end
143
+
144
+ return nil unless field
145
+
146
+ if (meta && !field.static?) ||
147
+ (!meta && field.static?)
148
+ field == nil
149
+ end
150
+
151
+ # check accessibility
152
+ # TODO: protected field access check appropriate to current type
153
+ if setter
154
+ raise "cannot set final field '#{name}' on class #{mapped_type}" if field.final?
155
+ end
156
+ raise "cannot access field '#{name}' on class #{mapped_type}" unless field.public?
157
+
158
+ field
159
+ end
160
+
161
+ def inner_class(params, type, meta, name)
162
+ return unless params.size == 0 && meta
163
+ log("Attempting inner class lookup for '#{name}' on #{type}")
164
+ type.inner_class_getter(name)
165
+ end
166
+
167
+ def each_is_exact(incoming, target)
168
+ incoming.each_with_index do |in_type, i|
169
+ target_type = target[i]
170
+
171
+ # exact match
172
+ return false unless target_type == in_type
173
+ end
174
+ return true
175
+ end
176
+
177
+ def each_is_exact_or_subtype_or_convertible(incoming, target)
178
+ incoming.each_with_index do |in_type, i|
179
+ target_type = target[i]
180
+
181
+ # exact match
182
+ next if target_type == in_type
183
+
184
+ # primitive is safely convertible
185
+ if target_type.primitive?
186
+ if in_type.primitive?
187
+ next if primitive_convertible? in_type, target_type
188
+ end
189
+ return false
190
+ end
191
+
192
+ # object type is assignable
193
+ return false unless target_type.assignable_from? in_type
194
+ end
195
+ return true
196
+ end
197
+
198
+ def primitive_convertible?(in_type, target_type)
199
+ in_type.convertible_to?(target_type)
200
+ end
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,737 @@
1
+ require 'mirah'
2
+ require 'mirah/ast'
3
+ require 'mirah/jvm/types'
4
+ require 'mirah/jvm/compiler'
5
+ require 'mirah/jvm/source_generator/builder'
6
+ require 'mirah/jvm/source_generator/precompile'
7
+ require 'mirah/jvm/source_generator/loops'
8
+
9
+ class String
10
+ def compile(compiler, expression)
11
+ compiler.method.print self if expression
12
+ end
13
+ end
14
+
15
+ module Duby
16
+ module Compiler
17
+ class JavaSource < JVMCompilerBase
18
+ JVMTypes = Duby::JVM::Types
19
+ attr_accessor :lvalue
20
+
21
+ Operators = [
22
+ '+', '-', '+@', '-@', '/', '%', '*', '<',
23
+ '<=', '==', '!=', '>=', '>',
24
+ '<<', '>>', '>>>', '|', '&', '^', '~'
25
+ ]
26
+ ArrayOps = [
27
+ '[]', '[]=', 'length'
28
+ ]
29
+
30
+ ImplicitReturn = Struct.new(:value)
31
+
32
+ def initialize
33
+ super
34
+ end
35
+
36
+ def file_builder(filename)
37
+ Duby::JavaSource::Builder.new(filename, self)
38
+ end
39
+
40
+ def output_type
41
+ "source files"
42
+ end
43
+
44
+ def define_method(node)
45
+ base_define_method(node, false) do |method, _|
46
+ with :method => method do
47
+ log "Starting new method #{node.name}"
48
+ @method.start
49
+
50
+ prepare_binding(node) do
51
+ declare_locals(node.static_scope)
52
+ unless @method.type.nil? || @method.type.void?
53
+ self.return(ImplicitReturn.new(node.body))
54
+ else
55
+ node.body.compile(self, false) if node.body
56
+ end
57
+ end
58
+
59
+ log "Method #{node.name} complete!"
60
+ @method.stop
61
+ end
62
+ end
63
+ end
64
+
65
+ def annotate(node, annotations)
66
+ node.annotate(annotations)
67
+ end
68
+
69
+ def define_optarg_chain(name, arg, return_type,
70
+ args_for_opt, arg_types_for_opt)
71
+ # declare all args so they get their values
72
+ @method.print "return " unless @method.type.nil? || @method.type.void?
73
+ @method.print "this." unless @static
74
+ @method.print "#{name}("
75
+ @method.print args_for_opt.map(&:name).join(', ')
76
+ @method.print ', 'if args_for_opt.size > 0
77
+ arg.value.compile(self, true)
78
+
79
+ # invoke the next one in the chain
80
+ @method.print ");\n"
81
+ end
82
+
83
+ def constructor(node)
84
+ super(node, false) do |method, _|
85
+ with :method => method do
86
+ @method.start
87
+ if node.delegate_args
88
+ delegate = if node.calls_super
89
+ "super"
90
+ else
91
+ "this"
92
+ end
93
+ method.print "#{delegate}("
94
+ node.delegate_args.each_with_index do |arg, index|
95
+ method.print ', ' unless index == 0
96
+ raise "Invalid constructor argument #{arg}" unless arg.expr?(self)
97
+ arg.compile(self, true)
98
+ end
99
+ method.puts ");"
100
+ end
101
+
102
+ prepare_binding(node) do
103
+ declare_locals(node.static_scope)
104
+ node.body.compile(self, false) if node.body
105
+ end
106
+ method.stop
107
+ end
108
+ end
109
+ end
110
+
111
+ def prepare_binding(scope)
112
+ if scope.has_binding?
113
+ type = scope.binding_type
114
+ @binding = @bindings[type]
115
+ @method.puts "#{type.to_source} $binding = new #{type.to_source}();"
116
+ end
117
+ begin
118
+ yield
119
+ ensure
120
+ if scope.has_binding?
121
+ @binding.stop
122
+ @binding = nil
123
+ end
124
+ end
125
+ end
126
+
127
+ def define_closure(class_def, expression)
128
+ compiler = ClosureCompiler.new(@file, @type, self)
129
+ compiler.define_class(class_def, expression)
130
+ end
131
+
132
+ def return(node)
133
+ if @method.type.nil? || @method.type.void?
134
+ @method.puts 'return;'
135
+ return
136
+ end
137
+ if node.value.expr?(self)
138
+ @method.print 'return '
139
+ node.value.compile(self, true)
140
+ @method.puts ';'
141
+ else
142
+ store_value('return ', node.value)
143
+ end
144
+ end
145
+
146
+ def _raise(node)
147
+ if node.expr?(self)
148
+ @method.print 'throw '
149
+ node.compile(self, true)
150
+ @method.puts ';'
151
+ else
152
+ store_value('throw ', node)
153
+ end
154
+ end
155
+
156
+ def rescue(node, expression)
157
+ @method.block 'try' do
158
+ maybe_store(node.body, expression)
159
+ end
160
+ node.clauses.each do |clause|
161
+ clause.types.each do |type|
162
+ name = scoped_local_name(clause.name || 'tmp$ex', clause.static_scope)
163
+ @method.declare_local(type, name, false)
164
+ @method.block "catch (#{type.to_source} #{name})" do
165
+ declare_locals(clause.static_scope)
166
+ maybe_store(clause.body, expression)
167
+ end
168
+ end
169
+ end
170
+ end
171
+
172
+ def ensure(node, expression)
173
+ @method.block 'try' do
174
+ maybe_store(node.body, expression)
175
+ end
176
+ @method.block 'finally' do
177
+ node.clause.compile(self, false)
178
+ end
179
+ end
180
+
181
+ def line(num)
182
+ end
183
+
184
+ def declare_local(name, type)
185
+ @method.declare_local(type, name)
186
+ end
187
+
188
+ def declare_field(name, type, annotations)
189
+ @class.declare_field(name, type, @static, 'private', annotations)
190
+ end
191
+
192
+ def local(scope, name, type)
193
+ name = scoped_local_name(name, scope)
194
+ @method.print name
195
+ end
196
+
197
+ def field(name, type, annotations)
198
+ name = name[1..-1] if name =~ /^@/
199
+ declare_field(name, type, annotations)
200
+ @method.print "#{this}.#{name}"
201
+ end
202
+
203
+ def this(method=nil)
204
+ if method && method.static?
205
+ method.declaring_class.name
206
+ elsif @self_scope && @self_scope.self_node
207
+ scoped_local_name('self', @self_scope)
208
+ else
209
+ @static ? @class.class_name : 'this'
210
+ end
211
+ end
212
+
213
+ def declare_locals(scope)
214
+ scope.locals.each do |name|
215
+ full_name = scoped_local_name(name, scope)
216
+ unless scope.captured?(name) || method.local?(full_name)
217
+ declare_local(full_name, scope.local_type(name))
218
+ end
219
+ end
220
+ end
221
+
222
+ def local_assign(scope, name, type, expression, value)
223
+ simple = value.expr?(self)
224
+ value = value.precompile(self)
225
+ name = scoped_local_name(name, scope)
226
+ if method.local?(name)
227
+ if expression
228
+ if simple
229
+ @method.print '('
230
+ else
231
+ @method.print @lvalue
232
+ end
233
+ end
234
+ @method.print "#{name} = "
235
+ value.compile(self, true)
236
+ if simple && expression
237
+ @method.print ')'
238
+ else
239
+ @method.puts ';'
240
+ end
241
+ else
242
+ @method.declare_local(type, name) do
243
+ value.compile(self, true)
244
+ end
245
+ if expression
246
+ @method.puts "#{@lvalue}#{name};"
247
+ end
248
+ end
249
+ end
250
+
251
+ def field_declare(name, type, annotations)
252
+ name = name[1..-1] if name =~ /^@/
253
+ declare_field(name, type, annotations)
254
+ end
255
+
256
+ def local_declare(scope, name, type)
257
+ name = scoped_local_name(name, scope)
258
+ declare_local(name, type)
259
+ end
260
+
261
+ def field_assign(name, type, expression, value, annotations)
262
+ name = name[1..-1] if name =~ /^@/
263
+ declare_field(name, type, annotations)
264
+ lvalue = "#{@lvalue if expression}#{this}.#{name} = "
265
+ store_value(lvalue, value)
266
+ end
267
+
268
+ def captured_local_declare(scope, name, type)
269
+ unless declared_captures[name]
270
+ declared_captures[name] = type
271
+ @binding.declare_field(name, type, false, '')
272
+ end
273
+ end
274
+
275
+ def captured_local(scope, name, type)
276
+ captured_local_declare(scope, name, type)
277
+ @method.print "$binding.#{name}"
278
+ end
279
+
280
+ def captured_local_assign(node, expression)
281
+ scope, name, type = node.containing_scope, node.name, node.inferred_type
282
+ captured_local_declare(scope, name, type)
283
+ lvalue = "#{@lvalue if expression}$binding.#{name} = "
284
+ store_value(lvalue, node.value)
285
+ end
286
+
287
+ def store_value(lvalue, value)
288
+ if value.is_a? String
289
+ @method.puts "#{lvalue}#{value};"
290
+ elsif value.expr?(self)
291
+ @method.print lvalue
292
+ value.compile(self, true)
293
+ @method.puts ';'
294
+ else
295
+ with :lvalue => lvalue do
296
+ value.compile(self, true)
297
+ end
298
+ end
299
+ end
300
+
301
+ def assign(name, value)
302
+ store_value("#{name} = ", value)
303
+ name
304
+ end
305
+
306
+ def maybe_store(value, expression)
307
+ if expression
308
+ store_value(@lvalue, value)
309
+ else
310
+ value.compile(self, false)
311
+ end
312
+ end
313
+
314
+ def body(body, expression)
315
+ super(body, expression) do |last|
316
+ maybe_store(last, expression)
317
+ end
318
+ end
319
+
320
+ def scoped_body(scope, expression)
321
+ @method.block do
322
+ super
323
+ end
324
+ end
325
+
326
+ def branch_expression(node)
327
+ node.condition.compile(self, true)
328
+ @method.print ' ? ('
329
+ if node.body
330
+ node.body.compile(self, true)
331
+ else
332
+ @method.print @method.init_value(node.inferred_type)
333
+ end
334
+ @method.print ') : ('
335
+ if node.else
336
+ node.else.compile(self, true)
337
+ else
338
+ @method.print @method.init_value(node.inferred_type)
339
+ end
340
+ @method.print ')'
341
+ end
342
+
343
+ def branch(node, expression)
344
+ if expression && node.expr?(self)
345
+ return branch_expression(node)
346
+ end
347
+ predicate = node.condition.predicate.precompile(self)
348
+ @method.print 'if ('
349
+ predicate.compile(self, true)
350
+ @method.block ")" do
351
+ if node.body
352
+ maybe_store(node.body, expression)
353
+ elsif expression
354
+ store_value(@lvalue, @method.init_value(node.inferred_type))
355
+ end
356
+ end
357
+ if node.else || expression
358
+ @method.block 'else' do
359
+ if node.else
360
+ maybe_store(node.else, expression)
361
+ else
362
+ store_value(@lvalue, @method.init_value(node.inferred_type))
363
+ end
364
+ end
365
+ end
366
+ end
367
+
368
+ def loop(loop, expression)
369
+ if loop.redo? || loop.post || !loop.condition.predicate.expr?(self)
370
+ loop = ComplexWhileLoop.new(loop, self)
371
+ else
372
+ loop = SimpleWhileLoop.new(loop, self)
373
+ end
374
+ with(:loop => loop) do
375
+ loop.compile(expression)
376
+ end
377
+ end
378
+
379
+ def expr?(target, params)
380
+ !([target] + params).any? {|x| x.kind_of? Duby::AST::TempValue}
381
+ end
382
+
383
+ def operator(target, op, params, expression)
384
+ simple = expr?(target, params)
385
+ if expression && !simple
386
+ @method.print @lvalue
387
+ end
388
+ if params.size == 0
389
+ # unary operator
390
+ op = op[0,1]
391
+ @method.print op
392
+ target.compile(self, true)
393
+ else
394
+ @method.print '('
395
+ other = params[0]
396
+ target.compile(self, true)
397
+ @method.print " #{op} "
398
+ other.compile(self, true)
399
+ @method.print ')'
400
+ end
401
+ unless expression && simple
402
+ @method.puts ';'
403
+ end
404
+ end
405
+
406
+ def precompile_nodes(nodes)
407
+ if nodes.all? {|n| n.expr?(self)}
408
+ nodes
409
+ else
410
+ nodes.map do |node|
411
+ tempval = node.precompile(self)
412
+ if node == tempval && !node.kind_of?(Duby::AST::Literal)
413
+ tempval = node.temp(self)
414
+ end
415
+ tempval
416
+ end
417
+ end
418
+ end
419
+
420
+ def compile_args(call)
421
+ precompile_nodes(call.parameters)
422
+ end
423
+
424
+ def self_type
425
+ type = AST.type(nil, @class.name.tr('/', '.'))
426
+ type = type.meta if @static
427
+ type
428
+ end
429
+
430
+ def super_call(call, expression)
431
+ super_method_call(this, call, compile_args(call), expression)
432
+ end
433
+
434
+ def cast(call, expression)
435
+ args = compile_args(call)
436
+ simple = call.expr?(self)
437
+ @method.print @lvalue if expression && !simple
438
+ @method.print "((#{call.inferred_type.to_source})("
439
+ args.each{|arg| arg.compile(self, true)}
440
+ @method.print "))"
441
+ @method.puts ';' unless simple && expression
442
+ end
443
+
444
+ def self_call(call, expression)
445
+ if call.cast?
446
+ cast(call, expression)
447
+ else
448
+ type = call.scope.static_scope.self_type
449
+ type = type.meta if (@static && type == @type)
450
+ params = call.parameters.map do |param|
451
+ param.inferred_type
452
+ end
453
+ method = type.get_method(call.name, params)
454
+ method_call(this(method), call, compile_args(call), expression)
455
+ end
456
+ end
457
+
458
+ def call(call, expression)
459
+ return cast(call, expression) if call.cast?
460
+ if Duby::AST::Constant === call.target
461
+ target = call.target.inferred_type.to_source
462
+ else
463
+ target = call.precompile_target(self)
464
+ end
465
+ params = compile_args(call)
466
+
467
+ if Operators.include? call.name
468
+ operator(target, call.name, params, expression)
469
+ elsif call.target.inferred_type.array? && ArrayOps.include?(call.name)
470
+ array_op(target, call.name, params, expression)
471
+ elsif call.name == 'nil?'
472
+ operator(target, '==', ['null'], expression)
473
+ else
474
+ method_call(target, call, params, expression)
475
+ end
476
+ end
477
+
478
+ def array_op(target, name, args, expression)
479
+ simple = expr?(target, args)
480
+ index, value = args
481
+ if expression && !simple
482
+ @method.print @lvalue
483
+ end
484
+ target.compile(self, true)
485
+ if name == 'length'
486
+ @method.print '.length'
487
+ else
488
+ @method.print '['
489
+ index.compile(self, true)
490
+ @method.print ']'
491
+ if name == '[]='
492
+ @method.print " = "
493
+ value.compile(self, true)
494
+ end
495
+ end
496
+ unless simple && expression
497
+ @method.puts ';'
498
+ end
499
+ end
500
+
501
+ def break(node)
502
+ error("break outside of loop", node) unless @loop
503
+ @loop.break
504
+ end
505
+
506
+ def next(node)
507
+ error("next outside of loop", node) unless @loop
508
+ @loop.next
509
+ end
510
+
511
+ def redo(node)
512
+ error("redo outside of loop", node) unless @loop
513
+ @loop.redo
514
+ end
515
+
516
+ # TODO: merge cleanly with method_call logic
517
+ def super_method_call(target, call, params, expression)
518
+ simple = call.expr?(self)
519
+ method = call.method(self)
520
+ unless simple || method.return_type.void?
521
+ @method.print @lvalue if expression
522
+ end
523
+ if method.constructor?
524
+ @method.print "super("
525
+ else
526
+ @method.print "super.#{method.name}("
527
+ end
528
+ params.each_with_index do |param, index|
529
+ @method.print ', ' unless index == 0
530
+ param.compile(self, true)
531
+ end
532
+ if simple && expression
533
+ @method.print ')'
534
+ else
535
+ @method.puts ');'
536
+ end
537
+ if method.return_type.void? && expression
538
+ @method.print @lvalue
539
+ if method.static?
540
+ @method.puts 'null;'
541
+ else
542
+ target.compile(self, true)
543
+ @method.puts ';'
544
+ end
545
+ end
546
+
547
+ end
548
+
549
+ def method_call(target, call, params, expression)
550
+ simple = call.expr?(self)
551
+ method = call.method(self)
552
+ unless simple || method.return_type.void?
553
+ @method.print @lvalue if expression
554
+ end
555
+
556
+ # preamble
557
+ if method.constructor?
558
+ @method.print "new "
559
+ target.compile(self, true)
560
+ @method.print '('
561
+ elsif method.field?
562
+ target.compile(self, true)
563
+ @method.print ".#{method.name}"
564
+ if method.argument_types.size == 1
565
+ @method.print " = ("
566
+ end
567
+ elsif Duby::JVM::Types::Intrinsic === method
568
+ method.call(self, call, expression)
569
+ return
570
+ else
571
+ target.compile(self, true)
572
+ @method.print ".#{method.name}("
573
+ end
574
+
575
+ # args
576
+ params.each_with_index do |param, index|
577
+ @method.print ', ' unless index == 0
578
+ param.compile(self, true)
579
+ end
580
+
581
+ # postamble
582
+ if !method.field? || (method.field? && method.argument_types.size == 1)
583
+ if simple && expression
584
+ @method.print ')'
585
+ else
586
+ @method.puts ');'
587
+ end
588
+ end
589
+
590
+ # cleanup
591
+ if method.return_type.void? && expression
592
+ @method.print @lvalue
593
+ if method.static?
594
+ @method.puts 'null;'
595
+ else
596
+ target.compile(self, true)
597
+ @method.puts ';'
598
+ end
599
+ end
600
+ end
601
+
602
+ def temp(expression, value=nil)
603
+ value ||= expression
604
+ type = value.inferred_type
605
+ if value.expr?(self)
606
+ @method.tmp(type) do
607
+ value.compile(self, true)
608
+ end
609
+ else
610
+ assign(@method.tmp(type), value)
611
+ end
612
+ end
613
+
614
+ def empty_array(type, size)
615
+ sizevar = size.precompile(self)
616
+ @method.print "#{@lvalue unless size.expr?(self)}new #{type.name}["
617
+ sizevar.compile(self, true)
618
+ @method.print ']'
619
+ end
620
+
621
+ def string(value)
622
+ @method.print value.inspect
623
+ end
624
+
625
+ def boolean(value)
626
+ @method.print value ? 'true' : 'false'
627
+ end
628
+
629
+ def regexp(value, flags = 0)
630
+ @method.print "java.util.regex.Pattern.compile("
631
+ @method.print value.inspect
632
+ @method.print ")"
633
+ end
634
+
635
+ def array(node, expression)
636
+ if expression
637
+ # create unmodifiable list from array (simplest way to do this in Java source)
638
+ @method.print "java.util.Collections.unmodifiableList(java.util.Arrays.asList("
639
+
640
+ # elements, as expressions
641
+ comma = false
642
+ node.children.each do |n|
643
+ @method.print ", " if comma
644
+ n.compile(self, true)
645
+ comma = true
646
+ end
647
+
648
+ @method.print("))")
649
+ else
650
+ # elements, as non-expressions
651
+ # TODO: ensure they're all reference types!
652
+ node.children.each do |n|
653
+ n.compile(self, false)
654
+ end
655
+ end
656
+ end
657
+
658
+ def build_string(orig_nodes, expression)
659
+ if expression
660
+ nodes = precompile_nodes(orig_nodes)
661
+ simple = nodes.equal?(orig_nodes)
662
+ if !simple
663
+ @method.print(lvalue)
664
+ end
665
+ first = true
666
+ unless nodes[0].kind_of?(Duby::AST::String)
667
+ @method.print '""'
668
+ first = false
669
+ end
670
+ nodes.each do |node|
671
+ @method.print ' + ' unless first
672
+ first = false
673
+ node.compile(self, true)
674
+ end
675
+ @method.puts ';' unless simple
676
+ else
677
+ orig_nodes.each {|n| n.compile(self, false)}
678
+ end
679
+ end
680
+
681
+ def to_string(body, expression)
682
+ body.compile(self, expression)
683
+ end
684
+
685
+ def null
686
+ @method.print 'null'
687
+ end
688
+
689
+ def binding_reference
690
+ @method.print '$binding'
691
+ end
692
+
693
+ def real_self
694
+ @method.print 'this'
695
+ end
696
+
697
+ def print(node)
698
+ value = node.parameters[0]
699
+ value = value && value.precompile(self)
700
+ if node.println
701
+ @method.print "System.out.println("
702
+ else
703
+ @method.print "System.out.print("
704
+ end
705
+ value.compile(self, true) if value
706
+ @method.puts ');'
707
+ end
708
+
709
+ class ClosureCompiler < JavaSource
710
+ def initialize(file, type, parent)
711
+ @file = file
712
+ @type = type
713
+ @parent = parent
714
+ end
715
+
716
+ def prepare_binding(scope)
717
+ if scope.has_binding?
718
+ type = scope.binding_type
719
+ @binding = @parent.get_binding(type)
720
+ @method.puts("#{type.to_source} $binding = this.binding;")
721
+ end
722
+ begin
723
+ yield
724
+ ensure
725
+ if scope.has_binding?
726
+ @binding = nil
727
+ end
728
+ end
729
+ end
730
+
731
+ def declared_captures
732
+ @parent.declared_captures(@binding)
733
+ end
734
+ end
735
+ end
736
+ end
737
+ end