unageanu-javaclass 0.2.1 → 0.4.0

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 (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