javaclass 0.0.3 → 0.0.4
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/Rakefile +67 -56
- data/Readme.txt +42 -39
- data/example_task.rb +172 -0
- data/examples/check_interface_names.rb +44 -0
- data/examples/corpus.rb +19 -0
- data/examples/count_classes_in_modules.rb +38 -0
- data/examples/cumulative_dependencies.rb +39 -0
- data/examples/find_all_imported_types.rb +44 -0
- data/examples/find_referenced_modules.rb +53 -0
- data/examples/find_unreferenced_classes.rb +65 -0
- data/examples/generate_class_lists.rb +67 -43
- data/examples/profiler_scratchpad.rb +33 -0
- data/examples/simple_usage.rb +42 -0
- data/history.txt +29 -7
- data/javaclass.gemspec +31 -0
- data/lib/javaclass/adder_tree.rb +92 -0
- data/lib/javaclass/analyse/dependencies.rb +52 -0
- data/lib/javaclass/{metric/metrics.txt → analyse/ideas.txt} +2 -2
- data/lib/javaclass/analyse/transitive_dependencies.rb +52 -0
- data/lib/javaclass/classfile/access_flag_constants.rb +24 -0
- data/lib/javaclass/classfile/access_flags.rb +49 -26
- data/lib/javaclass/classfile/class_format_error.rb +37 -0
- data/lib/javaclass/classfile/class_magic.rb +16 -8
- data/lib/javaclass/classfile/class_version.rb +19 -25
- data/lib/javaclass/classfile/constant_pool.rb +110 -45
- data/lib/javaclass/classfile/constants/base.rb +33 -12
- data/lib/javaclass/classfile/constants/double_reference.rb +55 -41
- data/lib/javaclass/classfile/constants/single_reference.rb +29 -21
- data/lib/javaclass/classfile/constants/value.rb +43 -33
- data/lib/javaclass/classfile/java_class_header.rb +72 -46
- data/lib/javaclass/classfile/java_class_header_as_java_name.rb +33 -0
- data/lib/javaclass/classfile/java_class_header_shortcuts.rb +19 -0
- data/lib/javaclass/classfile/references.rb +21 -19
- data/lib/javaclass/classlist/class_entry.rb +26 -27
- data/lib/javaclass/classlist/jar_searcher.rb +34 -25
- data/lib/javaclass/classlist/list.rb +31 -31
- data/lib/javaclass/classlist/package_entry.rb +25 -24
- data/lib/javaclass/classpath/any_classpath.rb +48 -0
- data/lib/javaclass/classpath/class_not_found_error.rb +20 -0
- data/lib/javaclass/classpath/classpaths.txt +2 -2
- data/lib/javaclass/classpath/composite_classpath.rb +56 -54
- data/lib/javaclass/classpath/convention_classpath.rb +38 -0
- data/lib/javaclass/classpath/eclipse_classpath.rb +74 -0
- data/lib/javaclass/classpath/factory.rb +65 -0
- data/lib/javaclass/classpath/file_classpath.rb +47 -0
- data/lib/javaclass/classpath/folder_classpath.rb +42 -44
- data/lib/javaclass/classpath/jar_classpath.rb +91 -52
- data/lib/javaclass/classpath/java_home_classpath.rb +24 -13
- data/lib/javaclass/classpath/maven_classpath.rb +44 -0
- data/lib/javaclass/classpath/temporary_unpacker.rb +111 -0
- data/lib/javaclass/classpath/tracking_classpath.rb +144 -0
- data/lib/javaclass/classscanner/ideas.txt +3 -0
- data/lib/javaclass/classscanner/imported_types.rb +29 -0
- data/lib/javaclass/classscanner/scanners.rb +29 -0
- data/lib/javaclass/delegate_directive.rb +15 -0
- data/lib/javaclass/dsl/caching_classpath.rb +38 -0
- data/lib/javaclass/dsl/classpath_analysers.rb +27 -0
- data/lib/javaclass/dsl/java_name_factory.rb +79 -0
- data/lib/javaclass/dsl/loader.rb +42 -0
- data/lib/javaclass/dsl/loading_classpath.rb +53 -0
- data/lib/javaclass/dsl/mixin.rb +54 -0
- data/lib/javaclass/gems/zip_file.rb +154 -0
- data/lib/javaclass/java_language.rb +50 -0
- data/lib/javaclass/java_name.rb +329 -55
- data/lib/javaclass/java_name_scanner.rb +95 -0
- data/lib/javaclass/resources/iso_3166_countries.txt +240 -0
- data/lib/javaclass/resources/jdk0_packages.txt +6 -0
- data/lib/javaclass/resources/jdk1_packages.txt +6 -0
- data/lib/javaclass/resources/jdk2_packages.txt +4 -0
- data/lib/javaclass/resources/jdk3_packages.txt +6 -0
- data/lib/javaclass/resources/jdk4_packages.txt +22 -0
- data/lib/javaclass/resources/jdk5_packages.txt +5 -0
- data/lib/javaclass/resources/jdk6_packages.txt +7 -0
- data/lib/javaclass/resources/jdk7_packages.txt +0 -0
- data/lib/javaclass/resources/jdk_packages.txt +53 -0
- data/lib/javaclass/resources/reserved_words.txt +50 -0
- data/lib/javaclass/string_hexdump.rb +76 -0
- data/lib/javaclass/string_ux.rb +21 -10
- data/lib/javaclass.rb +16 -41
- data/license.txt +28 -0
- data/planned.txt +13 -0
- data/test/data/Object_102.class +0 -0
- data/test/data/Runnable_102.class +0 -0
- data/test/data/access_flags/AccessFlagsTestAnnotation.class +0 -0
- data/test/data/access_flags/AccessFlagsTestAnnotation.java +3 -0
- data/test/data/access_flags/AccessFlagsTestEnum$1.class +0 -0
- data/test/data/access_flags/AccessFlagsTestEnum.class +0 -0
- data/test/data/access_flags/AccessFlagsTestEnum.java +6 -0
- data/test/data/access_flags/AccessFlagsTestInner$1.class +0 -0
- data/test/data/access_flags/AccessFlagsTestInner$2.class +0 -0
- data/test/data/access_flags/AccessFlagsTestInner.class +0 -0
- data/test/data/access_flags/AccessFlagsTestInner.java +13 -0
- data/test/data/access_flags/AccessFlagsTestPackage.class +0 -0
- data/test/data/access_flags/AccessFlagsTestPackage.java +1 -1
- data/test/data/api/packagename/AccessFlagsTestPublic.class +0 -0
- data/test/data/class_version/ClassVersionTest17.class +0 -0
- data/test/data/class_version/make.bat +6 -2
- data/test/data/eclipse_classpath/classes/ClassVersionTest12.class +0 -0
- data/test/data/eclipse_classpath/lib/JarClasspathTest.jar +0 -0
- data/test/data/eclipse_classpath/test-classes/ClassVersionTest13.class +0 -0
- data/test/data/folder_classpath/{JarClasspathTestFolder → classes}/ClassVersionTest10.class +0 -0
- data/test/data/folder_classpath/{JarClasspathTestFolder → classes}/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 +6 -2
- data/test/data/jar_searcher/BrokenRunnable_102.class +0 -0
- data/test/data/java_home_classpath/jdk118/lib/classes.zip +0 -0
- data/test/data/java_name_scanner/META-INF/MANIFEST.MF +12 -0
- data/test/data/java_name_scanner/plugin.xml +18 -0
- data/test/data/maven_classpath/module/pom.xml +8 -0
- data/test/data/maven_classpath/module/target/classes/ClassVersionTest12.class +0 -0
- data/test/data/maven_classpath/pom.xml +8 -0
- data/test/data/maven_classpath/target/classes/ClassVersionTest10.class +0 -0
- data/test/data/maven_classpath/target/test-classes/ClassVersionTest11.class +0 -0
- data/test/data/transitive_dependencies/A.class +0 -0
- data/test/data/transitive_dependencies/A.java +5 -0
- data/test/data/transitive_dependencies/B.class +0 -0
- data/test/data/transitive_dependencies/B.java +3 -0
- data/test/data/transitive_dependencies/C.class +0 -0
- data/test/data/transitive_dependencies/C.java +3 -0
- data/test/data/transitive_dependencies/Start.class +0 -0
- data/test/data/transitive_dependencies/Start.java +4 -0
- data/test/data/transitive_dependencies/make.bat +3 -0
- data/test/data/zip_file/commons-math-2.2-broken.zip +0 -0
- data/test/data/zip_file/regenerated-with-7zip.zip +0 -0
- data/test/data/zip_file/regenerated-with-jar.zip +0 -0
- data/test/dot_classpath.rb +33 -0
- data/test/logging_folder_classpath.rb +19 -0
- data/test/setup.rb +1 -1
- data/test/test_access_flags.rb +58 -32
- data/test/test_adder_tree.rb +78 -0
- data/test/test_any_classpath.rb +39 -0
- data/test/test_base.rb +9 -7
- data/test/test_caching_classpath.rb +41 -0
- data/test/test_class_entry.rb +60 -60
- data/test/test_class_magic.rb +31 -0
- data/test/test_class_version.rb +25 -25
- data/test/test_composite_classpath.rb +22 -23
- data/test/test_constant_pool.rb +37 -13
- data/test/test_convention_classpath.rb +39 -0
- data/test/test_eclipse_classpath.rb +73 -0
- data/test/test_factory.rb +61 -0
- data/test/test_folder_classpath.rb +26 -10
- data/test/test_imported_types.rb +34 -0
- data/test/test_jar_classpath.rb +29 -14
- data/test/test_jar_searcher.rb +27 -14
- data/test/test_java_class_header.rb +22 -10
- data/test/test_java_class_header_as_java_name.rb +41 -0
- data/test/test_java_home_classpath.rb +17 -11
- data/test/test_java_name.rb +204 -64
- data/test/test_java_name_factory.rb +52 -0
- data/test/test_java_name_scanner.rb +24 -0
- data/test/test_javaclass_api.rb +43 -0
- data/test/test_list.rb +58 -44
- data/test/test_load_directive.rb +34 -0
- data/test/test_maven_classpath.rb +46 -0
- data/test/test_package_entry.rb +27 -22
- data/test/test_references.rb +14 -14
- data/test/test_string_hexdump.rb +24 -0
- data/test/test_string_ux.rb +18 -106
- data/test/test_tracking_classpath.rb +112 -0
- data/test/test_transitive_dependencies.rb +31 -0
- data/test/test_unpacking_jar_classpath.rb +43 -0
- data/test/test_zip_file.rb +33 -0
- data/test/ts_all_tests.rb +80 -18
- data/thanks.txt +2 -0
- metadata +151 -22
- data/lib/javaclass/classpath/port_ClassPathEntry.java +0 -202
- data/lib/javaclass/classpath/port_ClassPathEntryFactory.java +0 -311
- data/lib/javaclass/classpath/port_DirectoryRepository.java +0 -24
- data/lib/javaclass/metric/ccd.rb +0 -68
- data/lib/javaclass/metric/class_usage.rb +0 -41
- data/test/test_javaclass.rb +0 -22
@@ -0,0 +1,24 @@
|
|
1
|
+
module JavaClass
|
2
|
+
module ClassFile
|
3
|
+
|
4
|
+
# Access flags as defined by JVM spec.
|
5
|
+
# Author:: Peter Kofler
|
6
|
+
module AccessFlagsConstants
|
7
|
+
|
8
|
+
ACC_PUBLIC = 0x0001
|
9
|
+
ACC_FINAL = 0x0010
|
10
|
+
ACC_SUPER = 0x0020 # old invokespecial instruction semantics
|
11
|
+
ACC_INTERFACE = 0x0200
|
12
|
+
ACC_ABSTRACT = 0x0400
|
13
|
+
ACC_SYNTHETIC = 0x1000 # TODO Check what the synthetic flag means
|
14
|
+
ACC_ANNOTATION = 0x2000
|
15
|
+
ACC_ENUM = 0x4000
|
16
|
+
# TODO How were Java 1.0's "private protected" fields? set up? (see old JVM spec)
|
17
|
+
|
18
|
+
# Bitmask for unknown/not supported flags.
|
19
|
+
ACC_OTHER = 0xffff ^ ACC_PUBLIC ^ ACC_FINAL ^ ACC_SUPER ^ ACC_INTERFACE ^ ACC_ABSTRACT ^ ACC_SYNTHETIC ^ ACC_ENUM ^ ACC_ANNOTATION
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -1,52 +1,75 @@
|
|
1
1
|
require 'javaclass/string_ux'
|
2
|
+
require 'javaclass/classfile/access_flag_constants'
|
3
|
+
require 'javaclass/classfile/class_format_error'
|
2
4
|
|
3
5
|
module JavaClass
|
4
|
-
module ClassFile
|
5
|
-
|
6
|
+
module ClassFile
|
7
|
+
|
6
8
|
# The access flags of a class or interface.
|
7
9
|
# Author:: Peter Kofler
|
8
10
|
class AccessFlags
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
ACC_FINAL = 0x0010
|
13
|
-
ACC_SUPER = 0x0020 # old invokespecial instruction semantics
|
14
|
-
ACC_INTERFACE = 0x0200
|
15
|
-
ACC_ABSTRACT = 0x0400
|
16
|
-
ACC_INNER = 0x1000 # TODO unknown access flag, find in spec
|
17
|
-
ACC_OTHER = 0xffff ^ ACC_PUBLIC ^ ACC_FINAL ^ ACC_SUPER ^ ACC_INTERFACE ^ ACC_ABSTRACT ^ ACC_INNER
|
11
|
+
include AccessFlagsConstants
|
12
|
+
|
13
|
+
attr_reader :flags
|
18
14
|
|
19
15
|
def initialize(data, pos)
|
20
16
|
@flags = data.u2(pos)
|
21
|
-
|
17
|
+
correct_flags
|
18
|
+
assert_flags
|
19
|
+
end
|
20
|
+
|
21
|
+
def correct_flags
|
22
22
|
if interface? && !abstract?
|
23
23
|
# JDK 1.0 and 1.1 do have non abstract interfaces, fix it
|
24
24
|
@flags = @flags | ACC_ABSTRACT
|
25
25
|
end
|
26
|
-
raise "inconsistent flags #{@flags} (interface not abstract)" if interface? && !abstract?
|
27
|
-
raise "inconsistent flags #{@flags} (interface is final)" if interface? && final?
|
28
|
-
raise "inconsistent flags #{@flags} (other value #{@flags & ACC_OTHER})" if (@flags & ACC_OTHER) != 0
|
29
26
|
end
|
27
|
+
private :correct_flags
|
30
28
|
|
29
|
+
def assert_flags
|
30
|
+
raise ClassFormatError, "inconsistent flags #{@flags} (abstract and final)" if abstract? && final?
|
31
|
+
raise ClassFormatError, "inconsistent flags #{@flags} (interface not abstract)" if interface? && !abstract?
|
32
|
+
raise ClassFormatError, "inconsistent flags #{@flags} (interface is final)" if interface? && final?
|
33
|
+
raise ClassFormatError, "inconsistent flags #{@flags} (annotation not interface)" if annotation? && !interface?
|
34
|
+
raise ClassFormatError, "inconsistent flags #{@flags} (other value #{@flags & ACC_OTHER})" if (@flags & ACC_OTHER) != 0
|
35
|
+
end
|
36
|
+
private :assert_flags
|
37
|
+
|
31
38
|
# Return +true+ if the class is public.
|
32
39
|
def public?
|
33
|
-
|
40
|
+
(@flags & ACC_PUBLIC) != 0
|
34
41
|
end
|
35
|
-
alias accessible? public?
|
36
|
-
|
42
|
+
alias accessible? public?
|
43
|
+
|
37
44
|
def final?
|
38
|
-
|
45
|
+
(@flags & ACC_FINAL) != 0
|
39
46
|
end
|
40
|
-
|
47
|
+
|
41
48
|
def abstract?
|
42
|
-
|
49
|
+
(@flags & ACC_ABSTRACT) != 0
|
43
50
|
end
|
44
|
-
|
51
|
+
|
45
52
|
def interface?
|
46
|
-
|
53
|
+
(@flags & ACC_INTERFACE) != 0
|
54
|
+
end
|
55
|
+
|
56
|
+
def enum?
|
57
|
+
(@flags & ACC_ENUM) != 0
|
58
|
+
end
|
59
|
+
|
60
|
+
def annotation?
|
61
|
+
(@flags & ACC_ANNOTATION) != 0
|
62
|
+
end
|
63
|
+
|
64
|
+
# def inner?
|
65
|
+
# (@flags & ACC_INNER) != 0
|
66
|
+
# end
|
67
|
+
|
68
|
+
# Return the hex word of the flag.
|
69
|
+
def flags_hex
|
70
|
+
format '%4.4X', @flags
|
47
71
|
end
|
48
|
-
|
49
72
|
end
|
50
|
-
|
73
|
+
|
51
74
|
end
|
52
|
-
end
|
75
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module JavaClass
|
2
|
+
module ClassFile
|
3
|
+
|
4
|
+
# An error in the class file format. This indicates a broken class file or not supported feature.
|
5
|
+
# Author:: Peter Kofler
|
6
|
+
class ClassFormatError < StandardError
|
7
|
+
|
8
|
+
attr_reader :classname
|
9
|
+
attr_reader :classpath
|
10
|
+
|
11
|
+
def initialize(*args)
|
12
|
+
super
|
13
|
+
@classname = nil
|
14
|
+
@classpath = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
# Record the offending _classname_ and optional _classpath_ name.
|
18
|
+
def add_classname(classname, classpath=nil)
|
19
|
+
raise ArgumentError, "classname already set to #{@classname}" if @classname
|
20
|
+
@classname = classname
|
21
|
+
@classpath = classpath
|
22
|
+
end
|
23
|
+
|
24
|
+
def message
|
25
|
+
if @classname && @classpath
|
26
|
+
super + "\nin class #{@classname} on classpath #{@classpath}"
|
27
|
+
elsif @classname
|
28
|
+
super + "\nin class #{@classname}"
|
29
|
+
else
|
30
|
+
super
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -1,29 +1,37 @@
|
|
1
|
+
require 'javaclass/classfile/class_format_error'
|
2
|
+
|
1
3
|
module JavaClass
|
2
|
-
module ClassFile
|
3
|
-
|
4
|
+
module ClassFile
|
5
|
+
|
4
6
|
# The +CAFEBABE+ magic of a class file. This just checks if CAFEBABE is here.
|
5
7
|
# Author:: Peter Kofler
|
6
8
|
class ClassMagic # ZenTest SKIP
|
7
|
-
|
9
|
+
|
8
10
|
CAFE_BABE = "\xCA\xFE\xBA\xBE"
|
9
|
-
|
11
|
+
|
10
12
|
# Check the class magic in the _data_ beginning at position _start_ (which is usually 0).
|
11
13
|
def initialize(data, start=0)
|
12
|
-
# "parsing"
|
13
14
|
@bytes = data[start..start+3]
|
14
15
|
end
|
15
|
-
|
16
|
+
|
16
17
|
# Return +true+ if the data was valid, i.e. if the class started with +CAFEBABE+.
|
17
18
|
def valid?
|
18
19
|
@bytes == CAFE_BABE
|
19
20
|
end
|
20
|
-
|
21
|
+
|
21
22
|
# Return the value of the magic in this class.
|
22
23
|
def bytes
|
23
24
|
@bytes.dup
|
24
25
|
end
|
25
26
|
|
27
|
+
# Check if this magic is valid and raise an ClassFormatError if not with an optional _msg_ .
|
28
|
+
def check(msg='invalid java class magic')
|
29
|
+
unless valid?
|
30
|
+
raise(ClassFormatError, msg)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
26
34
|
end
|
27
|
-
|
35
|
+
|
28
36
|
end
|
29
37
|
end
|
@@ -1,44 +1,46 @@
|
|
1
1
|
require 'javaclass/string_ux'
|
2
2
|
|
3
3
|
module JavaClass
|
4
|
-
module ClassFile
|
5
|
-
|
4
|
+
module ClassFile
|
5
|
+
|
6
6
|
# Version of a class file.
|
7
7
|
# Author:: Peter Kofler
|
8
8
|
# See:: http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html#75883
|
9
9
|
class ClassVersion # ZenTest FULL to find method to_s
|
10
|
-
|
10
|
+
|
11
11
|
attr_reader :minor
|
12
12
|
attr_reader :major
|
13
|
-
|
13
|
+
|
14
14
|
# Extract the class version from the bytes _data_ starting at position _start_ (which is usually 4).
|
15
15
|
def initialize(data, start=4)
|
16
|
-
# parsing
|
17
16
|
@minor = data.u2(start)
|
18
17
|
@major = data.u2(start+2)
|
19
18
|
end
|
20
|
-
|
19
|
+
|
21
20
|
# Return the class file version as +major+.+minor+ string like 48.0 (Java 1.4) or 50.0 (Java 6).
|
22
21
|
def to_s
|
23
22
|
"#{@major}.#{@minor}"
|
24
23
|
end
|
25
|
-
|
24
|
+
|
26
25
|
# Return the version as +major+.+minor+ float.
|
27
26
|
def to_f
|
27
|
+
@major + @minor/denom
|
28
|
+
end
|
29
|
+
|
30
|
+
def denom
|
28
31
|
if @minor <= 0
|
29
|
-
|
32
|
+
1.0
|
30
33
|
else
|
31
|
-
|
34
|
+
1.0 * 10**(Math.log10(@minor).floor + 1)
|
32
35
|
end
|
33
|
-
|
34
|
-
@major + @minor/denom
|
35
36
|
end
|
37
|
+
private :denom
|
36
38
|
|
37
39
|
# Return a debug output of this version.
|
38
40
|
def dump
|
39
|
-
[" minor version: #{@minor}", " major version: #{@major}"]
|
41
|
+
[" minor version: #{@minor}", " major version: #{@major}"]
|
40
42
|
end
|
41
|
-
|
43
|
+
|
42
44
|
# Return the JDK version corresponding to this version like "1.6" or "unknown" if none matched.
|
43
45
|
def jdk_version
|
44
46
|
v = to_f
|
@@ -46,22 +48,14 @@ module JavaClass
|
|
46
48
|
'1.0'
|
47
49
|
elsif v > 45.3 && v <= 45.65535 # 1.1.X can support class file formats of versions in the range 45.0 through 45.65535 inclusive
|
48
50
|
'1.1'
|
49
|
-
elsif
|
50
|
-
|
51
|
-
elsif v == 47.0 # JDK 1.3=47
|
52
|
-
'1.3'
|
53
|
-
elsif v == 48.0 # JDK 1.4=48
|
54
|
-
'1.4'
|
55
|
-
elsif v == 49.0 # J2SE 5.0=49
|
56
|
-
'1.5'
|
57
|
-
elsif v == 50.0 # J2SE 6.0=50
|
58
|
-
'1.6'
|
51
|
+
elsif @major >= 46 && @minor == 0
|
52
|
+
"1.#{@major-44}"
|
59
53
|
else
|
60
54
|
'unknown'
|
61
55
|
end
|
62
56
|
end
|
63
|
-
|
57
|
+
|
64
58
|
end
|
65
|
-
|
59
|
+
|
66
60
|
end
|
67
61
|
end
|
@@ -2,90 +2,155 @@ require 'javaclass/string_ux'
|
|
2
2
|
require 'javaclass/classfile/constants/value'
|
3
3
|
require 'javaclass/classfile/constants/single_reference'
|
4
4
|
require 'javaclass/classfile/constants/double_reference'
|
5
|
+
require 'javaclass/classfile/class_format_error'
|
5
6
|
|
6
7
|
module JavaClass
|
7
|
-
module ClassFile
|
8
|
-
|
8
|
+
module ClassFile
|
9
|
+
|
9
10
|
# Container of the constant pool's constants.
|
10
11
|
# Author:: Peter Kofler
|
11
12
|
class ConstantPool
|
12
|
-
|
13
|
+
|
13
14
|
# Types of constants by their +tag+.
|
14
15
|
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,
|
16
|
+
CLASS_TAG = 7 => Constants::ConstantClass,
|
17
|
+
FIELD_TAG = 9 => Constants::ConstantField,
|
18
|
+
METHOD_TAG = 10 => Constants::ConstantMethod,
|
19
|
+
INTERFACE_METHOD_TAG = 11 => Constants::ConstantInterfaceMethod,
|
20
|
+
STRING_TAG = 8 => Constants::ConstantString,
|
21
|
+
INT_TAG = 3 => Constants::ConstantInt,
|
22
|
+
FLOAT_TAG = 4 => Constants::ConstantFloat,
|
23
|
+
LONG_TAG = 5 => Constants::ConstantLong,
|
24
|
+
DOUBLE_TAG = 6 => Constants::ConstantDouble,
|
25
|
+
NAME_AND_TYPE_TAG = 12 => Constants::ConstantNameAndType,
|
25
26
|
ASCIZ_TAG = 1 => Constants::ConstantAsciz,
|
26
27
|
}
|
27
|
-
|
28
|
+
|
28
29
|
# Size of the whole constant pool in bytes.
|
29
30
|
attr_reader :size
|
30
|
-
|
31
|
+
|
31
32
|
# Parse the constant pool from the bytes _data_ beginning at position _start_ (which is usually 8).
|
32
33
|
def initialize(data, start=8)
|
33
|
-
|
34
|
+
creator = PoolCreator.new(data, start)
|
35
|
+
creator.create!
|
34
36
|
|
35
|
-
#
|
36
|
-
@item_count =
|
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
|
37
|
+
@pool = creator.pool # cnt (Fixnum) => constant class
|
38
|
+
@item_count = creator.item_count
|
53
39
|
|
54
|
-
@size =
|
40
|
+
@size = @pool.values.inject(0) { |sum, constant| sum + constant.size } + 2
|
55
41
|
end
|
56
|
-
|
57
|
-
# Return the number of pool items. This number might be larger than +items+ available,
|
42
|
+
|
43
|
+
# Return the number of pool items. This number might be larger than +items+ available,
|
58
44
|
# because +long+ and +double+ constants take two slots.
|
59
45
|
def item_count
|
60
|
-
@item_count-1
|
46
|
+
@item_count - 1
|
61
47
|
end
|
62
|
-
|
63
|
-
# Return the _index_'th pool item. _index_ is the real index in the pool which may skip numbers.
|
48
|
+
|
49
|
+
# Return the _index_'th pool item. _index_ is the real index in the pool which may skip numbers.
|
64
50
|
def[](index)
|
51
|
+
check_index(index)
|
65
52
|
@pool[index]
|
66
53
|
end
|
67
|
-
|
54
|
+
|
55
|
+
def check_index(index)
|
56
|
+
if index < 0 || index > item_count
|
57
|
+
raise IndexError, "index #{index} is out of bounds of constant pool"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
private :check_index
|
61
|
+
|
68
62
|
# Return an array of the ordered list of constants.
|
69
63
|
def items
|
70
64
|
@pool.keys.sort.collect { |k| self[k] }
|
71
65
|
end
|
72
|
-
|
66
|
+
|
73
67
|
# Return an array of all constants of the given _tags_ types.
|
74
68
|
def find(*tags)
|
75
69
|
items.find_all { |item| tags.include? item.tag }
|
76
70
|
end
|
77
|
-
|
71
|
+
|
78
72
|
# Return all string constants.
|
79
73
|
def strings
|
80
74
|
find(STRING_TAG)
|
81
75
|
end
|
82
|
-
|
76
|
+
|
83
77
|
# Return a debug output of the whole pool.
|
84
78
|
def dump
|
85
79
|
[" Constant pool:"] + @pool.keys.sort.collect { |k| "const ##{k} = #{self[k].dump}"}
|
86
80
|
end
|
87
|
-
|
81
|
+
|
82
|
+
# Return the constant class from _index_'th pool item.
|
83
|
+
def class_item(index)
|
84
|
+
if self[index] && !self[index].const_class?
|
85
|
+
raise ClassFormatError, "inconsistent constant pool entry #{index} for class, should be Constant Class"
|
86
|
+
end
|
87
|
+
self[index]
|
88
|
+
end
|
89
|
+
|
90
|
+
# Return the constant field from _index_'th pool item.
|
91
|
+
def field_item(index)
|
92
|
+
if self[index] && !self[index].const_field?
|
93
|
+
raise ClassFormatError, "inconsistent constant pool entry #{index} for field, should be Constant Field"
|
94
|
+
end
|
95
|
+
self[index]
|
96
|
+
end
|
97
|
+
|
98
|
+
# Return the constant method from _index_'th pool item.
|
99
|
+
def method_item(index)
|
100
|
+
if self[index] && !self[index].const_method?
|
101
|
+
raise ClassFormatError, "inconsistent constant pool entry #{index} for method, should be Constant Method"
|
102
|
+
end
|
103
|
+
self[index]
|
104
|
+
end
|
105
|
+
|
88
106
|
end
|
89
107
|
|
108
|
+
class PoolCreator # :nodoc:
|
109
|
+
|
110
|
+
attr_reader :pool, :item_count
|
111
|
+
|
112
|
+
def initialize(data, start)
|
113
|
+
@data = data
|
114
|
+
@start = start
|
115
|
+
end
|
116
|
+
|
117
|
+
def create!
|
118
|
+
create_pool
|
119
|
+
fill_pool
|
120
|
+
end
|
121
|
+
|
122
|
+
def create_pool
|
123
|
+
@pool = {}
|
124
|
+
@item_count = @data.u2(@start)
|
125
|
+
@pos = @start + 2
|
126
|
+
end
|
127
|
+
|
128
|
+
def fill_pool
|
129
|
+
@cnt = 1
|
130
|
+
while @cnt <= @item_count-1
|
131
|
+
create_next_constant
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def create_next_constant
|
136
|
+
type = determine_constant_type
|
137
|
+
constant = type.new(@pool, @data, @pos)
|
138
|
+
@pool[@cnt] = constant
|
139
|
+
@pos += constant.size
|
140
|
+
@cnt += constant.slots
|
141
|
+
end
|
142
|
+
|
143
|
+
def determine_constant_type
|
144
|
+
tag_index = @data.u1(@pos)
|
145
|
+
type = ConstantPool::CONSTANT_TYPE_TAGS[tag_index]
|
146
|
+
unless type
|
147
|
+
raise ClassFormatError, "const ##{@cnt} contains unknown constant pool tag/index #{tag_index} (at pos #{@pos} in class).\n" +
|
148
|
+
"allowed are #{ConstantPool::CONSTANT_TYPE_TAGS.keys.sort.join(',')}"
|
149
|
+
end
|
150
|
+
type
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
90
155
|
end
|
91
156
|
end
|
@@ -1,33 +1,54 @@
|
|
1
1
|
require 'javaclass/string_ux'
|
2
2
|
|
3
|
-
module JavaClass
|
4
|
-
module ClassFile
|
5
|
-
|
6
|
-
|
3
|
+
module JavaClass
|
4
|
+
module ClassFile
|
5
|
+
|
6
|
+
# The module Constants is for separating namespaces. It contains the logic to parse constant pool constants.
|
7
|
+
# Author:: Peter Kofler
|
8
|
+
module Constants
|
9
|
+
|
7
10
|
# Superclass of all constant values in the constant pool. Every constant has a +name+, a +tag+ and a +size+ in bytes.
|
8
11
|
# Author:: Peter Kofler
|
9
12
|
class Base # ZenTest FULL to find method name
|
10
|
-
|
13
|
+
|
11
14
|
attr_reader :name
|
12
15
|
attr_reader :tag
|
13
16
|
attr_reader :size
|
14
17
|
attr_reader :slots
|
15
|
-
|
18
|
+
|
16
19
|
# Set default constants.
|
17
20
|
def initialize(name=nil)
|
18
|
-
|
19
|
-
|
21
|
+
if name
|
22
|
+
@name = name
|
23
|
+
else
|
24
|
+
@name = self.class.to_s[/::[^:]+$/][10..-1] # skip modules (::) and "Constant"
|
25
|
+
end
|
20
26
|
@size = 3
|
21
27
|
@slots = 1
|
22
28
|
end
|
23
|
-
|
29
|
+
|
24
30
|
# Return part of debug output.
|
25
31
|
def dump
|
26
|
-
"#{@name}\t" # #{@tag}
|
32
|
+
"#{@name}\t" # #{@tag}
|
33
|
+
end
|
34
|
+
|
35
|
+
# Return false for sanity check if it's a class. Subclasses should overwrite.
|
36
|
+
def const_class?
|
37
|
+
false
|
27
38
|
end
|
28
|
-
|
39
|
+
|
40
|
+
# Return false for sanity check if it's a field. Subclasses should overwrite.
|
41
|
+
def const_field?
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
# Return false for sanity check if it's a method. Subclasses should overwrite.
|
46
|
+
def const_method?
|
47
|
+
false
|
48
|
+
end
|
49
|
+
|
29
50
|
end
|
30
|
-
|
51
|
+
|
31
52
|
end
|
32
53
|
end
|
33
54
|
end
|