bitescript 0.0.9 → 0.1.0
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/bitescript.gemspec +1 -1
- data/examples/invokedynamic.bs +1 -1
- data/examples/simple_loop.rb +9 -0
- data/lib/bitescript.rb +14 -5
- data/lib/bitescript/asm.rb +9 -7
- data/lib/bitescript/asm3/asm.rb +46 -0
- data/lib/bitescript/asm3/builder.rb +624 -0
- data/lib/bitescript/asm3/bytecode.rb +458 -0
- data/lib/bitescript/asm3/mirror.rb +649 -0
- data/lib/bitescript/asm3/signature.rb +133 -0
- data/lib/bitescript/builder.rb +10 -0
- data/lib/bitescript/bytecode.rb +5 -5
- data/lib/bitescript/mirror.rb +50 -18
- data/test/test_builder.rb +6 -0
- metadata +8 -5
data/History.txt
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
=== 0.1.0 / 2011-11-10
|
2
|
+
|
3
|
+
be71f3f Re-add old ASM3 versions of everything for backward compat.
|
4
|
+
56e20b0 More fixes for ASM 4.0.
|
5
|
+
9daf504 Fixes for asm 4.0.
|
6
|
+
dff922a Added a note about the simple loop example explaining how it works.
|
7
|
+
f0ba202 Merge pull request #2 from calavera/master
|
8
|
+
|
1
9
|
=== 0.0.9 / 2011-07-11
|
2
10
|
|
3
11
|
62a10d2 Update time format in gemspec
|
data/bitescript.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{bitescript}
|
5
|
-
s.version = "0.0
|
5
|
+
s.version = "0.1.0"
|
6
6
|
s.authors = ["Charles Oliver Nutter", "Ryan Brown"]
|
7
7
|
s.date = Time.now.strftime('%Y-%m-%d')
|
8
8
|
s.description = %q{BiteScript is a Ruby DSL for generating Java bytecode and classes.}
|
data/examples/invokedynamic.bs
CHANGED
@@ -8,7 +8,7 @@ JClass = java.lang.Class
|
|
8
8
|
# Our main method, which does one invokedynamic
|
9
9
|
main do
|
10
10
|
# handle for our bootstrap, which binds invokedynamic to a CallSite
|
11
|
-
bootstrap =
|
11
|
+
bootstrap = h_invokestatic this, 'bootstrap', CallSite, Lookup, string, MethodType
|
12
12
|
|
13
13
|
ldc 'Hello, invokedynamic!'
|
14
14
|
invokedynamic 'print', [void, string], bootstrap
|
data/examples/simple_loop.rb
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
# Simple Loop
|
2
|
+
#
|
3
|
+
# Run this file to create a SimpleLoop.class file.
|
4
|
+
# It takes one argument and prints it out in a loop
|
5
|
+
# until you stop the process.
|
6
|
+
#
|
7
|
+
# ruby examples/simple_loop.rb
|
8
|
+
# java SimpleLoop repeatMe
|
9
|
+
#
|
1
10
|
require 'bitescript'
|
2
11
|
|
3
12
|
include BiteScript
|
data/lib/bitescript.rb
CHANGED
@@ -1,9 +1,17 @@
|
|
1
1
|
$: << File.dirname(__FILE__)
|
2
|
-
|
3
|
-
require 'bitescript/
|
4
|
-
require 'bitescript/
|
5
|
-
require 'bitescript/
|
6
|
-
require 'bitescript/
|
2
|
+
begin
|
3
|
+
require 'bitescript/asm'
|
4
|
+
require 'bitescript/signature'
|
5
|
+
require 'bitescript/bytecode'
|
6
|
+
require 'bitescript/builder'
|
7
|
+
require 'bitescript/mirror'
|
8
|
+
rescue LoadError
|
9
|
+
require 'bitescript/asm3/asm'
|
10
|
+
require 'bitescript/asm3/signature'
|
11
|
+
require 'bitescript/asm3/bytecode'
|
12
|
+
require 'bitescript/asm3/builder'
|
13
|
+
require 'bitescript/asm3/mirror'
|
14
|
+
end
|
7
15
|
|
8
16
|
module BiteScript
|
9
17
|
include BiteScript::ASM
|
@@ -11,6 +19,7 @@ module BiteScript
|
|
11
19
|
JAVA1_5 = Opcodes::V1_5
|
12
20
|
JAVA1_6 = Opcodes::V1_6
|
13
21
|
JAVA1_7 = Opcodes::V1_7
|
22
|
+
JAVA1_8 = defined?(Opcodes::V1_8) ? Opcodes::V1_8 : Opcodes::V1_7
|
14
23
|
|
15
24
|
class << self
|
16
25
|
attr_accessor :bytecode_version
|
data/lib/bitescript/asm.rb
CHANGED
@@ -23,6 +23,15 @@ module BiteScript
|
|
23
23
|
java_import asm_package.Opcodes
|
24
24
|
end
|
25
25
|
end
|
26
|
+
|
27
|
+
# Handle was introduced in ASM 4.0, and is only available to
|
28
|
+
# JRuby > 1.6.1
|
29
|
+
begin
|
30
|
+
java_import asm_package.Handle
|
31
|
+
rescue
|
32
|
+
raise LoadError, "ASM 4 not available, using ASM3 compat"
|
33
|
+
end
|
34
|
+
|
26
35
|
java_import asm_package.Label
|
27
36
|
java_import asm_package.Type
|
28
37
|
java_import asm_package.AnnotationVisitor
|
@@ -35,12 +44,5 @@ module BiteScript
|
|
35
44
|
java_import asm_package.signature.SignatureReader
|
36
45
|
java_import asm_package.signature.SignatureVisitor
|
37
46
|
java_import asm_package.signature.SignatureWriter
|
38
|
-
|
39
|
-
# MethodHandle was introduced in ASM 4.0, and is only available to
|
40
|
-
# JRuby > 1.6.1
|
41
|
-
begin
|
42
|
-
java_import asm_package.MethodHandle
|
43
|
-
rescue
|
44
|
-
end
|
45
47
|
end
|
46
48
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'java'
|
2
|
+
|
3
|
+
module BiteScript
|
4
|
+
module ASM
|
5
|
+
begin
|
6
|
+
# try mangled names for the version included with JRuby <=1.6.0.RC2
|
7
|
+
java.lang.Class.for_name 'jruby.objectweb.asm.Opcodes'
|
8
|
+
|
9
|
+
# no error, proceed with mangled name
|
10
|
+
asm_package = Java::jruby.objectweb.asm
|
11
|
+
java_import asm_package.Opcodes
|
12
|
+
rescue Exception
|
13
|
+
begin
|
14
|
+
# try mangled names for the version included with JRuby >=1.6.0.RC3
|
15
|
+
java.lang.Class.for_name 'org.jruby.org.objectweb.asm.Opcodes'
|
16
|
+
|
17
|
+
# no error, proceed with mangled name
|
18
|
+
asm_package = Java::org.jruby.org.objectweb.asm
|
19
|
+
java_import asm_package.Opcodes
|
20
|
+
rescue Exception
|
21
|
+
# fall back on standard names
|
22
|
+
asm_package = org.objectweb.asm
|
23
|
+
java_import asm_package.Opcodes
|
24
|
+
end
|
25
|
+
end
|
26
|
+
java_import asm_package.Label
|
27
|
+
java_import asm_package.Type
|
28
|
+
java_import asm_package.AnnotationVisitor
|
29
|
+
java_import asm_package.ClassVisitor
|
30
|
+
java_import asm_package.FieldVisitor
|
31
|
+
java_import asm_package.MethodVisitor
|
32
|
+
java_import asm_package.ClassReader
|
33
|
+
java_import asm_package.ClassWriter
|
34
|
+
java_import asm_package.util.CheckClassAdapter
|
35
|
+
java_import asm_package.signature.SignatureReader
|
36
|
+
java_import asm_package.signature.SignatureVisitor
|
37
|
+
java_import asm_package.signature.SignatureWriter
|
38
|
+
|
39
|
+
# MethodHandle was introduced in ASM 4.0, and is only available to
|
40
|
+
# JRuby > 1.6.1
|
41
|
+
begin
|
42
|
+
java_import asm_package.MethodHandle
|
43
|
+
rescue
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,624 @@
|
|
1
|
+
require 'bitescript/asm3/bytecode'
|
2
|
+
require 'bitescript/asm3/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
|
+
module Annotatable
|
63
|
+
java_import "java.lang.annotation.Retention"
|
64
|
+
def annotate(cls, runtime=nil)
|
65
|
+
if runtime.nil?
|
66
|
+
retention = find_retention(cls)
|
67
|
+
return if retention == 'SOURCE'
|
68
|
+
runtime = retention == 'RUNTIME'
|
69
|
+
end
|
70
|
+
|
71
|
+
annotation = visit_annotation(Signature.ci(cls), runtime)
|
72
|
+
annotation.extend AnnotationBuilder
|
73
|
+
|
74
|
+
yield annotation
|
75
|
+
annotation.visit_end
|
76
|
+
end
|
77
|
+
|
78
|
+
def find_retention(cls)
|
79
|
+
if cls.kind_of?(BiteScript::ASM::ClassMirror)
|
80
|
+
retention = cls.getDeclaredAnnotation('java.lang.annotation.Retention')
|
81
|
+
elsif cls.kind_of?(BiteScript::ASM::Type)
|
82
|
+
mirror = BiteScript::ASM::ClassMirror.for_name(cls.class_name) rescue nil
|
83
|
+
return find_retention(mirror) if mirror
|
84
|
+
elsif Java::JavaClass === cls
|
85
|
+
retention = cls.annotation(Retention.java_class)
|
86
|
+
else
|
87
|
+
retention = cls.java_class.annotation(Retention.java_class)
|
88
|
+
end
|
89
|
+
return 'CLASS' if retention.nil?
|
90
|
+
return retention.value.name
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class FileBuilder
|
95
|
+
include Util
|
96
|
+
include QuickTypes
|
97
|
+
|
98
|
+
attr_accessor :file_name
|
99
|
+
attr_accessor :class_builders
|
100
|
+
attr_accessor :imports
|
101
|
+
attr_accessor :package
|
102
|
+
|
103
|
+
def initialize(file_name)
|
104
|
+
@file_name = file_name
|
105
|
+
@class_builders = {}
|
106
|
+
@imports = {}
|
107
|
+
@package = []
|
108
|
+
|
109
|
+
init_imports
|
110
|
+
end
|
111
|
+
|
112
|
+
def init_imports
|
113
|
+
# set up a few useful imports
|
114
|
+
@imports[:int.to_s] = Java::int.java_class
|
115
|
+
@imports[:string.to_s] = Java::java.lang.String.java_class
|
116
|
+
@imports[:object.to_s] = Java::java.lang.Object.java_class
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.build(filename, &block)
|
120
|
+
fb = new(filename)
|
121
|
+
if block_given?
|
122
|
+
fb.instance_eval(&block)
|
123
|
+
end
|
124
|
+
fb
|
125
|
+
end
|
126
|
+
|
127
|
+
def define_class(class_name, opts, &block)
|
128
|
+
pkg = opts[:package] || @package.dup || []
|
129
|
+
|
130
|
+
class_name = pkg.empty? ? class_name : "#{pkg.join('/')}/#{class_name}"
|
131
|
+
class_builder = ClassBuilder.new(self, class_name, @file_name, opts)
|
132
|
+
@class_builders[class_name] ||= class_builder # TODO Is this really what we want?
|
133
|
+
|
134
|
+
if block_given?
|
135
|
+
if block.arity == 1
|
136
|
+
block.call(class_builder)
|
137
|
+
else
|
138
|
+
class_builder.instance_eval(&block)
|
139
|
+
end
|
140
|
+
else
|
141
|
+
return class_builder
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def public_class(class_name, superclass = java.lang.Object, *interfaces, &block)
|
146
|
+
define_class(class_name, :visibility => :public, :superclass => superclass, :interfaces => interfaces, &block)
|
147
|
+
end
|
148
|
+
|
149
|
+
def public_interface(class_name, *interfaces, &block)
|
150
|
+
define_class(class_name, :visibility => :public, :interface => true, :interfaces => interfaces, &block)
|
151
|
+
end
|
152
|
+
|
153
|
+
def protected_class(class_name, superclass = java.lang.Object, *interfaces, &block)
|
154
|
+
define_class(class_name, :visibility => :protected, :superclass => superclass, :interfaces => interfaces, &block)
|
155
|
+
end
|
156
|
+
|
157
|
+
def private_class(class_name, superclass = java.lang.Object, *interfaces, &block)
|
158
|
+
define_class(class_name, :visibility => :private, :superclass => superclass, :interfaces => interfaces, &block)
|
159
|
+
end
|
160
|
+
|
161
|
+
def default_class(class_name, superclass = java.lang.Object, *interfaces, &block)
|
162
|
+
define_class(class_name, :visibility => :default, :superclass => superclass, :interfaces => interfaces, &block)
|
163
|
+
end
|
164
|
+
|
165
|
+
def generate
|
166
|
+
@class_builders.each do |class_name, class_builder|
|
167
|
+
class_file = "#{class_name.gsub('.', '/')}.class"
|
168
|
+
|
169
|
+
yield class_file, class_builder
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def line(line)
|
174
|
+
# No tracking of lines at the file level, so we ignore
|
175
|
+
end
|
176
|
+
|
177
|
+
def package(*names)
|
178
|
+
return @package unless names.size > 0
|
179
|
+
|
180
|
+
packages = unpack_packages(*names)
|
181
|
+
@package.concat(packages)
|
182
|
+
yield
|
183
|
+
@package = @package[0..(packages.size - 1)]
|
184
|
+
end
|
185
|
+
|
186
|
+
def package=(name)
|
187
|
+
@package = unpack_packages(name)
|
188
|
+
end
|
189
|
+
|
190
|
+
def unpack_packages(*names)
|
191
|
+
package = []
|
192
|
+
names.each do |name_maybe_dotted|
|
193
|
+
name_maybe_dotted.split(/\./).each do |name|
|
194
|
+
package.push name
|
195
|
+
end
|
196
|
+
end
|
197
|
+
package
|
198
|
+
end
|
199
|
+
|
200
|
+
def method?
|
201
|
+
false
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
class ClassBuilder
|
206
|
+
include Util
|
207
|
+
include QuickTypes
|
208
|
+
include Annotatable
|
209
|
+
include ASM
|
210
|
+
|
211
|
+
java_import java.lang.Object
|
212
|
+
java_import java.lang.Void
|
213
|
+
include Signature
|
214
|
+
|
215
|
+
attr_accessor :class_name
|
216
|
+
attr_accessor :superclass
|
217
|
+
attr_accessor :constructors
|
218
|
+
attr_accessor :methods
|
219
|
+
attr_accessor :imports
|
220
|
+
attr_accessor :fields
|
221
|
+
attr_accessor :interfaces
|
222
|
+
|
223
|
+
def initialize(file_builder, class_name, file_name, opts)
|
224
|
+
@parent = file_builder
|
225
|
+
@class_name = class_name
|
226
|
+
@superclass = opts[:superclass] || Object
|
227
|
+
@interfaces = opts[:interfaces] || []
|
228
|
+
@interface = opts[:interface]
|
229
|
+
flags = Opcodes::ACC_SUPER
|
230
|
+
flags |= Opcodes::ACC_ABSTRACT if opts[:abstract]
|
231
|
+
if @interface
|
232
|
+
flags = Opcodes::ACC_INTERFACE | Opcodes::ACC_ABSTRACT
|
233
|
+
end
|
234
|
+
|
235
|
+
@class_writer = ClassWriter.new(ClassWriter::COMPUTE_FRAMES | ClassWriter::COMPUTE_MAXS)
|
236
|
+
|
237
|
+
interface_paths = []
|
238
|
+
(@interfaces).each {|interface| interface_paths << path(interface)}
|
239
|
+
|
240
|
+
visibility = case (opts[:visibility] && opts[:visibility].to_sym)
|
241
|
+
when nil
|
242
|
+
Opcodes::ACC_PUBLIC # NOTE Not specified means public -- must explicitly ask for default
|
243
|
+
when :default
|
244
|
+
0
|
245
|
+
when :public
|
246
|
+
Opcodes::ACC_PUBLIC
|
247
|
+
when :private
|
248
|
+
Opcodes::ACC_PRIVATE
|
249
|
+
when :protected
|
250
|
+
Opcodes::ACC_PROTECTED
|
251
|
+
else
|
252
|
+
raise "Unknown visibility: #{opts[:visibility]}"
|
253
|
+
end
|
254
|
+
|
255
|
+
@class_writer.visit(BiteScript.bytecode_version, visibility | flags, class_name, nil, path(superclass), interface_paths.to_java(:string))
|
256
|
+
@class_writer.visit_source(file_name, nil)
|
257
|
+
|
258
|
+
@constructor = nil
|
259
|
+
@constructors = {}
|
260
|
+
@methods = {}
|
261
|
+
|
262
|
+
@imports = {}
|
263
|
+
|
264
|
+
@fields = {}
|
265
|
+
end
|
266
|
+
|
267
|
+
def start
|
268
|
+
end
|
269
|
+
|
270
|
+
def stop
|
271
|
+
# if we haven't seen a constructor, generate a default one
|
272
|
+
unless @constructor || @interface
|
273
|
+
method = public_constructor([])
|
274
|
+
method.start
|
275
|
+
method.aload 0
|
276
|
+
method.invokespecial @superclass, "<init>", [Void::TYPE]
|
277
|
+
method.returnvoid
|
278
|
+
method.stop
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def generate
|
283
|
+
bytes = @class_writer.to_byte_array
|
284
|
+
if ENV['BS_CHECK_CLASSES']
|
285
|
+
BiteScript::ASM::CheckClassAdapter.verify(
|
286
|
+
BiteScript::ASM::ClassReader.new(bytes),
|
287
|
+
JRuby.runtime.jruby_class_loader,
|
288
|
+
false,
|
289
|
+
java.io.PrintWriter.new(java.lang.System.out, true))
|
290
|
+
end
|
291
|
+
String.from_java_bytes(bytes)
|
292
|
+
end
|
293
|
+
|
294
|
+
%w[public private protected].each do |modifier|
|
295
|
+
# instance fields
|
296
|
+
eval "
|
297
|
+
def #{modifier}_field(name, type)
|
298
|
+
field(Opcodes::ACC_#{modifier.upcase}, name, type)
|
299
|
+
end
|
300
|
+
", binding, __FILE__, __LINE__
|
301
|
+
# static fields
|
302
|
+
eval "
|
303
|
+
def #{modifier}_static_field(name, type)
|
304
|
+
field(Opcodes::ACC_STATIC | Opcodes::ACC_#{modifier.upcase}, name, type)
|
305
|
+
end
|
306
|
+
", binding, __FILE__, __LINE__
|
307
|
+
# instance methods; also defines a "this" local at index 0
|
308
|
+
eval "
|
309
|
+
def #{modifier}_method(name, exceptions=[], *signature, &block)
|
310
|
+
method(Opcodes::ACC_#{modifier.upcase}, name, signature, exceptions, &block)
|
311
|
+
end
|
312
|
+
", binding, __FILE__, __LINE__
|
313
|
+
# static methods
|
314
|
+
eval "
|
315
|
+
def #{modifier}_static_method(name, exceptions=[], *signature, &block)
|
316
|
+
method(Opcodes::ACC_STATIC | Opcodes::ACC_#{modifier.upcase}, name, signature, exceptions, &block)
|
317
|
+
end
|
318
|
+
", binding, __FILE__, __LINE__
|
319
|
+
# native methods
|
320
|
+
eval "
|
321
|
+
def #{modifier}_native_method(name, exceptions=[], *signature)
|
322
|
+
method(Opcodes::ACC_NATIVE | Opcodes::ACC_#{modifier.upcase}, name, signature, exceptions)
|
323
|
+
end
|
324
|
+
", binding, __FILE__, __LINE__
|
325
|
+
# constructors; also defines a "this" local at index 0
|
326
|
+
eval "
|
327
|
+
def #{modifier}_constructor(exceptions=[], *signature, &block)
|
328
|
+
@constructor = method(Opcodes::ACC_#{modifier.upcase}, \"<init>\", [nil, *signature], exceptions, &block)
|
329
|
+
end
|
330
|
+
", binding, __FILE__, __LINE__
|
331
|
+
end
|
332
|
+
|
333
|
+
def static_init(&block)
|
334
|
+
method(Opcodes::ACC_STATIC, "<clinit>", [void], [], &block)
|
335
|
+
end
|
336
|
+
|
337
|
+
def build_method(name, visibility, static, exceptions, type, *args)
|
338
|
+
flags =
|
339
|
+
case visibility
|
340
|
+
when :public; Opcodes::ACC_PUBLIC
|
341
|
+
when :private; Opcodes::ACC_PRIVATE
|
342
|
+
when :protected; Opcodes::ACC_PROTECTED
|
343
|
+
end
|
344
|
+
flags |= Opcodes::ACC_STATIC if static
|
345
|
+
method(flags, name, [type, *args], exceptions)
|
346
|
+
end
|
347
|
+
|
348
|
+
def build_constructor(visibility, exceptions, *args)
|
349
|
+
flags =
|
350
|
+
case visibility
|
351
|
+
when :public; Opcodes::ACC_PUBLIC
|
352
|
+
when :private; Opcodes::ACC_PRIVATE
|
353
|
+
when :protected; Opcodes::ACC_PROTECTED
|
354
|
+
end
|
355
|
+
@constructor = method(flags, "<init>", [nil, *args], exceptions)
|
356
|
+
end
|
357
|
+
|
358
|
+
def method(flags, name, signature, exceptions, &block)
|
359
|
+
flags |= Opcodes::ACC_ABSTRACT if interface?
|
360
|
+
mb = MethodBuilder.new(self, flags, name, exceptions, signature)
|
361
|
+
|
362
|
+
if name == "<init>"
|
363
|
+
constructors[signature[1..-1]] = mb
|
364
|
+
else
|
365
|
+
methods[name] ||= {}
|
366
|
+
methods[name][signature[1..-1]] = mb
|
367
|
+
end
|
368
|
+
|
369
|
+
# non-static methods reserve index 0 for 'this'
|
370
|
+
mb.local 'this', self if (flags & Opcodes::ACC_STATIC) == 0
|
371
|
+
|
372
|
+
if block_given? && !interface?
|
373
|
+
mb.start
|
374
|
+
if block.arity == 1
|
375
|
+
block.call(mb)
|
376
|
+
else
|
377
|
+
mb.instance_eval(&block)
|
378
|
+
end
|
379
|
+
mb.stop
|
380
|
+
end
|
381
|
+
|
382
|
+
mb
|
383
|
+
end
|
384
|
+
|
385
|
+
def java_method(name, *params)
|
386
|
+
if methods[name]
|
387
|
+
method = methods[name][params]
|
388
|
+
end
|
389
|
+
|
390
|
+
method or raise NameError.new("failed to find method #{name}#{sig(params)} on #{self}")
|
391
|
+
end
|
392
|
+
|
393
|
+
def main(&b)
|
394
|
+
raise "already defined main" if methods[name]
|
395
|
+
|
396
|
+
public_static_method "main", [], void, string[], &b
|
397
|
+
end
|
398
|
+
|
399
|
+
def constructor(*params)
|
400
|
+
constructors[params] or raise NameError.new("failed to find constructor #{sig(params)} on #{self}")
|
401
|
+
end
|
402
|
+
|
403
|
+
def interface?
|
404
|
+
# TODO: interface types
|
405
|
+
@interface
|
406
|
+
end
|
407
|
+
|
408
|
+
def field(flags, name, type)
|
409
|
+
field = @class_writer.visit_field(flags, name, ci(type), nil, nil)
|
410
|
+
field.extend Annotatable
|
411
|
+
end
|
412
|
+
|
413
|
+
# name for signature generation using the class being generated
|
414
|
+
def name
|
415
|
+
@class_name
|
416
|
+
end
|
417
|
+
|
418
|
+
# never generating an array
|
419
|
+
def array?
|
420
|
+
false
|
421
|
+
end
|
422
|
+
|
423
|
+
# never generating a primitive
|
424
|
+
def primitive?
|
425
|
+
false
|
426
|
+
end
|
427
|
+
|
428
|
+
def this
|
429
|
+
self
|
430
|
+
end
|
431
|
+
|
432
|
+
def visit_annotation(*args)
|
433
|
+
@class_writer.visit_annotation(*args)
|
434
|
+
end
|
435
|
+
|
436
|
+
def new_method(modifiers, name, signature, exceptions)
|
437
|
+
exceptions ||= []
|
438
|
+
unless exceptions.kind_of?(Array)
|
439
|
+
raise ArgumentError, "Expected array of exceptions, got #{exceptions.inspect}"
|
440
|
+
end
|
441
|
+
exceptions = exceptions.map {|e| path(e)}
|
442
|
+
@class_writer.visit_method(modifiers, name, sig(*signature), nil, exceptions.to_java(:string))
|
443
|
+
end
|
444
|
+
|
445
|
+
def macro(name, &b)
|
446
|
+
MethodBuilder.send :define_method, name, &b
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
class MethodBuilder
|
451
|
+
include QuickTypes
|
452
|
+
include Annotatable
|
453
|
+
include BiteScript::Bytecode
|
454
|
+
include ASM
|
455
|
+
|
456
|
+
attr_reader :method_visitor
|
457
|
+
attr_reader :static
|
458
|
+
alias :static? :static
|
459
|
+
attr_reader :signature
|
460
|
+
attr_reader :name
|
461
|
+
attr_reader :class_builder
|
462
|
+
|
463
|
+
def initialize(class_builder, modifiers, name, exceptions, signature)
|
464
|
+
@class_builder = class_builder
|
465
|
+
@modifiers = modifiers
|
466
|
+
@name = name
|
467
|
+
@signature = signature
|
468
|
+
|
469
|
+
@method_visitor = class_builder.new_method(modifiers, name, signature, exceptions)
|
470
|
+
|
471
|
+
@locals = {}
|
472
|
+
@next_local = 0
|
473
|
+
|
474
|
+
@static = (modifiers & Opcodes::ACC_STATIC) != 0
|
475
|
+
@start_label = labels[:_start] = self.label
|
476
|
+
@end_label = labels[:_end] = self.label
|
477
|
+
@exceptions = exceptions || []
|
478
|
+
end
|
479
|
+
|
480
|
+
def parameter_types
|
481
|
+
signature[1..-1]
|
482
|
+
end
|
483
|
+
|
484
|
+
def return_type
|
485
|
+
signature[0]
|
486
|
+
end
|
487
|
+
|
488
|
+
def declaring_class
|
489
|
+
@class_builder
|
490
|
+
end
|
491
|
+
|
492
|
+
def self.build(class_builder, modifiers, name, signature, &block)
|
493
|
+
mb = MethodBuilder.new(class_builder, modifiers, name, signature)
|
494
|
+
mb.start
|
495
|
+
if block.arity == 1
|
496
|
+
block.call(mb)
|
497
|
+
else
|
498
|
+
mb.instance_eval(&block)
|
499
|
+
end
|
500
|
+
mb.stop
|
501
|
+
end
|
502
|
+
|
503
|
+
def self.build2(class_builder, modifiers, name, signature, &block)
|
504
|
+
mb = MethodBuilder.new(class_builder, modifiers, name, signature)
|
505
|
+
mb.start
|
506
|
+
block.call(mb)
|
507
|
+
mb.stop
|
508
|
+
end
|
509
|
+
|
510
|
+
def generate(&block)
|
511
|
+
start
|
512
|
+
block.call(self)
|
513
|
+
stop
|
514
|
+
end
|
515
|
+
|
516
|
+
def this
|
517
|
+
@class_builder
|
518
|
+
end
|
519
|
+
|
520
|
+
def local(name, type=nil)
|
521
|
+
if name == "this" && @static
|
522
|
+
raise "'this' attempted to load from static method"
|
523
|
+
end
|
524
|
+
|
525
|
+
if @locals[name]
|
526
|
+
return @locals[name][-1][0]
|
527
|
+
else
|
528
|
+
raise ArgumentError, 'Local type required' unless type
|
529
|
+
return push_local(name, type, @start_label)
|
530
|
+
end
|
531
|
+
end
|
532
|
+
|
533
|
+
def push_local(name, type, start=nil)
|
534
|
+
start ||= self.label.set!
|
535
|
+
type = ci(type)
|
536
|
+
big = "JD".include? type
|
537
|
+
match = @locals[name].find {|local| !big || local[1]} if @locals[name]
|
538
|
+
if match
|
539
|
+
index = match[0]
|
540
|
+
else
|
541
|
+
index = @next_local
|
542
|
+
@next_local += 1
|
543
|
+
@next_local += 1 if big
|
544
|
+
end
|
545
|
+
|
546
|
+
if @locals[name] && @locals[name].size > 0
|
547
|
+
local_debug_info(name, @locals[name][-1])
|
548
|
+
else
|
549
|
+
@locals[name] = []
|
550
|
+
end
|
551
|
+
@locals[name] << [index, big, type, start]
|
552
|
+
index
|
553
|
+
end
|
554
|
+
|
555
|
+
def local_debug_info(name, local, end_label=nil)
|
556
|
+
return unless local
|
557
|
+
index, big, type, start = local
|
558
|
+
end_label ||= self.label.set!
|
559
|
+
method_visitor.visit_local_variable(name, type, nil,
|
560
|
+
start.label,
|
561
|
+
end_label.label,
|
562
|
+
index)
|
563
|
+
end
|
564
|
+
|
565
|
+
def pop_local(name)
|
566
|
+
here = self.label.set!
|
567
|
+
local_debug_info(name, @locals[name].pop, here)
|
568
|
+
@locals[name][-1][-1] = here if @locals[name].size > 0
|
569
|
+
end
|
570
|
+
|
571
|
+
def visit_annotation(*args)
|
572
|
+
@method_visitor.visit_annotation(*args)
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
576
|
+
module AnnotationBuilder
|
577
|
+
include Signature
|
578
|
+
def method_missing(name, val)
|
579
|
+
name_str = name.to_s
|
580
|
+
if name_str[-1] == ?=
|
581
|
+
name_str = name_str[0..-2]
|
582
|
+
if Array === val
|
583
|
+
array(name_str) do |ary|
|
584
|
+
val.each {|x| ary.visit(nil, x)}
|
585
|
+
end
|
586
|
+
else
|
587
|
+
visit name_str, val
|
588
|
+
end
|
589
|
+
else
|
590
|
+
super
|
591
|
+
end
|
592
|
+
end
|
593
|
+
def value(k, v)
|
594
|
+
visit k, v
|
595
|
+
end
|
596
|
+
def annotation(name, cls)
|
597
|
+
if Java::JavaClass === cls || BiteScript::ASM::Type === cls
|
598
|
+
java_class = cls
|
599
|
+
else
|
600
|
+
java_class = cls.java_class
|
601
|
+
end
|
602
|
+
|
603
|
+
sub_annotation = visit_annotation(name, ci(java_class))
|
604
|
+
sub_annotation.extend AnnotationBuilder
|
605
|
+
yield sub_annotation
|
606
|
+
sub_annotation.visit_end
|
607
|
+
end
|
608
|
+
def array(name)
|
609
|
+
sub_annotation = visit_array(name)
|
610
|
+
sub_annotation.extend AnnotationBuilder
|
611
|
+
yield sub_annotation
|
612
|
+
sub_annotation.visit_end
|
613
|
+
end
|
614
|
+
def enum(name, cls, value)
|
615
|
+
if JavaClass == cls
|
616
|
+
java_class = cls
|
617
|
+
else
|
618
|
+
java_class = cls.java_class
|
619
|
+
end
|
620
|
+
|
621
|
+
visit_enum(name, ci(java_class), value)
|
622
|
+
end
|
623
|
+
end
|
624
|
+
end
|