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.
@@ -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