unageanu-javaclass 0.2.1 → 0.4.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/ChangeLog +5 -0
- data/javaclass.gemspec +18 -0
- data/lib/javaclass.rb +1 -0
- data/lib/javaclass/accessflag.rb +1 -1
- data/lib/javaclass/attribute.rb +293 -2
- data/lib/javaclass/base.rb +33 -4
- data/lib/javaclass/code.rb +78 -0
- data/lib/javaclass/constant.rb +2 -2
- data/lib/javaclass/member.rb +18 -18
- data/lib/javaclass/reader.rb +460 -6
- data/sample/caller.rb +95 -0
- data/sample/java_class/com/example/Constants.class +0 -0
- data/sample/java_class/com/example/Deprecated.class +0 -0
- data/sample/java_class/com/example/InnerClass.class +0 -0
- data/sample/java_class/com/example/InnerClassImpl.class +0 -0
- data/sample/java_class/com/example/Kitten.class +0 -0
- data/sample/java_class/com/example/ThrowsException.class +0 -0
- data/sample/java_class/com/example/annotation/Annotated.class +0 -0
- data/sample/java_class/com/example/annotation/AnnotatedMethod.class +0 -0
- data/sample/java_class/com/example/annotation/ClassAnnotationA.class +0 -0
- data/sample/java_class/com/example/annotation/HasDefaultValue.class +0 -0
- data/sample/java_class/com/example/annotation/RuntimeAnnotationA.class +0 -0
- data/sample/java_class/com/example/annotation/RuntimeAnnotationB.class +0 -0
- data/sample/java_class/com/example/annotation/RuntimeAnnotationC.class +0 -0
- data/sample/java_class/com/example/annotation/SourceAnnotationA.class +0 -0
- data/sample/java_class/com/example/caller/A.class +0 -0
- data/sample/java_class/com/example/caller/B.class +0 -0
- data/sample/java_class/com/example/caller/C.class +0 -0
- data/sample/java_class/com/example/caller/CallSample$A.class +0 -0
- data/sample/java_class/com/example/caller/CallSample$B.class +0 -0
- data/sample/java_class/com/example/caller/CallSample$C.class +0 -0
- data/sample/java_class/com/example/caller/CallSample.class +0 -0
- data/sample/java_class/com/example/caller/D.class +0 -0
- data/sample/java_class/com/example/code/Statement.class +0 -0
- data/sample/java_class/com/example/types/TypeVariables.class +0 -0
- data/sample/java_src/com/example/caller/A.java +19 -0
- data/sample/java_src/com/example/caller/B.java +7 -0
- data/sample/java_src/com/example/caller/C.java +7 -0
- data/sample/java_src/com/example/caller/CallSample.java +23 -0
- data/sample/java_src/com/example/caller/D.java +5 -0
- data/sample/java_src/com/example/code/Statement.java +41 -0
- data/sample/sample.rb +3 -3
- data/test/all_tests.rb +9 -0
- data/test/java_src/com/example/TestClass1.java +35 -0
- data/test/test_attribute.rb +150 -2
- data/test/test_class.rb +0 -1
- data/test/test_member.rb +4 -1
- metadata +44 -46
data/ChangeLog
CHANGED
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
data/lib/javaclass/accessflag.rb
CHANGED
data/lib/javaclass/attribute.rb
CHANGED
@@ -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
|
-
|
806
|
+
|
807
|
+
tmp = []
|
807
808
|
@codes.each {|c|
|
808
|
-
|
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
|
data/lib/javaclass/base.rb
CHANGED
@@ -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
|
-
|
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
|