duby 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/Manifest.txt +68 -0
- data/README.txt +31 -0
- data/Rakefile +37 -0
- data/bin/duby +9 -0
- data/bin/dubyc +9 -0
- data/bin/dubyp +9 -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/bench_fractal.duby +57 -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/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/javalib/JRubyParser.jar +0 -0
- data/lib/duby.rb +170 -0
- data/lib/duby/ast.rb +340 -0
- data/lib/duby/ast/call.rb +73 -0
- data/lib/duby/ast/class.rb +145 -0
- data/lib/duby/ast/flow.rb +328 -0
- data/lib/duby/ast/intrinsics.rb +46 -0
- data/lib/duby/ast/literal.rb +93 -0
- data/lib/duby/ast/local.rb +77 -0
- data/lib/duby/ast/method.rb +187 -0
- data/lib/duby/ast/structure.rb +44 -0
- data/lib/duby/ast/type.rb +93 -0
- data/lib/duby/c/compiler.rb +134 -0
- data/lib/duby/compiler.rb +261 -0
- data/lib/duby/jvm/compiler.rb +684 -0
- data/lib/duby/jvm/method_lookup.rb +185 -0
- data/lib/duby/jvm/source_compiler.rb +516 -0
- data/lib/duby/jvm/source_generator/builder.rb +368 -0
- data/lib/duby/jvm/source_generator/loops.rb +132 -0
- data/lib/duby/jvm/source_generator/precompile.rb +154 -0
- data/lib/duby/jvm/source_generator/typer.rb +11 -0
- data/lib/duby/jvm/typer.rb +118 -0
- data/lib/duby/jvm/types.rb +326 -0
- data/lib/duby/jvm/types/basic_types.rb +18 -0
- data/lib/duby/jvm/types/boolean.rb +11 -0
- data/lib/duby/jvm/types/factory.rb +151 -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 +157 -0
- data/lib/duby/jvm/types/literals.rb +82 -0
- data/lib/duby/jvm/types/methods.rb +344 -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 +908 -0
- data/lib/duby/typer.rb +359 -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 +57 -0
- data/test/test_jvm_compiler.rb +1459 -0
- data/test/test_math_plugin.rb +87 -0
- data/test/test_typer.rb +246 -0
- metadata +155 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module Duby::JVM::Types
|
4
|
+
|
5
|
+
# Represents a literal number that can be represented
|
6
|
+
# in multiple types
|
7
|
+
class NarrowingType < DelegateClass(PrimitiveType)
|
8
|
+
def initialize(default_type, narrowed_type)
|
9
|
+
super(default_type)
|
10
|
+
@narrowed = default_type != narrowed_type && narrowed_type
|
11
|
+
end
|
12
|
+
|
13
|
+
def hash
|
14
|
+
__getobj__.hash
|
15
|
+
end
|
16
|
+
|
17
|
+
# Changes this type to the smallest type that will hold
|
18
|
+
# its literal value.
|
19
|
+
def narrow!
|
20
|
+
if @narrowed
|
21
|
+
__setobj__(@narrowed)
|
22
|
+
true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class FixnumLiteral < NarrowingType
|
28
|
+
def self.range(type)
|
29
|
+
type::MIN_VALUE .. type::MAX_VALUE
|
30
|
+
end
|
31
|
+
|
32
|
+
BYTE_RANGE = range(java.lang.Byte)
|
33
|
+
SHORT_RANGE = range(java.lang.Short)
|
34
|
+
INT_RANGE = range(java.lang.Integer)
|
35
|
+
LONG_RANGE = range(java.lang.Long)
|
36
|
+
|
37
|
+
def initialize(literal)
|
38
|
+
default_type = case literal
|
39
|
+
when INT_RANGE
|
40
|
+
Int
|
41
|
+
else
|
42
|
+
Long
|
43
|
+
end
|
44
|
+
|
45
|
+
# TODO chars?
|
46
|
+
# There's not really any way to tell if we should narrow to a char
|
47
|
+
# or a byte/short. I suppose we could try both, but that seems ugly.
|
48
|
+
# Maybe it's the right thing to do though?
|
49
|
+
narrowed_type = case literal
|
50
|
+
when BYTE_RANGE
|
51
|
+
Byte
|
52
|
+
when SHORT_RANGE
|
53
|
+
Short
|
54
|
+
when INT_RANGE
|
55
|
+
Int
|
56
|
+
else
|
57
|
+
Long
|
58
|
+
end
|
59
|
+
|
60
|
+
super(default_type, narrowed_type)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class FloatLiteral < NarrowingType
|
65
|
+
FLOAT_RANGE = java.lang.Float::MIN_VALUE .. java.lang.Float::MAX_VALUE
|
66
|
+
NaN = java.lang.Float::NaN
|
67
|
+
POSITIVE_INFINITY = java.lang.Float::POSITIVE_INFINITY
|
68
|
+
NEGATIVE_INFINITY = java.lang.Float::NEGATIVE_INFINITY
|
69
|
+
|
70
|
+
def initialize(literal)
|
71
|
+
narrowed_type = case literal
|
72
|
+
when FLOAT_RANGE, NaN, POSITIVE_INFINITY, NEGATIVE_INFINITY
|
73
|
+
Float
|
74
|
+
else
|
75
|
+
Double
|
76
|
+
end
|
77
|
+
|
78
|
+
# TODO Double should be default once it supports operators
|
79
|
+
super(narrowed_type, narrowed_type)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,344 @@
|
|
1
|
+
require 'duby/jvm/types'
|
2
|
+
|
3
|
+
class Java::JavaMethod
|
4
|
+
def static?
|
5
|
+
java.lang.reflect.Modifier.static?(modifiers)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module Duby::JVM::Types
|
10
|
+
AST ||= Duby::AST
|
11
|
+
|
12
|
+
module ArgumentConversion
|
13
|
+
def convert_args(compiler, values, types=nil)
|
14
|
+
# TODO boxing/unboxing
|
15
|
+
# TODO varargs
|
16
|
+
types ||= argument_types
|
17
|
+
values.zip(types).each do |value, type|
|
18
|
+
value.compile(compiler, true)
|
19
|
+
if type.primitive? && type != value.inferred_type
|
20
|
+
value.inferred_type.widen(compiler.method, type)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
Type.send :include, ArgumentConversion
|
27
|
+
|
28
|
+
class Intrinsic
|
29
|
+
include ArgumentConversion
|
30
|
+
attr_reader :name, :argument_types, :return_type
|
31
|
+
|
32
|
+
def initialize(klass, name, args, type, &block)
|
33
|
+
raise ArgumentError, "Block required" unless block_given?
|
34
|
+
@class = klass
|
35
|
+
@name = name
|
36
|
+
@argument_types = args
|
37
|
+
@return_type = type
|
38
|
+
@block = block
|
39
|
+
end
|
40
|
+
|
41
|
+
def call(builder, ast, expression)
|
42
|
+
@block.call(builder, ast, expression)
|
43
|
+
end
|
44
|
+
|
45
|
+
def declaring_class
|
46
|
+
@class
|
47
|
+
end
|
48
|
+
|
49
|
+
def constructor?
|
50
|
+
false
|
51
|
+
end
|
52
|
+
|
53
|
+
def exceptions
|
54
|
+
[]
|
55
|
+
end
|
56
|
+
|
57
|
+
def actual_return_type
|
58
|
+
return_type
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class JavaConstructor
|
63
|
+
include ArgumentConversion
|
64
|
+
def initialize(member)
|
65
|
+
@member = member
|
66
|
+
end
|
67
|
+
|
68
|
+
def name
|
69
|
+
@name ||= @member.name
|
70
|
+
end
|
71
|
+
|
72
|
+
def argument_types
|
73
|
+
@argument_types ||= @member.argument_types.map do |arg|
|
74
|
+
AST.type(arg) if arg
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def return_type
|
79
|
+
declaring_class
|
80
|
+
end
|
81
|
+
|
82
|
+
def actual_return_type
|
83
|
+
return_type
|
84
|
+
end
|
85
|
+
|
86
|
+
def exceptions
|
87
|
+
@member.exception_types.map do |exception|
|
88
|
+
AST.type(exception.name)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def declaring_class
|
93
|
+
AST.type(@member.declaring_class)
|
94
|
+
end
|
95
|
+
|
96
|
+
def call(compiler, ast, expression)
|
97
|
+
target = ast.target.inferred_type
|
98
|
+
compiler.method.new target
|
99
|
+
compiler.method.dup if expression
|
100
|
+
convert_args(compiler, ast.parameters)
|
101
|
+
compiler.method.invokespecial(
|
102
|
+
target,
|
103
|
+
"<init>",
|
104
|
+
[nil, *@member.argument_types])
|
105
|
+
end
|
106
|
+
|
107
|
+
def constructor?
|
108
|
+
true
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class JavaMethod < JavaConstructor
|
113
|
+
def return_type
|
114
|
+
@return_type ||= begin
|
115
|
+
if @member.return_type
|
116
|
+
AST.type(@member.return_type)
|
117
|
+
else
|
118
|
+
declaring_class
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def actual_return_type
|
124
|
+
if @member.return_type
|
125
|
+
return_type
|
126
|
+
else
|
127
|
+
Void
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def static?
|
132
|
+
@member.static?
|
133
|
+
end
|
134
|
+
|
135
|
+
def void?
|
136
|
+
@member.return_type.nil?
|
137
|
+
end
|
138
|
+
|
139
|
+
def constructor?
|
140
|
+
false
|
141
|
+
end
|
142
|
+
|
143
|
+
def call(compiler, ast, expression)
|
144
|
+
target = ast.target.inferred_type
|
145
|
+
ast.target.compile(compiler, true)
|
146
|
+
|
147
|
+
# if expression, void methods return the called object,
|
148
|
+
# for consistency and chaining
|
149
|
+
# TODO: inference phase needs to track that signature is
|
150
|
+
# void but actual type is callee
|
151
|
+
if expression && void?
|
152
|
+
compiler.method.dup
|
153
|
+
end
|
154
|
+
|
155
|
+
convert_args(compiler, ast.parameters)
|
156
|
+
if target.interface?
|
157
|
+
compiler.method.invokeinterface(
|
158
|
+
target,
|
159
|
+
name,
|
160
|
+
[@member.return_type, *@member.argument_types])
|
161
|
+
else
|
162
|
+
compiler.method.invokevirtual(
|
163
|
+
target,
|
164
|
+
name,
|
165
|
+
[@member.return_type, *@member.argument_types])
|
166
|
+
end
|
167
|
+
|
168
|
+
unless expression || void?
|
169
|
+
compiler.method.pop
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
class JavaStaticMethod < JavaMethod
|
175
|
+
def return_type
|
176
|
+
@return_type ||= begin
|
177
|
+
if @member.return_type
|
178
|
+
AST.type(@member.return_type)
|
179
|
+
else
|
180
|
+
Void
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def call(compiler, ast, expression)
|
186
|
+
target = ast.target.inferred_type
|
187
|
+
convert_args(compiler, ast.parameters)
|
188
|
+
compiler.method.invokestatic(
|
189
|
+
target,
|
190
|
+
name,
|
191
|
+
[@member.return_type, *@member.argument_types])
|
192
|
+
# if expression, void static methods return null, for consistency
|
193
|
+
# TODO: inference phase needs to track that signature is void
|
194
|
+
# but actual type is null object
|
195
|
+
compiler.method.aconst_null if expression && void?
|
196
|
+
compiler.method.pop unless expression || void?
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
class DubyMember
|
201
|
+
attr_reader :name, :argument_types, :declaring_class, :return_type
|
202
|
+
attr_reader :exception_types
|
203
|
+
|
204
|
+
def initialize(klass, name, args, return_type, static, exceptions)
|
205
|
+
if return_type == Void
|
206
|
+
return_type = nil
|
207
|
+
end
|
208
|
+
@declaring_class = klass
|
209
|
+
@name = name
|
210
|
+
@argument_types = args
|
211
|
+
@return_type = return_type
|
212
|
+
@static = static
|
213
|
+
@exception_types = exceptions || []
|
214
|
+
end
|
215
|
+
|
216
|
+
def static?
|
217
|
+
@static
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
class Type
|
222
|
+
def get_method(name, args)
|
223
|
+
method = find_method(self, name, args, meta?)
|
224
|
+
unless method
|
225
|
+
# Allow constant narrowing for assignment methods
|
226
|
+
if name =~ /=$/ && args[-1].respond_to?(:narrow!)
|
227
|
+
if args[-1].narrow!
|
228
|
+
method = find_method(self, name, args, meta?)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
method
|
233
|
+
end
|
234
|
+
|
235
|
+
def constructor(*types)
|
236
|
+
types = types.map {|type| type.jvm_type}
|
237
|
+
constructor = (jvm_type.constructor(*types) rescue nil)
|
238
|
+
return JavaConstructor.new(constructor) if constructor
|
239
|
+
raise NameError, "No constructor #{name}(#{types.join ', '})"
|
240
|
+
end
|
241
|
+
|
242
|
+
def java_method(name, *types)
|
243
|
+
intrinsic = intrinsics[name][types]
|
244
|
+
return intrinsic if intrinsic
|
245
|
+
types = types.map {|type| type.jvm_type}
|
246
|
+
method = (jvm_type.java_method(name, *types) rescue nil)
|
247
|
+
if method && method.static? == meta?
|
248
|
+
return JavaStaticMethod.new(method) if method.static?
|
249
|
+
return JavaMethod.new(method)
|
250
|
+
end
|
251
|
+
raise NameError, "No method #{self.name}.#{name}(#{types.join ', '})"
|
252
|
+
end
|
253
|
+
|
254
|
+
def declared_instance_methods
|
255
|
+
if jvm_type && !array?
|
256
|
+
methods = jvm_type.declared_instance_methods.map do |method|
|
257
|
+
JavaMethod.new(method)
|
258
|
+
end
|
259
|
+
else
|
260
|
+
methods = []
|
261
|
+
end
|
262
|
+
methods.concat((meta? ? unmeta : self).declared_intrinsics)
|
263
|
+
end
|
264
|
+
|
265
|
+
def declared_class_methods
|
266
|
+
methods = jvm_type.declared_class_methods.map do |method|
|
267
|
+
JavaStaticMethod.new(method)
|
268
|
+
end
|
269
|
+
methods.concat(meta.declared_intrinsics)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
class TypeDefinition
|
274
|
+
def java_method(name, *types)
|
275
|
+
method = instance_methods[name].find {|m| m.argument_types == types}
|
276
|
+
return method if method
|
277
|
+
raise NameError, "No method #{self.name}.#{name}(#{types.join ', '})"
|
278
|
+
end
|
279
|
+
|
280
|
+
def java_static_method(name, *types)
|
281
|
+
method = static_methods[name].find {|m| m.argument_types == types}
|
282
|
+
return method if method
|
283
|
+
raise NameError, "No method #{self.name}.#{name}(#{types.join ', '})"
|
284
|
+
end
|
285
|
+
|
286
|
+
def constructor(*types)
|
287
|
+
constructor = constructors.find {|c| c.argument_types == types}
|
288
|
+
return constructor if constructor
|
289
|
+
raise NameError, "No constructo #{name}(#{types.join ', '})"
|
290
|
+
end
|
291
|
+
|
292
|
+
def declared_instance_methods
|
293
|
+
instance_methods.values.flatten
|
294
|
+
end
|
295
|
+
|
296
|
+
def declared_class_methods
|
297
|
+
static_methods.values.flatten
|
298
|
+
end
|
299
|
+
|
300
|
+
def constructors
|
301
|
+
@constructors ||= []
|
302
|
+
end
|
303
|
+
|
304
|
+
def instance_methods
|
305
|
+
@instance_methods ||= Hash.new {|h, k| h[k] = []}
|
306
|
+
end
|
307
|
+
|
308
|
+
def static_methods
|
309
|
+
@static_methods ||= Hash.new {|h, k| h[k] = []}
|
310
|
+
end
|
311
|
+
|
312
|
+
def declare_method(name, arguments, type, exceptions)
|
313
|
+
member = DubyMember.new(self, name, arguments, type, false, exceptions)
|
314
|
+
if name == 'initialize'
|
315
|
+
constructors << JavaConstructor.new(member)
|
316
|
+
else
|
317
|
+
instance_methods[name] << JavaMethod.new(member)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
def declare_static_method(name, arguments, type, exceptions)
|
322
|
+
member = DubyMember.new(self, name, arguments, type, true, exceptions)
|
323
|
+
static_methods[name] << JavaStaticMethod.new(member)
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
class TypeDefMeta
|
328
|
+
def constructor(*args)
|
329
|
+
unmeta.constructor(*args)
|
330
|
+
end
|
331
|
+
|
332
|
+
def java_method(*args)
|
333
|
+
unmeta.java_static_method(*args)
|
334
|
+
end
|
335
|
+
|
336
|
+
def declared_instance_methods
|
337
|
+
unmeta.declared_instance_methods
|
338
|
+
end
|
339
|
+
|
340
|
+
def declared_class_methods
|
341
|
+
unmeta.declared_class_methods
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Duby::JVM::Types
|
2
|
+
class Number < PrimitiveType
|
3
|
+
# The type returned by arithmetic operations with this type.
|
4
|
+
def math_type
|
5
|
+
self
|
6
|
+
end
|
7
|
+
|
8
|
+
def suffix
|
9
|
+
''
|
10
|
+
end
|
11
|
+
|
12
|
+
# Adds an intrinsic that delegates to an intrinsic in another primitive
|
13
|
+
# type. That type must support promoting the "this" argument.
|
14
|
+
def delegate_intrinsic(name, type, return_type)
|
15
|
+
args = [type]
|
16
|
+
delegate = type.intrinsics[name][args]
|
17
|
+
add_method(name, args, return_type) do |compiler, call, expression|
|
18
|
+
if expression
|
19
|
+
delegate.call(compiler, call, expression)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_delegates(name, return_type = nil)
|
25
|
+
index = TYPE_ORDERING.index(math_type)
|
26
|
+
larger_types = TYPE_ORDERING[index + 1, TYPE_ORDERING.size]
|
27
|
+
larger_types.each do |type|
|
28
|
+
delegate_intrinsic(name, return_type || type, return_type || type)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# if_cmpxx for non-ints
|
33
|
+
def jump_if(builder, op, label)
|
34
|
+
builder.send "#{prefix}cmp#{suffix}"
|
35
|
+
builder.send "if#{op}", label
|
36
|
+
end
|
37
|
+
|
38
|
+
def boolean_operator(name, op)
|
39
|
+
add_method(name, [math_type], Boolean) do |compiler, call, expression|
|
40
|
+
if expression
|
41
|
+
# Promote the target or the argument if necessary
|
42
|
+
convert_args(compiler,
|
43
|
+
[call.target, *call.parameters],
|
44
|
+
[math_type, math_type])
|
45
|
+
compiler.method.op_to_bool do |label|
|
46
|
+
jump_if(compiler.method, op, label)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
add_delegates(name, Boolean)
|
51
|
+
end
|
52
|
+
|
53
|
+
def math_operator(name, op)
|
54
|
+
add_method(name, [math_type], math_type) do |compiler, call, expression|
|
55
|
+
if expression
|
56
|
+
# Promote the target or the argument if necessary
|
57
|
+
convert_args(compiler,
|
58
|
+
[call.target, *call.parameters],
|
59
|
+
[math_type, math_type])
|
60
|
+
compiler.method.send "#{prefix}#{op}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
add_delegates(name)
|
64
|
+
end
|
65
|
+
|
66
|
+
def unary_operator(name, op)
|
67
|
+
add_method(name, [], math_type) do |compiler, call, expression|
|
68
|
+
if expression
|
69
|
+
call.target.compile(compiler, true)
|
70
|
+
compiler.method.send("#{prefix}#{op}") if op
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def add_intrinsics
|
76
|
+
boolean_operator('<', :lt)
|
77
|
+
boolean_operator('<=', :le)
|
78
|
+
boolean_operator('==', :eq)
|
79
|
+
boolean_operator('!=', :ne)
|
80
|
+
boolean_operator('>=', :ge)
|
81
|
+
boolean_operator('>', :gt)
|
82
|
+
math_operator('+', :add)
|
83
|
+
math_operator('-', :sub)
|
84
|
+
math_operator('*', :mul)
|
85
|
+
math_operator('/', :div)
|
86
|
+
math_operator('%', :rem)
|
87
|
+
unary_operator('-@', :neg)
|
88
|
+
unary_operator('+@', nil)
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|