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.
Files changed (175) hide show
  1. data/Rakefile +67 -56
  2. data/Readme.txt +42 -39
  3. data/example_task.rb +172 -0
  4. data/examples/check_interface_names.rb +44 -0
  5. data/examples/corpus.rb +19 -0
  6. data/examples/count_classes_in_modules.rb +38 -0
  7. data/examples/cumulative_dependencies.rb +39 -0
  8. data/examples/find_all_imported_types.rb +44 -0
  9. data/examples/find_referenced_modules.rb +53 -0
  10. data/examples/find_unreferenced_classes.rb +65 -0
  11. data/examples/generate_class_lists.rb +67 -43
  12. data/examples/profiler_scratchpad.rb +33 -0
  13. data/examples/simple_usage.rb +42 -0
  14. data/history.txt +29 -7
  15. data/javaclass.gemspec +31 -0
  16. data/lib/javaclass/adder_tree.rb +92 -0
  17. data/lib/javaclass/analyse/dependencies.rb +52 -0
  18. data/lib/javaclass/{metric/metrics.txt → analyse/ideas.txt} +2 -2
  19. data/lib/javaclass/analyse/transitive_dependencies.rb +52 -0
  20. data/lib/javaclass/classfile/access_flag_constants.rb +24 -0
  21. data/lib/javaclass/classfile/access_flags.rb +49 -26
  22. data/lib/javaclass/classfile/class_format_error.rb +37 -0
  23. data/lib/javaclass/classfile/class_magic.rb +16 -8
  24. data/lib/javaclass/classfile/class_version.rb +19 -25
  25. data/lib/javaclass/classfile/constant_pool.rb +110 -45
  26. data/lib/javaclass/classfile/constants/base.rb +33 -12
  27. data/lib/javaclass/classfile/constants/double_reference.rb +55 -41
  28. data/lib/javaclass/classfile/constants/single_reference.rb +29 -21
  29. data/lib/javaclass/classfile/constants/value.rb +43 -33
  30. data/lib/javaclass/classfile/java_class_header.rb +72 -46
  31. data/lib/javaclass/classfile/java_class_header_as_java_name.rb +33 -0
  32. data/lib/javaclass/classfile/java_class_header_shortcuts.rb +19 -0
  33. data/lib/javaclass/classfile/references.rb +21 -19
  34. data/lib/javaclass/classlist/class_entry.rb +26 -27
  35. data/lib/javaclass/classlist/jar_searcher.rb +34 -25
  36. data/lib/javaclass/classlist/list.rb +31 -31
  37. data/lib/javaclass/classlist/package_entry.rb +25 -24
  38. data/lib/javaclass/classpath/any_classpath.rb +48 -0
  39. data/lib/javaclass/classpath/class_not_found_error.rb +20 -0
  40. data/lib/javaclass/classpath/classpaths.txt +2 -2
  41. data/lib/javaclass/classpath/composite_classpath.rb +56 -54
  42. data/lib/javaclass/classpath/convention_classpath.rb +38 -0
  43. data/lib/javaclass/classpath/eclipse_classpath.rb +74 -0
  44. data/lib/javaclass/classpath/factory.rb +65 -0
  45. data/lib/javaclass/classpath/file_classpath.rb +47 -0
  46. data/lib/javaclass/classpath/folder_classpath.rb +42 -44
  47. data/lib/javaclass/classpath/jar_classpath.rb +91 -52
  48. data/lib/javaclass/classpath/java_home_classpath.rb +24 -13
  49. data/lib/javaclass/classpath/maven_classpath.rb +44 -0
  50. data/lib/javaclass/classpath/temporary_unpacker.rb +111 -0
  51. data/lib/javaclass/classpath/tracking_classpath.rb +144 -0
  52. data/lib/javaclass/classscanner/ideas.txt +3 -0
  53. data/lib/javaclass/classscanner/imported_types.rb +29 -0
  54. data/lib/javaclass/classscanner/scanners.rb +29 -0
  55. data/lib/javaclass/delegate_directive.rb +15 -0
  56. data/lib/javaclass/dsl/caching_classpath.rb +38 -0
  57. data/lib/javaclass/dsl/classpath_analysers.rb +27 -0
  58. data/lib/javaclass/dsl/java_name_factory.rb +79 -0
  59. data/lib/javaclass/dsl/loader.rb +42 -0
  60. data/lib/javaclass/dsl/loading_classpath.rb +53 -0
  61. data/lib/javaclass/dsl/mixin.rb +54 -0
  62. data/lib/javaclass/gems/zip_file.rb +154 -0
  63. data/lib/javaclass/java_language.rb +50 -0
  64. data/lib/javaclass/java_name.rb +329 -55
  65. data/lib/javaclass/java_name_scanner.rb +95 -0
  66. data/lib/javaclass/resources/iso_3166_countries.txt +240 -0
  67. data/lib/javaclass/resources/jdk0_packages.txt +6 -0
  68. data/lib/javaclass/resources/jdk1_packages.txt +6 -0
  69. data/lib/javaclass/resources/jdk2_packages.txt +4 -0
  70. data/lib/javaclass/resources/jdk3_packages.txt +6 -0
  71. data/lib/javaclass/resources/jdk4_packages.txt +22 -0
  72. data/lib/javaclass/resources/jdk5_packages.txt +5 -0
  73. data/lib/javaclass/resources/jdk6_packages.txt +7 -0
  74. data/lib/javaclass/resources/jdk7_packages.txt +0 -0
  75. data/lib/javaclass/resources/jdk_packages.txt +53 -0
  76. data/lib/javaclass/resources/reserved_words.txt +50 -0
  77. data/lib/javaclass/string_hexdump.rb +76 -0
  78. data/lib/javaclass/string_ux.rb +21 -10
  79. data/lib/javaclass.rb +16 -41
  80. data/license.txt +28 -0
  81. data/planned.txt +13 -0
  82. data/test/data/Object_102.class +0 -0
  83. data/test/data/Runnable_102.class +0 -0
  84. data/test/data/access_flags/AccessFlagsTestAnnotation.class +0 -0
  85. data/test/data/access_flags/AccessFlagsTestAnnotation.java +3 -0
  86. data/test/data/access_flags/AccessFlagsTestEnum$1.class +0 -0
  87. data/test/data/access_flags/AccessFlagsTestEnum.class +0 -0
  88. data/test/data/access_flags/AccessFlagsTestEnum.java +6 -0
  89. data/test/data/access_flags/AccessFlagsTestInner$1.class +0 -0
  90. data/test/data/access_flags/AccessFlagsTestInner$2.class +0 -0
  91. data/test/data/access_flags/AccessFlagsTestInner.class +0 -0
  92. data/test/data/access_flags/AccessFlagsTestInner.java +13 -0
  93. data/test/data/access_flags/AccessFlagsTestPackage.class +0 -0
  94. data/test/data/access_flags/AccessFlagsTestPackage.java +1 -1
  95. data/test/data/api/packagename/AccessFlagsTestPublic.class +0 -0
  96. data/test/data/class_version/ClassVersionTest17.class +0 -0
  97. data/test/data/class_version/make.bat +6 -2
  98. data/test/data/eclipse_classpath/classes/ClassVersionTest12.class +0 -0
  99. data/test/data/eclipse_classpath/lib/JarClasspathTest.jar +0 -0
  100. data/test/data/eclipse_classpath/test-classes/ClassVersionTest13.class +0 -0
  101. data/test/data/folder_classpath/{JarClasspathTestFolder → classes}/ClassVersionTest10.class +0 -0
  102. data/test/data/folder_classpath/{JarClasspathTestFolder → classes}/package/ClassVersionTest11.class +0 -0
  103. data/test/data/jar_classpath/JarClasspathTest.jar +0 -0
  104. data/test/data/jar_classpath/JarClasspathTest.zip +0 -0
  105. data/test/data/jar_classpath/JarClasspathTestManifest.jar +0 -0
  106. data/test/data/jar_classpath/JarClasspathTestMultiManifest.jar +0 -0
  107. data/test/data/jar_classpath/make.bat +6 -2
  108. data/test/data/jar_searcher/BrokenRunnable_102.class +0 -0
  109. data/test/data/java_home_classpath/jdk118/lib/classes.zip +0 -0
  110. data/test/data/java_name_scanner/META-INF/MANIFEST.MF +12 -0
  111. data/test/data/java_name_scanner/plugin.xml +18 -0
  112. data/test/data/maven_classpath/module/pom.xml +8 -0
  113. data/test/data/maven_classpath/module/target/classes/ClassVersionTest12.class +0 -0
  114. data/test/data/maven_classpath/pom.xml +8 -0
  115. data/test/data/maven_classpath/target/classes/ClassVersionTest10.class +0 -0
  116. data/test/data/maven_classpath/target/test-classes/ClassVersionTest11.class +0 -0
  117. data/test/data/transitive_dependencies/A.class +0 -0
  118. data/test/data/transitive_dependencies/A.java +5 -0
  119. data/test/data/transitive_dependencies/B.class +0 -0
  120. data/test/data/transitive_dependencies/B.java +3 -0
  121. data/test/data/transitive_dependencies/C.class +0 -0
  122. data/test/data/transitive_dependencies/C.java +3 -0
  123. data/test/data/transitive_dependencies/Start.class +0 -0
  124. data/test/data/transitive_dependencies/Start.java +4 -0
  125. data/test/data/transitive_dependencies/make.bat +3 -0
  126. data/test/data/zip_file/commons-math-2.2-broken.zip +0 -0
  127. data/test/data/zip_file/regenerated-with-7zip.zip +0 -0
  128. data/test/data/zip_file/regenerated-with-jar.zip +0 -0
  129. data/test/dot_classpath.rb +33 -0
  130. data/test/logging_folder_classpath.rb +19 -0
  131. data/test/setup.rb +1 -1
  132. data/test/test_access_flags.rb +58 -32
  133. data/test/test_adder_tree.rb +78 -0
  134. data/test/test_any_classpath.rb +39 -0
  135. data/test/test_base.rb +9 -7
  136. data/test/test_caching_classpath.rb +41 -0
  137. data/test/test_class_entry.rb +60 -60
  138. data/test/test_class_magic.rb +31 -0
  139. data/test/test_class_version.rb +25 -25
  140. data/test/test_composite_classpath.rb +22 -23
  141. data/test/test_constant_pool.rb +37 -13
  142. data/test/test_convention_classpath.rb +39 -0
  143. data/test/test_eclipse_classpath.rb +73 -0
  144. data/test/test_factory.rb +61 -0
  145. data/test/test_folder_classpath.rb +26 -10
  146. data/test/test_imported_types.rb +34 -0
  147. data/test/test_jar_classpath.rb +29 -14
  148. data/test/test_jar_searcher.rb +27 -14
  149. data/test/test_java_class_header.rb +22 -10
  150. data/test/test_java_class_header_as_java_name.rb +41 -0
  151. data/test/test_java_home_classpath.rb +17 -11
  152. data/test/test_java_name.rb +204 -64
  153. data/test/test_java_name_factory.rb +52 -0
  154. data/test/test_java_name_scanner.rb +24 -0
  155. data/test/test_javaclass_api.rb +43 -0
  156. data/test/test_list.rb +58 -44
  157. data/test/test_load_directive.rb +34 -0
  158. data/test/test_maven_classpath.rb +46 -0
  159. data/test/test_package_entry.rb +27 -22
  160. data/test/test_references.rb +14 -14
  161. data/test/test_string_hexdump.rb +24 -0
  162. data/test/test_string_ux.rb +18 -106
  163. data/test/test_tracking_classpath.rb +112 -0
  164. data/test/test_transitive_dependencies.rb +31 -0
  165. data/test/test_unpacking_jar_classpath.rb +43 -0
  166. data/test/test_zip_file.rb +33 -0
  167. data/test/ts_all_tests.rb +80 -18
  168. data/thanks.txt +2 -0
  169. metadata +151 -22
  170. data/lib/javaclass/classpath/port_ClassPathEntry.java +0 -202
  171. data/lib/javaclass/classpath/port_ClassPathEntryFactory.java +0 -311
  172. data/lib/javaclass/classpath/port_DirectoryRepository.java +0 -24
  173. data/lib/javaclass/metric/ccd.rb +0 -68
  174. data/lib/javaclass/metric/class_usage.rb +0 -41
  175. 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
- # Access flags as defined by JVM spec.
11
- ACC_PUBLIC = 0x0001
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
- raise "inconsistent flags #{@flags} (abstract and final)" if abstract? && final?
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
- (@flags & ACC_PUBLIC) != 0
40
+ (@flags & ACC_PUBLIC) != 0
34
41
  end
35
- alias accessible? public?
36
-
42
+ alias accessible? public?
43
+
37
44
  def final?
38
- (@flags & ACC_FINAL) != 0
45
+ (@flags & ACC_FINAL) != 0
39
46
  end
40
-
47
+
41
48
  def abstract?
42
- (@flags & ACC_ABSTRACT) != 0
49
+ (@flags & ACC_ABSTRACT) != 0
43
50
  end
44
-
51
+
45
52
  def interface?
46
- (@flags & ACC_INTERFACE) != 0
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
- denom = 1.0
32
+ 1.0
30
33
  else
31
- denom = 1.0 * 10**(Math.log10(@minor).floor + 1)
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 v == 46.0 # JDK 1.2=46
50
- '1.2'
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
- @pool = {} # cnt (fixnum) => constant
34
+ creator = PoolCreator.new(data, start)
35
+ creator.create!
34
36
 
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
37
+ @pool = creator.pool # cnt (Fixnum) => constant class
38
+ @item_count = creator.item_count
53
39
 
54
- @size = pos - start
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
- module Constants # :nodoc:
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
- @name = self.class.to_s[/::[^:]+$/][10..-1] # skip modules (::) and "Constant"
19
- @name = name if name
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