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