duby 0.0.2-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.
- data/History.txt +8 -0
- data/README.txt +39 -0
- data/Rakefile +13 -0
- data/bin/duby +9 -0
- data/bin/dubyc +9 -0
- data/bin/dubyp +9 -0
- data/examples/README +16 -0
- data/examples/appengine/Rakefile +72 -0
- data/examples/appengine/Readme +27 -0
- data/examples/appengine/config.ru +7 -0
- data/examples/appengine/lib/duby/plugin/datastore.rb +171 -0
- data/examples/appengine/src/com/google/appengine/ext/duby/db/Model.duby +132 -0
- data/examples/appengine/src/com/ribrdb/DubyApp.duby +28 -0
- data/examples/appengine/src/com/ribrdb/list.dhtml +15 -0
- data/examples/construction.duby +8 -0
- data/examples/edb.duby +3 -0
- data/examples/fib.duby +24 -0
- data/examples/fields.duby +22 -0
- data/examples/fractal.duby +57 -0
- data/examples/java_thing.duby +13 -0
- data/examples/simple_class.duby +12 -0
- data/examples/swing.duby +20 -0
- data/examples/tak.duby +15 -0
- data/examples/test.edb +9 -0
- data/javalib/JRubyParser.jar +0 -0
- data/lib/duby.rb +168 -0
- data/lib/duby/ast.rb +386 -0
- data/lib/duby/ast/call.rb +145 -0
- data/lib/duby/ast/class.rb +154 -0
- data/lib/duby/ast/flow.rb +332 -0
- data/lib/duby/ast/intrinsics.rb +56 -0
- data/lib/duby/ast/literal.rb +97 -0
- data/lib/duby/ast/local.rb +92 -0
- data/lib/duby/ast/method.rb +244 -0
- data/lib/duby/ast/structure.rb +62 -0
- data/lib/duby/ast/type.rb +93 -0
- data/lib/duby/c/compiler.rb +134 -0
- data/lib/duby/compiler.rb +282 -0
- data/lib/duby/jvm/compiler.rb +766 -0
- data/lib/duby/jvm/method_lookup.rb +193 -0
- data/lib/duby/jvm/source_compiler.rb +605 -0
- data/lib/duby/jvm/source_generator/builder.rb +387 -0
- data/lib/duby/jvm/source_generator/loops.rb +110 -0
- data/lib/duby/jvm/source_generator/precompile.rb +170 -0
- data/lib/duby/jvm/source_generator/typer.rb +11 -0
- data/lib/duby/jvm/typer.rb +131 -0
- data/lib/duby/jvm/types.rb +331 -0
- data/lib/duby/jvm/types/basic_types.rb +19 -0
- data/lib/duby/jvm/types/boolean.rb +11 -0
- data/lib/duby/jvm/types/enumerable.rb +63 -0
- data/lib/duby/jvm/types/factory.rb +155 -0
- data/lib/duby/jvm/types/floats.rb +70 -0
- data/lib/duby/jvm/types/integers.rb +110 -0
- data/lib/duby/jvm/types/intrinsics.rb +230 -0
- data/lib/duby/jvm/types/literals.rb +82 -0
- data/lib/duby/jvm/types/methods.rb +381 -0
- data/lib/duby/jvm/types/number.rb +92 -0
- data/lib/duby/nbcompiler.rb +29 -0
- data/lib/duby/old/compiler_old.rb +845 -0
- data/lib/duby/old/declaration.rb +72 -0
- data/lib/duby/old/mapper.rb +72 -0
- data/lib/duby/old/signature.rb +52 -0
- data/lib/duby/old/typer_old.rb +163 -0
- data/lib/duby/plugin/edb.rb +25 -0
- data/lib/duby/plugin/java.rb +42 -0
- data/lib/duby/plugin/math.rb +84 -0
- data/lib/duby/transform.rb +1028 -0
- data/lib/duby/typer.rb +369 -0
- data/test/TestUser.class +0 -0
- data/test/test_ast.rb +391 -0
- data/test/test_compilation.rb +98 -0
- data/test/test_java_typer.rb +199 -0
- data/test/test_javac_compiler.rb +58 -0
- data/test/test_jvm_compiler.rb +1770 -0
- data/test/test_math_plugin.rb +87 -0
- data/test/test_typer.rb +246 -0
- metadata +156 -0
@@ -0,0 +1,766 @@
|
|
1
|
+
require 'duby'
|
2
|
+
require 'duby/jvm/method_lookup'
|
3
|
+
require 'duby/jvm/types'
|
4
|
+
require 'duby/typer'
|
5
|
+
require 'duby/plugin/java'
|
6
|
+
require 'bitescript'
|
7
|
+
|
8
|
+
module Duby
|
9
|
+
module AST
|
10
|
+
class FunctionalCall
|
11
|
+
attr_accessor :target
|
12
|
+
end
|
13
|
+
|
14
|
+
class Super
|
15
|
+
attr_accessor :target
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module Compiler
|
20
|
+
class JVM
|
21
|
+
java_import java.lang.System
|
22
|
+
java_import java.io.PrintStream
|
23
|
+
include Duby::JVM::MethodLookup
|
24
|
+
Types = Duby::JVM::Types
|
25
|
+
|
26
|
+
class << self
|
27
|
+
attr_accessor :verbose
|
28
|
+
|
29
|
+
def log(message)
|
30
|
+
puts "* [#{name}] #{message}" if JVM.verbose
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module JVMLogger
|
35
|
+
def log(message); JVM.log(message); end
|
36
|
+
end
|
37
|
+
include JVMLogger
|
38
|
+
|
39
|
+
class ImplicitSelf
|
40
|
+
attr_reader :inferred_type
|
41
|
+
|
42
|
+
def initialize(type)
|
43
|
+
@inferred_type = type
|
44
|
+
end
|
45
|
+
|
46
|
+
def compile(compiler, expression)
|
47
|
+
compiler.compile_self if expression
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
attr_accessor :filename, :src, :method, :static, :class
|
52
|
+
|
53
|
+
def initialize(filename)
|
54
|
+
@filename = File.basename(filename)
|
55
|
+
@src = ""
|
56
|
+
@static = true
|
57
|
+
classname = File.basename(filename, '.duby')
|
58
|
+
BiteScript.bytecode_version = BiteScript::JAVA1_5
|
59
|
+
@file = BiteScript::FileBuilder.new(@filename)
|
60
|
+
AST.type_factory.define_types(@file)
|
61
|
+
@type = AST::type(classname)
|
62
|
+
@jump_scope = []
|
63
|
+
end
|
64
|
+
|
65
|
+
def compile(ast, expression = false)
|
66
|
+
ast.compile(self, expression)
|
67
|
+
log "Compilation successful!"
|
68
|
+
end
|
69
|
+
|
70
|
+
def toplevel_class
|
71
|
+
@class = @type.define(@file)
|
72
|
+
end
|
73
|
+
|
74
|
+
def push_jump_scope(node)
|
75
|
+
raise "Not a node" unless Duby::AST::Node === node
|
76
|
+
begin
|
77
|
+
@jump_scope << node
|
78
|
+
yield
|
79
|
+
ensure
|
80
|
+
@jump_scope.pop
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def find_ensures(before)
|
85
|
+
found = []
|
86
|
+
@jump_scope.reverse_each do |scope|
|
87
|
+
if Duby::AST::Ensure === scope
|
88
|
+
found << scope
|
89
|
+
end
|
90
|
+
break if scope === before
|
91
|
+
end
|
92
|
+
found
|
93
|
+
end
|
94
|
+
|
95
|
+
def define_main(body)
|
96
|
+
if body.class != AST::ClassDefinition
|
97
|
+
@class = @type.define(@file)
|
98
|
+
with :method => @class.main do
|
99
|
+
log "Starting main method"
|
100
|
+
|
101
|
+
@method.start
|
102
|
+
|
103
|
+
# declare argv variable
|
104
|
+
@method.local('argv', AST.type('string', true))
|
105
|
+
|
106
|
+
body.compile(self, false)
|
107
|
+
|
108
|
+
@method.returnvoid
|
109
|
+
@method.stop
|
110
|
+
end
|
111
|
+
@class.stop
|
112
|
+
else
|
113
|
+
body.compile(self, false)
|
114
|
+
end
|
115
|
+
|
116
|
+
log "Main method complete!"
|
117
|
+
end
|
118
|
+
|
119
|
+
def define_method(node)
|
120
|
+
push_jump_scope(node) do
|
121
|
+
name, signature, args = node.name, node.signature, node.arguments.args
|
122
|
+
arg_types = if args
|
123
|
+
args.map { |arg| arg.inferred_type }
|
124
|
+
else
|
125
|
+
[]
|
126
|
+
end
|
127
|
+
return_type = signature[:return]
|
128
|
+
exceptions = signature[:throws]
|
129
|
+
|
130
|
+
with :static => @static || node.static? do
|
131
|
+
if @static
|
132
|
+
method = @class.public_static_method(name.to_s, exceptions, return_type, *arg_types)
|
133
|
+
else
|
134
|
+
method = @class.public_method(name.to_s, exceptions, return_type, *arg_types)
|
135
|
+
end
|
136
|
+
|
137
|
+
annotate(method, node.annotations)
|
138
|
+
|
139
|
+
return if @class.interface?
|
140
|
+
|
141
|
+
log "Starting new method #{name}(#{arg_types})"
|
142
|
+
method_body(method, args, node.body, signature[:return])
|
143
|
+
|
144
|
+
arg_types_for_opt = []
|
145
|
+
args_for_opt = []
|
146
|
+
if args
|
147
|
+
args.each do |arg|
|
148
|
+
if AST::OptionalArgument === arg
|
149
|
+
if @static
|
150
|
+
method = @class.public_static_method(name.to_s, exceptions, return_type, *arg_types_for_opt)
|
151
|
+
else
|
152
|
+
method = @class.public_method(name.to_s, exceptions, return_type, *arg_types_for_opt)
|
153
|
+
end
|
154
|
+
|
155
|
+
with :method => method do
|
156
|
+
log "Starting new method #{name}(#{arg_types_for_opt})"
|
157
|
+
|
158
|
+
@method.start unless started
|
159
|
+
|
160
|
+
# declare all args so they get their values
|
161
|
+
|
162
|
+
expression = signature[:return] != Types::Void
|
163
|
+
|
164
|
+
@method.aload(0) unless @static
|
165
|
+
args_for_opt.each {|req_arg| @method.local(req_arg.name, req_arg.inferred_type)}
|
166
|
+
arg.children[0].compile(self, true)
|
167
|
+
|
168
|
+
# invoke the next one in the chain
|
169
|
+
if @static
|
170
|
+
@method.invokestatic(@class, name.to_s, [return_type] + arg_types_for_opt + [arg.inferred_type])
|
171
|
+
else
|
172
|
+
@method.invokevirtual(@class, name.to_s, [return_type] + arg_types_for_opt + [arg.inferred_type])
|
173
|
+
end
|
174
|
+
|
175
|
+
signature[:return].return(@method)
|
176
|
+
|
177
|
+
@method.stop
|
178
|
+
end
|
179
|
+
end
|
180
|
+
arg_types_for_opt << arg.inferred_type
|
181
|
+
args_for_opt << arg
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
log "Method #{name}(#{arg_types}) complete!" end
|
187
|
+
end
|
188
|
+
|
189
|
+
def constructor(node)
|
190
|
+
push_jump_scope(node) do
|
191
|
+
args = node.arguments.args || []
|
192
|
+
arg_types = args.map { |arg| arg.inferred_type }
|
193
|
+
exceptions = node.signature[:throws]
|
194
|
+
method = @class.public_constructor(exceptions, *arg_types)
|
195
|
+
annotate(method, node.annotations)
|
196
|
+
method_body(method, args, node.body, Types::Void) do
|
197
|
+
method.aload 0
|
198
|
+
if node.delegate_args
|
199
|
+
if node.calls_super
|
200
|
+
delegate_class = @type.superclass
|
201
|
+
else
|
202
|
+
delegate_class = @type
|
203
|
+
end
|
204
|
+
delegate_types = node.delegate_args.map {|arg| arg.inferred_type}
|
205
|
+
constructor = delegate_class.constructor(*delegate_types)
|
206
|
+
node.delegate_args.each do |arg|
|
207
|
+
arg.compile(self, true)
|
208
|
+
end
|
209
|
+
method.invokespecial(
|
210
|
+
delegate_class, "<init>",
|
211
|
+
[@method.void, *constructor.argument_types])
|
212
|
+
else
|
213
|
+
method.invokespecial @class.superclass, "<init>", [@method.void]
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def method_body(method, args, body, return_type)
|
220
|
+
with :method => method do
|
221
|
+
|
222
|
+
method.start
|
223
|
+
|
224
|
+
# declare all args so they get their values
|
225
|
+
if args
|
226
|
+
args.each {|arg| @method.local(arg.name, arg.inferred_type)}
|
227
|
+
end
|
228
|
+
|
229
|
+
yield if block_given?
|
230
|
+
|
231
|
+
expression = return_type != Types::Void
|
232
|
+
body.compile(self, expression) if body
|
233
|
+
|
234
|
+
return_type.return(@method)
|
235
|
+
|
236
|
+
@method.stop
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def define_class(class_def, expression)
|
241
|
+
with(:type => class_def.inferred_type,
|
242
|
+
:class => class_def.inferred_type.define(@file),
|
243
|
+
:static => false) do
|
244
|
+
annotate(@class, class_def.annotations)
|
245
|
+
class_def.body.compile(self, false) if class_def.body
|
246
|
+
@class.stop
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def declare_argument(name, type)
|
251
|
+
# declare local vars for arguments here
|
252
|
+
end
|
253
|
+
|
254
|
+
def branch(iff, expression)
|
255
|
+
elselabel = @method.label
|
256
|
+
donelabel = @method.label
|
257
|
+
|
258
|
+
# this is ugly...need a better way to abstract the idea of compiling a
|
259
|
+
# conditional branch while still fitting into JVM opcodes
|
260
|
+
predicate = iff.condition.predicate
|
261
|
+
if iff.body || expression
|
262
|
+
jump_if_not(predicate, elselabel)
|
263
|
+
|
264
|
+
if iff.body
|
265
|
+
iff.body.compile(self, expression)
|
266
|
+
elsif expression
|
267
|
+
iff.inferred_type.init_value(@method)
|
268
|
+
end
|
269
|
+
|
270
|
+
@method.goto(donelabel)
|
271
|
+
else
|
272
|
+
jump_if(predicate, donelabel)
|
273
|
+
end
|
274
|
+
|
275
|
+
elselabel.set!
|
276
|
+
|
277
|
+
if iff.else
|
278
|
+
iff.else.compile(self, expression)
|
279
|
+
elsif expression
|
280
|
+
iff.inferred_type.init_value(@method)
|
281
|
+
end
|
282
|
+
|
283
|
+
donelabel.set!
|
284
|
+
end
|
285
|
+
|
286
|
+
def loop(loop, expression)
|
287
|
+
push_jump_scope(loop) do
|
288
|
+
with(:break_label => @method.label,
|
289
|
+
:redo_label => @method.label,
|
290
|
+
:next_label => @method.label) do
|
291
|
+
predicate = loop.condition.predicate
|
292
|
+
|
293
|
+
loop.init.compile(self, false) if loop.init?
|
294
|
+
|
295
|
+
pre_label = @redo_label
|
296
|
+
|
297
|
+
if loop.check_first
|
298
|
+
@next_label.set! unless loop.post?
|
299
|
+
if loop.negative
|
300
|
+
# if condition, exit
|
301
|
+
jump_if(predicate, @break_label)
|
302
|
+
else
|
303
|
+
# if not condition, exit
|
304
|
+
jump_if_not(predicate, @break_label)
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
if loop.pre?
|
309
|
+
pre_label = method.label
|
310
|
+
pre_label.set!
|
311
|
+
loop.pre.compile(self, false)
|
312
|
+
end
|
313
|
+
|
314
|
+
|
315
|
+
@redo_label.set!
|
316
|
+
loop.body.compile(self, false)
|
317
|
+
|
318
|
+
if loop.check_first && !loop.post?
|
319
|
+
@method.goto(@next_label)
|
320
|
+
else
|
321
|
+
@next_label.set!
|
322
|
+
loop.post.compile(self, false) if loop.post?
|
323
|
+
if loop.negative
|
324
|
+
# if not condition, continue
|
325
|
+
jump_if_not(predicate, pre_label)
|
326
|
+
else
|
327
|
+
# if condition, continue
|
328
|
+
jump_if(predicate, pre_label)
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
@break_label.set!
|
333
|
+
|
334
|
+
# loops always evaluate to null
|
335
|
+
@method.aconst_null if expression
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
def break(node)
|
341
|
+
handle_ensures(find_ensures(Duby::AST::Loop))
|
342
|
+
@method.goto(@break_label)
|
343
|
+
end
|
344
|
+
|
345
|
+
def next(node)
|
346
|
+
handle_ensures(find_ensures(Duby::AST::Loop))
|
347
|
+
@method.goto(@next_label)
|
348
|
+
end
|
349
|
+
|
350
|
+
def redo(node)
|
351
|
+
handle_ensures(find_ensures(Duby::AST::Loop))
|
352
|
+
@method.goto(@redo_label)
|
353
|
+
end
|
354
|
+
|
355
|
+
def jump_if(predicate, target)
|
356
|
+
raise "Expected boolean, found #{predicate.inferred_type}" unless predicate.inferred_type == Types::Boolean
|
357
|
+
predicate.compile(self, true)
|
358
|
+
@method.ifne(target)
|
359
|
+
end
|
360
|
+
|
361
|
+
def jump_if_not(predicate, target)
|
362
|
+
raise "Expected boolean, found #{predicate.inferred_type}" unless predicate.inferred_type == Types::Boolean
|
363
|
+
predicate.compile(self, true)
|
364
|
+
@method.ifeq(target)
|
365
|
+
end
|
366
|
+
|
367
|
+
def call(call, expression)
|
368
|
+
target = call.target.inferred_type
|
369
|
+
params = call.parameters.map do |param|
|
370
|
+
param.inferred_type
|
371
|
+
end
|
372
|
+
method = target.get_method(call.name, params)
|
373
|
+
if method
|
374
|
+
method.call(self, call, expression)
|
375
|
+
else
|
376
|
+
raise "Missing method #{target}.#{call.name}(#{params.join ', '})"
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
def self_call(fcall, expression)
|
381
|
+
return cast(fcall, expression) if fcall.cast?
|
382
|
+
type = @type
|
383
|
+
type = type.meta if @static
|
384
|
+
fcall.target = ImplicitSelf.new(type)
|
385
|
+
|
386
|
+
params = fcall.parameters.map do |param|
|
387
|
+
param.inferred_type
|
388
|
+
end
|
389
|
+
method = type.get_method(fcall.name, params)
|
390
|
+
unless method
|
391
|
+
target = static ? @class.name : 'self'
|
392
|
+
|
393
|
+
raise NameError, "No method %s.%s(%s)" %
|
394
|
+
[target, fcall.name, params.join(', ')]
|
395
|
+
end
|
396
|
+
method.call(self, fcall, expression)
|
397
|
+
end
|
398
|
+
|
399
|
+
def super_call(sup, expression)
|
400
|
+
type = @type.superclass
|
401
|
+
sup.target = ImplicitSelf.new(type)
|
402
|
+
|
403
|
+
params = sup.parameters.map do |param|
|
404
|
+
param.inferred_type
|
405
|
+
end
|
406
|
+
method = type.get_method(sup.name, params)
|
407
|
+
unless method
|
408
|
+
|
409
|
+
raise NameError, "No method %s.%s(%s)" %
|
410
|
+
[type, sup.name, params.join(', ')]
|
411
|
+
end
|
412
|
+
method.call_special(self, sup, expression)
|
413
|
+
end
|
414
|
+
|
415
|
+
def cast(fcall, expression)
|
416
|
+
# casting operation, not a call
|
417
|
+
castee = fcall.parameters[0]
|
418
|
+
|
419
|
+
# TODO move errors to inference phase
|
420
|
+
source_type_name = castee.inferred_type.name
|
421
|
+
target_type_name = fcall.inferred_type.name
|
422
|
+
case source_type_name
|
423
|
+
when "byte", "short", "char", "int", "long", "float", "double"
|
424
|
+
case target_type_name
|
425
|
+
when "byte", "short", "char", "int", "long", "float", "double"
|
426
|
+
# ok
|
427
|
+
primitive = true
|
428
|
+
else
|
429
|
+
raise TypeError.new "not a reference type: #{castee.inferred_type}"
|
430
|
+
end
|
431
|
+
when "boolean"
|
432
|
+
if target_type_name != "boolean"
|
433
|
+
raise TypeError.new "not a boolean type: #{castee.inferred_type}"
|
434
|
+
end
|
435
|
+
primitive = true
|
436
|
+
else
|
437
|
+
case target_type_name
|
438
|
+
when "byte", "short", "char", "int", "long", "float", "double"
|
439
|
+
raise TypeError.new "not a primitive type: #{castee.inferred_type}"
|
440
|
+
else
|
441
|
+
# ok
|
442
|
+
primitive = false
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
castee.compile(self, expression)
|
447
|
+
if expression
|
448
|
+
if primitive
|
449
|
+
source_type_name = 'int' if %w[byte short char].include? source_type_name
|
450
|
+
if (source_type_name != 'int') && (%w[byte short char].include? target_type_name)
|
451
|
+
target_type_name = 'int'
|
452
|
+
end
|
453
|
+
|
454
|
+
if source_type_name != target_type_name
|
455
|
+
if RUBY_VERSION == "1.9"
|
456
|
+
@method.send "#{source_type_name[0]}2#{target_type_name[0]}"
|
457
|
+
else
|
458
|
+
@method.send "#{source_type_name[0].chr}2#{target_type_name[0].chr}"
|
459
|
+
end
|
460
|
+
end
|
461
|
+
else
|
462
|
+
if source_type_name != target_type_name
|
463
|
+
@method.checkcast fcall.inferred_type
|
464
|
+
end
|
465
|
+
end
|
466
|
+
end
|
467
|
+
end
|
468
|
+
|
469
|
+
def body(body, expression)
|
470
|
+
# all except the last element in a body of code is treated as a statement
|
471
|
+
i, last = 0, body.children.size - 1
|
472
|
+
while i < last
|
473
|
+
body.children[i].compile(self, false)
|
474
|
+
i += 1
|
475
|
+
end
|
476
|
+
# last element is an expression only if the body is an expression
|
477
|
+
body.children[last].compile(self, expression)
|
478
|
+
end
|
479
|
+
|
480
|
+
def local(name, type)
|
481
|
+
type.load(@method, @method.local(name, type))
|
482
|
+
end
|
483
|
+
|
484
|
+
def local_assign(name, type, expression, value)
|
485
|
+
declare_local(name, type)
|
486
|
+
|
487
|
+
value.compile(self, true)
|
488
|
+
|
489
|
+
# if expression, dup the value we're assigning
|
490
|
+
@method.dup if expression
|
491
|
+
|
492
|
+
type.store(@method, @method.local(name, type))
|
493
|
+
end
|
494
|
+
|
495
|
+
def declared_locals
|
496
|
+
@declared_locals ||= {}
|
497
|
+
end
|
498
|
+
|
499
|
+
def annotate(builder, annotations)
|
500
|
+
annotations.each do |annotation|
|
501
|
+
builder.annotate(annotation.type.jvm_type) do |visitor|
|
502
|
+
# todo values
|
503
|
+
end
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
def declare_local(name, type)
|
508
|
+
# TODO confirm types are compatible
|
509
|
+
unless declared_locals[name]
|
510
|
+
declared_locals[name] = type
|
511
|
+
index = @method.local(name, type)
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
def local_declare(name, type)
|
516
|
+
declare_local(name, type)
|
517
|
+
type.init_value(@method)
|
518
|
+
type.store(@method, @method.local(name, type))
|
519
|
+
end
|
520
|
+
|
521
|
+
def field(name, type, annotations)
|
522
|
+
name = name[1..-1]
|
523
|
+
|
524
|
+
real_type = declared_fields[name] || type
|
525
|
+
|
526
|
+
declare_field(name, real_type, annotations)
|
527
|
+
|
528
|
+
# load self object unless static
|
529
|
+
method.aload 0 unless static
|
530
|
+
|
531
|
+
if static
|
532
|
+
@method.getstatic(@class, name, type)
|
533
|
+
else
|
534
|
+
@method.getfield(@class, name, type)
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
def declared_fields
|
539
|
+
@declared_fields ||= {}
|
540
|
+
@declared_fields[@class] ||= {}
|
541
|
+
end
|
542
|
+
|
543
|
+
def declare_field(name, type, annotations)
|
544
|
+
# TODO confirm types are compatible
|
545
|
+
unless declared_fields[name]
|
546
|
+
declared_fields[name] = type
|
547
|
+
field = if static
|
548
|
+
@class.private_static_field name, type
|
549
|
+
else
|
550
|
+
@class.private_field name, type
|
551
|
+
end
|
552
|
+
annotate(field, annotations)
|
553
|
+
end
|
554
|
+
end
|
555
|
+
|
556
|
+
def field_declare(name, type, annotations)
|
557
|
+
name = name[1..-1]
|
558
|
+
declare_field(name, type, annotations)
|
559
|
+
end
|
560
|
+
|
561
|
+
def field_assign(name, type, expression, value, annotations)
|
562
|
+
name = name[1..-1]
|
563
|
+
|
564
|
+
real_type = declared_fields[name] || type
|
565
|
+
|
566
|
+
declare_field(name, real_type, annotations)
|
567
|
+
|
568
|
+
method.aload 0 unless static
|
569
|
+
value.compile(self, true)
|
570
|
+
if expression
|
571
|
+
instruction = 'dup'
|
572
|
+
instruction << '2' if type.wide?
|
573
|
+
instruction << '_x1' unless static
|
574
|
+
method.send instruction
|
575
|
+
end
|
576
|
+
|
577
|
+
if static
|
578
|
+
@method.putstatic(@class, name, real_type)
|
579
|
+
else
|
580
|
+
@method.putfield(@class, name, real_type)
|
581
|
+
end
|
582
|
+
end
|
583
|
+
|
584
|
+
def string(value)
|
585
|
+
@method.ldc(value)
|
586
|
+
end
|
587
|
+
|
588
|
+
def boolean(value)
|
589
|
+
value ? @method.iconst_1 : @method.iconst_0
|
590
|
+
end
|
591
|
+
|
592
|
+
def array(node, expression)
|
593
|
+
if expression
|
594
|
+
# create basic arraylist
|
595
|
+
@method.new java::util::ArrayList
|
596
|
+
@method.dup
|
597
|
+
@method.ldc_int node.children ? node.children.size : 0
|
598
|
+
@method.invokespecial java::util::ArrayList, "<init>", [@method.void, @method.int]
|
599
|
+
|
600
|
+
# elements, as expressions
|
601
|
+
# TODO: ensure they're all reference types!
|
602
|
+
node.children.each do |node|
|
603
|
+
@method.dup
|
604
|
+
node.compile(self, true)
|
605
|
+
@method.invokeinterface java::util::List, "add", [@method.boolean, @method.object]
|
606
|
+
@method.pop
|
607
|
+
end
|
608
|
+
|
609
|
+
# make it unmodifiable
|
610
|
+
@method.invokestatic java::util::Collections, "unmodifiableList", [java::util::List, java::util::List]
|
611
|
+
else
|
612
|
+
# elements, as non-expressions
|
613
|
+
# TODO: ensure they're all reference types!
|
614
|
+
node.children.each do |node|
|
615
|
+
node.compile(self, false)
|
616
|
+
end
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
def null
|
621
|
+
@method.aconst_null
|
622
|
+
end
|
623
|
+
|
624
|
+
def compile_self
|
625
|
+
method.aload(0)
|
626
|
+
end
|
627
|
+
|
628
|
+
def newline
|
629
|
+
# TODO: line numbering
|
630
|
+
end
|
631
|
+
|
632
|
+
def line(num)
|
633
|
+
@method.line(num) if @method
|
634
|
+
end
|
635
|
+
|
636
|
+
def generate
|
637
|
+
log "Generating classes..."
|
638
|
+
@file.generate do |filename, builder|
|
639
|
+
log " #{builder.class_name}"
|
640
|
+
if block_given?
|
641
|
+
yield filename, builder
|
642
|
+
else
|
643
|
+
File.open(filename, 'w') {|f| f.write(builder.generate)}
|
644
|
+
end
|
645
|
+
end
|
646
|
+
log "...done!"
|
647
|
+
end
|
648
|
+
|
649
|
+
def import(short, long)
|
650
|
+
end
|
651
|
+
|
652
|
+
def print(print_node)
|
653
|
+
@method.getstatic System, "out", PrintStream
|
654
|
+
print_node.parameters.each {|param| param.compile(self, true)}
|
655
|
+
params = print_node.parameters.map {|param| param.inferred_type.jvm_type}
|
656
|
+
method_name = print_node.println ? "println" : "print"
|
657
|
+
method = find_method(PrintStream.java_class, method_name, params, false)
|
658
|
+
if (method)
|
659
|
+
@method.invokevirtual(
|
660
|
+
PrintStream,
|
661
|
+
method_name,
|
662
|
+
[method.return_type, *method.parameter_types])
|
663
|
+
else
|
664
|
+
log "Could not find a match for #{PrintStream}.#{method_name}(#{params})"
|
665
|
+
fail "Could not compile"
|
666
|
+
end
|
667
|
+
end
|
668
|
+
|
669
|
+
def return(return_node)
|
670
|
+
return_node.value.compile(self, true)
|
671
|
+
handle_ensures(find_ensures(Duby::AST::MethodDefinition))
|
672
|
+
return_node.inferred_type.return(@method)
|
673
|
+
end
|
674
|
+
|
675
|
+
def _raise(exception)
|
676
|
+
exception.compile(self, true)
|
677
|
+
@method.athrow
|
678
|
+
end
|
679
|
+
|
680
|
+
def rescue(rescue_node, expression)
|
681
|
+
start = @method.label.set!
|
682
|
+
body_end = @method.label
|
683
|
+
done = @method.label
|
684
|
+
rescue_node.body.compile(self, expression)
|
685
|
+
body_end.set!
|
686
|
+
@method.goto(done)
|
687
|
+
rescue_node.clauses.each do |clause|
|
688
|
+
target = @method.label.set!
|
689
|
+
if clause.name
|
690
|
+
@method.astore(@method.push_local(clause.name, clause.type))
|
691
|
+
else
|
692
|
+
@method.pop
|
693
|
+
end
|
694
|
+
clause.body.compile(self, expression)
|
695
|
+
@method.pop_local(clause.name) if clause.name
|
696
|
+
@method.goto(done)
|
697
|
+
clause.types.each do |type|
|
698
|
+
@method.trycatch(start, body_end, target, type)
|
699
|
+
end
|
700
|
+
end
|
701
|
+
done.set!
|
702
|
+
end
|
703
|
+
|
704
|
+
def handle_ensures(nodes)
|
705
|
+
nodes.each do |ensure_node|
|
706
|
+
ensure_node.clause.compile(self, false)
|
707
|
+
end
|
708
|
+
end
|
709
|
+
|
710
|
+
def ensure(node, expression)
|
711
|
+
node.state = @method.label # Save the ensure target for JumpNodes
|
712
|
+
start = @method.label.set!
|
713
|
+
body_end = @method.label
|
714
|
+
done = @method.label
|
715
|
+
push_jump_scope(node) do
|
716
|
+
node.body.compile(self, expression) # First compile the body
|
717
|
+
end
|
718
|
+
body_end.set!
|
719
|
+
handle_ensures([node]) # run the ensure clause
|
720
|
+
@method.goto(done) # and continue on after the exception handler
|
721
|
+
target = @method.label.set! # Finally, create the exception handler
|
722
|
+
@method.trycatch(start, body_end, target, nil)
|
723
|
+
handle_ensures([node])
|
724
|
+
@method.athrow
|
725
|
+
done.set!
|
726
|
+
end
|
727
|
+
|
728
|
+
def empty_array(type, size)
|
729
|
+
size.compile(self, true)
|
730
|
+
type.newarray(@method)
|
731
|
+
end
|
732
|
+
|
733
|
+
def with(vars)
|
734
|
+
orig_values = {}
|
735
|
+
begin
|
736
|
+
vars.each do |name, new_value|
|
737
|
+
name = "@#{name}"
|
738
|
+
orig_values[name] = instance_variable_get name
|
739
|
+
instance_variable_set name, new_value
|
740
|
+
end
|
741
|
+
yield
|
742
|
+
ensure
|
743
|
+
orig_values.each do |name, value|
|
744
|
+
instance_variable_set name, value
|
745
|
+
end
|
746
|
+
end
|
747
|
+
end
|
748
|
+
end
|
749
|
+
end
|
750
|
+
end
|
751
|
+
|
752
|
+
if __FILE__ == $0
|
753
|
+
Duby::Typer.verbose = true
|
754
|
+
Duby::AST.verbose = true
|
755
|
+
Duby::Compiler::JVM.verbose = true
|
756
|
+
ast = Duby::AST.parse(File.read(ARGV[0]))
|
757
|
+
|
758
|
+
typer = Duby::Typer::Simple.new(:script)
|
759
|
+
ast.infer(typer)
|
760
|
+
typer.resolve(true)
|
761
|
+
|
762
|
+
compiler = Duby::Compiler::JVM.new(ARGV[0])
|
763
|
+
compiler.compile(ast)
|
764
|
+
|
765
|
+
compiler.generate
|
766
|
+
end
|