javaclass 0.4.1 → 0.4.2
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.
- checksums.yaml +7 -0
- data/Rakefile +188 -194
- data/Readme.txt +33 -29
- data/examples/generate_class_lists.rb +16 -9
- data/history.txt +15 -4
- data/javaclass.gemspec +10 -8
- data/lib/generated/examples/chart_class_dependencies.txt +35 -0
- data/lib/generated/examples/chart_module_dependencies.txt +43 -0
- data/lib/generated/examples/check_interface_names.txt +36 -0
- data/lib/generated/examples/count_classes_in_modules.txt +31 -0
- data/lib/generated/examples/cumulative_dependencies.txt +28 -0
- data/lib/generated/examples/find_all_imported_types.txt +38 -0
- data/lib/generated/examples/find_incoming_dependency_graph.txt +73 -0
- data/lib/generated/examples/find_layers_of_modules.txt +70 -0
- data/lib/generated/examples/find_referenced_modules.txt +41 -0
- data/lib/generated/examples/find_unreferenced_classes.txt +66 -0
- data/lib/generated/examples/generate_class_lists.txt +53 -0
- data/lib/generated/examples/show_jar_api.txt +64 -0
- data/lib/generated/examples/simple_usage.txt +38 -0
- data/lib/javaclass/classfile/access_flag_constants.rb +32 -5
- data/lib/javaclass/classfile/access_flags.rb +39 -20
- data/lib/javaclass/classfile/attributes/attributes.rb +134 -0
- data/lib/javaclass/classfile/class_access_flags.rb +37 -0
- data/lib/javaclass/classfile/class_file_attributes.rb +62 -0
- data/lib/javaclass/classfile/class_version.rb +0 -1
- data/lib/javaclass/classfile/constant_pool.rb +17 -11
- data/lib/javaclass/classfile/constants/single_reference.rb +53 -0
- data/lib/javaclass/classfile/fields.rb +37 -0
- data/lib/javaclass/classfile/java_class_header.rb +22 -13
- data/lib/javaclass/classfile/java_class_header_shortcuts.rb +6 -2
- data/lib/javaclass/classfile/methods.rb +37 -0
- data/lib/javaclass/classlist/jar_searcher.rb +38 -4
- data/lib/javaclass/classpath/temporary_unpacker.rb +1 -1
- data/lib/javaclass/string_hexdump.rb +1 -1
- data/license.txt +7 -7
- data/test/data/access_flags/{AccessFlagsTestInner$1.class → AccessFlagsTestAnonym$1.class} +0 -0
- data/test/data/access_flags/{AccessFlagsTestInner.class → AccessFlagsTestAnonym.class} +0 -0
- data/test/data/access_flags/AccessFlagsTestAnonym.java +9 -0
- data/test/data/access_flags/AccessFlagsTestPublic_javap.txt +1 -0
- data/test/data/constant_pool/Java8_JavaFX_Animation$1_Tag15.class +0 -0
- data/test/data/constant_pool/Java9_Activation_module-info_Tag20.class +0 -0
- data/test/data/jar_searcher/JarClassListTest.jar +0 -0
- data/test/data/jar_searcher/PublicClass.java +44 -1
- data/test/data/jar_searcher/make.bat +1 -2
- data/test/{test_access_flags.rb → test_class_access_flags.rb} +91 -93
- data/test/test_class_file_attributes.rb +57 -0
- data/test/test_constant_pool.rb +31 -0
- data/test/test_jar_searcher.rb +40 -7
- data/test/test_javaclass_api.rb +2 -2
- data/test/test_string_hexdump.rb +4 -1
- data/test/ts_all_tests.rb +4 -2
- metadata +100 -116
- data/test/data/access_flags/AccessFlagsTestInner$2.class +0 -0
- data/test/data/access_flags/AccessFlagsTestInner.java +0 -13
@@ -0,0 +1,41 @@
|
|
1
|
+
# Example usage of JavaClass::Classpath and JavaClass::Classpath::TrackingClasspath:
|
2
|
+
# Use the classes of one module and mark all their dependencies. Then find all modules
|
3
|
+
# which were not referenced. This list contains *potential* unused libraries (modules).
|
4
|
+
# Note that the libraries may still be used by reflection or internal from other libraries.
|
5
|
+
# Author:: Peter Kofler
|
6
|
+
# Copyright:: Copyright (c) 2009, Peter Kofler.
|
7
|
+
# License:: {BSD License}[link:/files/license_txt.html]
|
8
|
+
#
|
9
|
+
# === Steps
|
10
|
+
#
|
11
|
+
# require 'javaclass/dsl/mixin'
|
12
|
+
#
|
13
|
+
# 1) create a classpath of the main module(s) under test
|
14
|
+
# main_classpath = classpath("#{main_location};#{test_location}")
|
15
|
+
# puts "#{main_classpath.count} classes found in main classpath:"
|
16
|
+
# puts " #{main_classpath.elements.join("\n ")}"
|
17
|
+
#
|
18
|
+
# 2) mix in JavaClass::Classpath::TrackingClasspath before creating any new classpaths
|
19
|
+
# require 'javaclass/classpath/tracking_classpath'
|
20
|
+
#
|
21
|
+
# 3) create the (tracking) composite classpath of the given workspace
|
22
|
+
# cp = workspace(location)
|
23
|
+
# puts "#{cp.elements.size} classpaths found under the workspace #{location}"
|
24
|
+
# puts " #{cp.elements.join("\n ")}"
|
25
|
+
# puts "#{cp.count} classes found in classpaths"
|
26
|
+
#
|
27
|
+
# 4) mark all their referenced types as accessed in the workspace
|
28
|
+
# puts 'mapping referenced classes... (can take several minutes)'
|
29
|
+
# cp.reset_access
|
30
|
+
# main_classpath.external_types.each { |clazz| cp.mark_accessed(clazz) }
|
31
|
+
# puts 'referenced classes mapped'
|
32
|
+
#
|
33
|
+
# 5a) now find non accessed modules/libraries (i.e. classpath elements)
|
34
|
+
# unused = cp.elements.find_all { |clp| clp.jar? && clp.accessed == 0 }
|
35
|
+
# puts "\n#{unused.size} unused modules found:\n #{unused.join("\n ")}"
|
36
|
+
#
|
37
|
+
# 5b) or print the list of classpath elements (e.g. JARs) with their access
|
38
|
+
# puts "\nlibrary (module path): number of accessed classes"
|
39
|
+
# puts cp.elements.map { |clp| [clp.to_s, clp.accessed] }.
|
40
|
+
# sort { |a,b| a[1] <=> b[1] }.
|
41
|
+
# map { |e| " #{e[0]}: #{e[1]}" }
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# Advanced example usage of JavaClass::Classpath::TrackingClasspath. Load all
|
2
|
+
# classes of an Eclipse workspace. Then mark all referenced classes. In the end
|
3
|
+
# report remaining classes as unreferenced. This lists *potential* unused classes.
|
4
|
+
# Note that the classes may still be used by reflection. Also this can be used to
|
5
|
+
# find classes that have a certain number of references to them, e.g. only used once.
|
6
|
+
# Author:: Peter Kofler
|
7
|
+
# Copyright:: Copyright (c) 2009, Peter Kofler.
|
8
|
+
# License:: {BSD License}[link:/files/license_txt.html]
|
9
|
+
#
|
10
|
+
# === Steps
|
11
|
+
#
|
12
|
+
# require 'javaclass/dsl/mixin'
|
13
|
+
# require 'javaclass/classpath/tracking_classpath'
|
14
|
+
#
|
15
|
+
# speed up loading by skipping non source file classpaths
|
16
|
+
# Eclipse.skip_lib
|
17
|
+
#
|
18
|
+
# 1) create the (tracking) composite classpath of the given workspace
|
19
|
+
# cp = workspace(location)
|
20
|
+
# puts "#{cp.elements.size} classpaths found under the workspace #{location}:"
|
21
|
+
# puts " #{cp.elements.join("\n ")}"
|
22
|
+
#
|
23
|
+
# create a filter to limit all operations to the classes of two base packages
|
24
|
+
# filter = Proc.new { |clazz| clazz.same_or_subpackage_of?(package1) ||
|
25
|
+
# clazz.same_or_subpackage_of?(package2) }
|
26
|
+
#
|
27
|
+
# 2) load all classes in the given packages
|
28
|
+
# puts 'loading all *filtered* classes... (can take several minutes)'
|
29
|
+
# classes = cp.values(&filter)
|
30
|
+
# puts "#{classes.size} classes loaded from classpaths"
|
31
|
+
#
|
32
|
+
# 3) mark all referenced types in the given packages as accessed
|
33
|
+
# cp.reset_access
|
34
|
+
# classes.map { |clazz| clazz.imported_types }.
|
35
|
+
# flatten.
|
36
|
+
# find_all(&filter).
|
37
|
+
# each { |classname| cp.mark_accessed(classname) }
|
38
|
+
# puts '*filtered* classes mapped'
|
39
|
+
#
|
40
|
+
# 4) also mark all classes referenced from config files (e.g. hardcoded class names)
|
41
|
+
# hardcoded_classnames = scan_config_for_3rd_party_class_names(location).find_all(&filter)
|
42
|
+
# puts "#{hardcoded_classnames.size} classes references found in configs"
|
43
|
+
# hardcoded_classnames.each { |classname| cp.mark_accessed(classname) }
|
44
|
+
# puts 'hardcoded classes mapped'
|
45
|
+
#
|
46
|
+
# 5) mark unit tests (all classes ending in Test) on the test projects (classpath elements ending in .test)
|
47
|
+
# test_projects = cp.elements.find_all { |cpe| cpe.to_s =~ /\.test\// }
|
48
|
+
# test_projects.each do |project|
|
49
|
+
# project.names { |classname| classname =~ /Test(?:Case|Suite|s)?\.class/ }.
|
50
|
+
# each { |classname| project.mark_accessed(classname) }
|
51
|
+
# end
|
52
|
+
# puts 'test classes mapped'
|
53
|
+
#
|
54
|
+
# 6) find non accessed classes in specific packages and report them
|
55
|
+
# unused_classes = classes.find_all { |clazz| cp.accessed(clazz) == 0 }
|
56
|
+
# report = unused_classes.map { |clazz| "#{clazz.to_classname}" }
|
57
|
+
# puts "#{report.size} unused classes found:\n #{report.join("\n ")}"
|
58
|
+
#
|
59
|
+
# 7) find only once accessed classes and report them
|
60
|
+
# once_used_classes = classes.find_all { |clazz| cp.accessed(clazz) == 1 }
|
61
|
+
# report = once_used_classes.map { |clazz| "#{clazz.to_classname}" }
|
62
|
+
# puts "#{report.size} once used classes found:\n #{report.join("\n ")}"
|
63
|
+
#
|
64
|
+
# twice_used_classes = classes.find_all { |clazz| cp.accessed(clazz) == 2 }
|
65
|
+
# report = twice_used_classes.map { |clazz| "#{clazz.to_classname}" }
|
66
|
+
# puts "#{report.size} twice used classes found:\n #{report.join("\n ")}"
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Generate a JavaClass::ClassList, which contains all class
|
2
|
+
# names with version numbers when introduced. The list is created
|
3
|
+
# iterating all JDKs, opening the RT.jars and loading all classes.
|
4
|
+
# Author:: Peter Kofler
|
5
|
+
# Copyright:: Copyright (c) 2009, Peter Kofler.
|
6
|
+
# License:: {BSD License}[link:/files/license_txt.html]
|
7
|
+
#
|
8
|
+
# === Usage
|
9
|
+
#
|
10
|
+
# require 'javaclass/classlist/jar_searcher'
|
11
|
+
# require 'javaclass/classpath/jar_classpath'
|
12
|
+
# require 'javaclass/classlist/list'
|
13
|
+
#
|
14
|
+
# Struct to keep configuration what kind of JDK classes should be searched and added.
|
15
|
+
# JDK_CONFIG = Struct.new(:version, :label, :paths)
|
16
|
+
#
|
17
|
+
# configuration for some JDKs
|
18
|
+
# JDKS = [ JDK_CONFIG.new(...), ... ]
|
19
|
+
#
|
20
|
+
# 1) create a JavaClass::ClassList::JarSearcher
|
21
|
+
# JavaClass.unpack_jars!(:unpack)
|
22
|
+
# searcher = JavaClass::ClassList::JarSearcher.new
|
23
|
+
# searcher.skip_inner_classes = false
|
24
|
+
# searcher.skip_package_classes = true
|
25
|
+
#
|
26
|
+
# 2) filter out unwanted classes, e.g. vendor classes
|
27
|
+
# searcher.filters = %w[sun/ sunw/ com/oracle/ com/sun/ netscape/ com/rsa/
|
28
|
+
# quicktime/ com/apple/mrj/macos/carbon/ org/jcp/xml/dsig/internal/
|
29
|
+
# oracle/jrockit/ jdk/internal/ jdk/nashorn/internal/
|
30
|
+
# jdk/dynalink/internal/ jdk/incubator/http.internal/ jdk/javadoc/internal/ jdk/tools/jlink/internal/ ]
|
31
|
+
#
|
32
|
+
# 3) create a new JavaClass::ClassList::List to contain the classes
|
33
|
+
# list = JavaClass::ClassList::List.new
|
34
|
+
#
|
35
|
+
#
|
36
|
+
# Work on all lists defined in +JDKS+, add to the list and write list files.
|
37
|
+
# JDKS.each do |conf|
|
38
|
+
# # 4) iterate JARs and compile a list
|
39
|
+
# conf.paths.each { |p| list = searcher.compile_list(conf.version, p, list) }
|
40
|
+
#
|
41
|
+
# # 5) save various kind of lists
|
42
|
+
# basename = "./ClassLists/jdk#{conf.label.gsub(/\./, '')}"
|
43
|
+
# # File.open("#{basename}_new_package_classes.txt", "w") { |f| f.print list.old_access_list.collect{|m| m.sub(/\s.*$/, '')} }
|
44
|
+
# # File.open("#{basename}_all_classes.txt", "w") { |f| f.print list.plain_class_list }
|
45
|
+
# File.open("#{basename}_all_packages.txt", "w") { |f| f.print list.to_s }
|
46
|
+
# File.open("#{basename}_all_public_classes.txt", "w") { |f| f.print list.plain_class_list { |c| c.public? }.join() }
|
47
|
+
# File.open("#{basename}_new_public_classes.txt", "w") { |f| f.print list.plain_class_list { |c| c.public? and c.version.first == conf.version }.join() }
|
48
|
+
#
|
49
|
+
# baseversion = conf.label.gsub(/\.|-.+$/, '')
|
50
|
+
# File.open("./fullClassList#{baseversion}.txt", "w") { |f| f.print list.full_class_list.join() }
|
51
|
+
#
|
52
|
+
# puts "processed #{conf.label}"
|
53
|
+
# end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# Generate a JavaClass::ClassList, which contains all class of a given
|
2
|
+
# JAR in the local Maven repository. Use JavaClass::Classpath::MavenArtefact
|
3
|
+
# to identify and (possibly download) the JAR under question.
|
4
|
+
# Author:: Peter Kofler
|
5
|
+
# Copyright:: Copyright (c) 2009, Peter Kofler.
|
6
|
+
# License:: {BSD License}[link:/files/license_txt.html]
|
7
|
+
#
|
8
|
+
# === Usage
|
9
|
+
#
|
10
|
+
# require 'javaclass/classlist/jar_searcher'
|
11
|
+
# require 'javaclass/classpath/maven_artefact'
|
12
|
+
# require 'javaclass/classlist/list'
|
13
|
+
#
|
14
|
+
# Define configuration for some Maven artefacts
|
15
|
+
# JARS = [ JavaClass::Classpath::MavenArtefact.new('commons-lang', 'commons-lang', '2.6'),, ... ]
|
16
|
+
#
|
17
|
+
# For all artefacts listed in +JARS+, load classes and write list files.
|
18
|
+
# JARS.each do |artefact|
|
19
|
+
#
|
20
|
+
# 1) create a new JavaClass::ClassList::List to contain the classes of this JAR
|
21
|
+
# list = JavaClass::ClassList::List.new
|
22
|
+
#
|
23
|
+
# 2) create a JavaClass::ClassList::JarSearcher
|
24
|
+
# searcher = JavaClass::ClassList::JarSearcher.new
|
25
|
+
# searcher.skip_package_classes = true
|
26
|
+
#
|
27
|
+
# 3) create the classpath of the artefact's JAR
|
28
|
+
# artefact.download_if_needed
|
29
|
+
# classpath = artefact.classpath
|
30
|
+
#
|
31
|
+
# 4) scan the JAR and add classes to the list
|
32
|
+
# searcher.add_list_from_classpath(artefact.version, classpath, list)
|
33
|
+
#
|
34
|
+
# 5) save the list to a file named "Commons Lang 2.6.txt" (after the artefact)
|
35
|
+
# File.open("#{artefact.title} #{artefact.version}.txt", "w") do |f|
|
36
|
+
# # print title
|
37
|
+
# f.print "*** #{artefact.title}\n"
|
38
|
+
#
|
39
|
+
# list.packages.each { |pkg|
|
40
|
+
# f.print "\n"
|
41
|
+
# # print package name
|
42
|
+
# f.print "* #{pkg.name}\n"
|
43
|
+
#
|
44
|
+
# # print class names
|
45
|
+
# pkg.classes.each { |c| f.print " #{c.name}\n" }
|
46
|
+
# }
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# puts "processed #{artefact.name}"
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# 6) the generated file looks like
|
53
|
+
# *** Commons Lang
|
54
|
+
#
|
55
|
+
# * org.apache.commons.lang
|
56
|
+
# ArrayUtils
|
57
|
+
# BitField
|
58
|
+
# ...
|
59
|
+
#
|
60
|
+
# * org.apache.commons.lang.builder
|
61
|
+
# CompareToBuilder
|
62
|
+
# EqualsBuilder
|
63
|
+
# HashCodeBuilder
|
64
|
+
# ...
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# Example of basic usage of JavaClass to load and inspect class files.
|
2
|
+
# Author:: Peter Kofler
|
3
|
+
# Copyright:: Copyright (c) 2009, Peter Kofler.
|
4
|
+
# License:: {BSD License}[link:/files/license_txt.html]
|
5
|
+
#
|
6
|
+
# === Usage
|
7
|
+
#
|
8
|
+
# 1) require the basic module
|
9
|
+
# require 'javaclass'
|
10
|
+
#
|
11
|
+
# 2a) load a class directly from the file system
|
12
|
+
# clazz = JavaClass.load_fs('./test/data/access_flags/AccessFlagsTestPublic.class')
|
13
|
+
#
|
14
|
+
# 2b) or get a class from the system classpath which needs _JAVA_HOME_ to be set
|
15
|
+
# cp = JavaClass.environment_classpath
|
16
|
+
# puts cp.includes?('java/lang/String.class') # => 1 (true)
|
17
|
+
#
|
18
|
+
# 2c) or look up the class from some JavaClass::Classpath by its Java qualified name
|
19
|
+
# cp = JavaClass.classpath('./test/data/access_flags')
|
20
|
+
# puts cp.includes?('AccessFlagsTestPublic') # => 1 (true)
|
21
|
+
# clazz = JavaClass.load_cp('AccessFlagsTestPublic', cp)
|
22
|
+
#
|
23
|
+
# 3) then retrieve low level information about the class
|
24
|
+
# puts clazz.version # => "50.0"
|
25
|
+
# puts clazz.constant_pool.items[1] # => "packagename/AccessFlagsTestPublic"
|
26
|
+
# puts clazz.access_flags.public? # => true
|
27
|
+
# puts clazz.access_flags.final? # => false
|
28
|
+
# puts clazz.this_class # => "packagename/AccessFlagsTestPublic"
|
29
|
+
# puts clazz.super_class # => "java/lang/Object"
|
30
|
+
# puts clazz.super_class.to_classname # => "java.lang.Object"
|
31
|
+
# puts clazz.references.referenced_methods[0] # => "java/lang/Object.<init>:()V"
|
32
|
+
# puts clazz.interfaces # => []
|
33
|
+
#
|
34
|
+
# Returned class names are not just Strings, but JavaClass::JavaQualifiedName
|
35
|
+
# puts clazz.this_class.to_java_file # => "packagename/AccessFlagsTestPublic.java"
|
36
|
+
# puts clazz.this_class.full_name # => "packagename.AccessFlagsTestPublic"
|
37
|
+
# puts clazz.this_class.package # => "packagename"
|
38
|
+
# puts clazz.this_class.simple_name # => "AccessFlagsTestPublic"
|
@@ -5,19 +5,46 @@ module JavaClass
|
|
5
5
|
# Author:: Peter Kofler
|
6
6
|
module AccessFlagsConstants
|
7
7
|
|
8
|
+
# field
|
9
|
+
# ACC_PUBLIC = 0x0001
|
10
|
+
ACC_PRIVATE = 0x0002
|
11
|
+
ACC_PROTECTED = 0x0004
|
12
|
+
# TODO How were Java 1.0's "private protected" fields? set up? (see old JVM spec)
|
13
|
+
ACC_STATIC = 0x0008
|
14
|
+
# ACC_FINAL = 0x0010
|
15
|
+
ACC_VOLATILE = 0x0040
|
16
|
+
ACC_TRANSIENT = 0x0080
|
17
|
+
# ACC_SYNTHETIC = 0x1000
|
18
|
+
# ACC_ENUM = 0x4000
|
19
|
+
|
20
|
+
# method
|
21
|
+
# ACC_PUBLIC = 0x0001
|
22
|
+
# ACC_PRIVATE = 0x0002
|
23
|
+
# ACC_PROTECTED = 0x0004
|
24
|
+
# ACC_STATIC = 0x0008
|
25
|
+
# ACC_FINAL = 0x0010
|
26
|
+
ACC_SYNCHRONIZED = 0x0020
|
27
|
+
ACC_ACC_BRIDGE = 0x0040
|
28
|
+
ACC_VARARGS = 0x0080
|
29
|
+
ACC_NATIVE = 0x0100
|
30
|
+
# ACC_ABSTRACT = 0x0400
|
31
|
+
ACC_STRICT = 0x0800
|
32
|
+
# ACC_SYNTHETIC = 0x1000
|
33
|
+
|
34
|
+
# class
|
8
35
|
ACC_PUBLIC = 0x0001
|
9
36
|
ACC_FINAL = 0x0010
|
10
37
|
ACC_SUPER = 0x0020 # old invokespecial instruction semantics
|
11
38
|
ACC_INTERFACE = 0x0200
|
12
39
|
ACC_ABSTRACT = 0x0400
|
13
|
-
ACC_SYNTHETIC = 0x1000 #
|
40
|
+
ACC_SYNTHETIC = 0x1000 # may vary between different compilers
|
14
41
|
ACC_ANNOTATION = 0x2000
|
15
42
|
ACC_ENUM = 0x4000
|
16
|
-
|
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
|
43
|
+
ACC_MODULE = 0x8000
|
20
44
|
|
45
|
+
ACC_IMPLICIT = 0x8000 # e.g. default constructor
|
46
|
+
ACC_MANDATED = 0x8000 # formal parameter implicit declared
|
47
|
+
|
21
48
|
end
|
22
49
|
|
23
50
|
end
|
@@ -1,19 +1,18 @@
|
|
1
|
-
require 'javaclass/string_ux'
|
2
1
|
require 'javaclass/classfile/access_flag_constants'
|
3
2
|
require 'javaclass/classfile/class_format_error'
|
4
3
|
|
5
4
|
module JavaClass
|
6
5
|
module ClassFile
|
7
6
|
|
8
|
-
# The access flags
|
7
|
+
# The general JVM access flags.
|
9
8
|
# Author:: Peter Kofler
|
10
9
|
class AccessFlags
|
11
10
|
include AccessFlagsConstants
|
12
11
|
|
13
12
|
attr_reader :flags
|
14
13
|
|
15
|
-
def initialize(
|
16
|
-
@flags =
|
14
|
+
def initialize(flags)
|
15
|
+
@flags = flags
|
17
16
|
correct_flags
|
18
17
|
assert_flags
|
19
18
|
end
|
@@ -27,44 +26,64 @@ module JavaClass
|
|
27
26
|
private :correct_flags
|
28
27
|
|
29
28
|
def assert_flags
|
30
|
-
raise ClassFormatError, "inconsistent flags #{
|
31
|
-
raise ClassFormatError, "inconsistent flags #{
|
32
|
-
raise ClassFormatError, "inconsistent flags #{
|
33
|
-
raise ClassFormatError, "inconsistent flags #{
|
34
|
-
raise ClassFormatError, "inconsistent flags #{@flags} (other value #{@flags & ACC_OTHER})" if (@flags & ACC_OTHER) != 0
|
29
|
+
raise ClassFormatError, "inconsistent flags #{flags} (abstract and final)" if abstract? && final?
|
30
|
+
raise ClassFormatError, "inconsistent flags #{flags} (interface not abstract)" if interface? && !abstract?
|
31
|
+
raise ClassFormatError, "inconsistent flags #{flags} (interface is final)" if interface? && final?
|
32
|
+
raise ClassFormatError, "inconsistent flags #{flags} (annotation not interface)" if annotation? && !interface?
|
35
33
|
end
|
36
34
|
private :assert_flags
|
37
35
|
|
36
|
+
# Return +true+ if the bit _flag_ is set.
|
37
|
+
def is?(flag)
|
38
|
+
(@flags & flag) != 0
|
39
|
+
end
|
40
|
+
|
38
41
|
# Return +true+ if the class is public.
|
39
42
|
def public?
|
40
|
-
|
43
|
+
is? ACC_PUBLIC
|
41
44
|
end
|
42
45
|
alias accessible? public?
|
43
46
|
|
44
|
-
def
|
45
|
-
|
47
|
+
def private?
|
48
|
+
is? ACC_PRIVATE
|
46
49
|
end
|
47
50
|
|
51
|
+
def protected?
|
52
|
+
is? ACC_PROTECTED
|
53
|
+
end
|
54
|
+
|
55
|
+
def static?
|
56
|
+
is? ACC_STATIC
|
57
|
+
end
|
58
|
+
|
59
|
+
def final?
|
60
|
+
is? ACC_FINAL
|
61
|
+
end
|
62
|
+
|
48
63
|
def abstract?
|
49
|
-
|
64
|
+
is? ACC_ABSTRACT
|
65
|
+
end
|
66
|
+
|
67
|
+
def synthetic?
|
68
|
+
is? ACC_SYNTHETIC
|
50
69
|
end
|
51
70
|
|
52
71
|
def interface?
|
53
|
-
|
72
|
+
is? ACC_INTERFACE
|
54
73
|
end
|
55
74
|
|
56
75
|
def enum?
|
57
|
-
|
76
|
+
is? ACC_ENUM
|
58
77
|
end
|
59
78
|
|
60
79
|
def annotation?
|
61
|
-
|
80
|
+
is? ACC_ANNOTATION
|
62
81
|
end
|
63
82
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
83
|
+
def module?
|
84
|
+
is? ACC_MODULE
|
85
|
+
end
|
86
|
+
|
68
87
|
# Return the hex word of the flag.
|
69
88
|
def flags_hex
|
70
89
|
format '%4.4X', @flags
|
@@ -0,0 +1,134 @@
|
|
1
|
+
module JavaClass
|
2
|
+
module ClassFile
|
3
|
+
module Attributes
|
4
|
+
|
5
|
+
# General container of the attributes.
|
6
|
+
# Author:: Peter Kofler
|
7
|
+
class Attributes
|
8
|
+
|
9
|
+
# Size of the whole attributes structure in bytes.
|
10
|
+
attr_reader :size
|
11
|
+
|
12
|
+
# Parse the attributes structure from the bytes _data_ beginning at position _start_.
|
13
|
+
def initialize(data, start, constant_pool)
|
14
|
+
creator = AttributesCreator.new(data, start, constant_pool)
|
15
|
+
creator.create!
|
16
|
+
@attributes = creator.attributes
|
17
|
+
@size = creator.size
|
18
|
+
end
|
19
|
+
|
20
|
+
# Find the attribute with the given _name_.
|
21
|
+
def with(name)
|
22
|
+
@attributes.find { |attr| attr.name == name }
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
class AttributesCreator # :nodoc:
|
28
|
+
|
29
|
+
attr_reader :attributes
|
30
|
+
|
31
|
+
def initialize(data, start, constant_pool)
|
32
|
+
@data = data
|
33
|
+
@start = start
|
34
|
+
@pos = start
|
35
|
+
@constant_pool = constant_pool
|
36
|
+
end
|
37
|
+
|
38
|
+
def size
|
39
|
+
@pos - @start
|
40
|
+
end
|
41
|
+
|
42
|
+
def create!
|
43
|
+
@attributes = []
|
44
|
+
|
45
|
+
count = @data.u2(@pos)
|
46
|
+
@pos += 2
|
47
|
+
|
48
|
+
(1..count).each do |i|
|
49
|
+
attribute_name_idx = @data.u2(@pos)
|
50
|
+
@pos += 2
|
51
|
+
attribute_name = @constant_pool[attribute_name_idx].string
|
52
|
+
|
53
|
+
attribute_length = @data.u4(@pos)
|
54
|
+
@pos += 4
|
55
|
+
|
56
|
+
if attribute_name == 'SourceFile'
|
57
|
+
@attributes << source_file(attribute_name)
|
58
|
+
elsif attribute_name == 'InnerClasses'
|
59
|
+
@attributes << inner_classes(attribute_name)
|
60
|
+
else # skip
|
61
|
+
@pos += attribute_length
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def source_file(attribute_name)
|
68
|
+
sourcefile_idx = @data.u2(@pos)
|
69
|
+
@pos += 2
|
70
|
+
source_file = @constant_pool[sourcefile_idx].string
|
71
|
+
SourceFile.new(attribute_name, source_file)
|
72
|
+
end
|
73
|
+
|
74
|
+
def inner_classes(attribute_name)
|
75
|
+
number_of_classes = @data.u2(@pos)
|
76
|
+
@pos += 2
|
77
|
+
inner_classes = (1..number_of_classes).collect do |i|
|
78
|
+
inner_class_info_idx = @data.u2(@pos)
|
79
|
+
@pos += 2
|
80
|
+
|
81
|
+
@pos += 4
|
82
|
+
inner_class_access_flags = @data.u2(@pos)
|
83
|
+
@pos += 2
|
84
|
+
|
85
|
+
inner_class_info = @constant_pool[inner_class_info_idx]
|
86
|
+
|
87
|
+
InnerClass.new(inner_class_info.class_name, AccessFlags.new(inner_class_access_flags))
|
88
|
+
end
|
89
|
+
InnerClasses.new(attribute_name, inner_classes)
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
# Base class of the attributes
|
95
|
+
# Author:: Peter Kofler
|
96
|
+
class Attribute
|
97
|
+
attr_reader :name
|
98
|
+
|
99
|
+
def initialize(name)
|
100
|
+
@name = name
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class SourceFile < Attribute
|
105
|
+
attr_reader :source_file
|
106
|
+
|
107
|
+
def initialize(name, source_file)
|
108
|
+
super(name)
|
109
|
+
@source_file = source_file
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class InnerClasses < Attribute
|
114
|
+
attr_reader :inner_classes
|
115
|
+
|
116
|
+
def initialize(name, inner_classes)
|
117
|
+
super(name)
|
118
|
+
@inner_classes = inner_classes
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
class InnerClass
|
123
|
+
attr_reader :class_name
|
124
|
+
attr_reader :access_flags
|
125
|
+
|
126
|
+
def initialize(name, access_flags)
|
127
|
+
@class_name = name
|
128
|
+
@access_flags = access_flags
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|