unageanu-javaclass 0.2.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/ChangeLog +5 -0
  2. data/javaclass.gemspec +18 -0
  3. data/lib/javaclass.rb +1 -0
  4. data/lib/javaclass/accessflag.rb +1 -1
  5. data/lib/javaclass/attribute.rb +293 -2
  6. data/lib/javaclass/base.rb +33 -4
  7. data/lib/javaclass/code.rb +78 -0
  8. data/lib/javaclass/constant.rb +2 -2
  9. data/lib/javaclass/member.rb +18 -18
  10. data/lib/javaclass/reader.rb +460 -6
  11. data/sample/caller.rb +95 -0
  12. data/sample/java_class/com/example/Constants.class +0 -0
  13. data/sample/java_class/com/example/Deprecated.class +0 -0
  14. data/sample/java_class/com/example/InnerClass.class +0 -0
  15. data/sample/java_class/com/example/InnerClassImpl.class +0 -0
  16. data/sample/java_class/com/example/Kitten.class +0 -0
  17. data/sample/java_class/com/example/ThrowsException.class +0 -0
  18. data/sample/java_class/com/example/annotation/Annotated.class +0 -0
  19. data/sample/java_class/com/example/annotation/AnnotatedMethod.class +0 -0
  20. data/sample/java_class/com/example/annotation/ClassAnnotationA.class +0 -0
  21. data/sample/java_class/com/example/annotation/HasDefaultValue.class +0 -0
  22. data/sample/java_class/com/example/annotation/RuntimeAnnotationA.class +0 -0
  23. data/sample/java_class/com/example/annotation/RuntimeAnnotationB.class +0 -0
  24. data/sample/java_class/com/example/annotation/RuntimeAnnotationC.class +0 -0
  25. data/sample/java_class/com/example/annotation/SourceAnnotationA.class +0 -0
  26. data/sample/java_class/com/example/caller/A.class +0 -0
  27. data/sample/java_class/com/example/caller/B.class +0 -0
  28. data/sample/java_class/com/example/caller/C.class +0 -0
  29. data/sample/java_class/com/example/caller/CallSample$A.class +0 -0
  30. data/sample/java_class/com/example/caller/CallSample$B.class +0 -0
  31. data/sample/java_class/com/example/caller/CallSample$C.class +0 -0
  32. data/sample/java_class/com/example/caller/CallSample.class +0 -0
  33. data/sample/java_class/com/example/caller/D.class +0 -0
  34. data/sample/java_class/com/example/code/Statement.class +0 -0
  35. data/sample/java_class/com/example/types/TypeVariables.class +0 -0
  36. data/sample/java_src/com/example/caller/A.java +19 -0
  37. data/sample/java_src/com/example/caller/B.java +7 -0
  38. data/sample/java_src/com/example/caller/C.java +7 -0
  39. data/sample/java_src/com/example/caller/CallSample.java +23 -0
  40. data/sample/java_src/com/example/caller/D.java +5 -0
  41. data/sample/java_src/com/example/code/Statement.java +41 -0
  42. data/sample/sample.rb +3 -3
  43. data/test/all_tests.rb +9 -0
  44. data/test/java_src/com/example/TestClass1.java +35 -0
  45. data/test/test_attribute.rb +150 -2
  46. data/test/test_class.rb +0 -1
  47. data/test/test_member.rb +4 -1
  48. metadata +44 -46
data/sample/caller.rb ADDED
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require "rubygems"
4
+ require "zip/zip"
5
+ require "javaclass"
6
+ require "kconv"
7
+
8
+ # クラスプール
9
+ class ClassPool
10
+ def initialize
11
+ @classes = {}
12
+ @classes_r = {}
13
+ end
14
+ def <<( jc )
15
+ jc.methods.each {|m|
16
+ key = jc.name + "#" + m.name + m.descriptor
17
+ @classes[key] ||= []
18
+ next unless m.attributes.key? "Code"
19
+ m.attributes["Code"].codes.each {|code|
20
+ case code.opcode
21
+ when 0xB6,0xB7,0xB8,0xB9
22
+ m = jc.get_constant( code.operands[0].value )
23
+ add_caller( key,
24
+ m.class_name.name + "#" +
25
+ m.name_and_type.name + m.name_and_type.descriptor
26
+ )
27
+ when 0xB2,0xB3,0xB4,0xB5
28
+ m = jc.get_constant( code.operands[0].value )
29
+ add_caller( key,
30
+ m.class_name.name + "." + m.name_and_type.name
31
+ )
32
+ end
33
+ }
34
+ }
35
+ end
36
+ attr_reader :classes
37
+ attr_reader :classes_r
38
+ private
39
+ def add_caller( from, to )
40
+ @classes[from] ||= []
41
+ @classes[from] << to
42
+ @classes_r[to] ||= []
43
+ @classes_r[to] << from
44
+ end
45
+ end
46
+
47
+ #ノード
48
+ class Node
49
+ def initialize( name )
50
+ @name = name
51
+ @children = []
52
+ end
53
+ def <<(child)
54
+ @children << child
55
+ end
56
+ def to_s( indent="" )
57
+ str = indent.dup
58
+ str << name.to_s << "\n"
59
+ child_indent = indent.gsub(/├/, "│").gsub(/└/, " ")
60
+ @children.each_index {|i|
61
+ next if children[i] == nil
62
+ tmp = child_indent + ( i >= children.length-1 ? "└" : "├" )
63
+ str << children[i].to_s( tmp )
64
+ }
65
+ return str
66
+ end
67
+ attr :name, true
68
+ attr :children, true
69
+ end
70
+
71
+ #呼び出し元を収集する。
72
+ def collect( pool, start, stop=[], node=nil, checked=[] )
73
+ node ||= Node.new(start.to_s)
74
+ return node unless pool.classes_r.key? start
75
+ pool.classes_r[start].each {|caller|
76
+ next if checked.find{|i| i==caller }
77
+ next if stop && stop.find{|i| caller =~ i }
78
+ checked.push start.to_s
79
+ node << collect( pool, caller, stop, Node.new( caller ), checked )
80
+ checked.pop
81
+ }
82
+ return node
83
+ end
84
+
85
+ # クラス情報を収集
86
+ pool = ClassPool.new
87
+ JavaClass::Utils.each_class( *ARGV[0].split(";") ){|jc|
88
+ pool << jc
89
+ }
90
+ # 呼び出し元をツリー表示
91
+ puts collect(pool, ARGV[1]).to_s
92
+
93
+
94
+
95
+
@@ -0,0 +1,19 @@
1
+ package com.example.caller;
2
+
3
+ public class A {
4
+ public void aaa() {
5
+ aaa2();
6
+ new B().bbb();
7
+ C.ccc();
8
+ }
9
+ void aaa2() {
10
+ aaa3();
11
+ }
12
+ private void aaa3() {
13
+ new D().ddd();
14
+ }
15
+ void aaa4() {
16
+ aaa3();
17
+ aaa4();
18
+ }
19
+ }
@@ -0,0 +1,7 @@
1
+ package com.example.caller;
2
+
3
+ public class B {
4
+ public void bbb() {
5
+ new D().ddd();
6
+ }
7
+ }
@@ -0,0 +1,7 @@
1
+ package com.example.caller;
2
+
3
+ public class C {
4
+ public static void ccc() {
5
+ new D().ddd();
6
+ }
7
+ }
@@ -0,0 +1,23 @@
1
+ package com.example.caller;
2
+
3
+ public class CallSample {
4
+ static class A {
5
+ void aaa() {
6
+ new B().bbb();
7
+ }
8
+ void aaa2() {
9
+ new C().ccc();
10
+ }
11
+ void aaa3() {
12
+ aaa2();
13
+ }
14
+ }
15
+ static class B {
16
+ void bbb() {
17
+ new C().ccc();
18
+ }
19
+ }
20
+ static class C {
21
+ void ccc() {}
22
+ }
23
+ }
@@ -0,0 +1,5 @@
1
+ package com.example.caller;
2
+
3
+ public class D {
4
+ public void ddd() {}
5
+ }
@@ -0,0 +1,41 @@
1
+ package com.example.code;
2
+
3
+ import java.net.URI;
4
+ import java.net.URISyntaxException;
5
+
6
+ public class Statement {
7
+
8
+ public void basic( int i ) {
9
+ if ( i < 10 ) System.out.println( "a" );
10
+ if ( i > 10 ) {
11
+ System.out.println( "b" );
12
+ } else {
13
+ System.out.println( "c" );
14
+ }
15
+ for ( int j=0;j<10;j++ ) {
16
+ System.out.println( j );
17
+ }
18
+ for ( String str : new String[]{"a","b","c"} ) {
19
+ System.out.println( str );
20
+ }
21
+ int j = 9;
22
+ while ( j-- > 10 ) {
23
+ System.out.println( j );
24
+ }
25
+ switch (i) {
26
+ case 1 : System.out.println( "1" ); break;
27
+ case 2 : System.out.println( "2" );
28
+ default : System.out.println( "3" );
29
+ }
30
+ try {
31
+ new URI( "http://foo" + i );
32
+ } catch ( RuntimeException e ) {
33
+ e.printStackTrace();
34
+ } catch (URISyntaxException e) {
35
+ e.printStackTrace();
36
+ } finally {
37
+ System.out.println( "x" );
38
+ }
39
+ }
40
+
41
+ }
data/sample/sample.rb CHANGED
@@ -17,11 +17,11 @@ require "kconv"
17
17
  "./java_class/com/example/Deprecated.class",
18
18
  "./java_class/com/example/annotation/Annotated.class",
19
19
  "./java_class/com/example/annotation/HasDefaultValue.class",
20
- "./java_class/com/example/annotation/AnnotatedMethod.class"
20
+ "./java_class/com/example/annotation/AnnotatedMethod.class",
21
+ "./java_class/com/example/code/Statement.class"
21
22
  ].each { |c|
22
23
  open( c, "r+b" ) {|io|
23
24
  jc = JavaClass.from io
24
- puts jc.to_s.tosjis
25
- puts ""
25
+ puts jc.to_s
26
26
  }
27
27
  }
data/test/all_tests.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'runit/testcase'
3
+ require 'runit/cui/testrunner'
4
+
5
+ require 'test_access_flag'
6
+ require 'test_attribute'
7
+ require 'test_class'
8
+ require 'test_constant'
9
+ require 'test_member'
@@ -3,6 +3,8 @@ package com.example;
3
3
  import java.io.Closeable;
4
4
  import java.io.IOException;
5
5
  import java.io.Serializable;
6
+ import java.net.URI;
7
+ import java.net.URISyntaxException;
6
8
  import java.util.ArrayList;
7
9
  import java.util.List;
8
10
 
@@ -25,4 +27,37 @@ implements Serializable, Closeable{
25
27
  static boolean booleanConstant = false;
26
28
 
27
29
  public void close() throws IOException {}
30
+
31
+ public void statement( int i ) {
32
+ if ( i < 10 ) System.out.println( "a" );
33
+ if ( i > 10 ) {
34
+ System.out.println( "b" );
35
+ } else {
36
+ System.out.println( "c" );
37
+ }
38
+ for ( int j=0;j<10;j++ ) {
39
+ System.out.println( j );
40
+ }
41
+ for ( String str : new String[]{"a","b","c"} ) {
42
+ System.out.println( str );
43
+ }
44
+ int j = 9;
45
+ while ( j-- > 10 ) {
46
+ System.out.println( j );
47
+ }
48
+ switch (i) {
49
+ case 1 : System.out.println( "1" ); break;
50
+ case 2 : System.out.println( "2" );
51
+ default : System.out.println( "3" );
52
+ }
53
+ try {
54
+ new URI( "http://foo" + i );
55
+ } catch ( RuntimeException e ) {
56
+ e.printStackTrace();
57
+ } catch (URISyntaxException e) {
58
+ e.printStackTrace();
59
+ } finally {
60
+ System.out.println( "x" );
61
+ }
62
+ }
28
63
  }
@@ -36,6 +36,7 @@ module JavaClass
36
36
  @java_class.constant_pool[14] = UTF8Constant.new( @java_class, Constant::CONSTANT_Utf8, "LineNumberTable" )
37
37
  @java_class.constant_pool[15] = UTF8Constant.new( @java_class, Constant::CONSTANT_Utf8, "LocalVariableTable" )
38
38
  @java_class.constant_pool[16] = UTF8Constant.new( @java_class, Constant::CONSTANT_Utf8, "LocalVariableTypeTable" )
39
+ @java_class.constant_pool[17] = UTF8Constant.new( @java_class, Constant::CONSTANT_Utf8, "StackMapTable" )
39
40
 
40
41
  @java_class.constant_pool[30] = UTF8Constant.new( @java_class, Constant::CONSTANT_Utf8, "aaa" )
41
42
  @java_class.constant_pool[31] = UTF8Constant.new( @java_class, Constant::CONSTANT_Utf8, "bbb" )
@@ -274,12 +275,13 @@ module JavaClass
274
275
  def test_CodeAttribute
275
276
 
276
277
  exceptions = [Excpetion.new( @java_class, 1, 10, 16, 103 )]
277
- attr = CodeAttribute.new( @java_class, 13, 10, 8, [0x05, 0x06], exceptions, {} )
278
+ codes = [Code.new(@java_class, 0, 0x05), Code.new(@java_class, 1, 0x06)]
279
+ attr = CodeAttribute.new( @java_class, 13, 10, 8, codes, exceptions, {} )
278
280
  assert_attribute( attr ) {|a|
279
281
  assert_equal a.name, "Code"
280
282
  assert_equal a.max_stack, 10
281
283
  assert_equal a.max_locals, 8
282
- assert_equal a.codes, [0x05, 0x06]
284
+ assert_equal a.codes, codes
283
285
  assert_equal a.exception_table, exceptions
284
286
  assert_equal a.attributes, {}
285
287
  assert_equal a.dump, "000D0000 0016000A 00080000 00020506\n00010001 000A0010 00670000"
@@ -896,6 +898,139 @@ module JavaClass
896
898
  assert_equal a.to_bytes, [0x00, 0x66, 0x00, 0x65, 0x00, 0x1F, 0x02, 0x01]
897
899
  }
898
900
  end
901
+
902
+ #
903
+ #=== StackMapTableAttributeのテスト
904
+ #
905
+ def test_StackMapTableAttribute
906
+
907
+ vals = [
908
+ VariableInfo.new( 3 ),
909
+ ObjectVariableInfo.new( 7, 10 ),
910
+ UninitializedVariableInfo .new( 8, 2 )
911
+ ]
912
+ entries = [
913
+ SameFrame.new( 3 ),
914
+ SameLocals1StackItemFrame.new( 70, [UninitializedVariableInfo .new( 8, 2 )] ),
915
+ FullFrame.new( 255, 3, vals, vals[0..1] )
916
+ ]
917
+ attr = StackMapTableAttribute.new( @java_class, 17, entries )
918
+ assert_attribute( attr ) {|a|
919
+ assert_equal a.name, "StackMapTable"
920
+ assert_equal a.stack_map_frame_entries, entries
921
+ assert_equal a.dump, "00110000 00190003 03460800 02FF0003\n"+
922
+ "00030307 000A0800 02000203 07000A"
923
+ assert_equal a.to_bytes, [
924
+ 0x00, 0x11, 0x00, 0x00, 0x00, 0x19, 0x00, 0x03, 0x03, 0x46, 0x08, 0x00,
925
+ 0x02, 0xFF, 0x00, 0x03, 0x00, 0x03, 0x03, 0x07, 0x00, 0x0A, 0x08, 0x00,
926
+ 0x02, 0x00, 0x02, 0x03, 0x07, 0x00, 0x0A
927
+ ]
928
+ }
929
+
930
+ attr.stack_map_frame_entries = []
931
+ assert_attribute( attr ) {|a|
932
+ assert_equal a.name, "StackMapTable"
933
+ assert_equal a.stack_map_frame_entries, []
934
+ assert_equal a.dump, "00110000 00020000"
935
+ assert_equal a.to_bytes, [0x00, 0x11, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00]
936
+ }
937
+ end
938
+
939
+ #
940
+ #=== StackMapFrameのテスト
941
+ #
942
+ def test_StackMapFrame
943
+
944
+ vals = [
945
+ VariableInfo.new( 3 ),
946
+ ObjectVariableInfo.new( 7, 10 ),
947
+ UninitializedVariableInfo .new( 8, 2 )
948
+ ]
949
+
950
+ value = SameFrame.new( 3 )
951
+ assert_stack_map_frame( value ) {|a|
952
+ assert_equal a.frame_type, 3
953
+ assert_equal a.dump, "03"
954
+ assert_equal a.to_bytes, [0x03]
955
+ }
956
+ value = SameLocals1StackItemFrame.new( 70, vals[0..0] )
957
+ assert_stack_map_frame( value ) {|a|
958
+ assert_equal a.frame_type, 70
959
+ assert_equal a.verification_type_info, vals[0..0]
960
+ assert_equal a.dump, "4603"
961
+ assert_equal a.to_bytes, [0x46, 0x03]
962
+ }
963
+ value = SameLocals1StackItemFrameExtended.new( 247, 3, vals[1..1] )
964
+ assert_stack_map_frame( value ) {|a|
965
+ assert_equal a.frame_type, 247
966
+ assert_equal a.offset_delta, 3
967
+ assert_equal a.verification_type_info, vals[1..1]
968
+ assert_equal a.dump, "F7000307 000A"
969
+ assert_equal a.to_bytes, [0xF7, 0x00, 0x03, 0x07, 0x00, 0x0A]
970
+ }
971
+ value = ChopFrame.new( 248, 5 )
972
+ assert_stack_map_frame( value ) {|a|
973
+ assert_equal a.frame_type, 248
974
+ assert_equal a.offset_delta, 5
975
+ assert_equal a.dump, "F80005"
976
+ assert_equal a.to_bytes, [0xF8, 0x00, 0x05]
977
+ }
978
+ value = SameFrameExtended.new( 251, 16 )
979
+ assert_stack_map_frame( value ) {|a|
980
+ assert_equal a.frame_type, 251
981
+ assert_equal a.offset_delta, 16
982
+ assert_equal a.dump, "FB0010"
983
+ assert_equal a.to_bytes, [0xFB, 0x00, 0x10]
984
+ }
985
+ value = AppendFrame.new( 254, 10, vals )
986
+ assert_stack_map_frame( value ) {|a|
987
+ assert_equal a.frame_type, 254
988
+ assert_equal a.offset_delta, 10
989
+ assert_equal a.verification_type_info, vals
990
+ assert_equal a.dump, "FE000A03 07000A08 0002"
991
+ assert_equal a.to_bytes, [0xFE, 0x00, 0x0A, 0x03, 0x07, 0x00, 0x0A, 0x08, 0x00, 0x02]
992
+ }
993
+ value = FullFrame.new( 255, 3, vals, vals[0..1] )
994
+ assert_stack_map_frame( value ) {|a|
995
+ assert_equal a.frame_type, 255
996
+ assert_equal a.offset_delta, 3
997
+ assert_equal a.verification_type_info_local, vals
998
+ assert_equal a.verification_type_info_stack, vals[0..1]
999
+ assert_equal a.dump, "FF000300 03030700 0A080002 00020307\n" +
1000
+ "000A"
1001
+ assert_equal a.to_bytes, [
1002
+ 0xFF, 0x00, 0x03, 0x00, 0x03, 0x03, 0x07, 0x00, 0x0A, 0x08, 0x00, 0x02,
1003
+ 0x00, 0x02, 0x03, 0x07, 0x00, 0x0A
1004
+ ]
1005
+ }
1006
+ end
1007
+
1008
+ #
1009
+ #=== VariableInfoのテスト
1010
+ #
1011
+ def test_VariableInfo
1012
+
1013
+ value = VariableInfo.new( 3 )
1014
+ assert_variable_info( value ) {|a|
1015
+ assert_equal a.tag, 3
1016
+ assert_equal a.dump, "03"
1017
+ assert_equal a.to_bytes, [0x03]
1018
+ }
1019
+ value = ObjectVariableInfo.new( 7, 10 )
1020
+ assert_variable_info( value ) {|a|
1021
+ assert_equal a.tag, 7
1022
+ assert_equal a.cpool_index, 10
1023
+ assert_equal a.dump, "07000A"
1024
+ assert_equal a.to_bytes, [0x07, 0x00, 0x0A]
1025
+ }
1026
+ value = UninitializedVariableInfo .new( 8, 2 )
1027
+ assert_variable_info( value ) {|a|
1028
+ assert_equal a.tag, 8
1029
+ assert_equal a.offset, 2
1030
+ assert_equal a.dump, "080002"
1031
+ assert_equal a.to_bytes, [0x08, 0x00, 0x02]
1032
+ }
1033
+ end
899
1034
 
900
1035
  def assert_attribute( a, &block )
901
1036
  assert_to_byte_and_read a, block, proc {|io|
@@ -950,6 +1085,19 @@ module JavaClass
950
1085
  JavaClass.read_local_variable_type( io, @java_class )
951
1086
  }
952
1087
  end
1088
+
1089
+ def assert_stack_map_frame( a, &block )
1090
+ assert_to_byte_and_read a, block, proc {|io|
1091
+ JavaClass.read_stack_map_frame_entry( io, @java_class )
1092
+ }
1093
+ end
1094
+
1095
+ def assert_variable_info( a, &block )
1096
+ assert_to_byte_and_read a, block, proc {|io|
1097
+ JavaClass.read_variable_info( io, @java_class )
1098
+ }
1099
+ end
1100
+
953
1101
  end
954
1102
 
955
1103
  end