javaclass 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +51 -0
- data/Readme.txt +47 -0
- data/history.txt +29 -0
- data/lib/javaclass/access_flags.rb +44 -0
- data/lib/javaclass/class_magic.rb +27 -0
- data/lib/javaclass/class_version.rb +65 -0
- data/lib/javaclass/constant_pool.rb +85 -0
- data/lib/javaclass/constants/base.rb +31 -0
- data/lib/javaclass/constants/double_reference.rb +81 -0
- data/lib/javaclass/constants/single_reference.rb +58 -0
- data/lib/javaclass/constants/value.rb +114 -0
- data/lib/javaclass/java_class_header.rb +88 -0
- data/lib/javaclass/references.rb +31 -0
- data/lib/javaclass/string_ux.rb +39 -0
- data/lib/javaclass.rb +14 -0
- data/test/data/AccessFlagsTestAbstract.class +0 -0
- data/test/data/AccessFlagsTestAbstract.java +3 -0
- data/test/data/AccessFlagsTestFinal.class +0 -0
- data/test/data/AccessFlagsTestFinal.java +3 -0
- data/test/data/AccessFlagsTestInterface.class +0 -0
- data/test/data/AccessFlagsTestInterface.java +3 -0
- data/test/data/AccessFlagsTestPackage.class +0 -0
- data/test/data/AccessFlagsTestPackage.java +3 -0
- data/test/data/AccessFlagsTestPublic$InnerClass.class +0 -0
- data/test/data/AccessFlagsTestPublic.class +0 -0
- data/test/data/AccessFlagsTestPublic.java +7 -0
- data/test/data/ClassVersionTest.java +1 -0
- data/test/data/ClassVersionTest10.class +0 -0
- data/test/data/ClassVersionTest11.class +0 -0
- data/test/data/ClassVersionTest12.class +0 -0
- data/test/data/ClassVersionTest13.class +0 -0
- data/test/data/ClassVersionTest14.class +0 -0
- data/test/data/ClassVersionTest15.class +0 -0
- data/test/data/ClassVersionTest16.class +0 -0
- data/test/data/ConstantPoolTest.class +0 -0
- data/test/data/ConstantPoolTest.java +20 -0
- data/test/data/makeAccessFlagsTest.bat +9 -0
- data/test/data/makeClassVersionTest.bat +35 -0
- data/test/data/makeConstantPoolTest.bat +6 -0
- data/test/setup.rb +14 -0
- data/test/test_access_flags.rb +53 -0
- data/test/test_base.rb +22 -0
- data/test/test_class_version.rb +59 -0
- data/test/test_constant_pool.rb +90 -0
- data/test/test_java_class_header.rb +30 -0
- data/test/test_references.rb +29 -0
- data/test/test_string_ux.rb +30 -0
- data/test/ts_all_tests.rb +36 -0
- metadata +106 -0
data/Rakefile
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
require 'rake'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rake/packagetask'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
require 'rake/rdoctask'
|
8
|
+
|
9
|
+
jcversion = '0.0.2'
|
10
|
+
|
11
|
+
gemspec = Gem::Specification.new do |s|
|
12
|
+
s.name = 'javaclass'
|
13
|
+
s.version = jcversion
|
14
|
+
s.summary ='A parser and disassembler for Java class files'
|
15
|
+
s.files = FileList['Readme.txt', '{lib,test}/**/*.*', 'history.txt', 'Rakefile']
|
16
|
+
s.test_files = FileList["{test}/**/test_*.rb"]
|
17
|
+
s.require_path = 'lib'
|
18
|
+
s.has_rdoc = true
|
19
|
+
s.rubyforge_project = 'javaclass'
|
20
|
+
s.homepage = 'http://javaclass.rubyforge.org/'
|
21
|
+
s.author = 'Peter Kofler'
|
22
|
+
s.email = 'bruno41 at rubyforge dot org'
|
23
|
+
# TODO use new cc email
|
24
|
+
s.platform = Gem::Platform::RUBY
|
25
|
+
end
|
26
|
+
|
27
|
+
Rake::GemPackageTask.new(gemspec) do |pkg|
|
28
|
+
pkg.need_zip = true
|
29
|
+
end
|
30
|
+
|
31
|
+
Rake::PackageTask.new(gemspec.name, gemspec.version) do |pkg|
|
32
|
+
#pkg.need_tar = true - no compress in tar on unxutils in Windows
|
33
|
+
#pkg.need_tar_gz = true - no compress in tar on unxutils in Windows
|
34
|
+
pkg.need_zip = true
|
35
|
+
pkg.package_files.include gemspec.files
|
36
|
+
end
|
37
|
+
|
38
|
+
Rake::TestTask.new do |t|
|
39
|
+
t.pattern = 'test/**/test_*.rb'
|
40
|
+
t.warning = true
|
41
|
+
t.verbose = false
|
42
|
+
end
|
43
|
+
|
44
|
+
Rake::RDocTask.new do |rdoc|
|
45
|
+
# rdoc.rdoc_dir = 'html'
|
46
|
+
rdoc.title = "JavaClass javaclass-#{jcversion} Documentation"
|
47
|
+
rdoc.main = 'Readme.txt'
|
48
|
+
rdoc.rdoc_files.include 'Readme.txt', 'lib/**/*.rb', 'history.txt'
|
49
|
+
end
|
50
|
+
|
51
|
+
task :default => :test
|
data/Readme.txt
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
JavaClass
|
2
|
+
by {Peter Kofler}[http://kofler.dot.at/work/]
|
3
|
+
|
4
|
+
* {Homepage}[http://javaclass.rubyforge.org/]
|
5
|
+
* {Rubyforge Project}[http://rubyforge.org/projects/javaclass]
|
6
|
+
* email bruno41 at rubyforge dot org
|
7
|
+
|
8
|
+
== Description
|
9
|
+
|
10
|
+
JavaClass (Java Class File Parser) is a
|
11
|
+
parser and disassembler for Java class files, similar to the javap command.
|
12
|
+
It provides access to the package, protected, and public fields and methods
|
13
|
+
of the classes passed to it together with a list of all outgoing references.
|
14
|
+
|
15
|
+
== Motivation
|
16
|
+
|
17
|
+
I am still doing Java most of the time. I used to be quite enthusiatic about
|
18
|
+
it, but after 9 years I can see the advantages of being a polyglot. So I use
|
19
|
+
Ruby for all kind of stuff, just for fun. Recently I needed some Java class
|
20
|
+
analysis and happened to write it with Ruby. As I am a puritan, I did not
|
21
|
+
want to call javap from my script, so I started disassembling the class files,
|
22
|
+
which might be the the base for some serious static code analysis tools. (I
|
23
|
+
started adding methods to that end...)
|
24
|
+
|
25
|
+
== Install
|
26
|
+
|
27
|
+
sudo gem install javaclass
|
28
|
+
|
29
|
+
== Usage
|
30
|
+
|
31
|
+
require 'javaclass'
|
32
|
+
|
33
|
+
clazz = JavaClass.parse('packagename/Public.class')
|
34
|
+
clazz.version # => 50.0
|
35
|
+
clazz.constant_pool.items[1] # => packagename/Public
|
36
|
+
clazz.access_flags.public? # => true
|
37
|
+
clazz.this_class # => packagename/Public
|
38
|
+
clazz.super_class # => java/lang/Object
|
39
|
+
clazz.references.referenced_methods[0] # => java/lang/Object.<init>:()V
|
40
|
+
|
41
|
+
== Requirements
|
42
|
+
|
43
|
+
JavaClass does not depend on any other installed libraries or gems.
|
44
|
+
|
45
|
+
== License
|
46
|
+
|
47
|
+
Same as Ruby.
|
data/history.txt
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
=== 0.0.2 08/04/2009
|
2
|
+
|
3
|
+
* refactored code to smaller objects for version and constant pool
|
4
|
+
* fixed float and double constant pool items
|
5
|
+
* added tests
|
6
|
+
* continued implementation with class names and references
|
7
|
+
|
8
|
+
=== 0.0.1 01/03/2009
|
9
|
+
|
10
|
+
* initial version extracted from ClassList project
|
11
|
+
* reads the class version and package/public flag of a class
|
12
|
+
* understands the constant pool but does not use it
|
13
|
+
|
14
|
+
=== Planned
|
15
|
+
|
16
|
+
* continue function for fields and methods
|
17
|
+
* implement
|
18
|
+
* dump to return the class same as javap
|
19
|
+
* isAnnotation (checks version>=5 & superclass)
|
20
|
+
* are Annotation extendable, if so how do they look? (javap)
|
21
|
+
* isEnum (checks version>=5 & superclass)
|
22
|
+
* are Enums extendable (yes), if so how do they look? (javap)
|
23
|
+
* test
|
24
|
+
* test with all classes of all JDKs against javap from this version
|
25
|
+
* Java 1.0 - private protected fields.
|
26
|
+
* Java 1.0 - http://www.javaworld.com/javaworld/javaqa/1999-06/03-synchronized.html
|
27
|
+
* think about
|
28
|
+
* "metric" if all referencing classes of a class is another package, propose moving it there.
|
29
|
+
* add zip gem to load classes directly from the classpath (i.e. the jars), add gem dependency
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'javaclass/string_ux'
|
2
|
+
|
3
|
+
module JavaClass
|
4
|
+
|
5
|
+
# The access flags of a class or interface.
|
6
|
+
# Author:: Peter Kofler
|
7
|
+
class AccessFlags
|
8
|
+
|
9
|
+
# Access flags as defined by JVM spec.
|
10
|
+
ACC_PUBLIC = 0x0001
|
11
|
+
ACC_FINAL = 0x0010
|
12
|
+
ACC_SUPER = 0x0020 # old invokespecial instruction semantics
|
13
|
+
ACC_INTERFACE = 0x0200
|
14
|
+
ACC_ABSTRACT = 0x0400
|
15
|
+
ACC_OTHER = 0xffff ^ ACC_PUBLIC ^ ACC_FINAL ^ ACC_SUPER ^ ACC_INTERFACE ^ ACC_ABSTRACT
|
16
|
+
|
17
|
+
def initialize(data, pos)
|
18
|
+
@flags = data.u2(pos)
|
19
|
+
raise "inconsistent flags #{flags}" if abstract? && final?
|
20
|
+
raise "inconsistent flags #{flags}" if interface? && (!abstract? || final?)
|
21
|
+
raise "inconsistent flags #{flags}" if (@flags & ACC_OTHER) != 0
|
22
|
+
end
|
23
|
+
|
24
|
+
# Return +true+ if the class is public.
|
25
|
+
def public?
|
26
|
+
(@flags & ACC_PUBLIC) != 0
|
27
|
+
end
|
28
|
+
alias accessible? public?
|
29
|
+
|
30
|
+
def final?
|
31
|
+
(@flags & ACC_FINAL) != 0
|
32
|
+
end
|
33
|
+
|
34
|
+
def abstract?
|
35
|
+
(@flags & ACC_ABSTRACT) != 0
|
36
|
+
end
|
37
|
+
|
38
|
+
def interface?
|
39
|
+
(@flags & ACC_INTERFACE) != 0
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module JavaClass
|
2
|
+
|
3
|
+
# The +CAFEBABE+ magic of a class file.
|
4
|
+
# Author:: Peter Kofler
|
5
|
+
class ClassMagic
|
6
|
+
|
7
|
+
CAFE_BABE = "\xCA\xFE\xBA\xBE"
|
8
|
+
|
9
|
+
# Check the class magic in the _data_ beginning at position _start_ (which is usually 0).
|
10
|
+
def initialize(data, start=0)
|
11
|
+
# "parsing"
|
12
|
+
@bytes = data[start..start+3]
|
13
|
+
end
|
14
|
+
|
15
|
+
# Return +true+ if the data was valid, i.e. if the class started with +CAFEBABE+.
|
16
|
+
def valid?
|
17
|
+
@bytes == CAFE_BABE
|
18
|
+
end
|
19
|
+
|
20
|
+
# Return the value of the magic in this class.
|
21
|
+
def bytes
|
22
|
+
@bytes.dup
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'javaclass/string_ux'
|
2
|
+
|
3
|
+
module JavaClass
|
4
|
+
|
5
|
+
# Version of a class file.
|
6
|
+
# Author:: Peter Kofler
|
7
|
+
# See:: http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html#75883
|
8
|
+
class ClassVersion
|
9
|
+
|
10
|
+
attr_reader :minor
|
11
|
+
attr_reader :major
|
12
|
+
|
13
|
+
# Extract the class version from the bytes _data_ starting at position _start_ (which is usually 4).
|
14
|
+
def initialize(data, start=4)
|
15
|
+
# parsing
|
16
|
+
@minor = data.u2(start)
|
17
|
+
@major = data.u2(start+2)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Return the class file version as +major+.+minor+ string like 48.0 (Java 1.4) or 50.0 (Java 6).
|
21
|
+
def to_s
|
22
|
+
"#{@major}.#{@minor}"
|
23
|
+
end
|
24
|
+
|
25
|
+
# Return the version as +major+.+minor+ float.
|
26
|
+
def to_f
|
27
|
+
if @minor <= 0
|
28
|
+
denom = 1.0
|
29
|
+
else
|
30
|
+
denom = 1.0 * 10**(Math.log10(@minor).floor + 1)
|
31
|
+
end
|
32
|
+
|
33
|
+
@major + @minor/denom
|
34
|
+
end
|
35
|
+
|
36
|
+
# Return a debug output of this version.
|
37
|
+
def dump
|
38
|
+
[" minor version: #{@minor}", " major version: #{@major}"]
|
39
|
+
end
|
40
|
+
|
41
|
+
# Return the JDK version corresponding to this version like "1.6" or "unknown" if none matched.
|
42
|
+
def jdk_version
|
43
|
+
v = to_f
|
44
|
+
if v >= 45.0 && v <= 45.3 # 1.0.2 supports class file format versions 45.0 through 45.3 inclusive.
|
45
|
+
'1.0'
|
46
|
+
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
|
47
|
+
'1.1'
|
48
|
+
elsif v == 46.0 # JDK 1.2=46
|
49
|
+
'1.2'
|
50
|
+
elsif v == 47.0 # JDK 1.3=47
|
51
|
+
'1.3'
|
52
|
+
elsif v == 48.0 # JDK 1.4=48
|
53
|
+
'1.4'
|
54
|
+
elsif v == 49.0 # J2SE 5.0=49
|
55
|
+
'1.5'
|
56
|
+
elsif v == 50.0 # J2SE 6.0=50
|
57
|
+
'1.6'
|
58
|
+
else
|
59
|
+
'unknown'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'javaclass/string_ux'
|
2
|
+
require 'javaclass/constants/value'
|
3
|
+
require 'javaclass/constants/single_reference'
|
4
|
+
require 'javaclass/constants/double_reference'
|
5
|
+
|
6
|
+
module JavaClass
|
7
|
+
|
8
|
+
# Container of the constant pool's constants.
|
9
|
+
# Author:: Peter Kofler
|
10
|
+
class ConstantPool
|
11
|
+
|
12
|
+
# Types of constants by their +tag+.
|
13
|
+
CONSTANT_TYPE_TAGS = {
|
14
|
+
CLASS_TAG = 7 => Constants::ConstantClass,
|
15
|
+
FIELD_TAG = 9 => Constants::ConstantField,
|
16
|
+
METHOD_TAG = 10 => Constants::ConstantMethod,
|
17
|
+
INTERFACE_METHOD_TAG = 11 => Constants::ConstantInterfaceMethod,
|
18
|
+
STRING_TAG = 8 => Constants::ConstantString,
|
19
|
+
INT_TAG = 3 => Constants::ConstantInt,
|
20
|
+
FLOAT_TAG = 4 => Constants::ConstantFloat,
|
21
|
+
LONG_TAG = 5 => Constants::ConstantLong,
|
22
|
+
DOUBLE_TAG = 6 => Constants::ConstantDouble,
|
23
|
+
NAME_AND_TYPE_TAG = 12 => Constants::ConstantNameAndType,
|
24
|
+
ASCIZ_TAG = 1 => Constants::ConstantAsciz,
|
25
|
+
}
|
26
|
+
|
27
|
+
# Size of the whole constant pool in bytes.
|
28
|
+
attr_reader :size
|
29
|
+
|
30
|
+
# Parse the constant pool from the bytes _data_ beginning at position _start_ (which is usually 8).
|
31
|
+
def initialize(data, start=8)
|
32
|
+
@pool = {} # cnt (fixnum) => constant
|
33
|
+
|
34
|
+
# parsing
|
35
|
+
@item_count = data.u2(start)
|
36
|
+
pos = start + 2
|
37
|
+
cnt = 1
|
38
|
+
while cnt <= @item_count-1
|
39
|
+
|
40
|
+
type = CONSTANT_TYPE_TAGS[data.u1(pos)]
|
41
|
+
unless type
|
42
|
+
#puts dump.join("\n")
|
43
|
+
raise "const ##{cnt} = unknown constant pool tag #{data[pos]} at pos #{pos} in class"
|
44
|
+
end
|
45
|
+
|
46
|
+
constant = type.new(@pool, data, pos)
|
47
|
+
@pool[cnt] = constant
|
48
|
+
pos += constant.size
|
49
|
+
cnt += constant.slots
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
@size = pos - start
|
54
|
+
end
|
55
|
+
|
56
|
+
# Return the number of pool items. This number might be larger than +items+ available,
|
57
|
+
# because +long+ and +double+ constants take two slots.
|
58
|
+
def item_count
|
59
|
+
@item_count-1
|
60
|
+
end
|
61
|
+
|
62
|
+
# Return the _index_'th pool item. _index_ is the real index in the pool which may skip numbers.
|
63
|
+
def[](index)
|
64
|
+
@pool[index]
|
65
|
+
end
|
66
|
+
|
67
|
+
# Return an array of the ordered list of constants.
|
68
|
+
def items
|
69
|
+
@pool.keys.sort.collect { |k| self[k] }
|
70
|
+
end
|
71
|
+
|
72
|
+
# Return an array of all constants of the given _tags_ types.
|
73
|
+
def find(*tags)
|
74
|
+
items.find_all { |item| tags.include? item.tag }
|
75
|
+
end
|
76
|
+
|
77
|
+
# Return a debug output of the whole pool.
|
78
|
+
def dump
|
79
|
+
[" Constant pool:"] + @pool.keys.sort.collect { |k| "const ##{k} = #{self[k].dump}"}
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'javaclass/string_ux'
|
2
|
+
|
3
|
+
module JavaClass
|
4
|
+
module Constants # :nodoc:
|
5
|
+
|
6
|
+
# Superclass of constant values in the constant pool. Every constant has a +name+, a +tag+ and a +size+ in bytes.
|
7
|
+
# Author:: Peter Kofler
|
8
|
+
class Base
|
9
|
+
|
10
|
+
attr_reader :name
|
11
|
+
attr_reader :tag
|
12
|
+
attr_reader :size
|
13
|
+
attr_reader :slots
|
14
|
+
|
15
|
+
# Set default constants.
|
16
|
+
def initialize(name=nil)
|
17
|
+
@name = self.class.to_s[/::[^:]+$/][10..-1] # skip modules (::) and "Constant"
|
18
|
+
@name = name if name
|
19
|
+
@size = 3
|
20
|
+
@slots = 1
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return part of debug output.
|
24
|
+
def dump
|
25
|
+
"#{@name}\t" # #{@tag}
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'javaclass/constants/single_reference'
|
2
|
+
|
3
|
+
module JavaClass
|
4
|
+
module Constants # :nodoc:
|
5
|
+
|
6
|
+
# Superclass of double reference constants like +ConstantField+ (+FieldRef+) in the constant pool.
|
7
|
+
# Author:: Peter Kofler
|
8
|
+
class DoubleReference < SingleReference
|
9
|
+
|
10
|
+
attr_reader :second_index
|
11
|
+
|
12
|
+
# Define a double reference into _pool_ from _data_ beginning at _start_
|
13
|
+
def initialize(pool, data, start, name=nil)
|
14
|
+
super(pool, data, start, name)
|
15
|
+
@size = 5
|
16
|
+
|
17
|
+
@second_index = data.u2(start+3)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Return the second value, which is the referenced value from the pool.
|
21
|
+
def second_value
|
22
|
+
get(@second_index)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Return the value, which are both referenced values from the pool.
|
26
|
+
def to_s
|
27
|
+
"#{super}.#{second_value}"
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return part of debug output.
|
31
|
+
def dump
|
32
|
+
"#{@name}\t##{@first_index}.##{@second_index};\t// #{to_s}"
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
class ConstantField < DoubleReference
|
38
|
+
alias class_index first_index
|
39
|
+
alias name_and_type_index second_index
|
40
|
+
def initialize(pool, data, start)
|
41
|
+
super(pool, data, start)
|
42
|
+
end
|
43
|
+
alias class_name first_value
|
44
|
+
end
|
45
|
+
|
46
|
+
class ConstantMethod < DoubleReference
|
47
|
+
alias class_index first_index
|
48
|
+
alias name_and_type_index second_index
|
49
|
+
def initialize(pool, data, start)
|
50
|
+
super(pool, data, start)
|
51
|
+
end
|
52
|
+
alias class_name first_value
|
53
|
+
end
|
54
|
+
|
55
|
+
class ConstantInterfaceMethod < DoubleReference
|
56
|
+
alias class_index first_index
|
57
|
+
alias name_and_type_index second_index
|
58
|
+
def initialize(pool, data, start)
|
59
|
+
super(pool, data, start)
|
60
|
+
end
|
61
|
+
alias class_name first_value
|
62
|
+
end
|
63
|
+
|
64
|
+
class ConstantNameAndType < DoubleReference
|
65
|
+
alias name_index first_index
|
66
|
+
alias descriptor_index second_index
|
67
|
+
def initialize(pool, data, start)
|
68
|
+
super(pool, data, start)
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_s
|
72
|
+
"#{get(name_index)}:#{get(descriptor_index)}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def dump
|
76
|
+
"#{@name}\t##{name_index}:##{descriptor_index};// #{to_s}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'javaclass/constants/base'
|
2
|
+
|
3
|
+
module JavaClass
|
4
|
+
module Constants # :nodoc:
|
5
|
+
|
6
|
+
# Superclass of single reference constants like +ConstantClass+ (+Class+) in the constant pool.
|
7
|
+
# Author:: Peter Kofler
|
8
|
+
class SingleReference < Base
|
9
|
+
|
10
|
+
attr_reader :first_index
|
11
|
+
|
12
|
+
# Define a single reference into _pool_ from _data_ beginning at _start_
|
13
|
+
def initialize(pool, data, start, name=nil)
|
14
|
+
super(name)
|
15
|
+
@tag = data.u1(start)
|
16
|
+
|
17
|
+
@enclosing_pool = pool
|
18
|
+
@first_index = data.u2(start+1)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Return the value, which is the referenced value from the pool.
|
22
|
+
def first_value
|
23
|
+
get(@first_index)
|
24
|
+
end
|
25
|
+
alias to_s first_value
|
26
|
+
|
27
|
+
# Return part of debug output.
|
28
|
+
def dump
|
29
|
+
super + "##{@first_index};\t// #{to_s}"
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
# Get a reference _ref_ from the +enclosing_pool+
|
35
|
+
def get(ref)
|
36
|
+
@enclosing_pool[ref].to_s
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
class ConstantClass < SingleReference
|
42
|
+
alias name_index first_index
|
43
|
+
def initialize(pool, data, start)
|
44
|
+
super(pool, data, start, "class")
|
45
|
+
end
|
46
|
+
alias class_name first_value
|
47
|
+
end
|
48
|
+
|
49
|
+
class ConstantString < SingleReference
|
50
|
+
alias string_index first_index
|
51
|
+
def initialize(pool, data, start)
|
52
|
+
super(pool, data, start)
|
53
|
+
end
|
54
|
+
alias string_value first_value
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'javaclass/constants/base'
|
2
|
+
|
3
|
+
module JavaClass
|
4
|
+
module Constants # :nodoc:
|
5
|
+
|
6
|
+
# Superclass of value constants like +ConstantInt+ (+Integer+) in the constant pool.
|
7
|
+
# Author:: Peter Kofler
|
8
|
+
class Value < Base
|
9
|
+
|
10
|
+
attr_reader :value
|
11
|
+
|
12
|
+
# Create a constant value with an optional downcase _name_
|
13
|
+
def initialize(name=self.class.to_s[/::[^:]+$/][10..-1].downcase)
|
14
|
+
super(name)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Return the value as string.
|
18
|
+
def to_s
|
19
|
+
@value.to_s
|
20
|
+
end
|
21
|
+
|
22
|
+
# Return part of debug output.
|
23
|
+
def dump
|
24
|
+
super + "#{@value}"
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
# Define a +value+ from _data_ beginning at position _start_ with the _size_ in bytes and _slots_ (1 or 2).
|
30
|
+
def get_value(data, start, size, slots=1)
|
31
|
+
@tag = data.u1(start)
|
32
|
+
@size = size
|
33
|
+
@slots = slots
|
34
|
+
|
35
|
+
data[start+1..start+size-1]
|
36
|
+
end
|
37
|
+
|
38
|
+
# Dummy method to "fix" unused warning of param _pool_ in Eclipse.
|
39
|
+
def silence_unused_warning(pool)
|
40
|
+
raise "pool is nil" unless pool
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
class ConstantInt < Value
|
46
|
+
def initialize(pool, data, start)
|
47
|
+
super()
|
48
|
+
silence_unused_warning(pool)
|
49
|
+
@value = get_value(data, start, 5).u4
|
50
|
+
end
|
51
|
+
|
52
|
+
def dump
|
53
|
+
super + ';'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class ConstantFloat < Value
|
58
|
+
def initialize(pool, data, start)
|
59
|
+
super()
|
60
|
+
silence_unused_warning(pool)
|
61
|
+
@value = get_value(data, start, 5).single
|
62
|
+
end
|
63
|
+
def to_s
|
64
|
+
super.upcase # sprintf('%E',@value)
|
65
|
+
end
|
66
|
+
def dump
|
67
|
+
super + 'f;'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class ConstantLong < Value
|
72
|
+
def initialize(pool, data, start)
|
73
|
+
super()
|
74
|
+
silence_unused_warning(pool)
|
75
|
+
@value = get_value(data, start, 9, 2).u8
|
76
|
+
end
|
77
|
+
def dump
|
78
|
+
super + 'l;'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class ConstantDouble < Value
|
83
|
+
def initialize(pool, data, start)
|
84
|
+
super()
|
85
|
+
silence_unused_warning(pool)
|
86
|
+
@value = get_value(data, start, 9, 2).double
|
87
|
+
end
|
88
|
+
def to_s
|
89
|
+
@value.to_s.upcase # sprintf('%E',@value)
|
90
|
+
end
|
91
|
+
def dump
|
92
|
+
super + 'd;'
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class ConstantAsciz < Value
|
97
|
+
alias string value
|
98
|
+
def initialize(pool, data, start)
|
99
|
+
super('Asciz')
|
100
|
+
silence_unused_warning(pool)
|
101
|
+
@tag = data.u1(start)
|
102
|
+
|
103
|
+
@length = data.u2(start+1)
|
104
|
+
@size = 3 + @length
|
105
|
+
@value = data[start+3..start+3+@length-1]
|
106
|
+
end
|
107
|
+
|
108
|
+
def dump
|
109
|
+
super + ';'
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|