bitescript 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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