bitescript 0.0.9 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,458 @@
1
+ require 'bitescript/asm3/asm'
2
+ require 'bitescript/asm3/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.map(&:to_s).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
+ method_visitor.visit_ldc_insn(ASM::Type.get_type(ci(value)))
69
+ end
70
+ line = __LINE__; eval "
71
+ def #{const_down}(value)
72
+ size = 1
73
+
74
+ case value
75
+ when Symbol
76
+ method_visitor.visit_ldc_insn value.to_s
77
+ when Fixnum
78
+ size = push_int value
79
+ when Float
80
+ ldc_double(value)
81
+ when Module, ClassBuilder
82
+ ldc_class(value)
83
+ else
84
+ method_visitor.visit_ldc_insn(value)
85
+ end
86
+
87
+ size
88
+ end
89
+ ", b, __FILE__, line
90
+ OpcodeInstructions[const_name] = const_down
91
+
92
+ when "BIPUSH", "SIPUSH"
93
+ line = __LINE__; eval "
94
+ def #{const_down}(value)
95
+ method_visitor.visit_int_insn(Opcodes::#{const_name}, value)
96
+ 1
97
+ end
98
+ ", b, __FILE__, line
99
+ OpcodeInstructions[const_name] = const_down
100
+
101
+ when "INVOKESTATIC", "INVOKEVIRTUAL", "INVOKEINTERFACE", "INVOKESPECIAL"
102
+ # method instructions
103
+ line = __LINE__; eval "
104
+ def #{const_down}(type, name, call_sig)
105
+ method_visitor.visit_method_insn(Opcodes::#{const_name}, path(type), name.to_s, sig(*call_sig))
106
+
107
+ sig_stack_net(call_sig, #{const_name == 'INVOKESTATIC' ? 0 : 1})
108
+ end
109
+ ", b, __FILE__, line
110
+ OpcodeInstructions[const_name] = const_down
111
+
112
+ when "INVOKEDYNAMIC"
113
+ # invokedynamic instruction
114
+ line = __LINE__; eval "
115
+ def #{const_down}(name, call_sig, handle, *args)
116
+ method_visitor.visit_invoke_dynamic_insn(name.to_s, sig(*call_sig), handle, args.to_java)
117
+
118
+ sig_stack_net(call_sig, 0)
119
+ end
120
+ ", b, __FILE__, line
121
+ OpcodeInstructions[const_name] = const_down
122
+
123
+ when "RETURN"
124
+ # special case for void return, since return is a reserved word
125
+ def returnvoid()
126
+ method_visitor.visit_insn(Opcodes::RETURN)
127
+ 0
128
+ end
129
+ OpcodeInstructions['RETURN'] = 'returnvoid'
130
+
131
+ when "DUP", "SWAP", "POP", "POP2", "DUP_X1", "DUP_X2", "DUP2", "DUP2_X1", "DUP2_X2",
132
+ "NOP",
133
+ "ARRAYLENGTH",
134
+ "ARETURN", "ATHROW", "ACONST_NULL", "AALOAD", "AASTORE",
135
+ "BALOAD", "BASTORE",
136
+ "CALOAD", "CASTORE",
137
+ "SALOAD", "SASTORE",
138
+ "ICONST_M1", "ICONST_0", "ICONST_1", "ICONST_2", "ICONST_3", "ICONST_4", "ICONST_5", "IRETURN", "IALOAD",
139
+ "IADD", "ISUB", "IDIV", "IMUL", "INEG", "IAND", "IOR", "IXOR", "IASTORE",
140
+ "IUSHR", "ISHL", "ISHR", "I2L", "I2S", "I2F", "I2D", "I2B", "IREM", "I2C",
141
+ "LCONST_0", "LCONST_1", "LRETURN", "LALOAD", "LASTORE", "LCMP", "LSHL", "LSHR", "LREM", "LUSHR",
142
+ "LADD", "LINC", "LSUB", "LDIV", "LMUL", "LNEG", "LAND", "LOR", "LXOR", "L2I", "L2F", "L2D",
143
+ "FCONST_0", "FCONST_1", "FCONST_2", "FRETURN", "FALOAD", "F2D", "F2I", "FASTORE",
144
+ "FADD", "FSUB", "FDIV", "FMUL", "FNEG", "FREM", "FCMPG", "F2L", "FCMPL",
145
+ "DCONST_0", "DCONST_1", "DRETURN", "DALOAD", "DASTORE", "D2I", "D2F", "D2L",
146
+ "DADD", "DINC", "DSUB", "DDIV", "DMUL", "DNEG", "DCMPL", "DCMPG", "DREM",
147
+ "MONITORENTER", "MONITOREXIT"
148
+ # bare instructions
149
+ line = __LINE__; eval "
150
+ def #{const_down}
151
+ method_visitor.visit_insn(Opcodes::#{const_name})
152
+ #{OpcodeStackDeltas[const_name]}
153
+ end
154
+ ", b, __FILE__, line
155
+ OpcodeInstructions[const_name] = const_down
156
+
157
+ when "IINC"
158
+ def iinc(index, value)
159
+ method_visitor.visit_iinc_insn(index, value)
160
+ 0
161
+ end
162
+ OpcodeInstructions[const_name] = 'iinc'
163
+
164
+ when "NEW", "ANEWARRAY", "INSTANCEOF", "CHECKCAST"
165
+ # type instructions
166
+ line = __LINE__; eval "
167
+ def #{const_down}(type)
168
+ method_visitor.visit_type_insn(Opcodes::#{const_name}, tipath(type))
169
+ #{OpcodeStackDeltas[const_name]}
170
+ end
171
+ ", b, __FILE__, line
172
+ OpcodeInstructions[const_name] = const_down
173
+
174
+ when "NEWARRAY"
175
+ # newaray has its own peculiarities
176
+ NEWARRAY_TYPES = {
177
+ :boolean => Opcodes::T_BOOLEAN,
178
+ :byte => Opcodes::T_BYTE,
179
+ :short => Opcodes::T_SHORT,
180
+ :char => Opcodes::T_CHAR,
181
+ :int => Opcodes::T_INT,
182
+ :long => Opcodes::T_LONG,
183
+ :float => Opcodes::T_FLOAT,
184
+ :double => Opcodes::T_DOUBLE
185
+ }
186
+ NEWARRAY_TYPES.each do |type, op_type|
187
+ line = __LINE__; eval "
188
+ def new#{type}array()
189
+ method_visitor.visit_int_insn(Opcodes::#{const_name}, #{op_type})
190
+ #{OpcodeStackDeltas[const_name]}
191
+ end
192
+ ", b, __FILE__, line
193
+ end
194
+ def newarray(type)
195
+ t_type = NEWARRAY_TYPES[path(type).intern]
196
+ method_visitor.visit_int_insn(Opcodes::NEWARRAY, t_type)
197
+ end
198
+ OpcodeInstructions[const_name] = const_down
199
+
200
+ when "GETFIELD", "PUTFIELD", "GETSTATIC", "PUTSTATIC"
201
+ # field instructions
202
+ line = __LINE__; eval "
203
+ def #{const_down}(type, name, field_type)
204
+ method_visitor.visit_field_insn(Opcodes::#{const_name}, path(type), name.to_s, ci(field_type))
205
+
206
+ case field_type
207
+ when Java::boolean, Java::short, Java::char, Java::int, Java::float
208
+ delta = 1
209
+ when Java::long, Java::double
210
+ delta = 2
211
+ else
212
+ delta = 1
213
+ end
214
+
215
+ this_subtracted = #{const_name[3..7] == 'STATI' ? 0 : 1}
216
+
217
+ delta *= #{const_name[0..2] == 'PUT' ? -1 : 1}
218
+ delta -= this_subtracted
219
+ delta
220
+ end
221
+ ", b, __FILE__, line
222
+ OpcodeInstructions[const_name] = const_down
223
+
224
+ when "GOTO", "IFEQ", "IFNE", "IF_ACMPEQ", "IF_ACMPNE", "IF_ICMPEQ", "IF_ICMPNE", "IF_ICMPLT",
225
+ "IF_ICMPGT", "IF_ICMPLE", "IF_ICMPGE", "IFNULL", "IFNONNULL", "JSR",
226
+ "IFLE", "IFGE", "IFLT", "IFGT"
227
+ # jump instructions
228
+ line = __LINE__; eval "
229
+ def #{const_down}(target)
230
+ target = sym_to_label(target) if Symbol === target
231
+ method_visitor.visit_jump_insn(Opcodes::#{const_name}, target.label)
232
+ #{OpcodeStackDeltas[const_name]}
233
+ end
234
+ ", b, __FILE__, line
235
+ OpcodeInstructions[const_name] = const_down
236
+
237
+ when "MULTIANEWARRAY"
238
+ # multi-dim array
239
+ line = __LINE__; eval "
240
+ def #{const_down}(type, dims)
241
+ method_visitor.visit_multi_anew_array_insn(ci(type), dims)
242
+ -dims
243
+ end
244
+ ", b, __FILE__, line
245
+ OpcodeInstructions[const_name] = const_down
246
+
247
+ when "LOOKUPSWITCH"
248
+ def lookupswitch(default, ints, cases = [])
249
+ cases = cases.to_ary
250
+ if cases.size > 0
251
+ # symbol labels, map them to actual
252
+ case_labels = cases.map do |lbl|
253
+ lbl = sym_to_label(lbl) if Symbol === lbl
254
+ lbl.label
255
+ end
256
+ end
257
+ raise "Default case must be provided" unless default
258
+ if default && Symbol === default
259
+ default = sym_to_label(default)
260
+ end
261
+ method_visitor.visit_lookup_switch_insn(default.label, ints.to_java(:int), case_labels.to_java(ASM::Label))
262
+ -1
263
+ end
264
+ OpcodeInstructions['LOOKUPSWITCH'] = 'lookupswitch'
265
+
266
+ when "TABLESWITCH"
267
+ def tableswitch(min, max, default, cases = [])
268
+ cases = cases.to_ary
269
+ if cases.size > 0
270
+ # symbol labels, map them to actual
271
+ case_labels = cases.map do |lbl|
272
+ lbl = sym_to_label(lbl) if Symbol === lbl
273
+ lbl.label
274
+ end
275
+ end
276
+ raise "Default case must be provided" unless default
277
+ if default && Symbol === default
278
+ default = sym_to_label(default)
279
+ end
280
+ method_visitor.visit_table_switch_insn(min, max, default.label, case_labels.to_java(ASM::Label))
281
+ -1
282
+ end
283
+ OpcodeInstructions['TABLESWITCH'] = 'tableswitch'
284
+
285
+ when "MH_INVOKESPECIAL", "MH_INVOKESTATIC", "MH_PUTSTATIC", "MH_GETSTATIC", "MH_PUTFIELD",
286
+ "MH_GETFIELD", "MH_INVOKEVIRTUAL", "MH_INVOKEINTERFACE",
287
+ "MH_NEWINVOKESPECIAL"
288
+ line = __LINE__; eval "
289
+ def #{const_down}(cls, name, *call_sig)
290
+ MethodHandle.new(Opcodes::#{const_name}, path(cls), name, sig(*call_sig))
291
+ end
292
+ ", b, __FILE__, line
293
+ OpcodeInstructions[const_name] = const_down
294
+ when "F_FULL", "ACC_ENUM", "ACC_SYNTHETIC", "ACC_INTERFACE", "ACC_PUBLIC",
295
+ "ACC_PRIVATE", "ACC_PROTECTED", "ACC_DEPRECATED", "ACC_BRIDGE",
296
+ "ACC_VARARGS", "ACC_SUPER", "F_CHOP", "F_APPEND", "FLOAT", "F_SAME",
297
+ "T_LONG", "INTEGER", "T_BYTE", "ACC_STATIC", "ACC_SYNCHRONIZED",
298
+ "T_BOOLEAN", "ACC_ANNOTATION", "ACC_ABSTRACT", "LONG", "ACC_TRANSIENT",
299
+ "T_DOUBLE", "DOUBLE", "ACC_STRICT", "NULL", "T_FLOAT", "ACC_FINAL",
300
+ "F_SAME1", "ACC_NATIVE", "F_NEW", "T_CHAR", "T_INT", "ACC_VOLATILE",
301
+ "V1_6", "V1_5", "V1_4", "V1_3", "V1_2", "V1_1", "UNINITIALIZED_THIS",
302
+ "TOP", "T_SHORT", "INVOKEDYNAMIC_OWNER", "V1_7"
303
+
304
+ # non-instructions
305
+
306
+ else
307
+ raise "Unknown opcode: " + const_name
308
+
309
+ end
310
+ end
311
+
312
+ def start
313
+ method_visitor.visit_code
314
+ @start_label.set!
315
+ end
316
+
317
+ def stop
318
+ @end_label.set!
319
+ @locals.each do |name, locals|
320
+ local_debug_info(name, locals[-1], @end_label)
321
+ end
322
+ method_visitor.visit_maxs(1,1)
323
+ method_visitor.visit_end
324
+ end
325
+
326
+ def trycatch(from, to, target, type)
327
+ from = sym_to_label(from) if Symbol === from
328
+ to = sym_to_label(to) if Symbol === to
329
+ target = sym_to_label(target) if Symbol === target
330
+
331
+ method_visitor.visit_try_catch_block(from.label, to.label, target.label, type ? path(type) : nil)
332
+ end
333
+
334
+ class SmartLabel
335
+ attr_reader :label
336
+
337
+ def initialize(method_visitor)
338
+ @method_visitor = method_visitor
339
+ @label = ASM::Label.new
340
+ @set = false
341
+ end
342
+
343
+ def set!
344
+ raise "label set twice" if @set
345
+ @set = true
346
+ @method_visitor.visit_label(@label)
347
+ self
348
+ end
349
+
350
+ def set?
351
+ @set
352
+ end
353
+ end
354
+
355
+ def labels
356
+ @labels ||= {}
357
+ end
358
+
359
+ def sym_to_label(id)
360
+ lbl = labels[id]
361
+ unless lbl
362
+ lbl = labels[id] = label
363
+ end
364
+ lbl
365
+ end
366
+
367
+ def label(id = nil)
368
+ if id
369
+ lbl = sym_to_label(id)
370
+ lbl.set!
371
+ lbl
372
+ else
373
+ return SmartLabel.new(method_visitor)
374
+ end
375
+ end
376
+
377
+ def aprintln
378
+ println
379
+ end
380
+
381
+ def println(type = JObject)
382
+ getstatic JSystem, "out", JPrintStream
383
+ swap
384
+ invokevirtual JPrintStream, "println", [JVoid::TYPE, type]
385
+ end
386
+
387
+ def swap2
388
+ dup2_x2
389
+ pop2
390
+ end
391
+
392
+ def line(num)
393
+ if num && num != @line_num
394
+ method_visitor.visit_line_number(num, label.set!.label)
395
+ end
396
+ @line_num = num
397
+ end
398
+
399
+ def push_int(num)
400
+ if (num <= Java::java.lang.Byte::MAX_VALUE && num >= Java::java.lang.Byte::MIN_VALUE)
401
+ case num
402
+ when -1
403
+ iconst_m1
404
+ when 0
405
+ iconst_0
406
+ when 1
407
+ iconst_1
408
+ when 2
409
+ iconst_2
410
+ when 3
411
+ iconst_3
412
+ when 4
413
+ iconst_4
414
+ when 5
415
+ iconst_5
416
+ else
417
+ bipush(num)
418
+ end
419
+ elsif (num <= Java::java.lang.Short::MAX_VALUE && num >= Java::java.lang.Short::MIN_VALUE)
420
+ sipush(num)
421
+ elsif (num <= Java::java.lang.Integer::MAX_VALUE && num >= Java::java.lang.Integer::MIN_VALUE)
422
+ ldc_int(num)
423
+ else
424
+ ldc_long(num)
425
+ end
426
+ end
427
+
428
+ def sig_stack_net(call_sig, this_subtracted)
429
+ case call_sig[0]
430
+ when nil, Java::void, java.lang.Void
431
+ added = 0
432
+ when Java::boolean, Java::short, Java::char, Java::int, Java::float
433
+ added = 1
434
+ when Java::long, Java::double
435
+ added = 2
436
+ else
437
+ added = 1
438
+ end
439
+
440
+ args_subtracted = 0
441
+ [*call_sig][1..-1].each do |param|
442
+ case param
443
+ when nil, Java::void, java.lang.Void
444
+ args_subtracted += 0
445
+ when Java::boolean, Java::short, Java::char, Java::int, Java::float
446
+ args_subtracted += 1
447
+ when Java::long, Java::double
448
+ args_subtracted += 2
449
+ else
450
+ args_subtracted += 1
451
+ end
452
+ end
453
+
454
+ added - (this_subtracted + args_subtracted)
455
+ end
456
+ private :sig_stack_net
457
+ end
458
+ end