unageanu-javaclass 0.2.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/ChangeLog +5 -0
  2. data/javaclass.gemspec +18 -0
  3. data/lib/javaclass.rb +1 -0
  4. data/lib/javaclass/accessflag.rb +1 -1
  5. data/lib/javaclass/attribute.rb +293 -2
  6. data/lib/javaclass/base.rb +33 -4
  7. data/lib/javaclass/code.rb +78 -0
  8. data/lib/javaclass/constant.rb +2 -2
  9. data/lib/javaclass/member.rb +18 -18
  10. data/lib/javaclass/reader.rb +460 -6
  11. data/sample/caller.rb +95 -0
  12. data/sample/java_class/com/example/Constants.class +0 -0
  13. data/sample/java_class/com/example/Deprecated.class +0 -0
  14. data/sample/java_class/com/example/InnerClass.class +0 -0
  15. data/sample/java_class/com/example/InnerClassImpl.class +0 -0
  16. data/sample/java_class/com/example/Kitten.class +0 -0
  17. data/sample/java_class/com/example/ThrowsException.class +0 -0
  18. data/sample/java_class/com/example/annotation/Annotated.class +0 -0
  19. data/sample/java_class/com/example/annotation/AnnotatedMethod.class +0 -0
  20. data/sample/java_class/com/example/annotation/ClassAnnotationA.class +0 -0
  21. data/sample/java_class/com/example/annotation/HasDefaultValue.class +0 -0
  22. data/sample/java_class/com/example/annotation/RuntimeAnnotationA.class +0 -0
  23. data/sample/java_class/com/example/annotation/RuntimeAnnotationB.class +0 -0
  24. data/sample/java_class/com/example/annotation/RuntimeAnnotationC.class +0 -0
  25. data/sample/java_class/com/example/annotation/SourceAnnotationA.class +0 -0
  26. data/sample/java_class/com/example/caller/A.class +0 -0
  27. data/sample/java_class/com/example/caller/B.class +0 -0
  28. data/sample/java_class/com/example/caller/C.class +0 -0
  29. data/sample/java_class/com/example/caller/CallSample$A.class +0 -0
  30. data/sample/java_class/com/example/caller/CallSample$B.class +0 -0
  31. data/sample/java_class/com/example/caller/CallSample$C.class +0 -0
  32. data/sample/java_class/com/example/caller/CallSample.class +0 -0
  33. data/sample/java_class/com/example/caller/D.class +0 -0
  34. data/sample/java_class/com/example/code/Statement.class +0 -0
  35. data/sample/java_class/com/example/types/TypeVariables.class +0 -0
  36. data/sample/java_src/com/example/caller/A.java +19 -0
  37. data/sample/java_src/com/example/caller/B.java +7 -0
  38. data/sample/java_src/com/example/caller/C.java +7 -0
  39. data/sample/java_src/com/example/caller/CallSample.java +23 -0
  40. data/sample/java_src/com/example/caller/D.java +5 -0
  41. data/sample/java_src/com/example/code/Statement.java +41 -0
  42. data/sample/sample.rb +3 -3
  43. data/test/all_tests.rb +9 -0
  44. data/test/java_src/com/example/TestClass1.java +35 -0
  45. data/test/test_attribute.rb +150 -2
  46. data/test/test_class.rb +0 -1
  47. data/test/test_member.rb +4 -1
  48. metadata +44 -46
data/ChangeLog CHANGED
@@ -0,0 +1,5 @@
1
+ 2010-01-17 0.4.0
2
+ * add sample/caller.rb.
3
+
4
+ 2009-12-23 0.3.0
5
+ * support the analysis of stack map table attributes and code attribute.
data/javaclass.gemspec ADDED
@@ -0,0 +1,18 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "unageanu-javaclass"
3
+ spec.version = "0.4.0"
4
+ spec.summary = "javaclass is a java class file parser for ruby."
5
+ spec.author = "Masaya Yamauchi"
6
+ spec.email = "y-masaya@red.hot.co.jp"
7
+ spec.homepage = "http://github.com/unageanu/javaclass/tree/master"
8
+ spec.test_files = Dir.glob( "test/**/*" )
9
+ spec.files = [
10
+ "README",
11
+ "javaclass.gemspec",
12
+ "ChangeLog"
13
+ ]+Dir.glob( "lib/**/*" )+Dir.glob( "sample/**/*" )+spec.test_files
14
+ spec.has_rdoc = true
15
+ spec.rdoc_options << "--main" << "README"
16
+ spec.extra_rdoc_files = ["README"]
17
+ spec.add_dependency('rubyzip', '>= 0.9.1')
18
+ end
data/lib/javaclass.rb CHANGED
@@ -10,5 +10,6 @@ require "javaclass/attribute"
10
10
  require "javaclass/class"
11
11
  require "javaclass/constant"
12
12
  require "javaclass/member"
13
+ require "javaclass/code"
13
14
  require "javaclass/reader"
14
15
  require "javaclass/ziputils"
@@ -3,7 +3,7 @@ require "javaclass/base"
3
3
  module JavaClass
4
4
 
5
5
  #
6
- #===クラスのアクセスフラグの基底クラス。
6
+ #===アクセスフラグの基底クラス。
7
7
  #
8
8
  class AccessFlag
9
9
  include JavaClass::Base
@@ -803,10 +803,14 @@ module JavaClass
803
803
 
804
804
  body = to_byte( @max_stack, 2)
805
805
  body += to_byte( @max_locals, 2)
806
- body += to_byte( @codes.length, 4)
806
+
807
+ tmp = []
807
808
  @codes.each {|c|
808
- body += to_byte( c, 1 )
809
+ tmp += c.to_bytes
809
810
  }
811
+ body += to_byte( tmp.length, 4)
812
+ body += tmp
813
+
810
814
  body += to_byte( @exception_table.length, 2)
811
815
  @exception_table.each {|ex|
812
816
  body += ex.to_bytes()
@@ -1058,4 +1062,291 @@ module JavaClass
1058
1062
  attr :signature_index, true
1059
1063
  attr :index, true
1060
1064
  end
1065
+
1066
+ #
1067
+ #=== スタックマップテーブル属性
1068
+ #
1069
+ class StackMapTableAttribute < Attribute
1070
+ #
1071
+ #===コンストラクタ
1072
+ #
1073
+ #*java_class::属性の所有者であるJavaクラス
1074
+ #*name_index::名前を示すconstant_poolのインデックス
1075
+ #*stack_map_frame_entries::スタックマップフレームエントリ
1076
+ #
1077
+ def initialize( java_class, name_index, stack_map_frame_entries=[] )
1078
+ super( java_class, name_index)
1079
+ @stack_map_frame_entries = stack_map_frame_entries
1080
+ end
1081
+ def to_bytes
1082
+ bytes = super
1083
+ body = to_byte( @stack_map_frame_entries.length, 2 )
1084
+ @stack_map_frame_entries.each {|e|
1085
+ body += e.to_bytes()
1086
+ }
1087
+ bytes += to_byte( body.length, 4 )
1088
+ bytes += body
1089
+ end
1090
+ attr :stack_map_frame_entries, true
1091
+ end
1092
+
1093
+ #
1094
+ #=== スタックマップフレーム
1095
+ #
1096
+ class StackMapFrame
1097
+ include JavaClass::Base
1098
+ #
1099
+ #===コンストラクタ
1100
+ #
1101
+ #*frame_type::種別
1102
+ #
1103
+ def initialize(frame_type)
1104
+ @frame_type = frame_type
1105
+ end
1106
+ def to_bytes
1107
+ to_byte( frame_type, 1 )
1108
+ end
1109
+ #種別
1110
+ attr :frame_type, true
1111
+ end
1112
+
1113
+ #
1114
+ #=== same_frame
1115
+ #
1116
+ class SameFrame < StackMapFrame
1117
+ end
1118
+
1119
+ #
1120
+ #=== same_locals_1_stack_item_frame
1121
+ #
1122
+ class SameLocals1StackItemFrame < StackMapFrame
1123
+ #
1124
+ #===コンストラクタ
1125
+ #
1126
+ #*frame_type::種別
1127
+ #*verification_type_info:: 型情報
1128
+ #
1129
+ def initialize(frame_type, verification_type_info=[])
1130
+ super(frame_type)
1131
+ @verification_type_info = verification_type_info
1132
+ end
1133
+ def to_bytes
1134
+ bytes = super
1135
+ verification_type_info.each {|v|
1136
+ bytes += v.to_bytes
1137
+ }
1138
+ bytes
1139
+ end
1140
+ #型情報
1141
+ attr :verification_type_info, true
1142
+ end
1143
+
1144
+ #
1145
+ #=== same_locals_1_stack_item_frame_extended
1146
+ #
1147
+ class SameLocals1StackItemFrameExtended < StackMapFrame
1148
+ #
1149
+ #===コンストラクタ
1150
+ #
1151
+ #*frame_type::種別
1152
+ #*offset_delta:: オフセットデルタ
1153
+ #*verification_type_info:: 型情報
1154
+ #
1155
+ def initialize(frame_type, offset_delta, verification_type_info=[])
1156
+ super(frame_type)
1157
+ @offset_delta = offset_delta
1158
+ @verification_type_info = verification_type_info
1159
+ end
1160
+ def to_bytes
1161
+ bytes = super
1162
+ bytes += to_byte( offset_delta, 2 )
1163
+ verification_type_info.each {|v|
1164
+ bytes += v.to_bytes
1165
+ }
1166
+ bytes
1167
+ end
1168
+ #オフセットデルタ
1169
+ attr :offset_delta, true
1170
+ #型情報
1171
+ attr :verification_type_info, true
1172
+ end
1173
+
1174
+ #
1175
+ #=== chop_frame
1176
+ #
1177
+ class ChopFrame < StackMapFrame
1178
+ #
1179
+ #===コンストラクタ
1180
+ #
1181
+ #*frame_type::種別
1182
+ #*offset_delta:: オフセットデルタ
1183
+ #
1184
+ def initialize(frame_type, offset_delta)
1185
+ super(frame_type)
1186
+ @offset_delta = offset_delta
1187
+ end
1188
+ def to_bytes
1189
+ bytes = super
1190
+ bytes += to_byte( offset_delta, 2 )
1191
+ bytes
1192
+ end
1193
+ #オフセットデルタ
1194
+ attr :offset_delta, true
1195
+ end
1196
+
1197
+ #
1198
+ #=== same_frame_extended
1199
+ #
1200
+ class SameFrameExtended < StackMapFrame
1201
+
1202
+ #
1203
+ #===コンストラクタ
1204
+ #
1205
+ #*frame_type::種別
1206
+ #*offset_delta:: オフセットデルタ
1207
+ #
1208
+ def initialize(frame_type, offset_delta)
1209
+ super(frame_type)
1210
+ @offset_delta = offset_delta
1211
+ end
1212
+ def to_bytes
1213
+ bytes = super
1214
+ bytes += to_byte( offset_delta, 2 )
1215
+ bytes
1216
+ end
1217
+ #オフセットデルタ
1218
+ attr :offset_delta, true
1219
+ end
1220
+
1221
+ #
1222
+ #=== append_frame
1223
+ #
1224
+ class AppendFrame < StackMapFrame
1225
+ #
1226
+ #===コンストラクタ
1227
+ #
1228
+ #*frame_type::種別
1229
+ #*offset_delta:: オフセットデルタ
1230
+ #*verification_type_info:: 型情報
1231
+ #
1232
+ def initialize(frame_type, offset_delta, verification_type_info=[])
1233
+ super(frame_type)
1234
+ @offset_delta = offset_delta
1235
+ @verification_type_info = verification_type_info
1236
+ end
1237
+ def to_bytes
1238
+ bytes = super
1239
+ bytes += to_byte( offset_delta, 2 )
1240
+ verification_type_info.each {|v|
1241
+ bytes += v.to_bytes
1242
+ }
1243
+ bytes
1244
+ end
1245
+ #オフセットデルタ
1246
+ attr :offset_delta, true
1247
+ #型情報
1248
+ attr :verification_type_info, true
1249
+ end
1250
+
1251
+ #
1252
+ #=== full_frame
1253
+ #
1254
+ class FullFrame < StackMapFrame
1255
+ #
1256
+ #===コンストラクタ
1257
+ #
1258
+ #*frame_type::種別
1259
+ #*offset_delta:: オフセットデルタ
1260
+ #*verification_type_info:: 型情報
1261
+ #
1262
+ def initialize(frame_type, offset_delta,
1263
+ verification_type_info_local=[], verification_type_info_stack=[])
1264
+ super(frame_type)
1265
+ @offset_delta = offset_delta
1266
+ @verification_type_info_local = verification_type_info_local
1267
+ @verification_type_info_stack = verification_type_info_stack
1268
+ end
1269
+ def to_bytes
1270
+ bytes = super
1271
+ bytes += to_byte( offset_delta, 2 )
1272
+ [verification_type_info_local, verification_type_info_stack].each {|vi|
1273
+ bytes += to_byte( vi.length, 2 )
1274
+ vi.each {|v|
1275
+ bytes += v.to_bytes
1276
+ }
1277
+ }
1278
+ bytes
1279
+ end
1280
+ #オフセットデルタ
1281
+ attr :offset_delta, true
1282
+ #ローカルの型情報
1283
+ attr :verification_type_info_local, true
1284
+ #スタックの型情報
1285
+ attr :verification_type_info_stack, true
1286
+ end
1287
+
1288
+ #
1289
+ #=== 変数情報(Object_variable_info,Uninitialized_variable_info以外)
1290
+ #
1291
+ class VariableInfo
1292
+ include JavaClass::Base
1293
+ #
1294
+ #===コンストラクタ
1295
+ #
1296
+ #*tag::タグ
1297
+ #
1298
+ def initialize(tag)
1299
+ @tag = tag
1300
+ end
1301
+ def to_bytes
1302
+ to_byte( tag, 1 )
1303
+ end
1304
+ #タグ
1305
+ attr :tag, true
1306
+ end
1307
+
1308
+ #
1309
+ #=== 変数情報(Object_variable_info)
1310
+ #
1311
+ class ObjectVariableInfo < VariableInfo
1312
+ include JavaClass::Base
1313
+ #
1314
+ #===コンストラクタ
1315
+ #
1316
+ #*tag::タグ
1317
+ #*cpool_index:: cpool_index
1318
+ #
1319
+ def initialize(tag,cpool_index)
1320
+ super(tag)
1321
+ @cpool_index = cpool_index
1322
+ end
1323
+ def to_bytes
1324
+ super + to_byte( cpool_index, 2 )
1325
+ end
1326
+ #cpool_index
1327
+ attr :cpool_index, true
1328
+ end
1329
+
1330
+ #
1331
+ #=== 変数情報(Uninitialized_variable_info)
1332
+ #
1333
+ class UninitializedVariableInfo < VariableInfo
1334
+ include JavaClass::Base
1335
+ #
1336
+ #===コンストラクタ
1337
+ #
1338
+ #*tag::タグ
1339
+ #*offset:: オフセット
1340
+ #
1341
+ def initialize(tag,offset)
1342
+ super(tag)
1343
+ @offset = offset
1344
+ end
1345
+ def to_bytes
1346
+ super + to_byte( offset, 2 )
1347
+ end
1348
+ #オフセット
1349
+ attr :offset, true
1350
+ end
1351
+
1061
1352
  end
@@ -1,11 +1,40 @@
1
1
 
2
2
  module JavaClass
3
-
3
+
4
+ #ユーティリティ
5
+ module Utils
6
+
7
+ #配列で指定されたパスをすべて探索して配下のjar,classを解析し通知する
8
+ #*paths:: 探索するパスの配列。ファイルの場合、jarまたはclassであれば解析。
9
+ # ディレクトリであれば再帰的に探索。
10
+ def self.each_class( *paths, &block )
11
+ paths.each {|path|
12
+ if File.directory?( path )
13
+ JavaClass::Utils.each_class( *Dir.glob("#{path}/*"), &block )
14
+ elsif File.file?( path )
15
+ if path =~ /\.class$/
16
+ open( path, "r" ) {|f|
17
+ block.call(JavaClass.from( f ))
18
+ }
19
+ elsif path =~ /\.jar$/
20
+ JavaClass::ZipUtils.each_class( path, &block )
21
+ end
22
+ end
23
+ }
24
+ end
25
+ end
26
+
4
27
  module Base
5
28
 
6
29
  # 0..255の数字の配列に変換する
7
- def to_byte( value, length )
30
+ def to_byte( value, length, signed=false )
8
31
  return [0x00]*length if value == nil
32
+
33
+ if signed && value < 0
34
+ border = 0x80 << (8 * (length-1))
35
+ value = (value + border) | border
36
+ end
37
+
9
38
  tmp = []
10
39
  length.times {|i|
11
40
  tmp.unshift( value & 0xFF )
@@ -359,9 +388,9 @@ module JavaClass
359
388
  when 0xfe; str = "impdep1"
360
389
  when 0xff; str = "impdep2"
361
390
  else
362
- aise "unknown code. code=" << code.to_s
391
+ raise "unknown code. code=" << code.to_s
363
392
  end
364
- return str;
393
+ return str
365
394
  end
366
395
 
367
396
  end
@@ -0,0 +1,78 @@
1
+
2
+
3
+ module JavaClass
4
+
5
+ # コード
6
+ class Code
7
+ include JavaClass::Base
8
+ def initialize( java_class, index, opcode, operands=[], wide=false )
9
+ @java_class = java_class
10
+ @index = index
11
+ @opcode = opcode
12
+ @operands = operands
13
+ @wide = wide
14
+ end
15
+ def to_s
16
+ # opts = values = operands.inject(""){|str,i|
17
+ # if i.name == "index"
18
+ # c = @java_class.get_constant(i.value)
19
+ # str += c.kind_of?(MemberRefConstant) ? c.name_and_type.name : c.to_s
20
+ # end
21
+ # str
22
+ # }
23
+ values = operands.map{|i|i.to_s}
24
+ values.unshift( Converters.convert_code(opcode) )
25
+ values.unshift( "#{@index} :" )
26
+ # values.push( "#" + opts ) if !opts.empty?
27
+ return values.join(" ")
28
+ end
29
+ def to_bytes
30
+ bytes = []
31
+ bytes += to_byte( 0xC4, 1 ) if wide
32
+ bytes += to_byte( opcode, 1 )
33
+ operands.each {|o|
34
+ bytes += o.to_bytes
35
+ }
36
+ return bytes
37
+ end
38
+ # JavaClass
39
+ attr :java_class, true
40
+ # インデックス
41
+ attr :index, true
42
+ # opcode
43
+ attr :opcode, true
44
+ # operands
45
+ attr :operands, true
46
+ # wideかどうか
47
+ attr :wide, true
48
+ end
49
+
50
+ # オペランド
51
+ class Operand
52
+ include JavaClass::Base
53
+ def initialize( size, value, name="" )
54
+ @size = size
55
+ @value = value
56
+ @name=name
57
+ end
58
+ def to_s
59
+ value.to_s
60
+ end
61
+ def to_bytes
62
+ bytes = []
63
+ if size =~ /(u|s)(\d)/
64
+ bytes += to_byte( value, $2.to_i, $1=="s" )
65
+ else
66
+ raise "unknown size. size=#{size}"
67
+ end
68
+ return bytes
69
+ end
70
+ # データサイズ( s1, u1 ..etc.. )
71
+ attr :size, true
72
+ # 値
73
+ attr :value, true
74
+ # 表示名
75
+ attr :name, true
76
+ end
77
+
78
+ end