javaclass 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +208 -23
- data/Readme.txt +56 -19
- data/examples/generate_class_lists.rb +61 -0
- data/history.txt +13 -19
- data/lib/javaclass/classfile/access_flags.rb +52 -0
- data/lib/javaclass/classfile/class_magic.rb +29 -0
- data/lib/javaclass/classfile/class_version.rb +67 -0
- data/lib/javaclass/classfile/constant_pool.rb +91 -0
- data/lib/javaclass/classfile/constants/base.rb +33 -0
- data/lib/javaclass/classfile/constants/double_reference.rb +99 -0
- data/lib/javaclass/classfile/constants/single_reference.rb +65 -0
- data/lib/javaclass/classfile/constants/value.rb +116 -0
- data/lib/javaclass/classfile/java_class_header.rb +102 -0
- data/lib/javaclass/classfile/references.rb +43 -0
- data/lib/javaclass/classlist/class_entry.rb +89 -0
- data/lib/javaclass/classlist/jar_searcher.rb +69 -0
- data/lib/javaclass/classlist/list.rb +126 -0
- data/lib/javaclass/classlist/package_entry.rb +87 -0
- data/lib/javaclass/classpath/classpaths.txt +2 -0
- data/lib/javaclass/classpath/composite_classpath.rb +99 -0
- data/lib/javaclass/classpath/folder_classpath.rb +83 -0
- data/lib/javaclass/classpath/jar_classpath.rb +100 -0
- data/lib/javaclass/classpath/java_home_classpath.rb +40 -0
- data/lib/javaclass/classpath/port_ClassPathEntry.java +202 -0
- data/lib/javaclass/classpath/port_ClassPathEntryFactory.java +311 -0
- data/lib/javaclass/classpath/port_DirectoryRepository.java +24 -0
- data/lib/javaclass/java_name.rb +90 -0
- data/lib/javaclass/metric/ccd.rb +68 -0
- data/lib/javaclass/metric/class_usage.rb +41 -0
- data/lib/javaclass/metric/metrics.txt +15 -0
- data/lib/javaclass.rb +44 -11
- data/test/data/ShortClientData$1-4000Wert.class +0 -0
- data/test/data/{AccessFlagsTestAbstract.class → access_flags/AccessFlagsTestAbstract.class} +0 -0
- data/test/data/{AccessFlagsTestAbstract.java → access_flags/AccessFlagsTestAbstract.java} +0 -0
- data/test/data/{AccessFlagsTestFinal.class → access_flags/AccessFlagsTestFinal.class} +0 -0
- data/test/data/{AccessFlagsTestFinal.java → access_flags/AccessFlagsTestFinal.java} +0 -0
- data/test/data/{AccessFlagsTestInterface.class → access_flags/AccessFlagsTestInterface.class} +0 -0
- data/test/data/{AccessFlagsTestInterface.java → access_flags/AccessFlagsTestInterface.java} +0 -0
- data/test/data/{AccessFlagsTestPackage.class → access_flags/AccessFlagsTestPackage.class} +0 -0
- data/test/data/{AccessFlagsTestPackage.java → access_flags/AccessFlagsTestPackage.java} +0 -0
- data/test/data/access_flags/AccessFlagsTestPublic$Inner.class +0 -0
- data/test/data/access_flags/AccessFlagsTestPublic$InnerInterface.class +0 -0
- data/test/data/access_flags/AccessFlagsTestPublic$StaticInner.class +0 -0
- data/test/data/access_flags/AccessFlagsTestPublic.class +0 -0
- data/test/data/access_flags/AccessFlagsTestPublic.java +11 -0
- data/test/data/access_flags/AccessFlagsTestPublic_javap.txt +29 -0
- data/test/data/{makeAccessFlagsTest.bat → access_flags/make.bat} +0 -0
- data/test/data/{ClassVersionTest.java → class_version/ClassVersionTest.java} +0 -0
- data/test/data/{ClassVersionTest10.class → class_version/ClassVersionTest10.class} +0 -0
- data/test/data/{ClassVersionTest11.class → class_version/ClassVersionTest11.class} +0 -0
- data/test/data/{ClassVersionTest12.class → class_version/ClassVersionTest12.class} +0 -0
- data/test/data/{ClassVersionTest13.class → class_version/ClassVersionTest13.class} +0 -0
- data/test/data/{ClassVersionTest14.class → class_version/ClassVersionTest14.class} +0 -0
- data/test/data/{ClassVersionTest15.class → class_version/ClassVersionTest15.class} +0 -0
- data/test/data/{ClassVersionTest16.class → class_version/ClassVersionTest16.class} +0 -0
- data/test/data/{makeClassVersionTest.bat → class_version/make.bat} +0 -0
- data/test/data/{ConstantPoolTest.class → constant_pool/ConstantPoolTest.class} +0 -0
- data/test/data/{ConstantPoolTest.java → constant_pool/ConstantPoolTest.java} +0 -0
- data/test/data/{makeConstantPoolTest.bat → constant_pool/make.bat} +0 -0
- data/test/data/folder_classpath/JarClasspathTestFolder/ClassVersionTest10.class +0 -0
- data/test/data/folder_classpath/JarClasspathTestFolder/package/ClassVersionTest11.class +0 -0
- data/test/data/jar_classpath/JarClasspathTest.jar +0 -0
- data/test/data/jar_classpath/JarClasspathTest.zip +0 -0
- data/test/data/jar_classpath/JarClasspathTestManifest.jar +0 -0
- data/test/data/jar_classpath/JarClasspathTestMultiManifest.jar +0 -0
- data/test/data/jar_classpath/make.bat +24 -0
- data/test/data/jar_searcher/JarClassListTest.jar +0 -0
- data/test/data/jar_searcher/PublicClass.java +12 -0
- data/test/data/jar_searcher/PublicInterface.java +5 -0
- data/test/data/jar_searcher/make.bat +13 -0
- data/test/data/java_home_classpath/jdk/jre/lib/rt.jar +0 -0
- data/test/data/java_home_classpath/jre/lib/rt.jar +0 -0
- data/test/data/java_home_classpath/jre-ext/lib/ext/ext.jar +0 -0
- data/test/data/java_home_classpath/jre-ext/lib/rt.jar +0 -0
- data/test/data/java_home_classpath/make.bat +13 -0
- data/test/data/references/ReferencesTest.class +0 -0
- data/test/data/references/ReferencesTest.java +12 -0
- data/test/data/references/make.bat +5 -0
- data/test/setup.rb +6 -5
- data/test/test_access_flags.rb +59 -44
- data/test/test_base.rb +20 -15
- data/test/test_class_entry.rb +198 -0
- data/test/test_class_version.rb +48 -46
- data/test/test_composite_classpath.rb +68 -0
- data/test/test_constant_pool.rb +49 -41
- data/test/test_folder_classpath.rb +45 -0
- data/test/test_jar_classpath.rb +57 -0
- data/test/test_jar_searcher.rb +93 -0
- data/test/test_java_class_header.rb +34 -22
- data/test/test_java_home_classpath.rb +50 -0
- data/test/test_java_name.rb +110 -0
- data/test/test_javaclass.rb +22 -0
- data/test/test_list.rb +123 -0
- data/test/test_package_entry.rb +91 -0
- data/test/test_references.rb +38 -20
- data/test/test_string_ux.rb +99 -2
- data/test/ts_all_tests.rb +42 -21
- metadata +171 -50
- data/lib/javaclass/access_flags.rb +0 -44
- data/lib/javaclass/class_magic.rb +0 -27
- data/lib/javaclass/class_version.rb +0 -65
- data/lib/javaclass/constant_pool.rb +0 -85
- data/lib/javaclass/constants/base.rb +0 -31
- data/lib/javaclass/constants/double_reference.rb +0 -81
- data/lib/javaclass/constants/single_reference.rb +0 -58
- data/lib/javaclass/constants/value.rb +0 -114
- data/lib/javaclass/java_class_header.rb +0 -88
- data/lib/javaclass/references.rb +0 -31
- data/test/data/AccessFlagsTestPublic$InnerClass.class +0 -0
- data/test/data/AccessFlagsTestPublic.class +0 -0
- data/test/data/AccessFlagsTestPublic.java +0 -7
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'javaclass/string_ux'
|
2
|
+
require 'javaclass/classfile/constants/value'
|
3
|
+
require 'javaclass/classfile/constants/single_reference'
|
4
|
+
require 'javaclass/classfile/constants/double_reference'
|
5
|
+
|
6
|
+
module JavaClass
|
7
|
+
module ClassFile
|
8
|
+
|
9
|
+
# Container of the constant pool's constants.
|
10
|
+
# Author:: Peter Kofler
|
11
|
+
class ConstantPool
|
12
|
+
|
13
|
+
# Types of constants by their +tag+.
|
14
|
+
CONSTANT_TYPE_TAGS = {
|
15
|
+
CLASS_TAG = 7 => Constants::ConstantClass,
|
16
|
+
FIELD_TAG = 9 => Constants::ConstantField,
|
17
|
+
METHOD_TAG = 10 => Constants::ConstantMethod,
|
18
|
+
INTERFACE_METHOD_TAG = 11 => Constants::ConstantInterfaceMethod,
|
19
|
+
STRING_TAG = 8 => Constants::ConstantString,
|
20
|
+
INT_TAG = 3 => Constants::ConstantInt,
|
21
|
+
FLOAT_TAG = 4 => Constants::ConstantFloat,
|
22
|
+
LONG_TAG = 5 => Constants::ConstantLong,
|
23
|
+
DOUBLE_TAG = 6 => Constants::ConstantDouble,
|
24
|
+
NAME_AND_TYPE_TAG = 12 => Constants::ConstantNameAndType,
|
25
|
+
ASCIZ_TAG = 1 => Constants::ConstantAsciz,
|
26
|
+
}
|
27
|
+
|
28
|
+
# Size of the whole constant pool in bytes.
|
29
|
+
attr_reader :size
|
30
|
+
|
31
|
+
# Parse the constant pool from the bytes _data_ beginning at position _start_ (which is usually 8).
|
32
|
+
def initialize(data, start=8)
|
33
|
+
@pool = {} # cnt (fixnum) => constant
|
34
|
+
|
35
|
+
# parsing
|
36
|
+
@item_count = data.u2(start)
|
37
|
+
pos = start + 2
|
38
|
+
cnt = 1
|
39
|
+
while cnt <= @item_count-1
|
40
|
+
|
41
|
+
type = CONSTANT_TYPE_TAGS[data.u1(pos)]
|
42
|
+
unless type
|
43
|
+
#puts dump.join("\n")
|
44
|
+
raise "const ##{cnt} = unknown constant pool tag #{data[pos]} at pos #{pos} in class"
|
45
|
+
end
|
46
|
+
|
47
|
+
constant = type.new(@pool, data, pos)
|
48
|
+
@pool[cnt] = constant
|
49
|
+
pos += constant.size
|
50
|
+
cnt += constant.slots
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
@size = pos - start
|
55
|
+
end
|
56
|
+
|
57
|
+
# Return the number of pool items. This number might be larger than +items+ available,
|
58
|
+
# because +long+ and +double+ constants take two slots.
|
59
|
+
def item_count
|
60
|
+
@item_count-1
|
61
|
+
end
|
62
|
+
|
63
|
+
# Return the _index_'th pool item. _index_ is the real index in the pool which may skip numbers.
|
64
|
+
def[](index)
|
65
|
+
@pool[index]
|
66
|
+
end
|
67
|
+
|
68
|
+
# Return an array of the ordered list of constants.
|
69
|
+
def items
|
70
|
+
@pool.keys.sort.collect { |k| self[k] }
|
71
|
+
end
|
72
|
+
|
73
|
+
# Return an array of all constants of the given _tags_ types.
|
74
|
+
def find(*tags)
|
75
|
+
items.find_all { |item| tags.include? item.tag }
|
76
|
+
end
|
77
|
+
|
78
|
+
# Return all string constants.
|
79
|
+
def strings
|
80
|
+
find(STRING_TAG)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Return a debug output of the whole pool.
|
84
|
+
def dump
|
85
|
+
[" Constant pool:"] + @pool.keys.sort.collect { |k| "const ##{k} = #{self[k].dump}"}
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'javaclass/string_ux'
|
2
|
+
|
3
|
+
module JavaClass
|
4
|
+
module ClassFile
|
5
|
+
module Constants # :nodoc:
|
6
|
+
|
7
|
+
# Superclass of all constant values in the constant pool. Every constant has a +name+, a +tag+ and a +size+ in bytes.
|
8
|
+
# Author:: Peter Kofler
|
9
|
+
class Base # ZenTest FULL to find method name
|
10
|
+
|
11
|
+
attr_reader :name
|
12
|
+
attr_reader :tag
|
13
|
+
attr_reader :size
|
14
|
+
attr_reader :slots
|
15
|
+
|
16
|
+
# Set default constants.
|
17
|
+
def initialize(name=nil)
|
18
|
+
@name = self.class.to_s[/::[^:]+$/][10..-1] # skip modules (::) and "Constant"
|
19
|
+
@name = name if name
|
20
|
+
@size = 3
|
21
|
+
@slots = 1
|
22
|
+
end
|
23
|
+
|
24
|
+
# Return part of debug output.
|
25
|
+
def dump
|
26
|
+
"#{@name}\t" # #{@tag}
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'javaclass/classfile/constants/single_reference'
|
2
|
+
require 'javaclass/java_name'
|
3
|
+
|
4
|
+
module JavaClass
|
5
|
+
module ClassFile
|
6
|
+
module Constants
|
7
|
+
|
8
|
+
# Superclass of double reference constants like +ConstantField+ (+FieldRef+) in the constant pool.
|
9
|
+
# Author:: Peter Kofler
|
10
|
+
class DoubleReference < SingleReference
|
11
|
+
|
12
|
+
attr_reader :second_index
|
13
|
+
|
14
|
+
# Define a double reference into _pool_ from _data_ beginning at _start_
|
15
|
+
def initialize(pool, data, start, name=nil)
|
16
|
+
super(pool, data, start, name)
|
17
|
+
@size = 5
|
18
|
+
|
19
|
+
@second_index = data.u2(start+3)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Return the second value, which is the referenced value from the pool.
|
23
|
+
def second_value
|
24
|
+
get(@second_index)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Return the value, which are both referenced values from the pool.
|
28
|
+
def to_s
|
29
|
+
"#{super}.#{second_value}"
|
30
|
+
end
|
31
|
+
|
32
|
+
# Return part of debug output.
|
33
|
+
def dump
|
34
|
+
"#{@name}\t##{@first_index}.##{@second_index};\t// #{to_s}"
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
class ConstantField < DoubleReference # ZenTest SKIP
|
40
|
+
alias class_index first_index
|
41
|
+
alias name_and_type_index second_index
|
42
|
+
def initialize(pool, data, start)
|
43
|
+
super(pool, data, start)
|
44
|
+
end
|
45
|
+
def first_value
|
46
|
+
# is a classname
|
47
|
+
super.to_javaname
|
48
|
+
end
|
49
|
+
alias class_name first_value
|
50
|
+
alias signature second_value
|
51
|
+
end
|
52
|
+
|
53
|
+
class ConstantMethod < DoubleReference # ZenTest SKIP
|
54
|
+
alias class_index first_index
|
55
|
+
alias name_and_type_index second_index
|
56
|
+
def initialize(pool, data, start)
|
57
|
+
super(pool, data, start)
|
58
|
+
end
|
59
|
+
def first_value
|
60
|
+
# is a classname
|
61
|
+
super.to_javaname
|
62
|
+
end
|
63
|
+
alias class_name first_value
|
64
|
+
alias signature second_value
|
65
|
+
end
|
66
|
+
|
67
|
+
class ConstantInterfaceMethod < DoubleReference # ZenTest SKIP
|
68
|
+
alias class_index first_index
|
69
|
+
alias name_and_type_index second_index
|
70
|
+
def initialize(pool, data, start)
|
71
|
+
super(pool, data, start)
|
72
|
+
end
|
73
|
+
def first_value
|
74
|
+
# is a classname
|
75
|
+
super.to_javaname
|
76
|
+
end
|
77
|
+
alias class_name first_value
|
78
|
+
alias signature second_value
|
79
|
+
end
|
80
|
+
|
81
|
+
class ConstantNameAndType < DoubleReference # ZenTest SKIP
|
82
|
+
alias name_index first_index
|
83
|
+
alias descriptor_index second_index
|
84
|
+
def initialize(pool, data, start)
|
85
|
+
super(pool, data, start)
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_s
|
89
|
+
"#{get(name_index)}:#{get(descriptor_index)}"
|
90
|
+
end
|
91
|
+
|
92
|
+
def dump
|
93
|
+
"#{@name}\t##{name_index}:##{descriptor_index};// #{to_s}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'javaclass/classfile/constants/base'
|
2
|
+
require 'javaclass/java_name'
|
3
|
+
|
4
|
+
module JavaClass
|
5
|
+
module ClassFile
|
6
|
+
module Constants
|
7
|
+
|
8
|
+
# Superclass of single reference constants like +ConstantClass+ (+Class+) in the constant pool.
|
9
|
+
# Author:: Peter Kofler
|
10
|
+
class SingleReference < Base
|
11
|
+
|
12
|
+
attr_reader :first_index
|
13
|
+
|
14
|
+
# Define a single reference into _pool_ from _data_ beginning at _start_
|
15
|
+
def initialize(pool, data, start, name=nil)
|
16
|
+
super(name)
|
17
|
+
@tag = data.u1(start)
|
18
|
+
|
19
|
+
@enclosing_pool = pool
|
20
|
+
@first_index = data.u2(start+1)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return the value, which is the referenced value from the pool.
|
24
|
+
def first_value
|
25
|
+
get(@first_index)
|
26
|
+
end
|
27
|
+
alias to_s first_value
|
28
|
+
|
29
|
+
# Return part of debug output.
|
30
|
+
def dump
|
31
|
+
super + "##{@first_index};\t// #{to_s}"
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
# Get a reference _ref_ from the +enclosing_pool+
|
37
|
+
def get(ref)
|
38
|
+
@enclosing_pool[ref].to_s
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
class ConstantClass < SingleReference # ZenTest SKIP
|
44
|
+
alias name_index first_index
|
45
|
+
def initialize(pool, data, start)
|
46
|
+
super(pool, data, start, "class")
|
47
|
+
end
|
48
|
+
def first_value
|
49
|
+
# is a classname
|
50
|
+
super.to_javaname
|
51
|
+
end
|
52
|
+
alias class_name first_value
|
53
|
+
end
|
54
|
+
|
55
|
+
class ConstantString < SingleReference # ZenTest SKIP
|
56
|
+
alias string_index first_index
|
57
|
+
def initialize(pool, data, start)
|
58
|
+
super(pool, data, start)
|
59
|
+
end
|
60
|
+
alias string_value first_value
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'javaclass/classfile/constants/base'
|
2
|
+
|
3
|
+
module JavaClass
|
4
|
+
module ClassFile
|
5
|
+
module Constants
|
6
|
+
|
7
|
+
# Superclass of value constants like +ConstantInt+ (+Integer+) in the constant pool.
|
8
|
+
# Author:: Peter Kofler
|
9
|
+
class Value < Base
|
10
|
+
|
11
|
+
attr_reader :value
|
12
|
+
|
13
|
+
# Create a constant value with an optional downcase _name_
|
14
|
+
def initialize(name=self.class.to_s[/::[^:]+$/][10..-1].downcase)
|
15
|
+
super(name)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Return the value as string.
|
19
|
+
def to_s
|
20
|
+
@value.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return part of debug output.
|
24
|
+
def dump
|
25
|
+
super + "#{@value}"
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
# Define a +value+ from _data_ beginning at position _start_ with the _size_ in bytes and _slots_ (1 or 2).
|
31
|
+
def get_value(data, start, size, slots=1)
|
32
|
+
@tag = data.u1(start)
|
33
|
+
@size = size
|
34
|
+
@slots = slots
|
35
|
+
|
36
|
+
data[start+1..start+size-1]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Dummy method to "fix" unused warning of param _pool_ in Eclipse.
|
40
|
+
def silence_unused_warning(pool)
|
41
|
+
raise "pool is nil" unless pool
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
class ConstantInt < Value # ZenTest SKIP
|
47
|
+
def initialize(pool, data, start)
|
48
|
+
super()
|
49
|
+
silence_unused_warning(pool)
|
50
|
+
@value = get_value(data, start, 5).u4
|
51
|
+
end
|
52
|
+
|
53
|
+
def dump
|
54
|
+
super + ';'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class ConstantFloat < Value # ZenTest SKIP
|
59
|
+
def initialize(pool, data, start)
|
60
|
+
super()
|
61
|
+
silence_unused_warning(pool)
|
62
|
+
@value = get_value(data, start, 5).single
|
63
|
+
end
|
64
|
+
def to_s
|
65
|
+
super.upcase # sprintf('%E',@value)
|
66
|
+
end
|
67
|
+
def dump
|
68
|
+
super + 'f;'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class ConstantLong < Value # ZenTest SKIP
|
73
|
+
def initialize(pool, data, start)
|
74
|
+
super()
|
75
|
+
silence_unused_warning(pool)
|
76
|
+
@value = get_value(data, start, 9, 2).u8
|
77
|
+
end
|
78
|
+
def dump
|
79
|
+
super + 'l;'
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class ConstantDouble < Value # ZenTest SKIP
|
84
|
+
def initialize(pool, data, start)
|
85
|
+
super()
|
86
|
+
silence_unused_warning(pool)
|
87
|
+
@value = get_value(data, start, 9, 2).double
|
88
|
+
end
|
89
|
+
def to_s
|
90
|
+
@value.to_s.upcase # sprintf('%E',@value)
|
91
|
+
end
|
92
|
+
def dump
|
93
|
+
super + 'd;'
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class ConstantAsciz < Value # ZenTest SKIP
|
98
|
+
alias string value
|
99
|
+
def initialize(pool, data, start)
|
100
|
+
super('Asciz')
|
101
|
+
silence_unused_warning(pool)
|
102
|
+
@tag = data.u1(start)
|
103
|
+
|
104
|
+
@length = data.u2(start+1)
|
105
|
+
@size = 3 + @length
|
106
|
+
@value = data[start+3..start+3+@length-1]
|
107
|
+
end
|
108
|
+
|
109
|
+
def dump
|
110
|
+
super + ';'
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'javaclass/string_ux'
|
2
|
+
require 'javaclass/classfile/class_magic'
|
3
|
+
require 'javaclass/classfile/class_version'
|
4
|
+
require 'javaclass/classfile/constant_pool'
|
5
|
+
require 'javaclass/classfile/references'
|
6
|
+
require 'javaclass/classfile/access_flags'
|
7
|
+
require 'javaclass/java_name'
|
8
|
+
|
9
|
+
module JavaClass
|
10
|
+
module ClassFile # :nodoc:
|
11
|
+
|
12
|
+
# Parse and disassemble Java class files, similar to the +javap+ command.
|
13
|
+
# Provides all information of a Java class file. This is just a container for all kind of
|
14
|
+
# specialised elements. The constuctor parses and creates all contained elements.
|
15
|
+
# See:: http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html
|
16
|
+
# See:: {en.wikipedia.org/wiki/Class}[http://en.wikipedia.org/wiki/Class_(file_format)]
|
17
|
+
# Author:: Peter Kofler
|
18
|
+
class JavaClassHeader
|
19
|
+
|
20
|
+
attr_reader :magic
|
21
|
+
attr_reader :version
|
22
|
+
attr_reader :constant_pool
|
23
|
+
attr_reader :access_flags
|
24
|
+
attr_reader :references
|
25
|
+
|
26
|
+
# Create a header with the binary _data_ from the class file.
|
27
|
+
def initialize(data)
|
28
|
+
|
29
|
+
# ClassFile {
|
30
|
+
# u4 magic; - ok
|
31
|
+
# u2 minor_version; - ok
|
32
|
+
# u2 major_version; - ok
|
33
|
+
# u2 constant_pool_count; - ok
|
34
|
+
# cp_info constant_pool[constant_pool_count-1]; - ok
|
35
|
+
# u2 access_flags; - ok
|
36
|
+
# u2 this_class; - ok
|
37
|
+
# u2 super_class; - ok
|
38
|
+
# TODO implement function for fields and methods (JVM spec)
|
39
|
+
# u2 interfaces_count;
|
40
|
+
# u2 interfaces[interfaces_count];
|
41
|
+
# u2 fields_count;
|
42
|
+
# field_info fields[fields_count];
|
43
|
+
# u2 methods_count;
|
44
|
+
# method_info methods[methods_count];
|
45
|
+
# u2 attributes_count;
|
46
|
+
# attribute_info attributes[attributes_count];
|
47
|
+
# }
|
48
|
+
# TODO Java 1.0 - "private protected" fields.
|
49
|
+
|
50
|
+
@magic = ClassMagic.new(data)
|
51
|
+
@version = ClassVersion.new(data)
|
52
|
+
|
53
|
+
@constant_pool = ConstantPool.new(data)
|
54
|
+
pos = 8 + @constant_pool.size
|
55
|
+
|
56
|
+
@access_flags = AccessFlags.new(data, pos)
|
57
|
+
pos += 2
|
58
|
+
|
59
|
+
idx = data.u2(pos)
|
60
|
+
pos += 2
|
61
|
+
@this_class_idx = idx
|
62
|
+
|
63
|
+
@references = References.new(@constant_pool, @this_class_idx)
|
64
|
+
|
65
|
+
idx = data.u2(pos)
|
66
|
+
pos += 2
|
67
|
+
@super_class_idx = idx
|
68
|
+
end
|
69
|
+
|
70
|
+
# Return the name of this class.
|
71
|
+
def this_class
|
72
|
+
@constant_pool[@this_class_idx].to_s.to_javaname
|
73
|
+
end
|
74
|
+
|
75
|
+
# Return the name of the superclass of this class or +nil+.
|
76
|
+
def super_class
|
77
|
+
if @super_class_idx > 0
|
78
|
+
@constant_pool[@super_class_idx].to_s.to_javaname
|
79
|
+
else
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Return a debug output of this class that looks similar to +javap+ output.
|
85
|
+
def dump
|
86
|
+
d = []
|
87
|
+
mod = @access_flags.public? ? 'public ' : ''
|
88
|
+
ext = super_class ? "extends #{super_class.to_classname}" : ''
|
89
|
+
d << "#{mod}class #{this_class.to_classname} #{ext}"
|
90
|
+
# d << " SourceFile: \"#{read from LineNumberTable?}\""
|
91
|
+
d += @version.dump
|
92
|
+
d += @constant_pool.dump
|
93
|
+
d << ''
|
94
|
+
d << '{'
|
95
|
+
d << '}'
|
96
|
+
d
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'javaclass/classfile/constant_pool'
|
2
|
+
|
3
|
+
module JavaClass
|
4
|
+
module ClassFile
|
5
|
+
|
6
|
+
# Container class for list of all classes, methods and fields referenced by this class.
|
7
|
+
# This information is derived from the constant pool, no analysis.
|
8
|
+
# Author:: Peter Kofler
|
9
|
+
class References
|
10
|
+
|
11
|
+
# Create a references container with the constant _pool_ and skip references to index _classidx_ which is the host class itself.
|
12
|
+
def initialize(pool, classidx)
|
13
|
+
@constant_pool = pool
|
14
|
+
@class_idx = classidx
|
15
|
+
end
|
16
|
+
|
17
|
+
# Return the constants referring to fields (Constants::ConstantField).
|
18
|
+
# If _includeown_ is +true+ then fields of this class are returned also.
|
19
|
+
def referenced_fields(includeown=false)
|
20
|
+
@constant_pool.find(ConstantPool::FIELD_TAG).find_all do |field|
|
21
|
+
includeown || field.class_index != @class_idx
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Return the constants referring to methods (Constants::ConstantMethod) in classes or interfaces.
|
26
|
+
# If _includeown_ is +true+ then methods of this class are returned also.
|
27
|
+
def referenced_methods(includeown=false)
|
28
|
+
@constant_pool.find(ConstantPool::METHOD_TAG, ConstantPool::INTERFACE_METHOD_TAG).find_all do |method|
|
29
|
+
includeown || method.class_index != @class_idx
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Return the list of all constants containing class names of all used classes.
|
34
|
+
def used_classes
|
35
|
+
@constant_pool.find(ConstantPool::CLASS_TAG).find_all do |cl|
|
36
|
+
cl.class_name != @constant_pool[@class_idx].to_s
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'javaclass/java_name'
|
2
|
+
|
3
|
+
module JavaClass
|
4
|
+
module ClassList # :nodoc:
|
5
|
+
|
6
|
+
# An entry in the list. A ClassEntry belongs to a PackageEntry and has a list ov versions it exists in.
|
7
|
+
# Author:: Peter Kofler
|
8
|
+
class ClassEntry
|
9
|
+
|
10
|
+
# Return the short (simple) name of this class.
|
11
|
+
attr_reader :name
|
12
|
+
attr_reader :full_name
|
13
|
+
# Return the list of versions this class exists.
|
14
|
+
attr_reader :version
|
15
|
+
|
16
|
+
# Create a new entry. _parent_ must provide a +version+ field to compare against. _vers_ is the
|
17
|
+
# base version of this class.
|
18
|
+
def initialize(parent, full_name, is_public, vers)
|
19
|
+
@parent = parent
|
20
|
+
@full_name = full_name.to_javaname.to_classname
|
21
|
+
@name = @full_name.simple_name
|
22
|
+
@is_public = is_public
|
23
|
+
@version = [vers]
|
24
|
+
end
|
25
|
+
|
26
|
+
def public?
|
27
|
+
@is_public
|
28
|
+
end
|
29
|
+
|
30
|
+
# Update the _version_ this class also exists in.
|
31
|
+
def update(version, is_public=@is_public)
|
32
|
+
raise "update class #{@name} is older than its last version: latest version=#{@version.last}, new version=#{version}" if version <= @version.last
|
33
|
+
# check for holes in versions
|
34
|
+
if version > @version.last+1
|
35
|
+
warn "#{@full_name} last in version #{@version.last}, not in #{@version.last+1}, but again in #{version}"
|
36
|
+
end
|
37
|
+
@version << version
|
38
|
+
|
39
|
+
if !is_public && @is_public
|
40
|
+
warn "#{@full_name} changed from public to package in version #{version}"
|
41
|
+
@is_public = is_public
|
42
|
+
@version = [version] # skip older versions
|
43
|
+
elsif is_public && ! @is_public
|
44
|
+
info "#{@full_name} changed from package to public in version #{version}"
|
45
|
+
@is_public = is_public
|
46
|
+
@version = [version] # skip older versions
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Sorts by simple +name+ inside the package.
|
51
|
+
def <=>(other)
|
52
|
+
@name.casecmp other.name
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_s
|
56
|
+
@full_name
|
57
|
+
end
|
58
|
+
|
59
|
+
# Return a string containing the full qualified name together with first and last version
|
60
|
+
# of this class. Ignore package versions, but obey _minversion_ and _maxversion_ .
|
61
|
+
# Print all versions, first to last, but skip <code>first<=minversion</code> and <code>last>=maxversion</code>.
|
62
|
+
def to_full_qualified_s(minversion, maxversion)
|
63
|
+
format_version(@full_name, minversion, maxversion)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Return a string containing the simple name and the version, if it is different from the package version.
|
67
|
+
def to_package_shortcut_s
|
68
|
+
vp = @parent.version
|
69
|
+
format_version(" #{@name}", vp.first, vp.last)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def format_version(start, minversion, maxversion)
|
75
|
+
# this class has a set of versions where it exists
|
76
|
+
# the parent has a set of versions where it exists, contains class versions
|
77
|
+
is_newer = @version.first > minversion
|
78
|
+
is_outdated = @version.last < maxversion
|
79
|
+
line = start +
|
80
|
+
" [#{ is_newer || (!@is_public && @version.first>0) ? @version.first.to_s : ''}" +
|
81
|
+
"#{is_outdated ? '-' + @version.last.to_s : ''}" +
|
82
|
+
"#{!@is_public ? 'p' : '' }]" +
|
83
|
+
" - \n"
|
84
|
+
line.sub(/\[-0/, "[0-0").sub(/(\d)-\1/, "only \\1").sub(/ \[\]/, '')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|