bitescript 0.0.1
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 +6 -0
- data/LICENSE.txt +9 -0
- data/Manifest.txt +20 -0
- data/README.txt +56 -0
- data/Rakefile +22 -0
- data/bin/bite +32 -0
- data/bin/bitec +22 -0
- data/examples/fib.bs +79 -0
- data/examples/mixed_bag.rb +73 -0
- data/examples/simple_loop.rb +24 -0
- data/lib/bitescript/asm.rb +17 -0
- data/lib/bitescript/builder.rb +459 -0
- data/lib/bitescript/bytecode.rb +415 -0
- data/lib/bitescript/signature.rb +117 -0
- data/lib/bitescript.rb +20 -0
- data/test/test_bitescript.rb +14 -0
- data/test/test_builder.rb +429 -0
- data/test/test_bytecode.rb +674 -0
- data/test/test_java_class.rb +58 -0
- data/test/test_signature.rb +68 -0
- metadata +91 -0
@@ -0,0 +1,459 @@
|
|
1
|
+
require 'bitescript/bytecode'
|
2
|
+
require 'bitescript/signature'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module BiteScript
|
6
|
+
module Util
|
7
|
+
def type_from_dotted(dotted_name)
|
8
|
+
JavaUtilities.get_proxy_class(dotted_name).java_class
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module QuickTypes
|
13
|
+
def void
|
14
|
+
Java::void
|
15
|
+
end
|
16
|
+
|
17
|
+
def boolean
|
18
|
+
Java::boolean
|
19
|
+
end
|
20
|
+
|
21
|
+
def byte
|
22
|
+
Java::byte
|
23
|
+
end
|
24
|
+
|
25
|
+
def short
|
26
|
+
Java::short
|
27
|
+
end
|
28
|
+
|
29
|
+
def char
|
30
|
+
Java::char
|
31
|
+
end
|
32
|
+
|
33
|
+
def int
|
34
|
+
Java::int
|
35
|
+
end
|
36
|
+
|
37
|
+
def long
|
38
|
+
Java::long
|
39
|
+
end
|
40
|
+
|
41
|
+
def float
|
42
|
+
Java::float
|
43
|
+
end
|
44
|
+
|
45
|
+
def double
|
46
|
+
Java::double
|
47
|
+
end
|
48
|
+
|
49
|
+
def object
|
50
|
+
Java::java.lang.Object
|
51
|
+
end
|
52
|
+
|
53
|
+
def string
|
54
|
+
Java::java.lang.String
|
55
|
+
end
|
56
|
+
|
57
|
+
def null
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class FileBuilder
|
63
|
+
include Util
|
64
|
+
include QuickTypes
|
65
|
+
|
66
|
+
attr_accessor :file_name
|
67
|
+
attr_accessor :class_builders
|
68
|
+
attr_accessor :imports
|
69
|
+
attr_accessor :package
|
70
|
+
|
71
|
+
def initialize(file_name)
|
72
|
+
@file_name = file_name
|
73
|
+
@class_builders = {}
|
74
|
+
@imports = {}
|
75
|
+
@package = []
|
76
|
+
|
77
|
+
init_imports
|
78
|
+
end
|
79
|
+
|
80
|
+
def init_imports
|
81
|
+
# set up a few useful imports
|
82
|
+
@imports[:int.to_s] = Java::int.java_class
|
83
|
+
@imports[:string.to_s] = Java::java.lang.String.java_class
|
84
|
+
@imports[:object.to_s] = Java::java.lang.Object.java_class
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.build(filename, &block)
|
88
|
+
fb = new(filename)
|
89
|
+
if block_given?
|
90
|
+
fb.instance_eval(&block)
|
91
|
+
end
|
92
|
+
fb
|
93
|
+
end
|
94
|
+
|
95
|
+
def public_class(class_name, superclass = java.lang.Object, *interfaces, &block)
|
96
|
+
class_name = @package.empty? ? class_name : "#{@package.join('/')}/#{class_name}"
|
97
|
+
class_builder = ClassBuilder.new(self, class_name, @file_name, superclass, *interfaces)
|
98
|
+
@class_builders[class_name] ||= class_builder
|
99
|
+
|
100
|
+
if block_given?
|
101
|
+
class_builder.instance_eval(&block)
|
102
|
+
else
|
103
|
+
return class_builder
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def generate
|
108
|
+
@class_builders.each do |class_name, class_builder|
|
109
|
+
class_file = "#{class_name.gsub('.', '/')}.class"
|
110
|
+
|
111
|
+
yield class_file, class_builder
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def line(line)
|
116
|
+
# No tracking of lines at the file level, so we ignore
|
117
|
+
end
|
118
|
+
|
119
|
+
def package(*names)
|
120
|
+
elements = 0
|
121
|
+
names.each do |name_maybe_dotted|
|
122
|
+
name_maybe_dotted.split(/\./).each do |name|
|
123
|
+
elements += 1
|
124
|
+
@package.push name
|
125
|
+
end
|
126
|
+
end
|
127
|
+
yield
|
128
|
+
elements.times {@package.pop}
|
129
|
+
end
|
130
|
+
|
131
|
+
def method?
|
132
|
+
false
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
class ClassBuilder
|
137
|
+
include Util
|
138
|
+
include QuickTypes
|
139
|
+
|
140
|
+
begin
|
141
|
+
java_import "jruby.objectweb.asm.Opcodes"
|
142
|
+
java_import "jruby.objectweb.asm.ClassWriter"
|
143
|
+
rescue
|
144
|
+
java_import "org.objectweb.asm.Opcodes"
|
145
|
+
java_import "org.objectweb.asm.ClassWriter"
|
146
|
+
end
|
147
|
+
|
148
|
+
java_import java.lang.Object
|
149
|
+
java_import java.lang.Void
|
150
|
+
include Signature
|
151
|
+
|
152
|
+
attr_accessor :class_name
|
153
|
+
attr_accessor :superclass
|
154
|
+
attr_accessor :constructors
|
155
|
+
attr_accessor :methods
|
156
|
+
attr_accessor :imports
|
157
|
+
attr_accessor :fields
|
158
|
+
|
159
|
+
def initialize(file_builder, class_name, file_name, superclass = Object, *interfaces)
|
160
|
+
@parent = file_builder
|
161
|
+
@class_name = class_name
|
162
|
+
@superclass = superclass
|
163
|
+
|
164
|
+
@class_writer = ClassWriter.new(ClassWriter::COMPUTE_MAXS)
|
165
|
+
|
166
|
+
interface_paths = []
|
167
|
+
interfaces.each {|interface| interface_paths << path(interface)}
|
168
|
+
@class_writer.visit(BiteScript.bytecode_version, Opcodes::ACC_PUBLIC | Opcodes::ACC_SUPER, class_name, nil, path(superclass), interface_paths.to_java(:string))
|
169
|
+
@class_writer.visit_source(file_name, nil)
|
170
|
+
|
171
|
+
@constructor = nil
|
172
|
+
@constructors = {}
|
173
|
+
@methods = {}
|
174
|
+
|
175
|
+
@imports = {}
|
176
|
+
|
177
|
+
@fields = {}
|
178
|
+
end
|
179
|
+
|
180
|
+
def start
|
181
|
+
end
|
182
|
+
|
183
|
+
def stop
|
184
|
+
# if we haven't seen a constructor, generate a default one
|
185
|
+
unless @constructor
|
186
|
+
method = MethodBuilder.new(self, Opcodes::ACC_PUBLIC, "<init>", [])
|
187
|
+
method.start
|
188
|
+
method.aload 0
|
189
|
+
method.invokespecial @superclass, "<init>", Void::TYPE
|
190
|
+
method.returnvoid
|
191
|
+
method.stop
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def generate
|
196
|
+
String.from_java_bytes(@class_writer.to_byte_array)
|
197
|
+
end
|
198
|
+
|
199
|
+
%w[public private protected].each do |modifier|
|
200
|
+
# instance fields
|
201
|
+
eval "
|
202
|
+
def #{modifier}_field(name, type)
|
203
|
+
field(Opcodes::ACC_#{modifier.upcase}, name, type)
|
204
|
+
end
|
205
|
+
", binding, __FILE__, __LINE__
|
206
|
+
# static fields
|
207
|
+
eval "
|
208
|
+
def #{modifier}_static_field(name, type)
|
209
|
+
field(Opcodes::ACC_STATIC | Opcodes::ACC_#{modifier.upcase}, name, type)
|
210
|
+
end
|
211
|
+
", binding, __FILE__, __LINE__
|
212
|
+
# instance methods; also defines a "this" local at index 0
|
213
|
+
eval "
|
214
|
+
def #{modifier}_method(name, *signature, &block)
|
215
|
+
method(Opcodes::ACC_#{modifier.upcase}, name, signature, &block)
|
216
|
+
end
|
217
|
+
", binding, __FILE__, __LINE__
|
218
|
+
# static methods
|
219
|
+
eval "
|
220
|
+
def #{modifier}_static_method(name, *signature, &block)
|
221
|
+
method(Opcodes::ACC_STATIC | Opcodes::ACC_#{modifier.upcase}, name, signature, &block)
|
222
|
+
end
|
223
|
+
", binding, __FILE__, __LINE__
|
224
|
+
# native methods
|
225
|
+
eval "
|
226
|
+
def #{modifier}_native_method(name, *signature)
|
227
|
+
method(Opcodes::ACC_NATIVE | Opcodes::ACC_#{modifier.upcase}, name, signature)
|
228
|
+
end
|
229
|
+
", binding, __FILE__, __LINE__
|
230
|
+
# constructors; also defines a "this" local at index 0
|
231
|
+
eval "
|
232
|
+
def #{modifier}_constructor(*signature, &block)
|
233
|
+
method(Opcodes::ACC_#{modifier.upcase}, \"<init>\", [nil, *signature], &block)
|
234
|
+
end
|
235
|
+
", binding, __FILE__, __LINE__
|
236
|
+
end
|
237
|
+
|
238
|
+
def static_init(&block)
|
239
|
+
method(Opcodes::ACC_STATIC, "<clinit>", [void], &block)
|
240
|
+
end
|
241
|
+
|
242
|
+
def method(flags, name, signature, &block)
|
243
|
+
mb = MethodBuilder.new(self, flags, name, signature)
|
244
|
+
|
245
|
+
if name == "<init>"
|
246
|
+
constructors[signature[1..-1]] = mb
|
247
|
+
else
|
248
|
+
methods[name] ||= {}
|
249
|
+
methods[name][signature[1..-1]] = mb
|
250
|
+
end
|
251
|
+
|
252
|
+
# non-static methods reserve index 0 for 'this'
|
253
|
+
mb.local 'this' if (flags & Opcodes::ACC_STATIC) == 0
|
254
|
+
|
255
|
+
if block_given?
|
256
|
+
mb.start
|
257
|
+
mb.instance_eval(&block)
|
258
|
+
mb.stop
|
259
|
+
end
|
260
|
+
|
261
|
+
mb
|
262
|
+
end
|
263
|
+
|
264
|
+
def java_method(name, *params)
|
265
|
+
if methods[name]
|
266
|
+
method = methods[name][params]
|
267
|
+
end
|
268
|
+
|
269
|
+
method or raise NameError.new("failed to find method #{name}#{sig(params)} on #{self}")
|
270
|
+
end
|
271
|
+
|
272
|
+
def main(&b)
|
273
|
+
raise "already defined main" if methods[name]
|
274
|
+
|
275
|
+
public_static_method "main", void, string[], &b
|
276
|
+
end
|
277
|
+
|
278
|
+
def constructor(*params)
|
279
|
+
constructors[params] or raise NameError.new("failed to find constructor #{sig(params)} on #{self}")
|
280
|
+
end
|
281
|
+
|
282
|
+
def interface?
|
283
|
+
# TODO: interface types
|
284
|
+
false
|
285
|
+
end
|
286
|
+
|
287
|
+
def field(flags, name, type)
|
288
|
+
@class_writer.visit_field(flags, name, ci(type), nil, nil)
|
289
|
+
end
|
290
|
+
|
291
|
+
# name for signature generation using the class being generated
|
292
|
+
def name
|
293
|
+
@class_name
|
294
|
+
end
|
295
|
+
|
296
|
+
# never generating an array
|
297
|
+
def array?
|
298
|
+
false
|
299
|
+
end
|
300
|
+
|
301
|
+
# never generating a primitive
|
302
|
+
def primitive?
|
303
|
+
false
|
304
|
+
end
|
305
|
+
|
306
|
+
def this
|
307
|
+
self
|
308
|
+
end
|
309
|
+
|
310
|
+
def new_method(modifiers, name, signature)
|
311
|
+
@class_writer.visit_method(modifiers, name, sig(*signature), nil, nil)
|
312
|
+
end
|
313
|
+
|
314
|
+
def macro(name, &b)
|
315
|
+
MethodBuilder.send :define_method, name, &b
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
class MethodBuilder
|
320
|
+
begin
|
321
|
+
java_import "jruby.objectweb.asm.Opcodes"
|
322
|
+
rescue
|
323
|
+
java_import "org.objectweb.asm.Opcodes"
|
324
|
+
end
|
325
|
+
|
326
|
+
include QuickTypes
|
327
|
+
include BiteScript::Bytecode
|
328
|
+
|
329
|
+
attr_reader :method_visitor
|
330
|
+
attr_reader :static
|
331
|
+
attr_reader :signature
|
332
|
+
attr_reader :name
|
333
|
+
attr_reader :class_builder
|
334
|
+
|
335
|
+
def initialize(class_builder, modifiers, name, signature)
|
336
|
+
@class_builder = class_builder
|
337
|
+
@modifiers = modifiers
|
338
|
+
@name = name
|
339
|
+
@signature = signature
|
340
|
+
|
341
|
+
@method_visitor = class_builder.new_method(modifiers, name, signature)
|
342
|
+
|
343
|
+
@locals = {}
|
344
|
+
|
345
|
+
@static = (modifiers & Opcodes::ACC_STATIC) != 0
|
346
|
+
end
|
347
|
+
|
348
|
+
def parameter_types
|
349
|
+
signature[1..-1]
|
350
|
+
end
|
351
|
+
|
352
|
+
def return_type
|
353
|
+
signature[0]
|
354
|
+
end
|
355
|
+
|
356
|
+
def declaring_class
|
357
|
+
@class_builder
|
358
|
+
end
|
359
|
+
|
360
|
+
def self.build(class_builder, modifiers, name, signature, &block)
|
361
|
+
mb = MethodBuilder.new(class_builder, modifiers, name, signature)
|
362
|
+
mb.start
|
363
|
+
mb.instance_eval(&block)
|
364
|
+
mb.stop
|
365
|
+
end
|
366
|
+
|
367
|
+
def self.build2(class_builder, modifiers, name, signature, &block)
|
368
|
+
mb = MethodBuilder.new(class_builder, modifiers, name, signature)
|
369
|
+
mb.start
|
370
|
+
block.call(mb)
|
371
|
+
mb.stop
|
372
|
+
end
|
373
|
+
|
374
|
+
def generate(&block)
|
375
|
+
start
|
376
|
+
block.call(self)
|
377
|
+
stop
|
378
|
+
end
|
379
|
+
|
380
|
+
def this
|
381
|
+
@class_builder
|
382
|
+
end
|
383
|
+
|
384
|
+
def local(name)
|
385
|
+
if name == "this" && @static
|
386
|
+
raise "'this' attempted to load from static method"
|
387
|
+
end
|
388
|
+
|
389
|
+
if @locals[name]
|
390
|
+
local_index = @locals[name]
|
391
|
+
else
|
392
|
+
local_index = @locals[name] = @locals.size
|
393
|
+
end
|
394
|
+
local_index
|
395
|
+
end
|
396
|
+
|
397
|
+
def annotate(cls, runtime = false)
|
398
|
+
if Java::JavaClass == cls
|
399
|
+
java_class = cls
|
400
|
+
else
|
401
|
+
java_class = cls.java_class
|
402
|
+
end
|
403
|
+
|
404
|
+
annotation = @method_visitor.visit_annotation(ci(java_class), true)
|
405
|
+
annotation.extend AnnotationBuilder
|
406
|
+
|
407
|
+
yield annotation
|
408
|
+
annotation.visit_end
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
module AnnotationBuilder
|
413
|
+
def method_missing(name, val)
|
414
|
+
name_str = name.to_s
|
415
|
+
if name_str[-1] == ?=
|
416
|
+
name_str = name_str[0..-2]
|
417
|
+
if Array === val
|
418
|
+
array(name_str) do |ary|
|
419
|
+
val.each {|x| ary.visit(nil, x)}
|
420
|
+
end
|
421
|
+
else
|
422
|
+
visit name_str, val
|
423
|
+
end
|
424
|
+
else
|
425
|
+
super
|
426
|
+
end
|
427
|
+
end
|
428
|
+
def value(k, v)
|
429
|
+
visit k, v
|
430
|
+
end
|
431
|
+
def annotation(name, cls)
|
432
|
+
if Java::JavaClass == cls
|
433
|
+
java_class = cls
|
434
|
+
else
|
435
|
+
java_class = cls.java_class
|
436
|
+
end
|
437
|
+
|
438
|
+
sub_annotation = visit_annotation(name, ci(java_class))
|
439
|
+
sub_annotation.extend AnnotationBuilder
|
440
|
+
yield sub_annotation
|
441
|
+
sub_annotation.visit_end
|
442
|
+
end
|
443
|
+
def array(name)
|
444
|
+
sub_annotation = visit_array(name)
|
445
|
+
sub_annotation.extend AnnotationBuilder
|
446
|
+
yield sub_annotation
|
447
|
+
sub_annotation.visit_end
|
448
|
+
end
|
449
|
+
def enum(name, cls, value)
|
450
|
+
if JavaClass == cls
|
451
|
+
java_class = cls
|
452
|
+
else
|
453
|
+
java_class = cls.java_class
|
454
|
+
end
|
455
|
+
|
456
|
+
visit_enum(name, ci(java_class), value)
|
457
|
+
end
|
458
|
+
end
|
459
|
+
end
|