OSDN Git Service

initial commit
authorIshio <ishio@ist.osaka-u.ac.jp>
Tue, 4 Aug 2015 02:47:58 +0000 (11:47 +0900)
committerIshio <ishio@ist.osaka-u.ac.jp>
Tue, 4 Aug 2015 02:47:58 +0000 (11:47 +0900)
118 files changed:
.classpath [new file with mode: 0755]
.project [new file with mode: 0755]
.settings/org.eclipse.core.resources.prefs [new file with mode: 0755]
.settings/org.eclipse.jdt.core.prefs [new file with mode: 0755]
doc/LICENSE.txt [new file with mode: 0755]
doc/technote-ja.md [new file with mode: 0755]
example/soba/example/dump/DumpMethodCall.java [new file with mode: 0755]
example/soba/example/dump/DumpMethodCallUsingVTA.java [new file with mode: 0755]
lib/asm-all-5.0.3-src.zip [new file with mode: 0755]
lib/asm-debug-all-5.0.3.jar [new file with mode: 0755]
lib/hamcrest-all-1.3-sources.jar [new file with mode: 0755]
lib/hamcrest-all-1.3.jar [new file with mode: 0755]
lib/trove-3.0.2-src.jar [new file with mode: 0755]
lib/trove-3.0.2.jar [new file with mode: 0755]
src/soba/core/ClassHierarchy.java [new file with mode: 0755]
src/soba/core/ClassInfo.java [new file with mode: 0755]
src/soba/core/ClassReadFailureException.java [new file with mode: 0755]
src/soba/core/FieldInfo.java [new file with mode: 0755]
src/soba/core/IClassFilter.java [new file with mode: 0755]
src/soba/core/IDynamicBindingResolver.java [new file with mode: 0755]
src/soba/core/JavaProgram.java [new file with mode: 0755]
src/soba/core/MD5.java [new file with mode: 0755]
src/soba/core/MethodInfo.java [new file with mode: 0755]
src/soba/core/method/CallSite.java [new file with mode: 0755]
src/soba/core/method/ControlDependence.java [new file with mode: 0755]
src/soba/core/method/DataDependence.java [new file with mode: 0755]
src/soba/core/method/DataFlowEdge.java [new file with mode: 0755]
src/soba/core/method/FieldAccess.java [new file with mode: 0755]
src/soba/core/method/ILocalVariableInfo.java [new file with mode: 0755]
src/soba/core/method/LocalVariables.java [new file with mode: 0755]
src/soba/core/method/OpcodeString.java [new file with mode: 0755]
src/soba/core/method/asm/DataFlowAnalyzer.java [new file with mode: 0755]
src/soba/core/method/asm/DataFlowInterpreter.java [new file with mode: 0755]
src/soba/core/method/asm/FastSourceInterpreter.java [new file with mode: 0755]
src/soba/core/method/asm/FastSourceValue.java [new file with mode: 0755]
src/soba/core/signature/MethodSignatureReader.java [new file with mode: 0755]
src/soba/core/signature/TypeConstants.java [new file with mode: 0755]
src/soba/core/signature/TypeResolver.java [new file with mode: 0755]
src/soba/core/signature/TypeVisitor.java [new file with mode: 0755]
src/soba/core/vta/CallResolver.java [new file with mode: 0755]
src/soba/core/vta/CallSiteVertices.java [new file with mode: 0755]
src/soba/core/vta/IAnalysisTarget.java [new file with mode: 0755]
src/soba/core/vta/ITopologicalVisitor.java [new file with mode: 0755]
src/soba/core/vta/MethodVertices.java [new file with mode: 0755]
src/soba/core/vta/NewVertices.java [new file with mode: 0755]
src/soba/core/vta/TopologicalOrderSearch.java [new file with mode: 0755]
src/soba/core/vta/TypeSet.java [new file with mode: 0755]
src/soba/core/vta/TypeSetManager.java [new file with mode: 0755]
src/soba/core/vta/VTAResolver.java [new file with mode: 0755]
src/soba/util/IntPairList.java [new file with mode: 0755]
src/soba/util/IntPairProc.java [new file with mode: 0755]
src/soba/util/IntPairSet.java [new file with mode: 0755]
src/soba/util/IntPairUtil.java [new file with mode: 0755]
src/soba/util/IntSetStack.java [new file with mode: 0755]
src/soba/util/IntStack.java [new file with mode: 0755]
src/soba/util/ObjectIdMap.java [new file with mode: 0755]
src/soba/util/Timer.java [new file with mode: 0755]
src/soba/util/debug/DumpClass.java [new file with mode: 0755]
src/soba/util/files/ClasspathUtil.java [new file with mode: 0755]
src/soba/util/files/Directory.java [new file with mode: 0755]
src/soba/util/files/IClassList.java [new file with mode: 0755]
src/soba/util/files/IClassListCallback.java [new file with mode: 0755]
src/soba/util/files/SingleFile.java [new file with mode: 0755]
src/soba/util/files/ZipFile.java [new file with mode: 0755]
src/soba/util/graph/DepthFirstSearch.java [new file with mode: 0755]
src/soba/util/graph/DirectedAcyclicGraph.java [new file with mode: 0755]
src/soba/util/graph/DirectedGraph.java [new file with mode: 0755]
src/soba/util/graph/DominanceTree.java [new file with mode: 0755]
src/soba/util/graph/GraphUtil.java [new file with mode: 0755]
src/soba/util/graph/IDepthFirstVisitor.java [new file with mode: 0755]
src/soba/util/graph/IDirectedGraph.java [new file with mode: 0755]
src/soba/util/graph/SingleRootDirectedGraph.java [new file with mode: 0755]
tests/soba/core/ClassHierarchyTest.java [new file with mode: 0755]
tests/soba/core/ClassInfoTest.java [new file with mode: 0755]
tests/soba/core/ExampleProgram.java [new file with mode: 0755]
tests/soba/core/FieldInfoTest.java [new file with mode: 0755]
tests/soba/core/JavaProgramTest.java [new file with mode: 0755]
tests/soba/core/MethodInfoTest.java [new file with mode: 0755]
tests/soba/core/method/ControlDependenceTest.java [new file with mode: 0755]
tests/soba/core/method/DataDependenceTest.java [new file with mode: 0755]
tests/soba/core/method/LocalVariablesTest.java [new file with mode: 0755]
tests/soba/core/method/asm/FastSourceValueTest.java [new file with mode: 0755]
tests/soba/core/signature/TypeResolverTest.java [new file with mode: 0755]
tests/soba/core/vta/CallSiteVerticesTest.java [new file with mode: 0755]
tests/soba/core/vta/MethodVerticesTest.java [new file with mode: 0755]
tests/soba/core/vta/NewVerticesTest.java [new file with mode: 0755]
tests/soba/core/vta/VTAResolverTest.java [new file with mode: 0755]
tests/soba/testdata/ControlDependenceCode.java [new file with mode: 0755]
tests/soba/testdata/DefUseTestData.java [new file with mode: 0755]
tests/soba/testdata/ObjectTransferCode.java [new file with mode: 0755]
tests/soba/testdata/ReflectionCode.java [new file with mode: 0755]
tests/soba/testdata/StatementUnitsData.java [new file with mode: 0755]
tests/soba/testdata/inheritance1/C.java [new file with mode: 0755]
tests/soba/testdata/inheritance1/D.java [new file with mode: 0755]
tests/soba/testdata/inheritance1/G.java [new file with mode: 0755]
tests/soba/testdata/inheritance1/I.java [new file with mode: 0755]
tests/soba/testdata/inheritance1/J.java [new file with mode: 0755]
tests/soba/testdata/inheritance1/K.java [new file with mode: 0755]
tests/soba/testdata/inheritance2/E.java [new file with mode: 0755]
tests/soba/testdata/inheritance2/F.java [new file with mode: 0755]
tests/soba/testdata/inheritance2/H.java [new file with mode: 0755]
tests/soba/testdata/inheritance2/L.java [new file with mode: 0755]
tests/soba/util/IntPairListTest.java [new file with mode: 0755]
tests/soba/util/IntPairSetTest.java [new file with mode: 0755]
tests/soba/util/IntPairUtilTest.java [new file with mode: 0755]
tests/soba/util/IntSetStackTest.java [new file with mode: 0755]
tests/soba/util/IntStackTest.java [new file with mode: 0755]
tests/soba/util/ObjectIdMapTest.java [new file with mode: 0755]
tests/soba/util/UtilForAssertThat.java [new file with mode: 0755]
tests/soba/util/files/ClasspathUtilTest.java [new file with mode: 0755]
tests/soba/util/files/DirectoryTest.java [new file with mode: 0755]
tests/soba/util/files/ZipFileTest.java [new file with mode: 0755]
tests/soba/util/graph/DepthFirstSearchTest.java [new file with mode: 0755]
tests/soba/util/graph/DirectedAcyclicGraphTest.java [new file with mode: 0755]
tests/soba/util/graph/DirectedGraphTest.java [new file with mode: 0755]
tests/soba/util/graph/DominanceTreeTest.java [new file with mode: 0755]
tests/soba/util/graph/GraphTestBase.java [new file with mode: 0755]
tests/soba/util/graph/SingleRootDirectedGraphTest.java [new file with mode: 0755]

diff --git a/.classpath b/.classpath
new file mode 100755 (executable)
index 0000000..6404e91
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<classpath>\r
+       <classpathentry excluding="**/.svn/*" kind="src" path="src"/>\r
+       <classpathentry excluding="**/.svn/*" kind="src" path="tests"/>\r
+       <classpathentry excluding="**/.svn/*" kind="src" path="example"/>\r
+       <classpathentry kind="lib" path="lib/hamcrest-all-1.3.jar" sourcepath="lib/hamcrest-all-1.3-sources.jar"/>\r
+       <classpathentry kind="lib" path="lib/trove-3.0.2.jar" sourcepath="lib/trove-3.0.2-src.jar"/>\r
+       <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>\r
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>\r
+       <classpathentry kind="lib" path="lib/asm-debug-all-5.0.3.jar" sourcepath="lib/asm-all-5.0.3-src.zip"/>\r
+       <classpathentry kind="output" path="bin"/>\r
+</classpath>\r
diff --git a/.project b/.project
new file mode 100755 (executable)
index 0000000..687fad1
--- /dev/null
+++ b/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<projectDescription>\r
+       <name>soba</name>\r
+       <comment></comment>\r
+       <projects>\r
+       </projects>\r
+       <buildSpec>\r
+               <buildCommand>\r
+                       <name>org.eclipse.jdt.core.javabuilder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+               <buildCommand>\r
+                       <name>edu.umd.cs.findbugs.plugin.eclipse.findbugsBuilder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+       </buildSpec>\r
+       <natures>\r
+               <nature>org.eclipse.jdt.core.javanature</nature>\r
+               <nature>edu.umd.cs.findbugs.plugin.eclipse.findbugsNature</nature>\r
+       </natures>\r
+</projectDescription>\r
diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs
new file mode 100755 (executable)
index 0000000..32340d5
--- /dev/null
@@ -0,0 +1,3 @@
+eclipse.preferences.version=1
+encoding//src/soba/milanova/outerdata/ReadOrWrite.java=SJIS
+encoding/<project>=UTF-8
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
new file mode 100755 (executable)
index 0000000..ace45ce
--- /dev/null
@@ -0,0 +1,12 @@
+eclipse.preferences.version=1\r
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled\r
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate\r
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8\r
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve\r
+org.eclipse.jdt.core.compiler.compliance=1.8\r
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate\r
+org.eclipse.jdt.core.compiler.debug.localVariable=generate\r
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate\r
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error\r
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error\r
+org.eclipse.jdt.core.compiler.source=1.8\r
diff --git a/doc/LICENSE.txt b/doc/LICENSE.txt
new file mode 100755 (executable)
index 0000000..e47bc8f
--- /dev/null
@@ -0,0 +1,66 @@
+\r
+The SOBA project is distributed under the MIT License.\r
+\r
+SOBA is dependent on three third party libraries: ASM, Hamcrest, and GNU Trove.\r
+ - ASM License is included in this file. \r
+ - Hamcrest is distributed under the BSD 3-Clause License.\r
+ - GNU Trove is distributed under the LGPL License.\r
+\r
+----------------------------------------------------------------------------\r
+The MIT License (MIT)\r
+\r
+Copyright (c) 2015 Takashi Ishio and Tomomi Hatano (Osaka University)\r
+\r
+Permission is hereby granted, free of charge, to any person obtaining a copy\r
+of this software and associated documentation files (the "Software"), to deal\r
+in the Software without restriction, including without limitation the rights\r
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
+copies of the Software, and to permit persons to whom the Software is\r
+furnished to do so, subject to the following conditions:\r
+\r
+The above copyright notice and this permission notice shall be included in\r
+all copies or substantial portions of the Software.\r
+\r
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
+THE SOFTWARE.\r
+----------------------------------------------------------------------------\r
+\r
+\r
+\r
+ASM License\r
+----------------------------------------------------------------------------\r
+Copyright (c) 2000-2011 INRIA, France Telecom\r
+All rights reserved.\r
+\r
+Redistribution and use in source and binary forms, with or without\r
+modification, are permitted provided that the following conditions\r
+are met:\r
+\r
+1. Redistributions of source code must retain the above copyright\r
+   notice, this list of conditions and the following disclaimer.\r
+\r
+2. Redistributions in binary form must reproduce the above copyright\r
+   notice, this list of conditions and the following disclaimer in the\r
+   documentation and/or other materials provided with the distribution.\r
+\r
+3. Neither the name of the copyright holders nor the names of its\r
+   contributors may be used to endorse or promote products derived from\r
+   this software without specific prior written permission.\r
+\r
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\r
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\r
+THE POSSIBILITY OF SUCH DAMAGE.\r
+----------------------------------------------------------------------------\r
diff --git a/doc/technote-ja.md b/doc/technote-ja.md
new file mode 100755 (executable)
index 0000000..0902abb
--- /dev/null
@@ -0,0 +1,51 @@
+
+# SOBA テクニカルノート
+
+## SOBA が持つ機能
+
+ * 指定ディレクトリ,あるいは JAR ファイルからクラス情報を読み込む機能.読み込まれたデータは JavaProgram オブジェクトとして表現されます.
+ * 読み込んだクラス群が持つメソッドに対して,バイトコードの命令をノードとした制御フローグラフ,制御依存関係,データ依存関係を取得することができます. 
+ * Class Hierarchy Analysis と Variable Type Analysis を使った動的束縛の解決
+
+
+## 解析時の注意点
+
+### バイトコードだけからでは,ソースコード位置を特定できないメソッドが存在する
+
+各メソッドのソースコード上での定義位置は `LineNumberTable` から知ることができるが,
+命令が1つもなく,戻り値が void 型であるようなメソッドは,命令がないために行番号も持たない.
+そのため,バイトコードだけからでは,メソッドの宣言位置を特定できない.
+
+
+
+### メソッドの実装が1つも存在しなければ動的束縛の解決結果は空になる
+
+CHA や VTA を使って「動的束縛の解決後」の呼び出し関係を取り出す場合は,
+メソッドの定義が存在するように,うまくクラスパスを設定しなくてはならない.
+対応するメソッドの定義が検索範囲に1つも含まれていない場合,
+動的束縛の解決結果(実際に呼ばれうるメソッドの集合)は空集合となる.
+呼び出し関係をグラフ化する場合などには,呼び出しが「なかったことになる」ため,注意が必要である.
+
+
+### Generic Types はまじめに扱っていない
+
+メソッドの引数に関する Generic Types は,何も考えず型パラメータの中身を文字列展開している.
+クラスの型パラメータ自体がそのままメソッドの引数に出ている可能性があるので,「すべて実際の型」とは考えてはいけない.
+
+ASM での Generics の使い方は,
+[IBM dW: クラスワーキング・ツールキット: ASMとジェネリックス](http://www.ibm.com/developerworks/jp/java/library/j-cwt02076/)と,
+[ASM API JavaDoc: SignatureVisitor](http://www.docjar.com/docs/api/org/objectweb/asm/signature/SignatureVisitor.html)クラスの内容を参照するとよい.
+
+
+### 異なるソースコードが同一バイトコードになる例が存在する
+
+`if (X && Y) {` と `if (X) { if (Y) {` という2つの記述方法は,前者だと2つの条件分岐命令の間に LABEL ノードが入らず,後者は入るために区別可能.
+
+`if ((X || !Y) && (!X || Y)) {` のように条件分岐が複雑になる(IF式中に合流点が生じる)と,
+OR で判定された結果が合流するための LABEL ノードが条件分岐命令間に生成されてしまい,IF文2つを入れ子で記述した場合とまったく同一のバイトコードになる.
+
+
+### INVOKEDYNAMIC の解析はできない
+
+ASM の機能としてはサポートされているが,SOBA でこの情報を活用できるようにはしていない.
+
diff --git a/example/soba/example/dump/DumpMethodCall.java b/example/soba/example/dump/DumpMethodCall.java
new file mode 100755 (executable)
index 0000000..a4e434c
--- /dev/null
@@ -0,0 +1,45 @@
+package soba.example.dump;\r
+\r
+import soba.core.ClassHierarchy;\r
+import soba.core.ClassInfo;\r
+import soba.core.JavaProgram;\r
+import soba.core.MethodInfo;\r
+import soba.core.method.CallSite;\r
+import soba.util.files.ClasspathUtil;\r
+\r
+public class DumpMethodCall {\r
+\r
+       public static void main(String[] args) {\r
+               long count = 0;\r
+               long t = System.currentTimeMillis();\r
+               int classCount = 0;\r
+               int methodCount = 0;\r
+               JavaProgram program = new JavaProgram(ClasspathUtil.getClassList(args));\r
+               ClassHierarchy ch = program.getClassHierarchy();\r
+               \r
+               for (ClassInfo c: program.getClasses()) {\r
+                       classCount++;\r
+                       for (MethodInfo m: c.getMethods()) {\r
+                               methodCount++;\r
+                               System.out.println(m.toLongString());\r
+                               for (CallSite cs: m.getCallSites()) {\r
+                                       MethodInfo[] callees = ch.resolveCall(cs);\r
+                                       if (callees.length > 0) {\r
+                                               for (MethodInfo callee: callees) {\r
+                                                       //System.out.println("  [inside] " + callee.toLongString());\r
+                                                       count++;\r
+                                               }\r
+                                       } else {\r
+                                               //System.out.println("  [outside] " + cs.toString());\r
+                                               count++;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               System.out.println(classCount + " classes");\r
+               System.out.println(methodCount + " methods");\r
+               System.out.println(count + " method calls");\r
+               System.out.println((System.currentTimeMillis() -t ) + "ms");\r
+       }\r
+\r
+}\r
diff --git a/example/soba/example/dump/DumpMethodCallUsingVTA.java b/example/soba/example/dump/DumpMethodCallUsingVTA.java
new file mode 100755 (executable)
index 0000000..8ec59f6
--- /dev/null
@@ -0,0 +1,33 @@
+package soba.example.dump;\r
+\r
+import soba.core.ClassInfo;\r
+import soba.core.JavaProgram;\r
+import soba.core.MethodInfo;\r
+import soba.core.method.CallSite;\r
+import soba.core.vta.CallResolver;\r
+import soba.util.files.ClasspathUtil;\r
+\r
+public class DumpMethodCallUsingVTA {\r
+\r
+       public static void main(String[] args) {\r
+               JavaProgram program = new JavaProgram(ClasspathUtil.getClassList(args));\r
+               CallResolver resolver = CallResolver.getVTA(program);\r
+               \r
+               for (ClassInfo c: program.getClasses()) {\r
+                       for (MethodInfo m: c.getMethods()) {\r
+                               System.out.println(m.toLongString());\r
+                               for (CallSite cs: m.getCallSites()) {\r
+                                       MethodInfo[] callees = resolver.resolveCall(cs);\r
+                                       if (callees.length > 0) {\r
+                                               for (MethodInfo callee: callees) {\r
+                                                       System.out.println("  [inside] " + callee.toLongString());\r
+                                               }\r
+                                       } else {\r
+                                               System.out.println("  [outside] " + cs.toString());\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+}\r
diff --git a/lib/asm-all-5.0.3-src.zip b/lib/asm-all-5.0.3-src.zip
new file mode 100755 (executable)
index 0000000..2e0e02a
Binary files /dev/null and b/lib/asm-all-5.0.3-src.zip differ
diff --git a/lib/asm-debug-all-5.0.3.jar b/lib/asm-debug-all-5.0.3.jar
new file mode 100755 (executable)
index 0000000..c531dea
Binary files /dev/null and b/lib/asm-debug-all-5.0.3.jar differ
diff --git a/lib/hamcrest-all-1.3-sources.jar b/lib/hamcrest-all-1.3-sources.jar
new file mode 100755 (executable)
index 0000000..77dfc35
Binary files /dev/null and b/lib/hamcrest-all-1.3-sources.jar differ
diff --git a/lib/hamcrest-all-1.3.jar b/lib/hamcrest-all-1.3.jar
new file mode 100755 (executable)
index 0000000..6f62ba0
Binary files /dev/null and b/lib/hamcrest-all-1.3.jar differ
diff --git a/lib/trove-3.0.2-src.jar b/lib/trove-3.0.2-src.jar
new file mode 100755 (executable)
index 0000000..efc7ddd
Binary files /dev/null and b/lib/trove-3.0.2-src.jar differ
diff --git a/lib/trove-3.0.2.jar b/lib/trove-3.0.2.jar
new file mode 100755 (executable)
index 0000000..12fb576
Binary files /dev/null and b/lib/trove-3.0.2.jar differ
diff --git a/src/soba/core/ClassHierarchy.java b/src/soba/core/ClassHierarchy.java
new file mode 100755 (executable)
index 0000000..4649f67
--- /dev/null
@@ -0,0 +1,575 @@
+package soba.core;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.HashMap;\r
+import java.util.HashSet;\r
+import java.util.LinkedList;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Queue;\r
+import java.util.Set;\r
+import java.util.Stack;\r
+\r
+import soba.core.method.CallSite;\r
+import soba.core.method.FieldAccess;\r
+\r
+/**\r
+ * This class represents a class hierarchy.\r
+ */\r
+public class ClassHierarchy implements IDynamicBindingResolver {\r
+\r
+       private boolean frozen;\r
+       \r
+       private static final String JAVA_LANG_OBJECT = "java" + ClassInfo.PACKAGE_SEPARATOR + "lang" + ClassInfo.PACKAGE_SEPARATOR + "Object"; \r
+       \r
+       private Map<String, ClassInfo> entries; // type -> a method list of the type\r
+       private Map<String, String> parentClass;   // type -> its super type \r
+       private Map<String, List<String>> parentInterfaces;  // type -> its interfaces\r
+       private Map<String, Set<String>> subtypes; // type -> a set of sub types \r
+       private Set<String> requestedClasses; // a set of type names that are queried but not found\r
+\r
+       private static List<String> EMPTY = Collections.unmodifiableList(new ArrayList<String>(0)); \r
+       \r
+       /**\r
+        * Creates a new <code>ClassHierarchy</code> instance.\r
+        */\r
+       public ClassHierarchy() {\r
+               frozen = false;\r
+               \r
+               subtypes = new HashMap<String, Set<String>>();\r
+               parentClass = new HashMap<String, String>();\r
+               parentInterfaces = new HashMap<String, List<String>>();\r
+               entries = new HashMap<String, ClassInfo>();\r
+\r
+               requestedClasses = new HashSet<String>();\r
+\r
+       }\r
+       \r
+       /**\r
+        * Resolves dynamic binding of a method invocation. \r
+        * @param cs is a method invocation\r
+        * @return an array of <code>MethodInfo</code> objects representing methods \r
+        * that might be executed by the invocation.\r
+        * The return value is an empty array if no method declaration \r
+        * matched to the invocation.\r
+        */\r
+       @Override\r
+       public MethodInfo[] resolveCall(CallSite cs) {\r
+               return resolveCall(cs.getClassName(), cs.getMethodName(), cs.getDescriptor(), !cs.isStaticOrSpecial());\r
+       }\r
+       \r
+       /**\r
+        * Resolves dynamic binding of a method invocation.\r
+        * @param className is a class name\r
+        * @param methodName is a method name\r
+        * @param methodDesc is a method descriptor\r
+        * @param dynamic is true if the invoked method is bound dynamically\r
+        * @return an array of <code>MethodInfo</code> objects representing methods \r
+        * that might be executed by the invocation.\r
+        * The return value is an empty array if no method declaration \r
+        * matched to the invocation.\r
+        */\r
+       public MethodInfo[] resolveCall(String className, String methodName, String methodDesc, boolean dynamic) {\r
+               if (!dynamic) {\r
+                       MethodInfo      m = resolveSpecialCall(className, methodName, methodDesc);\r
+                       return (m == null) ? new MethodInfo[0] : new MethodInfo[] {m};\r
+               } else {\r
+                       return resolveDynamicCall(className, methodName, methodDesc);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Resolve dynamic binding of a virtual method call.\r
+        * Please note that this method does not care about the detail of the method definition.\r
+        * If a static method or an undefined private method is specified, this method may return incorrect result.\r
+        * @param className specifies a class representing a receiver type.\r
+        * @param methodName specifies a method name.\r
+        * @param methodDesc specifies a method descriptor (without generics information). \r
+        * @return MethodInfo object representing the specified method.\r
+        * The return value may be an emtpy if the method is not found.\r
+        */\r
+       private MethodInfo[] resolveDynamicCall(String className, String methodName, String methodDesc) {\r
+               assert className != null && methodName != null && methodDesc != null : "Parameters cannot be null."; \r
+               String targetTypeName = className;\r
+               \r
+               // Find the declaration of the called method.\r
+               MethodInfo topDecl = findDeclaration(className, methodName, methodDesc);\r
+               if (topDecl == null) return new MethodInfo[0];  // Not found\r
+               \r
+               List<MethodInfo> result = new ArrayList<>(16);\r
+               if (topDecl.hasMethodBody()) result.add(topDecl);\r
+       \r
+               \r
+               // We explicitly avoid array types, because arrays are not included in ClassHierarchy.\r
+               if (!isArrayType(targetTypeName)) {\r
+                       // Find all implementation of the same method signature in subclasses.\r
+                       Set<String> checkedClasses = new HashSet<String>();\r
+                       Stack<String> classes = new Stack<String>();\r
+                       classes.add(targetTypeName);\r
+                       while (!classes.empty()) { \r
+                               String currentClass = classes.pop();\r
+       \r
+                               // skip the visited classes\r
+                               if (checkedClasses.contains(currentClass)) {\r
+                                       continue;\r
+                               }\r
+                               checkedClasses.add(currentClass);\r
+                               \r
+                               ClassInfo currentClassInfo = getClassInfo(currentClass);\r
+                               if (currentClassInfo != null) {\r
+                                       MethodInfo m = currentClassInfo.findMethod(methodName, methodDesc);\r
+                                       if (m != null) {\r
+                                               if (m.hasMethodBody() && (m != topDecl)) { \r
+                                                       // m overrides the target class.\r
+                                                       result.add(m); \r
+                                               }\r
+                                       }\r
+                                       \r
+                                       if ((m == null) || m.isOverridable()) { \r
+                                               // the method may be overridden by subclasses.\r
+                                               for (String c: getSubtypes(currentClass)) {\r
+                                                       if (m != null && m.isPackagePrivate()) {\r
+                                                               // A package-private method can be overridden by only classes in the same package.\r
+                                                               if (isSamePackage(c, currentClass)) {\r
+                                                                       classes.push(c);\r
+                                                               }\r
+                                                       } else {\r
+                                                               // Other methods can be overridden by sub-types.\r
+                                                               classes.push(c);\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               } else {\r
+                                       // Skip a class that is not included in the class hierarchy.\r
+                               }\r
+                       }\r
+                       \r
+               }\r
+       \r
+               MethodInfo[] resultArray = new MethodInfo[result.size()];\r
+               for (int i=0; i<result.size(); ++i) {\r
+                       resultArray[i] = result.get(i);\r
+               }\r
+               return resultArray;\r
+       }\r
+       \r
+       /**\r
+        * Resolve binding of a static/constructor call.\r
+        * @param className specifies a class representing a receiver type.\r
+        * @param methodName specifies a method name.\r
+        * @param methodDesc specifies a method descriptor (without generics information). \r
+        * @return MethodInfo object representing the specified method.\r
+        * The return value may be null if the method is not found.\r
+        */\r
+       public MethodInfo resolveSpecialCall(String className, String methodName, String methodDesc) {\r
+               MethodInfo m = findDeclaration(className, methodName, methodDesc);\r
+               if (m != null) {\r
+                       return m;\r
+               } else {\r
+                       return null;\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Finds method declaration specified by typeName and signature.\r
+        * @param typeName is fully qualified domain name of a class/interface.\r
+        * @param signatureId identifies a method by its name and parameters.  \r
+        * To obtain a method signature, use MethodUtil.getMethodSignature.\r
+        * @return MethodDecl object of the nearest ancestor (including the class specified by typeName) \r
+        * If no ancestor classes declare the method,\r
+        * MethodDecl comes from interfaces who declares the method.\r
+        * @throws ClassNotFoundException is thrown if typeName or its ancestors are not found in class hierarchy.\r
+        * @throws NoSuchMethodException is thrown if method is not found in ancestor classes and interfaces.\r
+        */\r
+       private MethodInfo findDeclaration(String className, String methodName, String methodDesc) {\r
+               // Find the nearest ancestor class implements the method\r
+               String currentClass = className;\r
+               while (currentClass != null) {\r
+                       ClassInfo currentClassInfo = getClassInfo(currentClass);\r
+                       if (currentClassInfo != null) {\r
+                               MethodInfo m = currentClassInfo.findMethod(methodName, methodDesc);\r
+                               if (m != null) {\r
+                                       return m;\r
+                               } else {\r
+                                       currentClass = getSuperClass(currentClass);\r
+                               }\r
+                       } else {\r
+                               if (isArrayType(currentClass)) { \r
+                                       currentClass = getSuperClass(currentClass);\r
+                                       continue;\r
+                               }\r
+                               return null;\r
+                       }\r
+               }\r
+               \r
+               // Search all interfaces\r
+               LinkedList<String> worklist = new LinkedList<String>();\r
+               currentClass = className;\r
+               while (currentClass != null) {\r
+                       worklist.addAll(getSuperInterfaces(currentClass));\r
+                       currentClass = getSuperClass(currentClass);\r
+               }\r
+               // Find a method declaration in the interfaces\r
+               while (!worklist.isEmpty()) {\r
+                       String interfaceName = worklist.pollFirst();\r
+                       ClassInfo currentClassInfo = getClassInfo(interfaceName);\r
+                       if (currentClassInfo != null) {\r
+                               MethodInfo m = currentClassInfo.findMethod(methodName, methodDesc);\r
+                               if (m != null) {\r
+                                       return m;\r
+                               } else {\r
+                                       // An interface may extend another interface.\r
+                                       worklist.addAll(getSuperInterfaces(interfaceName));\r
+                               }\r
+                       } else {\r
+                               if (isArrayType(interfaceName)) { \r
+                                       // ignore array types\r
+                                       continue;\r
+                               }\r
+                               // Skip an interfaceName that is not included in the class hierarchy.\r
+                               return null;\r
+                       }\r
+               }\r
+               \r
+               // Method not found at all\r
+               return null;\r
+       }\r
+       \r
+       /**\r
+        * Finds a accessed <code>FieldInfo</code> object.\r
+        * @param access specifies a field access instruction.\r
+        * @return a <code>FieldInfo</code> object.\r
+        */\r
+       public FieldInfo resolveField(FieldAccess access) {\r
+               String owner = resolveFieldOwner(access);\r
+               return getClassInfo(owner).findField(access.getFieldName(), access.getDescriptor());\r
+       }\r
+\r
+       /**\r
+        * Finds a class which has a field to be accessed. \r
+        * @param access specifies a field access instruction.\r
+        * @return the name of a class which has a specified field.\r
+        */\r
+       private String resolveFieldOwner(FieldAccess access) {\r
+               if (access.isStatic()) {\r
+                       return resolveStaticFieldOwner(access.getClassName(), access.getFieldName(), access.getDescriptor());\r
+               } else {\r
+                       return resolveInstanceFieldOwner(access.getClassName(), access.getFieldName(), access.getDescriptor());\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Finds a class which has an instance field to be accessed. \r
+        * @param className specifies a class name in a field access instruction.\r
+        * The class may inherit a field from its parent.\r
+        * @param fieldName \r
+        * @param fieldDesc\r
+        * @return a class name that defines the field specified by the arguments.\r
+        * The method may return null if an owner is not found.\r
+        */\r
+       public String resolveInstanceFieldOwner(String className, String fieldName, String fieldDesc) {\r
+       String current = className;\r
+               while (current != null) {\r
+                       ClassInfo c = getClassInfo(current);\r
+                       if (c != null) {\r
+                               if (c.findField(fieldName, fieldDesc) != null) {\r
+                                       return current;\r
+                               } else {\r
+                                       current = c.getSuperClass();\r
+                               }\r
+                       } else {\r
+                               // If a class is not registered, stop to resolve the owner.\r
+                               current = null;\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       /**\r
+        * Finds a class which has a static field to be accessed. \r
+        * @param className \r
+        * @param fieldName\r
+        * @param fieldDesc\r
+        * @return a class name.\r
+        * @see JVM Specification Section 5.4.3.2.\r
+        * If two public fields are accessible from the specified className and fieldName,\r
+        * javac reports the ambiguous field reference as an error.\r
+        */\r
+       public String resolveStaticFieldOwner(String className, String fieldName, String fieldDesc) {\r
+               // Search the current class\r
+               ClassInfo c = getClassInfo(className);\r
+               if (c == null) return null;\r
+               \r
+               if (c != null) {\r
+                       if (c.findField(fieldName, fieldDesc) != null) {\r
+                               return className;\r
+                       }\r
+               }\r
+\r
+               // If not defined, search interface\r
+               Stack<String> worklist = new Stack<String>();\r
+               worklist.push(className);\r
+               while (!worklist.isEmpty()) {\r
+                       String current = worklist.pop();\r
+                       c = getClassInfo(current);\r
+                       if (c != null) {\r
+                               if (c.findField(fieldName, fieldDesc) != null) {\r
+                                       return current;\r
+                               } else {\r
+                                       if (c.getInterfaces() != null) {\r
+                                               worklist.addAll(c.getInterfaces());\r
+                                       }\r
+                               }\r
+                       } else {\r
+                               // If c is not a registered class, ignore it. \r
+                       }\r
+               }\r
+               \r
+               // Recursively search a super class\r
+       c = getClassInfo(className);\r
+       if (c.getSuperClass() != null) return resolveStaticFieldOwner(c.getSuperClass(), fieldName, fieldDesc);\r
+       else return null;\r
+       }\r
+       \r
+       /**\r
+        * Prevents further modifications to the object.\r
+        */\r
+       public void freeze() {\r
+               assert !frozen: "ClassHierarchy is already frozen."; \r
+               frozen = true;\r
+       }\r
+       \r
+       /**\r
+        * @return true if this object is frozen. \r
+        */\r
+       public boolean isFrozen() {\r
+               return frozen;\r
+       }\r
+       \r
+       /**\r
+        * @return a set of class names which are requested \r
+        * by client methods, but not involved in this class hierarchy.\r
+        */\r
+       public Set<String> getRequestedClasses() {\r
+               return requestedClasses;\r
+       }\r
+       \r
+       /**\r
+        * @param className \r
+        * @return a <code>ClassInfo</code> object specified by the class name.\r
+        */\r
+       public ClassInfo getClassInfo(String className) {\r
+               ClassInfo c = entries.get(className);\r
+               if (c == null) {\r
+                       requestedClasses.add(className);                        \r
+               }\r
+               return c;\r
+       }\r
+       \r
+       /**\r
+        * @return the number of registered classes. \r
+        */\r
+       public int getClassCount() {\r
+               return entries.size();\r
+       }\r
+\r
+       /**\r
+        * @return the registered class names. \r
+        */\r
+       public Iterable<String> getClasses() {\r
+               return entries.keySet();\r
+       }\r
+       \r
+       /**\r
+        * Compare package names for the specified two types.\r
+        * @param typeName1 specifies a type to be compared.\r
+        * The type name must be registered to the class hierarchy. \r
+        * @param typeName2 also specifies a type to be comapred.\r
+        * @return true if the specified types belong to the same package.\r
+        */\r
+       public boolean isSamePackage(String typeName1, String typeName2) {\r
+               ClassInfo c1 = entries.get(typeName1);\r
+               ClassInfo c2 = entries.get(typeName2);\r
+               \r
+               if (c1 == null) requestedClasses.add(typeName1);\r
+               if (c2 == null) requestedClasses.add(typeName2);\r
+               \r
+               return (c1 != null)&&(c2 != null)&&(c1.getPackageName().equals(c2.getPackageName()));\r
+       }\r
+       \r
+       /**\r
+        * @param className specifies a class.\r
+        * @return a super class name for the specified class.\r
+        * This method returns null for "java/lang/Object". \r
+        * "java.lang.Object" is returned for an interface and an array type.\r
+        */\r
+       public String getSuperClass(String className) { \r
+               if (isArrayType(className)) return JAVA_LANG_OBJECT;\r
+               else {\r
+                       if (!parentClass.containsKey(className)) {\r
+                               requestedClasses.add(className);\r
+                       }\r
+                       return parentClass.get(className);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * @param className specifies a fully qualified class name. \r
+        * @return interfaces implemented by the specified class.\r
+        * If the class has no interfaces, this method returns an empty collection.\r
+        * If the class is an interface and it extends another interface B,\r
+        * B is regarded as a super-interface of the class.\r
+        * If the specified class has no interfaces, \r
+        * the result is an empty colleciton.\r
+        */\r
+       public Collection<String> getSuperInterfaces(String className) {\r
+               if (isArrayType(className)) return EMPTY;\r
+               else if (parentInterfaces.containsKey(className)) { \r
+                       return parentInterfaces.get(className);\r
+               } else {\r
+                       if (!entries.containsKey(className)) requestedClasses.add(className);\r
+                       return EMPTY;\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * List all direct and transitive super-types of a specified type. \r
+        * @param className specifies a fully qualified class name. \r
+        */\r
+       public Collection<String> listAllSuperTypes(String className) {\r
+               if (!entries.containsKey(className)) requestedClasses.add(className);\r
+\r
+               Set<String> classes = new HashSet<String>();\r
+               Queue<String> worklist = new LinkedList<String>();\r
+               worklist.add(className);\r
+               while (!worklist.isEmpty()) {\r
+                       String name = worklist.poll();\r
+                       String superClass = getSuperClass(name);\r
+                       if (superClass != null && !classes.contains(superClass)) {\r
+                               classes.add(superClass);\r
+                               worklist.add(superClass);\r
+                       }\r
+                       for (String s: getSuperInterfaces(name)) {\r
+                               if (s != null && !classes.contains(s)) {\r
+                                       classes.add(s);\r
+                                       worklist.add(s);\r
+                               }\r
+                       }\r
+               }\r
+               return classes;\r
+       }\r
+       \r
+       /**\r
+        * @param typeName specifies a type name.\r
+        * @return true if the type name represents array types.\r
+        */\r
+       public boolean isArrayType(String typeName) { \r
+               return typeName.endsWith("[]");\r
+       }\r
+\r
+       /**\r
+        * @return a collection of classes which extend/implement \r
+        * the specified type.\r
+        * The result may be an empty collection.\r
+        */\r
+       public Collection<String> getSubtypes(String typeName) {\r
+               if (!entries.containsKey(typeName)) requestedClasses.add(typeName);\r
+\r
+               if (subtypes.containsKey(typeName)) { \r
+                       return subtypes.get(typeName);\r
+               } else {\r
+                       return EMPTY;\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * @param typeNames\r
+        * @return a collection of all the sub-types for the specified types.\r
+        */\r
+       public Collection<String> getAllSubtypes(Iterable<String> typeNames) {\r
+               Stack<String> worklist = new Stack<String>();\r
+               for (String t: typeNames) {\r
+                       worklist.push(t);\r
+               }\r
+               \r
+               HashSet<String> visited = new HashSet<String>();\r
+               while (!worklist.isEmpty()) {\r
+                       String t = worklist.pop();\r
+                       if (visited.contains(t)) continue;\r
+                       \r
+                       visited.add(t);\r
+                       worklist.addAll(getSubtypes(t));\r
+               }\r
+               return visited;\r
+       }\r
+\r
+       /**\r
+        * This method registers a class info object to the hierarchy.\r
+        * This method calls registerSuperClass, registerSubtype and registerInterfaces. \r
+        * @param c is a registered <code>ClassInfo</code> object.\r
+        */\r
+       public void registerClass(ClassInfo c) {\r
+               if (frozen) {\r
+                       throw new FrozenHierarchyException();\r
+               }\r
+               entries.put(c.getClassName(), c);\r
+               registerSuperClass(c.getClassName(), c.getSuperClass());\r
+               registerSubtype(c.getClassName(), c.getSuperClass());\r
+               registerInterfaces(c.getClassName(), c.getInterfaces());\r
+               for (String interfaceName: c.getInterfaces()) {\r
+                       registerSubtype(c.getClassName(), interfaceName);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * This method allows developers to manually modify the class hierarchy.\r
+        * @param current specifies a class name. \r
+        * @param parent specifies the super class name of the current class.\r
+        */\r
+       public void registerSuperClass(String current, String parent) {\r
+               if (frozen) {\r
+                       throw new FrozenHierarchyException();\r
+               }\r
+               parentClass.put(current, parent);\r
+               \r
+       }\r
+\r
+       /**\r
+        * This method allows developers to manually modify the type hierarchy.\r
+        * A subtype of a class is a subclass.\r
+        * A subtype of an interface is an implementation class. \r
+        * @param typeName\r
+        * @param parentTypeName\r
+        */\r
+       public void registerSubtype(String typeName, String parentTypeName) {\r
+               if (frozen) {\r
+                       throw new FrozenHierarchyException();\r
+               }\r
+               if (subtypes.containsKey(parentTypeName)) {\r
+                       subtypes.get(parentTypeName).add(typeName);\r
+               } else {\r
+                       HashSet<String> types = new HashSet<String>();\r
+                       types.add(typeName);\r
+                       subtypes.put(parentTypeName, types);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * This method allows developers to manually modify the type hierarchy.\r
+        * @param current\r
+        * @param interfaces\r
+        */\r
+       public void registerInterfaces(String current, List<String> interfaces) {\r
+               if (frozen) {\r
+                       throw new FrozenHierarchyException();\r
+               }\r
+               parentInterfaces.put(current, interfaces);\r
+       }\r
+       \r
+       public class FrozenHierarchyException extends RuntimeException {\r
+               private static final long serialVersionUID = -8288161390304221032L;\r
+       }\r
+\r
+}\r
diff --git a/src/soba/core/ClassInfo.java b/src/soba/core/ClassInfo.java
new file mode 100755 (executable)
index 0000000..13f6c64
--- /dev/null
@@ -0,0 +1,281 @@
+package soba.core;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+import org.objectweb.asm.ClassReader;\r
+import org.objectweb.asm.MethodVisitor;\r
+import org.objectweb.asm.Opcodes;\r
+import org.objectweb.asm.commons.JSRInlinerAdapter;\r
+import org.objectweb.asm.tree.ClassNode;\r
+import org.objectweb.asm.tree.FieldNode;\r
+\r
+/**\r
+ * This class represents a java class.\r
+ */\r
+public class ClassInfo {\r
+\r
+       public static final String PACKAGE_SEPARATOR ="/";\r
+       private static final String DEFAULT_PACKAGE = "$default$";\r
+       public static final String LIBRARY_LABEL = "$library$";\r
+       \r
+       private String fileName;\r
+       private String sourceFileName;\r
+       private String packageName;\r
+       private String className;\r
+       private String md5hash;\r
+       private String label;\r
+       private List<MethodInfo> methods = new ArrayList<>();\r
+       private List<FieldInfo> fields = new ArrayList<>();\r
+       \r
+       private String superclassName;\r
+       private List<String> interfaceNames;\r
+       \r
+       /**\r
+        * Creates a new <code>ClassInfo</code> instance from a binary stream.\r
+        * @param fileName\r
+        * @param binaryStream specifies a stream of Java bytecode.\r
+        * @param loaderLabel specifies a label indicating a location/category for a class.\r
+        * @throws IOException\r
+        */\r
+       public ClassInfo(String fileName, InputStream binaryStream, String loaderLabel) throws IOException {\r
+               this(fileName, binaryStream);\r
+               this.label = loaderLabel;\r
+       }\r
+\r
+       /**\r
+        * Creates a new <code>ClassInfo</code> instance from a binary stream.\r
+        * @param fileName\r
+        * @param binaryStream specifies a stream of Java bytecode.  \r
+        * Please note that the stream is not closed by the method; \r
+        * the callers must close the stream by themselves.\r
+        */\r
+       public ClassInfo(String fileName, InputStream binaryStream) throws IOException {\r
+               this.fileName = fileName; \r
+               byte[] bytes = readToEnd(binaryStream);\r
+               ClassReader cr1;\r
+               try {\r
+                       cr1 = new ClassReader(bytes) {\r
+                               /**\r
+                                * This extension reduces the number of allocated strings.\r
+                                * When reading SOBA and GNU Trove class files, 52MB of 72MB strings can be discarded.\r
+                                */\r
+                               @Override\r
+                               public String readUTF8(int index, char[] buf) {\r
+                                       return super.readUTF8(index, buf);\r
+                               }\r
+                               \r
+                       };\r
+               } catch (ArrayIndexOutOfBoundsException e) {\r
+                       throw new ClassReadFailureException("ASM ClassReader cannot parse the bytecode. " + fileName + " " + e.getLocalizedMessage());\r
+               }\r
+               ClassNode classNode = new ClassNode(Opcodes.ASM5) {\r
+                       @Override\r
+                       public MethodVisitor visitMethod(int access, String name,\r
+                                       String desc, String signature, String[] exceptions) {\r
+                               return new JSRInlinerAdapter(super.visitMethod(access, name, desc, signature, exceptions), access, name, desc, signature, exceptions);\r
+                       }\r
+               };\r
+               cr1.accept(classNode, 0);\r
+               this.className = classNode.name;\r
+\r
+               int pkgIndex = className.lastIndexOf(PACKAGE_SEPARATOR);\r
+               if (pkgIndex >= 0) {\r
+                       packageName = className.substring(0, pkgIndex);\r
+               } else {\r
+                       packageName = DEFAULT_PACKAGE;\r
+               }\r
+               \r
+               if (classNode.sourceFile != null) {\r
+                       this.sourceFileName = getClassDirPath() + File.separator + classNode.sourceFile;\r
+               } else {\r
+                       this.sourceFileName = null;\r
+               }\r
+\r
+               this.md5hash = MD5.getMD5(bytes);\r
+\r
+               classNode.methods.forEach(m -> methods.add(new MethodInfo(this, m)));\r
+               \r
+               for (int i=0; i<classNode.fields.size(); ++i) {\r
+                       fields.add(new FieldInfo(this, (FieldNode)classNode.fields.get(i)));\r
+               }\r
+               superclassName = classNode.superName;\r
+               interfaceNames = new ArrayList<String>(classNode.interfaces.size());\r
+               for (int i=0; i<classNode.interfaces.size(); ++i) {\r
+                       interfaceNames.add((String)classNode.interfaces.get(i));\r
+               }\r
+       }\r
+\r
+       private static byte[] readToEnd(InputStream stream) throws IOException {\r
+               byte[] buf = new byte[4096];\r
+               ByteArrayOutputStream buffer = new ByteArrayOutputStream();\r
+               int n;\r
+               while ((n = stream.read(buf, 0, buf.length)) > 0) {\r
+                       buffer.write(buf, 0, n);\r
+               }\r
+               return buffer.toByteArray();\r
+       }\r
+\r
+       public static ClassInfo createLibraryClass(String fileName, InputStream binaryStream) throws IOException {\r
+               ClassInfo c = new ClassInfo(fileName, binaryStream);\r
+               c.label = LIBRARY_LABEL;\r
+               return c;\r
+       }\r
+       \r
+       /**\r
+        * @return a package name.\r
+        * A package name is separated by "/".\r
+        */\r
+       public String getPackageName() {\r
+               return packageName;\r
+       }\r
+\r
+       /**\r
+        * @return a class name with its package name.\r
+        * The class name is an internal representation; \r
+        * a package name is separated by "/".\r
+        */\r
+       public String getClassName() {\r
+               return className;\r
+       }\r
+\r
+       /**\r
+        * @return a super class name with its package name.\r
+        */\r
+       public String getSuperClass() {\r
+               return superclassName;\r
+       }\r
+       \r
+       /**\r
+        * @return a list of interface names this class implements.\r
+        */\r
+       public List<String> getInterfaces() {\r
+               return interfaceNames;\r
+       }\r
+       \r
+       /**\r
+        * @return a MD5 hash value.\r
+        */\r
+       public String getHash() {\r
+               return md5hash;\r
+       }\r
+\r
+       /**\r
+        * @return a directory path which has this class file.\r
+        */\r
+       public String getClassDirPath() {\r
+               return packageName.replace('/', File.separatorChar); \r
+       }\r
+\r
+       /**\r
+        * @return the file name whose containing the class data.\r
+        * If the class file is contained in a ZIP/JAR file,\r
+        * the resultant path incidates the zip file and an internal file path in the archive.\r
+        */\r
+       public String getClassFileName() {\r
+               return fileName;\r
+       }\r
+       \r
+       /**\r
+        * @return a label attached to this class.\r
+        * This is expected to distinguish a library class or an application class.\r
+        */\r
+       public String getLabel() {\r
+               return label;\r
+       }\r
+\r
+       /**\r
+        * @return true if this class is a library\r
+        */\r
+       public boolean isLibrary() {\r
+               return getLabel() == LIBRARY_LABEL;\r
+       }\r
+       \r
+       /**\r
+        * @return a Java source file name.\r
+        * The file name is a relative path.\r
+        * The method may return null. \r
+        */\r
+       public String getSourceFileName() {\r
+               return sourceFileName;\r
+       }\r
+\r
+       /**\r
+        * @return the number of methods declared in this class.\r
+        */\r
+       public int getMethodCount() {\r
+               return methods.size();\r
+       }\r
+\r
+       /**\r
+        * @param methodIndex\r
+        * @return a <code>MethodInfo</code> object specified by its index.\r
+        */\r
+       public MethodInfo getMethod(int methodIndex) {\r
+               return methods.get(methodIndex);\r
+       }\r
+\r
+       /**\r
+        * @return a list of <code>MethodInfo</code> objects declared in this class.\r
+        */\r
+       public List<MethodInfo> getMethods() {\r
+               return methods;\r
+       }\r
+\r
+       /**\r
+        * @param methodName is a method name.\r
+        * @param methodDesc is a method descriptor without generics information.\r
+        * @return a <code>MethodInfo</code> object if the class declares the specified method. \r
+        */\r
+       public MethodInfo findMethod(String methodName, String methodDesc) {\r
+               for (MethodInfo m: methods) {\r
+                       if (m.getMethodName().equals(methodName) &&\r
+                                       m.getDescriptor().equals(methodDesc)) {\r
+                               return m;\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       /**\r
+        * @return the number of fields declared in this class.\r
+        */\r
+       public int getFieldCount() {\r
+               return fields.size();\r
+       }\r
+\r
+       /**\r
+        * @param fieldIndex\r
+        * @return a <code>FieldInfo</code> object specified by its index.\r
+        */\r
+       public FieldInfo getField(int fieldIndex) {\r
+               return fields.get(fieldIndex);\r
+       }\r
+\r
+       /**\r
+        * @return a list of <code>FieldInfo</code> objects declared in this class.\r
+        */\r
+       public List<FieldInfo> getFields() {\r
+               return fields;\r
+       }\r
+       \r
+       /**\r
+        * @param fieldName is a field name.\r
+        * @param fieldDesc is a field descriptor without generics information.\r
+        * @return a <code>FieldInfo</code> object if the class declares the specified field.\r
+        */\r
+       public FieldInfo findField(String fieldName, String fieldDesc) {\r
+               for (FieldInfo f: fields) {\r
+                       if (f.getFieldName().equals(fieldName) &&\r
+                                       f.getDescriptor().equals(fieldDesc)) {\r
+                               return f;\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       \r
+}\r
diff --git a/src/soba/core/ClassReadFailureException.java b/src/soba/core/ClassReadFailureException.java
new file mode 100755 (executable)
index 0000000..30cd41e
--- /dev/null
@@ -0,0 +1,13 @@
+package soba.core;\r
+\r
+import java.io.IOException;\r
+\r
+public class ClassReadFailureException extends IOException {\r
+\r
+       private static final long serialVersionUID = -5653771195879228138L;\r
+\r
+       public ClassReadFailureException(String message) {\r
+               super(message);\r
+       }\r
+       \r
+}\r
diff --git a/src/soba/core/FieldInfo.java b/src/soba/core/FieldInfo.java
new file mode 100755 (executable)
index 0000000..e3a3cff
--- /dev/null
@@ -0,0 +1,80 @@
+package soba.core;\r
+\r
+import org.objectweb.asm.tree.FieldNode;\r
+\r
+import soba.core.signature.TypeResolver;\r
+\r
+/**\r
+ * This class represents a field variable.\r
+ */\r
+public class FieldInfo {\r
+\r
+       private ClassInfo owner;\r
+       private FieldNode fieldNode;\r
+       private String typeName;\r
+       \r
+       /**\r
+        * Creates a new <code>FieldInfo</code> instance.\r
+        * @param owner is a <code>ClassInfo</code> object which declares this field.\r
+        * @param fieldNode\r
+        */\r
+       public FieldInfo(ClassInfo owner, FieldNode fieldNode) {\r
+               this.owner = owner;\r
+               this.fieldNode = fieldNode;\r
+               this.typeName = null;\r
+       }\r
+       \r
+       /**\r
+        * @return the package name who has the field.\r
+        */\r
+       public String getPackageName() {\r
+               return owner.getPackageName();\r
+       }\r
+       \r
+       /**\r
+        * @return the class name who has the field.\r
+        */\r
+       public String getClassName() {\r
+               return owner.getClassName();\r
+       }\r
+       \r
+       /**\r
+        * @return the field name.\r
+        */\r
+       public String getFieldName() {\r
+               return fieldNode.name;\r
+       }\r
+       \r
+       /**\r
+        * @return the descriptor of the field.\r
+        */\r
+       public String getDescriptor() {\r
+               return fieldNode.desc;\r
+       }\r
+       \r
+       /**\r
+        * @return the type name of the field.\r
+        */\r
+       public String getFieldTypeName() {\r
+               if (typeName == null) {\r
+                       if (fieldNode.signature != null) {\r
+                               typeName = TypeResolver.getTypeName(fieldNode.signature);\r
+                       } else {\r
+                               typeName = TypeResolver.getTypeName(fieldNode.desc);\r
+                       }\r
+               }\r
+               return typeName;\r
+       }\r
+       \r
+       @Override\r
+       public String toString() {\r
+               StringBuilder builder = new StringBuilder();\r
+               builder.append(getClassName());\r
+               builder.append(".");\r
+               builder.append(getFieldName());\r
+               builder.append(": ");\r
+               builder.append(getFieldTypeName());\r
+               return builder.toString();\r
+       }\r
+       \r
+}\r
diff --git a/src/soba/core/IClassFilter.java b/src/soba/core/IClassFilter.java
new file mode 100755 (executable)
index 0000000..43aacfa
--- /dev/null
@@ -0,0 +1,19 @@
+package soba.core;\r
+\r
+public interface IClassFilter {\r
+\r
+       /**\r
+        * This method determines whether JavaProgram load the class.\r
+        * @param dataName indicates a class file name (maybe in a zip file).\r
+        * @return true to make JavaProgram load the class.\r
+        */\r
+       public boolean loadClass(String dataName);\r
+       \r
+       /**\r
+        * This method determines whether JavaProgram registers the class \r
+        * to a class hierarchy object. \r
+        * @param c is a loaded ClassInfo object.\r
+        * @return true to make JavaProgram include the ClassInfo object.\r
+        */\r
+       public boolean acceptClass(ClassInfo c);\r
+}\r
diff --git a/src/soba/core/IDynamicBindingResolver.java b/src/soba/core/IDynamicBindingResolver.java
new file mode 100755 (executable)
index 0000000..8c8a754
--- /dev/null
@@ -0,0 +1,13 @@
+package soba.core;\r
+\r
+import soba.core.method.CallSite;\r
+\r
+public interface IDynamicBindingResolver {\r
+\r
+       /**\r
+        * Resolves the dynamic binding of a method invocation.\r
+        * @param cs is a method invocation.\r
+        * @return an array of the methods which may be invoked.\r
+        */\r
+       public MethodInfo[] resolveCall(CallSite cs);\r
+}\r
diff --git a/src/soba/core/JavaProgram.java b/src/soba/core/JavaProgram.java
new file mode 100755 (executable)
index 0000000..1816ac4
--- /dev/null
@@ -0,0 +1,155 @@
+package soba.core;\r
+\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.stream.Collectors;\r
+\r
+import soba.util.files.IClassList;\r
+import soba.util.files.IClassListCallback;\r
+\r
+\r
+/**\r
+ * This class represents a Java program.\r
+ */\r
+public class JavaProgram {\r
+       \r
+       private Map<String, ClassInfo> classes;\r
+       private ClassHierarchy classHierarchy;\r
+       private List<ClassInfo> loaded;\r
+       private List<ClassInfo> duplicated;\r
+       private List<String> filtered;\r
+       private List<ErrorMessage> errors;\r
+\r
+       /**\r
+        * Creates a new <code>JavaProgram</code> instance.\r
+        * @param lists\r
+        */\r
+       public JavaProgram(final IClassList[] lists) {\r
+               this(lists, null);\r
+       }\r
+       \r
+       /**\r
+        * Creates a new <code>JavaProgram</code> instance specifying classes to be analyzed.\r
+        * @param fileEnumeraters\r
+        * @param filter specifies classes to be analyzed.\r
+        */\r
+       public JavaProgram(final IClassList[] lists, final IClassFilter filter) {\r
+               classes = new HashMap<String, ClassInfo>(65536);\r
+               errors = new ArrayList<ErrorMessage>(1024);\r
+               loaded = new ArrayList<ClassInfo>(65536);\r
+               duplicated = new ArrayList<ClassInfo>(1024);\r
+               filtered = new ArrayList<String>(1024);\r
+               classHierarchy = new ClassHierarchy();\r
+               \r
+               for (final IClassList list: lists) {\r
+                       if (list == null) continue;\r
+                       \r
+                       list.process(new IClassListCallback() {\r
+                               \r
+                               @Override\r
+                               public boolean reportError(String name, Exception e) {\r
+                                       errors.add(new ErrorMessage(name, e));\r
+                                       return false;\r
+                               }\r
+                               \r
+                               @Override\r
+                               public void process(String name, InputStream stream) throws IOException {\r
+                                       if (filter == null || filter.loadClass(name)) {\r
+                                               ClassInfo c = new ClassInfo(name, stream, list.getLabel());\r
+                                               if (filter == null || filter.acceptClass(c)) {\r
+                                                       if (!classes.containsKey(c.getClassName())) {\r
+                                                               classes.put(c.getClassName(), c);\r
+                                                               loaded.add(c);\r
+                                                               classHierarchy.registerClass(c);\r
+                                                       } else {\r
+                                                               duplicated.add(c);\r
+                                                       }\r
+                                               } else {\r
+                                                       filtered.add(name);\r
+                                               }\r
+                                       } else {\r
+                                               filtered.add(name);\r
+                                       }\r
+                               }\r
+                               \r
+                               @Override\r
+                               public boolean isTarget(String name) {\r
+                                       return name.endsWith(".class");\r
+                               }\r
+                       });\r
+               }\r
+       }\r
+               \r
+       /**\r
+        * @return a list of loaded <code>ClassInfo</code> objects.\r
+        */\r
+       public List<ClassInfo> getClasses() {\r
+               return loaded;\r
+       }\r
+       \r
+       /**\r
+        * @return a list of filtered classes.\r
+        */\r
+       public List<String> getFiltered() {\r
+               return filtered;\r
+       }\r
+       \r
+       /**\r
+        * @return a list of duplicated <code>ClassInfo</code> objects.\r
+        * If the analyzed files contain classes whose names are same\r
+        * (with their package names), this method returns a non-empty list. \r
+        */\r
+       public List<ClassInfo> getDuplicated() {\r
+               return duplicated;\r
+       }\r
+\r
+       /**\r
+        * @return a list of library classes.\r
+        */\r
+       public List<ClassInfo> getLibraryClasses() {\r
+               return classes.values().stream()\r
+                               .filter(c -> c.isLibrary())\r
+                               .collect(Collectors.toList());\r
+       }\r
+       \r
+       /**\r
+        * @param className is a class name including its package name.\r
+        * @return a <code>ClassInfo</code> object specified by its name.\r
+        */\r
+       public ClassInfo getClassInfo(String className) {\r
+               return classes.get(className);\r
+       }\r
+       \r
+       /**\r
+        * @return a <code>ClassHierarchy</code> object which has hierarchy information of the analyzed classes.\r
+        */\r
+       public ClassHierarchy getClassHierarchy() {\r
+               return classHierarchy;\r
+       }\r
+       \r
+       /**\r
+        * @return a list of error messages.\r
+        */\r
+       public List<ErrorMessage> getErrors() {\r
+               return errors;\r
+       }\r
+       \r
+       public static class ErrorMessage { \r
+               private String dataName;\r
+               private Exception exception;\r
+               public ErrorMessage(String name, Exception e) {\r
+                       this.dataName = name;\r
+                       this.exception = e;\r
+               }\r
+               public String getDataName() {\r
+                       return dataName;\r
+               }\r
+               public Exception getException() {\r
+                       return exception;\r
+               }\r
+       }\r
+}\r
diff --git a/src/soba/core/MD5.java b/src/soba/core/MD5.java
new file mode 100755 (executable)
index 0000000..e5a9853
--- /dev/null
@@ -0,0 +1,26 @@
+package soba.core;\r
+\r
+import java.security.MessageDigest;\r
+import java.security.NoSuchAlgorithmException;\r
+\r
+public class MD5 {\r
+\r
+       public static String getMD5(byte[] bytearray) {\r
+               try {\r
+                       MessageDigest digest = MessageDigest.getInstance("MD5");\r
+                       byte[] hash = digest.digest(bytearray);\r
+                       return getString(hash);\r
+               } catch (NoSuchAlgorithmException e) {\r
+                       return null;\r
+               }\r
+       }\r
+       \r
+       private static String getString(byte[] bytearray) {\r
+               StringBuilder b = new StringBuilder();\r
+               for (int i=0; i<bytearray.length; ++i) {\r
+                       String s = "0" + Integer.toHexString(bytearray[i]);\r
+                       b.append(s.substring(s.length()-2));\r
+               }\r
+               return b.toString();\r
+       }\r
+}\r
diff --git a/src/soba/core/MethodInfo.java b/src/soba/core/MethodInfo.java
new file mode 100755 (executable)
index 0000000..73220f7
--- /dev/null
@@ -0,0 +1,588 @@
+package soba.core;\r
+\r
+import gnu.trove.list.array.TIntArrayList;\r
+import gnu.trove.set.TIntSet;\r
+import gnu.trove.set.hash.TIntHashSet;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.List;\r
+\r
+import org.objectweb.asm.Opcodes;\r
+import org.objectweb.asm.tree.AbstractInsnNode;\r
+import org.objectweb.asm.tree.FieldInsnNode;\r
+import org.objectweb.asm.tree.LineNumberNode;\r
+import org.objectweb.asm.tree.LocalVariableNode;\r
+import org.objectweb.asm.tree.MethodInsnNode;\r
+import org.objectweb.asm.tree.MethodNode;\r
+import org.objectweb.asm.tree.analysis.AnalyzerException;\r
+\r
+import soba.core.method.CallSite;\r
+import soba.core.method.ControlDependence;\r
+import soba.core.method.DataDependence;\r
+import soba.core.method.FieldAccess;\r
+import soba.core.method.OpcodeString;\r
+import soba.core.method.asm.DataFlowAnalyzer;\r
+import soba.core.method.asm.DataFlowInterpreter;\r
+import soba.core.signature.MethodSignatureReader;\r
+import soba.util.ObjectIdMap;\r
+import soba.util.graph.DirectedGraph;\r
+\r
+/**\r
+ * This class represents a java method.\r
+ */\r
+public class MethodInfo {\r
+       \r
+       private ClassInfo ownerClass;\r
+       private MethodNode method;\r
+\r
+       private String returnType;\r
+       private String[] paramTypes;\r
+       private int[] paramIndex;\r
+       private int paramCount;\r
+       private boolean[] paramGeneric;\r
+       \r
+       private int[] lines;\r
+       private int maxLine;\r
+       private int minLine;\r
+       \r
+       private DataFlowAnalyzer analyzer;\r
+       private DataDependence dataDependence;\r
+\r
+       /**\r
+        * Creates a new <code>MethodInfo</code> instance.\r
+        * @param owner is a <code>ClassInfo</code> object which declares this method.\r
+        * @param method\r
+        */\r
+       public MethodInfo(ClassInfo owner, MethodNode method) {\r
+               this.ownerClass = owner;\r
+               this.method = method;\r
+       }\r
+       \r
+       /**\r
+        * @return the package name who has the method.\r
+        */\r
+       public String getPackageName() {\r
+               return ownerClass.getPackageName();\r
+       }\r
+\r
+       /**\r
+        * @return the class name who has the method.\r
+        */\r
+       public String getClassName() {\r
+               return ownerClass.getClassName();\r
+       }\r
+\r
+       /**\r
+        * @return a method name\r
+        */\r
+       public String getMethodName() {\r
+               return method.name;\r
+       }\r
+       \r
+       /**\r
+        * @return the descriptor of the method.\r
+        */\r
+       public String getDescriptor() {\r
+               return method.desc;\r
+       }\r
+       \r
+       /**\r
+        * @return a descriptor including generics information.\r
+        */\r
+       public String getGenericsSignature() {\r
+               return method.signature;\r
+       }\r
+\r
+       /**\r
+        * @return true if this method has a method body.\r
+        */\r
+       public boolean hasMethodBody() {\r
+               return (method.access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_NATIVE)) == 0;\r
+       }\r
+\r
+       /**\r
+        * @return true if this is a library method.\r
+        */\r
+       public boolean isLibrary() {\r
+               return ownerClass.isLibrary();\r
+       }\r
+       \r
+       /**\r
+        * @return true if this method is declared as a static method.\r
+        */\r
+       public boolean isStatic() {\r
+               return (method.access & Opcodes.ACC_STATIC) != 0;\r
+       }\r
+\r
+       /**\r
+        * @return true if this method is automatically generated by the compiler. \r
+        */\r
+       public boolean isSynthetic() {\r
+               return (method.access & Opcodes.ACC_SYNTHETIC) != 0;\r
+       }\r
+\r
+       /**\r
+        * @return true if this method is declared as a public method.\r
+        */\r
+       public boolean isPublic() {\r
+               return (method.access & Opcodes.ACC_PUBLIC) != 0;\r
+       }\r
+\r
+       /**\r
+        * @return true if this method is declared as a protected method.\r
+        */\r
+       public boolean isProtected() {\r
+               return (method.access & Opcodes.ACC_PROTECTED) != 0;\r
+       }\r
+\r
+       /**\r
+        * @return true if this method is declared as a private method.\r
+        */\r
+       public boolean isPrivate() {\r
+               return (method.access & Opcodes.ACC_PRIVATE) != 0;\r
+       }\r
+\r
+       /**\r
+        * @return true if this method can be accessed by the same package only.\r
+        */\r
+       public boolean isPackagePrivate() {\r
+               return (method.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED)) == 0;\r
+       }\r
+       \r
+       /**\r
+        * @return true if the method may be overridden by a subclass.\r
+        * In other words, the method is a non-final, non-private instance method. \r
+        */\r
+       public boolean isOverridable() {\r
+               return (method.access & (Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL | Opcodes.ACC_STATIC)) == 0;\r
+       }\r
+\r
+       /**\r
+        * @return the number of bytecode instructions in this method.\r
+        */\r
+       public int getInstructionCount() {\r
+               return method.instructions.size();\r
+       }\r
+       \r
+       /**\r
+        * @param instructionIndex\r
+        * @return AbstractInsnNode of instructionIndex\r
+        */\r
+       public AbstractInsnNode getAbstractInsnNode(int instructionIndex) {\r
+               return method.instructions.get(instructionIndex);\r
+       }\r
+       \r
+       /**\r
+        * @return the return value type.\r
+        * The method may return a generic type name such as "T". \r
+        */\r
+       public String getReturnType() {\r
+               extractParametersIfNecessary();\r
+               return returnType;\r
+       }\r
+       \r
+       /**\r
+        * @return the number of parameters of this method.\r
+        */\r
+       public int getParamCount() {\r
+               extractParametersIfNecessary();\r
+               return paramCount;\r
+       }\r
+       \r
+       /**\r
+        * @return the index value of a receiver object.\r
+        */\r
+       public int getReceiverObjectParamIndex() {\r
+               assert !isStatic();\r
+               return 0; // "this" is always the first argument even if a method is invoked for an inner/anonymous class.\r
+       }\r
+       \r
+       /**\r
+        * This method translates "N-th" parameter to an index value for the local variable table.\r
+        * This method is required because a double-word (long, double) variable \r
+        * occupies two words in a local variable table. \r
+        * @param paramIndex specifies the position of a parameter.\r
+        * E.g. 0, 1, 2, ... indicate the first, the second, the third, ... parameters. \r
+        * @return index value to accesss local variable table.\r
+        */\r
+       public int getVariableTableIndexOfParamAt(int index) {\r
+               return this.paramIndex[index];\r
+       }\r
+       \r
+       /**\r
+        * This method is reverse of getVariableTableIndexOfParamAt.\r
+        * @return a position of the parameter corresponding to an index value for the local variable table.\r
+        */\r
+       public int getParameterOrderingNumber(int localVarialbleIndex) {\r
+               for (int p = 0; p < this.paramCount; p++) {\r
+                       if (paramIndex[p] == localVarialbleIndex) {\r
+                               return p;\r
+                       }\r
+               }\r
+               throw new IllegalArgumentException("getParameterOrderingNumber:" + localVarialbleIndex + " is not Parameter");\r
+       }\r
+       \r
+       /**\r
+        * @param localVarialbleIndex specifies an index value for the local variable table.\r
+        * @return true if a local variable specified by the index value is a parameter of this method.\r
+        */\r
+       public boolean isParameterOrderingNumber(int localVarialbleIndex) {\r
+               for (int p = 0; p < this.paramCount; p++) {\r
+                       if (paramIndex[p] == localVarialbleIndex) {\r
+                               return true;\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+\r
+       /**\r
+        * @param index specifies the position of a parameter.\r
+        * @return a type name for the parameter.  \r
+        * A class name is fully qualified.\r
+        * The name may be a generic type parameter such as "T".\r
+        * An inner class name is concatinated by ".". \r
+        * For example, "A.B" is returned for a type "A<T>.B".  \r
+        */\r
+       public String getParamType(int index) {\r
+               extractParametersIfNecessary();\r
+               if (index >= paramTypes.length) return null;\r
+               return paramTypes[index];\r
+       }\r
+       \r
+       /**\r
+        * @param index specifies the position of a parameter.\r
+        * @return a formal parameter name. \r
+        */\r
+       public String getParamName(int index) {\r
+               if (method.localVariables == null) return null;\r
+               if (index >= method.localVariables.size()) return null;\r
+               int paramIndex = getVariableTableIndexOfParamAt(index);\r
+               for (int i=0; i<method.localVariables.size(); ++i) {\r
+                       LocalVariableNode var = (LocalVariableNode)method.localVariables.get(i);\r
+                       if (var.index == paramIndex && var.start == method.instructions.getFirst()) {\r
+                               return var.name;\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       private void extractParametersIfNecessary() {\r
+               if (paramTypes != null) return;\r
+               \r
+               MethodSignatureReader reader = new MethodSignatureReader(method.desc);\r
+               int thisParam = isStatic() ? 0: 1;   \r
+               \r
+               // read type names\r
+               this.returnType = reader.getReturnType();\r
+               this.paramCount = reader.getParamCount() + thisParam;\r
+               String[] params = new String[this.paramCount];\r
+               if (!isStatic()) {\r
+                       if (getClassName() != null) {\r
+                               params[0] = getClassName();\r
+                       } else {\r
+                               params[0] = "(Owner-Class)";\r
+                       }\r
+               }\r
+               for (int i=0; i<reader.getParamCount(); ++i) {\r
+                       params[i+thisParam] = reader.getParamType(i);\r
+               }\r
+               \r
+               // read generics flag\r
+               paramGeneric = new boolean[this.paramCount];\r
+               for (int i=0; i<reader.getParamCount(); ++i) {\r
+                       paramGeneric[i+thisParam] = reader.isGenericType(i);\r
+               }\r
+       \r
+               // compute index for local variable table\r
+               this.paramIndex = new int[params.length];\r
+               int index = 0;\r
+               for (int i=0; i<params.length; ++i) {\r
+                       paramIndex[i] = index;\r
+                       if (params[i].equals("double") || params[i].equals("long")) {\r
+                               // Double and Long are double word values.\r
+                               index += 2;\r
+                       } else {\r
+                               index += 1;\r
+                       }\r
+               }\r
+               \r
+               // finished\r
+               this.paramTypes = params;\r
+       }\r
+\r
+       /**\r
+        * @return a method node.\r
+        */\r
+       public MethodNode getMethodNode() {\r
+               return method;\r
+       }\r
+       \r
+       private void computeMinMaxLine() {\r
+               if (lines == null) {\r
+                       TIntHashSet array = new TIntHashSet(method.instructions.size());\r
+                       for (int i=0; i<method.instructions.size(); ++i) {\r
+                               if (method.instructions.get(i).getType() == AbstractInsnNode.LINE) {\r
+                                       LineNumberNode node = (LineNumberNode)method.instructions.get(i);\r
+                                       array.add(node.line);\r
+                               }\r
+                       }\r
+                       if (array.isEmpty()) {\r
+                               lines = new int[0];\r
+                               maxLine = 0;\r
+                               minLine = 0;\r
+                       } else {\r
+                               lines = array.toArray();\r
+                               Arrays.sort(lines);\r
+                               maxLine = lines[lines.length - 1];\r
+                               minLine = lines[0];\r
+                       }\r
+               }\r
+       }\r
+       \r
+       \r
+       /**\r
+        * @return the maximum line including an instruction of the method.\r
+        * 0 indicates the method has no line number information.\r
+        */\r
+       public int getMaxLine() {\r
+               computeMinMaxLine();\r
+               return maxLine;\r
+       }\r
+       \r
+       /**\r
+        * @return the minimum line including an instruction of the method.\r
+        * 0 indicates the method has no line number information.\r
+        */\r
+       public int getMinLine() {\r
+               computeMinMaxLine();\r
+               return minLine;\r
+       }\r
+       \r
+       /**\r
+        * @return an array which is filled with the numbers from minimum line to maximum line.\r
+        */\r
+       public int[] getLineNumbers() {\r
+               computeMinMaxLine();\r
+               return lines;\r
+       }\r
+\r
+       /**\r
+        * @param instructionIndex\r
+        * @return the line number including a specified instruction. \r
+        */\r
+       public int getLine(int instructionIndex) {\r
+               for (int i=instructionIndex; i>=0; --i) {\r
+                       if (method.instructions.get(i).getType() == AbstractInsnNode.LINE) {\r
+                               return ((LineNumberNode)method.instructions.get(i)).line;\r
+                       }\r
+               }\r
+               return 0;\r
+       }\r
+       \r
+       /**\r
+        * @param line\r
+        * @return an array of instruction index values that consist of a specified line.\r
+        */\r
+       public int[] getInstructions(int line) {\r
+               TIntArrayList lineInstructions = new TIntArrayList();\r
+               boolean inside = false;\r
+               for (int i=0; i<method.instructions.size(); ++i) {\r
+                       if (method.instructions.get(i).getType() == AbstractInsnNode.LINE) {\r
+                               inside = ((LineNumberNode)method.instructions.get(i)).line == line;\r
+                       }\r
+                       if (inside) lineInstructions.add(i);\r
+               }\r
+               return lineInstructions.toArray();\r
+       }\r
+\r
+       /**\r
+        * Returns a list of invocations in the method body.\r
+        * @return a list of <code>CallSite</code>.\r
+        */\r
+       public List<CallSite> getCallSites() {\r
+               List<CallSite> callsites = new ArrayList<CallSite>(method.instructions.size());\r
+               for (int i=0; i<method.instructions.size(); ++i) {\r
+                       CallSite c = getCallSite(i);\r
+                       if (c != null) callsites.add(c);\r
+               }\r
+               return callsites;\r
+       }\r
+       \r
+       /**\r
+        * Returns an invocation in a method call instruction. \r
+        * If the instruction is not a method call, this method returns null.\r
+        * @param instructionIndex\r
+        * @return a <code>CallSite</code> object for an instruction.\r
+        */\r
+       public CallSite getCallSite(final int instructionIndex) {\r
+               if (method.instructions.get(instructionIndex).getType() == AbstractInsnNode.METHOD_INSN) {\r
+                       MethodInsnNode m = (MethodInsnNode)method.instructions.get(instructionIndex);\r
+                       return new CallSite(this, instructionIndex, m.owner, m.name, m.desc, getInvokeType(m));\r
+               } else {\r
+                       return null;\r
+               }\r
+       }\r
+       \r
+       private CallSite.Kind getInvokeType(MethodInsnNode m) {\r
+               CallSite.Kind k = CallSite.Kind.VIRTUAL;\r
+               if (m.getOpcode() == Opcodes.INVOKESTATIC) k = CallSite.Kind.STATIC;\r
+               else if (m.getOpcode() == Opcodes.INVOKESPECIAL) k = CallSite.Kind.SPECIAL;\r
+               return k;\r
+       }\r
+       \r
+       /**\r
+        * Returns a list of field accesses in the method body.\r
+        * @return a list of <code>FieldAccess</code>.\r
+        */\r
+       public List<FieldAccess> getFieldAccesses() {\r
+               List<FieldAccess> fields = new ArrayList<FieldAccess>(32);\r
+               for (int i=0; i<method.instructions.size(); ++i) {\r
+                       if (method.instructions.get(i).getType() == AbstractInsnNode.FIELD_INSN) {\r
+                               FieldAccess fieldAccess = getFieldAccess(i);\r
+                               if (fieldAccess != null) { \r
+                                       fields.add(fieldAccess);\r
+                               }\r
+                       }\r
+               }\r
+               return fields;\r
+       }\r
+       \r
+       /**\r
+        * Returns a field access in an instruction.\r
+        * If the instruction is not a field access, this method returns null.\r
+        * @param instructionIndex\r
+        * @return a <FieldAccess> object.\r
+        */\r
+       public FieldAccess getFieldAccess(final int instructionIndex) {\r
+               assert method.instructions.get(instructionIndex).getType() == AbstractInsnNode.FIELD_INSN;\r
+               \r
+               final FieldInsnNode f = (FieldInsnNode)method.instructions.get(instructionIndex);\r
+               switch (f.getOpcode()) {\r
+               case Opcodes.PUTFIELD:\r
+                       return FieldAccess.createPutField(f.owner, f.name, f.desc, false);\r
+               case Opcodes.PUTSTATIC:\r
+                       return FieldAccess.createPutField(f.owner, f.name, f.desc, true);\r
+               case Opcodes.GETFIELD:\r
+                       return FieldAccess.createGetField(f.owner, f.name, f.desc, false);\r
+               case Opcodes.GETSTATIC:\r
+                       return FieldAccess.createGetField(f.owner, f.name, f.desc, true);\r
+               default:\r
+                       assert false: "Unknown Field Operation Found.";\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       /**\r
+        * @return an array of index values for return instructions.\r
+        */\r
+       public int[] getReturnInstructions() {\r
+               TIntSet returns = new TIntHashSet();\r
+               for (int i=0; i<method.instructions.size(); i++) {\r
+                       AbstractInsnNode ain = method.instructions.get(i);\r
+                       if (OpcodeString.isReturnOperation(ain)) {\r
+                               returns.add(i);\r
+                       }\r
+               }\r
+               return returns.toArray();\r
+       }\r
+\r
+       /**\r
+        * @return a <code>DataDependence</code> object.\r
+        * The object has information about data dependencies.\r
+        */\r
+       public DataDependence getDataDependence() {\r
+               computeFlow();\r
+               return dataDependence;\r
+       }\r
+       \r
+       /**\r
+        * @return a control dependence graph.\r
+        */\r
+       public DirectedGraph getControlDependence() {\r
+               return ControlDependence.getDependence(getInstructionCount(), getControlFlow());\r
+       }\r
+       \r
+       /**\r
+        * @return a control-flow graph.\r
+        */\r
+       public DirectedGraph getControlFlow() {\r
+               computeFlow();\r
+               return new DirectedGraph(getInstructionCount(), analyzer.getNormalControlFlow());\r
+       }\r
+       \r
+       /**\r
+        * @return a conservative control-flow graph.\r
+        * The graph assumes that every instruction in a try block may throw an exception.\r
+        */\r
+       public DirectedGraph getConservativeControlFlow() {\r
+               computeFlow();\r
+               return new DirectedGraph(getInstructionCount(), analyzer.getConservativeControlFlow());\r
+       }\r
+       \r
+       private void computeFlow() {\r
+               if (analyzer == null) {\r
+                       ObjectIdMap<AbstractInsnNode> instructions = new ObjectIdMap<AbstractInsnNode>(method.instructions.size());\r
+                       for (int i=0; i<method.instructions.size(); ++i) {\r
+                               instructions.add(method.instructions.get(i));\r
+                       }\r
+                       instructions.freeze();\r
+                       DataFlowInterpreter interpreter = new DataFlowInterpreter(instructions);\r
+                       analyzer = new DataFlowAnalyzer(interpreter);\r
+                       try {\r
+                               analyzer.analyze(method.name, method);\r
+                               dataDependence = new DataDependence(instructions, analyzer);\r
+                       } catch (AnalyzerException e) {\r
+                               System.err.println(e.getMessage());\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * @return a unique string that specifies the method declaration. \r
+        */\r
+       public String getMethodKey() {\r
+               return getClassName() + "#" + getMethodName() + "#" + getDescriptor();\r
+       }\r
+\r
+       /**\r
+        * @param instructionIndex\r
+        * @return a string representation of the specified instruction.\r
+        */\r
+       public String getInstructionString(final int instructionIndex) {\r
+               return OpcodeString.getInstructionString(method, instructionIndex);\r
+       }\r
+       \r
+       /**\r
+        * @return a shorter string representation of the method signature.\r
+        */\r
+       public String toString() {\r
+               return getMethodName() + getDescriptor();\r
+       }\r
+\r
+       /**\r
+        * @return a longer string representation of the method signature.\r
+        */\r
+       public String toLongString() {\r
+               StringBuilder name = new StringBuilder();\r
+               if (getClassName() != null) {\r
+                       name.append(getClassName());\r
+                       name.append(".");\r
+               }\r
+               name.append(getMethodName());\r
+               name.append("(");\r
+               for (int i=0; i<getParamCount(); ++i) {\r
+                       if (i>0) name.append(", ");\r
+                       name.append(getParamType(i));\r
+                       String paramName = getParamName(i);\r
+                       if (paramName != null) {\r
+                               name.append(":");\r
+                               name.append(paramName);\r
+                       }\r
+               }\r
+               name.append(")");\r
+               name.append(": ");\r
+               name.append(getReturnType());\r
+               return name.toString();\r
+       }\r
+}\r
diff --git a/src/soba/core/method/CallSite.java b/src/soba/core/method/CallSite.java
new file mode 100755 (executable)
index 0000000..0b66e69
--- /dev/null
@@ -0,0 +1,101 @@
+package soba.core.method;\r
+\r
+import soba.core.MethodInfo;\r
+\r
+/**\r
+ * This class has information about a call site in a method body.\r
+ */\r
+public class CallSite {\r
+\r
+       public enum Kind { STATIC, SPECIAL, VIRTUAL };\r
+       \r
+       private MethodInfo ownerMethod;\r
+       private int instructionIndex;\r
+       private String className;\r
+       private String methodName;\r
+       private String methodDesc;\r
+       private Kind invokeType;\r
+       \r
+       /**\r
+        * Creates a new <code>CallSite</code> instance.\r
+        * @param m specifies an owner method of a call site.\r
+        * @param instIndex specifies an instruction.\r
+        * @param className is a class name of a callee.\r
+        * @param methodName is a method name of a callee.\r
+        * @param methodDesc is a method descriptor of a callee.\r
+        * @param kind is an invocation kind.\r
+        */\r
+       public CallSite(MethodInfo m, int instIndex, String className, String methodName, String methodDesc, Kind kind) {\r
+               this.ownerMethod = m;\r
+               this.instructionIndex = instIndex;\r
+               this.className = className;\r
+               this.methodName = methodName;\r
+               this.methodDesc = methodDesc;\r
+               this.invokeType = kind;\r
+       }\r
+\r
+       /**\r
+        * @return a <code>MethodInfo</code> object which is owner of the call site.\r
+        */\r
+       public MethodInfo getOwnerMethod() {\r
+               return ownerMethod;\r
+       }\r
+       \r
+       /**\r
+        * @return an index value of the call site in the method body instructions.\r
+        */\r
+       public int getInstructionIndex() {\r
+               return instructionIndex;\r
+       }\r
+       \r
+       /**\r
+        * @return true if the method is NOT a virtual method call.\r
+        * In other words, the method to be invoked is declared as static\r
+        * or a certain implementation is specified by the invocation,  \r
+        * e.g. "super.m()". \r
+        */\r
+       public boolean isStaticOrSpecial() {\r
+               return invokeType != Kind.VIRTUAL;\r
+       }\r
+       \r
+       /**\r
+        * @return true if the invocation calls a static method.\r
+        */\r
+       public boolean isStaticMethod() {\r
+               return invokeType == Kind.STATIC;\r
+       }\r
+       \r
+       /**\r
+        * @return the class name of the callee.\r
+        */\r
+       public String getClassName() {\r
+               return className;\r
+       }\r
+       \r
+       /**\r
+        * @return the method name of the callee.\r
+        */\r
+       public String getMethodName() {\r
+               return methodName;\r
+       }\r
+       \r
+       /**\r
+        * @return the method descriptor of the callee.\r
+        */\r
+       public String getDescriptor() {\r
+               return methodDesc;\r
+       }\r
+       \r
+       @Override\r
+       public String toString() {\r
+               StringBuilder sb = new StringBuilder();\r
+               sb.append(className);\r
+               sb.append(".");\r
+               sb.append(methodName);\r
+               sb.append(methodDesc);\r
+               sb.append(" called by ");\r
+               sb.append(ownerMethod.toLongString());\r
+               return sb.toString();\r
+       }\r
+\r
+}\r
diff --git a/src/soba/core/method/ControlDependence.java b/src/soba/core/method/ControlDependence.java
new file mode 100755 (executable)
index 0000000..0b0b566
--- /dev/null
@@ -0,0 +1,92 @@
+package soba.core.method;\r
+\r
+import java.util.Arrays;\r
+\r
+import soba.util.IntPairList;\r
+import soba.util.IntPairProc;\r
+import soba.util.IntPairSet;\r
+import soba.util.graph.DepthFirstSearch;\r
+import soba.util.graph.DirectedGraph;\r
+import soba.util.graph.DominanceTree;\r
+import soba.util.graph.IDepthFirstVisitor;\r
+import soba.util.graph.IDirectedGraph;\r
+import soba.util.graph.SingleRootDirectedGraph;\r
+\r
+public class ControlDependence {\r
+\r
+       \r
+       /**\r
+        * @param instructionCount is the number of instructions.\r
+        * @param controlFlowGraph is a directed graph representing control-flow among instructions.\r
+        * @return a directed graph which represents control dependencies.\r
+        * The graph should be a "regular" control-flow graph excluding exceptional control-flow paths.\r
+        * Note: This graph does not contain dependencies from the method entry.\r
+        */\r
+       public static DirectedGraph getDependence(final int instructionCount, final DirectedGraph controlFlowGraph) {\r
+               IDirectedGraph reverseControlFlow = controlFlowGraph.getReverseGraph();\r
+               SingleRootDirectedGraph rootGraph = new SingleRootDirectedGraph(reverseControlFlow);\r
+               DominanceTree tree = new DominanceTree(rootGraph);\r
+\r
+               final IntPairList controlDependenceCandidate = new IntPairList();\r
+               for (int i=0; i<instructionCount; ++i) {\r
+                       if (controlFlowGraph.getEdges(i).length > 1) { // is branch\r
+                               final int postDom = tree.getDominator(i); // post dominator\r
+                               DepthFirstSearch.search(controlFlowGraph, i, new IDepthFirstVisitor() {\r
+                                       \r
+                                       private int start; \r
+                                       @Override\r
+                                       public void onStart(int startVertexId) {\r
+                                               this.start = startVertexId;\r
+                                       }\r
+\r
+                                       @Override\r
+                                       public boolean onVisit(int vertexId) {\r
+                                               if (start != vertexId && vertexId != postDom) {\r
+                                                       controlDependenceCandidate.add(start, vertexId);\r
+                                               }\r
+                                               return vertexId != postDom;\r
+                                       }\r
+                                       @Override\r
+                                       public void onVisitAgain(int vertexId) {\r
+                                       }\r
+                                       \r
+                                       @Override\r
+                                       public void onLeave(int vertexId) {\r
+                                       }\r
+                                       \r
+                                       @Override\r
+                                       public void onFinished(boolean[] visited) {\r
+                                       }\r
+                               });\r
+                       }\r
+               }\r
+               \r
+               // Removing redundant edges: if A->B and B->C, then A->C is redundant.  If A->B and B->A, both A->C and B->C are not redundant.  \r
+               DirectedGraph candidate = new DirectedGraph(instructionCount, controlDependenceCandidate);\r
+               final IntPairSet redundantEdges = new IntPairSet();\r
+               for (int src=0; src<instructionCount; ++src) {\r
+                       for (int v: candidate.getEdges(src)) {\r
+                               if (controlFlowGraph.getEdges(v).length > 1) { // v is a branch vertex\r
+                                       int[] edges = candidate.getEdges(v);\r
+                                       if (Arrays.binarySearch(edges, src) < 0) { // if not src->v and v->src\r
+                                               for (int d: candidate.getEdges(v)) {  \r
+                                                       redundantEdges.add(src, d);\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               final IntPairList controlDependence = new IntPairList();\r
+               controlDependenceCandidate.foreach(new IntPairProc() {\r
+                       @Override\r
+                       public boolean execute(int elem1, int elem2) {\r
+                               if (!redundantEdges.contains(elem1, elem2)) { \r
+                                       controlDependence.add(elem1, elem2);\r
+                               }\r
+                               return true;\r
+                       }\r
+               });\r
+               return new DirectedGraph(instructionCount, controlDependence);\r
+\r
+       }\r
+}\r
diff --git a/src/soba/core/method/DataDependence.java b/src/soba/core/method/DataDependence.java
new file mode 100755 (executable)
index 0000000..2cd4a88
--- /dev/null
@@ -0,0 +1,311 @@
+package soba.core.method;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.List;\r
+\r
+import org.objectweb.asm.tree.AbstractInsnNode;\r
+import org.objectweb.asm.tree.analysis.Frame;\r
+\r
+import soba.core.method.asm.DataFlowAnalyzer;\r
+import soba.core.method.asm.FastSourceInterpreter;\r
+import soba.core.method.asm.FastSourceValue;\r
+import soba.util.IntPairList;\r
+import soba.util.ObjectIdMap;\r
+import soba.util.graph.DirectedGraph;\r
+\r
+/**\r
+ * This class has data dependence information in a single method.\r
+ */\r
+public class DataDependence {\r
+\r
+       private ObjectIdMap<AbstractInsnNode> instructions;\r
+       private DataFlowAnalyzer analyzer;\r
+       private LocalVariables locals;\r
+       \r
+       private List<DataFlowEdge> dataFlowEdges;\r
+       private List<DataFlowEdge> dataFlowEdgesSourceOrder;\r
+       \r
+       /**\r
+        * Creates a new <code>DataDependence</code> instance.\r
+        * @param instructions are instructions in the method.\r
+        * @param analyzer\r
+        */\r
+       public DataDependence(ObjectIdMap<AbstractInsnNode> instructions, DataFlowAnalyzer analyzer) {\r
+               this.instructions = instructions;\r
+               this.analyzer = analyzer;\r
+               computeEdges();\r
+       }\r
+       \r
+       /**\r
+        * Returns a graph representing data-dependencies in a single method\r
+        * Note: This graph does not contain data dependence edges from formal parameters of the method.\r
+        */\r
+       public DirectedGraph getDependenceGraph() {\r
+               IntPairList edges = new IntPairList(dataFlowEdges.size());\r
+               for (DataFlowEdge e: dataFlowEdges) {\r
+                       if (e.getSourceInstruction() != FastSourceInterpreter.METHOD_ENTRY) {\r
+                               edges.add(e.getSourceInstruction(), e.getDestinationInstruction());\r
+                       }\r
+               }\r
+               return new DirectedGraph(instructions.size(), edges);\r
+       }\r
+       \r
+       /**\r
+        * @return a list of data flow edges.\r
+        * The edges are sorted by their destination instructions.\r
+        */\r
+       public List<DataFlowEdge> getEdges() {\r
+               return dataFlowEdges;\r
+       }\r
+       \r
+       /**\r
+        * @return a list of data flow edges.\r
+        * The edges are sorted by their source instructions.\r
+        */\r
+       public List<DataFlowEdge> getEdgesInSourceOrder() {\r
+               return dataFlowEdgesSourceOrder;\r
+       }\r
+       \r
+       /**\r
+        * @return a <code>LocalVariables</code> object.\r
+        */\r
+       public LocalVariables getLocalVariables() {\r
+               if (locals == null) {\r
+                       locals = new LocalVariables(this, analyzer.getAnalyzedMethod());\r
+               }\r
+               return locals;\r
+       }\r
+       \r
+       public String getVariableName(DataFlowEdge e) {\r
+               LocalVariables locals = getLocalVariables();\r
+               if (e.isLocal()) {\r
+                       if (e.getSourceInstruction() != FastSourceInterpreter.METHOD_ENTRY) {\r
+                               int sourceIndex = locals.findEntryForInstruction(e.getSourceInstruction());\r
+                               String sourceName = locals.getVariableName(sourceIndex);\r
+                               if (sourceName != null) {\r
+                                       return sourceName;\r
+                               }\r
+                       }\r
+                       int destinationIndex = locals.findEntryForInstruction(e.getDestinationInstruction());\r
+                       String destinationName = locals.getVariableName(destinationIndex);\r
+                       if (destinationName == null) {\r
+                               return e.getVariableIndex() + "_unavailable";\r
+                       }\r
+                       return destinationName;\r
+               } else {\r
+                       return null;\r
+               }\r
+       }\r
+       \r
+       public String getVariableDescriptor(DataFlowEdge e) {\r
+               LocalVariables locals = getLocalVariables();\r
+               if (e.isLocal()) {\r
+                       if (e.getSourceInstruction() != FastSourceInterpreter.METHOD_ENTRY) {\r
+                               int sourceIndex = locals.findEntryForInstruction(e.getSourceInstruction());\r
+                               String sourceDescriptor = locals.getDescriptor(sourceIndex);\r
+                               if (sourceDescriptor != null) {\r
+                                       return sourceDescriptor;\r
+                               }\r
+                       }\r
+                       int destinationIndex = locals.findEntryForInstruction(e.getDestinationInstruction());\r
+                       String destinationDescriptor = locals.getDescriptor(destinationIndex);\r
+                       if (destinationDescriptor == null) {\r
+                               return "null";\r
+                       }\r
+                       return destinationDescriptor;\r
+               } else {\r
+                       return null;\r
+               }\r
+       }\r
+       \r
+//     /**\r
+//      * Return a local variable corresponding to a data-flow edge.\r
+//      * @param e specifies a data-flow edge.\r
+//      * The edge must be returned by the same DataFlowInfo object.\r
+//      * @return a local variable information.\r
+//      * If the edge is caused by an operand stack, null is returned.\r
+//      */\r
+//     public ILocalVariableInfo getLocalVariable(DataFlowEdge e) {\r
+//             if (e.isLocal()) {\r
+//                     ILocalVariableInfo v1;\r
+//                     if (e.getSourceInstruction() == FastSourceInterpreter.METHOD_ENTRY) {\r
+//                             locals.getVariableName(e.getVariableIndex());\r
+//                             v1 = localVariables.getParam(e.getVariableIndex());\r
+//                     } else {\r
+//                             v1  = localVariables.getAccessedVariable(e.getSourceInstruction());\r
+//                     }\r
+//                     if (v1 != null && !v1.isAnonymous()) {\r
+//                             return v1;\r
+//                     }\r
+//                     ILocalVariableInfo v2 = localVariables.getAccessedVariable(e.getDestinationInstruction());\r
+//                     if (v2 != null && !v2.isAnonymous() || v1 == null) {\r
+//                             return v2;\r
+//                     }\r
+//                     // v1.isAnonymous AND (v2 == null OR v2.isAnonymous)\r
+//                     return v1;\r
+//             } else {\r
+//                     return null;\r
+//             }\r
+//     }\r
+       \r
+       /**\r
+        * Returns a list of data-definition vertices for each operand used by the specified instruction.\r
+        * @param instructionIndex specifies an instruction using an operand stack.\r
+        * @return a two-dimensional array.\r
+        * array[operandIndex] indicates a list of instructions that defined the operand.\r
+        * The result is consistent with a return value of getEdges().\r
+        */\r
+       public int[][] getDataDefinition(int instructionIndex) {\r
+               if (useStack(instructionIndex)) {\r
+                       int operands =  analyzer.getOperandCount(instructionIndex);\r
+                       int[][] operandDef = new int[operands][];\r
+                       for (int i=0; i<operands; ++i) {\r
+                               Frame<?> f = analyzer.getFrames()[instructionIndex];\r
+                               FastSourceValue value = (FastSourceValue)f.getStack(f.getStackSize() - operands + i);\r
+                               operandDef[i] = value.getInstructions();\r
+                       }\r
+                       return operandDef;\r
+               } else {\r
+                       AbstractInsnNode to = instructions.getItem(instructionIndex);\r
+                       if (referLocal(instructionIndex)) {\r
+                               int localIndex = OpcodeString.getVarIndex(to);\r
+                               Frame<?> f = analyzer.getFrames()[instructionIndex];\r
+                               if (f != null) {\r
+                                       FastSourceValue value = (FastSourceValue)f.getLocal(localIndex);\r
+                                       int[][] localDef = new int[1][];\r
+                                       localDef[0] = value.getInstructions();\r
+                                       return localDef;\r
+                               } else {\r
+                                       // To avoid a problem caused by certain methods including many JSRs\r
+                                       return new int[0][0];\r
+                               }\r
+                       }\r
+               }\r
+               return new int[0][];\r
+       }\r
+       \r
+       /**\r
+        * @param destinationInstruction is an instruction index value.\r
+        * @return a list of data flow edges which destination is specified.\r
+        */\r
+       public List<DataFlowEdge> getIncomingEdges(final int destinationInstruction) {\r
+               List<DataFlowEdge> edges = new ArrayList<>();\r
+               for (DataFlowEdge e: dataFlowEdges) {\r
+                       if (e.getDestinationInstruction() == destinationInstruction) {\r
+                               edges.add(e);\r
+                       }\r
+               }\r
+               return edges;\r
+       }\r
+       \r
+       /**\r
+        * @param destinationInstruction is an instruction index value.\r
+        * @param operandIndex is an operand index value in the instruction.\r
+        * @return a data flow edge which destination and operandIndex is specified.\r
+        * (Assumed that this incoming edge is just only one)\r
+        */\r
+       public DataFlowEdge getIncomingEdge(final int destinationInstruction, final int operandIndex) {\r
+               for (final DataFlowEdge dfe : dataFlowEdges) {\r
+                       if (dfe.getDestinationInstruction() == destinationInstruction && dfe.getDestinationOperandIndex() == operandIndex) {\r
+                               return dfe;\r
+                       }\r
+               }\r
+               throw new UnsupportedOperationException();\r
+       }\r
+       \r
+       /**\r
+        * @param destinationInstruction is an instruction index value.\r
+        * @param operandIndex is an operand index value in the instruction.\r
+        * @return a list of data flow edges which destination and operandIndex is specified.\r
+        */\r
+       public List<DataFlowEdge> getIncomingEdges(final int destinationInstruction, final int operandIndex) {\r
+               List<DataFlowEdge> edges = new ArrayList<>();\r
+               for (DataFlowEdge e: dataFlowEdges) {\r
+                       if (e.getDestinationInstruction() == destinationInstruction &&\r
+                                       e.getDestinationOperandIndex() == operandIndex) {\r
+                               edges.add(e);\r
+                       }\r
+               }\r
+               return edges;\r
+       }\r
+       \r
+       private void computeEdges() {\r
+               List<DataFlowEdge> edges = new ArrayList<DataFlowEdge>();\r
+               \r
+               for (int instructionIndex=0; instructionIndex<instructions.size(); ++instructionIndex) {\r
+                       Frame<?> f = analyzer.getFrames()[instructionIndex];\r
+                       if (useStack(instructionIndex)) {\r
+                               int operands = analyzer.getOperandCount(instructionIndex);\r
+                               for (int opIndex=0; opIndex<operands; ++opIndex) {\r
+                                       int stackPos = f.getStackSize() - operands + opIndex;\r
+                                       FastSourceValue value = (FastSourceValue)f.getStack(stackPos);\r
+                                       for (int from: value.getInstructions()) {\r
+                                               edges.add(new DataFlowEdge(from, instructionIndex, opIndex, operands, stackPos, false));\r
+                                       }\r
+                               }\r
+                       } else if (referLocal(instructionIndex)) {\r
+                               AbstractInsnNode to = instructions.getItem(instructionIndex);\r
+                               int localIndex = OpcodeString.getVarIndex(to);\r
+                               if (f != null) {\r
+                                       FastSourceValue value = (FastSourceValue)f.getLocal(localIndex);\r
+                                       for (int from: value.getInstructions()) {\r
+                                               edges.add(new DataFlowEdge(from, instructionIndex, 0, 1, localIndex, true));\r
+                                       }\r
+                               } else {\r
+                                       // A frame object is missing for several instructions in certain methods including many JSRs.\r
+                                       // We skip the data-flow edges for the "unknown" sources.\r
+                                       // edges.add(new DataFlowEdge(65536, instructionIndex, 0, 1, localIndex, true));\r
+                               }\r
+                       }\r
+               }\r
+               List<DataFlowEdge> sourceOrder = new ArrayList<DataFlowEdge>(edges.size());\r
+               sourceOrder.addAll(edges);\r
+               Collections.sort(sourceOrder, new DataFlowEdge.SourceComparator());\r
+               dataFlowEdgesSourceOrder = sourceOrder;\r
+               dataFlowEdges = edges;\r
+       }               \r
+\r
+       /**\r
+        * @return true if the specified instruction refers to operands on a stack.\r
+        */\r
+       public boolean useStack(int instructionIndex) {\r
+               return analyzer.getOperandCount(instructionIndex) > 0;\r
+       }\r
+\r
+       /**\r
+        * @return true if the specified instruction refers to a local variable.\r
+        */\r
+       private boolean referLocal(int instructionIndex) { \r
+               return OpcodeString.isLocalReferenceOperation(instructions.getItem(instructionIndex));\r
+       }\r
+       \r
+       /**\r
+        * @return an instruction object.\r
+        */\r
+       public AbstractInsnNode getInstruction(int index) {\r
+               return instructions.getItem(index);\r
+       }\r
+       \r
+       /**\r
+        * @param instructionIndex specifies an instruction.\r
+        * @return the number of operands used by the specified instruction.\r
+        * For an instruction that uses a local variable,\r
+        * 1 is returned.\r
+        */\r
+       public int getOperandCount(int instructionIndex) {\r
+               return analyzer.getOperandCount(instructionIndex);\r
+       }\r
+       \r
+       /**\r
+        * Returns a state of Frame at a specified instruction.\r
+        * Frame represents the current state of a operand stack and a local variable table.\r
+        * @param instructionIndex specifies an instruction.\r
+        * @return Frame object.  The return value may be null if \r
+        * control-flow analysis somewhat failed. \r
+        * (It is rarely occurs for certain methods.)\r
+        */\r
+       public Frame<?> getFrame(int instructionIndex) {\r
+               return analyzer.getFrames()[instructionIndex];\r
+       }\r
+}\r
diff --git a/src/soba/core/method/DataFlowEdge.java b/src/soba/core/method/DataFlowEdge.java
new file mode 100755 (executable)
index 0000000..74d5469
--- /dev/null
@@ -0,0 +1,171 @@
+package soba.core.method;\r
+\r
+import java.util.Comparator;\r
+\r
+import soba.core.method.asm.FastSourceInterpreter;\r
+\r
+/**\r
+ * This class represents a data flow edge.\r
+ */\r
+public class DataFlowEdge {\r
+\r
+       /**\r
+        * A field specifies an instruction.\r
+        */\r
+       private int from;\r
+       private int to;\r
+       private int operandIndex;\r
+       private int operandCount;\r
+       private int variableIndex;\r
+       private boolean isLocal;\r
+       \r
+       /**\r
+        * Creates a new <code>DataFlowEdge</code> instance.\r
+        * @param from is index value of a source instruction.\r
+        * @param to is index value of a destination instruction.\r
+        * @param operandIndex is a operand index value in the instruction.\r
+        * @param operandCount is the number of a operand.\r
+        * @param variableIndex is a index value of a local variable table or operand stack.\r
+        * @param isLocal is true if the instructions access a local variable.\r
+        */\r
+       public DataFlowEdge(int from, int to, int operandIndex, int operandCount, int variableIndex, boolean isLocal) {\r
+               assert operandIndex < operandCount;\r
+               \r
+               this.from = from;\r
+               this.to = to;\r
+               this.operandIndex = operandIndex;\r
+               this.operandCount = operandCount;\r
+               this.variableIndex = variableIndex;\r
+               this.isLocal = isLocal;\r
+       }\r
+       \r
+       /**\r
+        * @return the index value of an instruction which produces data.\r
+        * This method returns -1 if the definition instruction cannot be resolved.  \r
+        * For example, a catch clause starts with ASTORE instruction whose data source cannot be resolved. \r
+        * If isParameter() is true, it is a parameter from outside of the method.\r
+        */\r
+       public int getSourceInstruction() {\r
+               return from;\r
+       }\r
+       \r
+       /**\r
+        * @return the index value of an instruction which consumes data.\r
+        */\r
+       public int getDestinationInstruction() {\r
+               return to;\r
+       }\r
+       \r
+       /**\r
+        * @return the index value in the operand stack.\r
+        */\r
+       public int getDestinationOperandIndex() {\r
+               return operandIndex;\r
+       }\r
+       \r
+       /**\r
+        * @return the number of the operand in the destination instruction.\r
+        */\r
+       public int getDestinationOperandCount() {\r
+               return operandCount;\r
+       }\r
+       \r
+       /**\r
+        * @return a variable index pointing to an entry in \r
+        * a local variable table or a operand stack.\r
+        */\r
+       public int getVariableIndex() {\r
+               return variableIndex;\r
+       }\r
+       \r
+       /**\r
+        * @return true if the source instruction is a formal parameter.\r
+        */\r
+       public boolean isParameter() {\r
+               return isLocal && (from == FastSourceInterpreter.METHOD_ENTRY);\r
+       }\r
+       \r
+       /**\r
+        * @return true if the edge represents a data-flow through a local variable.\r
+        * The method returns false for a data-flow edge for an operand stack.\r
+        */\r
+       public boolean isLocal() {\r
+               return isLocal;\r
+       }\r
+       \r
+       @Override\r
+       public String toString() {\r
+               StringBuilder builder = new StringBuilder(64);\r
+               if (isParameter()) {\r
+                       builder.append("PARAM");\r
+               } else {\r
+                       builder.append(from);\r
+               }\r
+               builder.append(" -> ");\r
+               builder.append(to);\r
+               if (operandCount > 1) {\r
+                       builder.append(" [");\r
+                       builder.append(operandIndex + 1);\r
+                       builder.append("/");\r
+                       builder.append(operandCount);\r
+                       builder.append("]");\r
+               }\r
+               if (isLocal) {\r
+                       builder.append(" (LOCAL:");\r
+               } else {\r
+                       builder.append(" (STACK:");\r
+               }\r
+               builder.append(variableIndex);\r
+               builder.append(")");\r
+               return builder.toString();\r
+       }\r
+       \r
+       private static int compareVariable(DataFlowEdge o1, DataFlowEdge o2) {\r
+               if (o1.operandIndex == o2.operandIndex) {\r
+                       if (o1.variableIndex == o2.variableIndex) {\r
+                               if (o1.isLocal == o2.isLocal) {\r
+                                       return 0;\r
+                               } else {\r
+                                       if (o1.isLocal) return 1;\r
+                                       else return -1;\r
+                               }\r
+                       } else {\r
+                               return o1.variableIndex - o2.variableIndex;\r
+                       }\r
+               } else {\r
+                       return o1.operandIndex - o2.operandIndex;\r
+               }\r
+       }\r
+       \r
+       public static class SourceComparator implements Comparator<DataFlowEdge> {\r
+               \r
+               @Override\r
+               public int compare(DataFlowEdge o1, DataFlowEdge o2) {\r
+                       if (o1.from == o2.from) {\r
+                               if (o1.to == o2.to) {\r
+                                       return compareVariable(o1, o2);\r
+                               } else {\r
+                                       return o1.to - o2.to;\r
+                               }\r
+                       } else {\r
+                               return o1.from - o2.from;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       public static class DestinationComparator implements Comparator<DataFlowEdge> {\r
+               \r
+               @Override\r
+               public int compare(DataFlowEdge o1, DataFlowEdge o2) {\r
+                       if (o1.to == o2.to) {\r
+                               if (o1.from == o2.from) {\r
+                                       return compareVariable(o1, o2);\r
+                               } else {\r
+                                       return o1.from - o2.from;\r
+                               }\r
+                       } else {\r
+                               return o1.to - o2.to;\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/src/soba/core/method/FieldAccess.java b/src/soba/core/method/FieldAccess.java
new file mode 100755 (executable)
index 0000000..9e93824
--- /dev/null
@@ -0,0 +1,131 @@
+package soba.core.method;\r
+\r
+public class FieldAccess {\r
+\r
+       private String className;\r
+       private String fieldName;\r
+       private String fieldDesc;\r
+       private boolean isStatic;\r
+       private boolean isGet; // true==GET, false==PUT \r
+\r
+       private FieldAccess(String className, String fieldName, String fieldDesc, boolean isStatic, boolean isGet) {\r
+               this.className = className;\r
+               this.fieldName = fieldName;\r
+               this.fieldDesc = fieldDesc;\r
+               this.isStatic = isStatic;\r
+               this.isGet = isGet;\r
+       }\r
+       \r
+       /**\r
+        * Creates a new <code>FieldAccess</code> instance representing a load instruction of a field.\r
+        * @param className is a class name of the accessed field.\r
+        * @param fieldName is a field name.\r
+        * @param fieldDesc is a descriptor of the field.\r
+        * @param isStatic is true if it is a static field.\r
+        * @return a <code>FieldAccess</code> object.\r
+        */\r
+       public static FieldAccess createGetField(String className, String fieldName, String fieldDesc, boolean isStatic) {\r
+               return new FieldAccess(className, fieldName, fieldDesc, isStatic, true);\r
+       }\r
+\r
+       /**\r
+        * Creates a new <code>FieldAccess</code> instance representing a store instruction of a field.\r
+        * @param className is a class name of the accessed field.\r
+        * @param fieldName is a field name.\r
+        * @param fieldDesc is a descriptor of the field.\r
+        * @param isStatic is true if it is a static field.\r
+        * @return a <code>FieldAccess</code> object.\r
+        */\r
+       public static FieldAccess createPutField(String className, String fieldName, String fieldDesc, boolean isStatic) {\r
+               return new FieldAccess(className, fieldName, fieldDesc, isStatic, false);\r
+       }\r
+       \r
+       /**\r
+        * @return the class name of the field.\r
+        */\r
+       public String getClassName() {\r
+               return className;\r
+       }\r
+\r
+       /**\r
+        * @return the field name.\r
+        */\r
+       public String getFieldName() {\r
+               return fieldName;\r
+       }\r
+       \r
+       /**\r
+        * @return the descriptor of the field. \r
+        */\r
+       public String getDescriptor() {\r
+               return fieldDesc;\r
+       }\r
+       \r
+       /**\r
+        * @return true if the field is static.\r
+        */\r
+       public boolean isStatic() {\r
+               return isStatic;\r
+       }\r
+       \r
+       /**\r
+        * @return true if the instruction loads the field value.\r
+        */\r
+       public boolean isGet() {\r
+               return isGet;\r
+       }\r
+\r
+       /**\r
+        * @return true if the instruction stores the field value.\r
+        */\r
+       public boolean isPut() {\r
+               return !isGet;\r
+       }\r
+       \r
+       @Override\r
+       public String toString() {\r
+               StringBuilder b = new StringBuilder();\r
+               if (isGet) {\r
+                       b.append("GET");\r
+               } else {\r
+                       b.append("PUT");\r
+               }\r
+               if (isStatic) {\r
+                       b.append("STATIC");\r
+               } else {\r
+                       b.append("FIELD");\r
+               }\r
+               b.append(" ");\r
+               b.append(className);\r
+               b.append(".");\r
+               b.append(fieldName);\r
+               b.append(": ");\r
+               b.append(fieldDesc);\r
+               return b.toString();\r
+       }\r
+       \r
+       @Override\r
+       public boolean equals(Object obj) {\r
+               if (obj == null) { return false; }\r
+               \r
+               FieldAccess fa = (FieldAccess) obj;\r
+               return this.toString().equals(fa.toString());\r
+       }\r
+\r
+       @Override\r
+       public int hashCode() {\r
+               final int prime = 31;\r
+               int result = 1;\r
+               result = prime * result\r
+                               + ((className == null) ? 0 : className.hashCode());\r
+               result = prime * result\r
+                               + ((fieldDesc == null) ? 0 : fieldDesc.hashCode());\r
+               result = prime * result\r
+                               + ((fieldName == null) ? 0 : fieldName.hashCode());\r
+               result = prime * result + (isGet ? 1231 : 1237);\r
+               result = prime * result + (isStatic ? 1231 : 1237);\r
+               return result;\r
+       }\r
+       \r
+\r
+}\r
diff --git a/src/soba/core/method/ILocalVariableInfo.java b/src/soba/core/method/ILocalVariableInfo.java
new file mode 100755 (executable)
index 0000000..6d191d2
--- /dev/null
@@ -0,0 +1,11 @@
+package soba.core.method;\r
+\r
+\r
+public interface ILocalVariableInfo {\r
+\r
+       public boolean isAnonymous();\r
+       public String getName();\r
+       public String getDescriptor();\r
+       public String getGenericsSignature();\r
+       \r
+}\r
diff --git a/src/soba/core/method/LocalVariables.java b/src/soba/core/method/LocalVariables.java
new file mode 100755 (executable)
index 0000000..ca8f4e1
--- /dev/null
@@ -0,0 +1,438 @@
+package soba.core.method;\r
+\r
+import gnu.trove.iterator.TIntIterator;\r
+import gnu.trove.set.hash.TIntHashSet;\r
+\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+import org.objectweb.asm.Opcodes;\r
+import org.objectweb.asm.tree.AbstractInsnNode;\r
+import org.objectweb.asm.tree.LocalVariableNode;\r
+import org.objectweb.asm.tree.MethodNode;\r
+import org.objectweb.asm.tree.VarInsnNode;\r
+\r
+import soba.core.signature.TypeResolver;\r
+import soba.util.IntPairProc;\r
+import soba.util.IntPairSet;\r
+\r
+\r
+/**\r
+ * An instance of LocalVariables maintains a list of \r
+ * local variable entries.\r
+ * Each entry corresponds to a set of def-use chains \r
+ * that share the same instructions.\r
+ * \r
+ * A variable may be represented by two or more entries\r
+ * if the variable is re-used for several "independent"\r
+ * def-use chains.\r
+ */\r
+public class LocalVariables {\r
+       \r
+       private ArrayList<Entry> entries;\r
+       private MethodNode m;\r
+\r
+       /**\r
+        * Creates a new <code>LocalVariables</code> instance.\r
+        * @param dataDependence is a <code>DataDependence</code> object.\r
+        * @param node\r
+        */\r
+       public LocalVariables(DataDependence dataDependence, MethodNode node) {\r
+               this.m = node;\r
+               \r
+               // Create entries based on data-flow edges.\r
+               // Each entry is a set of data-flow edges that have some common def/use instructions.\r
+               ArrayList<Entry> entries = new ArrayList<Entry>();\r
+               for (DataFlowEdge edge: dataDependence.getEdges()) {\r
+                       if (edge.isLocal()) {\r
+                               int[] index = findEntries(entries, edge);\r
+                               if (index[0] != -1) {\r
+                                       Entry e1 = entries.get(index[0]);\r
+                                       if (index[1] == -1) {\r
+                                               e1.add(edge);\r
+                                               checkObjectFlag(e1, dataDependence, edge);\r
+                                       } else if (index[1] != index[0]) {\r
+                                               Entry e2 = entries.remove(index[1]);\r
+                                               e1.merge(e2);\r
+                                       } else { \r
+                                               // index[0] == index[1]; both instructions are already involved in a single entry.  \r
+                                               checkObjectFlag(e1, dataDependence, edge);\r
+                                       }\r
+                               } else {\r
+                                       // Both instructions are not included in an entry.\r
+                                       // Create a new entry for the edge.\r
+                                       Entry e = new Entry(edge); \r
+                                       entries.add(e);\r
+                                       checkObjectFlag(e, dataDependence, edge);\r
+                               }\r
+                               \r
+                       }\r
+               }\r
+               \r
+               // Associate local variable nodes to entries.\r
+               List<?> variables = node.localVariables;\r
+               for (int i=0; i<variables.size(); ++i) {\r
+                       LocalVariableNode var = (LocalVariableNode)variables.get(i);\r
+                       for (Entry e: entries) {                        \r
+                               if (e.isDataflowOf(var)) {\r
+                                       e.addLocalVariableNode(var);\r
+                               }\r
+                       }\r
+               }\r
+\r
+               this.entries = entries;\r
+               \r
+               // Add entries for STORE instructions without LOAD instructions.\r
+               for (int i=0; i<node.instructions.size(); ++i) {\r
+                       AbstractInsnNode instruction = node.instructions.get(i);\r
+                       if (OpcodeString.isStoreOperation(instruction) || OpcodeString.isLoadOperation(instruction)) {\r
+                               int entry = findEntryForInstruction(i);\r
+                               if (entry == -1) {\r
+                                       Entry e = new Entry(i, (VarInsnNode)instruction);\r
+                                       this.entries.add(e);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       private void checkObjectFlag(Entry e, DataDependence dataflow, DataFlowEdge edge) {\r
+               AbstractInsnNode varNode = dataflow.getInstruction(edge.getDestinationInstruction());\r
+               if (varNode.getOpcode() == Opcodes.ALOAD) {\r
+                       e.setObjectTypeEntry(true);\r
+               }\r
+               if (edge.getSourceInstruction() >= 0) {\r
+                       varNode = dataflow.getInstruction(edge.getSourceInstruction());\r
+                       if (varNode.getOpcode() == Opcodes.ASTORE) {\r
+                               e.setObjectTypeEntry(true);\r
+                       } else if (edge.getDestinationOperandIndex() == 0 && \r
+                                               (varNode.getOpcode() == Opcodes.AALOAD ||\r
+                                               varNode.getOpcode() == Opcodes.AASTORE)) {\r
+                               e.setArrayTypeEntry(true);\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * @return the number of the variable entries.\r
+        * It may be larger than the number of variables in the method. \r
+        */\r
+       public int getVariableEntryCount() {\r
+               return entries.size();\r
+       }\r
+       \r
+       /**\r
+        * @param entryIndex specifies a local variable.\r
+        * @return a variable name.\r
+        */\r
+       public String getVariableName(int entryIndex) {\r
+               return entries.get(entryIndex).getVariableName();\r
+       }\r
+       \r
+       /**\r
+        * @param entryIndex specifies a local variable.\r
+        * @return a descriptor of the specified variable.\r
+        */\r
+       public String getDescriptor(int entryIndex) {\r
+               return entries.get(entryIndex).getDesc();\r
+       }\r
+       \r
+       /**\r
+        * @param entryIndex specifies a local variable.\r
+        * @return the type name.  The value becomes null \r
+        * if the type name is not included in the analyzed classfile.\r
+        */\r
+       public String getVariableType(int entryIndex) {\r
+               return entries.get(entryIndex).getTypeName();\r
+       }\r
+       \r
+       /**\r
+        * @param entryIndex specifies a local variable.\r
+        * @return the index value in the local variable table.\r
+        */\r
+       public int getVariableIndex(int entryIndex) {\r
+               return entries.get(entryIndex).getVariableIndex();\r
+       }\r
+       \r
+       /**\r
+        * @param entryIndex specifies a local variable.\r
+        * @return true if the variable is an object type.\r
+        */\r
+       public boolean isObjectVariable(int entryIndex) {\r
+               return entries.get(entryIndex).isObjectType();\r
+       }\r
+       \r
+       /**\r
+        * @param entryIndex specifies a local variable.\r
+        * @return true if the variable is an array.\r
+        */\r
+       public boolean isArrayVariable(int entryIndex) {\r
+               return entries.get(entryIndex).isArrayType();\r
+       }\r
+       \r
+       /**\r
+        * @param entryIndex specifies a local variable.\r
+        * @return true if there is no instruction which uses the variable value.\r
+        */\r
+       public boolean hasNoDataDependence(int entryIndex) {\r
+               return entries.get(entryIndex).isAlone();\r
+       }\r
+       \r
+       /**\r
+        * @param entryIndex specifies a variable entry.\r
+        * @return true if the entry corresponds to a method parameter.\r
+        * Please note that this method returns false\r
+        * for a parameter whose value must be overwritten \r
+        * by another instruction.\r
+        */\r
+       public boolean isParameter(int entryIndex) {\r
+               return entries.get(entryIndex).isParameter();\r
+       }\r
+       \r
+       /**\r
+        * @param instructionIndex specifies an instruction.\r
+        * @return the index value in the variable entries.\r
+        * The variable is accessed by the specified instruction.\r
+        */\r
+       public int findEntryForInstruction(int instructionIndex) {\r
+               for (int i=0; i<entries.size(); ++i) {\r
+                       Entry e = entries.get(i);\r
+                       if (e.containsSource(instructionIndex) ||\r
+                               e.containsDestination(instructionIndex)) {\r
+                               return i;\r
+                       }\r
+               }\r
+               return -1;\r
+       }\r
+       \r
+       \r
+       /**\r
+        * Returns a pair of indices that indicate a pair of Entries\r
+        * containing the specified values.\r
+        * The return_value[0] specifies a set containing value1,\r
+        * while the return_value[1] specifies a set containing value2.\r
+        * If no set contains value1, \r
+        * return_value is a pair (a set containing value2, -1).\r
+        * If no values are found in the sets, (-1, -1) is returned.\r
+        * @return \r
+        */\r
+       private static int[] findEntries(ArrayList<Entry> entries, DataFlowEdge edge) {\r
+               int[] ret = new int[] {-1, -1};\r
+               for (int i=0; i<entries.size(); ++i) {\r
+                       Entry e = entries.get(i);\r
+                       if (e.isConnected(edge)) {\r
+                               if (ret[0] == -1) {\r
+                                       ret[0] = i;\r
+                               } else if (ret[1] == -1) {\r
+                                       ret[1] = i;\r
+                                       return ret;\r
+                               }\r
+                       }\r
+               }\r
+               return ret;\r
+       }\r
+       \r
+       private class Entry {\r
+               \r
+               private TIntHashSet defs;\r
+               private TIntHashSet refs;\r
+               private IntPairSet refWithOperands;\r
+               private int variableIndex;\r
+               private boolean isObjectType;\r
+               private boolean isArrayType;\r
+               private String typeName;\r
+               private String typeNameWithGenerics;\r
+               private String variableName;\r
+               private String desc;\r
+               private ArrayList<LocalVariableNode> variables;\r
+               private boolean isParam;\r
+               private boolean isAlone; // true if there is a store instruction without a load instruction.\r
+               \r
+               private Entry(DataFlowEdge e) {\r
+                       this.variableIndex = e.getVariableIndex();\r
+                       this.variables = new ArrayList<LocalVariableNode>();\r
+                       \r
+                       defs = new TIntHashSet();\r
+                       defs.add(e.getSourceInstruction());\r
+                       refs = new TIntHashSet();\r
+                       refs.add(e.getDestinationInstruction());\r
+                       refWithOperands = new IntPairSet();\r
+                       refWithOperands.add(e.getDestinationInstruction(), e.getDestinationOperandIndex());\r
+                       isParam = e.isParameter();\r
+                       isAlone = false;\r
+                       // isObjectType and isArrayType are set by an external method.\r
+               }\r
+\r
+               private Entry(int instructionIndex, VarInsnNode var) {\r
+                       assert OpcodeString.isStoreOperation(var) || OpcodeString.isAfterJSR(var) : "A STORE instruction may exist without LOAD instructions. But there are no LOAD instructions without STORE.";\r
+                       this.variableIndex = var.var;\r
+                       this.variables = new ArrayList<LocalVariableNode>(1);\r
+                       defs = new TIntHashSet(2);\r
+                       defs.add(instructionIndex);\r
+                       refs = new TIntHashSet();\r
+                       refWithOperands = new IntPairSet();\r
+                       isParam = false;\r
+                       isObjectType = var.getOpcode() == Opcodes.ASTORE;\r
+                       isAlone = true;\r
+               }\r
+               \r
+\r
+               public boolean isParameter() {\r
+                       return defs.contains(-1);\r
+               }\r
+               \r
+               public boolean isAlone() {\r
+                       return isAlone;\r
+               }\r
+               \r
+               private void setObjectTypeEntry(boolean value) { \r
+                       this.isObjectType = value;\r
+               }\r
+\r
+               private void setArrayTypeEntry(boolean value) { \r
+                       this.isArrayType = value;\r
+               }\r
+\r
+               private void add(DataFlowEdge e) {\r
+                       assert this.variableIndex == e.getVariableIndex();\r
+                       defs.add(e.getSourceInstruction());\r
+                       refs.add(e.getDestinationInstruction());\r
+                       refWithOperands.add(e.getDestinationInstruction(), e.getDestinationOperandIndex());\r
+                       isParam = isParam ||  e.isParameter();\r
+               }\r
+               \r
+               private void merge(Entry another) {\r
+                       assert this.variableIndex == another.variableIndex; \r
+                       \r
+                       defs.addAll(another.defs.toArray());\r
+                       another.refWithOperands.foreach(new IntPairProc() {\r
+                               @Override\r
+                               public boolean execute(int instructionIndex, int operand) {\r
+                                       refs.add(instructionIndex);\r
+                                       refWithOperands.add(instructionIndex, operand);\r
+                                       return true;\r
+                               }\r
+                       });\r
+                       if (another.isObjectType) {\r
+                               this.isObjectType = another.isObjectType;\r
+                       }\r
+               }\r
+               \r
+               private boolean isDataflowOf(LocalVariableNode var) {\r
+                       if (this.variableIndex == var.index) {\r
+                               \r
+                               for (TIntIterator it=defs.iterator(); it.hasNext(); ) {\r
+                                       int def = it.next();\r
+                                       if (0 <= def &&  def < m.instructions.size()) {\r
+                                               AbstractInsnNode defNode = m.instructions.get(def);\r
+                                               if (OpcodeString.isAccess(defNode, var)) {\r
+                                                       return true;\r
+                                               }\r
+                                       } else {\r
+                                               if (var.start == m.instructions.getFirst()) {\r
+                                                       return true;\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               for (TIntIterator it=refs.iterator(); it.hasNext(); ) {\r
+                                       int ref = it.next();\r
+                                       AbstractInsnNode refNode = m.instructions.get(ref);\r
+                                       if (OpcodeString.isAccess(refNode, var)) {\r
+                                               return true;\r
+                                       }\r
+                               }\r
+                               return false;\r
+                               \r
+                       } else {\r
+                               return false;\r
+                       }\r
+                       \r
+               }\r
+               \r
+               private void addLocalVariableNode(LocalVariableNode node) {\r
+                       variables.add(node);\r
+                       if (variableName == null) {\r
+                               variableName = node.name;\r
+                       } else {\r
+                               assert node.name == null || variableName.equals(node.name): "Combined " + variableName.toString() + " and " + node.name;\r
+                       }\r
+                       if (typeName == null) {\r
+                               if (node.desc != null) {\r
+                                       desc = node.desc;\r
+                                       typeName = TypeResolver.getTypeName(node.desc);\r
+                               }\r
+                       } else {\r
+                               assert node.desc == null || typeName.equals(TypeResolver.getTypeName(node.desc));\r
+                       }\r
+                       if (typeNameWithGenerics == null) {\r
+                               if (node.signature != null) {\r
+                                       typeNameWithGenerics = TypeResolver.getTypeName(node.signature);\r
+                               }\r
+                       } else {\r
+                               assert node.signature == null || typeNameWithGenerics.equals(TypeResolver.getTypeName(node.signature));\r
+                       }\r
+               }\r
+               \r
+               /**\r
+                * @param edge\r
+                * @return edge is connected with some vertices in the entry.\r
+                */\r
+               private boolean isConnected(DataFlowEdge edge) {\r
+                       return this.variableIndex == edge.getVariableIndex() &&\r
+                               containsSource(edge.getSourceInstruction()) ||\r
+                               containsDestinationOperand(edge.getDestinationInstruction(), edge.getDestinationOperandIndex());\r
+               }\r
+               \r
+               /**\r
+                * @param instruction\r
+                * @return true if the entry contains the specified \r
+                * source instruction.\r
+                */\r
+               private boolean containsSource(int instruction) {\r
+                       return defs.contains(instruction);\r
+               }\r
+\r
+               /**\r
+                * @param instruction\r
+                * @return true if the entry contains the specified \r
+                * destination instruction.\r
+                */\r
+               private boolean containsDestination(int instruction) {\r
+                       return refWithOperands.containsFirst(instruction);\r
+               }\r
+\r
+               /**\r
+                * @param instruction\r
+                * @return true if the entry contains the specified \r
+                * destination instruction.\r
+                */\r
+               private boolean containsDestinationOperand(int instruction, int operandIndex) {\r
+                       return refWithOperands.contains(instruction, operandIndex);\r
+               }\r
+\r
+               private boolean isObjectType() {\r
+                       return isObjectType;\r
+               }\r
+               \r
+               private boolean isArrayType() {\r
+                       return isArrayType;\r
+               }\r
+               \r
+               private int getVariableIndex() {\r
+                       return variableIndex;\r
+               }\r
+               \r
+               private String getTypeName() {\r
+                       return typeName;\r
+               }\r
+               \r
+               private String getVariableName() {\r
+                       return variableName;\r
+               }\r
+               \r
+               private String getDesc() {\r
+                       return desc;\r
+               }\r
+\r
+       }\r
+}\r
diff --git a/src/soba/core/method/OpcodeString.java b/src/soba/core/method/OpcodeString.java
new file mode 100755 (executable)
index 0000000..d5a74f8
--- /dev/null
@@ -0,0 +1,368 @@
+package soba.core.method;\r
+\r
+\r
+import java.util.List;\r
+\r
+import org.objectweb.asm.Opcodes;\r
+import org.objectweb.asm.tree.AbstractInsnNode;\r
+import org.objectweb.asm.tree.FieldInsnNode;\r
+import org.objectweb.asm.tree.FrameNode;\r
+import org.objectweb.asm.tree.IincInsnNode;\r
+import org.objectweb.asm.tree.JumpInsnNode;\r
+import org.objectweb.asm.tree.LabelNode;\r
+import org.objectweb.asm.tree.LdcInsnNode;\r
+import org.objectweb.asm.tree.LineNumberNode;\r
+import org.objectweb.asm.tree.LocalVariableNode;\r
+import org.objectweb.asm.tree.MethodInsnNode;\r
+import org.objectweb.asm.tree.MethodNode;\r
+import org.objectweb.asm.tree.VarInsnNode;\r
+\r
+import soba.core.signature.TypeResolver;\r
+\r
+\r
+\r
+public class OpcodeString {\r
+       \r
+       public static final String TYPE_PRIMITIVE = "P";\r
+       public static final String TYPE_REFERENCE = "R";\r
+       public static final String TYPE_UNKNOWN = "U";\r
+       \r
+\r
+       /**\r
+        * This method returns a local variable index accessed by \r
+        * the specified instruction node.\r
+        * \r
+        * The same index may be used for two or more local variables.\r
+        * To distinguish such variables, use findLocalVariable method. \r
+        */\r
+       public static int getVarIndex(AbstractInsnNode node) {\r
+               if (node.getType() == AbstractInsnNode.VAR_INSN) {\r
+                       return ((VarInsnNode)node).var;\r
+               } else if (node.getType() == AbstractInsnNode.IINC_INSN) {\r
+                       return ((IincInsnNode)node).var;\r
+               } else {\r
+                       assert false: "getVarIndex is called for an instruction without variable informaiton.";\r
+                       return -1;\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * This method returns true if a specified instruction \r
+        * reads or writes a local variable represented by \r
+        * the specified local variable node. \r
+        * \r
+        * @param insn specifies an instruction.\r
+        * @param var specifies a local variable.\r
+        * @return true if the instruction accesses the variable.\r
+        * If var is null, this method always returns false.\r
+        */\r
+       public static boolean isAccess(AbstractInsnNode insn, LocalVariableNode var) {\r
+               if (insn == null || var == null) return false;\r
+               \r
+               int varIndex = getVarIndex(insn);\r
+               if (var.index == varIndex) {\r
+                       // Check whether the variable's scope includes insn. \r
+                       AbstractInsnNode scopeEndInstruction; \r
+                       if (isStoreOperation(insn) || isIncrementOperation(insn)) {\r
+                               if (var.start == insn.getNext()) return true;\r
+                               else {\r
+                                       if (var.start != var.end &&\r
+                                               var.end.getPrevious() != null && \r
+                                               (isStoreOperation(var.end.getPrevious()) || \r
+                                               isIncrementOperation(var.end.getPrevious()))) {\r
+                                               scopeEndInstruction = var.end.getPrevious(); \r
+                                       } else {\r
+                                               scopeEndInstruction = var.end;\r
+                                       }\r
+                               }\r
+                       } else {\r
+                               assert isLoadOperation(insn) || isRET(insn);\r
+                               scopeEndInstruction = var.end;\r
+                       }\r
+                       boolean inScope = false;\r
+                       AbstractInsnNode pos = var.start;\r
+                       while ((pos != null)&&(pos != scopeEndInstruction)) {\r
+                               if (pos == insn) {\r
+                                       inScope = true;\r
+                                       break;\r
+                               }\r
+                               pos = pos.getNext();\r
+                       }\r
+                       return inScope;\r
+                       \r
+               } else {\r
+                       return false;\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * This method returns a local variable node accessed by \r
+        * the specified VarInsnNode (if exists). \r
+        * \r
+        * This method is necessary because two or more variables may \r
+        * have the same varIndex.\r
+        * It should be noted that a variable "x" becomes in-scope only AFTER\r
+        * its variable declaration such as "C x = v;".\r
+        * The STORE instruction accessing "x" is out of scope of the variable.\r
+        */\r
+       public static LocalVariableNode findLocalVariable(MethodNode method, AbstractInsnNode insn) {\r
+               if (method.localVariables == null) return null;\r
+\r
+               List<?> variables = method.localVariables;\r
+               for (int i=0; i<variables.size(); ++i) {\r
+                       LocalVariableNode var = (LocalVariableNode)variables.get(i);\r
+                       if (isAccess(insn, var)) {\r
+                               return var;\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+\r
+       \r
+       /**\r
+        * Return true if a node is an instruction to load a return address\r
+        * that is stored into an anonymous local variable.\r
+        * @param node\r
+        * @return\r
+        */\r
+       public static boolean isAfterJSR(AbstractInsnNode node) {\r
+               if (node.getOpcode() != Opcodes.ALOAD) return false;\r
+               \r
+               AbstractInsnNode n = node.getPrevious();\r
+               if (n.getType() == AbstractInsnNode.LABEL) {\r
+                       n = n.getPrevious();\r
+               }\r
+               return n != null && n.getOpcode() == Opcodes.JSR;\r
+       }\r
+       \r
+       public static boolean isDefUseOperation(AbstractInsnNode node) {\r
+               // Note: VarInsn and IincInsn except for "RET" instruction.\r
+               return isStoreOperation(node) || isLoadOperation(node) || isIncrementOperation(node);\r
+       }\r
+       \r
+       public static boolean isIncrementOperation(AbstractInsnNode node) {\r
+               return node.getOpcode() == Opcodes.IINC;\r
+       }\r
+       \r
+       public static boolean isPrimitiveOperation(AbstractInsnNode node) {\r
+               int opcode = node.getOpcode();\r
+               return (opcode == Opcodes.ISTORE ||\r
+                               opcode == Opcodes.FSTORE ||\r
+                               opcode == Opcodes.LSTORE ||\r
+                               opcode == Opcodes.DSTORE ||\r
+                               opcode == Opcodes.ILOAD ||\r
+                               opcode == Opcodes.FLOAD ||\r
+                               opcode == Opcodes.LLOAD ||\r
+                               opcode == Opcodes.DLOAD ||\r
+                               opcode == Opcodes.IINC);\r
+       }\r
+       \r
+       public static boolean isStoreOperation(AbstractInsnNode node) {\r
+               int opcode = node.getOpcode();\r
+               return (opcode == Opcodes.ISTORE ||\r
+                       opcode == Opcodes.ASTORE ||\r
+                       opcode == Opcodes.FSTORE ||\r
+                       opcode == Opcodes.LSTORE ||\r
+                       opcode == Opcodes.DSTORE);\r
+       }\r
+\r
+       public static boolean isRET(AbstractInsnNode node) {\r
+               return node.getOpcode() == Opcodes.RET;\r
+       }\r
+       \r
+       public static boolean isLocalReferenceOperation(AbstractInsnNode node) {\r
+               return isLoadOperation(node) || node.getOpcode() == Opcodes.IINC;\r
+       }\r
+       \r
+       public static boolean isLoadOperation(AbstractInsnNode node) {\r
+               int opcode = node.getOpcode();\r
+               return (opcode == Opcodes.ILOAD ||\r
+                               opcode == Opcodes.ALOAD ||\r
+                               opcode == Opcodes.FLOAD ||\r
+                               opcode == Opcodes.LLOAD ||\r
+                               opcode == Opcodes.DLOAD);\r
+       }\r
+       \r
+       public static boolean isReturnOperation(AbstractInsnNode node) {\r
+               int opcode = node.getOpcode();\r
+               return (opcode == Opcodes.IRETURN ||\r
+                               opcode == Opcodes.LRETURN ||\r
+                               opcode == Opcodes.FRETURN ||\r
+                               opcode == Opcodes.DRETURN ||\r
+                               opcode == Opcodes.ARETURN ||\r
+                               opcode == Opcodes.RETURN);\r
+       }\r
+       \r
+       public static boolean isConstantOperation(int opcode) {\r
+               switch (opcode) {               \r
+               case Opcodes.ACONST_NULL:\r
+               case Opcodes.BIPUSH:\r
+               case Opcodes.DCONST_0:\r
+               case Opcodes.DCONST_1:\r
+               case Opcodes.FCONST_0:\r
+               case Opcodes.FCONST_1:\r
+               case Opcodes.FCONST_2:\r
+               case Opcodes.ICONST_0:\r
+               case Opcodes.ICONST_1:\r
+               case Opcodes.ICONST_2:\r
+               case Opcodes.ICONST_3:\r
+               case Opcodes.ICONST_4:\r
+               case Opcodes.ICONST_5:\r
+               case Opcodes.ICONST_M1:\r
+               case Opcodes.LCONST_0:\r
+               case Opcodes.LCONST_1:\r
+               case Opcodes.LDC:\r
+               case Opcodes.SIPUSH:\r
+                       return true;\r
+                       \r
+               default:\r
+                       return false;\r
+               }\r
+       }\r
+       \r
+       public static String getVariableString(LocalVariableNode node) { \r
+               return node.name + ": " + TypeResolver.getTypeName(node.desc);\r
+       }\r
+       \r
+\r
+       /**\r
+        * @return a string representation for a label node.\r
+        * @see getLabelString(MethodNode, int)\r
+        */\r
+       public static String getLabelString(MethodNode method, LabelNode label) {\r
+               return getLabelString(method, method.instructions.indexOf(label));\r
+       }\r
+\r
+       /**\r
+        * @return a string representation for a label located in a position specified by index.\r
+        * While label.toString() returns a different string for each execution,\r
+        * this method returns the same string if a label is located in the same position in a method.\r
+        */\r
+       public static String getLabelString(MethodNode method, int index) {\r
+               assert method.instructions.get(index).getType() == AbstractInsnNode.LABEL;\r
+               String right = "00000" + Integer.toString(index);\r
+               return "L" + right.substring(right.length()-5);\r
+       }\r
+       \r
+       /**\r
+        * @param method specifies a method containing an instruction.\r
+        * @param index specifies the position of an instruction in the list of instructions.\r
+        * @return a string representation of an instruction.\r
+        */\r
+       public static String getInstructionString(MethodNode method, int index) {\r
+               if (index == -1) return "ARG";\r
+               \r
+               AbstractInsnNode node = method.instructions.get(index);\r
+               int opcode = node.getOpcode();\r
+               String op = Integer.toString(index) + ": " + OpcodeString.getString(opcode);\r
+\r
+               switch (node.getType()) {\r
+               case AbstractInsnNode.VAR_INSN:\r
+               case AbstractInsnNode.IINC_INSN:\r
+                       \r
+                       LocalVariableNode n = OpcodeString.findLocalVariable(method, node);\r
+                       if (n != null) {\r
+                               return op + " " + Integer.toString(n.index) + " (" + n.name + ")";\r
+                       } else {\r
+                               int varIndex = OpcodeString.getVarIndex(node);\r
+                               return op + " " + Integer.toString(varIndex);\r
+                       }\r
+               \r
+               case AbstractInsnNode.FIELD_INSN:\r
+                       FieldInsnNode fieldNode = (FieldInsnNode)node;\r
+                       return op + " " + fieldNode.owner + "#" + fieldNode.name + ": " + TypeResolver.getTypeName(fieldNode.desc);\r
+                       \r
+               case AbstractInsnNode.METHOD_INSN:\r
+                       MethodInsnNode methodInsnNode = (MethodInsnNode)node;\r
+                       return op + " " + methodInsnNode.owner + "#" + methodInsnNode.name + methodInsnNode.desc;\r
+               \r
+               case AbstractInsnNode.LINE:\r
+                       LineNumberNode lineNode = (LineNumberNode)node;\r
+                       return Integer.toString(index) + ": " + "(line=" + lineNode.line + ")";\r
+                       \r
+               case AbstractInsnNode.LABEL:\r
+                       return Integer.toString(index) + ": " + "(" + getLabelString(method, index) + ")";\r
+                       \r
+               case AbstractInsnNode.JUMP_INSN:\r
+                       JumpInsnNode jumpNode = (JumpInsnNode)node;\r
+                       return op + " " + getLabelString(method, jumpNode.label);\r
+                       \r
+               case AbstractInsnNode.FRAME:\r
+                       FrameNode frameNode = (FrameNode)node;\r
+                       return Integer.toString(index) + ": FRAME-OP(" + frameNode.type + ")";\r
+                       \r
+               case AbstractInsnNode.LDC_INSN:\r
+                       LdcInsnNode ldc = (LdcInsnNode)node;\r
+                       return op + " " + ldc.cst.toString();\r
+               \r
+               default: \r
+                       return op; \r
+               }\r
+       }\r
+       \r
+       private static String getString(int opcode) {\r
+               if (0 <= opcode && opcode < opcodeNames.length) {\r
+                       return opcodeNames[opcode];\r
+               } else {\r
+                       return Integer.toString(opcode);\r
+               }\r
+       }\r
+       \r
+       private static String[] opcodeNames = new String[] { \r
+           "NOP", "ACONST_NULL", "ICONST_M1", "ICONST_0", \r
+           "ICONST_1", "ICONST_2", "ICONST_3", "ICONST_4", \r
+           "ICONST_5", "LCONST_0", "LCONST_1", "FCONST_0",\r
+           "FCONST_1", "FCONST_2", "DCONST_0", "DCONST_1",\r
+           "BIPUSH", "SIPUSH", "LDC", "LDC_W", \r
+           "LDC2_W", "ILOAD", "LLOAD", "FLOAD", \r
+           "DLOAD", "ALOAD", "ILOAD_0", "ILOAD_1",\r
+           "ILOAD_2", "ILOAD_3", "LLOAD_0", "LLOAD_1",\r
+           "LLOAD_2", "LLOAD_3", "FLOAD_0", "FLOAD_1",\r
+           "FLOAD_2", "FLOAD_3", "DLOAD_0", "DLOAD_1",\r
+           "DLOAD_2", "DLOAD_3", "ALOAD_0", "ALOAD_1",\r
+           "ALOAD_2", "ALOAD_3", "IALOAD", "LALOAD",\r
+           "FALOAD", "DALOAD", "AALOAD", "BALOAD",\r
+           "CALOAD", "SALOAD", "ISTORE", "LSTORE",\r
+           "FSTORE", "DSTORE", "ASTORE", "ISTORE_0",\r
+           "ISTORE_1", "ISTORE_2", "ISTORE_3", "LSTORE_0",\r
+           "LSTORE_1", "LSTORE_2", "LSTORE_3", "FSTORE_0",\r
+           "FSTORE_1", "FSTORE_2", "FSTORE_3", "DSTORE_0",\r
+           "DSTORE_1", "DSTORE_2", "DSTORE_3", "ASTORE_0",\r
+           "ASTORE_1", "ASTORE_2", "ASTORE_3", "IASTORE",\r
+           "LASTORE", "FASTORE", "DASTORE", "AASTORE",\r
+           "BASTORE", "CASTORE", "SASTORE", "POP",\r
+           "POP2", "DUP", "DUP_X1", "DUP_X2",\r
+           "DUP2", "DUP2_X1", "DUP_X2", "SWAP",\r
+           "IADD", "LADD", "FADD", "DADD",\r
+           "ISUB", "LSUB", "FSUB", "DSUB",\r
+           "IMUL", "LMUL", "FMUL", "DMUL",\r
+           "IDIV", "LDIV", "FDIV", "DDIV",\r
+           "IREM", "LREM", "FREM", "DREM",\r
+           "INEG", "LNEG", "FNEG", "DNEG",\r
+           "ISHL", "LSHL", "ISHR", "LSHR",\r
+           "IUSHR","LUSHR", "IAND", "LAND",\r
+           "IOR", "LOR", "IXOR", "LXOR",\r
+           "IINC", "I2L", "I2F", "I2D",\r
+           "L2I", "L2F", "L2D", "F2I",\r
+           "F2L", "F2D", "D2I", "D2L",\r
+           "D2F", "I2B", "I2C", "I2S",\r
+           "LCMP", "FCMPL", "FCMPG", "DCMPL",\r
+           "DCMPG", "IFEQ", "IFNE", "IFLT",\r
+           "IFGE", "IFGT", "IFLE", "IF_ICMPEQ",\r
+           "IF_ICMPNE", "IF_ICMPLT", "IF_ICMPGE", "IF_ICMPGT",\r
+           "IF_ICMPLE", "IF_ACMPEQ", "IF_ACMPNE", "GOTO",\r
+           "JSR", "RET", "TABLESWITCH", "LOOKUPSWITCH", \r
+           "IRETURN", "LRETURN", "FRETURN", "DRETURN",\r
+           "ARETURN", "RETURN", "GETSTATIC", "PUTSTATIC",\r
+           "GETFIELD", "PUTFIELD", "INVOKEVIRTUAL", "INVOKESPECIAL",\r
+           "INVOKESTATIC", "INVOKEINTERFACE", "INVOKEDYNAMIC", "NEW", \r
+           "NEWARRAY", "ANEWARRAY", "ARRAYLENGTH", "ATHROW",\r
+           "CHECKCAST", "INSTANCEOF", "MONITORENTER", "MONITOREXIT", \r
+           "WIDE", "MULTIANEWARRAY", "IFNULL", "IFNONNULL", \r
+           "GOTO_W",  "JSR_W" \r
+       };\r
+\r
+       static {\r
+               assert opcodeNames.length == 202;\r
+       }\r
+}\r
diff --git a/src/soba/core/method/asm/DataFlowAnalyzer.java b/src/soba/core/method/asm/DataFlowAnalyzer.java
new file mode 100755 (executable)
index 0000000..2b26b18
--- /dev/null
@@ -0,0 +1,59 @@
+package soba.core.method.asm;\r
+\r
+import org.objectweb.asm.tree.MethodNode;\r
+import org.objectweb.asm.tree.analysis.Analyzer;\r
+import org.objectweb.asm.tree.analysis.AnalyzerException;\r
+import org.objectweb.asm.tree.analysis.Frame;\r
+import org.objectweb.asm.tree.analysis.Value;\r
+\r
+import soba.util.IntPairList;\r
+import soba.util.IntPairSet;\r
+import soba.util.IntPairUtil;\r
+\r
+public class DataFlowAnalyzer extends Analyzer<Value> {\r
+\r
+       private MethodNode method;\r
+    private IntPairSet controlFlow = new IntPairSet();\r
+    private IntPairSet exceptionalFlow = new IntPairSet();\r
+    private DataFlowInterpreter interpreter;\r
+\r
+       public DataFlowAnalyzer(DataFlowInterpreter interpreter) {\r
+               super(interpreter);\r
+               this.interpreter = interpreter;\r
+       }\r
+       \r
+       @Override\r
+       public Frame<Value>[] analyze(String owner, MethodNode m) throws AnalyzerException {\r
+               this.method = m;\r
+               return super.analyze(owner, m);\r
+       }\r
+       \r
+       @Override\r
+       protected void newControlFlowEdge(int insn, int successor) {\r
+               controlFlow.add(insn, successor);\r
+               super.newControlFlowEdge(insn, successor);\r
+       }\r
+       \r
+       @Override\r
+       protected boolean newControlFlowExceptionEdge(int insn, int successor) {\r
+               exceptionalFlow.add(insn, successor);\r
+               return super.newControlFlowExceptionEdge(insn, successor);\r
+       }\r
+       \r
+       public MethodNode getAnalyzedMethod() {\r
+               return method;\r
+       }\r
+       \r
+       public IntPairList getNormalControlFlow() {\r
+               return IntPairUtil.createList(controlFlow);\r
+       }\r
+       \r
+       public IntPairList getConservativeControlFlow() {\r
+               return IntPairUtil.createList(controlFlow, exceptionalFlow);\r
+       }\r
+       \r
+       public int getOperandCount(int instructionIndex) {\r
+               return interpreter.getOperandCount(instructionIndex);\r
+       }\r
+       \r
+}\r
diff --git a/src/soba/core/method/asm/DataFlowInterpreter.java b/src/soba/core/method/asm/DataFlowInterpreter.java
new file mode 100755 (executable)
index 0000000..fc550db
--- /dev/null
@@ -0,0 +1,115 @@
+package soba.core.method.asm;\r
+\r
+import java.util.List;\r
+\r
+import org.objectweb.asm.Type;\r
+import org.objectweb.asm.tree.AbstractInsnNode;\r
+import org.objectweb.asm.tree.analysis.Value;\r
+\r
+import soba.util.ObjectIdMap;\r
+\r
+public class DataFlowInterpreter extends FastSourceInterpreter {\r
+       \r
+       private ObjectIdMap<AbstractInsnNode> instructions;\r
+       private int[] operands;\r
+       \r
+       public DataFlowInterpreter(ObjectIdMap<AbstractInsnNode> instructions) {\r
+               super(instructions);\r
+               this.instructions = instructions;\r
+               this.operands = new int[instructions.size()];\r
+       }\r
+       \r
+       public int getInstructionCount() {\r
+               return instructions.size();\r
+       }\r
+       \r
+       public int getOperandCount(int opIndex) { \r
+               return operands[opIndex];\r
+       }\r
+\r
+       @Override\r
+       public Value unaryOperation(AbstractInsnNode insn, Value value) {\r
+               if (insn.getOpcode() == IINC) {\r
+                       // IINC does not use Operand Stack.\r
+                       operands[ instructions.getId(insn) ] = 0;\r
+                       return super.unaryOperation(insn, value);\r
+               } else {\r
+                       operands[ instructions.getId(insn) ] = 1;\r
+                       return super.unaryOperation(insn, value);\r
+               }\r
+       }\r
+       \r
+       @Override\r
+       public void returnOperation(AbstractInsnNode insn, Value value,\r
+                       Value expected) {\r
+               operands[ instructions.getId(insn) ] = 1;\r
+               super.returnOperation(insn, value, expected);\r
+       }\r
+       \r
+       @Override\r
+       public Value ternaryOperation(AbstractInsnNode insn, Value value1,\r
+                       Value value2, Value value3) {\r
+               operands[ instructions.getId(insn) ] = 3;\r
+               return super.ternaryOperation(insn, value1, value2, value3);\r
+       }\r
+       \r
+       @Override\r
+       public Value binaryOperation(AbstractInsnNode insn, Value value1,\r
+                       Value value2) {\r
+               operands[ instructions.getId(insn) ] = 2;\r
+               return super.binaryOperation(insn, value1, value2);\r
+       }\r
+       \r
+       @Override\r
+       public Value copyOperation(AbstractInsnNode insn, Value value) {\r
+               int operandCount = 0;\r
+               switch (insn.getOpcode()) {\r
+               case ILOAD:\r
+               case LLOAD:\r
+               case FLOAD:\r
+               case DLOAD:\r
+               case ALOAD:\r
+                       operandCount = 0;\r
+                       break;\r
+               case ISTORE:\r
+               case LSTORE:\r
+               case FSTORE:\r
+               case DSTORE:\r
+               case ASTORE:\r
+                       operandCount = 1;\r
+                       break;\r
+\r
+               case DUP:\r
+               case DUP_X1:\r
+               case DUP_X2:\r
+               case DUP2:\r
+               case DUP2_X1:\r
+               case DUP2_X2:\r
+               case SWAP:\r
+                       // DUP and SWAP do not modify the value.\r
+                       // Propagate the defined value.\r
+                       return value;\r
+                       \r
+               }\r
+               operands[ instructions.getId(insn) ] = operandCount;\r
+               return super.copyOperation(insn, value);\r
+       }\r
+       \r
+       @Override\r
+       public Value newValue(Type type) {\r
+               return super.newValue(type);\r
+       }\r
+       \r
+       @Override\r
+       public Value newOperation(AbstractInsnNode insn) {\r
+               // no arguments\r
+               return super.newOperation(insn);\r
+       }\r
+       \r
+       @Override \r
+       public Value naryOperation(AbstractInsnNode insn, List<? extends Value> values) {\r
+               operands[ instructions.getId(insn) ] = values.size();\r
+               return super.naryOperation(insn, values);\r
+       }\r
+       \r
+}\r
diff --git a/src/soba/core/method/asm/FastSourceInterpreter.java b/src/soba/core/method/asm/FastSourceInterpreter.java
new file mode 100755 (executable)
index 0000000..a44310b
--- /dev/null
@@ -0,0 +1,208 @@
+/**\r
+ * Note: this class is implemented based on SourceInterpreter involved in ASM.\r
+ * \r
+ * This class is different from SourceInterpreter:\r
+ * SourceInterpreter uses an empty set to represent a data-flow from a method parameter.\r
+ * Therefore, the class fails to merge a data-flow path from a method parameter \r
+ * and aother path that overwrites the value.\r
+ * An example code is soba.testdata.DefUseTestData.overwriteParam().\r
+ */\r
+/***\r
+ * ASM: a very small and fast Java bytecode manipulation framework\r
+ * Copyright (c) 2000-2007 INRIA, France Telecom\r
+ * All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions\r
+ * are met:\r
+ * 1. Redistributions of source code must retain the above copyright\r
+ *    notice, this list of conditions and the following disclaimer.\r
+ * 2. Redistributions in binary form must reproduce the above copyright\r
+ *    notice, this list of conditions and the following disclaimer in the\r
+ *    documentation and/or other materials provided with the distribution.\r
+ * 3. Neither the name of the copyright holders nor the names of its\r
+ *    contributors may be used to endorse or promote products derived from\r
+ *    this software without specific prior written permission.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\r
+ * THE POSSIBILITY OF SUCH DAMAGE.\r
+ */\r
+\r
+package soba.core.method.asm;\r
+\r
+import java.util.List;\r
+\r
+import org.objectweb.asm.Opcodes;\r
+import org.objectweb.asm.Type;\r
+import org.objectweb.asm.tree.AbstractInsnNode;\r
+import org.objectweb.asm.tree.FieldInsnNode;\r
+import org.objectweb.asm.tree.InvokeDynamicInsnNode;\r
+import org.objectweb.asm.tree.LdcInsnNode;\r
+import org.objectweb.asm.tree.MethodInsnNode;\r
+import org.objectweb.asm.tree.analysis.Interpreter;\r
+import org.objectweb.asm.tree.analysis.Value;\r
+\r
+import soba.util.ObjectIdMap;\r
+\r
+public class FastSourceInterpreter extends Interpreter<Value> implements Opcodes {\r
+       \r
+       public static final int METHOD_ENTRY = -1;\r
+       private ObjectIdMap<AbstractInsnNode> instructions;\r
+       \r
+       public FastSourceInterpreter(ObjectIdMap<AbstractInsnNode> instructions) {\r
+               super(ASM5);\r
+               this.instructions = instructions;\r
+       }\r
+\r
+       /**\r
+        * This implementation is different from SourceInterpreter.\r
+        * We distinguish a method parameter with an "un-initialized" \r
+        * entry for double-word data.\r
+        */\r
+    public Value newValue(final Type type) {\r
+       if (type == null) {\r
+               // an anonymous value that fills the second entry for double-word data.\r
+               return new FastSourceValue(1);\r
+       } else if (type == Type.VOID_TYPE) {\r
+            return null;\r
+        } else {\r
+            return new FastSourceValue(type.getSize(), METHOD_ENTRY);\r
+        }\r
+    }\r
+\r
+    public Value newOperation(final AbstractInsnNode insn) {\r
+        int size;\r
+        switch (insn.getOpcode()) {\r
+            case LCONST_0:\r
+            case LCONST_1:\r
+            case DCONST_0:\r
+            case DCONST_1:\r
+                size = 2;\r
+                break;\r
+            case LDC:\r
+                Object cst = ((LdcInsnNode) insn).cst;\r
+                size = cst instanceof Long || cst instanceof Double ? 2 : 1;\r
+                break;\r
+            case GETSTATIC:\r
+                size = Type.getType(((FieldInsnNode) insn).desc).getSize();\r
+                break;\r
+            default:\r
+                size = 1;\r
+        }\r
+        return new FastSourceValue(size, instructions.getId(insn));\r
+    }\r
+\r
+    public Value copyOperation(final AbstractInsnNode insn, final Value value) {\r
+        return new FastSourceValue(value.getSize(), instructions.getId(insn));\r
+    }\r
+\r
+    public Value unaryOperation(final AbstractInsnNode insn, final Value value)\r
+    {\r
+        int size;\r
+        switch (insn.getOpcode()) {\r
+            case LNEG:\r
+            case DNEG:\r
+            case I2L:\r
+            case I2D:\r
+            case L2D:\r
+            case F2L:\r
+            case F2D:\r
+            case D2L:\r
+                size = 2;\r
+                break;\r
+            case GETFIELD:\r
+                size = Type.getType(((FieldInsnNode) insn).desc).getSize();\r
+                break;\r
+            default:\r
+                size = 1;\r
+        }\r
+        return new FastSourceValue(size, instructions.getId(insn));\r
+    }\r
+\r
+    public Value binaryOperation(\r
+        final AbstractInsnNode insn,\r
+        final Value value1,\r
+        final Value value2)\r
+    {\r
+        int size;\r
+        switch (insn.getOpcode()) {\r
+            case LALOAD:\r
+            case DALOAD:\r
+            case LADD:\r
+            case DADD:\r
+            case LSUB:\r
+            case DSUB:\r
+            case LMUL:\r
+            case DMUL:\r
+            case LDIV:\r
+            case DDIV:\r
+            case LREM:\r
+            case DREM:\r
+            case LSHL:\r
+            case LSHR:\r
+            case LUSHR:\r
+            case LAND:\r
+            case LOR:\r
+            case LXOR:\r
+                size = 2;\r
+                break;\r
+            default:\r
+                size = 1;\r
+        }\r
+        return new FastSourceValue(size, instructions.getId(insn));\r
+    }\r
+\r
+    public Value ternaryOperation(\r
+        final AbstractInsnNode insn,\r
+        final Value value1,\r
+        final Value value2,\r
+        final Value value3)\r
+    {\r
+        return new FastSourceValue(1, instructions.getId(insn));\r
+    }\r
+\r
+    public Value naryOperation(final AbstractInsnNode insn, final List<? extends Value> values) {\r
+        int size;\r
+        if (insn.getOpcode() == MULTIANEWARRAY) {\r
+            size = 1;\r
+        } else if (insn.getOpcode() == INVOKEDYNAMIC) {\r
+                size = Type.getReturnType(((InvokeDynamicInsnNode) insn).desc).getSize();\r
+        } else {\r
+            size = Type.getReturnType(((MethodInsnNode) insn).desc).getSize();\r
+        }\r
+        return new FastSourceValue(size, instructions.getId(insn));\r
+    }\r
+\r
+    public void returnOperation(\r
+        final AbstractInsnNode insn,\r
+        final Value value,\r
+        final Value expected)\r
+    {\r
+    }\r
+\r
+    /**\r
+     * Implementation Note: This method must return v \r
+     * if v contains all elements in w.\r
+     */\r
+    public Value merge(final Value v, final Value w) {\r
+        if (v == w) return v;\r
+        FastSourceValue dv = (FastSourceValue)v;\r
+        FastSourceValue dw = (FastSourceValue)w;\r
+\r
+       if (dv.getSize() == dw.getSize() && dv.containsAll(dw)) {\r
+               return v;\r
+        } else {\r
+            return new FastSourceValue(dv, dw);\r
+        }\r
+    }\r
+\r
+}\r
diff --git a/src/soba/core/method/asm/FastSourceValue.java b/src/soba/core/method/asm/FastSourceValue.java
new file mode 100755 (executable)
index 0000000..fcb7849
--- /dev/null
@@ -0,0 +1,153 @@
+/**\r
+ * This class is implemented based on SourceValue involved in ASM.\r
+ */\r
+/***\r
+ * ASM: a very small and fast Java bytecode manipulation framework\r
+ * Copyright (c) 2000-2007 INRIA, France Telecom\r
+ * All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions\r
+ * are met:\r
+ * 1. Redistributions of source code must retain the above copyright\r
+ *    notice, this list of conditions and the following disclaimer.\r
+ * 2. Redistributions in binary form must reproduce the above copyright\r
+ *    notice, this list of conditions and the following disclaimer in the\r
+ *    documentation and/or other materials provided with the distribution.\r
+ * 3. Neither the name of the copyright holders nor the names of its\r
+ *    contributors may be used to endorse or promote products derived from\r
+ *    this software without specific prior written permission.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\r
+ * THE POSSIBILITY OF SUCH DAMAGE.\r
+ */\r
+package soba.core.method.asm;\r
+\r
+\r
+\r
+import java.util.Arrays;\r
+\r
+import gnu.trove.list.array.TIntArrayList;\r
+\r
+import org.objectweb.asm.tree.analysis.Value;\r
+\r
+public class FastSourceValue implements Value {\r
+\r
+    /**\r
+     * The number of words indicating the value.\r
+     */\r
+    private final int size;\r
+\r
+    /**\r
+     * Index of instructions that define the value.\r
+     */\r
+    private int[] instructions;\r
+    \r
+    private static int[] EMPTY_ARRAY = new int[0];\r
+\r
+    public FastSourceValue(int size) {\r
+       this.size = size;\r
+       this.instructions = EMPTY_ARRAY;\r
+    }\r
+\r
+    public FastSourceValue(int size, int instructionIndex) {\r
+        this.size = size;\r
+        this.instructions = new int[] { instructionIndex };\r
+    }\r
+\r
+    public FastSourceValue(int size, int[] instructionIndices) {\r
+        this.size = size;\r
+        this.instructions = instructionIndices;\r
+    }\r
+    \r
+    public int getSize() {\r
+        return size;\r
+    }\r
+    \r
+    public int[] getInstructions() {\r
+       return instructions;\r
+    }\r
+    \r
+    public FastSourceValue(FastSourceValue base1, FastSourceValue base2) {\r
+       this.size = Math.min(base1.size, base2.size);\r
+       TIntArrayList list = new TIntArrayList(base1.instructions.length + base2.instructions.length);\r
+       int index1 = 0;\r
+       int index2 = 0;\r
+       while (index1 < base1.instructions.length && index2 < base2.instructions.length) {\r
+                       int v1 = base1.instructions[index1];\r
+                       int v2 = base2.instructions[index2];\r
+                       if (v1 == v2) {\r
+                               // Add only one element (behaves as "Set")\r
+                               list.add(v1);\r
+                               index1++;\r
+                               index2++;\r
+                       } else if (v1 < v2) {\r
+                               list.add(v1);\r
+                               index1++;\r
+                       } else { //v1 > v2\r
+                               list.add(v2);\r
+                               index2++;\r
+                       }\r
+       }\r
+       while (index1 < base1.instructions.length) {\r
+               list.add(base1.instructions[index1]);\r
+               index1++;\r
+       }\r
+       while (index2 < base2.instructions.length) {\r
+               list.add(base2.instructions[index2]);\r
+               index2++;\r
+       }\r
+       this.instructions = list.toArray();\r
+    }\r
+    \r
+    public boolean containsAll(FastSourceValue another) {\r
+       int thisIndex = 0;\r
+       int anotherIndex = 0;\r
+       while (anotherIndex < another.instructions.length) {\r
+               if (thisIndex < this.instructions.length) {\r
+                       int thisValue = instructions[thisIndex];\r
+                       int anotherValue = another.instructions[anotherIndex];\r
+                       if (thisValue > anotherValue) {\r
+                               return false;\r
+                       } else if (thisValue == anotherValue) {\r
+                               thisIndex++;\r
+                               anotherIndex++;\r
+                       } else { // thisValue < anotherValue\r
+                               thisIndex++;\r
+                       }\r
+                       \r
+               } else {\r
+                       // End of this.instructions, but not the end of another.instruction\r
+                       return false;\r
+               }\r
+       }\r
+       return true;\r
+    }\r
+\r
+    /**\r
+     * Two FastSourceValues are the same if the value \r
+     * is defined by the same instructions.\r
+     */\r
+    public boolean equals(Object another) {\r
+       if (another instanceof FastSourceValue) {\r
+            FastSourceValue v = (FastSourceValue)another;\r
+            return size == v.size && Arrays.equals(instructions, v.instructions);\r
+       } else {\r
+               return false;\r
+       }\r
+    }\r
+\r
+    public int hashCode() {\r
+       return Arrays.hashCode(instructions);\r
+    }\r
+\r
+}\r
diff --git a/src/soba/core/signature/MethodSignatureReader.java b/src/soba/core/signature/MethodSignatureReader.java
new file mode 100755 (executable)
index 0000000..ad164a8
--- /dev/null
@@ -0,0 +1,159 @@
+package soba.core.signature;\r
+\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+import org.objectweb.asm.Opcodes;\r
+import org.objectweb.asm.signature.SignatureReader;\r
+import org.objectweb.asm.signature.SignatureVisitor;\r
+\r
+\r
+/**\r
+ * This class translates a method signature to \r
+ * a list of parameters, a return type, and a list of exception types.\r
+ * TODO The current implementation ignores type arguments for generic class.\r
+ */\r
+public class MethodSignatureReader {\r
+\r
+       private int paramCount;\r
+       private TypeVisitor returnTypeVisitor;\r
+       private List<TypeVisitor> exceptionTypeVisitors;\r
+       private List<TypeVisitor> paramTypeVisitors;\r
+\r
+       /**\r
+        * Creates a new <code>MethodSignatureReader</code> instance.\r
+        * @param signature\r
+        */\r
+       public MethodSignatureReader(String signature) {\r
+               exceptionTypeVisitors = new ArrayList<TypeVisitor>();\r
+               paramTypeVisitors = new ArrayList<TypeVisitor>();\r
+               paramCount = 0;\r
+\r
+               SignatureReader sigReader = new SignatureReader(signature);\r
+               sigReader.accept(new SignatureVisitor(Opcodes.ASM5) {\r
+                       \r
+                       /* \r
+                        * SignatureVisitor for a method signature receives a following method call sequence.\r
+                        * ( visitFormalTypeParameter visitClassBound? visitInterfaceBound* )* \r
+                        * ( visitParameterType* \r
+                        *   visitReturnType \r
+                        *   visitExceptionType* )\r
+                        */\r
+\r
+                       @Override\r
+                       public void visitFormalTypeParameter(String name) {\r
+                       }\r
+                       \r
+                       @Override\r
+                       public SignatureVisitor visitParameterType() {\r
+                               paramCount++;\r
+                               TypeVisitor param = new TypeVisitor();\r
+                               paramTypeVisitors.add(param);\r
+                               return param;\r
+                       }\r
+\r
+                       @Override\r
+                       public SignatureVisitor visitReturnType() {\r
+                               returnTypeVisitor = new TypeVisitor();\r
+                               return returnTypeVisitor;\r
+                       }\r
+                       \r
+                       @Override\r
+                       public SignatureVisitor visitExceptionType() {\r
+                               TypeVisitor exceptionTypeVisitor = new TypeVisitor();\r
+                               exceptionTypeVisitors.add(exceptionTypeVisitor);\r
+                               return exceptionTypeVisitor;\r
+                       }\r
+                       \r
+                       \r
+                       @Override\r
+                       public void visitTypeVariable(String name) {\r
+                       }\r
+                       \r
+                       @Override\r
+                       public SignatureVisitor visitTypeArgument(char wildcard) {\r
+                               return this;\r
+                       }\r
+                       \r
+                       @Override\r
+                       public void visitTypeArgument() {\r
+                       }\r
+                       \r
+                       @Override\r
+                       public SignatureVisitor visitSuperclass() {\r
+                               return this;\r
+                       }\r
+                       \r
+                       @Override\r
+                       public SignatureVisitor visitInterfaceBound() {\r
+                               return this;\r
+                       }\r
+                       \r
+                       @Override\r
+                       public SignatureVisitor visitInterface() {\r
+                               return this;\r
+                       }\r
+                       \r
+                       @Override\r
+                       public void visitInnerClassType(String name) {\r
+                       }\r
+\r
+                       @Override\r
+                       public void visitEnd() {\r
+                       }\r
+                       \r
+                       @Override\r
+                       public void visitClassType(String name) {\r
+                       }\r
+                       \r
+                       @Override\r
+                       public SignatureVisitor visitClassBound() {\r
+                               return this;\r
+                       }\r
+                       \r
+                       @Override\r
+                       public void visitBaseType(char descriptor) {\r
+                       }\r
+                       \r
+                       @Override\r
+                       public SignatureVisitor visitArrayType() {\r
+                               return null;\r
+                       }\r
+               });\r
+               assert paramTypeVisitors.size() == paramCount: \r
+                           "Failed to read a method signature: paramTypes=" + Integer.toString(paramTypeVisitors.size()) + ", paramCount=" + Integer.toString(paramCount);\r
+       }\r
+       \r
+       public int getParamCount() {\r
+               return paramCount;\r
+       }\r
+       \r
+       public String getParamType(int paramIndex) {\r
+               return paramTypeVisitors.get(paramIndex).getTypeName();\r
+       }\r
+\r
+       public boolean isGenericType(int paramIndex) {\r
+               return paramTypeVisitors.get(paramIndex).isGenericType();\r
+       }\r
+       \r
+       public String getReturnType() {\r
+               return returnTypeVisitor.getTypeName();\r
+       }\r
+\r
+       public boolean isGenericReturnType() {\r
+               return returnTypeVisitor.isGenericType();\r
+       }\r
+       \r
+       public int getExceptionCount() {\r
+               return exceptionTypeVisitors.size();\r
+       }\r
+       \r
+       public String getExceptionType(int exceptionIndex) {  \r
+               return exceptionTypeVisitors.get(exceptionIndex).getTypeName();\r
+       }\r
+\r
+       public boolean isGenericExceptionType(int exceptionIndex) {  \r
+               return exceptionTypeVisitors.get(exceptionIndex).isGenericType();\r
+       }\r
+\r
+}\r
diff --git a/src/soba/core/signature/TypeConstants.java b/src/soba/core/signature/TypeConstants.java
new file mode 100755 (executable)
index 0000000..9c4cd9e
--- /dev/null
@@ -0,0 +1,66 @@
+package soba.core.signature;\r
+\r
+public class TypeConstants {\r
+\r
+       public static final String BOOLEAN = "boolean";\r
+       public static final String BYTE = "byte";\r
+       public static final String CHAR = "char";\r
+       public static final String SHORT = "short";\r
+       public static final String INT = "int";\r
+       public static final String LONG = "long";\r
+       public static final String FLOAT = "float";\r
+       public static final String DOUBLE = "double";\r
+       public static final String VOID = "void";\r
+       public static final String JAVA_STRING = "java/lang/String";\r
+       public static final String UNKNOWN_TYPE = "UNKNOWN-TYPE";\r
+       \r
+       public static final String[] PRIMITIVE_TYPES = {\r
+               BOOLEAN, BYTE, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE \r
+       };\r
+\r
+       /**\r
+        * @param name specifies a type name.\r
+        * @return true if a given name is a primitive types\r
+        * (excluding "void").\r
+        */\r
+       public static boolean isPrimitiveTypeName(String name) { \r
+               for (int i=0; i<PRIMITIVE_TYPES.length; ++i) {\r
+                       if (PRIMITIVE_TYPES[i].equals(name)) return true;\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       /**\r
+        * return name == primitive || name == void\r
+        * NOTE: name is normal type name. (NOT bytecode descriptor, such as I or Z)\r
+        * @param name\r
+        * @return\r
+        */\r
+       public static boolean isPrimitiveOrVoid(String name) {\r
+               return isPrimitiveTypeName(name) || VOID.equals(name);\r
+       }\r
+       \r
+       /**\r
+        * return name == void\r
+        */\r
+       public static boolean isVoid(String name) {\r
+               return VOID.equals(name);\r
+       }\r
+       \r
+       /**\r
+        * @param name specifies a type.\r
+        * @return the number of words to store the type.\r
+        * 2 is returned for double and long.  \r
+        */\r
+       public static int getWordCount(String name) {\r
+               if (DOUBLE.equals(name) || LONG.equals(name)) {\r
+                       return 2;\r
+               } else {\r
+                       return 1;\r
+               }\r
+       }\r
+       \r
+       public static boolean isJavaString(String name) {\r
+               return name != null && name.equals(JAVA_STRING);\r
+       }\r
+}\r
diff --git a/src/soba/core/signature/TypeResolver.java b/src/soba/core/signature/TypeResolver.java
new file mode 100755 (executable)
index 0000000..960cc2f
--- /dev/null
@@ -0,0 +1,45 @@
+package soba.core.signature;\r
+\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+\r
+import org.objectweb.asm.signature.SignatureReader;\r
+\r
+\r
+/**\r
+ * A utility class to resolve a type name using TypeVisitor.\r
+ */\r
+public class TypeResolver {\r
+\r
+       private static Map<String, String> types = null;\r
+       \r
+       /**\r
+        * Returns a readable text for a specified type descriptor.\r
+        * If failed to parse, return argument's typeDesc.\r
+        * @param typeDescriptor is a type descriptor of a single type.\r
+        * @return a type name corresponding to a specified descriptor.\r
+        */\r
+       public static String getTypeName(String typeDesc) {\r
+               if (typeDesc == null) { \r
+                       return null;\r
+               }\r
+               if (types == null) {\r
+                       types = new HashMap<>(4096);\r
+               }\r
+               if (types.containsKey(typeDesc)) {\r
+                       return types.get(typeDesc);\r
+               } else {\r
+                       SignatureReader sig = new SignatureReader(typeDesc);\r
+                       TypeVisitor reader = new TypeVisitor();\r
+                       try {\r
+                               sig.acceptType(reader);\r
+                               types.put(typeDesc, reader.getTypeName());\r
+                               return reader.getTypeName();\r
+                       } catch(Exception e) {\r
+                               types.put(typeDesc, typeDesc);\r
+                               return typeDesc;\r
+                       }\r
+               }\r
+       }\r
+\r
+}\r
diff --git a/src/soba/core/signature/TypeVisitor.java b/src/soba/core/signature/TypeVisitor.java
new file mode 100755 (executable)
index 0000000..6a6b780
--- /dev/null
@@ -0,0 +1,228 @@
+package soba.core.signature;\r
+\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+import org.objectweb.asm.Opcodes;\r
+import org.objectweb.asm.signature.SignatureVisitor;\r
+\r
+/**\r
+ * TypeVisitor processes a type signature representing a single type.\r
+ */\r
+public class TypeVisitor extends SignatureVisitor {\r
+       \r
+       // TypeVisitor receives the following method calls:\r
+       // ( visitBaseType | \r
+       //   visitTypeVariable | \r
+       //   visitArrayType | \r
+       //   ( visitClassType visitTypeArgument* ( visitInnerClassType visitTypeArgument* )* visitEnd )\r
+       // )\r
+       \r
+       private int arrayDimension = 0;\r
+\r
+       private String typename = null;\r
+       private boolean generictype = false;\r
+       private List<TypeVisitor> typeArguments = null;\r
+       private List<String> innerTypeNames = null;\r
+       private List<List<TypeVisitor>> innerTypeArguments = null;\r
+       private boolean parsingInner = false;\r
+       \r
+       public TypeVisitor() {\r
+               super(Opcodes.ASM5);\r
+       }\r
+       \r
+       /**\r
+        * @return a type name.\r
+        * A return value may include generic types\r
+        * such as "java/util/List<java/lang/String>" and "java/util/List<T>".\r
+        */\r
+       public String getTypeName() {\r
+               StringBuilder buf = new StringBuilder();\r
+               buf.append(typename);\r
+               \r
+               if (typeArguments != null) {\r
+                       buf.append("<");\r
+                       for (int i=0; i<typeArguments.size(); ++i) {\r
+                               if (i>0) buf.append(",");\r
+                               buf.append(typeArguments.get(i).getTypeName());\r
+                       }\r
+                       buf.append(">");\r
+               }\r
+               if (innerTypeNames != null) {\r
+                       for (int i=0; i<innerTypeNames.size(); ++i) {\r
+                               buf.append(".");\r
+                               buf.append(innerTypeNames.get(i));\r
+                               \r
+                               List<TypeVisitor> arguments = innerTypeArguments.get(i);\r
+                               if (arguments.size() > 0) {\r
+                                       buf.append("<");\r
+                                       for (int innerArg=0; innerArg<arguments.size(); ++innerArg) {\r
+                                               if (innerArg>0) buf.append(",");\r
+                                               buf.append(arguments.get(innerArg).getTypeName());\r
+                                       }\r
+                                       buf.append(">");\r
+                               }\r
+                               \r
+                       }\r
+               }\r
+               \r
+               for (int i=0; i<arrayDimension; ++i) {\r
+                       buf.append("[]");\r
+               }\r
+               return buf.toString(); \r
+       }\r
+       \r
+       public boolean isGenericType() {\r
+               return generictype;\r
+       }\r
+       \r
+\r
+       @Override\r
+       public void visitBaseType(char descriptor) {\r
+               switch (descriptor) {\r
+               case 'Z':\r
+                       typename = TypeConstants.BOOLEAN;\r
+                       break;\r
+               case 'B':\r
+                       typename = TypeConstants.BYTE;\r
+                       break;\r
+               case 'C': \r
+                       typename = TypeConstants.CHAR;\r
+                       break;\r
+               case 'S':\r
+                       typename = TypeConstants.SHORT;\r
+                       break;\r
+               case 'I': \r
+                       typename = TypeConstants.INT;\r
+                       break;\r
+               case 'J': \r
+                       typename = TypeConstants.LONG;\r
+                       break;\r
+               case 'F':\r
+                       typename = TypeConstants.FLOAT;\r
+                       break;\r
+               case 'D':\r
+                       typename = TypeConstants.DOUBLE;\r
+                       break;\r
+               case 'V':\r
+                       typename = TypeConstants.VOID;\r
+                       break;\r
+               default:\r
+                       typename = Character.toString(descriptor);\r
+               }\r
+       }\r
+\r
+       @Override\r
+       public void visitTypeVariable(String name) {\r
+               typename = name;\r
+               generictype = true;\r
+       }\r
+\r
+       @Override\r
+       public SignatureVisitor visitArrayType() {\r
+               arrayDimension++;\r
+               return this; // reuse the object to parse the base type.\r
+       }\r
+\r
+       @Override\r
+       public void visitClassType(String name) {\r
+               typename = name;\r
+       }\r
+\r
+       private TypeVisitor createTypeArgumentVisitor() {\r
+               TypeVisitor argument = new TypeVisitor();\r
+               if (parsingInner) {\r
+                       int size = innerTypeArguments.size();\r
+                       innerTypeArguments.get(size-1).add(argument);\r
+               } else {\r
+                       if (typeArguments == null) {\r
+                               typeArguments = new ArrayList<TypeVisitor>();\r
+                       }\r
+                       typeArguments.add(argument);\r
+               }\r
+               return argument;\r
+       }\r
+       \r
+       @Override\r
+       public void visitTypeArgument() {\r
+               // Add a visitor and directly store data to the visitor.\r
+               TypeVisitor argument = createTypeArgumentVisitor();\r
+               argument.typename = "?";\r
+               argument.generictype = true;\r
+       }\r
+\r
+       @Override\r
+       public SignatureVisitor visitTypeArgument(char wildcard) {\r
+               TypeVisitor argument = createTypeArgumentVisitor();\r
+               return argument;\r
+       }\r
+\r
+       // Inner class type is separated from class type \r
+       // in order to process a nested generic class such as "C<T1>.INNER<T2>".\r
+       @Override\r
+       public void visitInnerClassType(String name) {\r
+               parsingInner = true;\r
+               if (innerTypeNames == null) {\r
+                       innerTypeNames = new ArrayList<String>();\r
+                       innerTypeArguments = new ArrayList<List<TypeVisitor>>();\r
+               }\r
+               innerTypeNames.add(name);\r
+               innerTypeArguments.add(new ArrayList<TypeVisitor>());\r
+       }\r
+\r
+       @Override\r
+       public void visitEnd() {\r
+       }\r
+\r
+       \r
+\r
+       // Following methods are not used.\r
+       \r
+       @Override\r
+       public SignatureVisitor visitInterface() {\r
+               assert false: "This method never used for parsing a type.";\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public SignatureVisitor visitInterfaceBound() {\r
+               assert false: "This method never used for parsing a type.";\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public SignatureVisitor visitClassBound() {\r
+               assert false: "This method never used for parsing a type.";\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public SignatureVisitor visitExceptionType() {\r
+               assert false: "This method never used for parsing a type.";\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public void visitFormalTypeParameter(String name) {\r
+               assert false: "This method never used for parsing a type.";\r
+       }\r
+\r
+       @Override\r
+       public SignatureVisitor visitParameterType() {\r
+               assert false: "This method never used for parsing a type.";\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public SignatureVisitor visitReturnType() {\r
+               assert false: "This method never used for parsing a type.";\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public SignatureVisitor visitSuperclass() {\r
+               assert false: "This method never used for parsing a type.";\r
+               return null;\r
+       }\r
+       \r
+}\r
diff --git a/src/soba/core/vta/CallResolver.java b/src/soba/core/vta/CallResolver.java
new file mode 100755 (executable)
index 0000000..40d0db5
--- /dev/null
@@ -0,0 +1,71 @@
+package soba.core.vta;\r
+\r
+import soba.core.ClassHierarchy;\r
+import soba.core.IDynamicBindingResolver;\r
+import soba.core.JavaProgram;\r
+import soba.core.MethodInfo;\r
+import soba.core.method.CallSite;\r
+\r
+/**\r
+ * This class wraps VTA resolver and CHA resolver.  \r
+ * While VTAResolver resolves only INVOKEVIRTUAL and INVOKEINTERFACE, \r
+ * this wrapper object can easily resolve all invocations using both algorithms.\r
+ */\r
+public abstract class CallResolver implements IDynamicBindingResolver {\r
+\r
+       public static CallResolver getCHA(JavaProgram program) {\r
+               return new CHA(program.getClassHierarchy());\r
+       }\r
+\r
+       public static CallResolver getCHA(ClassHierarchy hierarchy) {\r
+               return new CHA(hierarchy);\r
+       }\r
+       \r
+       public static CallResolver getVTA(JavaProgram program, IAnalysisTarget selector) {\r
+               VTAResolver vta = new VTAResolver(program, selector);\r
+               return new VTA(program.getClassHierarchy(), vta);\r
+       }\r
+       \r
+       public static CallResolver getVTA(JavaProgram program) {\r
+               VTAResolver vta = new VTAResolver(program);\r
+               return new VTA(program.getClassHierarchy(), vta);\r
+       }\r
+       \r
+       /**\r
+        * @param c specifies a call site.  This instance must be extracted from MethodBody object.\r
+        * @return\r
+        */\r
+       public abstract MethodInfo[] resolveCall(CallSite cs);\r
+               \r
+       private static class CHA extends CallResolver {\r
+               \r
+               private ClassHierarchy ch;\r
+               \r
+               public CHA(ClassHierarchy ch) {\r
+                       this.ch = ch;\r
+               }\r
+               \r
+               public MethodInfo[] resolveCall(CallSite c) {\r
+                       return ch.resolveCall(c);\r
+               }\r
+       }\r
+       \r
+       private static class VTA extends CallResolver {\r
+               \r
+               private ClassHierarchy ch;\r
+               private VTAResolver vta;\r
+               \r
+               public VTA(ClassHierarchy ch, VTAResolver vta) {\r
+                       this.ch = ch;\r
+                       this.vta = vta;\r
+               }\r
+               \r
+               public MethodInfo[] resolveCall(CallSite c) {\r
+                       if (c.isStaticOrSpecial()) {\r
+                               return ch.resolveCall(c);\r
+                       } else {\r
+                               return vta.resolveCall(c);\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/src/soba/core/vta/CallSiteVertices.java b/src/soba/core/vta/CallSiteVertices.java
new file mode 100755 (executable)
index 0000000..41f0b9f
--- /dev/null
@@ -0,0 +1,173 @@
+/**\r
+ * \r
+ */\r
+package soba.core.vta;\r
+\r
+\r
+import gnu.trove.list.array.TIntArrayList;\r
+\r
+import java.util.ArrayList;\r
+\r
+import soba.core.method.CallSite;\r
+import soba.core.signature.MethodSignatureReader;\r
+import soba.core.signature.TypeConstants;\r
+\r
+/**\r
+ * An instance of MethodParameters represents \r
+ * a set of formal parameters of a method \r
+ * or a set of actual parameters of a method call.\r
+ */\r
+class CallSiteVertices {\r
+       \r
+       /**\r
+        * The number of formal parameters of the method.\r
+        * This includes "this", excludes a return value.\r
+        */\r
+       private int paramCount;\r
+       \r
+       /**\r
+        * paramIndex[vertexIndex] specifies the position \r
+        * of a method argument corresponding to the vertex.\r
+        * If paramIndex[vertexIndex] == paramCount, \r
+        * the vertex represents a return value. \r
+        */\r
+       private int[] paramIndex;\r
+\r
+\r
+       /**\r
+        * vertexIDs[vertexIndex] indicates a vertex ID.\r
+        */\r
+       private int[] vertexIDs;\r
+       \r
+       /**\r
+        * vertexTypes[vertexIndex] indicates a type name\r
+        * corresponding to the vertex.\r
+        */\r
+       private String[] vertexTypes;\r
+       \r
+       /**\r
+        * Additional parameter for for actual parameters.\r
+        */\r
+       private CallSite callsite;\r
+       \r
+       public CallSiteVertices(CallSite c, int startID) {\r
+               this.callsite = c;\r
+               MethodSignatureReader sig = new MethodSignatureReader(c.getDescriptor());\r
+               if (!c.isStaticMethod()) {\r
+                       paramCount = sig.getParamCount() + 1;\r
+               } else {\r
+                       paramCount = sig.getParamCount();\r
+               }\r
+\r
+               TIntArrayList params = new TIntArrayList(paramCount);\r
+               ArrayList<String> types = new ArrayList<String>(paramCount);\r
+               int thisCount = 0;\r
+               if (!c.isStaticMethod()) {\r
+                       thisCount = 1;\r
+                       params.add(0);\r
+                       types.add(c.getClassName());\r
+               }\r
+               \r
+               for (int i=0; i<sig.getParamCount(); ++i) {\r
+                       String t = sig.getParamType(i);\r
+                       if (!TypeConstants.isPrimitiveOrVoid(t)) {\r
+                               params.add(i+thisCount);\r
+                               types.add(t+thisCount);\r
+                       }\r
+               }\r
+               if (!TypeConstants.isPrimitiveOrVoid(sig.getReturnType())) {\r
+                       params.add(paramCount);\r
+                       types.add(sig.getReturnType());\r
+               }\r
+               paramIndex = params.toArray();\r
+               vertexTypes = types.toArray(new String[0]);\r
+               vertexIDs = new int[paramIndex.length];\r
+               for (int i=0; i<paramIndex.length; ++i) {\r
+                       vertexIDs[i] = startID + i;\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * @return a call site information.\r
+        * This method returns non-null for objects \r
+        * representing actual-parameters.\r
+        */\r
+       public CallSite getCallSite() {\r
+               return callsite;\r
+       }\r
+       \r
+       public boolean isObjectParam(int param) {\r
+               for (int i=0; i<paramIndex.length; ++i) {\r
+                       if (paramIndex[i] == param) {\r
+                               return true;\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       /**\r
+        * @param param specifies the position of a method parameter\r
+        * in the parameter list.\r
+        * @return\r
+        */\r
+       public int getParamVertexId(int param) {\r
+               for (int i=0; i<paramIndex.length; ++i) {\r
+                       if (paramIndex[i] == param) {\r
+                               return vertexIDs[i];\r
+                       }\r
+               }\r
+               return VTAResolver.VERTEX_ERROR;\r
+       }\r
+       \r
+       /**\r
+        * @return the number of input parameters.\r
+        */\r
+       public int getParamCount() {\r
+               return paramCount;\r
+       }\r
+       \r
+       public boolean hasReturnValue() {\r
+               if (paramIndex.length > 0) { \r
+                       return paramIndex[paramIndex.length-1] == paramCount;\r
+               } else {\r
+                       return false;\r
+               }\r
+       }\r
+       \r
+       public int getReturnValueVertex() {\r
+               if (hasReturnValue()) {\r
+                       return vertexIDs[paramIndex.length-1];\r
+               } else {\r
+                       return VTAResolver.VERTEX_ERROR;\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * @return the number of vertices \r
+        */\r
+       public int getVertexCount() {\r
+               return paramIndex.length;\r
+       }\r
+       \r
+       /**\r
+        * @param vertexIndex specifies a vertex: 0 .. getVertexCount()-1. \r
+        * @return vertex ID.\r
+        */\r
+       public int getVertex(int vertexIndex) {\r
+               return vertexIDs[vertexIndex];\r
+       }\r
+       \r
+       public String getTypeName(int vertexIndex) {\r
+               return vertexTypes[vertexIndex];\r
+       }\r
+       \r
+       public String getReturnValueTypeName() {\r
+               if (hasReturnValue()) {\r
+                       return vertexTypes[paramIndex.length-1];\r
+               } else {\r
+                       assert false;\r
+                       return null;\r
+               }\r
+       }\r
+       \r
+}
\ No newline at end of file
diff --git a/src/soba/core/vta/IAnalysisTarget.java b/src/soba/core/vta/IAnalysisTarget.java
new file mode 100755 (executable)
index 0000000..2aaff87
--- /dev/null
@@ -0,0 +1,14 @@
+package soba.core.vta;\r
+\r
+import soba.core.FieldInfo;\r
+import soba.core.MethodInfo;\r
+\r
+public interface IAnalysisTarget {\r
+\r
+       public boolean isTargetMethod(MethodInfo m);\r
+       public boolean isTargetField(FieldInfo f);\r
+       public boolean assumeExternalCallers(MethodInfo m);\r
+\r
+       public boolean isExcludedType(String className);\r
+       \r
+}\r
diff --git a/src/soba/core/vta/ITopologicalVisitor.java b/src/soba/core/vta/ITopologicalVisitor.java
new file mode 100755 (executable)
index 0000000..b9c547e
--- /dev/null
@@ -0,0 +1,13 @@
+package soba.core.vta;\r
+\r
+public interface ITopologicalVisitor {\r
+       \r
+       /**\r
+        * @param vertexId identifies a visited vertex.\r
+        * @return true if you want to continue visiting \r
+        * process beyond the vertex.\r
+        */\r
+       public boolean onVisit(int vertexId);\r
+\r
+       public void onFinished();\r
+}\r
diff --git a/src/soba/core/vta/MethodVertices.java b/src/soba/core/vta/MethodVertices.java
new file mode 100755 (executable)
index 0000000..e733b6c
--- /dev/null
@@ -0,0 +1,116 @@
+package soba.core.vta;\r
+\r
+import gnu.trove.list.array.TIntArrayList;\r
+\r
+import java.util.ArrayList;\r
+\r
+import soba.core.MethodInfo;\r
+import soba.core.method.LocalVariables;\r
+import soba.core.signature.TypeConstants;\r
+\r
+public class MethodVertices {\r
+       \r
+       \r
+       private LocalVariables locals;\r
+       private int[] variableIndex;\r
+\r
+       private int[] vertexIDs;\r
+       private int returnVertexID;\r
+\r
+       private int[] paramVertexIDs;\r
+       private int vertexCount = 0;\r
+       \r
+       private ArrayList<String> typenames;\r
+\r
+       public MethodVertices(MethodInfo m, LocalVariables table, int startID) {\r
+               this.locals = table;\r
+               this.variableIndex = new int[m.getParamCount()];\r
+               this.typenames = new ArrayList<String>();\r
+               \r
+               paramVertexIDs = new int[m.getParamCount()];\r
+               int vID = startID;\r
+               int var = 0;\r
+               for (int i=0; i<m.getParamCount(); ++i) {\r
+                       String type = m.getParamType(i);\r
+                       variableIndex[i] = var;\r
+                       var += TypeConstants.getWordCount(type);\r
+                       if (!TypeConstants.isPrimitiveOrVoid(type)) {\r
+                               paramVertexIDs[i] = vID;\r
+                               vID++;\r
+                               typenames.add(type);\r
+                       }\r
+               }\r
+               \r
+               TIntArrayList vertices = new TIntArrayList();\r
+               for (int i=0; i<locals.getVariableEntryCount(); ++i) {\r
+                       if (locals.isObjectVariable(i)) {\r
+                               if (locals.isParameter(i)) {\r
+                                       int paramIndex = getParamIndex(locals.getVariableIndex(i));\r
+                                       vertices.add(paramVertexIDs[paramIndex]);\r
+                               } else {\r
+                                       vertices.add(vID);\r
+                                       vID++;\r
+                                       String t = locals.getVariableType(i);\r
+                                       if (t == null) {\r
+                                               if (locals.isArrayVariable(i)) {\r
+                                                       t = TypeSet.DEFAULT_UNKNOWN_ARRAYTYPE;\r
+                                               } else {\r
+                                                       t = TypeSet.DEFAULT_UNKNOWN_TYPE;\r
+                                               }\r
+                                       }\r
+                                       typenames.add(t);\r
+                               }\r
+                       } else {\r
+                               vertices.add(VTAResolver.VERTEX_ERROR);\r
+                       }\r
+               }\r
+               vertexIDs = vertices.toArray();\r
+               \r
+               if (!TypeConstants.isPrimitiveOrVoid(m.getReturnType())) {\r
+                       returnVertexID = vID;\r
+                       vID++;\r
+                       typenames.add(m.getReturnType());\r
+               }\r
+               vertexCount = vID - startID;\r
+       }\r
+       \r
+       public int getLocalVertex(int instruction) {\r
+               int entryIndex = locals.findEntryForInstruction(instruction);\r
+               if (entryIndex != -1) {\r
+                       return vertexIDs[entryIndex];\r
+               } else {\r
+                       // Some local variables are assigned but never used.\r
+                       return VTAResolver.VERTEX_ERROR;\r
+               }\r
+       }\r
+       \r
+       public int getReturnVertex() {\r
+               return returnVertexID;\r
+       }\r
+       \r
+       public int getFormalVertex(int paramIndex) {\r
+               return paramVertexIDs[paramIndex];\r
+       }\r
+       \r
+       public boolean hasFormalVertex(int paramIndex) {\r
+               return paramVertexIDs[paramIndex] != VTAResolver.VERTEX_ERROR;\r
+       }\r
+       \r
+       private int getParamIndex(int varIndex) {\r
+               for (int i=0; i<variableIndex.length; ++i) {\r
+                       if (variableIndex[i] == varIndex) {\r
+                               return i;\r
+                       }\r
+               }\r
+               return -1;\r
+       }\r
+       \r
+       public int getVertexCount() {\r
+               return vertexCount;\r
+       }\r
+       \r
+       public String getTypeName(int vertexIndex) { \r
+               return typenames.get(vertexIndex);\r
+       }\r
+\r
+}\r
diff --git a/src/soba/core/vta/NewVertices.java b/src/soba/core/vta/NewVertices.java
new file mode 100755 (executable)
index 0000000..afb6b3d
--- /dev/null
@@ -0,0 +1,83 @@
+package soba.core.vta;\r
+\r
+import gnu.trove.list.array.TIntArrayList;\r
+\r
+import java.util.ArrayList;\r
+\r
+\r
+\r
+\r
+import org.objectweb.asm.Opcodes;\r
+import org.objectweb.asm.tree.AbstractInsnNode;\r
+import org.objectweb.asm.tree.InsnList;\r
+import org.objectweb.asm.tree.MultiANewArrayInsnNode;\r
+import org.objectweb.asm.tree.TypeInsnNode;\r
+\r
+import soba.core.signature.TypeResolver;\r
+\r
+public class NewVertices {\r
+\r
+       private int[] instructionIndices;\r
+       private int[] vertexIDs;\r
+       private String[] types;\r
+\r
+       \r
+       public NewVertices(InsnList instructions, int startID) {\r
+               TIntArrayList indices = new TIntArrayList(); \r
+               TIntArrayList vIDs = new TIntArrayList(); \r
+               ArrayList<String> typeNames = new ArrayList<String>();\r
+               int vID = startID;\r
+               for (int i=0; i<instructions.size(); ++i) {\r
+                       AbstractInsnNode instruction = instructions.get(i);\r
+                       if (instruction.getOpcode() == Opcodes.NEW) {\r
+                               indices.add(i);\r
+                               vIDs.add(vID);\r
+                               vID++;\r
+                               typeNames.add(((TypeInsnNode)instruction).desc);\r
+                       } else if (instruction.getOpcode() == Opcodes.ANEWARRAY) {\r
+                               indices.add(i);\r
+                               vIDs.add(vID);\r
+                               vID++;\r
+                               String desc = ((TypeInsnNode)instruction).desc;\r
+                               if (desc.startsWith("[")) {\r
+                                       typeNames.add(TypeResolver.getTypeName(desc));\r
+                               } else {\r
+                                       typeNames.add(desc + "[]");\r
+                               }\r
+                       } else if (instruction.getOpcode() == Opcodes.MULTIANEWARRAY) {\r
+                               indices.add(i);\r
+                               vIDs.add(vID);\r
+                               vID++;\r
+                               typeNames.add(TypeResolver.getTypeName(((MultiANewArrayInsnNode)instruction).desc));\r
+                       }\r
+                       // Implementation Note: NEWARRAY is not included because the instruction generates an array of primitive values. \r
+               }\r
+               instructionIndices = indices.toArray();\r
+               vertexIDs = vIDs.toArray();\r
+               types = new String[typeNames.size()]; \r
+               for (int i=0; i<types.length; ++i) {\r
+                       types[i] = typeNames.get(i);\r
+               }\r
+       }\r
+       \r
+       public int getNewInstructionVertex(int instructionIndex) {\r
+               for (int i=0; i<instructionIndices.length; ++i) {\r
+                       if (instructionIndices[i] == instructionIndex) {\r
+                               return vertexIDs[i];\r
+                       }\r
+               }\r
+               return VTAResolver.VERTEX_ERROR;\r
+       }\r
+       \r
+       public int getVertex(int vertexIndex) {\r
+               return vertexIDs[vertexIndex];\r
+       }\r
+       \r
+       public String getTypeName(int vertexIndex) {\r
+               return types[vertexIndex];\r
+       }\r
+       \r
+       public int getVertexCount() {\r
+               return vertexIDs.length;\r
+       }\r
+}\r
diff --git a/src/soba/core/vta/TopologicalOrderSearch.java b/src/soba/core/vta/TopologicalOrderSearch.java
new file mode 100755 (executable)
index 0000000..f78ba89
--- /dev/null
@@ -0,0 +1,49 @@
+package soba.core.vta;\r
+\r
+import soba.util.graph.DirectedAcyclicGraph;\r
+\r
+public class TopologicalOrderSearch {\r
+\r
+       /**\r
+        * Visit vertices in their topological order.\r
+        * @param graph\r
+        * @param visit\r
+        */\r
+       public static void searchFromRoot(final DirectedAcyclicGraph graph, ITopologicalVisitor visit) {\r
+               // To find root vertices, count the number of incoming edges for each vertex.\r
+               int[] incoming = new int[graph.getVertexCount()];\r
+               for (int i=0; i<graph.getVertexCount(); ++i) {\r
+                       for (int to: graph.getEdges(i)) {\r
+                               incoming[to] += 1;\r
+                       }\r
+               }\r
+               \r
+               // Push root vertices into the queue.\r
+               int[] queue = new int[graph.getVertexCount()];\r
+               int queueEndIndex = 0;\r
+               for (int i=0; i<graph.getVertexCount(); ++i) {\r
+                       if (incoming[i] == 0 && graph.isRepresentativeNode(i)) {\r
+                               queue[queueEndIndex] = i;\r
+                               queueEndIndex++;\r
+                       }\r
+               }\r
+               \r
+               // Main Loop\r
+               int queueIndex = 0;\r
+               while (queueIndex < queueEndIndex) {\r
+                       int v = queue[queueIndex];\r
+                       queueIndex++;\r
+                       boolean continueVisit = visit.onVisit(v);\r
+                       if (continueVisit) {\r
+                               for (int to: graph.getEdges(v)) {\r
+                                       incoming[to] -= 1;\r
+                                       if (incoming[to] == 0) {\r
+                                               queue[queueEndIndex] = to;\r
+                                               queueEndIndex++;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               visit.onFinished();\r
+       }\r
+}\r
diff --git a/src/soba/core/vta/TypeSet.java b/src/soba/core/vta/TypeSet.java
new file mode 100755 (executable)
index 0000000..28902a4
--- /dev/null
@@ -0,0 +1,107 @@
+package soba.core.vta;\r
+\r
+import java.util.List;\r
+\r
+public class TypeSet {\r
+       \r
+       public static final String DEFAULT_UNKNOWN_TYPE = "java/lang/Object";\r
+       public static final String DEFAULT_UNKNOWN_ARRAYTYPE = DEFAULT_UNKNOWN_TYPE + "[]";\r
+\r
+       private TypeSetManager manager;\r
+       private int typesId;\r
+       private int approximatedTypesId;\r
+       \r
+       /**\r
+        * Creates a new <code>TypeSet</code> instance without type information.\r
+        * @param manager\r
+        */\r
+       public TypeSet(TypeSetManager manager) {\r
+               this.manager = manager;\r
+               this.typesId = manager.getEmptyId();\r
+               this.approximatedTypesId = manager.getEmptyId();\r
+       }\r
+       \r
+       /**\r
+        * Creates a new <code>TypeSet</code> with a single type. \r
+        * @param manager\r
+        * @param typename\r
+        */\r
+       public TypeSet(TypeSetManager manager, String typename) { \r
+               this.manager = manager;\r
+               assert typename != null;\r
+               this.typesId = manager.getId(typename);\r
+               this.approximatedTypesId = manager.getEmptyId();\r
+       }\r
+\r
+       /**\r
+        * Creates a new <code>TypeSet</code> with multiple types.\r
+        * @param manager\r
+        * @param parents\r
+        */\r
+       public TypeSet(TypeSetManager manager, List<TypeSet> parents) {\r
+               this.manager = manager;\r
+               if (parents.size() == 0) {\r
+                       this.typesId = manager.getEmptyId();\r
+                       this.approximatedTypesId = manager.getEmptyId();\r
+               } else if (parents.size() == 1) {\r
+                       this.typesId = parents.get(0).typesId;\r
+                       this.approximatedTypesId = parents.get(0).approximatedTypesId;\r
+               } else {\r
+                       int mergeTypesId = parents.get(0).typesId;\r
+                       int mergeAppoximatedTypesId = parents.get(0).approximatedTypesId;\r
+                       for (int i=1; i<parents.size(); ++i) {\r
+                               mergeTypesId = manager.merge(mergeTypesId, parents.get(i).typesId);\r
+                               mergeAppoximatedTypesId = manager.merge(mergeAppoximatedTypesId, parents.get(i).approximatedTypesId);\r
+                       }\r
+                       this.typesId = mergeTypesId;\r
+                       this.approximatedTypesId = mergeAppoximatedTypesId;\r
+               }\r
+       }\r
+       \r
+       public TypeSet addType(String additionalType) {\r
+               TypeSet copy = new TypeSet(manager);\r
+               copy.typesId = manager.merge(this.typesId, manager.getId(additionalType));\r
+               copy.approximatedTypesId = this.approximatedTypesId;\r
+               return copy;\r
+       }\r
+\r
+       public static TypeSet createApproximation(TypeSetManager manager, String typename) {\r
+               TypeSet t = new TypeSet(manager);\r
+               t.approximatedTypesId = manager.getId(typename); \r
+               return t;\r
+       }\r
+\r
+       public TypeSet addApproximatedType(String additionalType) {\r
+               TypeSet copy = new TypeSet(manager);\r
+               copy.typesId = this.typesId;\r
+               copy.approximatedTypesId = manager.merge(this.approximatedTypesId, manager.getId(additionalType));\r
+               return copy;\r
+       }\r
+       \r
+\r
+       public boolean contains(String t) {\r
+               if (t == null) return false;\r
+               String[] types = manager.getStrings(this.typesId);\r
+               for (int i=0; i<types.length; ++i) {\r
+                       if (t.equals(types[i])) return true;\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       public int getTypeCount() { \r
+               return manager.getStrings(typesId).length;\r
+       }\r
+       \r
+       public String getType(int index) {\r
+               return manager.getStrings(typesId)[index];\r
+       }\r
+       \r
+       public int getApproximatedTypeCount() {\r
+               return manager.getStrings(approximatedTypesId).length;\r
+       }\r
+       \r
+       public String getApproximatedType(int index) {\r
+               return manager.getStrings(approximatedTypesId)[index];\r
+       }\r
+       \r
+}\r
diff --git a/src/soba/core/vta/TypeSetManager.java b/src/soba/core/vta/TypeSetManager.java
new file mode 100755 (executable)
index 0000000..e78f6f3
--- /dev/null
@@ -0,0 +1,104 @@
+package soba.core.vta;\r
+\r
+\r
+import gnu.trove.map.hash.TIntIntHashMap;\r
+import gnu.trove.map.hash.TIntObjectHashMap;\r
+\r
+import java.util.Arrays;\r
+import java.util.HashSet;\r
+\r
+import soba.util.ObjectIdMap;\r
+\r
+public class TypeSetManager {\r
+\r
+       private ObjectIdMap<String> cache;\r
+       private TIntObjectHashMap<String[]> strings;\r
+       private static final String[] EMPTY = new String[0];\r
+       private static final String SEPARATOR = "|";\r
+       private static final String SEPARATOR_REGEX = "\\|";\r
+       \r
+       private TIntObjectHashMap<TIntIntHashMap> mergeMap;\r
+       \r
+       public TypeSetManager() {\r
+               cache = new ObjectIdMap<String>();\r
+               strings = new TIntObjectHashMap<String[]>();\r
+               cache.add("");\r
+               \r
+               mergeMap = new TIntObjectHashMap<TIntIntHashMap>();\r
+       }\r
+       \r
+       public int getEmptyId() {\r
+               return cache.getId("");\r
+       }\r
+       \r
+       public int getId(String singleString) {\r
+               return cache.getId(singleString);\r
+       }\r
+       \r
+       public int getId(String[] strings) {\r
+               return cache.getId(toSingleString(strings));\r
+       }\r
+       \r
+       public String[] getStrings(int id) {\r
+               if (strings.containsKey(id)) {\r
+                       return strings.get(id);\r
+               } else {\r
+                       String s = cache.getItem(id);\r
+                       if (s.length() == 0) {\r
+                               return EMPTY;\r
+                       } else {\r
+                               String[] array = s.split(SEPARATOR_REGEX);\r
+                               strings.put(id, array);\r
+                               return array;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       public int merge(int id1, int id2) {\r
+               if (id1 > id2) {\r
+                       int swap = id1;\r
+                       id1 = id2;\r
+                       id2 = swap;\r
+               }\r
+               TIntIntHashMap map = mergeMap.get(id1);\r
+               if (map != null) {\r
+                       if (map.containsKey(id2)) {\r
+                               return map.get(id2);\r
+                       }\r
+               }\r
+               \r
+               HashSet<String> union = new HashSet<String>();\r
+               for (String s: getStrings(id1)) {\r
+                       union.add(s);\r
+               }\r
+               for (String s: getStrings(id2)) {\r
+                       union.add(s);\r
+               }\r
+               String[] unionStrings = union.toArray(EMPTY);\r
+               Arrays.sort(unionStrings);\r
+               int result = getId(unionStrings);\r
+               if (map == null) {\r
+                       map = new TIntIntHashMap();\r
+                       mergeMap.put(id1, map);\r
+               }\r
+               map.put(id2, result);\r
+               return result;\r
+       }\r
+       \r
+       private String toSingleString(String[] types) {\r
+               StringBuilder b = new StringBuilder();\r
+               for (int i=0; i<types.length; ++i) {\r
+                       if (i > 0) b.append(SEPARATOR);\r
+                       b.append(types[i]);\r
+               }\r
+               return b.toString();\r
+       }\r
+\r
+       public int size() {\r
+               int total = 0;\r
+               for (int i=0; i<cache.size(); ++i) {\r
+                       total += cache.getItem(i).length();\r
+               }\r
+               return total;\r
+       }\r
+}\r
diff --git a/src/soba/core/vta/VTAResolver.java b/src/soba/core/vta/VTAResolver.java
new file mode 100755 (executable)
index 0000000..86e8101
--- /dev/null
@@ -0,0 +1,770 @@
+package soba.core.vta;\r
+\r
+import gnu.trove.list.array.TIntArrayList;\r
+import gnu.trove.map.hash.TIntObjectHashMap;\r
+import gnu.trove.procedure.TIntObjectProcedure;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.Collection;\r
+import java.util.Comparator;\r
+import java.util.HashMap;\r
+import java.util.HashSet;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+import org.objectweb.asm.tree.AbstractInsnNode;\r
+import org.objectweb.asm.tree.FieldInsnNode;\r
+import org.objectweb.asm.tree.InsnList;\r
+import org.objectweb.asm.tree.MethodNode;\r
+import org.objectweb.asm.tree.TryCatchBlockNode;\r
+import org.objectweb.asm.Opcodes;\r
+\r
+import soba.core.ClassHierarchy;\r
+import soba.core.ClassInfo;\r
+import soba.core.FieldInfo;\r
+import soba.core.IDynamicBindingResolver;\r
+import soba.core.JavaProgram;\r
+import soba.core.MethodInfo;\r
+import soba.core.method.CallSite;\r
+import soba.core.method.DataDependence;\r
+import soba.core.signature.TypeConstants;\r
+import soba.core.signature.TypeResolver;\r
+import soba.util.IntPairList;\r
+import soba.util.graph.DirectedAcyclicGraph;\r
+import soba.util.graph.DirectedGraph;\r
+\r
+public class VTAResolver implements IDynamicBindingResolver {\r
+       \r
+       public static int VERTEX_ERROR = 0;\r
+       private static final String ARRAY_SUFFIX = "[]";\r
+\r
+       private Map<FieldInfo, FieldVertex> fieldVertex;\r
+       private Map<MethodInfo, CallSiteVertices[]> callsiteMap; // methodInfo * instructionIndex -> callsite\r
+       private Map<MethodInfo, NewVertices> newVerticesMap;\r
+       private Map<MethodInfo, MethodVertices> localVerticesMap;\r
+       private TIntObjectHashMap<String> catchVariableVertices;\r
+       \r
+       private ArrayList<String> declaredTypeNames; // vertex ID -> type name (constraint) of the vertex.\r
+       \r
+       private ClassHierarchy hierarchy;\r
+       private IAnalysisTarget target;\r
+       \r
+       private IntPairList edges;\r
+       \r
+       private TypeSet[] reachingTypes;\r
+\r
+       private TypeSetManager typeSetManager;\r
+       \r
+       /**\r
+        * Creates a new <code>VTAResolver</code> instance.\r
+        * All methods and fields in the program are analyzed.\r
+        * @param program\r
+        */\r
+       public VTAResolver(final JavaProgram program) {\r
+               this(program, new IAnalysisTarget() {\r
+                       @Override\r
+                       public boolean isTargetMethod(MethodInfo m) {\r
+                               return true;\r
+                       }\r
+                       \r
+                       @Override\r
+                       public boolean isTargetField(FieldInfo f) {\r
+                               return true;\r
+                       }\r
+                       \r
+                       @Override\r
+                       public boolean isExcludedType(String className) {\r
+                               return false;\r
+                       }\r
+                       \r
+                       @Override\r
+                       public boolean assumeExternalCallers(MethodInfo m) {\r
+                               return false;\r
+                       }\r
+               });\r
+       }\r
+       \r
+       /**\r
+        * Creates a new <code>VTAResolver</code> instance.\r
+        * @param program\r
+        * @param selector specifies the analysis target in the program.\r
+        */\r
+       public VTAResolver(final JavaProgram program, final IAnalysisTarget selector) {\r
+               target = selector;\r
+               edges = new IntPairList(65536);\r
+               hierarchy = program.getClassHierarchy();\r
+               \r
+               callsiteMap = new HashMap<MethodInfo, CallSiteVertices[]>();\r
+               newVerticesMap = new HashMap<MethodInfo, NewVertices>();\r
+               fieldVertex = new HashMap<FieldInfo, FieldVertex>();\r
+               localVerticesMap = new HashMap<MethodInfo, MethodVertices>();\r
+               catchVariableVertices = new TIntObjectHashMap<String>();\r
+               declaredTypeNames = new ArrayList<String>(65536);\r
+               List<CallSiteVertices> callsitesWithoutCallees = new ArrayList<CallSiteVertices>(); \r
+               \r
+               // Create vertices for inter-procedural connection\r
+               fieldVertex = new HashMap<FieldInfo, FieldVertex>();\r
+               int vID = VERTEX_ERROR+1;\r
+               declaredTypeNames.add(TypeSet.DEFAULT_UNKNOWN_TYPE);\r
+               for (ClassInfo c: program.getClasses()) {\r
+                       for (int mIndex = 0; mIndex < c.getMethodCount(); mIndex++) {\r
+                               MethodInfo m = c.getMethod(mIndex);\r
+                               if (m.hasMethodBody() && (selector == null || selector.isTargetMethod(m))) {\r
+                                       // Create vertices for local variables (including formal parameters)\r
+//                                     MethodBody body = m.getMethodBody();\r
+                                       DataDependence dataflow = m.getDataDependence();\r
+                                       MethodVertices localVertices = new MethodVertices(m, dataflow.getLocalVariables(), vID);\r
+                                       this.localVerticesMap.put(m, localVertices);\r
+                                       vID += localVertices.getVertexCount();\r
+                                       for (int i=0; i<localVertices.getVertexCount(); ++i) {\r
+                                               declaredTypeNames.add(localVertices.getTypeName(i));\r
+                                       }\r
+                               }\r
+                       }\r
+                       for (int fIndex = 0; fIndex < c.getFieldCount(); fIndex++) {\r
+                               FieldInfo f = c.getField(fIndex);\r
+                               if (!TypeConstants.isPrimitiveTypeName(f.getFieldTypeName())) {\r
+                                       FieldVertex fv = new FieldVertex(f, vID);\r
+                                       fieldVertex.put(f, fv);\r
+                                       vID++;\r
+                                       declaredTypeNames.add(fv.getTypeName());\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               // Build a type propagation graph\r
+               for (ClassInfo c: program.getClasses()) {\r
+                       for (int mIndex = 0; mIndex < c.getMethodCount(); mIndex++) {\r
+                               MethodInfo m = c.getMethod(mIndex);\r
+                               if (m.hasMethodBody() && (selector == null || selector.isTargetMethod(m))) {\r
+//                                     MethodBody body = m.getMethodBody();\r
+                                       DataDependence dataflow = m.getDataDependence();\r
+                                       \r
+                                       MethodNode mnode = m.getMethodNode();\r
+\r
+                                       // Create vertices for "new" instructions \r
+                                       NewVertices newVertices = new NewVertices(mnode.instructions, vID);\r
+                                       vID += newVertices.getVertexCount();\r
+                                       this.newVerticesMap.put(m, newVertices);\r
+                                       for (int i=0; i<newVertices.getVertexCount(); ++i) {\r
+                                               declaredTypeNames.add(newVertices.getTypeName(i));\r
+                                               assert newVertices.getTypeName(i) != null;\r
+                                       }\r
+\r
+                                       // Create vertices for method invocations.\r
+                                       // Connect inter-procedural edges.\r
+                                       CallSiteVertices[] callsites = new CallSiteVertices[m.getInstructionCount()];\r
+                                       callsiteMap.put(m, callsites);\r
+\r
+                                       for (CallSite callsite: m.getCallSites()) {\r
+                                               MethodInfo[] methods = hierarchy.resolveCall(callsite);\r
+                                               CallSiteVertices actuals = new CallSiteVertices(callsite, vID);\r
+                                               callsites[callsite.getInstructionIndex()] = actuals;\r
+                                               vID += actuals.getVertexCount();\r
+                                               for (int i=0; i<actuals.getVertexCount(); ++i) {\r
+                                                       declaredTypeNames.add(actuals.getTypeName(i));\r
+                                               }\r
+\r
+                                               boolean methodNotIncluded = false;\r
+                                               if (methods.length > 0) {\r
+                                                       for (MethodInfo called: methods) {\r
+                                                               \r
+                                                               MethodVertices formals = localVerticesMap.get(called);\r
+                                                               if (formals != null) {\r
+                                                                       for (int i=0; i<actuals.getParamCount(); ++i) {\r
+                                                                               if (actuals.isObjectParam(i)) {\r
+                                                                                       assert actuals.getParamVertexId(i) != VERTEX_ERROR;\r
+                                                                                       assert formals.getFormalVertex(i) != VERTEX_ERROR;\r
+                                                                                       \r
+                                                                                       addEdge(actuals.getParamVertexId(i), formals.getFormalVertex(i));\r
+                                                                               }\r
+                                                                       }\r
+                                                                       if (actuals.hasReturnValue()) {\r
+                                                                               assert formals.getReturnVertex() != VERTEX_ERROR;\r
+                                                                               assert actuals.getReturnValueVertex() != VERTEX_ERROR;\r
+                                                                               addEdge(formals.getReturnVertex(), actuals.getReturnValueVertex());\r
+                                                                       }\r
+                                                               } else {\r
+                                                                       // The method may be out of target.\r
+                                                                       methodNotIncluded = true;\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                               if ((methodNotIncluded || methods.length == 0) && actuals.hasReturnValue()) callsitesWithoutCallees.add(actuals);\r
+\r
+                                       }\r
+\r
+                                       \r
+                                       // Process instructions in a method\r
+                                       for (int i=0; i<m.getInstructionCount(); ++i) {\r
+                                               AbstractInsnNode instruction = mnode.instructions.get(i);\r
+                                               analyzeInstruction(i, instruction, m, dataflow);\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+\r
+               // Construct a graph object\r
+               DirectedGraph graph = new DirectedGraph(vID, edges);\r
+               DirectedAcyclicGraph typePropagationDAG = new DirectedAcyclicGraph(graph);\r
+\r
+               this.typeSetManager = new TypeSetManager();\r
+               assignTypes(typePropagationDAG, callsitesWithoutCallees, selector);\r
+               propagateTypes(typePropagationDAG);\r
+       }\r
+       \r
+       \r
+       /**\r
+        * This method resolves dynamic binding for INVOKEVIRTUAL/INVOKEINTERFACE calls.\r
+        * It should be noted that this binding does not correctly return binding information\r
+        * for INVOKESPECIAL and INVOKESTATIC.\r
+        * @param cs specifies a method call.\r
+        * @return an array of <code>IMethodInfo</code> objects that are invoked by \r
+        * the specified invocation instruction.\r
+        * The array is a subset of CHA result (returned by <code>ClassHierarchy</code>).\r
+        * The array excludes unreachable types according to VTA analysis\r
+        * and a type filter represented by IAnalysisTarget.isExcludedType().\r
+        */\r
+       @Override\r
+       public MethodInfo[] resolveCall(CallSite cs) {\r
+               int instruction = cs.getInstructionIndex();\r
+               CallSiteVertices[] callsites = callsiteMap.get(cs.getOwnerMethod());\r
+               if (callsites != null) {\r
+                       CallSiteVertices params = callsites[instruction];\r
+                       if (params != null) {\r
+                               HashSet<MethodInfo> called = new HashSet<>();\r
+                               int v = params.getParamVertexId(0);\r
+                               TypeSet types = reachingTypes[v];\r
+                               String methodName = params.getCallSite().getMethodName();\r
+                               String methodDesc = params.getCallSite().getDescriptor();\r
+                               if (types != null) {\r
+                                       ArrayList<String> declaredType = new ArrayList<String>();\r
+                                       declaredType.add(declaredTypeNames.get(v));\r
+                                       Collection<String> declaredSubtypes = hierarchy.getAllSubtypes(declaredType);\r
+                                       \r
+                                       for (int i=0; i<types.getTypeCount(); ++i) {\r
+                                               String className = types.getType(i);\r
+                                               if (declaredSubtypes.contains(className)) {\r
+                                                       MethodInfo m = hierarchy.resolveSpecialCall(className, methodName, methodDesc);\r
+                                                       if (m != null && !target.isExcludedType(m.getClassName())) {\r
+                                                               called.add(m);\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                                       \r
+                                       ArrayList<String> approxTypes = new ArrayList<String>();\r
+                                       for (int i=0; i<types.getApproximatedTypeCount(); ++i) {\r
+                                               approxTypes.add(types.getApproximatedType(i));\r
+                                       }\r
+                                       Collection<String> subtypes = hierarchy.getAllSubtypes(approxTypes);\r
+                                       for (String className: subtypes) {\r
+                                               if (declaredSubtypes.contains(className)) {\r
+                                                       MethodInfo m = hierarchy.resolveSpecialCall(className, methodName, methodDesc);\r
+                                                       if (m != null && !target.isExcludedType(m.getClassName())) {\r
+                                                               called.add(m);\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                                       MethodInfo[] methods = called.toArray(new MethodInfo[0]);\r
+                                       Arrays.sort(methods, new Comparator<MethodInfo>() {\r
+                                               @Override\r
+                                               public int compare(MethodInfo o1, MethodInfo o2) {\r
+                                                       int idx = o1.getClassName().compareTo(o2.getClassName());\r
+                                                       if (idx != 0) return idx;\r
+                                                       \r
+                                                       idx = o1.getMethodName().compareTo(o2.getMethodName());\r
+                                                       if (idx != 0) return idx;\r
+\r
+                                                       idx = o1.getDescriptor().compareTo(o2.getDescriptor());\r
+                                                       if (idx != 0) return idx;\r
+                                                       \r
+                                                       return o1.hashCode() - o2.hashCode();\r
+                                               }\r
+                                       });\r
+                                       return methods;\r
+                               } else {\r
+                                       // types == null if the invocation is not processed -- this condition is never satisfied.\r
+                                       return new MethodInfo[0];\r
+                               }\r
+                       } else {\r
+                               // params == null if the specified instruction is not an invocation.\r
+                               return new MethodInfo[0];\r
+                       }\r
+               } else {\r
+                       // callsites == null if the caller is not included in the analysis.\r
+                       return new MethodInfo[0];\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Assign types for each vertex.\r
+        * @param typePropagationDAG\r
+        */\r
+       private void assignTypes(final DirectedAcyclicGraph typePropagationDAG, List<CallSiteVertices> callsitesWithoutCallees, IAnalysisTarget selector) {\r
+               reachingTypes = new TypeSet[typePropagationDAG.getVertexCount()];\r
+               for (NewVertices vertices: newVerticesMap.values()) {\r
+                       for (int i=0; i<vertices.getVertexCount(); ++i) {\r
+                               String typeName = extractBaseType(vertices.getTypeName(i));\r
+                               int v = vertices.getVertex(i);\r
+                               assignSpecificType(typePropagationDAG, v, typeName);\r
+                       }\r
+               }\r
+               for (CallSiteVertices vertices: callsitesWithoutCallees) {\r
+                       assert vertices.hasReturnValue();\r
+                       int v = vertices.getReturnValueVertex();\r
+                       assignApproximatedType(typePropagationDAG, v, extractBaseType(vertices.getReturnValueTypeName()));\r
+               }\r
+               // Assign approximated types for parameters from outside\r
+               for (MethodInfo m: localVerticesMap.keySet()) {\r
+                       if (selector != null && selector.assumeExternalCallers(m)) {\r
+                               MethodVertices methodVertices = localVerticesMap.get(m); \r
+                               for (int i=0; i<m.getParamCount(); ++i) {\r
+                                       if (methodVertices.hasFormalVertex(i)) {\r
+                                               int v = methodVertices.getFormalVertex(i);\r
+                                               assignApproximatedType(typePropagationDAG, v, extractBaseType(m.getParamType(i)));\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               // Assign approximated types for exception types in catch blocks\r
+               catchVariableVertices.forEachEntry(new TIntObjectProcedure<String>() {\r
+                       @Override\r
+                       public boolean execute(int v, String typeName) {\r
+                               assignApproximatedType(typePropagationDAG, v, typeName);\r
+                               return true;\r
+                       }\r
+               });\r
+               // Assign approximated types to fields which are not included in analysis target.\r
+               if (selector != null) {\r
+                       for (FieldVertex fv: fieldVertex.values()) {\r
+                               int vertexId = fv.getId();\r
+                               FieldInfo f = fv.getFieldInfo();\r
+                               if (!selector.isTargetField(f)) {\r
+                                       assignApproximatedType(typePropagationDAG, vertexId, extractBaseType(fv.getTypeName()));\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Assign a specific type to a vertex.\r
+        * @param typePropagationDAG specifies a DAG.\r
+        * @param v specifies a vertex.\r
+        * @param typeName specifies a type name.\r
+        */\r
+       private void assignSpecificType(DirectedAcyclicGraph typePropagationDAG, int v, String typeName) {\r
+               v = typePropagationDAG.getRepresentativeNode(v);\r
+               if (reachingTypes[v] == null) {\r
+                       reachingTypes[v] = new TypeSet(typeSetManager, typeName);\r
+               } else {\r
+                       reachingTypes[v] = reachingTypes[v].addType(typeName);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Assign an approximated type to a vertex.\r
+        * @param typePropagationDAG specifies a DAG.\r
+        * @param v specifies a vertex.\r
+        * @param typeName specifies a type name.\r
+        */\r
+       private void assignApproximatedType(DirectedAcyclicGraph typePropagationDAG, int v, String typeName) {\r
+               v = typePropagationDAG.getRepresentativeNode(v);\r
+               if (reachingTypes[v] == null) {\r
+                       reachingTypes[v] = TypeSet.createApproximation(typeSetManager, typeName);\r
+               } else {\r
+                       reachingTypes[v] = reachingTypes[v].addApproximatedType(typeName);\r
+               }\r
+       }\r
+       \r
+\r
+       \r
+       private void propagateTypes(final DirectedAcyclicGraph typePropagationDAG) {\r
+               final DirectedAcyclicGraph reverse = typePropagationDAG.getReverseGraph(); \r
+\r
+               TopologicalOrderSearch.searchFromRoot(typePropagationDAG, new ITopologicalVisitor() {\r
+                       \r
+                       @Override\r
+                       public boolean onVisit(int vertexId) {\r
+                               // Don't propagate types through ERROR vertex.\r
+                               if (vertexId == VERTEX_ERROR) {\r
+                                       reachingTypes[vertexId] = new TypeSet(typeSetManager);\r
+                                       return true;\r
+                               }\r
+                               \r
+\r
+                               int[] incoming = reverse.getEdges(vertexId);\r
+                               if (incoming.length == 1) {\r
+                                       if (reachingTypes[vertexId] == null) {\r
+                                               reachingTypes[vertexId] = reachingTypes[incoming[0]];\r
+                                       } else {\r
+                                               ArrayList<TypeSet> types = new ArrayList<TypeSet>();\r
+                                               types.add(reachingTypes[vertexId]);\r
+                                               types.add(reachingTypes[incoming[0]]);\r
+                                               reachingTypes[vertexId] = new TypeSet(typeSetManager, types);\r
+                                       }\r
+                               } else if (incoming.length > 1) {\r
+                                       // Merge reaching types.\r
+                                       ArrayList<TypeSet> types = new ArrayList<TypeSet>();\r
+                                       if (reachingTypes[vertexId] != null) {\r
+                                               types.add(reachingTypes[vertexId]);\r
+                                       }\r
+                                       for (int i=0; i<incoming.length; ++i) {\r
+                                               assert reachingTypes[incoming[i]] != null;\r
+                                               types.add(reachingTypes[incoming[i]]);\r
+                                       }\r
+                                       reachingTypes[vertexId] = new TypeSet(typeSetManager, types);\r
+                               } else {\r
+                                       assert incoming.length == 0: "Unreachable vertices";\r
+                                       // Assign an empty set for unreachable vertices.\r
+                                       if (reachingTypes[vertexId] == null) {\r
+                                               reachingTypes[vertexId] = new TypeSet(typeSetManager);\r
+                                       }\r
+                               }\r
+                               \r
+                               return true;\r
+                       }\r
+                       \r
+                       @Override\r
+                       public void onFinished() {\r
+                               // Vertices in the same SCC share the same TypeSet.\r
+                               for (int i=0; i<reachingTypes.length; ++i) {\r
+                                       int v = typePropagationDAG.getRepresentativeNode(i);\r
+                                       if (v != i) {\r
+                                               reachingTypes[i] = reachingTypes[v];\r
+                                       }\r
+                               }\r
+                       }\r
+               });\r
+       }\r
+       \r
+       private void analyzeInstruction(int index, AbstractInsnNode instruction, MethodInfo m, DataDependence dataflow) {\r
+               \r
+               switch (instruction.getOpcode()) { \r
+               case Opcodes.ARETURN:\r
+               {\r
+                       int targetVertexId = localVerticesMap.get(m).getReturnVertex();\r
+                       int[][] operandSources = dataflow.getDataDefinition(index);\r
+                       assert operandSources.length == 1: "ARETURN takes a single parameter.";\r
+                       int[] sources = operandSources[0];\r
+                       for (int sourceInstructionIndex: sources) {\r
+                               for (int sourceId: getSourceVertices(sourceInstructionIndex, m, dataflow)) {\r
+                                       addEdge(sourceId, targetVertexId);\r
+                               }\r
+                       }\r
+                       break;\r
+               }\r
+               case Opcodes.PUTFIELD:\r
+               case Opcodes.PUTSTATIC:\r
+               {\r
+                       FieldVertex v = getFieldVertexId((FieldInsnNode)instruction);\r
+                       if (v == null) return;\r
+                       int targetVertexId = v.getId();\r
+                       int[][] operandSources = dataflow.getDataDefinition(index);\r
+                       int[] sources;\r
+                       if (instruction.getOpcode() == Opcodes.PUTFIELD) {\r
+                               assert operandSources.length == 2: "PUTFIELD takes two parameters (object and value)";\r
+                               sources = operandSources[1];\r
+                       } else {\r
+                               assert operandSources.length == 1: "PUTFIELD takes a parameter (value)";\r
+                               sources = operandSources[0];\r
+                       }\r
+                       for (int sourceInstructionIndex: sources) {\r
+                               for (int sourceId: getSourceVertices(sourceInstructionIndex, m, dataflow)) {\r
+                                       addEdge(sourceId, targetVertexId);\r
+                               }\r
+                       }\r
+                       break;\r
+               }\r
+               case Opcodes.ASTORE:\r
+               {\r
+                       int[][] operandSources = dataflow.getDataDefinition(index);\r
+                       int[] sources = operandSources[0]; \r
+                       assert operandSources.length == 1: "ASTORE takes a single parameter (value)";\r
+                       if (sources.length == 1 && sources[0] == -1) {\r
+                               // ASTORE at the beginning of CATCH block has a direct data flow (without ALOAD instruction).\r
+                               List<?> blocks = m.getMethodNode().tryCatchBlocks;\r
+                               for (int i=0; i<blocks.size(); ++i) {\r
+                                       TryCatchBlockNode node = (TryCatchBlockNode)blocks.get(i);\r
+                                       AbstractInsnNode handler = node.handler;\r
+                                       while (handler != null) {\r
+                                               if (handler == instruction) {\r
+                                                       // instruction is to store an exception object to a local variable.\r
+                                                       int vertexId = getLocalVariableVertex(m, index);\r
+                                                       String type = node.type;\r
+                                                       if (type == null) type = TypeSet.DEFAULT_UNKNOWN_TYPE;\r
+                                                       catchVariableVertices.put(vertexId, type);\r
+                                                       return;\r
+                                               } else {\r
+                                                       if (handler.getType() == AbstractInsnNode.LABEL || \r
+                                                               handler.getType() == AbstractInsnNode.FRAME ||\r
+                                                               handler.getType() == AbstractInsnNode.LINE) {\r
+                                                               handler = handler.getNext();\r
+                                                       } else {\r
+                                                               break; // different try-catch or finally block\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                               assert false: "A separated ASTORE outside of try-catch blocks.";\r
+                       } else {\r
+                               int targetVertexId = getLocalVariableVertex(m, index);\r
+                               if (targetVertexId != VERTEX_ERROR) { \r
+                                       for (int sourceInstructionIndex: sources) {\r
+                                               for (int sourceId: getSourceVertices(sourceInstructionIndex, m, dataflow)) {\r
+                                                       addEdge(sourceId, targetVertexId);\r
+                                               }\r
+                                       }\r
+                               } else {\r
+                                       assert false: "ASTORE must have its corresponding ALOAD.";\r
+                               }\r
+                       }\r
+                       break;\r
+               }\r
+               case Opcodes.INVOKEVIRTUAL:\r
+               case Opcodes.INVOKESPECIAL:\r
+               case Opcodes.INVOKESTATIC:\r
+               case Opcodes.INVOKEDYNAMIC:\r
+               case Opcodes.INVOKEINTERFACE:\r
+               {\r
+                       // Create edges for invocations.\r
+                       // This process is separated from creation of vertices since\r
+                       // vertices of return values must be generated.\r
+                       CallSiteVertices actuals = callsiteMap.get(m)[index];\r
+                       if (actuals != null) {\r
+                               int[][] operandSources = dataflow.getDataDefinition(index);\r
+                               assert (operandSources.length == actuals.getParamCount()): "The number of operands must be the same as the number of actual vertices.";\r
+                               // In general, the number of operands is the same as actual parameters.\r
+                               for (int i=0; i<actuals.getParamCount(); ++i) {\r
+                                       if (actuals.isObjectParam(i)) {\r
+                                               int actualId = actuals.getParamVertexId(i);\r
+                                               for (int sourceInstruction: operandSources[i]) {\r
+                                                       for (int sourceVertexId: getSourceVertices(sourceInstruction, m, dataflow)) {\r
+                                                               addEdge(sourceVertexId, actualId);\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+                       break;\r
+               }\r
+               case Opcodes.AASTORE:\r
+               {\r
+                       int[][] operandSources = dataflow.getDataDefinition(index);\r
+                       int[] arraySources = operandSources[0];\r
+                       int[] valueSources = operandSources[2];\r
+                       assert operandSources.length == 3: "AASTORE takes three parameters: object, index and value.";\r
+                       for (int valueSourceInstructionIndex: valueSources) {\r
+                               for (int sourceId: getSourceVertices(valueSourceInstructionIndex, m, dataflow)) {\r
+                                       for (int arraySourceInstructionIndex: arraySources) {\r
+                                               for (int arrayId: getSourceVertices(arraySourceInstructionIndex, m, dataflow)) {\r
+                                                       addEdge(sourceId, arrayId);\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+                       break;\r
+               }\r
+               }\r
+       }\r
+       \r
+       private void addEdge(int sourceVertex, int destinationVertex) {\r
+               edges.add(sourceVertex, destinationVertex);\r
+               \r
+               // If at least one of the vertices is an array type,\r
+               // or both of the vertices are "java.lang.Object", \r
+               // then connect a back edge to represent an alias.\r
+               String sourceType = declaredTypeNames.get(sourceVertex);\r
+               String destinationType = declaredTypeNames.get(destinationVertex);\r
+               if ((sourceType.endsWith(ARRAY_SUFFIX) || destinationType.endsWith(ARRAY_SUFFIX)) || \r
+                               (sourceType.equals("java/lang/Object") && destinationType.equals("java/lang/Object"))) {\r
+                       edges.add(destinationVertex, sourceVertex);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Return a vertex ID representing a local variable accessed by a specified instruction.\r
+        * @param m\r
+        * @param instructionIndex\r
+        * @return\r
+        */\r
+       private int getLocalVariableVertex(MethodInfo m, int instructionIndex) {\r
+               MethodVertices l = localVerticesMap.get(m);\r
+               if (l != null) {\r
+                       return l.getLocalVertex(instructionIndex);\r
+               } else {\r
+                       assert false: "MethodVertices is not registered.";\r
+                       return VERTEX_ERROR;\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * @param instructionIndex specifies an instruction \r
+        * that generates a value. \r
+        * @return vertex IDs that may correspond to a value.  \r
+        */\r
+       private int[] getSourceVertices(int instructionIndex, MethodInfo m, DataDependence dataflow) {\r
+               assert instructionIndex >= 0: "Instruction must be >=0 : " + Integer.toString(instructionIndex);\r
+               \r
+               InsnList instructions = m.getMethodNode().instructions;\r
+               AbstractInsnNode node = instructions.get(instructionIndex);\r
+               switch (node.getOpcode()) {\r
+               case Opcodes.CHECKCAST:\r
+                       {\r
+                               int[][] operands = dataflow.getDataDefinition(instructionIndex);\r
+                               assert operands.length == 1: "CHECKCAST takes a single parameter.";\r
+                               TIntArrayList defs = new TIntArrayList();\r
+                               for (int sourceInstruction: operands[0]) {\r
+                                       defs.add(getSourceVertices(sourceInstruction, m, dataflow));\r
+                               }\r
+                               return defs.toArray();\r
+                       }\r
+               case Opcodes.INVOKEINTERFACE:\r
+               case Opcodes.INVOKESPECIAL:\r
+               case Opcodes.INVOKESTATIC:\r
+               case Opcodes.INVOKEVIRTUAL:\r
+               case Opcodes.INVOKEDYNAMIC:\r
+                       CallSiteVertices[] sites = callsiteMap.get(m);\r
+                       if (sites[instructionIndex] != null) {\r
+                               return new int[] { sites[instructionIndex].getReturnValueVertex() };\r
+                       } else {\r
+                               return new int[] { VERTEX_ERROR };\r
+                       }\r
+\r
+               case Opcodes.GETFIELD:\r
+               case Opcodes.GETSTATIC:\r
+                       FieldInsnNode f = (FieldInsnNode)node;\r
+                       FieldVertex v = getFieldVertexId(f);\r
+                       if (v != null) {\r
+                               return new int[] { v.getId() };\r
+                       } else {\r
+                               return new int[0];\r
+                       }\r
+\r
+               case Opcodes.AALOAD:\r
+                       {\r
+                               int[][] operands = dataflow.getDataDefinition(instructionIndex);\r
+                               assert operands.length == 2: "AALOAD takes two parameters.";\r
+                               TIntArrayList defs = new TIntArrayList();\r
+                               for (int sourceInstruction: operands[0]) { // arrayRef\r
+                                       defs.add(getSourceVertices(sourceInstruction, m, dataflow));\r
+                               }\r
+                               return defs.toArray();\r
+                       }\r
+                       \r
+               case Opcodes.ALOAD:\r
+                       return new int[] { getLocalVariableVertex(m, instructionIndex) };\r
+                       \r
+               case Opcodes.MULTIANEWARRAY:\r
+               case Opcodes.ANEWARRAY: \r
+               case Opcodes.NEW:\r
+                       return new int[] { newVerticesMap.get(m).getNewInstructionVertex(instructionIndex) };\r
+                       \r
+               default:\r
+                       return new int[] { VERTEX_ERROR };\r
+               }\r
+       }\r
+       \r
+       private FieldVertex getFieldVertexId(FieldInsnNode node) {\r
+               String className = node.owner;\r
+               String fieldName = node.name;\r
+               String desc = node.desc;\r
+               String owner;\r
+               if (node.getOpcode() == Opcodes.PUTSTATIC || \r
+                       node.getOpcode() == Opcodes.GETSTATIC) {\r
+                       owner = hierarchy.resolveStaticFieldOwner(className, fieldName, desc);\r
+               } else {\r
+                       assert node.getOpcode() == Opcodes.PUTFIELD || \r
+                               node.getOpcode() == Opcodes.GETFIELD;\r
+                       owner = hierarchy.resolveInstanceFieldOwner(className, fieldName, desc);\r
+               }\r
+               ClassInfo c = hierarchy.getClassInfo(owner);\r
+               if (c != null) {\r
+                       FieldInfo f = c.findField(fieldName, desc);\r
+                       if (f != null) {\r
+                               FieldVertex fv = fieldVertex.get(f);\r
+                               if (fv != null) {\r
+                                       return fv;\r
+                               } else {\r
+                                       assert TypeConstants.isPrimitiveTypeName(f.getFieldTypeName());\r
+                                       return null;\r
+                               }\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       /**\r
+        * Return a set of types that may be invoked by a method call.\r
+        * @param m specifies a caller method.\r
+        * @param instruction specifies an INVOKE instruction in the caller method.\r
+        * @return a TypeSet including types that may be assigned to a receiver object.\r
+        */\r
+       public TypeSet getReceiverTypeAtCallsite(MethodInfo m, int instruction) {\r
+               CallSiteVertices[] callsites = callsiteMap.get(m);\r
+               if (callsites != null) {\r
+                       CallSiteVertices params = callsites[instruction];\r
+                       if (params != null) {\r
+                               if (!params.getCallSite().isStaticMethod()) {\r
+                                       int v = params.getParamVertexId(0);\r
+                                       return reachingTypes[v];\r
+                               } else {\r
+                                       return null;\r
+                               }\r
+                       } else {\r
+                               // params == null if no method implementations are included in the analysis.\r
+                               return null;\r
+                       }\r
+               } else {\r
+                       return null;\r
+               }\r
+       }\r
+       \r
+       public TypeSet getMethodParamType(MethodInfo m, int paramIndex) {\r
+               MethodVertices vertices = localVerticesMap.get(m);\r
+               if (vertices.hasFormalVertex(paramIndex)) {\r
+                       return reachingTypes[vertices.getFormalVertex(paramIndex)];\r
+               } else {\r
+                       return null;\r
+               }\r
+       }\r
+               \r
+       private static class FieldVertex {\r
+               private int vertexID;\r
+               private FieldInfo fieldInfo;\r
+               public FieldVertex(FieldInfo f, int vID) {\r
+                       this.vertexID = vID;\r
+                       this.fieldInfo= f;\r
+               }\r
+               \r
+               public int getId() {\r
+                       return vertexID;\r
+               }\r
+               \r
+               public String getTypeName() {\r
+                       return TypeResolver.getTypeName(fieldInfo.getDescriptor());\r
+               }\r
+               \r
+               public FieldInfo getFieldInfo() {\r
+                       return fieldInfo;\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * @param typename represents a type. \r
+        * The value may be an array type.\r
+        * @return If typename is a regular type, \r
+        * the typename is returned.\r
+        * If typename is an array type, \r
+        * the return value is a base type of the array.  \r
+        */\r
+       private String extractBaseType(String typename) {\r
+               assert typename != null;\r
+               while (typename.endsWith("[]")) {\r
+                       typename = typename.substring(0, typename.length() - 2);\r
+               }\r
+               assert typename != null;\r
+               return typename;\r
+       }\r
+       \r
+}\r
diff --git a/src/soba/util/IntPairList.java b/src/soba/util/IntPairList.java
new file mode 100755 (executable)
index 0000000..5c091bb
--- /dev/null
@@ -0,0 +1,172 @@
+package soba.util;\r
+\r
+import java.util.Arrays;\r
+\r
+/**\r
+ * This class represents a list of integer pairs.\r
+ */\r
+public class IntPairList {\r
+\r
+       private int count;\r
+       private long[] values;\r
+       private boolean frozen;\r
+\r
+       /**\r
+        * Creates a new <code>IntPairList</code> instance with the default size.\r
+        */\r
+       public IntPairList() {\r
+               this(1024);\r
+       }\r
+\r
+       /**\r
+        * @param capacity specifies the list size.\r
+        * Creates a new <code>IntPairList</code> instance with the specified size.\r
+        */\r
+       public IntPairList(int capacity) {\r
+               values = new long[capacity]; \r
+               count = 0;\r
+       }\r
+       \r
+       /**\r
+        * @return the number of elements.\r
+        */\r
+       public int size() {\r
+               return count; \r
+       }\r
+       \r
+       private long compose(int elem1, int elem2) {\r
+               return (((long)elem1) << 32) | elem2;\r
+       }\r
+\r
+       /**\r
+        * @param index specifies a pair.\r
+        * @return a first value of the specified pair.\r
+        */\r
+       public int getFirstValue(int index) {\r
+               if (index < 0 || index >= count) {\r
+                       throw new ArrayIndexOutOfBoundsException(index);\r
+               }\r
+               return (int)(values[index] >> 32);\r
+       }\r
+\r
+       /**\r
+        * @param index specifies a pair.\r
+        * @return a second value of the specified pair.\r
+        */\r
+       public int getSecondValue(int index) {\r
+               if (index < 0 || index >= count) {\r
+                       throw new ArrayIndexOutOfBoundsException(index);\r
+               }\r
+               return (int)(values[index] & 0xFFFFFFFF);\r
+       }\r
+\r
+       /**\r
+        * Adds a pair of integers.\r
+        * @param elem1 specifies a first value.\r
+        * @param elem2 specifies a second value.\r
+        */\r
+       public void add(int elem1, int elem2) {\r
+               if (!frozen) {\r
+                       if (values.length == count) growUp();\r
+                       values[count] = compose(elem1, elem2);\r
+                       count++;\r
+               } else {\r
+                       throw new FrozenListException();\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Adds all the pairs of another list.\r
+        * @param another is a <code>IntPairList</code> object.\r
+        */\r
+       public void addAll(IntPairList another) {\r
+               if (!frozen) {\r
+                       int anotherSize = another.size();\r
+                       for (int i=0; i<anotherSize; ++i) {\r
+                               if (values.length == count) growUp();\r
+                               values[count] = another.values[i];\r
+                               count++;\r
+                       }\r
+               } else {\r
+                       throw new FrozenListException();\r
+               }\r
+       }\r
+\r
+       private void growUp() {\r
+               long[] newValues = new long[values.length * 2];\r
+               for (int i=0; i<count; ++i) {\r
+                       newValues[i] = values[i];\r
+               }\r
+               values = newValues;\r
+       }\r
+       \r
+       /**\r
+        * Sets a first value of a pair at the specified position.\r
+        * @param index specifies a position in the list.\r
+        * @param first is a stored value.\r
+        */\r
+       public void setFirstValue(int index, int first) {\r
+               if (!frozen) {\r
+                       if ((index < 0) || (count <= index)) {\r
+                               throw new ArrayIndexOutOfBoundsException();\r
+                       } else {\r
+                               int second = getSecondValue(index);\r
+                               values[index] = compose(first, second);\r
+                       }\r
+               } else {\r
+                       throw new FrozenListException();\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Sets a second value of a pair at the specified position.\r
+        * @param index specifies a position in the list.\r
+        * @param second is a stored value.\r
+        */\r
+       public void setSecondValue(int index, int second) {\r
+               if (!frozen) {\r
+                       if ((index < 0) || (count <= index)) {\r
+                               throw new ArrayIndexOutOfBoundsException();\r
+                       } else {\r
+                               int first = getFirstValue(index);\r
+                               values[index] = compose(first, second);\r
+                       }\r
+               } else {\r
+                       throw new FrozenListException();\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Sorts a list by the order of the composed values.\r
+        */\r
+       public void sort() {\r
+               Arrays.sort(values, 0, count);\r
+       }\r
+       \r
+       /**\r
+        * Executes a procedure for each element.\r
+        * @param proc\r
+        */\r
+       public void foreach(IntPairProc proc) {\r
+               boolean cont = true;\r
+               for (int i=0; cont && (i<count); ++i) {\r
+                       cont = proc.execute(getFirstValue(i), getSecondValue(i));\r
+               }\r
+       }\r
+       \r
+       \r
+       /**\r
+        * This method freezes the list and releases \r
+        * unnecessary memory buffer for additional elements.\r
+        * If you add a new pair of integers, \r
+        * this list throws an exception.\r
+        */\r
+       public void freeze() {\r
+               frozen = true;\r
+       }\r
+       \r
+       public static class FrozenListException extends RuntimeException {\r
+               private static final long serialVersionUID = 1519361503126979153L;\r
+       }\r
+       \r
+}\r
diff --git a/src/soba/util/IntPairProc.java b/src/soba/util/IntPairProc.java
new file mode 100755 (executable)
index 0000000..f11692c
--- /dev/null
@@ -0,0 +1,15 @@
+package soba.util;\r
+\r
+/**\r
+ * A callback interface for a pair of integers.\r
+ * @author ishio\r
+ *\r
+ */\r
+public interface IntPairProc {\r
+\r
+       /**\r
+        * @return true if you want to continue this loop.\r
+        */\r
+       public boolean execute(int elem1, int elem2);\r
+\r
+}\r
diff --git a/src/soba/util/IntPairSet.java b/src/soba/util/IntPairSet.java
new file mode 100755 (executable)
index 0000000..c37127c
--- /dev/null
@@ -0,0 +1,105 @@
+package soba.util;\r
+\r
+import gnu.trove.map.hash.TIntObjectHashMap;\r
+import gnu.trove.procedure.TIntObjectProcedure;\r
+import gnu.trove.procedure.TIntProcedure;\r
+import gnu.trove.set.hash.TIntHashSet;\r
+\r
+/**\r
+ * This class represents a set of integer pairs.\r
+ */\r
+public class IntPairSet {\r
+\r
+       private TIntObjectHashMap<TIntHashSet> intPairs;\r
+       private int pairCount;\r
+       \r
+       /**\r
+        * Creates a new <code>IntPairSet</code> instance.\r
+        */\r
+       public IntPairSet() {\r
+               intPairs = new TIntObjectHashMap<TIntHashSet>();\r
+               pairCount = 0;\r
+       }\r
+       \r
+       /**\r
+        * Adds a pair of integers.\r
+        * If the same pair of integers has been already stored,\r
+        * this method does not change the state.\r
+        * @param elem1 specifies a first value.\r
+        * @param elem2 specifies a second value.\r
+        */\r
+       public void add(int elem1, int elem2) {\r
+               TIntHashSet secondValues = intPairs.get(elem1);\r
+               if (secondValues == null) {\r
+                       secondValues = new TIntHashSet();\r
+                       intPairs.put(elem1, secondValues);\r
+               }\r
+               boolean added = secondValues.add(elem2);\r
+               if (added) {\r
+                       ++pairCount;\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * @return true if the set contains a pair (elem1, elem2).\r
+        */\r
+       public boolean contains(int elem1, int elem2) {\r
+               TIntHashSet secondValues = intPairs.get(elem1);\r
+               if (secondValues != null) {\r
+                       return secondValues.contains(elem2);\r
+               } else {\r
+                       return false;\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * @return true if the set contains a pair (elem1, _).\r
+        */\r
+       public boolean containsFirst(int elem1) {\r
+               TIntHashSet secondValues = intPairs.get(elem1);\r
+               if (secondValues != null) {\r
+                       return !secondValues.isEmpty();\r
+               } else {\r
+                       return false;\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * @return the number of pairs in the set.\r
+        */\r
+       public int size() {\r
+               return pairCount;\r
+       }\r
+       \r
+       /**\r
+        * Executes a procedure for each element.\r
+        * @param proc\r
+        */\r
+       public void foreach(final IntPairProc proc) { \r
+               intPairs.forEachEntry(new IntPairIterator(proc));\r
+       }\r
+       \r
+       private class IntPairIterator implements TIntObjectProcedure<TIntHashSet> {\r
+               \r
+               private IntPairProc proc;\r
+               private boolean continueFlag;\r
+               \r
+               public IntPairIterator(IntPairProc proc) {\r
+                       this.proc = proc;\r
+                       this.continueFlag = true;\r
+               }\r
+               \r
+               @Override\r
+               public boolean execute(final int firstValue, TIntHashSet secondValues) {\r
+                       secondValues.forEach(new TIntProcedure() {\r
+                               \r
+                               @Override\r
+                               public boolean execute(int secondValue) {\r
+                                       continueFlag = proc.execute(firstValue, secondValue);\r
+                                       return continueFlag;\r
+                               }\r
+                       });                     \r
+                       return continueFlag;\r
+               }\r
+       }\r
+}\r
diff --git a/src/soba/util/IntPairUtil.java b/src/soba/util/IntPairUtil.java
new file mode 100755 (executable)
index 0000000..ca8f738
--- /dev/null
@@ -0,0 +1,51 @@
+package soba.util;\r
+\r
+/**\r
+ * A utility for collections of integer pairs.\r
+ */\r
+public class IntPairUtil {\r
+       \r
+       /**\r
+        * Creates an IntPairList from an IntPairSet.\r
+        * The resultant list is not sorted;\r
+        * the order of elements depends on the set's implementation.\r
+        */\r
+       public static IntPairList createList(IntPairSet set) {\r
+               final IntPairList list = new IntPairList(set.size());\r
+               set.foreach(new IntPairProc() {\r
+                       @Override\r
+                       public boolean execute(int elem1, int elem2) {\r
+                               list.add(elem1, elem2);\r
+                               return true;\r
+                       }\r
+               });\r
+               return list;\r
+       }\r
+       \r
+       \r
+       /**\r
+        * Creates an IntPairList that includes two IntPairList sets.\r
+        */\r
+       public static IntPairList createList(final IntPairSet set1, final IntPairSet set2) {\r
+               final IntPairList list = new IntPairList(set1.size() + set2.size());\r
+               set1.foreach(new IntPairProc() {\r
+                       @Override\r
+                       public boolean execute(int elem1, int elem2) {\r
+                               list.add(elem1, elem2);\r
+                               return true;\r
+                       }\r
+               });\r
+               set2.foreach(new IntPairProc() {\r
+                       @Override\r
+                       public boolean execute(int elem1, int elem2) {\r
+                               if (!set1.contains(elem1, elem2)) {\r
+                                       list.add(elem1, elem2);\r
+                               }\r
+                               return true;\r
+                       }\r
+               });\r
+               return list;\r
+               \r
+       }\r
+\r
+}\r
diff --git a/src/soba/util/IntSetStack.java b/src/soba/util/IntSetStack.java
new file mode 100755 (executable)
index 0000000..192568f
--- /dev/null
@@ -0,0 +1,124 @@
+package soba.util;\r
+\r
+/**\r
+ * <code>IntSetStack</code> is a stack in which int values can be stored.\r
+ * The stack cannot contain two instances of the same value;\r
+ * for example, "aIntSetStack.push(0); aIntSetStack.push(0);" \r
+ * causes a DuplicatedValueException.\r
+ * Instead of the set constraint, this stack \r
+ * provides O(1) implementation of contains(int).\r
+ */\r
+public class IntSetStack extends IntStack {\r
+\r
+       private int max;\r
+       private static final int DIV = 31;\r
+       private int[] elements;\r
+       private boolean ignoreDuplicatedElement;\r
+        \r
+       private static int elementIndex(int value) {\r
+               return value/DIV;\r
+       }\r
+\r
+       private static int bitMask(int value) {\r
+               int b = value%DIV;\r
+               return 1 << b;\r
+       }\r
+\r
+       /**\r
+        * Creates a new <code>IntSetStack</code> instance.\r
+        * @param maxValue is a maximum size of the stack.\r
+        */\r
+       public IntSetStack(int maxValue) {\r
+               super();\r
+               max = maxValue;\r
+               elements = new int[elementIndex(maxValue)+1];\r
+       }\r
+       \r
+       /**\r
+        * Creates a new <code>IntSetStack</code> instance with a specified size.\r
+        * @param maxValue is a maximum size of the stack.\r
+        * @param capacity is a size of the stack. \r
+        */\r
+       public IntSetStack(int maxValue, int capacity) {\r
+               super(capacity);\r
+               elements = new int[elementIndex(maxValue)+1];\r
+       }\r
+       \r
+       public void setIgnoreDuplicatedElement(boolean ignore) {\r
+               ignoreDuplicatedElement = ignore;\r
+       }\r
+       \r
+       /**\r
+        * O(1) implementation \r
+        */\r
+       @Override\r
+       public boolean contains(int value) {\r
+               int bit = elements[ elementIndex(value) ];\r
+               return ((bit & bitMask(value)) != 0);\r
+       }\r
+       \r
+       /** {@inheritDoc} */\r
+       @Override\r
+       public void push(int value) {\r
+               if (max < value) throw new InvalidElementException(max, value);\r
+               int index = elementIndex(value);\r
+               int bit = elements[ index ];\r
+               int bitMask = bitMask(value);\r
+               if ((bit & bitMask) == 0) {\r
+                       super.push(value);\r
+                       elements[ index ] |= bitMask;\r
+               } else {\r
+                       if (!ignoreDuplicatedElement) {\r
+                               throw new DuplicatedElementException();\r
+                       }\r
+               }\r
+               assert (elements[index] & bitMask) != 0;\r
+       }\r
+       \r
+       /** {@inheritDoc} */\r
+       @Override\r
+       public int pop() {\r
+               int value = super.pop();\r
+               int index = elementIndex(value);\r
+               int bitMask = bitMask(value);\r
+               elements[index] ^= bitMask;\r
+               assert (elements[index] & bitMask) == 0;\r
+               return value;\r
+       }\r
+\r
+       public class InvalidElementException extends RuntimeException {\r
+\r
+               private static final long serialVersionUID = -8544940855615952576L;\r
+\r
+               private int max;\r
+               private int value;\r
+               \r
+               public InvalidElementException(int max, int value) {\r
+                       this.max = max;\r
+                       this.value = value;\r
+               }\r
+               \r
+               @Override\r
+               public String getMessage() {\r
+                       return "The given value cannot be stored into the stack. MAX=" + \r
+                               Integer.toString(max) + ", VALUE=" + Integer.toString(value);\r
+               }\r
+               \r
+               public int getValue() {\r
+                       return value;\r
+               }\r
+               \r
+               public int getMax() {\r
+                       return max;\r
+               }\r
+       }\r
+\r
+       public class DuplicatedElementException extends RuntimeException {\r
+\r
+               private static final long serialVersionUID = -3896583429225358307L;\r
+\r
+               public DuplicatedElementException() {\r
+               }\r
+       }\r
+\r
+}\r
diff --git a/src/soba/util/IntStack.java b/src/soba/util/IntStack.java
new file mode 100755 (executable)
index 0000000..60ba225
--- /dev/null
@@ -0,0 +1,98 @@
+package soba.util;\r
+\r
+import java.util.EmptyStackException;\r
+\r
+/**\r
+ * This class represents a integer stack.\r
+ */\r
+public class IntStack {\r
+\r
+       private int count = 0;\r
+       private int[] values;\r
+\r
+       /**\r
+        * Creates a new <code>IntStack</code> with the default size.\r
+        */\r
+       public IntStack() {\r
+               this(1024);\r
+       }\r
+       \r
+       /**\r
+        * Creates a new <code>IntStack</code> with a specified size.\r
+        * @param capacity is a size of the stack.\r
+        */\r
+       public IntStack(int capacity) {\r
+               count = 0;\r
+               values = new int[capacity];\r
+       }\r
+       \r
+       /**\r
+        * @return true if the stack is empty.\r
+        */\r
+       public boolean isEmpty() {\r
+               return count == 0;\r
+       }\r
+       \r
+       /**\r
+        * Pushes a value to the top of the stack.\r
+        * @param value\r
+        */\r
+       public void push(int value) {\r
+               if (count >= values.length) {\r
+                       growUp();\r
+               }\r
+               values[count] = value;\r
+               count++;\r
+       }\r
+       \r
+       /**\r
+        * Gets a value from the top of the stack.\r
+        * The value is deleted from the stack.\r
+        * @return a top value of the stack.\r
+        */\r
+       public int pop() {\r
+               if (count == 0) {\r
+                       throw new EmptyStackException();\r
+               }\r
+               count--;\r
+               return values[count];\r
+       }\r
+       \r
+       /**\r
+        * Gets a top value of the stack.\r
+        * The value is not deleted from the stack.\r
+        * @return a top value of the stack.\r
+        */\r
+       public int peek() {\r
+               if (count == 0) {\r
+                       throw new EmptyStackException();\r
+               }\r
+               return values[count-1];\r
+       }\r
+       \r
+       /**\r
+        * @param value\r
+        * @return true if the value is contained in the stack.\r
+        */\r
+       public boolean contains(int value) {\r
+               for (int i=0; i<count; ++i) {\r
+                       if (values[i] == value) return true;\r
+               }\r
+               return false;\r
+       }\r
+\r
+       private void growUp() {\r
+               int[] newValues = new int[values.length * 2];\r
+               for (int i=0; i<count; ++i) {\r
+                       newValues[i] = values[i];\r
+               }\r
+               values = newValues;\r
+       }\r
+       \r
+       /**\r
+        * @return the number of elements in the stack.\r
+        */\r
+       public int size() {\r
+               return count;\r
+       }\r
+}\r
diff --git a/src/soba/util/ObjectIdMap.java b/src/soba/util/ObjectIdMap.java
new file mode 100755 (executable)
index 0000000..30bdf1c
--- /dev/null
@@ -0,0 +1,93 @@
+package soba.util;\r
+\r
+import gnu.trove.map.hash.TObjectIntHashMap;\r
+\r
+import java.util.ArrayList;\r
+\r
+public class ObjectIdMap<T> {\r
+       \r
+       private TObjectIntHashMap<T> map;\r
+       private ArrayList<T> idToObject;\r
+       private boolean frozen;\r
+       \r
+       /**\r
+        * Creates a new <code>ObjectIdMap</code> instance with the default size.\r
+        */\r
+       public ObjectIdMap() {\r
+               this(1024 * 1024);\r
+       }\r
+\r
+       /**\r
+        * Creates a new <code>ObjectIdMap</code> instance with a specified size.\r
+        * @param capacity\r
+        */\r
+       public ObjectIdMap(int capacity) {\r
+               map = new TObjectIntHashMap<T>(capacity * 2);\r
+               idToObject = new ArrayList<T>(capacity);\r
+               frozen = false;\r
+       }\r
+       \r
+       /**\r
+        * Disable assigning new IDs.\r
+        * For frozen maps, getId method with a new object \r
+        * returns -1 (or throws AssertionError).\r
+        */\r
+       public void freeze() {\r
+               frozen = true;\r
+       }\r
+       \r
+       /**\r
+        * Adds a new object.\r
+        * @param s\r
+        */\r
+       public void add(T s) {\r
+               getId(s);\r
+       }\r
+       \r
+       /**\r
+        * @param item specifies an object.\r
+        * @return the ID integer corresponding to the object.\r
+        * If a new object is given, this method returns a new id.\r
+        */\r
+       public int getId(T item) {\r
+               if (map.containsKey(item)) {\r
+                       return map.get(item);\r
+               } else {\r
+                       if (frozen) {\r
+                               // A new object is added to the frozen id map.\r
+                               throw new FrozenMapException();\r
+                       } else {\r
+                               int newId = idToObject.size();\r
+                               map.put(item, newId);\r
+                               idToObject.add(item);\r
+                               return newId;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * @param id\r
+        * @return an object.\r
+        */\r
+       public T getItem(int id) {\r
+               if ((id < 0) || (id >= idToObject.size())) return null;\r
+               else return idToObject.get(id);\r
+       }\r
+       \r
+       /**\r
+        * @return the number of elements.\r
+        */\r
+       public int size() {\r
+               return idToObject.size();\r
+       }\r
+       \r
+       public static class FrozenMapException extends RuntimeException {\r
+               \r
+               private static final long serialVersionUID = -1682458461699095201L;\r
+\r
+               public FrozenMapException() {\r
+                       super();\r
+               }\r
+       }\r
+\r
+}\r
diff --git a/src/soba/util/Timer.java b/src/soba/util/Timer.java
new file mode 100755 (executable)
index 0000000..00f5617
--- /dev/null
@@ -0,0 +1,34 @@
+package soba.util;\r
+\r
+/**\r
+ * <code>Timer</code> records time consumed by this program.\r
+ */\r
+public class Timer {\r
+\r
+       private long startTimestamp;\r
+       private long timestamp;\r
+\r
+       /**\r
+        * Starts a timer.\r
+        */\r
+       public Timer() {\r
+               timestamp = System.currentTimeMillis();\r
+               startTimestamp = timestamp;\r
+       }\r
+       \r
+       /**\r
+        * @return consumed milliseconds since a previous checkpoint.\r
+        */\r
+       public long checkpoint() {\r
+               long oldTimestamp = timestamp;\r
+               timestamp = System.currentTimeMillis();\r
+               return timestamp - oldTimestamp;\r
+       }\r
+       \r
+       /**\r
+        * @return consumed milliseconds since the timer is created.\r
+        */\r
+       public long getTotaltime() {\r
+               return System.currentTimeMillis() - startTimestamp;\r
+       }\r
+}\r
diff --git a/src/soba/util/debug/DumpClass.java b/src/soba/util/debug/DumpClass.java
new file mode 100755 (executable)
index 0000000..940d423
--- /dev/null
@@ -0,0 +1,309 @@
+package soba.util.debug;\r
+\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+import org.objectweb.asm.Opcodes;\r
+import org.objectweb.asm.tree.LocalVariableNode;\r
+import org.objectweb.asm.tree.MethodNode;\r
+import org.objectweb.asm.tree.TryCatchBlockNode;\r
+import org.objectweb.asm.tree.analysis.Frame;\r
+\r
+import soba.core.ClassInfo;\r
+import soba.core.JavaProgram;\r
+import soba.core.MethodInfo;\r
+import soba.core.method.DataDependence;\r
+import soba.core.method.DataFlowEdge;\r
+import soba.core.method.OpcodeString;\r
+import soba.core.method.asm.FastSourceValue;\r
+import soba.util.IntPairProc;\r
+import soba.util.Timer;\r
+import soba.util.files.Directory;\r
+import soba.util.files.IClassList;\r
+import soba.util.files.SingleFile;\r
+import soba.util.files.ZipFile;\r
+import soba.util.graph.IDirectedGraph;\r
+\r
+public class DumpClass {\r
+\r
+       static Timer timer;\r
+       static int classCount = 0;\r
+       static int methodCount = 0;\r
+       static int failedMethodCount = 0;\r
+       static long instructionCount = 0;\r
+       static long dataflowEdgeCount = 0;\r
+       static boolean enableOutput = true;\r
+       static boolean dumpStackframe = false;\r
+       static boolean dumpParamName = false;\r
+       static boolean dumpTryBlock = false;\r
+       \r
+       /**\r
+        * @param args\r
+        */\r
+       public static void main(String[] args) {\r
+               timer = new Timer();\r
+               List<IClassList> files = new ArrayList<IClassList>();\r
+               \r
+               if (args.length == 0) {\r
+                       Directory bin = new Directory(new File("bin"));\r
+                       Directory lib = new Directory(new File("lib"));\r
+                       files.add(bin);\r
+                       files.add(lib);\r
+               } else {\r
+                       for (String arg: args) {\r
+                               if (arg.equals("--disable-output")) {\r
+                                       enableOutput = false;\r
+                               } else if (arg.equals("--output-frame")) {\r
+                                       dumpStackframe = true;\r
+                               } else if (arg.equals("--output-param")) {\r
+                                       dumpParamName = true;\r
+                               } else if (arg.equals("--output-try")) {\r
+                                       dumpTryBlock = true;\r
+                               } else {\r
+                                       File f = new File(arg);\r
+                                       if (f.isDirectory()) {\r
+                                               Directory dir = new Directory(f);\r
+                                               dir.enableRecursiveZipSearch();\r
+                                               files.add(dir);\r
+                                       } else if (ZipFile.isZipFile(f)) {\r
+                                               ZipFile zip = new ZipFile(f);\r
+                                               zip.enableRecursiveSearch();\r
+                                               files.add(zip);\r
+                                       } else {\r
+                                               SingleFile file = new SingleFile(f);\r
+                                               files.add(file);\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               JavaProgram program = new JavaProgram((IClassList[]) files.toArray(new IClassList[files.size()]));\r
+               program.getClasses().forEach(c -> processClass(c));\r
+               \r
+               System.err.println("FINISHED: " +  timer.getTotaltime() + " ms");\r
+               System.err.println("#Classes: " +  classCount);\r
+               System.err.println("#Methods: " +  methodCount);\r
+               System.err.println("#Instructions: " + instructionCount);\r
+               System.err.println("#Edges: " + dataflowEdgeCount);\r
+               System.err.println("#Failed: " +  failedMethodCount);\r
+       }\r
+       \r
+       private static void processClass(ClassInfo c) {\r
+               classCount++;\r
+               for (MethodInfo m: c.getMethods()) {\r
+                       if (enableOutput) {\r
+                               System.out.println(constructMethodNameString(c, m.getMethodNode()));\r
+                       }\r
+                       \r
+                       MethodInfo methodInfo = m;\r
+                       if (!methodInfo.hasMethodBody()) continue;\r
+\r
+                       DataDependence info = methodInfo.getDataDependence();\r
+                       \r
+                       if (info != null) {\r
+                               methodCount++;\r
+                               instructionCount += methodInfo.getInstructionCount();\r
+                               \r
+                               if (dumpParamName) {\r
+                                       for (int i=0; i<methodInfo.getParamCount(); ++i) {\r
+                                               System.out.print("  Param ");\r
+                                               System.out.print(i + 1);\r
+                                               System.out.print(" ");\r
+                                               System.out.print(methodInfo.getParamType(i));\r
+                                               System.out.print(" ");\r
+                                               System.out.println(methodInfo.getParamName(i));\r
+                                       }\r
+                               }\r
+\r
+                               \r
+                               for (int i=0; i<methodInfo.getInstructionCount(); ++i) {\r
+                                       if (enableOutput) {\r
+                                               System.out.println("  " + OpcodeString.getInstructionString(m.getMethodNode(), i));     \r
+                                       }\r
+                                       if (dumpStackframe) dumpStackframe(info, i);\r
+                               }\r
+                               \r
+                               if (dumpTryBlock) {\r
+                                       if (m.getMethodNode().tryCatchBlocks != null) {\r
+                                               System.out.println("  Try-catch Table:");\r
+                                               for (TryCatchBlockNode node: m.getMethodNode().tryCatchBlocks) {\r
+                                                       System.out.println("    start=" + OpcodeString.getLabelString(m.getMethodNode(), node.start) + ", end=" + OpcodeString.getLabelString(m.getMethodNode(), node.end) + ", handler=" + OpcodeString.getLabelString(m.getMethodNode(), node.handler) + " (" + node.type + ")");\r
+                                               }\r
+                                               System.out.println("  Try-catch Table End");\r
+                                       }\r
+                               }\r
+                               \r
+                               List<DataFlowEdge> edges = info.getEdges();\r
+                               if (enableOutput) {\r
+                                       for (DataFlowEdge e: edges) {\r
+                                               System.out.print("    ");\r
+                                               System.out.print(e.toString());\r
+                                               System.out.print("  ");\r
+//                                             ILocalVariableInfo v = info.getLocalVariable(e);\r
+//                                             if (v != null) {\r
+//                                                     System.out.print(v.getName() + ": " + v.getDescriptor());\r
+//                                             }\r
+                                               String variableName = info.getVariableName(e);\r
+                                               if (variableName != null) {\r
+                                                       System.out.print(variableName + ": " + info.getVariableDescriptor(e));\r
+                                               }\r
+                                               System.out.println();\r
+                                       }\r
+                               }\r
+                               dataflowEdgeCount += edges.size();\r
+                               \r
+                               IDirectedGraph cflow = methodInfo.getControlFlow();\r
+                               if (enableOutput) {\r
+                                       cflow.forEachEdge(new IntPairProc() {\r
+                                               @Override\r
+                                               public boolean execute(int elem1, int elem2) {\r
+                                                       System.out.println("    [CFLOW] " + elem1 + " -> " + elem2);\r
+                                                       return true;\r
+                                               }\r
+                                       });\r
+                               }\r
+                               \r
+                               IDirectedGraph cdepends = methodInfo.getControlDependence();\r
+                               if (enableOutput) {\r
+                                       cdepends.forEachEdge(new IntPairProc() {\r
+                                               @Override\r
+                                               public boolean execute(int elem1, int elem2) {\r
+                                                       System.out.println("    [Control] " + elem1 + " -> " + elem2);\r
+                                                       return true;\r
+                                               }\r
+                                       });\r
+                               }\r
+                               \r
+                       } else {\r
+                               failedMethodCount++;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       private static void dumpStackframe(DataDependence info, int i) {\r
+               Frame<?> f = info.getFrame(i);\r
+               if (f == null) {\r
+                       if (enableOutput) {\r
+                               System.out.print("    ");\r
+                               System.out.print(i);\r
+                               System.out.println(" [STACK] null");\r
+                       }\r
+               } else {\r
+                       int size = f.getStackSize();\r
+                       for (int s=0; s<size; ++s) {\r
+                               FastSourceValue source = (FastSourceValue)f.getStack(s);\r
+                               for (int pos: source.getInstructions()) {\r
+                                       if (enableOutput) {\r
+                                               System.out.print("    ");\r
+                                               System.out.print(i);\r
+                                               System.out.print(" [STACK] <");\r
+                                               System.out.print(s);\r
+                                               System.out.print("/");\r
+                                               System.out.print(size);\r
+                                               System.out.print("> ");\r
+                                               System.out.print(pos);\r
+                                               System.out.print(": ");\r
+                                               if (pos >= 0) {\r
+                                                       System.out.println(info.getInstruction(pos).toString());\r
+                                               } else {\r
+                                                       System.out.println("method param");\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+                       int locals = f.getLocals();\r
+                       for (int l=0; l<locals; ++l) {\r
+                               FastSourceValue source = (FastSourceValue)f.getLocal(l);\r
+                               for (int pos: source.getInstructions()) {\r
+                                       if (enableOutput) {\r
+                                               System.out.print("    ");\r
+                                               System.out.print(i);\r
+                                               System.out.print(" [LOCAL] <");\r
+                                               System.out.print(l);\r
+                                               System.out.print("/");\r
+                                               System.out.print(locals);\r
+                                               System.out.print("> ");\r
+                                               System.out.print(pos);\r
+                                               System.out.print(": ");\r
+                                               if (pos >= 0) {\r
+                                                       System.out.println(info.getInstruction(pos).toString());\r
+                                               } else {\r
+                                                       System.out.println("method param");\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       int consumeStack = info.getOperandCount(i);\r
+                       int maxStack = f.getStackSize();\r
+                       for (int s=0; s<consumeStack; ++s) {\r
+                               FastSourceValue source = (FastSourceValue)f.getStack(maxStack-1-s);\r
+                               for (int pos: source.getInstructions()) {\r
+                                       if (enableOutput) {\r
+                                               System.out.print("    ");\r
+                                               System.out.print(i);\r
+                                               System.out.print(" [OPERAND");\r
+                                               System.out.print(s);\r
+                                               System.out.print("] ");\r
+                                               System.out.print(pos);\r
+                                               System.out.print(": ");\r
+                                               if (pos >= 0) {\r
+                                                       System.out.println(info.getInstruction(pos).toString());\r
+                                               } else {\r
+                                                       System.out.println("method param");\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               int[][] def = info.getDataDefinition(i);\r
+               if (enableOutput) {\r
+                       for (int operandIndex=0; operandIndex<def.length; ++operandIndex) {\r
+                               System.out.print("    Operand " + Integer.toString(operandIndex) + " Data Dependence: ");\r
+                               if (def[operandIndex] != null) {\r
+                                       if (def[operandIndex].length == 0) {\r
+                                               System.out.print("EMPTY");\r
+                                       } else {\r
+                                               for (int fromIndex=0; fromIndex<def[operandIndex].length; ++fromIndex) {\r
+                                                       if (fromIndex>0) System.out.print(", ");\r
+                                                       System.out.print(def[operandIndex][fromIndex]);\r
+                                               }\r
+                                       }\r
+                               } else {\r
+                                       System.out.print("null");\r
+                               }\r
+                               System.out.println();\r
+                       }\r
+               }\r
+       }               \r
+       \r
+       private static String constructMethodNameString(ClassInfo c, MethodNode m) {\r
+               StringBuilder buf = new StringBuilder();\r
+               buf.append(c.getClassName());\r
+               buf.append("#");\r
+               buf.append(m.name);\r
+               buf.append("#");\r
+               buf.append(m.desc);\r
+               buf.append("#");\r
+               buf.append(m.signature);\r
+               if ((m.access & Opcodes.ACC_SYNTHETIC) != 0) {\r
+                       buf.append(" [Synthetic]");\r
+               }\r
+               \r
+               return buf.toString();\r
+       }\r
+       \r
+       @SuppressWarnings({ "unused" } ) \r
+       private static void dumpVariables(MethodNode m) {\r
+               List<LocalVariableNode> variables = m.localVariables;\r
+               if (variables != null) {\r
+                       System.out.println("---VARIABLES BEGIN---");\r
+                       for (LocalVariableNode v: variables) {\r
+                               System.out.println("   " + v.name + ": " + v.desc + " ");\r
+                       }\r
+                       System.out.println("---VARIABLES END---");\r
+               }\r
+       }\r
+       \r
+}\r
diff --git a/src/soba/util/files/ClasspathUtil.java b/src/soba/util/files/ClasspathUtil.java
new file mode 100755 (executable)
index 0000000..ae43c55
--- /dev/null
@@ -0,0 +1,95 @@
+package soba.util.files;\r
+\r
+import java.io.File;\r
+import java.io.FileFilter;\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.List;\r
+\r
+import soba.core.ClassInfo;\r
+\r
+/**\r
+ * The factory creates a list of JAR files in the system class paths.\r
+ */\r
+public class ClasspathUtil {\r
+       \r
+       public static IClassList[] getClassList(String[] files) {\r
+               return getClassList(Arrays.asList(files), null);\r
+       }\r
+\r
+       public static IClassList[] getClassList(String[] files, String label) {\r
+               return getClassList(Arrays.asList(files), label);\r
+       }\r
+\r
+       public static IClassList[] getClassList(List<String> files) { \r
+               return getClassList(files, null);\r
+       }\r
+\r
+       public static IClassList[] getClassList(String[] appFiles, String[] libFiles) {\r
+               IClassList[] apps = getClassList(appFiles);\r
+               IClassList[] libs = getClassList(libFiles, ClassInfo.LIBRARY_LABEL);\r
+               return merge(apps, libs);\r
+       }\r
+       \r
+       public static IClassList[] getClassList(List<String> files, String label) { \r
+               List<IClassList> result = new ArrayList<IClassList>();\r
+               for (String filepath: files) {\r
+                       File f = new File(filepath);\r
+                       if (f.isDirectory()) {\r
+                               Directory dir = new Directory(f);\r
+                               dir.enableRecursiveZipSearch();\r
+                               dir.setLabel(label);\r
+                               result.add(dir);\r
+                       } else if (ZipFile.isZipFile(f)) {\r
+                               ZipFile zip = new ZipFile(f);\r
+                               zip.enableRecursiveSearch();\r
+                               zip.setLabel(label);\r
+                               result.add(zip);\r
+                       } else if (ZipFile.isClassFile(f)) {\r
+                               SingleFile file = new SingleFile(f);\r
+                               file.setLabel(label);\r
+                               result.add(file);                               \r
+                       }\r
+               }\r
+               return result.toArray(new IClassList[0]);\r
+       }\r
+\r
+       public static List<String> enumerateSystemClasspath() {\r
+               final String PATH_SPLIT_REGEX = "\\s*" + File.pathSeparatorChar + "\\s*";\r
+               List<String> classpath = new ArrayList<String>(1024);\r
+               \r
+        String classPath = System.getProperty("java.class.path");\r
+        for (String path: classPath.split(PATH_SPLIT_REGEX)) {\r
+               classpath.add(new File(path).getAbsolutePath());\r
+        }\r
+        \r
+        String bootClassPath = System.getProperty("sun.boot.class.path");\r
+        for (String path: bootClassPath.split(PATH_SPLIT_REGEX)) {\r
+               classpath.add(new File(path).getAbsolutePath());\r
+        }\r
+        \r
+        String extDirs = System.getProperty("java.ext.dirs");\r
+        for (String extDirPath: extDirs.split(PATH_SPLIT_REGEX)) {\r
+               File extDir = new File(extDirPath);\r
+               if (extDir.isDirectory() && extDir.canRead()) {\r
+                       File[] extFiles = extDir.listFiles(new FileFilter() {\r
+                                       @Override\r
+                                       public boolean accept(File pathname) {\r
+                                               String lowerFilename = pathname.getAbsolutePath();\r
+                                               return lowerFilename.endsWith(".jar") || lowerFilename.endsWith(".zip");\r
+                                       }\r
+                               });\r
+                       for (File extFile: extFiles) {\r
+                               classpath.add(extFile.getAbsolutePath());\r
+                       }\r
+               }\r
+        }\r
+               return classpath;\r
+       }\r
+\r
+       public static IClassList[] merge(IClassList[] list1, IClassList[] list2) {\r
+               IClassList[] result = Arrays.copyOf(list1, list1.length + list2.length);\r
+               System.arraycopy(list2, 0, result, list1.length, list2.length);\r
+               return result;\r
+       }\r
+}\r
diff --git a/src/soba/util/files/Directory.java b/src/soba/util/files/Directory.java
new file mode 100755 (executable)
index 0000000..694e4b5
--- /dev/null
@@ -0,0 +1,136 @@
+package soba.util.files;\r
+\r
+import java.io.File;\r
+import java.io.FileFilter;\r
+import java.io.FileInputStream;\r
+import java.io.IOException;\r
+import java.util.ArrayList;\r
+import java.util.Stack;\r
+\r
+\r
+public class Directory implements IClassList {\r
+\r
+       private File dir;\r
+       private String label;\r
+       private boolean searchZip = false;\r
+       private boolean searchZipRecursive = false;\r
+       private boolean autoOpen = true;\r
+       \r
+       public Directory(File dir) {\r
+               assert dir.isDirectory(): dir.getAbsolutePath() + " is not a directory.";\r
+               this.dir = dir;\r
+       }\r
+       \r
+       public void setLabel(String l) {\r
+               this.label = l;\r
+       }\r
+       \r
+       @Override\r
+       public String getLabel() {\r
+               return label;\r
+       }\r
+       \r
+       /**\r
+        * Enumerate a list of directories involved in the specified top directory.\r
+        * @param topDir specifies a directory including sub-directories to be enumerated.\r
+        * @param depth specifies the maximum number of recursive search.\r
+        * "0" returns the top directory itself. "1" returns a list of directories in the top directory.\r
+        * @return\r
+        */\r
+       public static Directory[] listSubdirectories(File topDir, int depth) {\r
+               ArrayList<File> dirs = new ArrayList<File>();\r
+               dirs.add(topDir);\r
+               \r
+               for (int i=0; i<depth; ++i) {\r
+                       ArrayList<File> subdirs = new ArrayList<File>();\r
+                       for (File d: dirs) {\r
+                               for (File f: d.listFiles()) {\r
+                                       if (f.isDirectory() && \r
+                                               !f.getName().equals(".") &&\r
+                                               !f.getName().equals("..")) subdirs.add(f);\r
+                               }\r
+                       }\r
+                       dirs = subdirs;\r
+               }\r
+               \r
+               Directory[] result = new Directory[dirs.size()];\r
+               for (int i=0; i<dirs.size(); ++i) {\r
+                       result[i] = new Directory(dirs.get(i));\r
+               }\r
+               return result;\r
+       }\r
+       \r
+       /**\r
+        * @return a directory information corresponding to the object.\r
+        */\r
+       public File getDirectory() {\r
+               return dir;\r
+       }\r
+       \r
+       public void enableZipSearch() {\r
+               this.searchZip = true;\r
+       }\r
+       \r
+       public void enableRecursiveZipSearch() {\r
+               this.searchZip = true;\r
+               this.searchZipRecursive = true;\r
+       }\r
+       \r
+       public void disableAutoOpen() {\r
+               this.autoOpen = false;\r
+       }\r
+       \r
+       @Override\r
+       public void process(IClassListCallback c) {\r
+               FileFilterCallback filter = new FileFilterCallback(c); \r
+               Stack<File> worklist = new Stack<File>();\r
+               worklist.push(dir);\r
+               \r
+               while (!worklist.empty()) {\r
+                       File f = worklist.pop();\r
+                       if (f.isDirectory()) { \r
+                               if (f.exists() && f.canRead()) {\r
+                                       File[] contents = f.listFiles(filter);\r
+                                       if (contents != null) {\r
+                                               for (File content: contents) {\r
+                                                       if (!dir.equals(content)) { \r
+                                                               worklist.add(content);\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                       } else if (f.isFile() && c.isTarget(f.getAbsolutePath()) &&  f.canRead()) {\r
+                               try {\r
+                                       if (autoOpen) {\r
+                                               FileInputStream binaryStream = new FileInputStream(f);\r
+                                               c.process(f.getCanonicalPath(), binaryStream);\r
+                                               binaryStream.close();\r
+                                       } else {\r
+                                               c.process(f.getCanonicalPath(), null);\r
+                                       }\r
+                               } catch (IOException e) {\r
+                                       boolean stop = c.reportError(f.getAbsolutePath(), e);\r
+                                       if (stop) break;\r
+                               }\r
+                       } else if (ZipFile.isZipFile(f) && f.canRead()) {\r
+                               ZipFile zip = new ZipFile(f);\r
+                               if (searchZipRecursive) zip.enableRecursiveSearch();\r
+                               zip.process(c);\r
+                       }\r
+               }\r
+       }\r
+       \r
+       private class FileFilterCallback implements FileFilter { \r
+               \r
+               private IClassListCallback callback;\r
+               public FileFilterCallback(IClassListCallback c) {\r
+                       this.callback = c;\r
+               }\r
+               @Override\r
+               public boolean accept(File f) {\r
+               return f.isDirectory() ||\r
+                       (searchZip && ZipFile.isZipFile(f)) || \r
+                       (f.isFile() && callback.isTarget(f.getAbsolutePath()));\r
+               }\r
+       }\r
+}\r
diff --git a/src/soba/util/files/IClassList.java b/src/soba/util/files/IClassList.java
new file mode 100755 (executable)
index 0000000..fc31e33
--- /dev/null
@@ -0,0 +1,14 @@
+package soba.util.files;\r
+\r
+public interface IClassList {\r
+\r
+       /**\r
+        * Start a file enumeration process.\r
+        * @param c receives call back method calls from the enumerator.\r
+        */\r
+       public void process(IClassListCallback c);\r
+       \r
+       \r
+       public String getLabel();\r
+       \r
+}\r
diff --git a/src/soba/util/files/IClassListCallback.java b/src/soba/util/files/IClassListCallback.java
new file mode 100755 (executable)
index 0000000..d977fa8
--- /dev/null
@@ -0,0 +1,28 @@
+package soba.util.files;\r
+\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+\r
+public interface IClassListCallback {\r
+\r
+       \r
+       /**\r
+        * @param name is a string representing the data. \r
+        * @return The implementation returns true if the implementation \r
+        * want to process the data.\r
+        * The implementation may return false to skip the data. \r
+        */\r
+       public boolean isTarget(String name);\r
+       \r
+       public void process(String name, InputStream stream) throws IOException;\r
+       \r
+       /**\r
+        * @param name represents the data.\r
+        * @param e is an exception occurred during the process.\r
+        * @return The implementation returns true if the implementation\r
+        * want to stop the process as soon as possible.\r
+        */\r
+       public boolean reportError(String name, Exception e);\r
+       \r
+\r
+}\r
diff --git a/src/soba/util/files/SingleFile.java b/src/soba/util/files/SingleFile.java
new file mode 100755 (executable)
index 0000000..42f33ca
--- /dev/null
@@ -0,0 +1,40 @@
+package soba.util.files;\r
+\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.IOException;\r
+\r
+public class SingleFile implements IClassList {\r
+       \r
+       private File file;\r
+       private String label;\r
+\r
+       public SingleFile(File f) {\r
+               assert f.isFile(): f.getAbsolutePath() + " is not a file.";\r
+               this.file = f;\r
+       }\r
+\r
+       public void setLabel(String l) {\r
+               this.label = l;\r
+       }\r
+       \r
+       @Override\r
+       public String getLabel() {\r
+               return label;\r
+       }\r
+       \r
+       \r
+       @Override\r
+       public void process(IClassListCallback c) {\r
+               String filename = file.getAbsolutePath();\r
+               if (c.isTarget(filename)) {\r
+                       try {\r
+                               FileInputStream binaryStream = new FileInputStream(file);\r
+                               c.process(filename, binaryStream);\r
+                               binaryStream.close();\r
+                       } catch (IOException e) {\r
+                               c.reportError(filename, e);\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/src/soba/util/files/ZipFile.java b/src/soba/util/files/ZipFile.java
new file mode 100755 (executable)
index 0000000..abde5e8
--- /dev/null
@@ -0,0 +1,105 @@
+package soba.util.files;\r
+\r
+import java.io.File;\r
+import java.io.FileFilter;\r
+import java.io.FileInputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.util.zip.ZipEntry;\r
+import java.util.zip.ZipInputStream;\r
+\r
+/**\r
+ * A class to select files to be processed.\r
+ *\r
+ */\r
+public class ZipFile implements IClassList {\r
+\r
+       private static final String[] zipExt = new String[] { ".jar", ".zip", ".war" };\r
+\r
+       public static boolean isZipFile(String filename) { \r
+               String lowerFileName = filename.toLowerCase();\r
+               for (String ext: zipExt) {\r
+                       if (lowerFileName.endsWith(ext)) return true;\r
+               }\r
+               return false;\r
+       }\r
+\r
+       public static boolean isZipFile(File f) {\r
+               return f.isFile() && isZipFile(f.getAbsolutePath());\r
+       }\r
+\r
+\r
+       public static boolean isClassFile(String filename) {\r
+               String lowerFileName = filename.toLowerCase(); \r
+               return lowerFileName.endsWith(".class");\r
+       }\r
+\r
+       public static boolean isClassFile(File f) {\r
+               return f.isFile() && isClassFile(f.getAbsolutePath());\r
+       }\r
+\r
+       public static class ClassFileFilter implements FileFilter {\r
+\r
+               @Override\r
+               public boolean accept(File f) {\r
+                       return f.isDirectory() || isClassFile(f) || isZipFile(f);\r
+               }\r
+       }\r
+       \r
+       private File zip;\r
+       private String label;\r
+       private boolean searchRecursive; \r
+       \r
+       public ZipFile(File zipFile) {\r
+               assert isZipFile(zipFile);\r
+               this.zip = zipFile;\r
+       }\r
+       \r
+       public void setLabel(String l) {\r
+               this.label = l;\r
+       }\r
+       \r
+       @Override\r
+       public String getLabel() {\r
+               return label;\r
+       }\r
+       \r
+       public void enableRecursiveSearch() {\r
+               searchRecursive = true;\r
+       }\r
+\r
+       @Override\r
+       public void process(IClassListCallback c) {\r
+               try {\r
+                       processZip(new FileInputStream(zip), zip.getAbsolutePath(), c, true);\r
+               } catch (IOException e) {\r
+                       c.reportError(zip.getAbsolutePath(), e);\r
+               }\r
+       }\r
+\r
+       private void processZip(InputStream stream, String zipFilename, IClassListCallback c, boolean closeStream) {\r
+               ZipInputStream zip = new ZipInputStream(stream);\r
+               String lastEntry = zipFilename;\r
+               try {\r
+                       ZipEntry entry = zip.getNextEntry();\r
+                       while (entry != null) {\r
+                               lastEntry = zipFilename + "/" + entry.getName();\r
+                               if (c.isTarget(entry.getName())) {\r
+                                       c.process(lastEntry, zip);\r
+                               } else if (searchRecursive && ZipFile.isZipFile(entry.getName())) {\r
+                                       processZip(zip, lastEntry, c, false);\r
+                               }\r
+                               zip.closeEntry();\r
+                               entry = zip.getNextEntry();\r
+                       }\r
+                       if (closeStream) {\r
+                               zip.close();\r
+                       }\r
+               } catch (IOException e) {\r
+                       c.reportError(lastEntry, e);\r
+               } catch (RuntimeException e) {\r
+                       c.reportError(lastEntry, e);\r
+               }\r
+       }\r
+\r
+}\r
diff --git a/src/soba/util/graph/DepthFirstSearch.java b/src/soba/util/graph/DepthFirstSearch.java
new file mode 100755 (executable)
index 0000000..0860a21
--- /dev/null
@@ -0,0 +1,98 @@
+package soba.util.graph;\r
+\r
+import java.util.Stack;\r
+\r
+/**\r
+ * An implementation of Depth-First Search \r
+ * for IDirectedGraph object.\r
+ */\r
+public class DepthFirstSearch {\r
+\r
+       /**\r
+        * Executes DFS from the specified vertex.\r
+        * @param startVertexId\r
+        * @param visit will receive a call back from this method.\r
+        */\r
+       public static void search(final IDirectedGraph graph, int startVertexId, IDepthFirstVisitor visit) {\r
+               Stack<DfsProgress> stack = new Stack<DfsProgress>();\r
+               boolean[] visited = new boolean[graph.getVertexCount()];\r
+               visit.onStart(startVertexId);\r
+\r
+               DfsProgress progress = new DfsProgress(graph, startVertexId);\r
+               stack.push(progress);\r
+               \r
+               while (!stack.isEmpty()) {\r
+                       // The top of the stack represents the current location and state.\r
+                       DfsProgress node = stack.peek();\r
+                       \r
+                       boolean isFirstVisit = !visited[node.getVertex()];\r
+                       visited[node.getVertex()] = true;\r
+                       \r
+                       if (isFirstVisit) { \r
+                               boolean continueVisit = visit.onVisit(node.getVertex());\r
+                               if (!continueVisit) {\r
+                                       stack.pop(); // go back to the previous vertex\r
+                                       visit.onLeave(node.getVertex());\r
+                                       continue; \r
+                               }\r
+                       }\r
+                       \r
+                       // Find a next node to visit\r
+                       int nextNode = -1;\r
+                       while (node.hasNext()) {\r
+                               int nextNodeCandidate = node.next();\r
+                               if (!visited[nextNodeCandidate]) {\r
+                                       nextNode = nextNodeCandidate;\r
+                                       break;\r
+                               } else {\r
+                                       visit.onVisitAgain(nextNodeCandidate);\r
+                               }\r
+                       } \r
+                       if (nextNode != -1) { \r
+                               // If found, set the next visit \r
+                               stack.push(new DfsProgress(graph, nextNode));\r
+                       } else {\r
+                               // If not found, go back to the previous node from the current node. \r
+                               stack.pop();\r
+                               visit.onLeave(node.getVertex());\r
+                       }\r
+               }\r
+               \r
+               visit.onFinished(visited);\r
+       }\r
+       \r
+       /**\r
+        * This object manages visited edges from a vertex. \r
+        */\r
+       private static class DfsProgress {\r
+\r
+               private IDirectedGraph graph;\r
+               private int vertex;\r
+               private int edgeIndex;\r
+               \r
+               public DfsProgress(IDirectedGraph graph, int vertexId) {\r
+                       this.graph = graph;\r
+                       this.vertex = vertexId;\r
+                       this.edgeIndex = 0;\r
+               }\r
+               public int getVertex() {\r
+                       return vertex;\r
+               }\r
+               public boolean hasNext() {\r
+                       int[] edgeList = graph.getEdges(vertex);\r
+                       return (edgeList != null) && (edgeIndex < edgeList.length);\r
+               }\r
+               public int next() {\r
+                       if (graph.getEdges(vertex) != null) {\r
+                               int next = graph.getEdges(vertex)[edgeIndex];\r
+                               edgeIndex++;\r
+                               return next;\r
+                       } else {\r
+                               return -1;\r
+                       }\r
+               }\r
+               \r
+       }\r
+       \r
+\r
+}\r
diff --git a/src/soba/util/graph/DirectedAcyclicGraph.java b/src/soba/util/graph/DirectedAcyclicGraph.java
new file mode 100755 (executable)
index 0000000..967fda6
--- /dev/null
@@ -0,0 +1,195 @@
+package soba.util.graph;\r
+\r
+import java.util.Arrays;\r
+\r
+import soba.util.IntPairProc;\r
+import soba.util.IntPairSet;\r
+import soba.util.IntPairUtil;\r
+import soba.util.IntSetStack;\r
+import soba.util.IntStack;\r
+\r
+/**\r
+ * This class represents a directed acyclic graph.\r
+ */\r
+public class DirectedAcyclicGraph implements IDirectedGraph {\r
+       \r
+       private IDirectedGraph base;\r
+       private int[] sccIds;\r
+       private DirectedGraph dag;\r
+\r
+       /**\r
+        * Creates a new <code>DirectedAcyclicGraph</code> instance from a specified base graph.\r
+        * If the base graph has strongly connected components, \r
+        * they are removed by Tarjan's algorithm.\r
+        * @param base is a directed graph.\r
+        */\r
+       public DirectedAcyclicGraph(IDirectedGraph base) {\r
+               this.base = base;\r
+               this.sccIds = new int[base.getVertexCount()];\r
+               removeStronglyConnectedComponents();\r
+       }\r
+       \r
+       /**\r
+        * A copy constructor for getReverseGraph()\r
+        */\r
+       private DirectedAcyclicGraph(DirectedAcyclicGraph g) {\r
+               this.base = g.base;\r
+               this.sccIds = g.sccIds;\r
+               this.dag = g.dag;\r
+       }\r
+       \r
+    /** {@inheritDoc} */\r
+       @Override\r
+       public int[] getEdges(int memberId) {\r
+               return dag.getEdges(memberId);\r
+       }\r
+       \r
+       /**\r
+        * @return the number of vertices.\r
+        * The value is the same as the base graph.\r
+        */\r
+       @Override\r
+       public int getVertexCount() {\r
+               return dag.getVertexCount();\r
+       }\r
+       \r
+    /** {@inheritDoc} */\r
+       @Override\r
+       public void forEachEdge(IntPairProc proc) {\r
+               dag.forEachEdge(proc);\r
+       }\r
+       \r
+       /**\r
+        * @param vertexId specifies a vertex.\r
+        * @return true if the ID specifies a vertex \r
+        * included in the DAG.\r
+        * If the vertex is a part of strongly connected components\r
+        * and excluded from the DAG, the method returns false. \r
+        */\r
+       public boolean isRepresentativeNode(int vertexId) {\r
+               return sccIds[vertexId] == vertexId;\r
+       }\r
+       \r
+       /**\r
+        * @param vertexId specifies a vertex.\r
+        * @return a vertex ID which is a representative node \r
+        * of the strongly connected components including the specified vertexId.\r
+        */\r
+       public int getRepresentativeNode(int vertexId) {\r
+               return sccIds[vertexId];\r
+       }\r
+       \r
+       /**\r
+        * @return a new graph with reversed edges.\r
+        * An edge from vertex A to vertex B in the original graph is \r
+        * translated into an edge from B to A in the new graph.\r
+        */\r
+       public DirectedAcyclicGraph getReverseGraph() {\r
+               DirectedAcyclicGraph reverse = new DirectedAcyclicGraph(this);\r
+               reverse.dag = this.dag.getReverseGraph();\r
+               return reverse;\r
+       }\r
+       \r
+       @Override\r
+       public boolean equals(Object obj) {\r
+               if (obj instanceof DirectedAcyclicGraph) {\r
+                       DirectedAcyclicGraph another = (DirectedAcyclicGraph)obj;\r
+                       if (this.getVertexCount() == another.getVertexCount()) {\r
+                               for (int i=0; i<getVertexCount(); ++i) {\r
+                                       if (this.sccIds[i] != another.sccIds[i]) return false;\r
+                                       if (isRepresentativeNode(i)) {\r
+                                               int[] to1 = this.getEdges(i);\r
+                                               int[] to2 = another.getEdges(i);\r
+                                               if (to1.length != to2.length) return false;\r
+                                               for (int j=0; j<to1.length; ++j) {\r
+                                                       if (to1[j] != to2[j]) return false;\r
+                                               }\r
+                                       }\r
+                               }\r
+                               \r
+                               return true;\r
+                       } else {\r
+                               return false;\r
+                       }\r
+                       \r
+               } else {\r
+                       return false;\r
+               }\r
+       }\r
+\r
+       \r
+       private TarjanData data;\r
+       /**\r
+        * Apply Tarjan's algorithm to detect SCCs.\r
+        */\r
+       private void removeStronglyConnectedComponents() { \r
+               data = new TarjanData();\r
+               for (int i=0; i<base.getVertexCount(); ++i) {\r
+                       if (data.visitIndex[i] == -1) {\r
+                               tarjanDFS(i);\r
+                       }\r
+               }\r
+               \r
+               final IntPairSet dagEdges = new IntPairSet();\r
+               base.forEachEdge(new IntPairProc() {\r
+                       \r
+                       @Override\r
+                       public boolean execute(int elem1, int elem2) {\r
+                               int from = sccIds[elem1];\r
+                               int to = sccIds[elem2];\r
+                               if (from != to) dagEdges.add(from, to);\r
+                               return true;\r
+                       }\r
+               });\r
+               dag = new DirectedGraph(base.getVertexCount(), IntPairUtil.createList(dagEdges));\r
+               data = null;\r
+       }\r
+       \r
+       private void tarjanDFS(int vId) {\r
+               data.visitIndex[vId] = data.currentIndex;\r
+               data.lowlink[vId] = data.currentIndex;\r
+               data.currentIndex++;\r
+               data.stack.push(vId);\r
+               //System.out.println("currentIndex = " + data.currentIndex + ", stack = " + data.stack.size());\r
+               \r
+               // For each edge from vId\r
+               for (int to: base.getEdges(vId)) {\r
+                       // If the next vertex is not visited yet, visit it.\r
+                       if (data.visitIndex[to] == -1) {\r
+                               tarjanDFS(to);\r
+                               data.lowlink[vId] = Math.min(data.lowlink[vId], data.lowlink[to]); \r
+                       } else if (data.stack.contains(to)) {\r
+                               data.lowlink[vId] = Math.min(data.lowlink[vId], data.visitIndex[to]); \r
+                       }\r
+               }\r
+\r
+               // If the minimum ID of reachable vertices is equals to the vertex ID,\r
+               // then the vertex is a "root" of SCC.\r
+               // (If vID is not a "root" and included in a SCC, \r
+               //  the vID is kept on the stack -- the root of SCC will pop the vID.)\r
+               if (data.lowlink[vId] == data.visitIndex[vId]) {\r
+                       int pop;\r
+                       do {\r
+                               pop = data.stack.pop();\r
+                               sccIds[pop] = vId;\r
+                       } while (pop != vId); // until pop == vId\r
+               }\r
+       }\r
+       \r
+       private class TarjanData {\r
+               public int currentIndex;\r
+               public int[] visitIndex; // ID for each vertex\r
+               public int[] lowlink;    // the minimum ID of vertices reachable from a vertex\r
+               public IntStack stack;\r
+               \r
+               public TarjanData() {\r
+                       currentIndex = 0;\r
+                       visitIndex = new int[base.getVertexCount()];\r
+                       lowlink = new int[base.getVertexCount()];\r
+                       stack = new IntSetStack(base.getVertexCount());\r
+                       Arrays.fill(visitIndex, -1);\r
+               }\r
+               \r
+       }\r
+\r
+}\r
diff --git a/src/soba/util/graph/DirectedGraph.java b/src/soba/util/graph/DirectedGraph.java
new file mode 100755 (executable)
index 0000000..19b38ac
--- /dev/null
@@ -0,0 +1,112 @@
+package soba.util.graph;\r
+\r
+import java.util.Arrays;\r
+\r
+import soba.util.IntPairList;\r
+import soba.util.IntPairProc;\r
+\r
+import gnu.trove.set.hash.TIntHashSet;\r
+\r
+/**\r
+ * An instance of this class is a directed graph.\r
+ * Each vertex is represented as an integer\r
+ * between 0 and vertexCount-1. \r
+ */\r
+public class DirectedGraph implements IDirectedGraph {\r
+\r
+       private int vertexCount;\r
+       private int edgeCount;\r
+       private IntPairList edges;\r
+       private int[][] forward;\r
+       \r
+       private static final int[] EMPTY_ARRAY = new int[0];\r
+                   \r
+       /**\r
+        * Creates a new <code>DirectedGraph</code> instance.\r
+        * Duplicated edges are ignored.\r
+        * @param vertexCount is the number of vertices.\r
+        * @param edges will be modified by DirectedGraph.\r
+        */\r
+       public DirectedGraph(int vertexCount, IntPairList edges) {\r
+               this.vertexCount = vertexCount;\r
+               this.edges = edges;\r
+               this.edgeCount = edges.size();\r
+               this.edges.sort();\r
+               this.forward = constructEdgeArray(edges);\r
+       }\r
+       \r
+       /**\r
+        * @param edges are pairs of vertex IDs. \r
+        * @return array representing edges.\r
+        * Duplicated edges are excluded in the resultant array.\r
+        */\r
+       private int[][] constructEdgeArray(IntPairList edges) {\r
+               // forwardTemp[V] means a list of edges from vertex V.\r
+               TIntHashSet[] forwardTemp = new TIntHashSet[vertexCount];\r
+               for (int i=0; i<edges.size(); ++i) {\r
+                       int from = edges.getFirstValue(i);\r
+                       int to = edges.getSecondValue(i);\r
+\r
+                       if (forwardTemp[from] == null) {\r
+                               forwardTemp[from] = new TIntHashSet(2);\r
+                       }\r
+                       forwardTemp[from].add(to);\r
+               }\r
+               // Translate a set object to an array.\r
+               int[][] forward = new int[vertexCount][];\r
+               for (int i=0; i<vertexCount; ++i) {\r
+                       if (forwardTemp[i] != null) {\r
+                               int[] array = forwardTemp[i].toArray();\r
+                               Arrays.sort(array);\r
+                               forward[i] = array;\r
+                       } else {\r
+                               forward[i] = EMPTY_ARRAY;\r
+                       }\r
+               }\r
+               return forward;\r
+       }\r
+\r
+       /**\r
+        * @return the number of edges.\r
+        */\r
+       public int getEdgeCount() {\r
+               return edgeCount;\r
+       }\r
+\r
+    /** {@inheritDoc} */\r
+       @Override\r
+       public int getVertexCount() {\r
+               return vertexCount;\r
+       }\r
+       \r
+    /** {@inheritDoc} */\r
+       @Override\r
+       public void forEachEdge(IntPairProc proc) {\r
+               edges.foreach(proc);\r
+       }\r
+       \r
+    /** {@inheritDoc} */\r
+       @Override\r
+       public int[] getEdges(int memberId) {\r
+               return this.forward[memberId];\r
+       }\r
+\r
+       /**\r
+        * @return a new graph with reversed edges.\r
+        * An edge from vertex A to vertex B in the original graph is \r
+        * translated into an edge from B to A in the new graph.\r
+        */\r
+       public DirectedGraph getReverseGraph() {\r
+               return GraphUtil.getReverseGraph(this);\r
+       }\r
+       \r
+       /**\r
+        * @return a new graph whose vertices are connected by undirected edges.\r
+        * In other words, a directed edge from vertex A to vertex B is \r
+        * translated into two edges A to B and B to A. \r
+        */\r
+       public DirectedGraph getUndirectedGraph() {\r
+               return GraphUtil.getUndirectedGraph(this);\r
+       }\r
+\r
+}\r
diff --git a/src/soba/util/graph/DominanceTree.java b/src/soba/util/graph/DominanceTree.java
new file mode 100755 (executable)
index 0000000..474e3af
--- /dev/null
@@ -0,0 +1,129 @@
+package soba.util.graph;\r
+\r
+/**\r
+ * This class implements an algorithm described in \r
+ * Keith D. Cooper, Timothy J. Harvey and Ken Kennedy: A Simple, Fast Dominance Algorithm.\r
+ */\r
+public class DominanceTree {\r
+\r
+       private SingleRootDirectedGraph base;\r
+       private IDirectedGraph reverse;\r
+       private int[] reversePostOrder;\r
+       private int[] immediateDominator;\r
+\r
+       /**\r
+        * Creates a new <code>DominanceTree</code> instance from a <code>SingleRootDirectedGraph</code> object.\r
+        * @param graph is a <code>SingleRootDirectedGraph</code> object.\r
+        */\r
+       public DominanceTree(SingleRootDirectedGraph graph) {\r
+               this.base = graph;\r
+               this.reverse = graph.getReverseGraph();\r
+               this.reversePostOrder = new int[base.getVertexCount()];\r
+               this.immediateDominator = new int[base.getVertexCount()];\r
+               computeSpanningTree();\r
+               computeDominators();\r
+       }\r
+       \r
+       /**\r
+        * @param vertexID specifies a vertex.\r
+        * @return true if the specified vertex is root of the tree.\r
+        */\r
+       public boolean isRoot(int vertexID) {\r
+               return vertexID == base.getRootId();\r
+       }\r
+       \r
+       /**\r
+        * @param vertexID specifies a vertex.\r
+        * @return the dominator vertex ID of the specified vertex.\r
+        */\r
+       public int getDominator(int vertexID) {\r
+               return immediateDominator[vertexID];\r
+       }\r
+\r
+       /**\r
+        * Visit nodes using depth-first search.\r
+        * Reverse-post-order traversal.\r
+        */\r
+       private void computeSpanningTree() {\r
+               \r
+               DepthFirstSearch.search(base, base.getRootId(), new IDepthFirstVisitor() {\r
+                       \r
+                       private int reversePostOrderIndex = base.getVertexCount() - 1;\r
+\r
+                       @Override\r
+                       public void onStart(int startVertexId) {\r
+                       }\r
+                       \r
+                       @Override\r
+                       public boolean onVisit(int vertexId) {\r
+                               return true;\r
+                       }\r
+                       \r
+                       @Override\r
+                       public void onLeave(int vertexId) {\r
+                               reversePostOrder[vertexId] = reversePostOrderIndex;\r
+                               reversePostOrderIndex--;\r
+                       }\r
+                       \r
+                       @Override\r
+                       public void onFinished(boolean[] visited) {\r
+                       }\r
+                       \r
+                       @Override\r
+                       public void onVisitAgain(int vertexId) {\r
+                       }\r
+               });\r
+       }\r
+       \r
+       private void computeDominators() {\r
+               \r
+               // sort vertices according to the descending order of reverse post order (a regular post order)\r
+               int[] sortedVertices = new int[base.getVertexCount()];\r
+               for (int i=0; i<base.getVertexCount(); ++i) {\r
+                       sortedVertices[reversePostOrder[i]] = i;\r
+               }\r
+               \r
+               // initialize idom\r
+               for (int from=0; from<base.getVertexCount(); ++from) {\r
+                       for (int to: base.getEdges(from)) {\r
+                               if (reversePostOrder[from] < reversePostOrder[to]) immediateDominator[to] = from;\r
+                       }\r
+               }\r
+               \r
+               // iteratively compute dominators\r
+               boolean changed = true;\r
+               while (changed) {\r
+                       changed = false;\r
+                       for (int v: sortedVertices) {\r
+                               // dom(v) == Nearest Common Ancestor of v's predecessors\r
+                               int[] pred = reverse.getEdges(v);\r
+                               for (int idx=0; idx<pred.length; ++idx) {\r
+                                       int idom = immediateDominator[v];\r
+                                       int nca = nearestCommonAncestor(idom, pred[idx]);\r
+                                       if (idom != nca) {\r
+                                               changed = true;\r
+                                               immediateDominator[v] = nca;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Returns the nearest common ancestor of two nodes.\r
+        */\r
+       public final int nearestCommonAncestor(int v1, int v2) {\r
+               while (v1 != v2) {\r
+                       int i1 = reversePostOrder[v1];\r
+                       int i2 = reversePostOrder[v2];\r
+                       if (i1 > i2) { \r
+                               v1 = immediateDominator[v1];\r
+                       } else {\r
+                               assert (i1 < i2): "v1!=v2 implies i1!=i2";\r
+                               v2 = immediateDominator[v2];\r
+                       }\r
+               }\r
+               return v1;\r
+       }\r
+\r
+}\r
diff --git a/src/soba/util/graph/GraphUtil.java b/src/soba/util/graph/GraphUtil.java
new file mode 100755 (executable)
index 0000000..3a1e297
--- /dev/null
@@ -0,0 +1,43 @@
+package soba.util.graph;\r
+\r
+import soba.util.IntPairList;\r
+import soba.util.IntPairProc;\r
+\r
+public class GraphUtil {\r
+\r
+       /**\r
+        * @return a new graph with reversed edges.\r
+        * An edge from vertex A to vertex B in the original graph is \r
+        * translated into an edge from B to A in the new graph.\r
+        */\r
+       public static DirectedGraph getReverseGraph(IDirectedGraph g) {\r
+               final IntPairList reverseEdges = new IntPairList();\r
+               g.forEachEdge(new IntPairProc() {\r
+                       @Override\r
+                       public boolean execute(int elem1, int elem2) {\r
+                               reverseEdges.add(elem2, elem1);\r
+                               return true;\r
+                       }\r
+               });\r
+               return new DirectedGraph(g.getVertexCount(), reverseEdges);\r
+       }\r
+       \r
+       /**\r
+        * @return a new graph whose vertices are connected by undirected edges.\r
+        * In other words, a directed edge from vertex A to vertex B is \r
+        * translated into two edges A to B and B to A. \r
+        */\r
+       public static DirectedGraph getUndirectedGraph(IDirectedGraph g) {\r
+               final IntPairList undirectedEdges = new IntPairList();\r
+               g.forEachEdge(new IntPairProc() {\r
+                       @Override\r
+                       public boolean execute(int elem1, int elem2) {\r
+                               undirectedEdges.add(elem1, elem2);\r
+                               undirectedEdges.add(elem2, elem1);\r
+                               return true;\r
+                       }\r
+               });\r
+               return new DirectedGraph(g.getVertexCount(), undirectedEdges);\r
+       }\r
+\r
+}\r
diff --git a/src/soba/util/graph/IDepthFirstVisitor.java b/src/soba/util/graph/IDepthFirstVisitor.java
new file mode 100755 (executable)
index 0000000..6e5d035
--- /dev/null
@@ -0,0 +1,35 @@
+package soba.util.graph;\r
+\r
+public interface IDepthFirstVisitor {\r
+\r
+       /**\r
+        * This method is called in order to notify the beginning of a visiting process.\r
+        * @param startVertexId\r
+        */\r
+       public void onStart(int startVertexId);\r
+       \r
+       /**\r
+        * @param vertexId identifies a visited vertex.\r
+        * @return true if you want to continue visiting process beyond the vertex.\r
+        */\r
+       public boolean onVisit(int vertexId);\r
+       \r
+       /**\r
+        * This method is called when a vertex is visited again.\r
+        * You may use this method to detect two or more paths for the vertex.\r
+        * @param vertexId\r
+        */\r
+       public void onVisitAgain(int vertexId);\r
+       \r
+       /**\r
+        * @param vertexId\r
+        */\r
+       public void onLeave(int vertexId);\r
+       \r
+       /**\r
+        * This method is called when the visit process is finished.\r
+        * @param visited\r
+        */\r
+       public void onFinished(boolean[] visited);\r
+\r
+}\r
diff --git a/src/soba/util/graph/IDirectedGraph.java b/src/soba/util/graph/IDirectedGraph.java
new file mode 100755 (executable)
index 0000000..9debe91
--- /dev/null
@@ -0,0 +1,23 @@
+package soba.util.graph;\r
+\r
+import soba.util.IntPairProc;\r
+\r
+public interface IDirectedGraph {\r
+\r
+       /**\r
+        * @return the number of vertices.\r
+        */\r
+       public int getVertexCount();\r
+       \r
+       /**\r
+        * @param memberId specifies a vertex.\r
+        * @return an array of vertex IDs connected from the specified vertex.\r
+        */\r
+       public int[] getEdges(int memberId);\r
+       \r
+       /**\r
+        * Executes a procedure for each edge.\r
+        */\r
+       public void forEachEdge(IntPairProc proc);\r
+\r
+}\r
diff --git a/src/soba/util/graph/SingleRootDirectedGraph.java b/src/soba/util/graph/SingleRootDirectedGraph.java
new file mode 100755 (executable)
index 0000000..4046c4a
--- /dev/null
@@ -0,0 +1,107 @@
+package soba.util.graph;\r
+\r
+import soba.util.IntPairList;\r
+import soba.util.IntPairProc;\r
+\r
+/**\r
+ * A directed graph that has a single root node.\r
+ */\r
+public class SingleRootDirectedGraph implements IDirectedGraph {\r
+\r
+       private IDirectedGraph base;\r
+       private int[] edgesFromRoot;\r
+       \r
+       /**\r
+        * Creates a new <code>SingleRootDirectedGraph</code> instance from a base graph.\r
+        * The instance is a directed graph with a single root.\r
+        * The root is connected to vertices which have no incoming edges in the original graph.\r
+        * @param base is a directed graph.\r
+        */\r
+       public SingleRootDirectedGraph(IDirectedGraph base) {\r
+               this.base = base;\r
+               \r
+               // To find vertices without incoming edges, first cycles must be removed from the graph.\r
+               DirectedAcyclicGraph dag = new DirectedAcyclicGraph(base);\r
+               final boolean[] hasIncomingEdge = new boolean[base.getVertexCount()];\r
+               dag.forEachEdge(new IntPairProc() {\r
+                       @Override\r
+                       public boolean execute(int from, int to) {\r
+                               hasIncomingEdge[to] = true;\r
+                               return true;\r
+                       }\r
+               });\r
+               \r
+               // Find vertices which have incoming edges\r
+               int count = 0;\r
+               for (int i=0; i<hasIncomingEdge.length; ++i) {\r
+                       if (!hasIncomingEdge[i] && dag.isRepresentativeNode(i)) count++;\r
+               }\r
+               \r
+               // Connect the root vertex to the vertices that have no incoming edges\r
+               edgesFromRoot =  new int[count];\r
+               int edgeIndex = 0;\r
+               for (int i=0; i<base.getVertexCount(); ++i) {\r
+                       if (!hasIncomingEdge[i] && dag.isRepresentativeNode(i)) {\r
+                               edgesFromRoot[edgeIndex] = i;\r
+                               edgeIndex++;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * @return the root vertex ID.\r
+        */\r
+       public int getRootId() {\r
+               return base.getVertexCount();\r
+       }\r
+       \r
+       @Override\r
+       public int getVertexCount() {\r
+               return base.getVertexCount() + 1;\r
+       }\r
+       \r
+       @Override\r
+       public int[] getEdges(int memberId) {\r
+               if (memberId < base.getVertexCount()) {\r
+                       return base.getEdges(memberId);\r
+               } else {\r
+                       return edgesFromRoot;\r
+               }\r
+       }\r
+       \r
+       @Override\r
+       public void forEachEdge(IntPairProc proc) {\r
+               // base vertices\r
+               for (int from=0; from<base.getVertexCount(); ++from) {\r
+                       for (int to: base.getEdges(from)) {\r
+                               if (!proc.execute(from, to)) return;\r
+                       }\r
+               }\r
+               // the root vertex\r
+               for (int to: edgesFromRoot) {\r
+                       if (!proc.execute(getRootId(), to)) return;\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * @return a new graph with reversed edges.\r
+        * An edge from vertex A to vertex B in the original graph is \r
+        * translated into an edge from B to A in the new graph.\r
+        */\r
+       public DirectedGraph getReverseGraph() {\r
+               final IntPairList reverseEdges = new IntPairList();\r
+               base.forEachEdge(new IntPairProc() {\r
+                       @Override\r
+                       public boolean execute(int elem1, int elem2) {\r
+                               reverseEdges.add(elem2, elem1);\r
+                               return true;\r
+                       }\r
+               });\r
+               for (int id: getEdges(getRootId())) {\r
+                       reverseEdges.add(id, getRootId());\r
+               }\r
+               return new DirectedGraph(getVertexCount(), reverseEdges);\r
+       }\r
+\r
+       \r
+}\r
diff --git a/tests/soba/core/ClassHierarchyTest.java b/tests/soba/core/ClassHierarchyTest.java
new file mode 100755 (executable)
index 0000000..933a052
--- /dev/null
@@ -0,0 +1,241 @@
+package soba.core;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+\r
+import java.io.FileInputStream;\r
+import java.io.IOException;\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.Collection;\r
+import java.util.HashSet;\r
+import java.util.Set;\r
+import java.util.stream.Collectors;\r
+\r
+import org.junit.Before;\r
+import org.junit.BeforeClass;\r
+import org.junit.Test;\r
+\r
+import soba.core.ClassHierarchy;\r
+import soba.core.ClassInfo;\r
+import soba.core.ClassHierarchy.FrozenHierarchyException;\r
+import soba.core.method.FieldAccess;\r
+\r
+public class ClassHierarchyTest implements ExampleProgram {\r
+\r
+       private ClassHierarchy ch;\r
+\r
+       // Class hierarchy\r
+       //\r
+       // C\r
+       //  -> D\r
+       //      -> H\r
+       //  -> F\r
+       //  -> G\r
+       // E\r
+       \r
+       private static ClassInfo c;\r
+       private static ClassInfo d;\r
+       private static ClassInfo e;\r
+       private static ClassInfo f;\r
+       private static ClassInfo g;\r
+       private static ClassInfo h;\r
+       private static ClassInfo i;\r
+       private static ClassInfo j;\r
+       private static ClassInfo k;\r
+       \r
+       @BeforeClass\r
+       public static void setUpClassInfo() throws IOException {\r
+               c = new ClassInfo("C.class", new FileInputStream("bin/" + CLASS_C + ".class"));\r
+               d = new ClassInfo("D.class", new FileInputStream("bin/" + CLASS_D + ".class"));\r
+               e = new ClassInfo("E.class", new FileInputStream("bin/" + CLASS_E + ".class"));\r
+               f = new ClassInfo("F.class", new FileInputStream("bin/" + CLASS_F + ".class"));\r
+               g = new ClassInfo("G.class", new FileInputStream("bin/" + CLASS_G + ".class"));\r
+               h = new ClassInfo("H.class", new FileInputStream("bin/" + CLASS_H + ".class"));\r
+               i = new ClassInfo("I.class", new FileInputStream("bin/" + CLASS_I + ".class"));\r
+               j = new ClassInfo("I.class", new FileInputStream("bin/" + CLASS_J + ".class"));\r
+               k = new ClassInfo("I.class", new FileInputStream("bin/" + CLASS_K + ".class"));\r
+       }\r
+       \r
+       @Before\r
+       public void createHierarchy() {\r
+               ch = new ClassHierarchy();\r
+               ch.registerClass(c);\r
+               ch.registerClass(d);\r
+               ch.registerClass(e);\r
+               ch.registerClass(f);\r
+               ch.registerClass(g);\r
+               ch.registerClass(h);\r
+               ch.registerClass(i);\r
+               ch.registerClass(j);\r
+               ch.registerClass(k);\r
+       }\r
+       \r
+       @Test\r
+       public void testClasses() {\r
+               assertThat(ch.getClassCount(), is(9));\r
+               assertThat(ch.getClasses(), containsInAnyOrder(CLASS_C, CLASS_D, CLASS_E, CLASS_F, CLASS_G,\r
+                                                                                                          CLASS_H, CLASS_I, CLASS_J, CLASS_K));\r
+               assertThat(ch.getRequestedClasses(), is(empty()));\r
+       }\r
+       \r
+       @Test\r
+       public void testGetClassInfo() {\r
+               assertThat(ch.getClassInfo(CLASS_C), is(c));\r
+               assertThat(ch.getClassInfo(CLASS_D), is(d));\r
+               assertThat(ch.getClassInfo(CLASS_E), is(e));\r
+               assertThat(ch.getClassInfo("pkg/Unknown"), is(nullValue()));\r
+       }\r
+       \r
+       @Test\r
+       public void testGetSuperClass() {\r
+               assertThat(ch.getSuperClass(CLASS_D), is(CLASS_C));\r
+               assertThat(ch.getSuperClass(CLASS_C), is("java/lang/Object"));\r
+               assertThat(ch.getSuperClass(CLASS_E), is("java/lang/Object"));\r
+               assertThat(ch.getSuperClass("pkg/Unknown"), is(nullValue()));\r
+       }\r
+       \r
+       @Test\r
+       public void testIsSamePackage() {\r
+               assertThat(ch.isSamePackage(CLASS_C, CLASS_D), is(true));\r
+               assertThat(ch.isSamePackage(CLASS_C, CLASS_F), is(false));\r
+               assertThat(ch.isSamePackage(CLASS_C, "pkg/Unknown"), is(false));\r
+               assertThat(ch.isSamePackage("pkg/Unknown", CLASS_D), is(false));\r
+       }\r
+       \r
+       @Test\r
+       public void testGetSubtypes() {\r
+               Collection<String> subtypesOfC = ch.getSubtypes(CLASS_C);\r
+               assertThat(subtypesOfC, containsInAnyOrder(CLASS_D, CLASS_F, CLASS_G));\r
+               Collection<String> subtypesOfE = ch.getSubtypes(CLASS_E);\r
+               assertThat(subtypesOfE, is(empty()));\r
+               Collection<String> subtypesOfI = ch.getSubtypes(CLASS_I);\r
+               assertThat(subtypesOfI, containsInAnyOrder(CLASS_D, CLASS_K));\r
+       }\r
+       \r
+       @Test\r
+       public void testGetAllSubtypes() {\r
+               Set<String> typeNames = new HashSet<>();\r
+               typeNames.add(CLASS_C);\r
+               Collection<String> allSubtypesOfC = ch.getAllSubtypes(typeNames);\r
+               assertThat(allSubtypesOfC, containsInAnyOrder(CLASS_C, CLASS_D, CLASS_H, CLASS_F, CLASS_G));\r
+\r
+               typeNames.add(CLASS_I);\r
+               Collection<String> allSubtypesOfCandI = ch.getAllSubtypes(typeNames);\r
+               assertThat(allSubtypesOfCandI, containsInAnyOrder(CLASS_C, CLASS_D, CLASS_H, CLASS_F,\r
+                                                                                                                 CLASS_G, CLASS_I, CLASS_K));\r
+       }\r
+       \r
+       @Test\r
+       public void testListAllSuperTypes() {\r
+               Collection<String> supertypesOfH = ch.listAllSuperTypes(CLASS_H);\r
+               assertThat(supertypesOfH, containsInAnyOrder(CLASS_C, CLASS_D, CLASS_K, CLASS_I, "java/lang/Object"));\r
+\r
+               Collection<String> supertypesOfI = ch.listAllSuperTypes(CLASS_I);\r
+               assertThat(supertypesOfI, containsInAnyOrder("java/lang/Object"));\r
+       }\r
+       \r
+       @Test\r
+       public void testGetSuperInterfaces() {\r
+               Collection<String> superInterfaceOfH = ch.getSuperInterfaces(CLASS_D);\r
+               assertThat(superInterfaceOfH, containsInAnyOrder(CLASS_I, CLASS_K));\r
+               Collection<String> superInterfaceOfC = ch.getSuperInterfaces(CLASS_C);\r
+               assertThat(superInterfaceOfC, is(empty()));\r
+       }\r
+       \r
+       @Test\r
+       public void testResolveCall01() {\r
+               MethodInfo[] methodMain = ch.resolveCall(CLASS_E, "main", "([Ljava/lang/String;)V", false);\r
+               checkClasses(methodMain, CLASS_E);\r
+       }\r
+\r
+       @Test\r
+       public void testResolveCall02() {\r
+               // C.n() has 4 implementation: C, D, G, H.  F.n() is different.\r
+               MethodInfo[] methodsN = ch.resolveCall(CLASS_C, "n", "()V", true);\r
+               checkClasses(methodsN, CLASS_C, CLASS_D, CLASS_G, CLASS_H);\r
+               \r
+               MethodInfo[] methodsNf = ch.resolveCall(CLASS_F, "n", "()V", true);\r
+               checkClasses(methodsNf, CLASS_F);\r
+       }\r
+       \r
+       @Test\r
+       public void testResolveCall03() {\r
+               // C.m() and D.m() are defined.  H.m() is not implemented. \r
+               MethodInfo[] methodsM = ch.resolveCall(CLASS_D, "m", "()V", true);\r
+               checkClasses(methodsM, CLASS_D);\r
+\r
+               MethodInfo[] methodsM2 = ch.resolveCall(CLASS_H, "m", "()V", true);\r
+               checkClasses(methodsM2, CLASS_D);\r
+\r
+               MethodInfo[] methodsM3 = ch.resolveCall(CLASS_C, "m", "()V", true);\r
+               checkClasses(methodsM3, CLASS_C, CLASS_D);\r
+       }\r
+       \r
+       @Test\r
+       public void testResolveCall04() {\r
+               // p() is defined by C and H, not by D.\r
+               MethodInfo[] methodsPc = ch.resolveCall(CLASS_C, "p", "(I)V", true);\r
+               checkClasses(methodsPc, CLASS_C, CLASS_H);\r
+               MethodInfo[] methodsPd = ch.resolveCall(CLASS_C, "p", "(I)V", true);\r
+               checkClasses(methodsPd, CLASS_C, CLASS_H);\r
+               MethodInfo[] methodsPh = ch.resolveCall(CLASS_H, "p", "(I)V", true);\r
+               checkClasses(methodsPh, CLASS_H);\r
+       }\r
+       \r
+       @Test\r
+       public void testResolveCall05() {\r
+               // q() is defined by C and H but it is private.\r
+               MethodInfo[] methodsQc = ch.resolveCall(CLASS_C, "q", "(D)V", true);\r
+               checkClasses(methodsQc, CLASS_C);\r
+               MethodInfo[] methodsQh = ch.resolveCall(CLASS_H, "q", "(D)V", true);\r
+               checkClasses(methodsQh, CLASS_H);\r
+       }\r
+       \r
+       @Test\r
+       public void testResolveCall06() {\r
+               MethodInfo[] methodsMi = ch.resolveCall(CLASS_I, "m", "()V", true);\r
+               checkClasses(methodsMi, CLASS_D);\r
+       }\r
+       \r
+       @Test\r
+       public void testFields() {\r
+               assertThat(ch.resolveField(FieldAccess.createGetField(CLASS_D, "x", "I", false)).getClassName(), is(CLASS_C));\r
+               assertThat(ch.resolveField(FieldAccess.createGetField(CLASS_C, "x", "I", false)).getClassName(), is(CLASS_C));\r
+               assertThat(ch.resolveField(FieldAccess.createGetField(CLASS_H, "x", "I", false)).getClassName(), is(CLASS_H));\r
+               assertThat(ch.resolveField(FieldAccess.createGetField(CLASS_I, "x", "I", true)).getClassName(), is(CLASS_I));\r
+               assertThat(ch.resolveField(FieldAccess.createGetField(CLASS_J, "x", "I", true)).getClassName(), is(CLASS_J));\r
+               assertThat(ch.resolveField(FieldAccess.createGetField(CLASS_K, "x", "I", true)).getClassName(), is(CLASS_I));\r
+       }\r
+       \r
+       private void checkClasses(MethodInfo[] resolved, String... classNames) {\r
+               assertThat(Arrays.stream(resolved).map(m -> m.getClassName()).collect(Collectors.toList()), containsInAnyOrder(classNames));\r
+       }\r
+       \r
+       @Test\r
+       public void testFreeze() {\r
+               assertThat(ch.isFrozen(), is(false));\r
+               ch.freeze();\r
+               assertThat(ch.isFrozen(), is(true));\r
+               try {\r
+                       ch.registerClass(f);\r
+                       fail();\r
+               } catch (FrozenHierarchyException e) {\r
+               }\r
+               try {\r
+                       ch.registerInterfaces(CLASS_F, new ArrayList<String>());\r
+                       fail();\r
+               } catch (FrozenHierarchyException e) {\r
+               }\r
+               try {\r
+                       ch.registerSuperClass(CLASS_C, "java/lang/Object");\r
+                       fail();\r
+               } catch (FrozenHierarchyException e) {\r
+               }\r
+               try {\r
+                       ch.registerSubtype("pkg/NewChild", CLASS_C);\r
+                       fail();\r
+               } catch (FrozenHierarchyException e) {\r
+               }\r
+       }\r
+}\r
diff --git a/tests/soba/core/ClassInfoTest.java b/tests/soba/core/ClassInfoTest.java
new file mode 100755 (executable)
index 0000000..3296c2d
--- /dev/null
@@ -0,0 +1,84 @@
+package soba.core;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.IOException;\r
+\r
+import org.junit.Test;\r
+\r
+import soba.core.ClassInfo;\r
+\r
+public class ClassInfoTest implements ExampleProgram {\r
+\r
+       @Test\r
+       public void testClassInfo01() throws Exception {\r
+               String fileName = "bin/" + CLASS_D + ".class";\r
+               ClassInfo c = new ClassInfo(fileName, new FileInputStream(fileName));\r
+               \r
+               assertThat(c.getPackageName(), is("soba/testdata/inheritance1"));\r
+               assertThat(c.getClassName(), is(CLASS_D));\r
+               assertThat(c.getSuperClass(), is(CLASS_C));\r
+               assertThat(c.getInterfaces(), containsInAnyOrder("soba/testdata/inheritance1/I", \r
+                                                                                                                "soba/testdata/inheritance1/K"));\r
+               assertThat(c.getHash(), is(notNullValue()));\r
+               assertThat(c.getClassDirPath(), is("soba" + File.separator + "testdata" + File.separator + "inheritance1"));\r
+               assertThat(c.getClassFileName(), is(fileName));\r
+               assertThat(c.getSourceFileName(), is("soba" + File.separator + "testdata" + File.separator + "inheritance1" + File.separator + "D.java"));\r
+               assertThat(c.getLabel(), is(nullValue()));\r
+               assertThat(c.isLibrary(), is(false));\r
+               \r
+               assertThat(c.getMethodCount(), is(11));\r
+               assertThat(c.getMethods(), hasSize(11));\r
+               assertThat(c.getMethod(0), is(notNullValue()));\r
+               assertThat(c.findMethod("m", "()V"), is(notNullValue()));\r
+               assertThat(c.findMethod("n", "()V"), is(notNullValue()));\r
+               assertThat(c.findMethod("x", "(I)V"), is(notNullValue()));\r
+               assertThat(c.findMethod("example", "(IJDLjava/lang/String;)I"), is(notNullValue()));\r
+               assertThat(c.findMethod("toString", "()Ljava/lang/String;"), is(notNullValue()));\r
+               assertThat(c.findMethod("notExist", "()V"), is(nullValue()));\r
+               \r
+               assertThat(c.getFieldCount(), is(0));\r
+               assertThat(c.getFields(), is(empty()));\r
+       }\r
+       \r
+       @Test\r
+       public void testClassInfo02() throws Exception {\r
+               String fileName = "bin/" + CLASS_H + ".class";\r
+               ClassInfo c = new ClassInfo(fileName, new FileInputStream(fileName), "label");\r
+               \r
+               assertThat(c.getPackageName(), is("soba/testdata/inheritance2"));\r
+               assertThat(c.getClassName(), is(CLASS_H));\r
+               assertThat(c.getSuperClass(), is(CLASS_D));\r
+               assertThat(c.getInterfaces(), is(empty()));\r
+               assertThat(c.getHash(), is(notNullValue()));\r
+               assertThat(c.getClassDirPath(), is("soba" + File.separator + "testdata" + File.separator + "inheritance2"));\r
+               assertThat(c.getClassFileName(), is(fileName));\r
+               assertThat(c.getSourceFileName(), is("soba" + File.separator + "testdata" + File.separator + "inheritance2" + File.separator + "H.java"));\r
+               assertThat(c.getLabel(), is("label"));\r
+               assertThat(c.isLibrary(), is(false));\r
+               \r
+               assertThat(c.getMethodCount(), is(4));\r
+               assertThat(c.getMethods(), hasSize(4));\r
+               assertThat(c.getMethod(0), is(notNullValue()));\r
+               assertThat(c.findMethod("n", "()V"), is(notNullValue()));\r
+               assertThat(c.findMethod("p", "(I)V"), is(notNullValue()));\r
+               assertThat(c.findMethod("q", "(D)V"), is(notNullValue()));\r
+               assertThat(c.findMethod("<init>", "()V"), is(notNullValue()));\r
+               \r
+               assertThat(c.getFieldCount(), is(1));\r
+               assertThat(c.getFields(), hasSize(1));\r
+               assertThat(c.getField(0), is(notNullValue()));\r
+               assertThat(c.findField("x", "I"), is(notNullValue()));\r
+       }\r
+       \r
+       @Test\r
+       public void testLibrary01() throws IOException {\r
+               String fileName = "bin/" + CLASS_H + ".class";\r
+               ClassInfo c = ClassInfo.createLibraryClass(fileName, new FileInputStream(fileName));\r
+               assertThat(c.isLibrary(), is(true));\r
+       }\r
+       \r
+}\r
diff --git a/tests/soba/core/ExampleProgram.java b/tests/soba/core/ExampleProgram.java
new file mode 100755 (executable)
index 0000000..c50dc79
--- /dev/null
@@ -0,0 +1,16 @@
+package soba.core;\r
+\r
+public interface ExampleProgram {\r
+\r
+       public static final String CLASS_C = "soba/testdata/inheritance1/C";\r
+       public static final String CLASS_D = "soba/testdata/inheritance1/D";\r
+       public static final String CLASS_E = "soba/testdata/inheritance2/E";\r
+       public static final String CLASS_F = "soba/testdata/inheritance2/F";\r
+       public static final String CLASS_G = "soba/testdata/inheritance1/G";\r
+       public static final String CLASS_H = "soba/testdata/inheritance2/H";\r
+       public static final String CLASS_I = "soba/testdata/inheritance1/I";\r
+       public static final String CLASS_J = "soba/testdata/inheritance1/J";\r
+       public static final String CLASS_K = "soba/testdata/inheritance1/K";\r
+\r
+       \r
+}\r
diff --git a/tests/soba/core/FieldInfoTest.java b/tests/soba/core/FieldInfoTest.java
new file mode 100755 (executable)
index 0000000..4f8978e
--- /dev/null
@@ -0,0 +1,64 @@
+package soba.core;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+\r
+import org.junit.After;\r
+import org.junit.AfterClass;\r
+import org.junit.Before;\r
+import org.junit.BeforeClass;\r
+import org.junit.Test;\r
+\r
+import soba.core.ClassInfo;\r
+import soba.core.FieldInfo;\r
+import soba.core.JavaProgram;\r
+\r
+public class FieldInfoTest {\r
+\r
+       private static FieldInfo fi;\r
+       \r
+       @BeforeClass\r
+       public static void setUpBeforeClass() throws Exception {\r
+               JavaProgram program = JavaProgramTest.readExampleProgram();\r
+               ClassInfo c = program.getClassInfo(ExampleProgram.CLASS_C);\r
+               fi = c.getField(0);\r
+       }\r
+\r
+       @AfterClass\r
+       public static void tearDownAfterClass() throws Exception {\r
+       }\r
+\r
+       @Before\r
+       public void setUp() throws Exception {\r
+       }\r
+\r
+       @After\r
+       public void tearDown() throws Exception {\r
+       }\r
+\r
+       @Test\r
+       public void testGetPackageName() {\r
+               assertThat(fi.getPackageName(), is("soba/testdata/inheritance1"));\r
+       }\r
+\r
+       @Test\r
+       public void testGetClassName() {\r
+               assertThat(fi.getClassName(), is("soba/testdata/inheritance1/C"));\r
+       }\r
+\r
+       @Test\r
+       public void testGetFieldName() {\r
+               assertThat(fi.getFieldName(), is("x"));\r
+       }\r
+\r
+       @Test\r
+       public void testGetDescriptor() {\r
+               assertThat(fi.getDescriptor(), is("I"));\r
+       }\r
+\r
+       @Test\r
+       public void testGetFieldTypeName() {\r
+               assertThat(fi.getFieldTypeName(), is("int"));\r
+       }\r
+\r
+}\r
diff --git a/tests/soba/core/JavaProgramTest.java b/tests/soba/core/JavaProgramTest.java
new file mode 100755 (executable)
index 0000000..1f9382e
--- /dev/null
@@ -0,0 +1,52 @@
+package soba.core;\r
+\r
+import java.io.File;\r
+\r
+import soba.core.JavaProgram;\r
+import soba.util.files.Directory;\r
+import soba.util.files.IClassList;\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+\r
+import org.junit.BeforeClass;\r
+import org.junit.Test;\r
+\r
+public class JavaProgramTest implements ExampleProgram {\r
+\r
+       private static JavaProgram program;\r
+       \r
+       public static JavaProgram readExampleProgram() {\r
+               Directory dir = new Directory(new File("bin/soba/testdata/"));\r
+               JavaProgram program = new JavaProgram(new IClassList[] {dir});\r
+               return program;\r
+       }\r
+       \r
+       @BeforeClass\r
+       public static void setUpBeforeClass() {\r
+               program = readExampleProgram();\r
+       }\r
+\r
+       @Test\r
+       public void testJavaProgram01() {\r
+               assertThat(program.getClasses(), is(notNullValue()));\r
+               assertThat(program.getClasses(), hasSize(22));\r
+\r
+               assertThat(program.getFiltered(), is(empty()));\r
+               assertThat(program.getDuplicated(), is(empty()));\r
+               assertThat(program.getErrors(), is(empty()));\r
+\r
+               assertThat(program.getClassHierarchy(), is(notNullValue()));\r
+\r
+               assertThat(program.getClassInfo(CLASS_C), is(notNullValue()));\r
+               assertThat(program.getClassInfo(CLASS_D), is(notNullValue()));\r
+               assertThat(program.getClassInfo(CLASS_E), is(notNullValue()));\r
+               assertThat(program.getClassInfo(CLASS_F), is(notNullValue()));\r
+               assertThat(program.getClassInfo(CLASS_G), is(notNullValue()));\r
+               assertThat(program.getClassInfo(CLASS_H), is(notNullValue()));\r
+               assertThat(program.getClassInfo(CLASS_I), is(notNullValue()));\r
+               assertThat(program.getClassInfo(CLASS_J), is(notNullValue()));\r
+               assertThat(program.getClassInfo(CLASS_K), is(notNullValue()));\r
+               assertThat(program.getClassInfo("NotExistClass"), is(nullValue()));\r
+       }\r
+\r
+}\r
diff --git a/tests/soba/core/MethodInfoTest.java b/tests/soba/core/MethodInfoTest.java
new file mode 100755 (executable)
index 0000000..6aef30e
--- /dev/null
@@ -0,0 +1,157 @@
+package soba.core;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+\r
+import org.junit.BeforeClass;\r
+import org.junit.Test;\r
+\r
+import soba.core.ClassInfo;\r
+import soba.core.JavaProgram;\r
+import soba.core.MethodInfo;\r
+import soba.util.UtilForAssertThat;\r
+import soba.util.graph.DirectedGraph;\r
+\r
+public class MethodInfoTest implements ExampleProgram {\r
+\r
+       private static JavaProgram program; \r
+       \r
+       @BeforeClass\r
+       public static void readExampleProgram() {\r
+               program = JavaProgramTest.readExampleProgram();\r
+       }\r
+       \r
+       @Test\r
+       public void testMethodInfo01() {\r
+               ClassInfo c = program.getClassInfo(CLASS_D);\r
+               MethodInfo m = c.findMethod("example", "(IJDLjava/lang/String;)I");\r
+               \r
+               assertThat(m.getPackageName(), is("soba/testdata/inheritance1"));\r
+               assertThat(m.getClassName(), is(CLASS_D));\r
+               assertThat(m.getMethodName(), is("example"));\r
+               assertThat(m.getDescriptor(), is("(IJDLjava/lang/String;)I"));\r
+               assertThat(m.getGenericsSignature(), is(nullValue()));\r
+               assertThat(m.hasMethodBody(), is(true));\r
+               assertThat(m.isLibrary(), is(false));\r
+               assertThat(m.isPrivate(), is(false));\r
+               assertThat(m.isPublic(), is(true));\r
+               assertThat(m.isProtected(), is(false));\r
+               assertThat(m.isStatic(), is(false));\r
+               assertThat(m.isSynthetic(), is(false));\r
+               assertThat(m.isOverridable(), is(true));\r
+               assertThat(m.isPackagePrivate(), is(false));\r
+               assertThat(m.getInstructionCount(), is(5));\r
+               assertThat(m.getParamCount(), is(5));\r
+               assertThat(m.getReturnType(), is("int"));\r
+               assertThat(m.getReceiverObjectParamIndex(), is(0));\r
+               \r
+               assertThat(m.getParamName(0), is("this"));\r
+               assertThat(m.getParamName(1), is("i"));\r
+               assertThat(m.getParamName(2), is("l"));\r
+               assertThat(m.getParamName(3), is("d"));\r
+               assertThat(m.getParamName(4), is("s"));\r
+               assertThat(m.getParamName(5), is(nullValue()));\r
+               assertThat(m.getParamType(0), is(CLASS_D));\r
+               assertThat(m.getParamType(1), is("int"));\r
+               assertThat(m.getParamType(2), is("long"));\r
+               assertThat(m.getParamType(3), is("double"));\r
+               assertThat(m.getParamType(4), is("java/lang/String"));\r
+               assertThat(m.getVariableTableIndexOfParamAt(0), is(0));\r
+               assertThat(m.getVariableTableIndexOfParamAt(1), is(1));\r
+               assertThat(m.getVariableTableIndexOfParamAt(2), is(2));\r
+               assertThat(m.getVariableTableIndexOfParamAt(3), is(4));\r
+               assertThat(m.getVariableTableIndexOfParamAt(4), is(6));\r
+               assertThat(m.isParameterOrderingNumber(0), is(true));\r
+               assertThat(m.getParameterOrderingNumber(0), is(0));\r
+\r
+               assertThat(m.getMaxLine(), is(49));\r
+               assertThat(m.getMinLine(), is(49));\r
+               assertThat(UtilForAssertThat.asIntegerArray(m.getLineNumbers()), is(arrayContaining(49)));\r
+               assertThat(m.getLine(2), is(49));\r
+               assertThat(UtilForAssertThat.asIntegerArray(m.getInstructions(49)), is(arrayContainingInAnyOrder(1, 2, 3, 4)));\r
+               \r
+               assertThat(m.getCallSites(), is(empty()));\r
+               assertThat(m.getCallSite(2), is(nullValue()));\r
+               \r
+               assertThat(m.getFieldAccesses(), is(empty()));\r
+               \r
+               assertThat(UtilForAssertThat.asIntegerArray(m.getReturnInstructions()), is(arrayContainingInAnyOrder(3)));\r
+               \r
+               assertThat(m.getDataDependence(), is(notNullValue()));\r
+               assertThat(m.getControlDependence(), is(notNullValue()));\r
+               assertThat(m.getConservativeControlFlow(), is(notNullValue()));\r
+               DirectedGraph cfg = m.getControlFlow();\r
+               assertThat(cfg.getVertexCount(), is(5));\r
+               assertThat(cfg.getEdgeCount(), is(3));\r
+               Integer[] edges0 = UtilForAssertThat.asIntegerArray(cfg.getEdges(0));\r
+               Integer[] edges1 = UtilForAssertThat.asIntegerArray(cfg.getEdges(1));\r
+               Integer[] edges2 = UtilForAssertThat.asIntegerArray(cfg.getEdges(2));\r
+               Integer[] edges3 = UtilForAssertThat.asIntegerArray(cfg.getEdges(3));\r
+               Integer[] edges4 = UtilForAssertThat.asIntegerArray(cfg.getEdges(4));\r
+               assertThat(edges0, is(arrayContainingInAnyOrder(1)));\r
+               assertThat(edges1, is(arrayContainingInAnyOrder(2)));\r
+               assertThat(edges2, is(arrayContainingInAnyOrder(3)));\r
+               assertThat(edges3, is(emptyArray()));\r
+               assertThat(edges4, is(emptyArray()));\r
+               \r
+               assertThat(m.getMethodKey(), is(CLASS_D + "#example#(IJDLjava/lang/String;)I"));\r
+               assertThat(m.toLongString(), is(CLASS_D + ".example(" + CLASS_D + ":this, int:i, long:l, double:d, java/lang/String:s): int"));\r
+               assertThat(m.getInstructionString(0), is("0: (L00000)"));\r
+               assertThat(m.getInstructionString(1), is("1: (line=49)"));\r
+               assertThat(m.getInstructionString(2), is("2: ILOAD 1 (i)"));\r
+               assertThat(m.getInstructionString(3), is("3: IRETURN"));\r
+               assertThat(m.getInstructionString(4), is("4: (L00004)"));\r
+       }\r
+       \r
+       @Test\r
+       public void testMethodInfo02() {\r
+               ClassInfo c = program.getClassInfo(CLASS_C);\r
+               MethodInfo m = c.findMethod("main", "([Ljava/lang/String;)V");\r
+               \r
+               assertThat(m.getPackageName(), is("soba/testdata/inheritance1"));\r
+               assertThat(m.getClassName(), is(CLASS_C));\r
+               assertThat(m.getMethodName(), is("main"));\r
+               assertThat(m.getDescriptor(), is("([Ljava/lang/String;)V"));\r
+               assertThat(m.getGenericsSignature(), is(nullValue()));\r
+               assertThat(m.hasMethodBody(), is(true));\r
+               assertThat(m.isLibrary(), is(false));\r
+               assertThat(m.isPrivate(), is(false));\r
+               assertThat(m.isPublic(), is(true));\r
+               assertThat(m.isProtected(), is(false));\r
+               assertThat(m.isStatic(), is(true));\r
+               assertThat(m.isSynthetic(), is(false));\r
+               assertThat(m.isOverridable(), is(false));\r
+               assertThat(m.isPackagePrivate(), is(false));\r
+               assertThat(m.getInstructionCount(), is(27));\r
+               assertThat(m.getParamCount(), is(1));\r
+               assertThat(m.getReturnType(), is("void"));\r
+               \r
+               assertThat(m.getParamName(0), is("args"));\r
+               assertThat(m.getParamType(0), is("java/lang/String[]"));\r
+               assertThat(m.getVariableTableIndexOfParamAt(0), is(0));\r
+               assertThat(m.isParameterOrderingNumber(0), is(true));\r
+               assertThat(m.getParameterOrderingNumber(0), is(0));\r
+\r
+               assertThat(m.getMaxLine(), is(15));\r
+               assertThat(m.getMinLine(), is(10));\r
+               assertThat(UtilForAssertThat.asIntegerArray(m.getLineNumbers()), is(arrayContaining(10, 11, 12, 13, 14, 15)));\r
+               assertThat(m.getLine(9), is(11));\r
+               assertThat(UtilForAssertThat.asIntegerArray(m.getInstructions(11)), is(arrayContainingInAnyOrder(6, 7, 8, 9, 10, 11)));\r
+               \r
+               assertThat(m.getCallSites(), hasSize(4));\r
+               assertThat(m.getCallSite(9), is(notNullValue()));\r
+               \r
+               assertThat(m.getFieldAccesses(), hasSize(1));\r
+               \r
+               assertThat(UtilForAssertThat.asIntegerArray(m.getReturnInstructions()), is(arrayContainingInAnyOrder(25)));\r
+               \r
+               assertThat(m.getDataDependence(), is(notNullValue()));\r
+               assertThat(m.getControlDependence(), is(notNullValue()));\r
+               assertThat(m.getConservativeControlFlow(), is(notNullValue()));\r
+               assertThat(m.getControlFlow(), is(notNullValue()));\r
+               \r
+               assertThat(m.getMethodKey(), is(CLASS_C + "#main#([Ljava/lang/String;)V"));\r
+               assertThat(m.toLongString(), is(CLASS_C + ".main(java/lang/String[]:args): void"));\r
+       }\r
+       \r
+}\r
diff --git a/tests/soba/core/method/ControlDependenceTest.java b/tests/soba/core/method/ControlDependenceTest.java
new file mode 100755 (executable)
index 0000000..46072c8
--- /dev/null
@@ -0,0 +1,77 @@
+package soba.core.method;\r
+\r
+import org.junit.Test;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+import soba.core.method.ControlDependence;\r
+import soba.util.IntPairList;\r
+import soba.util.UtilForAssertThat;\r
+import soba.util.graph.DirectedGraph;\r
+\r
+public class ControlDependenceTest {\r
+\r
+       public static DirectedGraph buildControlFlowGraph() { \r
+               IntPairList edges = new IntPairList();\r
+               edges.add(0, 1); // cycle 0->1->2\r
+               edges.add(1, 2);\r
+               edges.add(2, 0);\r
+               edges.add(1, 3);\r
+               \r
+               edges.add(3, 4);\r
+               edges.add(3, 5);\r
+               \r
+               edges.add(5, 6); // another cycle: 5->6->8->5\r
+               edges.add(6, 8);\r
+               edges.add(8, 5);\r
+\r
+               edges.add(6, 7); \r
+               edges.add(7, 9);\r
+               edges.add(7, 10);\r
+               edges.add(9, 11);\r
+               edges.add(10, 11);\r
+               edges.add(11, 12); // 13 is not connected to any other vertices\r
+               \r
+               return new DirectedGraph(14, edges);\r
+       }\r
+\r
+\r
+       @Test\r
+       public void testControlDependence() {\r
+               DirectedGraph g = buildControlFlowGraph();\r
+               DirectedGraph cd = ControlDependence.getDependence(14, g);\r
+               \r
+               // Not a branch vertex\r
+               Integer[] edgesFrom0 = UtilForAssertThat.asIntegerArray(cd.getEdges(0));\r
+               Integer[] edgesFrom2 = UtilForAssertThat.asIntegerArray(cd.getEdges(2));\r
+               Integer[] edgesFrom4 = UtilForAssertThat.asIntegerArray(cd.getEdges(4));\r
+               Integer[] edgesFrom5 = UtilForAssertThat.asIntegerArray(cd.getEdges(5));\r
+               Integer[] edgesFrom8 = UtilForAssertThat.asIntegerArray(cd.getEdges(8));\r
+               Integer[] edgesFrom9 = UtilForAssertThat.asIntegerArray(cd.getEdges(9));\r
+               Integer[] edgesFrom10 = UtilForAssertThat.asIntegerArray(cd.getEdges(10));\r
+               Integer[] edgesFrom11 = UtilForAssertThat.asIntegerArray(cd.getEdges(11));\r
+               Integer[] edgesFrom12 = UtilForAssertThat.asIntegerArray(cd.getEdges(12));\r
+               Integer[] edgesFrom13 = UtilForAssertThat.asIntegerArray(cd.getEdges(13));\r
+               assertThat(edgesFrom0, is(emptyArray()));\r
+               assertThat(edgesFrom2, is(emptyArray()));\r
+               assertThat(edgesFrom4, is(emptyArray()));\r
+               assertThat(edgesFrom5, is(emptyArray()));\r
+               assertThat(edgesFrom8, is(emptyArray()));\r
+               assertThat(edgesFrom9, is(emptyArray()));\r
+               assertThat(edgesFrom10, is(emptyArray()));\r
+               assertThat(edgesFrom11, is(emptyArray()));\r
+               assertThat(edgesFrom12, is(emptyArray()));\r
+               assertThat(edgesFrom13, is(emptyArray()));\r
+               \r
+               // Conditional branches\r
+               Integer[] edgesFrom1 = UtilForAssertThat.asIntegerArray(cd.getEdges(1));\r
+               Integer[] edgesFrom3 = UtilForAssertThat.asIntegerArray(cd.getEdges(3));\r
+               Integer[] edgesFrom6 = UtilForAssertThat.asIntegerArray(cd.getEdges(6));\r
+               Integer[] edgesFrom7 = UtilForAssertThat.asIntegerArray(cd.getEdges(7));\r
+               assertThat(edgesFrom1, is(arrayContainingInAnyOrder(0, 2)));\r
+               assertThat(edgesFrom3, is(arrayContainingInAnyOrder(4, 6, 7, 11, 12)));\r
+               assertThat(edgesFrom6, is(arrayContainingInAnyOrder(5, 8)));\r
+               assertThat(edgesFrom7, is(arrayContainingInAnyOrder(9, 10)));\r
+       }\r
+       \r
+}\r
diff --git a/tests/soba/core/method/DataDependenceTest.java b/tests/soba/core/method/DataDependenceTest.java
new file mode 100755 (executable)
index 0000000..0e53616
--- /dev/null
@@ -0,0 +1,172 @@
+package soba.core.method;\r
+\r
+import java.util.List;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+\r
+import org.junit.BeforeClass;\r
+import org.junit.Test;\r
+\r
+import soba.core.JavaProgram;\r
+import soba.core.JavaProgramTest;\r
+import soba.core.MethodInfo;\r
+import soba.core.method.DataFlowEdge;\r
+import soba.util.UtilForAssertThat;\r
+import soba.util.graph.DirectedGraph;\r
+\r
+public class DataDependenceTest {\r
+\r
+       private static JavaProgram program;\r
+       \r
+       @BeforeClass\r
+       public static void setUpBeforeClass() {\r
+               program = JavaProgramTest.readExampleProgram();\r
+       }\r
+       \r
+       @Test\r
+       public void testDataDependence01() {\r
+               MethodInfo m = program.getClassInfo("soba/testdata/DefUseTestData").findMethod("overwriteParam", "(II)V");\r
+               DataDependence dd = m.getDataDependence();\r
+               assertThat(dd, is(notNullValue()));\r
+\r
+               List<DataFlowEdge> edges = dd.getEdges();\r
+               assertThat(edges, hasSize(7));\r
+               assertThat(containsEdge(edges, -1, 2), is(true));\r
+               assertThat(containsEdge(edges, 2, 3), is(true));\r
+               assertThat(containsEdge(edges, 4, 5), is(true));\r
+               assertThat(containsEdge(edges, -1, 10), is(true));\r
+               assertThat(containsEdge(edges, 5, 10), is(true));\r
+               assertThat(containsEdge(edges, 9, 11), is(true));\r
+               assertThat(containsEdge(edges, 10, 11), is(true));\r
+               \r
+               List<DataFlowEdge> edgesSourceOrder = dd.getEdgesInSourceOrder();\r
+               assertThat(edgesSourceOrder, hasSize(7));\r
+               assertThat(containsEdgeAt(edgesSourceOrder, -1, 2, 0), is(true));\r
+               assertThat(containsEdgeAt(edgesSourceOrder, -1, 10, 1), is(true));\r
+               assertThat(containsEdgeAt(edgesSourceOrder, 2, 3, 2), is(true));\r
+               assertThat(containsEdgeAt(edgesSourceOrder, 4, 5, 3), is(true));\r
+               assertThat(containsEdgeAt(edgesSourceOrder, 5, 10, 4), is(true));\r
+               assertThat(containsEdgeAt(edgesSourceOrder, 9, 11, 5), is(true));\r
+               assertThat(containsEdgeAt(edgesSourceOrder, 10, 11, 6), is(true));\r
+               \r
+               DirectedGraph dependence = dd.getDependenceGraph();\r
+               assertThat(dependence.getVertexCount(), is(m.getInstructionCount()));\r
+               assertThat(dependence.getEdgeCount(), is(5));\r
+               Integer[] edgesFrom0 = UtilForAssertThat.asIntegerArray(dependence.getEdges(0));\r
+               Integer[] edgesFrom1 = UtilForAssertThat.asIntegerArray(dependence.getEdges(1));\r
+               Integer[] edgesFrom2 = UtilForAssertThat.asIntegerArray(dependence.getEdges(2));\r
+               Integer[] edgesFrom3 = UtilForAssertThat.asIntegerArray(dependence.getEdges(3));\r
+               Integer[] edgesFrom4 = UtilForAssertThat.asIntegerArray(dependence.getEdges(4));\r
+               Integer[] edgesFrom5 = UtilForAssertThat.asIntegerArray(dependence.getEdges(5));\r
+               Integer[] edgesFrom6 = UtilForAssertThat.asIntegerArray(dependence.getEdges(6));\r
+               Integer[] edgesFrom7 = UtilForAssertThat.asIntegerArray(dependence.getEdges(7));\r
+               Integer[] edgesFrom8 = UtilForAssertThat.asIntegerArray(dependence.getEdges(8));\r
+               Integer[] edgesFrom9 = UtilForAssertThat.asIntegerArray(dependence.getEdges(9));\r
+               Integer[] edgesFrom10 = UtilForAssertThat.asIntegerArray(dependence.getEdges(10));\r
+               Integer[] edgesFrom11 = UtilForAssertThat.asIntegerArray(dependence.getEdges(11));\r
+               Integer[] edgesFrom12 = UtilForAssertThat.asIntegerArray(dependence.getEdges(12));\r
+               Integer[] edgesFrom13 = UtilForAssertThat.asIntegerArray(dependence.getEdges(13));\r
+               Integer[] edgesFrom14 = UtilForAssertThat.asIntegerArray(dependence.getEdges(14));\r
+               Integer[] edgesFrom15 = UtilForAssertThat.asIntegerArray(dependence.getEdges(15));\r
+               assertThat(edgesFrom2, is(arrayContainingInAnyOrder(3)));\r
+               assertThat(edgesFrom4, is(arrayContainingInAnyOrder(5)));\r
+               assertThat(edgesFrom5, is(arrayContainingInAnyOrder(10)));\r
+               assertThat(edgesFrom9, is(arrayContainingInAnyOrder(11)));\r
+               assertThat(edgesFrom10, is(arrayContainingInAnyOrder(11)));\r
+               assertThat(edgesFrom0, is(emptyArray()));\r
+               assertThat(edgesFrom1, is(emptyArray()));\r
+               assertThat(edgesFrom3, is(emptyArray()));\r
+               assertThat(edgesFrom6, is(emptyArray()));\r
+               assertThat(edgesFrom7, is(emptyArray()));\r
+               assertThat(edgesFrom8, is(emptyArray()));\r
+               assertThat(edgesFrom11, is(emptyArray()));\r
+               assertThat(edgesFrom12, is(emptyArray()));\r
+               assertThat(edgesFrom13, is(emptyArray()));\r
+               assertThat(edgesFrom14, is(emptyArray()));\r
+               assertThat(edgesFrom15, is(emptyArray()));\r
+\r
+               List<DataFlowEdge> incomingEdges2 = dd.getIncomingEdges(2);\r
+               assertThat(incomingEdges2, hasSize(1));\r
+               assertThat(containsEdge(incomingEdges2, -1, 2), is(true));\r
+               assertThat(dd.getVariableName(incomingEdges2.get(0)), is("x"));\r
+               assertThat(dd.getVariableDescriptor(incomingEdges2.get(0)), is("I"));\r
+               List<DataFlowEdge> incomingEdges11 = dd.getIncomingEdges(11);\r
+               assertThat(incomingEdges11, hasSize(2));\r
+               assertThat(containsEdge(incomingEdges11, 9, 11), is(true));\r
+               assertThat(containsEdge(incomingEdges11, 10, 11), is(true));\r
+               \r
+               DataFlowEdge incomingEdge11At0 = dd.getIncomingEdge(11, 0);\r
+               assertThat(incomingEdge11At0.getSourceInstruction(), is(9));\r
+               DataFlowEdge incommingEdge11At1 = dd.getIncomingEdge(11, 1);\r
+               assertThat(incommingEdge11At1.getSourceInstruction(), is(10));\r
+               \r
+               List<DataFlowEdge> incomingEdges11At0 = dd.getIncomingEdges(11, 0);\r
+               assertThat(incomingEdges11At0, hasSize(1));\r
+               assertThat(containsEdge(incomingEdges11At0, 9, 11), is(true));\r
+               List<DataFlowEdge> incomingEdges11At1 = dd.getIncomingEdges(11, 1);\r
+               assertThat(incomingEdges11At1, hasSize(1));\r
+               assertThat(containsEdge(incomingEdges11At1, 10, 11), is(true));\r
+               \r
+               int[][] definition10 = dd.getDataDefinition(10);\r
+               assertThat(definition10.length, is(1));\r
+               Integer[] local = UtilForAssertThat.asIntegerArray(definition10[0]);\r
+               assertThat(local, is(arrayContainingInAnyOrder(-1, 5)));\r
+               int[][] definition11 = dd.getDataDefinition(11);\r
+               assertThat(definition11.length, is(2));\r
+               Integer[] operand0 = UtilForAssertThat.asIntegerArray(definition11[0]);\r
+               Integer[] operand1 = UtilForAssertThat.asIntegerArray(definition11[1]);\r
+               assertThat(operand0, is(arrayContainingInAnyOrder(9)));\r
+               assertThat(operand1, is(arrayContainingInAnyOrder(10)));\r
+               \r
+               assertThat(dd.getOperandCount(2), is(0));\r
+               assertThat(dd.getOperandCount(3), is(1));\r
+               assertThat(dd.getOperandCount(11), is(2));\r
+       }\r
+       \r
+       @Test\r
+       public void testDataDependence02() {\r
+               MethodInfo m = program.getClassInfo("soba/testdata/DefUseTestData").findMethod("localDataDependence", "()V");\r
+               DataDependence dd = m.getDataDependence();\r
+               assertThat(dd, is(notNullValue()));\r
+\r
+               List<DataFlowEdge> edges = dd.getEdges();\r
+               assertThat(edges, hasSize(17));\r
+               assertThat(containsEdge(edges, 2, 3), is(true));\r
+               assertThat(containsEdge(edges, 3, 6), is(true));\r
+               assertThat(containsEdge(edges, 6, 7), is(true));\r
+               assertThat(containsEdge(edges, 10, 11), is(true));\r
+               assertThat(containsEdge(edges, 18, 19), is(true));\r
+               assertThat(containsEdge(edges, 19, 23), is(true));\r
+               assertThat(containsEdge(edges, 22, 24), is(true));\r
+               assertThat(containsEdge(edges, 23, 24), is(true));\r
+               assertThat(containsEdge(edges, 27, 28), is(true));\r
+               assertThat(containsEdge(edges, 11, 33), is(true));\r
+               assertThat(containsEdge(edges, 28, 33), is(true));\r
+               assertThat(containsEdge(edges, 32, 34), is(true));\r
+               assertThat(containsEdge(edges, 11, 38), is(true));\r
+               assertThat(containsEdge(edges, 28, 38), is(true));\r
+               assertThat(containsEdge(edges, 37, 39), is(true));\r
+               assertThat(containsEdge(edges, 38, 39), is(true));\r
+       }\r
+       \r
+       @Test\r
+       public void testDataDependence03() {\r
+               MethodInfo m = program.getClassInfo("soba/testdata/DefUseTestData").findMethod("tryFinallyDependence", "()I");\r
+               DataDependence dd = m.getDataDependence();\r
+               assertThat(dd, is(notNullValue()));\r
+       }\r
+       \r
+       private boolean containsEdge(List<DataFlowEdge> edges, int from, int to) {\r
+               return edges.stream().anyMatch(\r
+                               e -> e.getSourceInstruction() == from && \r
+                                        e.getDestinationInstruction() == to);\r
+       }\r
+       \r
+       private boolean containsEdgeAt(List<DataFlowEdge> edges, int from, int to, int pos) {\r
+               return edges.size() > pos && \r
+                           edges.get(pos).getSourceInstruction() == from &&\r
+                           edges.get(pos).getDestinationInstruction() == to;\r
+       }\r
+\r
+}\r
diff --git a/tests/soba/core/method/LocalVariablesTest.java b/tests/soba/core/method/LocalVariablesTest.java
new file mode 100755 (executable)
index 0000000..494e303
--- /dev/null
@@ -0,0 +1,131 @@
+package soba.core.method;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+\r
+import org.junit.After;\r
+import org.junit.AfterClass;\r
+import org.junit.Before;\r
+import org.junit.BeforeClass;\r
+import org.junit.Test;\r
+import org.objectweb.asm.Opcodes;\r
+import org.objectweb.asm.tree.InsnList;\r
+\r
+import soba.core.ClassInfo;\r
+import soba.core.JavaProgram;\r
+import soba.core.JavaProgramTest;\r
+import soba.core.MethodInfo;\r
+import soba.core.method.LocalVariables;\r
+\r
+public class LocalVariablesTest {\r
+\r
+       private static JavaProgram program;\r
+       private static ClassInfo c;\r
+       private static LocalVariables variables;\r
+       private static InsnList instructions;\r
+       \r
+       @BeforeClass\r
+       public static void setUpBeforeClass() throws Exception {\r
+               program = JavaProgramTest.readExampleProgram();\r
+               c = program.getClassInfo("soba/testdata/DefUseTestData");\r
+               MethodInfo m = c.findMethod("localDataDependence", "()V");\r
+               instructions = m.getMethodNode().instructions;\r
+               variables = new LocalVariables(m.getDataDependence(), m.getMethodNode());\r
+       }\r
+\r
+       @AfterClass\r
+       public static void tearDownAfterClass() throws Exception {\r
+       }\r
+\r
+       @Before\r
+       public void setUp() throws Exception {\r
+       }\r
+\r
+       @After\r
+       public void tearDown() throws Exception {\r
+       }\r
+\r
+       @Test\r
+       public void testGetVariableEntryCount() {\r
+               assertThat(variables.getVariableEntryCount(), is(3));\r
+       }\r
+\r
+       @Test\r
+       public void testGetVariableName() {\r
+               assertThat(variables.getVariableName(0), is("b"));\r
+               assertThat(variables.getVariableName(1), is("x"));\r
+               assertThat(variables.getVariableName(2), is("x"));\r
+       }\r
+\r
+       @Test\r
+       public void testGetVariableType() {\r
+               assertThat(variables.getVariableType(0), is("boolean"));\r
+               assertThat(variables.getVariableType(1), is("int"));\r
+               assertThat(variables.getVariableType(2), is("int"));\r
+       }\r
+\r
+       @Test\r
+       public void testGetVariableIndex() {\r
+               assertThat(variables.getVariableIndex(0), is(2)); // variable b\r
+               assertThat(variables.getVariableIndex(1), is(1)); // variable x\r
+               assertThat(variables.getVariableIndex(2), is(1)); // variable x\r
+       }\r
+\r
+       @Test\r
+       public void testIsObjectVariable() {\r
+               assertThat(variables.isObjectVariable(0), is(false));\r
+               assertThat(variables.isObjectVariable(1), is(false));\r
+               assertThat(variables.isObjectVariable(2), is(false));\r
+       }\r
+\r
+       @Test\r
+       public void testIsArrayVariable() {\r
+               assertThat(variables.isArrayVariable(0), is(false));\r
+               assertThat(variables.isArrayVariable(1), is(false));\r
+               assertThat(variables.isArrayVariable(2), is(false));\r
+       }\r
+\r
+       @Test\r
+       public void testHasNoDataDependence() {\r
+               assertThat(variables.hasNoDataDependence(0), is(false));\r
+               assertThat(variables.hasNoDataDependence(1), is(false));\r
+               assertThat(variables.hasNoDataDependence(2), is(false));\r
+       }\r
+\r
+       @Test\r
+       public void testIsParameter() {\r
+               assertThat(variables.isParameter(0), is(false));\r
+               assertThat(variables.isParameter(1), is(false));\r
+               assertThat(variables.isParameter(2), is(false));\r
+       }\r
+\r
+       @Test\r
+       public void testFindEntryForInstruction() {\r
+               int storeCount = 0;\r
+               int loadCount = 0;\r
+               for (int i = 0; i < instructions.size(); i++) {\r
+                       if (instructions.get(i).getOpcode() == Opcodes.ISTORE) {\r
+                               if (storeCount == 0) {\r
+                                       assertThat(variables.findEntryForInstruction(i), is(0));\r
+                               } else if (storeCount == 1 || storeCount == 3) {\r
+                                       assertThat(variables.findEntryForInstruction(i), is(2));\r
+                               } else {\r
+                                       assertThat(variables.findEntryForInstruction(i), is(1));\r
+                               }\r
+                               storeCount++;\r
+                       } else if (instructions.get(i).getOpcode() == Opcodes.ILOAD) {\r
+                               if (loadCount == 0) {\r
+                                       assertThat(variables.findEntryForInstruction(i), is(0));\r
+                               } else if (loadCount == 1) {\r
+                                       assertThat(variables.findEntryForInstruction(i), is(1));\r
+                               } else {\r
+                                       assertThat(variables.findEntryForInstruction(i), is(2));\r
+                               }\r
+                               loadCount++;\r
+                       } else {\r
+                               assertThat(variables.findEntryForInstruction(i), is(-1));\r
+                       }\r
+               }\r
+       }\r
+\r
+}\r
diff --git a/tests/soba/core/method/asm/FastSourceValueTest.java b/tests/soba/core/method/asm/FastSourceValueTest.java
new file mode 100755 (executable)
index 0000000..8a48868
--- /dev/null
@@ -0,0 +1,163 @@
+package soba.core.method.asm;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+\r
+//import org.junit.Assert;\r
+import org.junit.Test;\r
+\r
+import soba.core.method.asm.FastSourceValue;\r
+import soba.util.UtilForAssertThat;\r
+\r
+public class FastSourceValueTest {\r
+\r
+       @Test\r
+       public void testConstructorWithoutInstruction() {\r
+               FastSourceValue value1 = new FastSourceValue(1);\r
+               assertThat(value1.getSize(), is(1));\r
+               assertThat(value1.getInstructions().length, is(0));\r
+\r
+               FastSourceValue value2 = new FastSourceValue(2);\r
+               assertThat(value2.getSize(), is(2));\r
+               assertThat(value2.getInstructions().length, is(0));\r
+       }\r
+       \r
+       @Test\r
+       public void testConstructorWithSingleInstruction() {\r
+               FastSourceValue value1 = new FastSourceValue(1, 2);\r
+               assertThat(value1.getSize(), is(1));\r
+               assertThat(value1.getInstructions().length, is(1));\r
+               assertThat(value1.getInstructions()[0], is(2));\r
+       }\r
+       \r
+       @Test\r
+       public void testConstructorWithArray() {\r
+               FastSourceValue value1 = new FastSourceValue(2, new int[] { 1, 4, 9 });\r
+               assertThat(value1.getSize(), is(2));\r
+               assertThat(value1.getInstructions().length, is(3));\r
+               assertThat(value1.getInstructions()[0], is(1));\r
+               assertThat(value1.getInstructions()[1], is(4));\r
+               assertThat(value1.getInstructions()[2], is(9));\r
+       }\r
+       \r
+       @Test\r
+       public void testMerge() {\r
+               FastSourceValue value123 = new FastSourceValue(1, new int[] {1, 2, 3});\r
+               FastSourceValue value456 = new FastSourceValue(1, new int[] {4, 5, 6});\r
+               FastSourceValue value135 = new FastSourceValue(1, new int[] {1, 3, 5});\r
+               FastSourceValue value246 = new FastSourceValue(1, new int[] {2, 4, 6});\r
+               FastSourceValue valueNULL = new FastSourceValue(1, new int[0]);\r
+               FastSourceValue value123Size2 = new FastSourceValue(2, new int[] {1, 2, 3});\r
+               \r
+               FastSourceValue value123456concat = new FastSourceValue(value123, value456);\r
+               Integer[] instructions123456 = UtilForAssertThat.asIntegerArray(value123456concat.getInstructions());\r
+               assertThat(instructions123456, is(arrayContainingInAnyOrder(1, 2, 3, 4, 5, 6)));\r
+               \r
+               FastSourceValue value123456anotherConcat = new FastSourceValue(value456, value123);\r
+               Integer[] instructions123456another = UtilForAssertThat.asIntegerArray(value123456anotherConcat.getInstructions());\r
+               assertThat(instructions123456another, is(arrayContainingInAnyOrder(1, 2, 3, 4, 5, 6)));\r
+\r
+               FastSourceValue value123456mix = new FastSourceValue(value135, value246);\r
+               Integer[] instructions123456mix = UtilForAssertThat.asIntegerArray(value123456mix.getInstructions());\r
+               assertThat(instructions123456mix, is(arrayContainingInAnyOrder(1, 2, 3, 4, 5, 6)));\r
+\r
+               FastSourceValue value123456anotherMix = new FastSourceValue(value246, value135);\r
+               Integer[] instructions123456anotherMix = UtilForAssertThat.asIntegerArray(value123456anotherMix.getInstructions());\r
+               assertThat(instructions123456anotherMix, is(arrayContainingInAnyOrder(1, 2, 3, 4, 5, 6)));\r
+\r
+               FastSourceValue value123unchanged = new FastSourceValue(value123, valueNULL);\r
+               Integer[] instructions123 = UtilForAssertThat.asIntegerArray(value123unchanged.getInstructions());\r
+               assertThat(instructions123, is(arrayContainingInAnyOrder(1, 2, 3)));\r
+\r
+               FastSourceValue value246unchanged = new FastSourceValue(valueNULL, value246);\r
+               Integer[] instructions246 = UtilForAssertThat.asIntegerArray(value246unchanged.getInstructions());\r
+               assertThat(instructions246, is(arrayContainingInAnyOrder(2, 4, 6)));\r
+               \r
+               FastSourceValue valueConcatNull = new FastSourceValue(valueNULL, valueNULL);\r
+               Integer[] instructionsNull = UtilForAssertThat.asIntegerArray(valueConcatNull.getInstructions());\r
+               assertThat(instructionsNull, is(emptyArray()));\r
+               \r
+               FastSourceValue value1235 = new FastSourceValue(value123, value135);\r
+               Integer[] instructions1235 = UtilForAssertThat.asIntegerArray(value1235.getInstructions());\r
+               assertThat(instructions1235, is(arrayContainingInAnyOrder(1, 2, 3, 5)));\r
+\r
+               FastSourceValue value1235another = new FastSourceValue(value135, value123);\r
+               Integer[] instructions1235another = UtilForAssertThat.asIntegerArray(value1235another.getInstructions());\r
+               assertThat(instructions1235another, is(arrayContainingInAnyOrder(1, 2, 3 ,5)));\r
+\r
+               FastSourceValue value2456 = new FastSourceValue(value246, value456);\r
+               Integer[] instructions2456 = UtilForAssertThat.asIntegerArray(value2456.getInstructions());\r
+               assertThat(instructions2456, is(arrayContainingInAnyOrder(2, 4, 5, 6)));\r
+\r
+               FastSourceValue value2456another = new FastSourceValue(value456, value246);\r
+               Integer[] instructions2456another = UtilForAssertThat.asIntegerArray(value2456another.getInstructions());\r
+               assertThat(instructions2456another, is(arrayContainingInAnyOrder(2, 4, 5, 6)));\r
+               \r
+               FastSourceValue value123differentSize = new FastSourceValue(value123, value123Size2);\r
+               Integer[] instructions123different = UtilForAssertThat.asIntegerArray(value123differentSize.getInstructions());\r
+               assertThat(instructions123different, is(arrayContainingInAnyOrder(1, 2, 3)));\r
+               assertThat(value123differentSize.getSize(), is(1));\r
+\r
+               FastSourceValue value123differentSizeAnother = new FastSourceValue(value123Size2, value123);\r
+               Integer[] instructions123differentAnother = UtilForAssertThat.asIntegerArray(value123differentSizeAnother.getInstructions());\r
+               assertThat(instructions123differentAnother, is(arrayContainingInAnyOrder(1, 2, 3)));\r
+               assertThat(value123differentSizeAnother.getSize(), is(1));\r
+       }\r
+\r
+       @Test\r
+       public void testContainsAll() {\r
+               FastSourceValue value123 = new FastSourceValue(1, new int[] {1, 2, 3});\r
+               FastSourceValue value246 = new FastSourceValue(1, new int[] {2, 4, 6});\r
+               FastSourceValue valueNULL = new FastSourceValue(1, new int[0]);\r
+               FastSourceValue value0 = new FastSourceValue(1, 0);\r
+               FastSourceValue value1 = new FastSourceValue(1, 1);\r
+               FastSourceValue value2 = new FastSourceValue(1, 2);\r
+               FastSourceValue value3 = new FastSourceValue(1, 3);\r
+               FastSourceValue value4 = new FastSourceValue(1, 4);\r
+               FastSourceValue value12 = new FastSourceValue(1, new int[] {1, 2});\r
+               FastSourceValue value23 = new FastSourceValue(1, new int[] {2, 3});\r
+               FastSourceValue value45 = new FastSourceValue(1, new int[] {4, 5});\r
+               FastSourceValue value46 = new FastSourceValue(1, new int[] {4, 6});\r
+               FastSourceValue value1234 = new FastSourceValue(1, new int[] {1, 2, 3, 4});\r
+               FastSourceValue value24 = new FastSourceValue(1, new int[] {2, 4});\r
+               \r
+               assertThat(value123.containsAll(valueNULL), is(true));\r
+               assertThat(value123.containsAll(value123), is(true));\r
+               assertThat(value123.containsAll(value1), is(true));\r
+               assertThat(value123.containsAll(value2), is(true));\r
+               assertThat(value123.containsAll(value3), is(true));\r
+               assertThat(value123.containsAll(value12), is(true));\r
+               assertThat(value123.containsAll(value23), is(true));\r
+               assertThat(value123.containsAll(value0), is(false));\r
+               assertThat(value123.containsAll(value4), is(false));\r
+               assertThat(value123.containsAll(value246), is(false));\r
+               assertThat(value123.containsAll(value45), is(false));\r
+               assertThat(value123.containsAll(value46), is(false));\r
+               assertThat(value123.containsAll(value1234), is(false));\r
+               assertThat(value123.containsAll(value24), is(false));\r
+               \r
+               assertThat(valueNULL.containsAll(valueNULL), is(true));\r
+               assertThat(valueNULL.containsAll(value1), is(false));\r
+               assertThat(valueNULL.containsAll(value123), is(false));\r
+               \r
+               assertThat(value246.containsAll(value46), is(true));\r
+               assertThat(value246.containsAll(value4), is(true));\r
+               assertThat(value246.containsAll(value24), is(true));\r
+               assertThat(value246.containsAll(value123), is(false));\r
+       }\r
+\r
+       @Test\r
+       public void testEquals() {\r
+               int[] v123 = new int[] {1, 2, 3};\r
+               FastSourceValue value123 = new FastSourceValue(1, v123);\r
+               FastSourceValue value123another = new FastSourceValue(1, v123);\r
+               FastSourceValue value123differentSize = new FastSourceValue(2, v123);\r
+               FastSourceValue value123differentArray = new FastSourceValue(1, new int[] {1, 2, 3});\r
+               FastSourceValue value123differentSizeAndArray = new FastSourceValue(2, new int[] {1, 2, 3});\r
+               \r
+               assertThat(value123.equals(value123another), is(true));\r
+               assertThat(value123.equals(value123differentSize), is(false));\r
+               assertThat(value123.equals(value123differentArray), is(true));\r
+               assertThat(value123.equals(value123differentSizeAndArray), is(false));\r
+       }\r
+}\r
diff --git a/tests/soba/core/signature/TypeResolverTest.java b/tests/soba/core/signature/TypeResolverTest.java
new file mode 100755 (executable)
index 0000000..aa7af35
--- /dev/null
@@ -0,0 +1,26 @@
+package soba.core.signature;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+\r
+import org.junit.Test;\r
+\r
+public class TypeResolverTest {\r
+\r
+       @Test\r
+       public void testGetTypeName() {\r
+               assertThat(TypeResolver.getTypeName("Z"), is("boolean"));\r
+               assertThat(TypeResolver.getTypeName("B"), is("byte"));\r
+               assertThat(TypeResolver.getTypeName("C"), is("char"));\r
+               assertThat(TypeResolver.getTypeName("S"), is("short"));\r
+               assertThat(TypeResolver.getTypeName("I"), is("int"));\r
+               assertThat(TypeResolver.getTypeName("J"), is("long"));\r
+               assertThat(TypeResolver.getTypeName("F"), is("float"));\r
+               assertThat(TypeResolver.getTypeName("D"), is("double"));\r
+               assertThat(TypeResolver.getTypeName("V"), is("void"));\r
+               assertThat(TypeResolver.getTypeName("Ljava/lang/String;"), is("java/lang/String"));\r
+               assertThat(TypeResolver.getTypeName("[I"), is("int[]"));\r
+               assertThat(TypeResolver.getTypeName("[[[I"), is("int[][][]"));\r
+       }\r
+\r
+}\r
diff --git a/tests/soba/core/vta/CallSiteVerticesTest.java b/tests/soba/core/vta/CallSiteVerticesTest.java
new file mode 100755 (executable)
index 0000000..b56ebb0
--- /dev/null
@@ -0,0 +1,94 @@
+package soba.core.vta;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+\r
+import org.junit.BeforeClass;\r
+import org.junit.Test;\r
+\r
+import soba.core.ClassInfo;\r
+import soba.core.JavaProgram;\r
+import soba.core.JavaProgramTest;\r
+import soba.core.MethodInfo;\r
+import soba.core.method.CallSite;\r
+import soba.core.vta.CallSiteVertices;\r
+\r
+public class CallSiteVerticesTest {\r
+\r
+       private static JavaProgram program;\r
+       private static MethodInfo m;\r
+       private static CallSiteVertices v;\r
+       private static final int startID = 1;\r
+       \r
+       @BeforeClass\r
+       public static void setUpBeforeClass() throws Exception {\r
+               program = JavaProgramTest.readExampleProgram();\r
+               ClassInfo c = program.getClassInfo("soba/testdata/ObjectTransferCode");\r
+               m = c.findMethod("newObject", "(I)[[I");\r
+               CallSite callSite = null;\r
+               for (int i = 0; i < m.getInstructionCount(); i++) {\r
+                       CallSite cs = m.getCallSite(i);\r
+                       if (cs != null && cs.getMethodName().equals("m2")) {\r
+                               callSite = cs;\r
+                               break;\r
+                       }\r
+               }\r
+               v = new CallSiteVertices(callSite, startID);\r
+       }\r
+\r
+//     @Test\r
+//     public void testGetCallSite() {\r
+//             fail("Not yet implemented");\r
+//     }\r
+\r
+       @Test\r
+       public void testIsObjectParam() {\r
+               assertThat(v.isObjectParam(0), is(true));\r
+               assertThat(v.isObjectParam(1), is(true));\r
+       }\r
+\r
+       @Test\r
+       public void testGetParamVertexId() {\r
+               assertThat(v.getParamVertexId(0), is(startID));\r
+               assertThat(v.getParamVertexId(1), is(startID + 1));\r
+       }\r
+\r
+       @Test\r
+       public void testGetParamCount() {\r
+               assertThat(v.getParamCount(), is(2));\r
+       }\r
+\r
+       @Test\r
+       public void testHasReturnValue() {\r
+               assertThat(v.hasReturnValue(), is(true));\r
+       }\r
+\r
+       @Test\r
+       public void testGetReturnValueVertex() {\r
+               assertThat(v.getReturnValueVertex(), is(startID + 2));\r
+       }\r
+\r
+       @Test\r
+       public void testGetVertexCount() {\r
+               assertThat(v.getVertexCount(), is(3));\r
+       }\r
+\r
+       @Test\r
+       public void testGetVertex() {\r
+               assertThat(v.getVertex(0), is(startID));\r
+               assertThat(v.getVertex(1), is(startID + 1));\r
+               assertThat(v.getVertex(2), is(startID + 2));\r
+       }\r
+\r
+       @Test\r
+       public void testGetTypeName() {\r
+               assertThat(v.getTypeName(0), is("soba/testdata/ObjectTransferCode"));\r
+               assertThat(v.getTypeName(1), is("soba/testdata/ObjectTransferCode1"));\r
+       }\r
+\r
+       @Test\r
+       public void testGetReturnValueTypeName() {\r
+               assertThat(v.getReturnValueTypeName(), is("soba/testdata/ObjectTransferCode"));\r
+       }\r
+\r
+}\r
diff --git a/tests/soba/core/vta/MethodVerticesTest.java b/tests/soba/core/vta/MethodVerticesTest.java
new file mode 100755 (executable)
index 0000000..853380d
--- /dev/null
@@ -0,0 +1,98 @@
+package soba.core.vta;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+\r
+import org.junit.BeforeClass;\r
+import org.junit.Test;\r
+import org.objectweb.asm.Opcodes;\r
+import org.objectweb.asm.tree.InsnList;\r
+\r
+import soba.core.ClassInfo;\r
+import soba.core.JavaProgram;\r
+import soba.core.JavaProgramTest;\r
+import soba.core.MethodInfo;\r
+import soba.core.vta.MethodVertices;\r
+import soba.core.vta.VTAResolver;\r
+\r
+public class MethodVerticesTest {\r
+\r
+       private static JavaProgram program;\r
+       private static MethodInfo m;\r
+       private static MethodVertices v;\r
+       private static final int startID = 1;\r
+       \r
+       @BeforeClass\r
+       public static void setUpBeforeClass() throws Exception {\r
+               program = JavaProgramTest.readExampleProgram();\r
+               ClassInfo c = program.getClassInfo("soba/testdata/ObjectTransferCode");\r
+               m = c.findMethod("newObject", "(I)[[I");\r
+               v = new MethodVertices(m, m.getDataDependence().getLocalVariables(), startID);\r
+       }\r
+\r
+       @Test\r
+       public void testGetLocalVertex() {\r
+               int storeCount = 0;\r
+               int loadCount = 0;\r
+               InsnList instructions = m.getMethodNode().instructions;\r
+               for (int i = 0; i < instructions.size(); i++) {\r
+                       if (instructions.get(i).getOpcode() == Opcodes.ASTORE) {\r
+                               if (storeCount <= 2) {\r
+                                       assertThat(v.getLocalVertex(i), is(startID + storeCount + 1));\r
+                               } else if (storeCount == 3) {\r
+                                       assertThat(v.getLocalVertex(i), is(startID + 6));\r
+                               } else {\r
+                                       assertThat(v.getLocalVertex(i), is(startID + storeCount));\r
+                               }\r
+                               storeCount++;\r
+                       } else if (instructions.get(i).getOpcode() == Opcodes.ALOAD) {\r
+                               if (loadCount == 0) {\r
+                                       assertThat(v.getLocalVertex(i), is(startID + 1));\r
+                               } else if (loadCount == 1 || loadCount == 3) {\r
+                                       assertThat(v.getLocalVertex(i), is(startID));\r
+                               } else if (loadCount == 2) {\r
+                                       assertThat(v.getLocalVertex(i), is(startID + 2));\r
+                               } else {\r
+                                       assertThat(v.getLocalVertex(i), is(startID + loadCount - 1));\r
+                               }\r
+                               loadCount++;\r
+                       } else {\r
+                               assertThat(v.getLocalVertex(i), is(VTAResolver.VERTEX_ERROR));\r
+                       }\r
+               }\r
+       }\r
+\r
+       @Test\r
+       public void testGetReturnVertex() {\r
+               assertThat(v.getReturnVertex(), is(startID + 7));\r
+       }\r
+\r
+       @Test\r
+       public void testGetFormalVertex() {\r
+               assertThat(v.getFormalVertex(0), is(startID));\r
+       }\r
+\r
+       @Test\r
+       public void testHasFormalVertex() {\r
+               assertThat(v.hasFormalVertex(0), is(true));\r
+               assertThat(v.hasFormalVertex(1), is(false));\r
+       }\r
+\r
+       @Test\r
+       public void testGetVertexCount() {\r
+               assertThat(v.getVertexCount(), is(8));\r
+       }\r
+\r
+       @Test\r
+       public void testGetTypeName() {\r
+               assertThat(v.getTypeName(0), is("soba/testdata/ObjectTransferCode"));\r
+               assertThat(v.getTypeName(1), is("soba/testdata/ObjectTransferCode"));\r
+               assertThat(v.getTypeName(2), is("soba/testdata/ObjectTransferCode"));\r
+               assertThat(v.getTypeName(3), is("soba/testdata/ObjectTransferCode"));\r
+               assertThat(v.getTypeName(4), is("soba/testdata/ObjectTransferCode[]"));\r
+               assertThat(v.getTypeName(5), is("int[][]"));\r
+               assertThat(v.getTypeName(6), is("java/lang/Object"));\r
+               assertThat(v.getTypeName(7), is("int[][]"));\r
+}\r
+\r
+}\r
diff --git a/tests/soba/core/vta/NewVerticesTest.java b/tests/soba/core/vta/NewVerticesTest.java
new file mode 100755 (executable)
index 0000000..21a2d27
--- /dev/null
@@ -0,0 +1,70 @@
+package soba.core.vta;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+\r
+import org.junit.BeforeClass;\r
+import org.junit.Test;\r
+import org.objectweb.asm.Opcodes;\r
+import org.objectweb.asm.tree.InsnList;\r
+\r
+import soba.core.ClassInfo;\r
+import soba.core.JavaProgram;\r
+import soba.core.JavaProgramTest;\r
+import soba.core.MethodInfo;\r
+import soba.core.vta.NewVertices;\r
+import soba.core.vta.VTAResolver;\r
+\r
+public class NewVerticesTest {\r
+\r
+       private static JavaProgram program;\r
+       private static MethodInfo m;\r
+       private static NewVertices v;\r
+       private static final int startID = 1;\r
+       \r
+       @BeforeClass\r
+       public static void setUpBeforeClass() throws Exception {\r
+               program = JavaProgramTest.readExampleProgram();\r
+               ClassInfo c = program.getClassInfo("soba/testdata/ObjectTransferCode");\r
+               m = c.findMethod("newObject", "(I)[[I");\r
+               v = new NewVertices(m.getMethodNode().instructions, startID);\r
+       }\r
+\r
+       @Test\r
+       public void testGetNewInstructionVertex() {\r
+               int count = startID;\r
+               InsnList instructions = m.getMethodNode().instructions;\r
+               for (int i = 0; i < instructions.size(); i++) {\r
+                       switch (instructions.get(i).getOpcode()) {\r
+                       case Opcodes.NEW:\r
+                       case Opcodes.ANEWARRAY:\r
+                       case Opcodes.MULTIANEWARRAY:\r
+                               assertThat(v.getNewInstructionVertex(i), is(count));\r
+                               count++;\r
+                               break;\r
+                       default:\r
+                               assertThat(v.getNewInstructionVertex(i), is(VTAResolver.VERTEX_ERROR));\r
+                       }\r
+               }\r
+       }\r
+\r
+       @Test\r
+       public void testGetVertex() {\r
+               assertThat(v.getVertex(0), is(startID));\r
+               assertThat(v.getVertex(1), is(startID + 1));\r
+               assertThat(v.getVertex(2), is(startID + 2));\r
+       }\r
+\r
+       @Test\r
+       public void testGetTypeName() {\r
+               assertThat(v.getTypeName(0), is("soba/testdata/ObjectTransferCode"));\r
+               assertThat(v.getTypeName(1), is("soba/testdata/ObjectTransferCode[]"));\r
+               assertThat(v.getTypeName(2), is("int[][]"));\r
+       }\r
+\r
+       @Test\r
+       public void testGetVertexCount() {\r
+               assertThat(v.getVertexCount(), is(3));\r
+       }\r
+\r
+}\r
diff --git a/tests/soba/core/vta/VTAResolverTest.java b/tests/soba/core/vta/VTAResolverTest.java
new file mode 100755 (executable)
index 0000000..8565b64
--- /dev/null
@@ -0,0 +1,253 @@
+package soba.core.vta;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+\r
+import java.util.Arrays;\r
+import java.util.stream.Collectors;\r
+\r
+import org.junit.BeforeClass;\r
+import org.junit.Test;\r
+import org.objectweb.asm.Opcodes;\r
+import org.objectweb.asm.tree.InsnList;\r
+import org.objectweb.asm.tree.MethodInsnNode;\r
+\r
+import soba.core.ClassInfo;\r
+import soba.core.ExampleProgram;\r
+import soba.core.FieldInfo;\r
+import soba.core.JavaProgram;\r
+import soba.core.JavaProgramTest;\r
+import soba.core.MethodInfo;\r
+import soba.core.vta.IAnalysisTarget;\r
+import soba.core.vta.TypeSet;\r
+import soba.core.vta.VTAResolver;\r
+\r
+public class VTAResolverTest implements ExampleProgram {\r
+\r
+       private static JavaProgram program;\r
+       private static VTAResolver resolver;\r
+       \r
+       @BeforeClass\r
+       public static void setupResolver() {\r
+               program = JavaProgramTest.readExampleProgram();\r
+               resolver = new VTAResolver(program, new IAnalysisTarget(){\r
+                       @Override\r
+                       public boolean assumeExternalCallers(MethodInfo m) {\r
+                               return false;\r
+                       }\r
+                       @Override\r
+                       public boolean isExcludedType(String className) {\r
+                               return false;\r
+                       }\r
+                       @Override\r
+                       public boolean isTargetMethod(MethodInfo m) {\r
+                               return m.getClassName().startsWith("soba/testdata");\r
+                       }\r
+                       @Override\r
+                       public boolean isTargetField(FieldInfo f) {\r
+                               return true;\r
+                       }\r
+               });\r
+       }\r
+       \r
+       private void checkClasses(MethodInfo[] resolved, String... classNames) {\r
+               assertThat(Arrays.stream(resolved).map(m -> m.getClassName()).collect(Collectors.toList()), containsInAnyOrder(classNames));\r
+       }\r
+       \r
+       @Test\r
+       public void testResolveCall01() {\r
+               ClassInfo c = program.getClassInfo(CLASS_E);\r
+               MethodInfo m = c.findMethod("testDynamicBinding1", "()V");\r
+               InsnList instructions = m.getMethodNode().instructions;\r
+               int counter = 0;\r
+               for (int i=0; i<instructions.size(); ++i) {\r
+                       if (instructions.get(i).getOpcode() == Opcodes.INVOKEVIRTUAL) {\r
+                               if (counter == 0 || counter == 1) {\r
+                                       MethodInfo[] methods = resolver.resolveCall(m.getCallSite(i));\r
+                                       checkClasses(methods, CLASS_D, CLASS_G);\r
+                               }\r
+                               counter++;\r
+                       }\r
+               }\r
+               assertThat(counter, is(2));\r
+               \r
+       }\r
+\r
+       @Test\r
+       public void testResolveCall02() {\r
+               ClassInfo c = program.getClassInfo("soba/testdata/inheritance2/E");\r
+               MethodInfo m = c.findMethod("testDynamicBinding2", "()V");\r
+               InsnList instructions = m.getMethodNode().instructions;\r
+               int counter = 0;\r
+               for (int i=0; i<instructions.size(); ++i) {\r
+                       if (instructions.get(i).getOpcode() == Opcodes.INVOKEVIRTUAL) {\r
+                               if (counter == 0) {\r
+                                       MethodInfo[] methods = resolver.resolveCall(m.getCallSite(i));\r
+                                       checkClasses(methods, CLASS_D);\r
+                               } else if (counter == 1) {\r
+                                       MethodInfo[] methods = resolver.resolveCall(m.getCallSite(i));\r
+                                       checkClasses(methods, CLASS_G);\r
+                               }\r
+                               counter++;\r
+                       }\r
+               }\r
+               assertThat(counter, is(2));\r
+               \r
+       }\r
+\r
+       /**\r
+        * Inherited but not overridden methods\r
+        */\r
+       @Test\r
+       public void testResolvedCall03() {\r
+               ClassInfo c = program.getClassInfo(CLASS_E);\r
+               MethodInfo m = c.findMethod("testDynamicBinding3", "()V");\r
+               InsnList instructions = m.getMethodNode().instructions;\r
+               int counter = 0;\r
+               for (int i=0; i<instructions.size(); ++i) {\r
+                       if (instructions.get(i).getOpcode() == Opcodes.INVOKEVIRTUAL) {\r
+                               if (counter == 0) {\r
+                                       MethodInfo[] methods = resolver.resolveCall(m.getCallSite(i));\r
+                                       checkClasses(methods, CLASS_C);\r
+                               } else if (counter == 1) {\r
+                                       MethodInfo[] methods = resolver.resolveCall(m.getCallSite(i));\r
+                                       checkClasses(methods, CLASS_C);\r
+                               }\r
+                               counter++;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Declared but not implemented method\r
+        */\r
+       @Test\r
+       public void testResolvedCall04() {\r
+               ClassInfo c = program.getClassInfo(CLASS_E);\r
+               MethodInfo m = c.findMethod("testDynamicBinding6", "(Lsoba/testdata/inheritance2/L;)V");\r
+               InsnList instructions = m.getMethodNode().instructions;\r
+               int counter = 0;\r
+               for (int i=0; i<instructions.size(); ++i) {\r
+                       if (instructions.get(i).getOpcode() == Opcodes.INVOKEVIRTUAL) {\r
+                               if (counter == 0) {\r
+                                       MethodInfo[] methods = resolver.resolveCall(m.getCallSite(i));\r
+                                       assertThat(methods, is(emptyArray()));\r
+                               } else if (counter == 1) {\r
+                                       MethodInfo[] methods = resolver.resolveCall(m.getCallSite(i));\r
+                                       checkClasses(methods, CLASS_C, CLASS_D, CLASS_G);\r
+                               }\r
+                               counter++;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Resolves library method calls.\r
+        */\r
+       @Test\r
+       public void testResolveCall05() {\r
+               ClassInfo c = program.getClassInfo(CLASS_E);\r
+               MethodInfo m = c.findMethod("testDynamicBinding7", "(Z)V");\r
+               InsnList instructions = m.getMethodNode().instructions;\r
+               for (int i = 0; i < instructions.size(); i++) {\r
+                       if (instructions.get(i).getOpcode() == Opcodes.INVOKEVIRTUAL) {\r
+                               MethodInfo[] methods = resolver.resolveCall(m.getCallSite(i));\r
+                               checkClasses(methods, "java/util/ArrayList", "java/util/LinkedList");\r
+                       }\r
+               }\r
+       }\r
+       \r
+       private void checkLoopBinding(MethodInfo m) {\r
+               InsnList instructions = m.getMethodNode().instructions;\r
+               for (int i=0; i<instructions.size(); ++i) {\r
+                       if (instructions.get(i).getOpcode() == Opcodes.INVOKEVIRTUAL) {\r
+                               MethodInsnNode call = (MethodInsnNode)instructions.get(i);\r
+                               if (call.name.equals("x")) {\r
+                                       MethodInfo[] methods = resolver.resolveCall(m.getCallSite(i));\r
+                                       checkClasses(methods, CLASS_D, CLASS_G);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       private void checkParamBinding(MethodInfo m) {\r
+               TypeSet typeset = resolver.getMethodParamType(m, 1);\r
+               assertThat(typeset.getTypeCount(), is(2));\r
+       }\r
+       \r
+       @Test\r
+       public void testLoopBinding() {\r
+               ClassInfo c = program.getClassInfo(CLASS_E);\r
+               checkLoopBinding(c.findMethod("testDynamicBinding4", "(Lsoba/testdata/inheritance1/C;)V"));\r
+               checkLoopBinding(c.findMethod("testDynamicBinding5", "(Lsoba/testdata/inheritance1/C;)V"));\r
+       }\r
+       \r
+       @Test\r
+       public void testMethodParamType() {\r
+               ClassInfo c = program.getClassInfo(CLASS_E);\r
+               checkParamBinding(c.findMethod("testDynamicBinding4", "(Lsoba/testdata/inheritance1/C;)V"));\r
+               checkParamBinding(c.findMethod("testDynamicBinding5", "(Lsoba/testdata/inheritance1/C;)V"));\r
+       }\r
+\r
+       @Test\r
+       public void testgetReceiverTypeAtCallsite() {\r
+               ClassInfo c = program.getClassInfo(CLASS_C);\r
+               MethodInfo m = c.findMethod("main", "([Ljava/lang/String;)V");\r
+               InsnList instructions = m.getMethodNode().instructions;\r
+               int count = 0;\r
+               for (int i = 0; i < instructions.size(); i++) {\r
+                       if (instructions.get(i).getOpcode() == Opcodes.INVOKEVIRTUAL) {\r
+                               if (count == 0) {\r
+                                       TypeSet typeSet = resolver.getReceiverTypeAtCallsite(m, i);\r
+                                       assertThat(typeSet.getTypeCount(), is(0));\r
+                               } else {\r
+                                       TypeSet typeSet = resolver.getReceiverTypeAtCallsite(m, i);\r
+                                       assertThat(typeSet.getTypeCount(), is(1));\r
+                               }\r
+                               count++;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       @Test\r
+       public void testReflection() {\r
+               ClassInfo c = program.getClassInfo("soba/testdata/ReflectionCode");\r
+               MethodInfo m = c.findMethod("newInstanceUser", "()V");\r
+               InsnList instructions = m.getMethodNode().instructions;\r
+               int counter = 0;\r
+               for (int i=0; i<instructions.size(); ++i) {\r
+                       if (instructions.get(i).getOpcode() == Opcodes.INVOKEVIRTUAL) {\r
+                               MethodInsnNode call = (MethodInsnNode)instructions.get(i);\r
+                               if (call.name.equals("toString")) {\r
+                                       MethodInfo[] methods = resolver.resolveCall(m.getCallSite(i));\r
+                                       counter++;\r
+                                       if (counter == 1) {\r
+                                               checkClasses(methods, CLASS_C, CLASS_D);\r
+                                       } else {\r
+                                               checkClasses(methods, CLASS_D);\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               assertThat(counter, is(2));\r
+       }\r
+       \r
+       @Test\r
+       public void testReflection2() {\r
+               ClassInfo c = program.getClassInfo("soba/testdata/ReflectionCode");\r
+               MethodInfo m = c.findMethod("newInstanceUser2", "()V");\r
+               InsnList instructions = m.getMethodNode().instructions;\r
+               int counter = 0;\r
+               for (int i=0; i<instructions.size(); ++i) {\r
+                       if (instructions.get(i).getOpcode() == Opcodes.INVOKEVIRTUAL) {\r
+                               MethodInsnNode call = (MethodInsnNode)instructions.get(i);\r
+                               if (call.name.equals("toString")) {\r
+                                       MethodInfo[] methods = resolver.resolveCall(m.getCallSite(i));\r
+                                       assertThat(methods.length, is(greaterThan(1))); // at least C and D implements toString.\r
+                                       counter++;\r
+                               }\r
+                       }\r
+               }\r
+               assertThat(counter, is(1));\r
+       }\r
+}\r
diff --git a/tests/soba/testdata/ControlDependenceCode.java b/tests/soba/testdata/ControlDependenceCode.java
new file mode 100755 (executable)
index 0000000..1890a8f
--- /dev/null
@@ -0,0 +1,34 @@
+package soba.testdata;\r
+\r
+public class ControlDependenceCode {\r
+\r
+       /**\r
+        * This code fragment causes a loop of control dependency. \r
+        */\r
+       public static void main(String[] args) {\r
+               int x = 0;\r
+               for (String s : args) { // controls "if" statements in the loop.\r
+                       if (s == null) {\r
+                               continue;\r
+                       }\r
+                       if (s.length() == 1) { // Because this goes to the exit of this method, this statement controls the enclosing "for" statement. \r
+                               test();\r
+                               return;\r
+                       } else if (s.length() == 2) {\r
+                               x = 2;\r
+                               continue;\r
+                       } else if (s.length() == 3) {\r
+                               x = 3;\r
+                       }\r
+               }\r
+               use(x);\r
+       }\r
+       \r
+       private static void use(int x) {\r
+               \r
+       }\r
+       \r
+       private static void test() {\r
+               \r
+       }\r
+}\r
diff --git a/tests/soba/testdata/DefUseTestData.java b/tests/soba/testdata/DefUseTestData.java
new file mode 100755 (executable)
index 0000000..14af33a
--- /dev/null
@@ -0,0 +1,146 @@
+package soba.testdata;\r
+\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.IOException;\r
+\r
+/**\r
+ * This class itself has no functionality. \r
+ * The code is used only for testing.\r
+ * @author ishio\r
+ */\r
+public class DefUseTestData {\r
+\r
+       \r
+       public void overwriteParam(int x, int y) {\r
+               if (x == 0) y = 1;\r
+               System.out.println(y);\r
+       }\r
+       \r
+       public void localDataDependence() {\r
+               int x;\r
+               boolean b = true;\r
+               if (b) {\r
+                       x = 1;\r
+               } else {\r
+                       x = 2;\r
+                       System.err.println(x);  // x = 2\r
+                       x = 3;\r
+               }\r
+               System.err.println(x); // x = 1 or 3\r
+               System.err.println(x); // x = 1 or 3\r
+       }\r
+\r
+       /**\r
+        * The method body has no meaning; this code is to use a finally block.\r
+        * @return\r
+        */\r
+       public int tryFinallyDependence() {\r
+               int x = 0;\r
+               File f = new File("test");\r
+               FileInputStream stream = null;\r
+               try {\r
+                       stream = new FileInputStream(f);\r
+                       x = stream.read();\r
+                       if (x == 0) {\r
+                               return x;\r
+                       } else {\r
+                               return x+1;\r
+                       }\r
+               } catch (IOException e) {\r
+                       e.printStackTrace();\r
+               } finally {\r
+                       try {\r
+                               if (stream != null) stream.close();\r
+                       } catch (IOException e) {\r
+                       }\r
+               }\r
+               return 0;\r
+       }\r
+       \r
+       public int forStatement() {\r
+               int x = 0;\r
+               for (int i=0; i<100; i++) {\r
+                       x += i;\r
+                       System.out.println(i);\r
+                       if (i / 80 == 1) return x;\r
+                       System.out.println(x);\r
+               }\r
+               return x;\r
+       }\r
+\r
+       /**\r
+        * The same implementation as forStatement.\r
+        */\r
+       public int whileStatement() {\r
+               int x = 0;\r
+               int i = 0;\r
+               while (i<100) {\r
+                       x += i;\r
+                       System.out.println(i);\r
+                       if (i / 80 == 1) return x;\r
+                       System.out.println(x);\r
+                       i++;\r
+               }\r
+               return x;\r
+       }\r
+       \r
+       public void withInnerClass() {\r
+               \r
+               final int k = 100;\r
+               \r
+               final Inner1 inner1 = new Inner1() {\r
+                       \r
+                       private int x = k;\r
+                       private int y = 100;\r
+                       \r
+                       public void printInner() {\r
+                               System.out.println(x);\r
+                               System.out.println(y);\r
+                       }\r
+                       \r
+                       @Override\r
+                       public void print() {\r
+                               printInner();\r
+                       }\r
+               };\r
+               \r
+               Inner2 inner2 = new Inner2(inner1) {\r
+\r
+                       private int x = k;\r
+                       private Inner3 i = new Inner3(new Inner1());\r
+\r
+                       public void print() {\r
+                               System.out.println(x);\r
+                               System.out.println(i);\r
+                       }\r
+               };\r
+               inner2.print();\r
+               \r
+       }\r
+       \r
+       class Inner1 {\r
+               \r
+               public Inner1() {\r
+               }\r
+               \r
+               public void print() {\r
+               }\r
+       }\r
+       \r
+       class Inner2 {\r
+               private Inner1 arg;\r
+               public Inner2(Inner1 arg) {\r
+                       this.arg = arg;\r
+               }\r
+               public void print() {\r
+                       arg.print();\r
+               }\r
+\r
+               class Inner3 {\r
+                       public Inner3(Inner1 arg) {\r
+                               \r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/tests/soba/testdata/ObjectTransferCode.java b/tests/soba/testdata/ObjectTransferCode.java
new file mode 100755 (executable)
index 0000000..d3767ea
--- /dev/null
@@ -0,0 +1,26 @@
+package soba.testdata;\r
+\r
+public class ObjectTransferCode {\r
+\r
+       public int[][] newObject(int x) {\r
+               ObjectTransferCode obj1 = new ObjectTransferCode();\r
+               ObjectTransferCode obj2, obj3;\r
+               obj2 = obj1;\r
+               obj3 = null;\r
+               m1(obj2);\r
+               obj1 = m2(obj3);\r
+               ObjectTransferCode[] array = new ObjectTransferCode[1];\r
+               int[][] multiArray = new int[1][1];\r
+               System.out.println(array.length);\r
+//             System.out.println(multiArray.length);\r
+               return multiArray;\r
+       }\r
+       \r
+       public void m1(ObjectTransferCode obj) {\r
+               \r
+       }\r
+       \r
+       public ObjectTransferCode m2(ObjectTransferCode obj) {\r
+               return obj;\r
+       }\r
+}\r
diff --git a/tests/soba/testdata/ReflectionCode.java b/tests/soba/testdata/ReflectionCode.java
new file mode 100755 (executable)
index 0000000..65d57b4
--- /dev/null
@@ -0,0 +1,33 @@
+package soba.testdata;\r
+\r
+\r
+import soba.testdata.inheritance1.D;\r
+\r
+public class ReflectionCode {\r
+\r
+       \r
+       public void newInstanceUser() {\r
+               Class<?> c = D.class;\r
+               try {\r
+                       Object o = c.newInstance();\r
+                       System.out.print(o.toString());\r
+                       \r
+                       D obj = (D)o;\r
+                       System.out.print(obj.toString());\r
+               } catch (IllegalAccessException e) {\r
+               } catch (InstantiationException e) {\r
+               }\r
+       }\r
+\r
+       public void newInstanceUser2() {\r
+               Class<?> c = D.class;\r
+               try {\r
+                       Object o = c.newInstance();\r
+                       System.out.print(o.toString());\r
+               } catch (IllegalAccessException e) {\r
+               } catch (InstantiationException e) {\r
+               }\r
+       }\r
+       \r
+\r
+}\r
diff --git a/tests/soba/testdata/StatementUnitsData.java b/tests/soba/testdata/StatementUnitsData.java
new file mode 100755 (executable)
index 0000000..394f6d9
--- /dev/null
@@ -0,0 +1,123 @@
+package soba.testdata;\r
+\r
+public class StatementUnitsData {\r
+\r
+       public void f1(boolean b) {\r
+               int i = b ? 1: 0;\r
+               System.out.println(i);\r
+       }\r
+       \r
+       public void f2(boolean b) {\r
+               int i = b ? 1: 0;\r
+               System.out.println(i == 0 ? true : false);\r
+       }\r
+\r
+       public void f3(boolean b1, boolean b2) {\r
+               boolean i = b1 && b2 ? b2 : (b1 || b2);\r
+               System.out.println(i);\r
+       }\r
+\r
+       public void if1(boolean b1, boolean b2) {\r
+               if (b1 && b2) {\r
+                       System.out.println(b1);\r
+               }\r
+       }\r
+\r
+       public void if1nest(boolean b1, boolean b2) {\r
+               if (b1) {\r
+                       if (b2) {\r
+                               System.out.println(b1);\r
+                       }\r
+               }\r
+       }\r
+\r
+       public void if2(boolean b1, boolean b2) {\r
+               if (b1 || b2) {\r
+                       System.out.println(b1);\r
+               }\r
+       }\r
+\r
+       public void if3(boolean b1, boolean b2) {\r
+               if ((b1 && !b2) || (!b1 && b2)) {\r
+                       System.out.println(b1);\r
+               }\r
+       }\r
+\r
+       public void if4(boolean b1, boolean b2) {\r
+               if ((b1 || !b2) && (!b1 || b2)) {\r
+                       System.out.println(b1);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * This method has exactly the same bytecode as if4. (in Eclipse 3.8.0)\r
+        */\r
+       public void if4nest(boolean b1, boolean b2) {\r
+               if (b1 || !b2) { if (!b1 || b2) {\r
+                               System.out.println(b1);\r
+               } }\r
+       }\r
+\r
+       public void g1(boolean b1, boolean b2) {\r
+               if ((b1 || b2) ? b1 : b2) {\r
+                       System.out.println(b1);\r
+               }\r
+       }\r
+\r
+       public void g1nest(boolean b1, boolean b2) {\r
+               if ((b1 || b2)) {\r
+                       if (b1) {\r
+                               System.out.println(b1);\r
+                       }\r
+               } else {\r
+                       if (b2) {\r
+                               System.out.println(b1);\r
+                       }\r
+               }\r
+       }\r
+\r
+       public void g2(boolean b1, boolean b2, boolean b3) {\r
+               boolean b = !(b1 && b2) || b3;\r
+               System.out.println(b);\r
+       }\r
+\r
+       public void h1(boolean b1, boolean b2, boolean b3) {\r
+               boolean b;\r
+               if (b1) {\r
+                       b = b2;\r
+               } else {\r
+                       b = b3;\r
+               }\r
+               System.out.println(b);\r
+       }\r
+\r
+       public void h2(boolean b1, boolean b2, boolean b3, boolean b4) {\r
+               boolean b;\r
+               if (b1 && b2) {\r
+                       b = b3;\r
+               } else {\r
+                       b = b4;\r
+               }\r
+               System.out.println(b);\r
+       }\r
+\r
+       public void h3(int x, int y, int z, boolean b) {\r
+               if ((x = (b ? y : z)) == 0 ) {\r
+                       System.out.println(b);\r
+               }\r
+       }\r
+\r
+       public void i1(Object obj, boolean x, boolean y) {\r
+               boolean b = (x && y) ? (obj == null) : false;\r
+               System.out.println(b);\r
+       }\r
+\r
+       public void i2(Object obj) {\r
+               boolean b = (obj == null);\r
+               System.out.println(b);\r
+       }\r
+\r
+       public interface I {\r
+               public void method(int x, int y);\r
+       }\r
+}\r
diff --git a/tests/soba/testdata/inheritance1/C.java b/tests/soba/testdata/inheritance1/C.java
new file mode 100755 (executable)
index 0000000..6b5301a
--- /dev/null
@@ -0,0 +1,58 @@
+package soba.testdata.inheritance1;\r
+\r
+import soba.testdata.inheritance2.F;\r
+\r
+public class C {\r
+\r
+       protected int x;\r
+       \r
+       public static void main(String[] args) {\r
+               System.err.println(J.x);\r
+               F f = new F();\r
+               C c = f;\r
+               c.o();\r
+               f.o();\r
+       }\r
+       \r
+       public C(int x) {\r
+               System.err.println("C.<init>");\r
+               q(0.0);\r
+       }\r
+\r
+       protected void m() {\r
+               System.err.println("C.m");\r
+       }\r
+       \r
+       void n() {\r
+               System.err.println("C.n()");\r
+       }\r
+       \r
+       void o() {\r
+               System.err.println("C.o()");\r
+       }\r
+       \r
+       protected void p(int x) {\r
+               System.out.println("C.p(int)");\r
+       }\r
+       \r
+       private void q(double d) {\r
+               System.out.println("C.q(double)");\r
+       }\r
+       \r
+       public static void novariables() {\r
+               \r
+       }\r
+       \r
+       public void x(int t) {\r
+               System.out.println("C.x(int)");\r
+       }\r
+       \r
+       public void y(int t) {\r
+               System.out.println("C.y(int)");\r
+       }\r
+       \r
+       @Override\r
+       public String toString() {\r
+               return super.toString();\r
+       }\r
+}\r
diff --git a/tests/soba/testdata/inheritance1/D.java b/tests/soba/testdata/inheritance1/D.java
new file mode 100755 (executable)
index 0000000..be6d70d
--- /dev/null
@@ -0,0 +1,60 @@
+package soba.testdata.inheritance1;\r
+\r
+import soba.testdata.inheritance2.F;\r
+import soba.testdata.inheritance2.H;\r
+\r
+public class D extends C implements I, K {\r
+\r
+       \r
+       public D() {\r
+               this(K.x);\r
+               System.err.println("D.<init>");\r
+       }\r
+       \r
+       public D(int x) {\r
+               super(x);\r
+               System.err.println("D.<init>(int)");\r
+       }\r
+       \r
+       public void m() {\r
+               super.m();\r
+               System.err.println("D.m");\r
+       }\r
+       \r
+       public void testPackagePrivate() {\r
+               C c = new C(0);\r
+               c.n();\r
+       }\r
+\r
+       public void testPackagePrivate2() {\r
+               C c = new F();\r
+               c.n();\r
+       }\r
+\r
+       public void testPackagePrivate3() {\r
+               C c = new G();\r
+               c.n();\r
+       }\r
+\r
+       public void testPackagePrivate4() {\r
+               C c = new H();\r
+               c.n();\r
+       }\r
+\r
+       public void n() {\r
+               System.err.println("D.n()");\r
+       }\r
+       \r
+       public int example(int i, long l, double d, String s) {\r
+               return i;\r
+       }\r
+       \r
+       public void x(int t) {\r
+               System.out.println("D.x(int)");\r
+       }\r
+\r
+       @Override\r
+       public String toString() {\r
+               return super.toString();\r
+       }\r
+}\r
diff --git a/tests/soba/testdata/inheritance1/G.java b/tests/soba/testdata/inheritance1/G.java
new file mode 100755 (executable)
index 0000000..1b34a8e
--- /dev/null
@@ -0,0 +1,43 @@
+package soba.testdata.inheritance1;\r
+\r
+public class G extends C {\r
+\r
+       public G() {\r
+               super(0);\r
+       }\r
+       \r
+       @Override\r
+       void n() {\r
+               super.n();\r
+               System.err.println("G.n()");\r
+       }\r
+       \r
+       public final void finalMethod() {\r
+               \r
+       }\r
+       \r
+       protected volatile int volatileField = 0;\r
+       public transient int transientField = 0;\r
+       private final int finalField = 0;\r
+       \r
+       \r
+       public class InternalG {\r
+               \r
+               public InternalG() {\r
+                       super();\r
+                       System.out.println();\r
+               }\r
+               \r
+               public void m() {\r
+                       finalMethod();\r
+                       n();\r
+                       System.out.println(finalField);\r
+               }\r
+               \r
+       }\r
+       \r
+       public void x(int t) {\r
+               System.out.println("G.x(int)");\r
+       }\r
+\r
+}\r
diff --git a/tests/soba/testdata/inheritance1/I.java b/tests/soba/testdata/inheritance1/I.java
new file mode 100755 (executable)
index 0000000..e7e476f
--- /dev/null
@@ -0,0 +1,7 @@
+package soba.testdata.inheritance1;\r
+\r
+public interface I {\r
+\r
+       public static final int x = 2;\r
+       public void m();\r
+}\r
diff --git a/tests/soba/testdata/inheritance1/J.java b/tests/soba/testdata/inheritance1/J.java
new file mode 100755 (executable)
index 0000000..45b558b
--- /dev/null
@@ -0,0 +1,6 @@
+package soba.testdata.inheritance1;\r
+\r
+public interface J {\r
+\r
+       public static final int x = 1;\r
+}\r
diff --git a/tests/soba/testdata/inheritance1/K.java b/tests/soba/testdata/inheritance1/K.java
new file mode 100755 (executable)
index 0000000..854e324
--- /dev/null
@@ -0,0 +1,5 @@
+package soba.testdata.inheritance1;\r
+\r
+public interface K extends I {\r
+\r
+}\r
diff --git a/tests/soba/testdata/inheritance2/E.java b/tests/soba/testdata/inheritance2/E.java
new file mode 100755 (executable)
index 0000000..93273ba
--- /dev/null
@@ -0,0 +1,105 @@
+package soba.testdata.inheritance2;\r
+\r
+import java.util.ArrayList;\r
+import java.util.LinkedList;\r
+import java.util.List;\r
+\r
+import soba.testdata.inheritance1.C;\r
+import soba.testdata.inheritance1.D;\r
+import soba.testdata.inheritance1.G;\r
+\r
+public class E {\r
+\r
+       public static void main(String[] args) {\r
+               new E().exec();\r
+       }\r
+       \r
+       public E() {\r
+               D d = new D(0);\r
+               d.m();\r
+       }\r
+       \r
+       public void testPackagePrivate() {\r
+               F f = new F();\r
+               f.n();\r
+       }\r
+\r
+\r
+       public void exec() {\r
+               System.err.println("Hello, World!");\r
+               D d = new D();\r
+               d.testPackagePrivate();\r
+               d.testPackagePrivate2();\r
+               d.testPackagePrivate3();\r
+               d.testPackagePrivate4();\r
+       }\r
+       \r
+       private int flag;\r
+       \r
+       /*\r
+        * Test case:\r
+        * def1 -> use1, use2\r
+        * def2 -> use2\r
+        * VTA judges both use1 and use2 may call D.x(int) and G.x(int)\r
+        */\r
+       public void testDynamicBinding1() {\r
+               C c = new D(0);   // def1\r
+               c.x(0);           // use1\r
+               if (flag != 0) { \r
+                       c = new G();  // def2\r
+               }\r
+               c.x(1);           // use2\r
+       }\r
+       \r
+       /*\r
+        * Test case:\r
+        * def1 -> use1\r
+        * def2 -> use2\r
+        * Our VTA implementation judges use1 call D.x(int), use2 calls G.x(int)\r
+        * This is because our VTA uses a flow-sensitive data-flow analysis \r
+        * to distinguish local variable entries.\r
+        */\r
+       public void testDynamicBinding2() {\r
+               C c = new D(0);   // def1 \r
+               c.x(0);           // use1\r
+               c = new G();      // def2\r
+               c.x(1);           // use2\r
+       }\r
+       \r
+       public void testDynamicBinding3() {\r
+               D d1 = new D(0);   // def1 \r
+               d1.y(1);           // use1 -- this invokes C.y() because D does not override C.y().\r
+               C d2 = new D(0);   // def2 \r
+               d2.y(1);           // use2\r
+       }\r
+\r
+       public void testDynamicBinding4(C c) {\r
+               if (c == null) {\r
+                       c = new D(0);\r
+               }\r
+               testDynamicBinding5(c);\r
+               c.x(0);\r
+       }\r
+\r
+       public void testDynamicBinding5(C c) {\r
+               if (c == null) {\r
+                       c = new G();\r
+               }\r
+               testDynamicBinding4(c);\r
+               c.x(1);\r
+       }\r
+       \r
+       public void testDynamicBinding6(L l) {\r
+               l.getC().x(0);  // There are no implementation of getC()\r
+       }\r
+\r
+       public void testDynamicBinding7(boolean b) {\r
+               List<String> list;\r
+               if (b) {\r
+                       list = new ArrayList<>();\r
+               } else {\r
+                       list = new LinkedList<>();\r
+               }\r
+               int size =list.size();\r
+       }\r
+}\r
diff --git a/tests/soba/testdata/inheritance2/F.java b/tests/soba/testdata/inheritance2/F.java
new file mode 100755 (executable)
index 0000000..5c0dbf1
--- /dev/null
@@ -0,0 +1,29 @@
+package soba.testdata.inheritance2;\r
+\r
+import soba.testdata.inheritance1.C;\r
+\r
+public class F extends C {\r
+\r
+       public F() {\r
+               super(0);\r
+       }\r
+       \r
+       /**\r
+        *  This method declaration does not override C.n()\r
+        *  because C.n() is package-private.\r
+        */\r
+       void n() {\r
+               System.err.println("F.n()");\r
+       }\r
+       \r
+       public void o() {\r
+               System.err.println("F.o()");\r
+       }\r
+       \r
+       public void k() {\r
+               String[][][] array = new String[10][20][30];\r
+               System.err.println(array.toString());\r
+               String[] another = new String[10];\r
+               System.err.println(another.toString());\r
+       }\r
+}\r
diff --git a/tests/soba/testdata/inheritance2/H.java b/tests/soba/testdata/inheritance2/H.java
new file mode 100755 (executable)
index 0000000..9e3b0d3
--- /dev/null
@@ -0,0 +1,27 @@
+package soba.testdata.inheritance2;\r
+\r
+import soba.testdata.inheritance1.D;\r
+\r
+public class H extends D {\r
+\r
+       protected int x;\r
+       \r
+       public H() {\r
+               p(1);\r
+               q(0.1);\r
+       }\r
+       \r
+       @Override\r
+       public void n() {\r
+               System.err.println("H.n()");\r
+       }\r
+       \r
+       protected void p(int x) {\r
+               System.out.println("C.p(int)");\r
+       }\r
+       \r
+       private void q(double d) {\r
+               System.out.println("C.q(double)");\r
+       }\r
+\r
+}\r
diff --git a/tests/soba/testdata/inheritance2/L.java b/tests/soba/testdata/inheritance2/L.java
new file mode 100755 (executable)
index 0000000..180c6a8
--- /dev/null
@@ -0,0 +1,9 @@
+package soba.testdata.inheritance2;\r
+\r
+import soba.testdata.inheritance1.C;\r
+\r
+public abstract class L {\r
+\r
+       public abstract C getC();\r
+       \r
+}\r
diff --git a/tests/soba/util/IntPairListTest.java b/tests/soba/util/IntPairListTest.java
new file mode 100755 (executable)
index 0000000..b320501
--- /dev/null
@@ -0,0 +1,181 @@
+package soba.util;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+\r
+import org.junit.Test;\r
+\r
+\r
+public class IntPairListTest {\r
+\r
+       @Test\r
+       public void testAdd() throws Exception {\r
+               IntPairList list = new IntPairList(2);\r
+               list.add(1, 2);\r
+               list.add(3, 4);\r
+               list.add(5, 6);\r
+               \r
+               assertThat(list.getFirstValue(0), is(1));\r
+               assertThat(list.getSecondValue(0), is(2));\r
+               assertThat(list.getFirstValue(1), is(3));\r
+               assertThat(list.getSecondValue(1), is(4));\r
+               assertThat(list.getFirstValue(2), is(5));\r
+               assertThat(list.getSecondValue(2), is(6));\r
+               assertThat(list.size(), is(3));\r
+       }\r
+\r
+       @Test\r
+       public void testSortByFirstValues() throws Exception {\r
+               IntPairList list = new IntPairList();\r
+               list.add(1, 1); // 1\r
+               list.add(2, 0); // 2\r
+               list.add(3, 0); // 3\r
+               list.add(4, 4); // 4\r
+               list.add(5, 5); // 5\r
+               list.add(6, 3); // 6\r
+               list.add(7, 2); // 7\r
+               list.add(8, 4); // 8\r
+               list.add(9, 1); // 9\r
+               list.add(0, 5); // 0\r
+               \r
+               list.sort();\r
+               \r
+               assertThat(list.getFirstValue(2), is(2));\r
+               assertThat(list.getSecondValue(2), is(0));\r
+               assertThat(list.getFirstValue(4), is(4));\r
+               assertThat(list.getSecondValue(4), is(4));\r
+               assertThat(list.getFirstValue(9), is(9));\r
+               assertThat(list.getSecondValue(9), is(1));\r
+       }\r
+\r
+\r
+       @Test\r
+       public void testSetValues() throws Exception {\r
+               IntPairList list = new IntPairList(2);\r
+               list.add(1, 2);\r
+               list.add(3, 4);\r
+               list.add(5, 6);\r
+               \r
+               list.setFirstValue(1, 10);\r
+               assertThat(list.getFirstValue(0), is(1));\r
+               assertThat(list.getFirstValue(1), is(10));\r
+               assertThat(list.getFirstValue(2), is(5));\r
+               assertThat(list.getSecondValue(0), is(2));\r
+               assertThat(list.getSecondValue(1), is(4));\r
+               assertThat(list.getSecondValue(2), is(6));\r
+               \r
+               list.setSecondValue(2, 27);\r
+               assertThat(list.getFirstValue(0), is(1));\r
+               assertThat(list.getFirstValue(1), is(10));\r
+               assertThat(list.getFirstValue(2), is(5));\r
+               assertThat(list.getSecondValue(0), is(2));\r
+               assertThat(list.getSecondValue(1), is(4));\r
+               assertThat(list.getSecondValue(2), is(27));\r
+       }\r
+       \r
+       @Test\r
+       public void testAddAll() { \r
+               IntPairList list = new IntPairList(2);\r
+               list.add(1, 2);\r
+               list.add(3, 4);\r
+               IntPairList another = new IntPairList(2);\r
+               another.add(5, 6);\r
+               another.add(7, 8);\r
+               \r
+               list.addAll(another);\r
+               assertThat(list.size(), is(4));\r
+               assertThat(list.getFirstValue(0), is(1));\r
+               assertThat(list.getSecondValue(0), is(2));\r
+               assertThat(list.getFirstValue(1), is(3));\r
+               assertThat(list.getSecondValue(1), is(4));\r
+               assertThat(list.getFirstValue(2), is(5));\r
+               assertThat(list.getSecondValue(2), is(6));\r
+               assertThat(list.getFirstValue(3), is(7));\r
+               assertThat(list.getSecondValue(3), is(8));\r
+               assertThat(another.size(), is(2));\r
+\r
+               another.addAll(another);\r
+               assertThat(another.size(), is(4));\r
+               assertThat(another.getFirstValue(0), is(5));\r
+               assertThat(another.getSecondValue(0), is(6));\r
+               assertThat(another.getFirstValue(1), is(7));\r
+               assertThat(another.getSecondValue(1), is(8));\r
+               assertThat(another.getFirstValue(2), is(5));\r
+               assertThat(another.getSecondValue(2), is(6));\r
+               assertThat(another.getFirstValue(3), is(7));\r
+               assertThat(another.getSecondValue(3), is(8));\r
+       }\r
+       \r
+       @Test\r
+       public void testFreeze() {\r
+               IntPairList list = new IntPairList(2);\r
+               list.add(1, 2);\r
+               list.freeze();\r
+               try {\r
+                       list.add(3, 4);\r
+                       fail();\r
+               } catch (IntPairList.FrozenListException e) {\r
+               }\r
+               try {\r
+                       list.setFirstValue(0, 1);\r
+                       fail();\r
+               } catch (IntPairList.FrozenListException e) {\r
+               }\r
+               try {\r
+                       list.addAll(list);\r
+                       fail();\r
+               } catch (IntPairList.FrozenListException e) {\r
+               }\r
+       }\r
+       \r
+       @Test\r
+       public void testArrayIndexOutOfBounds() {\r
+               IntPairList list = new IntPairList(2);\r
+               list.add(1, 2);\r
+               try {\r
+                       list.getFirstValue(-1);\r
+                       fail();\r
+               } catch (ArrayIndexOutOfBoundsException e) {\r
+               }\r
+               try {\r
+                       list.getFirstValue(1);\r
+                       fail();\r
+               } catch (ArrayIndexOutOfBoundsException e) {\r
+               }\r
+               try {\r
+                       list.getSecondValue(-1);\r
+                       fail();\r
+               } catch (ArrayIndexOutOfBoundsException e) {\r
+               }\r
+               try {\r
+                       list.getSecondValue(1);\r
+                       fail();\r
+               } catch (ArrayIndexOutOfBoundsException e) {\r
+               }\r
+       }\r
+       \r
+       @Test\r
+       public void testForEach() {\r
+               IntPairList list = new IntPairList(2);\r
+               list.add(1, 2);\r
+               list.add(3, 4);\r
+               list.add(5, 6);\r
+               list.foreach(new IntPairProc() {\r
+                       int times = 0;\r
+                       @Override\r
+                       public boolean execute(int elem1, int elem2) {\r
+                               if (times == 0) {\r
+                                       assertThat(elem1, is(1));\r
+                                       assertThat(elem2, is(2));\r
+                               } else if (times == 1) {\r
+                                       assertThat(elem1, is(3));\r
+                                       assertThat(elem2, is(4));\r
+                               } else {\r
+                                       fail();\r
+                               }\r
+                               times++;\r
+                               return times == 1;\r
+                       }\r
+               });\r
+       }\r
+}\r
diff --git a/tests/soba/util/IntPairSetTest.java b/tests/soba/util/IntPairSetTest.java
new file mode 100755 (executable)
index 0000000..772ab78
--- /dev/null
@@ -0,0 +1,88 @@
+package soba.util;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+\r
+import org.junit.Test;\r
+\r
+public class IntPairSetTest {\r
+\r
+       @Test\r
+       public void testIntPairSet() {\r
+               IntPairSet set = new IntPairSet();\r
+               assertThat(set.size(), is(0));\r
+               set.add(0, 1);\r
+               assertThat(set.size(), is(1));\r
+               set.add(3, 2);\r
+               assertThat(set.size(), is(2));\r
+               assertThat(set.contains(0, 1), is(true));\r
+               assertThat(set.contains(3, 2), is(true));\r
+               assertThat(set.contains(0, 2), is(false));\r
+               set.add(1, 2);\r
+               assertThat(set.size(), is(3));\r
+               set.add(0, 1);\r
+               assertThat(set.size(), is(3));\r
+               assertThat(set.contains(0, 1), is(true));\r
+               assertThat(set.contains(1, 2), is(true));\r
+               assertThat(set.contains(3, 2), is(true));\r
+       }\r
+\r
+       private boolean visited01 = false;\r
+       private boolean visited12 = false;\r
+       private boolean visited14 = false;\r
+       private boolean visited32 = false;\r
+       private boolean visited41 = false;\r
+       \r
+       @Test\r
+       public void testForEach() {\r
+               IntPairSet set = new IntPairSet();\r
+               set.add(4, 1);\r
+               set.add(3, 2);\r
+               set.add(1, 4);\r
+               set.add(1, 2);\r
+               set.add(0, 1);\r
+               set.foreach(new IntPairProc() {\r
+                       \r
+                       @Override\r
+                       public boolean execute(int elem1, int elem2) {\r
+                               switch (elem1) {\r
+                               case 0: \r
+                                       assertThat(elem2, is(1));\r
+                                       assertThat(visited01, is(false));\r
+                                       visited01 = true;\r
+                                       break;\r
+                               case 1:\r
+                                       if (elem2 == 2) {\r
+                                               assertThat(visited12, is(false));\r
+                                               visited12 = true;\r
+                                       } else if (elem2 == 4) {\r
+                                               assertThat(visited14, is(false));\r
+                                               visited14 = true;\r
+                                       } else {\r
+                                               fail();\r
+                                       }\r
+                                       break;\r
+                               case 3:\r
+                                       assertThat(elem2, is(2));\r
+                                       assertThat(visited32, is(false));\r
+                                       visited32 = true;\r
+                                       break;\r
+                               case 4:\r
+                                       assertThat(elem2, is(1));\r
+                                       assertThat(visited41, is(false));\r
+                                       visited41 = true;\r
+                                       break;\r
+                               default:\r
+                                       fail();\r
+                               }\r
+                               return true;\r
+                       }\r
+               });\r
+               assertThat(visited01, is(true));\r
+               assertThat(visited12, is(true));\r
+               assertThat(visited14, is(true));\r
+               assertThat(visited32, is(true));\r
+               assertThat(visited41, is(true));\r
+       }\r
+       \r
+}\r
diff --git a/tests/soba/util/IntPairUtilTest.java b/tests/soba/util/IntPairUtilTest.java
new file mode 100755 (executable)
index 0000000..157d8fe
--- /dev/null
@@ -0,0 +1,73 @@
+package soba.util;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+\r
+import org.junit.Test;\r
+\r
+public class IntPairUtilTest {\r
+\r
+       @Test\r
+       public void testCreateList() {\r
+               IntPairSet set = new IntPairSet();\r
+               set.add(4, 1);\r
+               set.add(3, 2);\r
+               set.add(1, 4);\r
+               set.add(1, 2);\r
+               set.add(4, 1);\r
+               set.add(0, 1);\r
+               set.add(2, 9);\r
+               set.add(3, 2);\r
+               IntPairList list = IntPairUtil.createList(set);\r
+               list.sort();\r
+               assertThat(list.size(), is(6));\r
+               assertThat(list.getFirstValue(0), is(0));\r
+               assertThat(list.getSecondValue(0), is(1));\r
+               assertThat(list.getFirstValue(1), is(1));\r
+               assertThat(list.getSecondValue(1), is(2));\r
+               assertThat(list.getFirstValue(2), is(1));\r
+               assertThat(list.getSecondValue(2), is(4));\r
+               assertThat(list.getFirstValue(3), is(2));\r
+               assertThat(list.getSecondValue(3), is(9));\r
+               assertThat(list.getFirstValue(4), is(3));\r
+               assertThat(list.getSecondValue(4), is(2));\r
+               assertThat(list.getFirstValue(5), is(4));\r
+               assertThat(list.getSecondValue(5), is(1));\r
+       }\r
+\r
+\r
+       @Test\r
+       public void testCreateList2() {\r
+               IntPairSet set = new IntPairSet();\r
+               set.add(4, 1);\r
+               set.add(3, 2);\r
+               set.add(1, 4);\r
+               set.add(1, 2);\r
+               set.add(4, 1);\r
+               set.add(0, 1);\r
+               set.add(2, 9);\r
+               set.add(3, 2);\r
+               IntPairSet set2 = new IntPairSet();\r
+               set2.add(1, 9);\r
+               set2.add(4, 1);\r
+               \r
+               IntPairList list = IntPairUtil.createList(set, set2);\r
+               list.sort();\r
+               assertThat(list.size(), is(7));\r
+               assertThat(list.getFirstValue(0), is(0));\r
+               assertThat(list.getSecondValue(0), is(1));\r
+               assertThat(list.getFirstValue(1), is(1));\r
+               assertThat(list.getSecondValue(1), is(2));\r
+               assertThat(list.getFirstValue(2), is(1));\r
+               assertThat(list.getSecondValue(2), is(4));\r
+               assertThat(list.getFirstValue(3), is(1));\r
+               assertThat(list.getSecondValue(3), is(9));\r
+               assertThat(list.getFirstValue(4), is(2));\r
+               assertThat(list.getSecondValue(4), is(9));\r
+               assertThat(list.getFirstValue(5), is(3));\r
+               assertThat(list.getSecondValue(5), is(2));\r
+               assertThat(list.getFirstValue(6), is(4));\r
+               assertThat(list.getSecondValue(6), is(1));\r
+       }\r
+\r
+}\r
diff --git a/tests/soba/util/IntSetStackTest.java b/tests/soba/util/IntSetStackTest.java
new file mode 100755 (executable)
index 0000000..ae4a1d8
--- /dev/null
@@ -0,0 +1,130 @@
+package soba.util;\r
+\r
+import java.util.EmptyStackException;\r
+\r
+import org.junit.Assert;\r
+import org.junit.Test;\r
+\r
+import soba.util.IntSetStack.DuplicatedElementException;\r
+import soba.util.IntSetStack.InvalidElementException;\r
+\r
+\r
+public class IntSetStackTest {\r
+\r
+       @Test\r
+       public void testStack() throws Exception {\r
+               IntSetStack stack = new IntSetStack(3);\r
+               Assert.assertTrue(stack.isEmpty());\r
+               Assert.assertFalse(stack.contains(0));\r
+               Assert.assertFalse(stack.contains(1));\r
+               Assert.assertFalse(stack.contains(2));\r
+               \r
+               stack.push(0);\r
+               Assert.assertEquals(0, stack.peek());\r
+               Assert.assertFalse(stack.isEmpty());\r
+               Assert.assertTrue(stack.contains(0));\r
+               Assert.assertFalse(stack.contains(1));\r
+               Assert.assertFalse(stack.contains(2));\r
+               \r
+               stack.push(1);\r
+               Assert.assertEquals(1, stack.peek());\r
+               Assert.assertFalse(stack.isEmpty());\r
+               Assert.assertTrue(stack.contains(0));\r
+               Assert.assertTrue(stack.contains(1));\r
+               Assert.assertFalse(stack.contains(2));\r
+               \r
+               stack.push(2);\r
+               Assert.assertEquals(2, stack.peek());\r
+               Assert.assertFalse(stack.isEmpty());\r
+               Assert.assertTrue(stack.contains(0));\r
+               Assert.assertTrue(stack.contains(1));\r
+               Assert.assertTrue(stack.contains(2));\r
+               \r
+               Assert.assertEquals(2, stack.pop());\r
+               Assert.assertEquals(1, stack.peek());\r
+               Assert.assertTrue(stack.contains(0));\r
+               Assert.assertTrue(stack.contains(1));\r
+               Assert.assertFalse(stack.contains(2));\r
+\r
+               Assert.assertEquals(1, stack.pop());\r
+               Assert.assertEquals(0, stack.pop());\r
+               Assert.assertTrue(stack.isEmpty());\r
+\r
+               Assert.assertFalse(stack.contains(0));\r
+               Assert.assertFalse(stack.contains(1));\r
+               Assert.assertFalse(stack.contains(2));\r
+\r
+               try {\r
+                       stack.pop();\r
+                       Assert.fail();\r
+               } catch (EmptyStackException e) {\r
+               }\r
+               \r
+               try {\r
+                       stack.peek();\r
+                       Assert.fail();\r
+               } catch (EmptyStackException e) {\r
+               }\r
+       }\r
+       \r
+       @Test\r
+       public void testStackContains() {\r
+               IntSetStack stack = new IntSetStack(128);\r
+               try {\r
+                       stack.push(64);\r
+                       stack.push(96);\r
+                       stack.push(127);\r
+                       stack.push(128);\r
+                       stack.push(129);\r
+                       Assert.fail();\r
+               } catch (InvalidElementException e) {\r
+                       Assert.assertEquals(129, e.getValue());\r
+               }\r
+               \r
+               Assert.assertTrue(stack.contains(64));\r
+               Assert.assertTrue(stack.contains(96));\r
+               Assert.assertTrue(stack.contains(127));\r
+               Assert.assertTrue(stack.contains(128));\r
+               Assert.assertFalse(stack.contains(63));\r
+               Assert.assertFalse(stack.contains(65));\r
+               Assert.assertFalse(stack.contains(95));\r
+               Assert.assertFalse(stack.contains(97));\r
+               Assert.assertFalse(stack.contains(126));\r
+               \r
+               try {\r
+                       stack.push(64);\r
+                       Assert.fail();\r
+               } catch (DuplicatedElementException e) {\r
+               }\r
+       }\r
+       \r
+       @Test\r
+       public void testStackIgnoreDuplicatedElement() {\r
+               IntSetStack stack = new IntSetStack(8);\r
+               stack.setIgnoreDuplicatedElement(true);\r
+               stack.push(1);\r
+               stack.push(2);\r
+               stack.push(3);\r
+               stack.push(4);\r
+               stack.push(2);\r
+               stack.push(4);\r
+               stack.push(1);\r
+               stack.push(3);\r
+               stack.push(0);\r
+               Assert.assertEquals(0, stack.pop());\r
+               Assert.assertEquals(4, stack.pop());\r
+               Assert.assertEquals(3, stack.pop());\r
+               Assert.assertEquals(2, stack.pop());\r
+               Assert.assertEquals(1, stack.pop());\r
+               Assert.assertTrue(stack.isEmpty());\r
+               stack.push(1);\r
+               stack.push(2);\r
+               stack.push(3);\r
+               stack.push(4);\r
+               Assert.assertEquals(4, stack.pop());\r
+               Assert.assertEquals(3, stack.pop());\r
+               Assert.assertEquals(2, stack.pop());\r
+               Assert.assertEquals(1, stack.pop());\r
+               Assert.assertTrue(stack.isEmpty());\r
+       }\r
+}\r
diff --git a/tests/soba/util/IntStackTest.java b/tests/soba/util/IntStackTest.java
new file mode 100755 (executable)
index 0000000..066c36c
--- /dev/null
@@ -0,0 +1,63 @@
+package soba.util;\r
+\r
+import java.util.EmptyStackException;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+\r
+import org.junit.Test;\r
+\r
+\r
+public class IntStackTest {\r
+\r
+       @Test\r
+       public void testStack() throws Exception {\r
+               IntStack stack = new IntStack(3);\r
+               assertThat(stack.isEmpty(), is(true));\r
+               stack.push(0);\r
+               assertThat(stack.peek(), is(0));\r
+               assertThat(stack.isEmpty(), is(false));\r
+               stack.push(1);\r
+               assertThat(stack.peek(), is(1));\r
+               assertThat(stack.isEmpty(), is(false));\r
+               stack.push(2);\r
+               assertThat(stack.peek(), is(2));\r
+               assertThat(stack.isEmpty(), is(false));\r
+               \r
+               assertThat(stack.pop(), is(2));\r
+               assertThat(stack.peek(), is(1));\r
+               assertThat(stack.pop(), is(1));\r
+               assertThat(stack.pop(), is(0));\r
+               assertThat(stack.isEmpty(), is(true));\r
+               try {\r
+                       stack.pop();\r
+                       fail();\r
+               } catch (EmptyStackException e) {\r
+               }\r
+               try {\r
+                       stack.peek();\r
+                       fail();\r
+               } catch (EmptyStackException e) {\r
+               }\r
+               \r
+               stack.push(0);\r
+               stack.push(1);\r
+               stack.push(2);\r
+               stack.push(3);\r
+               stack.push(4);\r
+               stack.push(5);\r
+               stack.push(6);\r
+               assertThat(stack.pop(), is(6));\r
+               assertThat(stack.contains(3), is(true));\r
+               assertThat(stack.pop(), is(5));\r
+               assertThat(stack.pop(), is(4));\r
+               assertThat(stack.contains(3), is(true));\r
+               assertThat(stack.pop(), is(3));\r
+               assertThat(stack.contains(3), is(false));\r
+               assertThat(stack.pop(), is(2));\r
+               assertThat(stack.pop(), is(1));\r
+               assertThat(stack.isEmpty(), is(false));\r
+               assertThat(stack.pop(), is(0));\r
+               assertThat(stack.isEmpty(), is(true));\r
+       }\r
+}\r
diff --git a/tests/soba/util/ObjectIdMapTest.java b/tests/soba/util/ObjectIdMapTest.java
new file mode 100755 (executable)
index 0000000..7fb2b28
--- /dev/null
@@ -0,0 +1,59 @@
+package soba.util;\r
+\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+\r
+import org.junit.Test;\r
+\r
+public class ObjectIdMapTest {\r
+\r
+       @Test\r
+       public void testObjectIdMap() {\r
+               ObjectIdMap<String> idMap = new ObjectIdMap<String>();\r
+               int idABC = idMap.getId("abc");\r
+               int idAB = idMap.getId("ab");\r
+               int idABC2 = idMap.getId("abc");\r
+               int idD = idMap.getId("d");\r
+               assertThat(idMap.size(), is(3));\r
+               assertThat(idABC, is(0));\r
+               assertThat(idABC2, is(0));\r
+               assertThat(idAB, is(1));\r
+               assertThat(idD, is(2));\r
+               assertThat(idMap.getItem(idABC), is("abc"));\r
+               assertThat(idMap.getItem(idAB), is("ab"));\r
+               assertThat(idMap.getItem(idD), is("d"));\r
+               assertThat(idMap.getItem(-1), is(nullValue()));\r
+               assertThat(idMap.getItem(4), is(nullValue()));\r
+       }\r
+       \r
+       @Test\r
+       public void testAdd() { \r
+               ObjectIdMap<String> idMap = new ObjectIdMap<String>();\r
+               idMap.add("abc");\r
+               idMap.add("ab");\r
+               idMap.add("ab");\r
+               idMap.add("abc");\r
+               assertThat(idMap.size(), is(2));\r
+               assertThat(idMap.getItem(0), is("abc"));\r
+               assertThat(idMap.getItem(1), is("ab"));\r
+       }\r
+\r
+       @Test\r
+       public void testFreeze() { \r
+               ObjectIdMap<String> idMap = new ObjectIdMap<String>();\r
+               idMap.add("abc");\r
+               idMap.add("ab");\r
+               idMap.freeze();\r
+               try {\r
+                       idMap.add("abc"); // ignored already registerd item\r
+                       idMap.add("xyz"); // throws an exception\r
+                       fail();\r
+               } catch (ObjectIdMap.FrozenMapException e) {\r
+               }\r
+               assertThat(idMap.getId("ab"), is(1));\r
+               assertThat(idMap.getItem(0), is("abc"));\r
+               assertThat(idMap.getItem(3), is(nullValue()));\r
+       }\r
+\r
+}\r
diff --git a/tests/soba/util/UtilForAssertThat.java b/tests/soba/util/UtilForAssertThat.java
new file mode 100755 (executable)
index 0000000..b338c73
--- /dev/null
@@ -0,0 +1,13 @@
+package soba.util;\r
+\r
+public class UtilForAssertThat {       \r
+\r
+       public static Integer[] asIntegerArray(int[] array) {\r
+               Integer[] result = new Integer[array.length];\r
+               for (int i = 0; i < result.length; i++) {\r
+                       result[i] = array[i];\r
+               }\r
+               return result;\r
+       }\r
+       \r
+}\r
diff --git a/tests/soba/util/files/ClasspathUtilTest.java b/tests/soba/util/files/ClasspathUtilTest.java
new file mode 100755 (executable)
index 0000000..0f9d67a
--- /dev/null
@@ -0,0 +1,60 @@
+package soba.util.files;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+import org.junit.Test;\r
+\r
+import soba.core.ClassInfo;\r
+\r
+public class ClasspathUtilTest {\r
+\r
+       @Test\r
+       public void testEnumerateSystemClasspath() {\r
+               List<String> systemList = ClasspathUtil.enumerateSystemClasspath();\r
+               assertThat(systemList, is(notNullValue()));\r
+       }\r
+       \r
+       @Test\r
+       public void testMerge() { \r
+               IClassList[] e1 = ClasspathUtil.getClassList(new String[] { "." });\r
+               IClassList[] e2 = ClasspathUtil.getClassList(new String[] { "..", "." });\r
+               IClassList[] e3 = ClasspathUtil.merge(e1, e2);\r
+               assertThat(e1[0] == e3[0], is(true));\r
+               assertThat(e2[0] == e3[1], is(true));\r
+               assertThat(e2[1] == e3[2], is(true));\r
+       }\r
+       \r
+       @Test\r
+       public void testGetClassList() {\r
+               String[] fileArray = new String[]{"bin/soba/testdata"};\r
+               IClassList[] results1 = ClasspathUtil.getClassList(fileArray);\r
+               assertThat(results1, is(arrayWithSize(1)));\r
+               IClassList[] results2 = ClasspathUtil.getClassList(fileArray, "");\r
+               assertThat(results2, is(arrayWithSize(1)));\r
+               \r
+               List<String> fileList = new ArrayList<>();\r
+               fileList.add("bin/soba/testdata");\r
+               IClassList[] results3 = ClasspathUtil.getClassList(fileList);\r
+               assertThat(results3, is(arrayWithSize(1)));\r
+               IClassList[] results4 = ClasspathUtil.getClassList(fileList, null);\r
+               assertThat(results4, is(arrayWithSize(1)));\r
+               \r
+               String[] zipFile = new String[]{"lib/asm-debug-all-5.0.3.jar"};\r
+               IClassList[] results5 = ClasspathUtil.getClassList(zipFile);\r
+               assertThat(results5, is(arrayWithSize(1)));\r
+               \r
+               String[] classFile = new String[]{"bin/soba/testdata/DefUseTestData.class"};\r
+               IClassList[] results6 = ClasspathUtil.getClassList(classFile);\r
+               assertThat(results6, is(arrayWithSize(1)));\r
+               \r
+               String[] appFiles = new String[]{"bin/soba/testdata/DefUseTestData.class"};\r
+               String[] libFiles = new String[]{"lib/asm-debug-all-5.0.3.jar"};\r
+               IClassList[] results7 = ClasspathUtil.getClassList(appFiles, libFiles);\r
+               assertThat(results7, is(arrayWithSize(2)));\r
+               assertThat(results7[1].getLabel(), is(ClassInfo.LIBRARY_LABEL));\r
+       }\r
+}\r
diff --git a/tests/soba/util/files/DirectoryTest.java b/tests/soba/util/files/DirectoryTest.java
new file mode 100755 (executable)
index 0000000..58b0ce6
--- /dev/null
@@ -0,0 +1,27 @@
+package soba.util.files;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+import static org.junit.Assume.*;\r
+\r
+import org.junit.Test;\r
+import java.io.File;\r
+\r
+public class DirectoryTest {\r
+       \r
+       @Test\r
+       public void testListSubdirectories() {\r
+               File f = new File("src");\r
+               assumeThat(f.isDirectory(), is(true));\r
+               \r
+               Directory[] dir = Directory.listSubdirectories(f, 0);\r
+               assertThat(dir.length, is(1));\r
+               assertThat(dir[0].getDirectory(), is(f));\r
+\r
+               File soba = new File(f, "soba");\r
+               Directory[] subdirs = Directory.listSubdirectories(f, 1);\r
+               assertThat(dir.length, is(1));\r
+               assertThat(subdirs[0].getDirectory(), is(soba));\r
+       }\r
+\r
+}\r
diff --git a/tests/soba/util/files/ZipFileTest.java b/tests/soba/util/files/ZipFileTest.java
new file mode 100755 (executable)
index 0000000..b10e174
--- /dev/null
@@ -0,0 +1,35 @@
+package soba.util.files;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+import static org.junit.Assume.*;\r
+\r
+import java.io.File;\r
+\r
+import org.junit.Test;\r
+\r
+public class ZipFileTest {\r
+\r
+       @Test\r
+       public void testIsZipFile() {\r
+               File jarFile = new File("lib/asm-debug-all-5.0.3.jar");\r
+               assumeThat(jarFile.exists(), is(true));\r
+               assertThat(ZipFile.isZipFile(jarFile), is(true));\r
+               \r
+               File sourceFile = new File("tests/soba/util/files/ZipFileTest.java");\r
+               assumeThat(sourceFile.exists(), is(true));\r
+               assertThat(ZipFile.isZipFile(sourceFile), is(false));\r
+       }\r
+\r
+       @Test\r
+       public void testIsClassFile() {\r
+               File classFile = new File("bin/soba/util/files/ZipFileTest.class");\r
+               assumeThat(classFile.exists(), is(true));\r
+               assertThat(ZipFile.isClassFile(classFile), is(true));\r
+               \r
+               File sourceFile = new File("tests/soba/util/files/ZipFileTest.java");\r
+               assumeThat(sourceFile.exists(), is(true));\r
+               assertThat(ZipFile.isClassFile(sourceFile), is(false));\r
+       }\r
+\r
+}\r
diff --git a/tests/soba/util/graph/DepthFirstSearchTest.java b/tests/soba/util/graph/DepthFirstSearchTest.java
new file mode 100755 (executable)
index 0000000..8b7d941
--- /dev/null
@@ -0,0 +1,107 @@
+package soba.util.graph;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+\r
+import org.junit.Before;\r
+import org.junit.Test;\r
+\r
+public class DepthFirstSearchTest {\r
+\r
+       private DirectedGraph graph;\r
+       \r
+       /**\r
+        * Check the sequence of visited vertices.\r
+        */\r
+       private class VisitList implements IDepthFirstVisitor {\r
+\r
+               int[] visitList;\r
+               int[] leaveList;\r
+               boolean[] expectedVisited;\r
+               int visitIndex = 0;\r
+               int leaveIndex = 0;\r
+               \r
+               public VisitList(int[] visitList, int[] leaveList) {\r
+                       this.visitList = visitList;\r
+                       this.leaveList = leaveList;\r
+                       this.expectedVisited = new boolean[graph.getVertexCount()];\r
+                       for (int v: visitList) {\r
+                               expectedVisited[v] = true;\r
+                       }\r
+                       for (int v: leaveList) {\r
+//                             Assert.assertTrue(expectedVisited[v]);\r
+                               assertThat(expectedVisited[v], is(true));\r
+                       }\r
+               }\r
+               \r
+               @Override\r
+               public void onStart(int startVertexId) {\r
+               }\r
+               \r
+               @Override\r
+               public boolean onVisit(int vertexId) {\r
+                       assertThat(vertexId, is(visitList[visitIndex]));\r
+                       visitIndex++;\r
+                       return continueVisit(vertexId);\r
+               }\r
+               \r
+               protected boolean continueVisit(int vertexId) {\r
+                       return true;\r
+               }\r
+               \r
+               @Override\r
+               public void onLeave(int vertexId) {\r
+                       assertThat(vertexId, is(leaveList[leaveIndex]));\r
+                       leaveIndex++;\r
+               }\r
+               @Override\r
+               public void onFinished(boolean[] visited) {\r
+                       for (int i=0; i<visited.length; ++i) {\r
+                               assertThat(visited[i], is(expectedVisited[i]));\r
+                       }\r
+               }\r
+               \r
+               @Override\r
+               public void onVisitAgain(int vertexId) {\r
+                       // The vertices must be visited\r
+                       boolean visited = false;\r
+                       for (int i=0; i<visitIndex; ++i) {\r
+                               if (visitList[i] == vertexId) {\r
+                                       visited = true;\r
+                                       break;\r
+                               }\r
+                       }\r
+                       assertThat(visited, is(true));\r
+               }\r
+               \r
+       }\r
+       \r
+       \r
+       /**\r
+        digraph {\r
+           0 -> 1;  1 -> 2;   2 -> 0;  1 -> 3;  3 -> 4;  3 -> 5;\r
+           5 -> 6;  6 -> 7;   6 -> 8;  7 -> 8;  8 -> 5;  7 -> 9;\r
+           7 -> 10;  9 -> 11;  10 -> 11;  11 -> 12;  13;\r
+        }\r
+        */\r
+       @Before\r
+       public void buildGraph() throws Exception {\r
+               graph = GraphTestBase.buildGraph();\r
+       }\r
+       \r
+       @Test\r
+       public void testDFS() throws Exception {\r
+               DepthFirstSearch.search(graph, 7, new VisitList(new int[]{7, 8, 5, 6, 9, 11, 12, 10}, new int[]{6, 5, 8, 12, 11, 9, 10, 7}));\r
+               DepthFirstSearch.search(graph, 7, new VisitList(new int[] {7}, new int[] {7}) {\r
+                       protected boolean continueVisit(int vertexId) {\r
+                               return false;\r
+                       };\r
+               });\r
+               DepthFirstSearch.search(graph, 0, new VisitList(new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, new int[] {2, 4, 8, 9, 10, 7, 6, 5,  3, 1, 0}) {\r
+                       protected boolean continueVisit(int vertexId) {\r
+                               return vertexId <= 7;\r
+                       };\r
+               });\r
+       }\r
+       \r
+}\r
diff --git a/tests/soba/util/graph/DirectedAcyclicGraphTest.java b/tests/soba/util/graph/DirectedAcyclicGraphTest.java
new file mode 100755 (executable)
index 0000000..8bf3f61
--- /dev/null
@@ -0,0 +1,66 @@
+package soba.util.graph;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+\r
+import org.junit.Before;\r
+import org.junit.Test;\r
+\r
+import soba.util.IntPairProc;\r
+import soba.util.UtilForAssertThat;\r
+\r
+public class DirectedAcyclicGraphTest {\r
+\r
+       DirectedGraph graph;\r
+       DirectedAcyclicGraph dag;\r
+       \r
+       @Before\r
+       public void buildGraph() throws Exception {\r
+               graph = GraphTestBase.buildGraph();\r
+               dag = new DirectedAcyclicGraph(graph);\r
+       }\r
+       \r
+       @Test\r
+       public void testAcyclicGraphNodes() {\r
+               assertThat(dag.getVertexCount(), is(graph.getVertexCount()));\r
+               assertThat(dag.getVertexCount(), is(graph.getVertexCount()));\r
+               assertThat(dag.isRepresentativeNode(0), is(true));\r
+               assertThat(dag.isRepresentativeNode(1), is(false));\r
+               assertThat(dag.isRepresentativeNode(2), is(false));\r
+               assertThat(dag.isRepresentativeNode(3), is(true));\r
+               assertThat(dag.isRepresentativeNode(4), is(true));\r
+               assertThat(dag.isRepresentativeNode(5), is(true));\r
+               assertThat(dag.isRepresentativeNode(6), is(false));\r
+               assertThat(dag.isRepresentativeNode(7), is(false));\r
+               assertThat(dag.isRepresentativeNode(8), is(false));\r
+               assertThat(dag.isRepresentativeNode(9), is(true));\r
+               assertThat(dag.isRepresentativeNode(10), is(true));\r
+               assertThat(dag.isRepresentativeNode(11), is(true));\r
+               assertThat(dag.isRepresentativeNode(12), is(true));\r
+               assertThat(dag.isRepresentativeNode(13), is(true));\r
+       }\r
+       \r
+       @Test\r
+       public void testAcyclicGraphEdges() {\r
+               dag.forEachEdge(new IntPairProc() {\r
+\r
+                       int index = 0;\r
+                       int[][] expected = new int[][] {\r
+                               {0, 3}, {3, 4}, {3, 5}, {5, 9}, {5, 10},\r
+                               {9, 11}, {10, 11}, {11, 12} };\r
+\r
+                       @Override\r
+                       public boolean execute(int elem1, int elem2) {\r
+                               assertThat(elem1, is(expected[index][0]));\r
+                               assertThat(elem2, is(expected[index][1]));\r
+                               index++;\r
+                               return true;\r
+                       }\r
+               });\r
+               \r
+               assertThat(UtilForAssertThat.asIntegerArray(dag.getEdges(13)), is(emptyArray()));\r
+               assertThat(UtilForAssertThat.asIntegerArray(dag.getEdges(3)), is(arrayContainingInAnyOrder(4, 5)));\r
+       }\r
+\r
+       \r
+}\r
diff --git a/tests/soba/util/graph/DirectedGraphTest.java b/tests/soba/util/graph/DirectedGraphTest.java
new file mode 100755 (executable)
index 0000000..b01e845
--- /dev/null
@@ -0,0 +1,126 @@
+package soba.util.graph;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+import gnu.trove.list.array.TIntArrayList;\r
+\r
+import org.junit.BeforeClass;\r
+import org.junit.Test;\r
+\r
+import soba.util.UtilForAssertThat;\r
+\r
+public class DirectedGraphTest {\r
+       \r
+       private static DirectedGraph graph;\r
+       \r
+       @BeforeClass\r
+       public static void buildGraph() {\r
+               graph = GraphTestBase.buildGraph();\r
+       }\r
+       \r
+       \r
+       @Test\r
+       public void testDepthFirstSearch() throws Exception {\r
+               final TIntArrayList visited = new TIntArrayList();\r
+               DepthFirstSearch.search(graph, 0, new IDepthFirstVisitor() {\r
+                       @Override\r
+                       public void onStart(int startVertexId) {\r
+                       }\r
+                       @Override\r
+                       public boolean onVisit(int vertexId) {\r
+                               visited.add(vertexId);\r
+                               return true;\r
+                       }\r
+                       @Override\r
+                       public void onLeave(int vertexId) {\r
+                       }\r
+                       @Override\r
+                       public void onFinished(boolean[] visited) {\r
+                               assertThat(visited.length, is(14));\r
+                               for (int i=0; i<13; ++i) {\r
+                                       assertThat(visited[i], is(true));\r
+                               }\r
+                               assertThat(visited[13], is(false));\r
+                       }\r
+                       @Override\r
+                       public void onVisitAgain(int vertexId) {\r
+                               assertThat(visited.contains(vertexId), is(true));\r
+                       }\r
+               });\r
+\r
+               assertThat(visited.size(), is(13));\r
+       }       \r
+       \r
+       @Test\r
+       public void testEdgeCount() {\r
+               assertThat(graph.getEdgeCount(), is(16));\r
+       }\r
+       \r
+       @Test\r
+       public void testReverseGraph() {\r
+               DirectedGraph r = graph.getReverseGraph();\r
+               assertThat(r.getVertexCount(), is(14));\r
+               assertThat(r.getEdgeCount(), is(16));\r
+               \r
+               // check all edges\r
+               Integer[] edgesFrom0 = UtilForAssertThat.asIntegerArray(r.getEdges(0));\r
+               assertThat(edgesFrom0, is(arrayContainingInAnyOrder(2)));\r
+\r
+               Integer[] edgesFrom1 = UtilForAssertThat.asIntegerArray(r.getEdges(1));\r
+               assertThat(edgesFrom1, is(arrayContainingInAnyOrder(0)));\r
+\r
+               Integer[] edgesFrom2 = UtilForAssertThat.asIntegerArray(r.getEdges(2));\r
+               assertThat(edgesFrom2, is(arrayContainingInAnyOrder(1)));\r
+\r
+               Integer[] edgesFrom3 = UtilForAssertThat.asIntegerArray(r.getEdges(3));\r
+               assertThat(edgesFrom3, is(arrayContainingInAnyOrder(1)));\r
+\r
+               Integer[] edgesFrom4 = UtilForAssertThat.asIntegerArray(r.getEdges(4));\r
+               assertThat(edgesFrom4, is(arrayContainingInAnyOrder(3)));\r
+               \r
+               Integer[] edgesFrom5 = UtilForAssertThat.asIntegerArray(r.getEdges(5));\r
+               assertThat(edgesFrom5, is(arrayContainingInAnyOrder(3, 8)));\r
+               \r
+               Integer[] edgesFrom6 = UtilForAssertThat.asIntegerArray(r.getEdges(6));\r
+               assertThat(edgesFrom6, is(arrayContainingInAnyOrder(5)));\r
+\r
+               Integer[] edgesFrom7 = UtilForAssertThat.asIntegerArray(r.getEdges(7));\r
+               assertThat(edgesFrom7, is(arrayContainingInAnyOrder(6)));\r
+               \r
+               Integer[] edgesFrom8 = UtilForAssertThat.asIntegerArray(r.getEdges(8));\r
+               assertThat(edgesFrom8, is(arrayContainingInAnyOrder(6, 7)));\r
+\r
+               Integer[] edgesFrom9 = UtilForAssertThat.asIntegerArray(r.getEdges(9));\r
+               assertThat(edgesFrom9, is(arrayContainingInAnyOrder(7)));\r
+               \r
+               Integer[] edgesFrom10 = UtilForAssertThat.asIntegerArray(r.getEdges(10));\r
+               assertThat(edgesFrom10, is(arrayContainingInAnyOrder(7)));\r
+\r
+               Integer[] edgesFrom11 = UtilForAssertThat.asIntegerArray(r.getEdges(11));\r
+               assertThat(edgesFrom11, is(arrayContainingInAnyOrder(9, 10)));\r
+\r
+               Integer[] edgesFrom12 = UtilForAssertThat.asIntegerArray(r.getEdges(12));\r
+               assertThat(edgesFrom12, is(arrayContainingInAnyOrder(11)));\r
+\r
+               Integer[] edgesFrom13 = UtilForAssertThat.asIntegerArray(r.getEdges(13));\r
+               assertThat(edgesFrom13, is(emptyArray()));\r
+       }\r
+       \r
+       @Test\r
+       public void testUndirectedGraph() {\r
+               DirectedGraph g = graph.getUndirectedGraph();\r
+               Integer[] edgesFrom1 = UtilForAssertThat.asIntegerArray(g.getEdges(1));\r
+               assertThat(edgesFrom1, is(arrayContainingInAnyOrder(0, 2, 3)));\r
+               \r
+               Integer[] edgesFrom10 = UtilForAssertThat.asIntegerArray(g.getEdges(10));\r
+               assertThat(edgesFrom10, is(arrayContainingInAnyOrder(7, 11)));\r
+               \r
+               Integer[] edgesFrom12 = UtilForAssertThat.asIntegerArray(g.getEdges(12));\r
+               assertThat(edgesFrom12, is(arrayContainingInAnyOrder(11)));\r
+               \r
+               Integer[] edgesFrom13 = UtilForAssertThat.asIntegerArray(g.getEdges(13));\r
+               assertThat(edgesFrom13, is(emptyArray()));\r
+               \r
+               assertThat(g.getEdgeCount(), is(32));\r
+       }\r
+}\r
diff --git a/tests/soba/util/graph/DominanceTreeTest.java b/tests/soba/util/graph/DominanceTreeTest.java
new file mode 100755 (executable)
index 0000000..815742d
--- /dev/null
@@ -0,0 +1,48 @@
+package soba.util.graph;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+\r
+import org.junit.BeforeClass;\r
+import org.junit.Test;\r
+\r
+\r
+public class DominanceTreeTest {\r
+\r
+       private static DirectedGraph graph;\r
+       \r
+       @BeforeClass\r
+       public static void buildGraph() throws Exception {\r
+               graph = GraphTestBase.buildGraph();\r
+       }\r
+\r
+       @Test\r
+       public void testDominanceTree() {\r
+               SingleRootDirectedGraph g = new SingleRootDirectedGraph(graph);\r
+               DominanceTree tree = new DominanceTree(g);\r
+               \r
+               assertThat(tree.isRoot(g.getRootId()), is(true));\r
+               assertThat(tree.isRoot(0), is(false));\r
+               assertThat(tree.isRoot(13), is(false));\r
+               assertThat(tree.getDominator(0), is(14));\r
+               assertThat(tree.getDominator(1), is(0));\r
+               assertThat(tree.getDominator(2), is(1));\r
+               assertThat(tree.getDominator(3), is(1));\r
+               assertThat(tree.getDominator(4), is(3));\r
+               assertThat(tree.getDominator(5), is(3));\r
+               assertThat(tree.getDominator(6), is(5));\r
+               assertThat(tree.getDominator(7), is(6));\r
+               assertThat(tree.getDominator(8), is(6));\r
+               assertThat(tree.getDominator(9), is(7));\r
+               assertThat(tree.getDominator(10), is(7));\r
+               assertThat(tree.getDominator(11), is(7));\r
+               assertThat(tree.getDominator(12), is(11));\r
+               assertThat(tree.getDominator(13), is(14));\r
+               \r
+               assertThat(tree.nearestCommonAncestor(1, 13), is(14));\r
+               assertThat(tree.nearestCommonAncestor(5, 12), is(5));\r
+               assertThat(tree.nearestCommonAncestor(4, 12), is(3));\r
+               assertThat(tree.nearestCommonAncestor(12, 4), is(3));\r
+               assertThat(tree.nearestCommonAncestor(8, 6), is(6));\r
+       }\r
+}\r
diff --git a/tests/soba/util/graph/GraphTestBase.java b/tests/soba/util/graph/GraphTestBase.java
new file mode 100755 (executable)
index 0000000..dc68202
--- /dev/null
@@ -0,0 +1,33 @@
+package soba.util.graph;\r
+\r
+\r
+import soba.util.IntPairList;\r
+\r
+public class GraphTestBase {\r
+\r
+       public static DirectedGraph buildGraph() { \r
+               IntPairList edges = new IntPairList();\r
+               edges.add(0, 1); // cycle 0->1->2\r
+               edges.add(1, 2);\r
+               edges.add(2, 0);\r
+               edges.add(1, 3);\r
+               \r
+               edges.add(3, 4);\r
+               edges.add(3, 5);\r
+               \r
+               edges.add(5, 6); // two cycles: 5->6->8->5\r
+               edges.add(6, 7); // and 5->6->7->8->5\r
+               edges.add(6, 8);\r
+               edges.add(7, 8);\r
+               edges.add(8, 5);\r
+\r
+               edges.add(7, 9);\r
+               edges.add(7, 10);\r
+               edges.add(9, 11);\r
+               edges.add(10, 11);\r
+               edges.add(11, 12); // 13 is not connected to any other vertices\r
+               \r
+               return new DirectedGraph(14, edges);\r
+       }\r
+       \r
+}\r
diff --git a/tests/soba/util/graph/SingleRootDirectedGraphTest.java b/tests/soba/util/graph/SingleRootDirectedGraphTest.java
new file mode 100755 (executable)
index 0000000..e4bb76f
--- /dev/null
@@ -0,0 +1,84 @@
+package soba.util.graph;\r
+\r
+import static org.junit.Assert.*;\r
+import static org.hamcrest.Matchers.*;\r
+\r
+import org.junit.Before;\r
+import org.junit.Test;\r
+\r
+import soba.util.IntPairProc;\r
+\r
+\r
+public class SingleRootDirectedGraphTest {\r
+\r
+\r
+       private DirectedGraph graph;\r
+       \r
+       @Before\r
+       public void buildGraph() throws Exception {\r
+               graph = GraphTestBase.buildGraph();\r
+       }\r
+\r
+       @Test\r
+       public void testSingleRootDirectedGraph() throws Exception {\r
+               SingleRootDirectedGraph base = new SingleRootDirectedGraph(graph);\r
+               assertThat(base.getVertexCount(), is(15));\r
+               assertThat(base.getRootId(), is(14));\r
+               assertThat(base.getEdges(14).length, is(2));\r
+               assertThat(base.getEdges(14)[0], is(0));\r
+               assertThat(base.getEdges(14)[1], is(13));\r
+       }\r
+       \r
+       @Test\r
+       public void testEdges() throws Exception {\r
+               final SingleRootDirectedGraph base = new SingleRootDirectedGraph(graph);\r
+               base.forEachEdge(new IntPairProc() {\r
+                       \r
+                       int index = 0;\r
+                       int[][] expected = new int[][] {\r
+                               {0, 1}, {1, 2}, {1, 3}, {2, 0},\r
+                               {3, 4}, {3, 5}, {5, 6}, {6, 7},\r
+                               {6, 8}, {7, 8}, {7, 9}, {7, 10},\r
+                               {8, 5}, {9, 11}, {10, 11}, {11, 12},\r
+                               {14, 0}, {14, 13}\r
+                       };\r
+                       @Override\r
+                       public boolean execute(int elem1, int elem2) {\r
+                               assertThat(elem1, is(expected[index][0]));\r
+                               assertThat(elem2, is(expected[index][1]));\r
+                               index++;\r
+                               return true;\r
+                       }\r
+               });\r
+               base.forEachEdge(new IntPairProc() {\r
+                       private boolean first = true;\r
+                       @Override\r
+                       public boolean execute(int elem1, int elem2) {\r
+                               if (first) {\r
+                                       first = false;\r
+                                       return false;\r
+                               } else {\r
+                                       fail();\r
+                                       return false;\r
+                               }\r
+                       }\r
+               });\r
+               base.forEachEdge(new IntPairProc() {\r
+                       private boolean firstFromRoot = true;\r
+                       @Override\r
+                       public boolean execute(int elem1, int elem2) {\r
+                               if (base.getRootId() == elem1) {\r
+                                       if (firstFromRoot) {\r
+                                               firstFromRoot = false;\r
+                                               return false;\r
+                                       } else {\r
+                                               fail();\r
+                                               return false;\r
+                                       }\r
+                               } else {\r
+                                       return true;\r
+                               }\r
+                       }\r
+               });\r
+       }\r
+}\r