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,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