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,415 @@
|
|
1
|
+
require 'bitescript/asm'
|
2
|
+
require 'bitescript/signature'
|
3
|
+
|
4
|
+
module BiteScript
|
5
|
+
# Bytecode is a simple adapter around an ASM MethodVisitor that makes it look like
|
6
|
+
# JVM assembly code. Included classes must just provide a method_visitor accessor
|
7
|
+
module Bytecode
|
8
|
+
include Signature
|
9
|
+
include ASM
|
10
|
+
|
11
|
+
JObject = java.lang.Object
|
12
|
+
JSystem = java.lang.System
|
13
|
+
JPrintStream = java.io.PrintStream
|
14
|
+
JVoid = java.lang.Void
|
15
|
+
JInteger = java.lang.Integer
|
16
|
+
JFloat = java.lang.Float
|
17
|
+
JLong = java.lang.Long
|
18
|
+
JDobule = java.lang.Double
|
19
|
+
|
20
|
+
b = binding
|
21
|
+
OpcodeStackDeltas = {}
|
22
|
+
%w[ALOAD ILOAD FLOAD BIPUSH SIPUSH DUP DUP_X1 DUP_X2 ACONST_NULL ICONST_M1
|
23
|
+
ICONST_0 ICONST_1 ICONST_2 ICONST_3 ICONST_4 ICONST_5 I2L I2D FCONST_0
|
24
|
+
FCONST_1 FCONST_2 F2L F2D NEW JSR].each {|opcode| OpcodeStackDeltas[opcode] = 1}
|
25
|
+
%w[LLOAD DLOAD DUP2 DUP2_X1 DUP2_X2 LCONST_0 LCONST_1 DCONST_0 DCONST_1].each {|opcode| OpcodeStackDeltas[opcode] = 2}
|
26
|
+
%w[ASTORE ISTORE FSTORE POP ARETURN IRETURN ATHROW AALOAD BALOAD CALOAD
|
27
|
+
SALOAD IALOAD FALOAD IADD ISUB IDIV IMUL IAND IOR IXOR IREM L2I L2F
|
28
|
+
FRETURN FADD FSUB FDIV FMUL FREM FCMPG FCMPL D2I D2F IFEQ IFNE IFNULL
|
29
|
+
IFNONNULL IFLT IFGT IFLE IFGE LOOKUPSWITCH TABLESWITCH MONITORENTER
|
30
|
+
MONITOREXIT].each {|opcode| OpcodeStackDeltas[opcode] = -1}
|
31
|
+
%w[LSTORE DSTORE POP2 LREM LADD LSUB LDIV LMUL LAND LOR LXOR LRETURN DRETURN
|
32
|
+
DADD DSUB DDIV DMUL DREM IF_ACMPEQ IF_ACMPNE IF_ICMPEQ IF_ICMPNE
|
33
|
+
IF_ICMPLT IF_ICMPGT IF_ICMPLE IF_ICMPGE].each {|opcode| OpcodeStackDeltas[opcode] = -2}
|
34
|
+
%w[AASTORE BASTORE CASTORE SASTORE IASTORE LCMP FASTORE DCMPL DCMPG].each {|opcode| OpcodeStackDeltas[opcode] = -3}
|
35
|
+
%w[LASTORE DASTORE].each {|opcode| OpcodeStackDeltas[opcode] = -4}
|
36
|
+
%w[RETURN SWAP NOP ARRAYLENGTH IINC INEG IUSHR ISHL ISHR I2S I2F I2B I2C
|
37
|
+
LALOAD LSHL LSHR LUSHR LNEG L2D FNEG F2I DALOAD D2L DNEG RET
|
38
|
+
CHECKCAST ANEWARRAY NEWARRAY GOTO INSTANCEOF].each {|opcode| OpcodeStackDeltas[opcode] = 0}
|
39
|
+
|
40
|
+
OpcodeInstructions = {}
|
41
|
+
|
42
|
+
Opcodes.constants.each do |const_name|
|
43
|
+
const_down = const_name.downcase
|
44
|
+
|
45
|
+
case const_name
|
46
|
+
when "ALOAD", "ASTORE",
|
47
|
+
"ISTORE", "ILOAD",
|
48
|
+
"LSTORE", "LLOAD",
|
49
|
+
"FSTORE", "FLOAD",
|
50
|
+
"DSTORE", "DLOAD",
|
51
|
+
"RET"
|
52
|
+
# variable instructions
|
53
|
+
line = __LINE__; eval "
|
54
|
+
def #{const_down}(var)
|
55
|
+
method_visitor.visit_var_insn(Opcodes::#{const_name}, var)
|
56
|
+
#{OpcodeStackDeltas[const_name]}
|
57
|
+
end
|
58
|
+
", b, __FILE__, line
|
59
|
+
OpcodeInstructions[const_name] = const_down
|
60
|
+
|
61
|
+
when "LDC"
|
62
|
+
# constant loading is tricky because overloaded invocation is pretty bad in JRuby
|
63
|
+
def ldc_int(value); method_visitor.visit_ldc_insn(java.lang.Integer.new(value)); 1; end
|
64
|
+
def ldc_long(value); method_visitor.visit_ldc_insn(java.lang.Long.new(value)); 2; end
|
65
|
+
def ldc_float(value); method_visitor.visit_ldc_insn(java.lang.Float.new(value)); 1; end
|
66
|
+
def ldc_double(value); method_visitor.visit_ldc_insn(java.lang.Double.new(value)); 2; end
|
67
|
+
def ldc_class(value)
|
68
|
+
value = value.java_class unless Java::JavaClass === value
|
69
|
+
method_visitor.visit_ldc_insn(ASM::Type.get_type(value))
|
70
|
+
1
|
71
|
+
end
|
72
|
+
line = __LINE__; eval "
|
73
|
+
def #{const_down}(value)
|
74
|
+
size = 1
|
75
|
+
|
76
|
+
case value
|
77
|
+
when Symbol
|
78
|
+
method_visitor.visit_ldc_insn value.to_s
|
79
|
+
when Fixnum
|
80
|
+
size = push_int value
|
81
|
+
when Float
|
82
|
+
ldc_double(value)
|
83
|
+
when Module
|
84
|
+
ldc_class(value)
|
85
|
+
else
|
86
|
+
method_visitor.visit_ldc_insn(value)
|
87
|
+
end
|
88
|
+
|
89
|
+
size
|
90
|
+
end
|
91
|
+
", b, __FILE__, line
|
92
|
+
OpcodeInstructions[const_name] = const_down
|
93
|
+
|
94
|
+
when "BIPUSH", "SIPUSH"
|
95
|
+
line = __LINE__; eval "
|
96
|
+
def #{const_down}(value)
|
97
|
+
method_visitor.visit_int_insn(Opcodes::#{const_name}, value)
|
98
|
+
1
|
99
|
+
end
|
100
|
+
", b, __FILE__, line
|
101
|
+
OpcodeInstructions[const_name] = const_down
|
102
|
+
|
103
|
+
when "INVOKESTATIC", "INVOKEVIRTUAL", "INVOKEINTERFACE", "INVOKESPECIAL"
|
104
|
+
# method instructions
|
105
|
+
line = __LINE__; eval "
|
106
|
+
def #{const_down}(type, name, call_sig)
|
107
|
+
method_visitor.visit_method_insn(Opcodes::#{const_name}, path(type), name.to_s, sig(*call_sig))
|
108
|
+
|
109
|
+
case call_sig[0]
|
110
|
+
when nil, Java::void, java.lang.Void
|
111
|
+
added = 0
|
112
|
+
when Java::boolean, Java::short, Java::char, Java::int, Java::float
|
113
|
+
added = 1
|
114
|
+
when Java::long, Java::double
|
115
|
+
added = 2
|
116
|
+
else
|
117
|
+
added = 1
|
118
|
+
end
|
119
|
+
|
120
|
+
this_subtracted = #{const_name == 'INVOKESTATIC' ? 0 : 1}
|
121
|
+
|
122
|
+
args_subtracted = 0
|
123
|
+
[*call_sig][1..-1].each do |param|
|
124
|
+
case param
|
125
|
+
when nil, Java::void, java.lang.Void
|
126
|
+
args_subtracted += 0
|
127
|
+
when Java::boolean, Java::short, Java::char, Java::int, Java::float
|
128
|
+
args_subtracted += 1
|
129
|
+
when Java::long, Java::double
|
130
|
+
args_subtracted += 2
|
131
|
+
else
|
132
|
+
args_subtracted += 1
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
added - (this_subtracted + args_subtracted)
|
137
|
+
end
|
138
|
+
", b, __FILE__, line
|
139
|
+
OpcodeInstructions[const_name] = const_down
|
140
|
+
|
141
|
+
when "RETURN"
|
142
|
+
# special case for void return, since return is a reserved word
|
143
|
+
def returnvoid()
|
144
|
+
method_visitor.visit_insn(Opcodes::RETURN)
|
145
|
+
0
|
146
|
+
end
|
147
|
+
OpcodeInstructions['RETURN'] = 'returnvoid'
|
148
|
+
|
149
|
+
when "DUP", "SWAP", "POP", "POP2", "DUP_X1", "DUP_X2", "DUP2", "DUP2_X1", "DUP2_X2",
|
150
|
+
"NOP",
|
151
|
+
"ARRAYLENGTH",
|
152
|
+
"ARETURN", "ATHROW", "ACONST_NULL", "AALOAD", "AASTORE",
|
153
|
+
"BALOAD", "BASTORE",
|
154
|
+
"CALOAD", "CASTORE",
|
155
|
+
"SALOAD", "SASTORE",
|
156
|
+
"ICONST_M1", "ICONST_0", "ICONST_1", "ICONST_2", "ICONST_3", "ICONST_4", "ICONST_5", "IRETURN", "IALOAD",
|
157
|
+
"IADD", "ISUB", "IDIV", "IMUL", "INEG", "IAND", "IOR", "IXOR", "IASTORE",
|
158
|
+
"IUSHR", "ISHL", "ISHR", "I2L", "I2S", "I2F", "I2D", "I2B", "IREM", "I2C",
|
159
|
+
"LCONST_0", "LCONST_1", "LRETURN", "LALOAD", "LASTORE", "LCMP", "LSHL", "LSHR", "LREM", "LUSHR",
|
160
|
+
"LADD", "LINC", "LSUB", "LDIV", "LMUL", "LNEG", "LAND", "LOR", "LXOR", "L2I", "L2F", "L2D",
|
161
|
+
"FCONST_0", "FCONST_1", "FCONST_2", "FRETURN", "FALOAD", "F2D", "F2I", "FASTORE",
|
162
|
+
"FADD", "FSUB", "FDIV", "FMUL", "FNEG", "FREM", "FCMPG", "F2L", "FCMPL",
|
163
|
+
"DCONST_0", "DCONST_1", "DRETURN", "DALOAD", "DASTORE", "D2I", "D2F", "D2L",
|
164
|
+
"DADD", "DINC", "DSUB", "DDIV", "DMUL", "DNEG", "DCMPL", "DCMPG", "DREM",
|
165
|
+
"MONITORENTER", "MONITOREXIT"
|
166
|
+
# bare instructions
|
167
|
+
line = __LINE__; eval "
|
168
|
+
def #{const_down}
|
169
|
+
method_visitor.visit_insn(Opcodes::#{const_name})
|
170
|
+
#{OpcodeStackDeltas[const_name]}
|
171
|
+
end
|
172
|
+
", b, __FILE__, line
|
173
|
+
OpcodeInstructions[const_name] = const_down
|
174
|
+
|
175
|
+
when "IINC"
|
176
|
+
def iinc(index, value)
|
177
|
+
method_visitor.visit_iinc_insn(index, value)
|
178
|
+
0
|
179
|
+
end
|
180
|
+
OpcodeInstructions[const_name] = 'iinc'
|
181
|
+
|
182
|
+
when "NEW", "ANEWARRAY", "INSTANCEOF", "CHECKCAST"
|
183
|
+
# type instructions
|
184
|
+
line = __LINE__; eval "
|
185
|
+
def #{const_down}(type)
|
186
|
+
method_visitor.visit_type_insn(Opcodes::#{const_name}, path(type))
|
187
|
+
#{OpcodeStackDeltas[const_name]}
|
188
|
+
end
|
189
|
+
", b, __FILE__, line
|
190
|
+
OpcodeInstructions[const_name] = const_down
|
191
|
+
|
192
|
+
when "NEWARRAY"
|
193
|
+
# newaray has its own peculiarities
|
194
|
+
{
|
195
|
+
:boolean => 'Opcodes::T_BOOLEAN',
|
196
|
+
:byte => 'Opcodes::T_BYTE',
|
197
|
+
:short => 'Opcodes::T_SHORT',
|
198
|
+
:char => 'Opcodes::T_CHAR',
|
199
|
+
:int => 'Opcodes::T_INT',
|
200
|
+
:long => 'Opcodes::T_LONG',
|
201
|
+
:float => 'Opcodes::T_FLOAT',
|
202
|
+
:double => 'Opcodes::T_DOUBLE'
|
203
|
+
}.each do |type, op_type|
|
204
|
+
line = __LINE__; eval "
|
205
|
+
def new#{type}array()
|
206
|
+
method_visitor.visit_int_insn(Opcodes::#{const_name}, #{op_type})
|
207
|
+
#{OpcodeStackDeltas[const_name]}
|
208
|
+
end
|
209
|
+
", b, __FILE__, line
|
210
|
+
end
|
211
|
+
OpcodeInstructions[const_name] = const_down
|
212
|
+
|
213
|
+
when "GETFIELD", "PUTFIELD", "GETSTATIC", "PUTSTATIC"
|
214
|
+
# field instructions
|
215
|
+
line = __LINE__; eval "
|
216
|
+
def #{const_down}(type, name, field_type)
|
217
|
+
method_visitor.visit_field_insn(Opcodes::#{const_name}, path(type), name.to_s, ci(field_type))
|
218
|
+
|
219
|
+
case field_type
|
220
|
+
when Java::boolean, Java::short, Java::char, Java::int, Java::float
|
221
|
+
delta = 1
|
222
|
+
when Java::long, Java::double
|
223
|
+
delta = 2
|
224
|
+
else
|
225
|
+
delta = 1
|
226
|
+
end
|
227
|
+
|
228
|
+
this_subtracted = #{const_name[3..7] == 'STATI' ? 0 : 1}
|
229
|
+
|
230
|
+
delta *= #{const_name[0..2] == 'PUT' ? -1 : 1}
|
231
|
+
delta -= this_subtracted
|
232
|
+
delta
|
233
|
+
end
|
234
|
+
", b, __FILE__, line
|
235
|
+
OpcodeInstructions[const_name] = const_down
|
236
|
+
|
237
|
+
when "GOTO", "IFEQ", "IFNE", "IF_ACMPEQ", "IF_ACMPNE", "IF_ICMPEQ", "IF_ICMPNE", "IF_ICMPLT",
|
238
|
+
"IF_ICMPGT", "IF_ICMPLE", "IF_ICMPGE", "IFNULL", "IFNONNULL", "JSR",
|
239
|
+
"IFLE", "IFGE", "IFLT", "IFGT"
|
240
|
+
# jump instructions
|
241
|
+
line = __LINE__; eval "
|
242
|
+
def #{const_down}(target)
|
243
|
+
target = sym_to_label(target) if Symbol === target
|
244
|
+
method_visitor.visit_jump_insn(Opcodes::#{const_name}, target.label)
|
245
|
+
#{OpcodeStackDeltas[const_name]}
|
246
|
+
end
|
247
|
+
", b, __FILE__, line
|
248
|
+
OpcodeInstructions[const_name] = const_down
|
249
|
+
|
250
|
+
when "MULTIANEWARRAY"
|
251
|
+
# multi-dim array
|
252
|
+
line = __LINE__; eval "
|
253
|
+
def #{const_down}(type, dims)
|
254
|
+
method_visitor.visit_multi_anew_array_insn(ci(type), dims)
|
255
|
+
-dims
|
256
|
+
end
|
257
|
+
", b, __FILE__, line
|
258
|
+
OpcodeInstructions[const_name] = const_down
|
259
|
+
|
260
|
+
when "LOOKUPSWITCH"
|
261
|
+
def lookupswitch(default, ints, cases = [])
|
262
|
+
cases = cases.to_ary
|
263
|
+
if cases.size > 0
|
264
|
+
# symbol labels, map them to actual
|
265
|
+
case_labels = cases.map do |lbl|
|
266
|
+
lbl = sym_to_label(lbl) if Symbol === lbl
|
267
|
+
lbl.label
|
268
|
+
end
|
269
|
+
end
|
270
|
+
raise "Default case must be provided" unless default
|
271
|
+
if default && Symbol === default
|
272
|
+
default = sym_to_label(default)
|
273
|
+
end
|
274
|
+
method_visitor.visit_lookup_switch_insn(default.label, ints.to_java(:int), case_labels.to_java(ASM::Label))
|
275
|
+
-1
|
276
|
+
end
|
277
|
+
OpcodeInstructions['LOOKUPSWITCH'] = 'lookupswitch'
|
278
|
+
|
279
|
+
when "TABLESWITCH"
|
280
|
+
def tableswitch(min, max, default, cases = [])
|
281
|
+
cases = cases.to_ary
|
282
|
+
if cases.size > 0
|
283
|
+
# symbol labels, map them to actual
|
284
|
+
case_labels = cases.map do |lbl|
|
285
|
+
lbl = sym_to_label(lbl) if Symbol === lbl
|
286
|
+
lbl.label
|
287
|
+
end
|
288
|
+
end
|
289
|
+
raise "Default case must be provided" unless default
|
290
|
+
if default && Symbol === default
|
291
|
+
default = sym_to_label(default)
|
292
|
+
end
|
293
|
+
method_visitor.visit_table_switch_insn(min, max, default.label, case_labels.to_java(ASM::Label))
|
294
|
+
-1
|
295
|
+
end
|
296
|
+
OpcodeInstructions['TABLESWITCH'] = 'tableswitch'
|
297
|
+
|
298
|
+
when "F_FULL", "ACC_ENUM", "ACC_SYNTHETIC", "ACC_INTERFACE", "ACC_PUBLIC",
|
299
|
+
"ACC_PRIVATE", "ACC_PROTECTED", "ACC_DEPRECATED", "ACC_BRIDGE",
|
300
|
+
"ACC_VARARGS", "ACC_SUPER", "F_CHOP", "F_APPEND", "FLOAT", "F_SAME",
|
301
|
+
"T_LONG", "INTEGER", "T_BYTE", "ACC_STATIC", "ACC_SYNCHRONIZED",
|
302
|
+
"T_BOOLEAN", "ACC_ANNOTATION", "ACC_ABSTRACT", "LONG", "ACC_TRANSIENT",
|
303
|
+
"T_DOUBLE", "DOUBLE", "ACC_STRICT", "NULL", "T_FLOAT", "ACC_FINAL",
|
304
|
+
"F_SAME1", "ACC_NATIVE", "F_NEW", "T_CHAR", "T_INT", "ACC_VOLATILE",
|
305
|
+
"V1_6", "V1_5", "V1_4", "V1_3", "V1_2", "V1_1", "UNINITIALIZED_THIS",
|
306
|
+
"TOP", "T_SHORT"
|
307
|
+
# non-instructions
|
308
|
+
|
309
|
+
else
|
310
|
+
raise "Unknown opcode: " + const_name
|
311
|
+
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
def start
|
316
|
+
method_visitor.visit_code
|
317
|
+
end
|
318
|
+
|
319
|
+
def stop
|
320
|
+
method_visitor.visit_maxs(1,1)
|
321
|
+
method_visitor.visit_end
|
322
|
+
end
|
323
|
+
|
324
|
+
def trycatch(from, to, target, type)
|
325
|
+
from = sym_to_label(from) if Symbol === from
|
326
|
+
to = sym_to_label(to) if Symbol === to
|
327
|
+
target = sym_to_label(target) if Symbol === target
|
328
|
+
|
329
|
+
method_visitor.visit_try_catch_block(from.label, to.label, target.label, type ? path(type) : nil)
|
330
|
+
end
|
331
|
+
|
332
|
+
class SmartLabel
|
333
|
+
attr_reader :label
|
334
|
+
|
335
|
+
def initialize(method_visitor)
|
336
|
+
@method_visitor = method_visitor
|
337
|
+
@label = ASM::Label.new
|
338
|
+
end
|
339
|
+
|
340
|
+
def set!
|
341
|
+
@method_visitor.visit_label(@label)
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
def labels
|
346
|
+
@labels ||= {}
|
347
|
+
end
|
348
|
+
|
349
|
+
def sym_to_label(id)
|
350
|
+
lbl = labels[id] or raise "Unknown label '#{id}'"
|
351
|
+
end
|
352
|
+
|
353
|
+
def label(id = nil)
|
354
|
+
if id
|
355
|
+
label = labels[id]
|
356
|
+
if label
|
357
|
+
raise "Duplicate label '#{id}'"
|
358
|
+
end
|
359
|
+
label = labels[id] = SmartLabel.new(method_visitor)
|
360
|
+
label.set!
|
361
|
+
label
|
362
|
+
else
|
363
|
+
return SmartLabel.new(method_visitor)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
def aprintln
|
368
|
+
println
|
369
|
+
end
|
370
|
+
|
371
|
+
def println(type = JObject)
|
372
|
+
getstatic JSystem, "out", JPrintStream
|
373
|
+
swap
|
374
|
+
invokevirtual JPrintStream, "println", [JVoid::TYPE, type]
|
375
|
+
end
|
376
|
+
|
377
|
+
def swap2
|
378
|
+
dup2_x2
|
379
|
+
pop2
|
380
|
+
end
|
381
|
+
|
382
|
+
def line(num)
|
383
|
+
method_visitor.visit_line_number num, ASM::Label.new
|
384
|
+
end
|
385
|
+
|
386
|
+
def push_int(num)
|
387
|
+
if (num <= Java::java.lang.Byte::MAX_VALUE && num >= Java::java.lang.Byte::MIN_VALUE)
|
388
|
+
case num
|
389
|
+
when -1
|
390
|
+
iconst_m1
|
391
|
+
when 0
|
392
|
+
iconst_0
|
393
|
+
when 1
|
394
|
+
iconst_1
|
395
|
+
when 2
|
396
|
+
iconst_2
|
397
|
+
when 3
|
398
|
+
iconst_3
|
399
|
+
when 4
|
400
|
+
iconst_4
|
401
|
+
when 5
|
402
|
+
iconst_5
|
403
|
+
else
|
404
|
+
bipush(num)
|
405
|
+
end
|
406
|
+
elsif (num <= Java::java.lang.Short::MAX_VALUE && num >= Java::java.lang.Short::MIN_VALUE)
|
407
|
+
sipush(num)
|
408
|
+
elsif (num <= Java::java.lang.Integer::MAX_VALUE && num >= Java::java.lang.Integer::MIN_VALUE)
|
409
|
+
ldc_int(num)
|
410
|
+
else
|
411
|
+
ldc_long(num)
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'java'
|
2
|
+
|
3
|
+
module BiteScript
|
4
|
+
module JavaTypes
|
5
|
+
java_import java.lang.Object
|
6
|
+
java_import java.lang.Byte
|
7
|
+
java_import java.lang.Boolean
|
8
|
+
java_import java.lang.Short
|
9
|
+
java_import java.lang.Character
|
10
|
+
java_import java.lang.Integer
|
11
|
+
java_import java.lang.Long
|
12
|
+
java_import java.lang.Float
|
13
|
+
java_import java.lang.Double
|
14
|
+
java_import java.lang.Void
|
15
|
+
end
|
16
|
+
module Signature
|
17
|
+
def classname(path)
|
18
|
+
path.gsub('/', '.')
|
19
|
+
end
|
20
|
+
module_function :classname
|
21
|
+
|
22
|
+
def path(cls)
|
23
|
+
case cls
|
24
|
+
when Symbol
|
25
|
+
return cls
|
26
|
+
when Class, Module
|
27
|
+
cls_name = cls.java_class.to_s || cls.java_class.name
|
28
|
+
else
|
29
|
+
cls_name = cls.name
|
30
|
+
end
|
31
|
+
cls_name.gsub('.', '/')
|
32
|
+
end
|
33
|
+
module_function :path
|
34
|
+
|
35
|
+
def class_id(cls)
|
36
|
+
cls = cls.java_class if Class === cls
|
37
|
+
|
38
|
+
if !cls || cls == java.lang.Void || cls == Java::void
|
39
|
+
return "V"
|
40
|
+
end
|
41
|
+
|
42
|
+
if Module === cls
|
43
|
+
return "L#{path(cls)};"
|
44
|
+
end
|
45
|
+
|
46
|
+
if cls.array?
|
47
|
+
cls = cls.component_type
|
48
|
+
if cls.primitive?
|
49
|
+
case cls
|
50
|
+
when JavaTypes::Byte::TYPE
|
51
|
+
return "[B"
|
52
|
+
when JavaTypes::Boolean::TYPE
|
53
|
+
return "[Z"
|
54
|
+
when JavaTypes::Short::TYPE
|
55
|
+
return "[S"
|
56
|
+
when JavaTypes::Character::TYPE
|
57
|
+
return "[C"
|
58
|
+
when JavaTypes::Integer::TYPE
|
59
|
+
return "[I"
|
60
|
+
when JavaTypes::Long::TYPE
|
61
|
+
return "[J"
|
62
|
+
when JavaTypes::Float::TYPE
|
63
|
+
return "[F"
|
64
|
+
when JavaTypes::Double::TYPE
|
65
|
+
return "[D"
|
66
|
+
else
|
67
|
+
raise "Unknown type in compiler: " + cls.name
|
68
|
+
end
|
69
|
+
else
|
70
|
+
return "[#{class_id(cls)}"
|
71
|
+
end
|
72
|
+
else
|
73
|
+
if cls.primitive?
|
74
|
+
case cls
|
75
|
+
when JavaTypes::Byte::TYPE
|
76
|
+
return "B"
|
77
|
+
when JavaTypes::Boolean::TYPE
|
78
|
+
return "Z"
|
79
|
+
when JavaTypes::Short::TYPE
|
80
|
+
return "S"
|
81
|
+
when JavaTypes::Character::TYPE
|
82
|
+
return "C"
|
83
|
+
when JavaTypes::Integer::TYPE
|
84
|
+
return "I"
|
85
|
+
when JavaTypes::Long::TYPE
|
86
|
+
return "J"
|
87
|
+
when JavaTypes::Float::TYPE
|
88
|
+
return "F"
|
89
|
+
when JavaTypes::Double::TYPE
|
90
|
+
return "D"
|
91
|
+
when JavaTypes::Void::TYPE, java.lang.Void
|
92
|
+
return "V"
|
93
|
+
else
|
94
|
+
raise "Unknown type in compiler: " + cls.name
|
95
|
+
end
|
96
|
+
else
|
97
|
+
return "L#{path(cls)};"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
alias ci class_id
|
102
|
+
module_function :class_id, :ci
|
103
|
+
|
104
|
+
def signature(*sig_classes)
|
105
|
+
if sig_classes.size == 0
|
106
|
+
return "()V"
|
107
|
+
end
|
108
|
+
|
109
|
+
return_class = sig_classes.shift
|
110
|
+
sig_string = "("
|
111
|
+
sig_classes.each {|arg_class| sig_string << class_id(arg_class)}
|
112
|
+
sig_string << ")#{class_id(return_class)}"
|
113
|
+
end
|
114
|
+
alias sig signature
|
115
|
+
module_function :signature, :sig
|
116
|
+
end
|
117
|
+
end
|
data/lib/bitescript.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
$: << File.dirname(__FILE__)
|
2
|
+
require 'bitescript/asm'
|
3
|
+
require 'bitescript/signature'
|
4
|
+
require 'bitescript/bytecode'
|
5
|
+
require 'bitescript/builder'
|
6
|
+
|
7
|
+
module BiteScript
|
8
|
+
VERSION = '0.0.1'
|
9
|
+
|
10
|
+
include BiteScript::ASM
|
11
|
+
JAVA1_4 = Opcodes::V1_4
|
12
|
+
JAVA1_5 = Opcodes::V1_5
|
13
|
+
JAVA1_6 = Opcodes::V1_6
|
14
|
+
|
15
|
+
class << self
|
16
|
+
attr_accessor :bytecode_version
|
17
|
+
|
18
|
+
BiteScript.bytecode_version = JAVA1_4
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__),'..','lib')
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'bitescript'
|
5
|
+
|
6
|
+
class TestBitescript < Test::Unit::TestCase
|
7
|
+
def test_bytecode_version
|
8
|
+
assert_equal BiteScript::JAVA1_4, BiteScript.bytecode_version
|
9
|
+
[BiteScript::JAVA1_4, BiteScript::JAVA1_5, BiteScript::JAVA1_6].each do |ver|
|
10
|
+
BiteScript.bytecode_version = ver
|
11
|
+
assert_equal(ver, BiteScript.bytecode_version)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|