diff --git a/DEVELOPING_OPAL/validate/src/it/scala/org/opalj/br/InvokedynamicRewritingExecutionTest.scala b/DEVELOPING_OPAL/validate/src/it/scala/org/opalj/br/InvokedynamicRewritingExecutionTest.scala index 925352173c..a7a0b05dae 100644 --- a/DEVELOPING_OPAL/validate/src/it/scala/org/opalj/br/InvokedynamicRewritingExecutionTest.scala +++ b/DEVELOPING_OPAL/validate/src/it/scala/org/opalj/br/InvokedynamicRewritingExecutionTest.scala @@ -374,7 +374,6 @@ class InvokedynamicRewritingExecutionTest extends AnyFunSpec with Matchers { // Otherwise, the hermes resources are not included and hermes won't find // HermesCLI.txt for example val paths = Array( - new File("TOOLS/hermes/src/main/resources/").toURI.toURL, new File("DEVELOPING_OPAL/tools/src/main/resources/").toURI.toURL, new File("OPAL/ai/src/main/resources/").toURI.toURL, new File("OPAL/ba/src/main/resources/").toURI.toURL, diff --git a/OPAL/ProjectDependencies.mmd b/OPAL/ProjectDependencies.mmd index 5fb11b0b22..57ceefc708 100644 --- a/OPAL/ProjectDependencies.mmd +++ b/OPAL/ProjectDependencies.mmd @@ -7,7 +7,6 @@ flowchart BT IDE[IDE
ide] BytecodeInfrastructure[Bytecode Infrastructure
bi] AbstractInterpretationFramework[Abstract Interpretation Framework
ai] - Hermes[Hermes
hermes] ConfigurationExplorer[Configuration Explorer
ce] Common[Common
common] BytecodeDisassembler[Bytecode Disassembler
da] @@ -21,7 +20,6 @@ flowchart BT style Common fill:#9cbecc,color:black style Framework fill:#c0ffc0 - style Hermes fill:#ffd7cf Framework --> BytecodeAssembler Framework --> ArchitectureValidation @@ -34,7 +32,6 @@ flowchart BT IDE --> BytecodeRepresentation BytecodeInfrastructure --> Common AbstractInterpretationFramework --> BytecodeRepresentation - Hermes --> Framework ConfigurationExplorer --> BytecodeRepresentation BytecodeDisassembler --> BytecodeInfrastructure StaticAnalysisInfrastructure --> Common diff --git a/OPAL/ProjectDependencies.pdf b/OPAL/ProjectDependencies.pdf index d8cc210f68..59c3ed48ef 100644 Binary files a/OPAL/ProjectDependencies.pdf and b/OPAL/ProjectDependencies.pdf differ diff --git a/OPAL/ProjectDependencies.svg b/OPAL/ProjectDependencies.svg index 0244c94178..b4d0a539fe 100644 --- a/OPAL/ProjectDependencies.svg +++ b/OPAL/ProjectDependencies.svg @@ -1 +1 @@ -

Framework
framework

Three Address Code
tac

APK
apk

Dependencies Extraction Library
de

IDE
ide

Bytecode Infrastructure
bi

Abstract Interpretation Framework
ai

Hermes
hermes

Configuration Explorer
ce

Common
common

Bytecode Disassembler
da

Static Analysis Infrastructure
si

IFDS
ifds

Bytecode Representation
br

Bytecode Assembler
ba

Architecture Validation
av

Demos
demos

Bytecode Creator
bc

\ No newline at end of file +

Framework
framework

Three Address Code
tac

APK
apk

Dependencies Extraction Library
de

IDE
ide

Bytecode Infrastructure
bi

Abstract Interpretation Framework
ai

Configuration Explorer
ce

Common
common

Bytecode Disassembler
da

Static Analysis Infrastructure
si

IFDS
ifds

Bytecode Representation
br

Bytecode Assembler
ba

Architecture Validation
av

Demos
demos

Bytecode Creator
bc

\ No newline at end of file diff --git a/README.markdown b/README.markdown index 8dd6af068b..bd3e465071 100644 --- a/README.markdown +++ b/README.markdown @@ -41,8 +41,6 @@ OPAL consists of several projects: * **APK** (OPAL/apk) Provides support for parsing Android APK packages, supporting DEX and native code. -* **Hermes** (OPAL/TOOLS/hermes): A framework to run various code queries against sets of projects. - ## Developer Tools OPAL also comes with a growing number of tools that are intended to help developers to become familiar with Java Bytecode and/or OPAL. These projects are found in the folder `DEVELOPING_OPAL/tools` and can be run using the SBT console. diff --git a/TOOLS/ce/src/main/resources/ce.conf b/TOOLS/ce/src/main/resources/ce.conf index a07608269b..9a35dcf020 100644 --- a/TOOLS/ce/src/main/resources/ce.conf +++ b/TOOLS/ce/src/main/resources/ce.conf @@ -7,7 +7,7 @@ # @description This setting contains a list of filenames that are in use for configuration files in this project # reference.conf, application.conf are default filenames defined by the maker of typesafe configuration # @type String - configurationFilenames = ["ce.conf","reference.conf","application.conf", "hermes.conf","CommandLineProject.conf","LibraryProject.conf","NoTransformations.conf"] + configurationFilenames = ["ce.conf","reference.conf","application.conf","CommandLineProject.conf","LibraryProject.conf","NoTransformations.conf"] # @brief Toggle for replacing classes in the configuration documentation # @description You can use this option to activate / deactivate the replacement of subclass type configuration entries diff --git a/TOOLS/hermes/README.markdown b/TOOLS/hermes/README.markdown deleted file mode 100644 index 53580c0c90..0000000000 --- a/TOOLS/hermes/README.markdown +++ /dev/null @@ -1,15 +0,0 @@ -# Overview -Hermes is a small, extensible project that helps to assess the quality of a given corpus of applications. It extracts a wide range of properties from the given projects to make it possible to asses which language features and API features are used by the respective project(s). This information can then be used to design small, efficient test fixtures/corpuses. - - -## Categories of Queries -Currently, we implement analyses across the following categories: - - - *Completeness - static* - Features of Java BytecodeM; e.g., "Number of classes with(out) debug information.", "Occurrences of specific types of constants.", "Number of invoke dynamic statements?", ... - - *Completeness - dynamic* - do we have an effective virtual method call, is field resolution is required, ... - - *Corner Cases* - (e.g., a method without a return statement, a switch with just a default case, ...) - - *Landmark APIs* - Java Reflection; finalize, Serialization - - *Weird Code* - e.g., methods which will never return, switches with only default targets,... - -## Extending Hermes -See Hermes' built-in documentation or go to [Hermes.md](https://github.com/opalj/opal/blob/master/src/site/Hermes.md) diff --git a/TOOLS/hermes/build.sbt b/TOOLS/hermes/build.sbt deleted file mode 100644 index b511e98651..0000000000 --- a/TOOLS/hermes/build.sbt +++ /dev/null @@ -1 +0,0 @@ -// build settings reside in the opal root build.sbt file diff --git a/TOOLS/hermes/src/main/resources/README.markdown b/TOOLS/hermes/src/main/resources/README.markdown deleted file mode 100644 index 31bf8ad2df..0000000000 --- a/TOOLS/hermes/src/main/resources/README.markdown +++ /dev/null @@ -1,35 +0,0 @@ -# Configuration and Running Hermes - -## application.conf -Static configuration of all available queries as well as further global settings. - -## hermes.json -The configuration file which is used to specify the projects which belong to a test corpus. -By default, those projects are specified which belong to OPAL's test corpus. -To evaluate another corpus just have to adapt this file. -Basically every project has the following settings: `id`, `cp`, `libcp` and `libcp_defaults`. - -### `id` -`id` specifies the unique name of the project. - -### `cp` -`cp` specifies the class path of the project that references all classes that are developed as part of the project. -It is possible to specify multiple folders/jars by separating them using the system's folder separator (on Mac: `:`). - -### `libcp` and `libcp_defaults`(Optional) -These parameters specify the used libraries that should be loaded. -The latter two settings are particularly required by more advanced queries which, e.g., require a complete class hierarchy. -`libcp_defaults` is used to specify that some generally available APIs should be added to the class path. -Currently, the keys `JRE` and `RTJar` are predefined and will always resolve against the current ***JRE***/the ***rt.jar***. - - { - "id": "OPAL-bytecode-infrastructure_2.11-SNAPSHOT-08-14-2014", - "cp": "../../OPAL/bi/src/test/resources/classfiles/OPAL-bytecode-infrastructure_2.11-SNAPSHOT-08-14-2014.jar", - "libcp": "../../OPAL/bi/src/test/resources/classfiles/OPAL-common_2.11-SNAPSHOT-08-14-2014.jar" - "libcp_defaults" : ["JRE"] - } - -## Note -Splitting the configuration across the two files: `application.conf` and `hermes.json` was purely done to provide some structure and to facilitate reuse. -All settings defined in `application.conf` can also be set in `hermes.json`. -It is in particular possible to turn of or add new queries by specifying them in `hermes.json`. diff --git a/TOOLS/hermes/src/main/resources/hermes-test-fixtures.json b/TOOLS/hermes/src/main/resources/hermes-test-fixtures.json deleted file mode 100644 index 169c664379..0000000000 --- a/TOOLS/hermes/src/main/resources/hermes-test-fixtures.json +++ /dev/null @@ -1,137 +0,0 @@ -{ - "org": { - "opalj": { - "hermes": { - "projects": [ - { - "id" : "JVM Features", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/jvm_features-1.8-g-parameters-genericsignature.jar", - "libcp_defaults" : "RTJar" - }, - { - "id": "ai", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/ai.jar", - "libcp_defaults" : "JRE" - }, - { - "id": "arrays", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/arrays.jar" - }, - { - "id": "bugs", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/bugs.jar", - "libcp_defaults" : "RTJar" - }, - { - "id": "classhierarchy", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/classhierarchy.jar" - }, - { - "id": "code", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/code.jar" - }, - { - "id": "constants", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/constants.jar" - }, - { - "id": "controlflow", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/controlflow.jar" - }, - { - "id": "cornercases", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/cornercases.jar" - }, - { - "id": "dependencies", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/dependencies-1.8-g-parameters-genericsignature-preserveAllLocals.jar" - }, - { - "id": "deprecated", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/deprecated.jar" - }, - { - "id": "equals_hashcode", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/equals_hashcode.jar" - }, - { - "id": "fields-g-8", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/fields-g-8-parameters-genericsignature.jar" - }, - { - "id": "fields-g=none", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/fields-g=none-5.jar" - }, - { - "id": "generictypes", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/generictypes.jar" - }, - { - "id": "immutability", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/immutability.jar" - }, - { - "id": "immutable", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/immutable.jar" - }, - { - "id": "innerclasses", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/innerclasses-1.8-g-parameters-genericsignature.jar" - }, - { - "id": "java8methodresolution", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/java8methodresolution.jar" - }, - { - "id": "jsr_ret", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/jsr_ret-1.3-g-preserveAllLocals-nowarn.jar" - }, - { - "id": "lambdas", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/lambdas-1.8-g-parameters-genericsignature.jar" - }, - { - "id": "methods_types", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/method_types.jar" - }, - { - "id": "methods", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/methods.jar" - }, - { - "id": "metrics", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/metrics.jar" - }, - { - "id": "polymorphism", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/polymorphism.jar" - }, - { - "id": "proxy", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/proxy.jar" - }, - { - "id": "pureness", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/pureness.jar" - }, - { - "id": "signatures", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/signatures.jar" - }, - { - "id": "tactest", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/tactest-8-preserveAllLocals.jar" - }, - { - "id": "type_annotations", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/type_annotations.jar" - }, - { - "id": "types", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/types.jar" - } - ] - } - } - } -} diff --git a/TOOLS/hermes/src/main/resources/hermes.conf b/TOOLS/hermes/src/main/resources/hermes.conf deleted file mode 100644 index 103eadb038..0000000000 --- a/TOOLS/hermes/src/main/resources/hermes.conf +++ /dev/null @@ -1,87 +0,0 @@ -# Configuration for the Hermes code query framework - -org.opalj.hermes { - - # Used to configure the number of locations that are kept per identified feature. - # In general there is no value in keeping "arbitrary large" numbers of locations. - maxLocations = 100000 - - # Configuration of the queries. - queries { - - # It is meaningful to sort the queries by their (expected) runtime in descending order - # to maximize parallelization. - registered = [ - // VERY SLOW - //{ query = org.opalj.hermes.queries.Metrics, activate = true } - //{ query = org.opalj.hermes.queries.MicroPatterns, activate = true } - - // SLOW - //{ query = org.opalj.hermes.queries.FieldAccessStatistics, activate = true } - //{ query = org.opalj.hermes.queries.TrivialReflectionUsage, activate = true } - //{ query = org.opalj.hermes.queries.BytecodeInstructions, activate = true } - - // NOT SO SLOW - //{ query = org.opalj.hermes.queries.RecursiveDataStructures, activate = true } - //{ query = org.opalj.hermes.queries.MethodsWithoutReturns, activate = true } - //{ query = org.opalj.hermes.queries.DebugInformation, activate = true } - - // OK - //{ query = org.opalj.hermes.queries.FanInFanOut, activate = true} - //{ query = org.opalj.hermes.queries.GUIAPIUsage, activate = false } - //{ query = org.opalj.hermes.queries.ClassLoaderAPIUsage, activate = true } - //{ query = org.opalj.hermes.queries.JavaCryptoArchitectureUsage, activate = true } - //{ query = org.opalj.hermes.queries.MethodTypes, activate = true } - //{ query = org.opalj.hermes.queries.ReflectionAPIUsage, activate = true } - //{ query = org.opalj.hermes.queries.SystemAPIUsage, activate = true } - //{ query = org.opalj.hermes.queries.ThreadAPIUsage, activate = true } - //{ query = org.opalj.hermes.queries.UnsafeAPIUsage, activate = true } - //{ query = org.opalj.hermes.queries.JDBCAPIUsage, activate = true } - //{ query = org.opalj.hermes.queries.BytecodeInstrumentationAPIUsage, activate = true } - - // FAST - { query = org.opalj.hermes.queries.ClassTypes, activate = true } - - // BLAZINGLY FAST - { query = org.opalj.hermes.queries.SizeOfInheritanceTree, activate = false } - { query = org.opalj.hermes.queries.ClassFileVersion, activate = false } - - // CALL GRAPH TEST PROJECT RELATED QUERIES (JCG) - { query = org.opalj.hermes.queries.jcg.Classloading, activate = true} - { query = org.opalj.hermes.queries.jcg.DynamicProxy, activate = true } - { query = org.opalj.hermes.queries.jcg.NonVirtualCalls, activate = false } - { query = org.opalj.hermes.queries.jcg.Java8Invokedynamics, activate = false } - { query = org.opalj.hermes.queries.jcg.Java8InterfaceMethods, activate = false } - { query = org.opalj.hermes.queries.jcg.JVMCalls, activate = false} - { query = org.opalj.hermes.queries.jcg.Library, activate = false} - { query = org.opalj.hermes.queries.jcg.ModernReflection, activate = false} - { query = org.opalj.hermes.queries.jcg.NativeMethods, activate = false } - { query = org.opalj.hermes.queries.jcg.NonJavaBytecode1, activate = false} - { query = org.opalj.hermes.queries.jcg.NonJavaBytecode2, activate = false} - { query = org.opalj.hermes.queries.jcg.PackageBoundaries, activate = false } - { query = org.opalj.hermes.queries.jcg.VirtualCalls, activate = false } - { query = org.opalj.hermes.queries.jcg.Reflection, activate = false} - { query = org.opalj.hermes.queries.jcg.Serialization, activate = false} - { query = org.opalj.hermes.queries.jcg.StaticInitializer, activate = false} - { query = org.opalj.hermes.queries.jcg.Types, activate = false } - { query = org.opalj.hermes.queries.jcg.Unsafe, activate = false } - - { query = org.opalj.hermes.queries.ReflectiveFieldWrites, activate = true } - ] - - FanInFanOut { - fanin.categories = 6 - fanin.categorySize = 1 - fanout.categories = 6 - fanout.categorySize = 1 - ratio.categories = 6 - ratio.categorySize = 1 - } - } -} - -org.opalj.br.reader.ClassFileReader { - # Disable rewriting so queries about those features aren't mislead - Invokedynamic.rewrite = false - DynamicConstants.rewrite = false -} \ No newline at end of file diff --git a/TOOLS/hermes/src/main/resources/hermes.json b/TOOLS/hermes/src/main/resources/hermes.json deleted file mode 100644 index cf246d6477..0000000000 --- a/TOOLS/hermes/src/main/resources/hermes.json +++ /dev/null @@ -1,187 +0,0 @@ -{ - "org": { - "opalj": { - "hermes": { - "projects": [ - { - "id": "Apache ANT 1.7.1 - Javac 6", - "cp": "../../OPAL/bi/src/test/resources/classfiles/Apache ANT 1.7.1 - javac 6 default target.jar" - }, - { - "id": "Apache ANT 1.7.1 - Target 1.2", - "cp": "../../OPAL/bi/src/test/resources/classfiles/Apache ANT 1.7.1 - target 1.2.jar" - }, - { - "id": "BAT2XML - Target 1.7", - "cp": "../../OPAL/bi/src/test/resources/classfiles/BAT2XML - target 1.7.jar" - }, - { - "id": "Groovy 2.1.5.jar", - "cp": "../../OPAL/bi/src/test/resources/classfiles/groovy-2.1.5-indy.jar" - }, - { - "id": "Groovy 2.2.1.jar", - "cp": "../../OPAL/bi/src/test/resources/classfiles/groovy-2.2.1-indy.jar" - }, - { - "id": "Batik 1.7.jar", - "cp": "../../OPAL/bi/src/test/resources/classfiles/batik-DOMViewer 1.7.jar", - "libcp": "../../OPAL/bi/src/test/resources/classfiles/batik-util 1.7.jar" - }, - { - "id": "argouml-excerpt", - "cp": "../../OPAL/bi/src/test/resources/classfiles/argouml-excerpt.jar" - }, - { - "id": "lecturedoc_2.10-0.0.0-one-jar", - "cp": "../../OPAL/bi/src/test/resources/classfiles/lecturedoc_2.10-0.0.0-one-jar.jar" - }, - { - "id": "OPAL-SNAPSHOT-0.3", - "cp": "../../OPAL/bi/src/test/resources/classfiles/OPAL-SNAPSHOT-0.3.jar" - }, - { - "id": "OPAL-MultiJar-SNAPSHOT-08-14-2014.jar", - "cp": "../../OPAL/bi/src/test/resources/classfiles/OPAL-MultiJar-SNAPSHOT-08-14-2014.jar" - }, - { - "id": "OPAL-MultiJar-SNAPSHOT-01-04-2018.jar", - "cp": "../../OPAL/bi/src/test/resources/classfiles/OPAL-MultiJar-SNAPSHOT-01-04-2018.jar", - "libcp": "../../OPAL/bi/src/test/resources/classfiles/OPAL-MultiJar-SNAPSHOT-01-04-2018-dependencies" - }, - { - "id": "Multithreaded RPN Calculator ", - "cp": "../../OPAL/bi/src/test/resources/classfiles/Multithreaded RPN Calculator 2008_10_17 - Java 6 all debug info.jar" - }, - { - "id": "JVM Features", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/jvm_features-1.8-g-parameters-genericsignature.jar", - "libcp_defaults": "RTJar" - }, - { - "id": "ai", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/ai.jar", - "libcp_defaults": "JRE" - }, - { - "id": "arrays", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/arrays.jar" - }, - { - "id": "bugs", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/bugs.jar", - "libcp_defaults": "RTJar" - }, - { - "id": "classhierarchy", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/classhierarchy.jar" - }, - { - "id": "code", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/code.jar" - }, - { - "id": "constants", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/constants.jar" - }, - { - "id": "controlflow", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/controlflow.jar" - }, - { - "id": "cornercases", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/cornercases.jar" - }, - { - "id": "dependencies", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/dependencies-1.8-g-parameters-genericsignature-preserveAllLocals.jar" - }, - { - "id": "deprecated", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/deprecated.jar" - }, - { - "id": "equals_hashcode", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/equals_hashcode.jar" - }, - { - "id": "fields-g-8", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/fields-g-8-parameters-genericsignature.jar" - }, - { - "id": "fields-g=none", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/fields-g=none-5.jar" - }, - { - "id": "generictypes", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/generictypes.jar" - }, - { - "id": "immutability", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/immutability.jar" - }, - { - "id": "immutable", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/immutable.jar" - }, - { - "id": "innerclasses", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/innerclasses-1.8-g-parameters-genericsignature.jar" - }, - { - "id": "java8methodresolution", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/java8methodresolution.jar" - }, - { - "id": "jsr_ret", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/jsr_ret-1.3-g-preserveAllLocals-nowarn.jar" - }, - { - "id": "lambdas", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/lambdas-1.8-g-parameters-genericsignature.jar" - }, - { - "id": "methods_types", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/method_types.jar" - }, - { - "id": "methods", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/methods.jar" - }, - { - "id": "metrics", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/metrics.jar" - }, - { - "id": "polymorphism", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/polymorphism.jar" - }, - { - "id": "proxy", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/proxy.jar" - }, - { - "id": "pureness", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/pureness.jar" - }, - { - "id": "signatures", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/signatures.jar" - }, - { - "id": "tactest", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/tactest-8-preserveAllLocals.jar" - }, - { - "id": "type_annotations", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/type_annotations.jar" - }, - { - "id": "types", - "cp": "../../OPAL/bi/target/scala-2.13/resource_managed/test/types.jar" - } - ] - } - } - } -} \ No newline at end of file diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/Hermes.css b/TOOLS/hermes/src/main/resources/org/opalj/hermes/Hermes.css deleted file mode 100644 index 0a1a0aafc5..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/Hermes.css +++ /dev/null @@ -1,8 +0,0 @@ -.table-view .table-column { - -fx-min-width: 4em; -} - -.table-view .column-header .label { - -fx-text-alignment: left; - -fx-text-origin: top; -} diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/Hermes.txt b/TOOLS/hermes/src/main/resources/org/opalj/hermes/Hermes.txt deleted file mode 100644 index c2656fd69c..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/Hermes.txt +++ /dev/null @@ -1,8 +0,0 @@ -OPAL - Hermes - -The parameter has to be the configuration which lists a corpus' projects and, -optionally, the file to which the results should be exported -(\"--csv=\"). If such a file is specified the application will -close automatically after running all analyses. - -java org.opalj.hermes.Hermes diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/HermesCLI.txt b/TOOLS/hermes/src/main/resources/org/opalj/hermes/HermesCLI.txt deleted file mode 100644 index e0672829c2..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/HermesCLI.txt +++ /dev/null @@ -1,20 +0,0 @@ -OPAL - Hermes -Parameters: - Input: - -config the configuration which lists a corpus' projects - Output: - -statistics the csv file to which the results should be exported - -mapping the properties file with the mapping between the feature - queries and the extracted features; format: - =(,)* - where in FeatureIDs every \ is replaced by \\ - ... new line ('\n') is replaced by \n - ... , is replaced by \, - -writeLocations the target directory to which the result file should be exported. - The export will create a file for each project and list all locations - where the given features have been found. - - Other Settings: - [-noProjectStatistics project statistics are not exported] - -java ...HermesCLI -config -statistics -writeLocations diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/OPAL-Logo-Small.png b/TOOLS/hermes/src/main/resources/org/opalj/hermes/OPAL-Logo-Small.png deleted file mode 100644 index a8219d7475..0000000000 Binary files a/TOOLS/hermes/src/main/resources/org/opalj/hermes/OPAL-Logo-Small.png and /dev/null differ diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/OPAL-Logo.png b/TOOLS/hermes/src/main/resources/org/opalj/hermes/OPAL-Logo.png deleted file mode 100644 index 8cb953379e..0000000000 Binary files a/TOOLS/hermes/src/main/resources/org/opalj/hermes/OPAL-Logo.png and /dev/null differ diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/Queries.css b/TOOLS/hermes/src/main/resources/org/opalj/hermes/Queries.css deleted file mode 100644 index a912445bf7..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/Queries.css +++ /dev/null @@ -1,122 +0,0 @@ - -body -{ - margin: 0; - padding: 2em; - font-size: 13px; - font-weight: 200; - color: #333; - background-color: #FFFFFF; - font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif; -} - -h1,h2,h3,h4,h5,h6 -{ - margin: 0 0 .5em; - font-weight: 500; - line-height: 1.1; -} - -h1 { font-size: 1.25em; } -h2 { font-size: 1.15em; } -h3 { font-size: 1.075em; } -h4 { font-size: 1.0125em; } -h5 { font-size: 1em; } -h6 { font-size: .875em; } - -p -{ - margin: 0 0 1.5em; - line-height: 1.5; -} - -blockquote -{ - padding: 1em 2em; - margin: 0 0 2em; - border-left: 5px solid #eee; -} - -hr -{ - height: 0; - margin-top: 1em; - margin-bottom: 2em; - border: 0; - border-top: 1px solid #ddd; -} - -table -{ - background-color: transparent; - border-spacing: 0; - border-collapse: collapse; - border-top: 1px solid #ddd; -} - -th, td -{ - padding: .5em 1em; - vertical-align: top; - text-align: left; - border-bottom: 1px solid #ddd; -} - -a:link { color: royalblue; } -a:visited { color: purple; } -a:focus { color: black; } -a:hover { color: green; } -a:active { color: red; } - - -.container -{ - max-width: 50em; - margin: 0 auto; - background-color: #fff; -} - -.header -{ - color: #fff; - background: #999; - padding: 1em 1.25em; -} - -.header-heading { margin: 0; } - -.content { padding: 1em 1.25em; } - -.footer -{ - color: #fff; - background: #000; - padding: 1em 1.25em; -} - -.table -{ - width: 100%; - max-width: 100%; - margin-bottom: 20px; -} - -.list-unstyled -{ - padding-left: 0; - list-style: none; -} - -.list-inline -{ - padding-left: 0; - margin-left: -5px; - list-style: none; -} - -.list-inline > li -{ - display: inline-block; - padding-right: 5px; - padding-left: 5px; -} diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/BytecodeInstrumentationAPIUsage.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/BytecodeInstrumentationAPIUsage.markdown deleted file mode 100644 index a7bc1abbbe..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/BytecodeInstrumentationAPIUsage.markdown +++ /dev/null @@ -1,20 +0,0 @@ -# BytecodeInstrumentation API Usage - -Extracts usage information about the `java.lang.instrument.Instrumentation` interface which allows -to instrument Java bytecode over JVM TI. - -## Class-File (Re-)Transformation - -Methods in this category facilitate dynamic transformation of classes that have already been loaded. - -## Native Method Instrumentation - -Methods in this category allow native methods to be instrumented by providing a JVM-aware mechanism for wrapping the native method. - -## Appending Class Loader Search - -Methods in this category allow instrumentation support classes to be defined in the appropriate class loader. - -## Retrieve Classes Information - -Methods in this categroy allow to retrieve information about currently loaded and instantiated classes. diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/ClassFileVersion.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/ClassFileVersion.markdown deleted file mode 100644 index 75db1a9e86..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/ClassFileVersion.markdown +++ /dev/null @@ -1,47 +0,0 @@ -# Class File Version -Extracts the class file version of each class file belonging to the project. - -## Note -Class files using Java 1.0 and 1.1. are grouped together because they use the same class file version number. - -## Major Features of Selected Class File Versions - -### Java 6 and newer -Generally, don't have *jsr*/*ret* instructions anymore, because Java stopped generating them. However, technically they are still allowed and were only forbidden with version 51 (*cf. Java Virtual Machine Specification - Constraints on Java Virtual Machine code - Static Constraints*) - -### Java 7 and newer -May have *invokedynamic* instructions; however, Java only started using *invokedynamic* with Java 8 and all other languages (e.g., Clojure, Groovy, Scala) also did not starte using them right away. - -### Java 8 and newer - - usually have *invokedynamic* instructions because Java started using it - - added default methods (this changed the way how method resolution works) - - -## Attributes per Class File Version - - - - - - - - - - - - - - - - - - - - - - - - - - -
AttributeClassFileVersionJava SE
ConstantValue45.31.0.2
Code45.31.0.2
Exceptions45.31.0.2
SourceFile45.31.0.2
LineNumberTable45.31.0.2
LocalVariableTable45.31.0.2
InnerClasses45.31.1
Synthetic45.31.1
Deprecated45.31.1
EnclosingMethod49.05.0
Signature49.05.0
SourceDebugExtension49.05.0
LocalVariableTypeTable 49.05.0
RuntimeVisibleAnnotations 49.0 5.0
RuntimeInvisibleAnnotations 49.0 5.0
RuntimeVisibleParameterAnnotations 49.0 5.0
RuntimeInvisibleParameterAnnotations 49.0 5.0
AnnotationDefault 49.0 5.0
StackMapTable 50.0 6
BootstrapMethods 51.0 7
RuntimeVisibleTypeAnnotations 52.0 8
RuntimeInvisibleTypeAnnotations 52.0 8
MethodParameters 52.0 8
diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/ClassLoaderAPIUsage.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/ClassLoaderAPIUsage.markdown deleted file mode 100644 index 78749c09f2..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/ClassLoaderAPIUsage.markdown +++ /dev/null @@ -1,9 +0,0 @@ -# ClassLoader API Usage - -Extracts the usage of methods of `java.lang.ClassLoader` in a project and also checks for -custom class loaders. - -- detects custom ClassLoaders that extend the `java.lang.ClassLoader`class. -- counts how often the `SystemClassLoader` is retrieved -- counts how often a class loader is retrieved in general -- counts how often resources or system resources are acquired via a class loader \ No newline at end of file diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/ClassTypes.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/ClassTypes.markdown deleted file mode 100644 index 2ea6e1d387..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/ClassTypes.markdown +++ /dev/null @@ -1,44 +0,0 @@ -# Class Types -Extracts the information about the type of the specified class. - -## (concrete) classes -Definition of regular non-abstract class. - -## abstract classes -Definition of an abstract class. - -## annotations -Definition of an annotation. - -## enumerations -Definition of an enumeration. - -## marker interfaces -A marker interface is an empty interface. For example, `java.io.Serializable` or `java.lang.Cloneable` are marker interfaces. - -## functional interfaces -*(Also called: single abstract method (SAM) interfaces)* - -> [Java 8 Lang. Spec.] A functional interface is an interface that has just one abstract method (aside from the methods of Object), and thus represents a single function contract. This "single" method may take the form of multiple abstract methods with override- equivalent signatures inherited from superinterfaces; in this case, the inherited methods logically represent a single method. - -Such interfaces are particularly used in combination with lambda expressions. - -[Functional Interfaces Demo Code](https://github.com/opalj/opal/blob/develop/OPAL/bi/src/test/fixtures-java/projects/jvm_features/class_types/SAMInterface.java) - - -## Interface with default methods (*Java >8*) -The interface defines a concrete instance method (a so-called default method.) - -## Interface which defines a static method (*Java >8*) -An interface which defines a static method. - - - > The static initializer is not considered in this case. - > The latter is always generated when a complex constant field is defined; e.g., - > - > public interface X { - > public static final String s = new String(new byte[]{25,26,27}) - > } - -## module (Java >9) -The class file defines a Java 9 module. diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/DebugInformation.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/DebugInformation.markdown deleted file mode 100644 index 0a3f6598c2..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/DebugInformation.markdown +++ /dev/null @@ -1,5 +0,0 @@ -# Debug Information - -Identifies those class file elements which have debug information. In case of the class file itself, the only potential debug information is the source (file) of the class file. In case of a method with a body, the potential debug information is the `local variable table`, the `local variable type table` and the `line number table`. - -This query simply checks for the presence of the attributes. diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/FanInFanOut.md b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/FanInFanOut.md deleted file mode 100644 index 8b8fa7c64a..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/FanInFanOut.md +++ /dev/null @@ -1,28 +0,0 @@ -# Fan-In and Fan-Out - -The query computes for each class the *Fan-In*, *Fan-Out* and the ratio between both. - -## Configuration - -### Configuration Options - -All three feature kinds are configurable over two dimensions. The first dimension is the number of catergories which also determines the number of features per metric. (Each category relates to a feature.) The second dimension is the cardinality of each category. It determines how many values belong to a single feature. - -Lets assume we have 3 categories with a cardinality of 2. This will result in the following categories/features w.r.t. the fan in/out: - -Category 1: `Fan In/Out < 2` - -Category 2: `2 ≤ Fan In/Out < 4` - -Category 3: `Fan In/Out ≥ 4` - -### Default Configuration - - org.opalj.hermes.queries.FanInFanOut { - fanin.categories = 6 - fanin.categorySize = 3 - fanout.categories = 6 - fanout.categorySize = 2 - ratio.categories = 4 - ratio.categorySize = 0.25 - } diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/FieldAccessStatistics.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/FieldAccessStatistics.markdown deleted file mode 100644 index 0f31beb633..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/FieldAccessStatistics.markdown +++ /dev/null @@ -1,6 +0,0 @@ -# Field Access Statistics - -Identifies unused fields and those fields which are not private but which are only accessed by its defining type – in the scope of the project. Fields which define compile-time constants. *Primitive constants – e.g., `public final static double pi = 3.1d` – and `String` constants are typically inlined by compilers and it is therefore "normal" that these fields have no (more) use and they are therefore ignored.* - - -Reflective accesses are not considered. diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/GUIAPIUsage.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/GUIAPIUsage.markdown deleted file mode 100644 index 7c45ca726c..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/GUIAPIUsage.markdown +++ /dev/null @@ -1,9 +0,0 @@ -#GUI API Usage - -Scans a class file's constant pool to check whether it refers to packages that belong to an API -for graphical user interfaces. Currently supported APIs are: - - - JavaFX (javafx.) - - SWT (org.eclipse.swt) - - Swing (javax.swing) - - AWT (java.awt) \ No newline at end of file diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/JDBCAPIUsage.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/JDBCAPIUsage.markdown deleted file mode 100644 index 0ee05a271f..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/JDBCAPIUsage.markdown +++ /dev/null @@ -1,9 +0,0 @@ -# JDBC API Usage - -The analysis derives the following features: - - 1. retrieving a database connection - 1. doing a database rollback - 1. usage of a `java.sql.Statement` - 1. usage of a `java.sql.PreparedStatement` - 1. usage of a `java.sql.CallableStatement` diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/JavaCryptoArchitectureUsage.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/JavaCryptoArchitectureUsage.markdown deleted file mode 100644 index 94ad9555cc..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/JavaCryptoArchitectureUsage.markdown +++ /dev/null @@ -1,48 +0,0 @@ -# Usage of the Java Crypto Architecture (JCA) - -Extracts the information about the usage of the core classes and interfaces of the Java Crypto -Architecture. The classes have been selected according to the official [JCA Reference Guide][1]. - -The analysis checks in particular for constructor calls or the `getInstance` method of a given -class/interface. - -## Algorithm kinds - -The analysis supports the following cryptographic primitives: - -- `Cipher` -- `Mac` -- `MessageDigest` -- `Signature` -- `SecureRandom` - -> Note: Each of these types represents a single feature. - -## Key Handling - -The _Key Handling_ group represents whether some of the following classes/interfaces are used: - -- `KeyFactory` -- `SecretKeyFactory` -- `KeyGenerator` -- `KeyPairGenerator` -- `KeyAgreement` - -> Note: Subclasses are not considered. However, querying theses types already gives a first intuition about -> the usage of cryptographic keys. Especially the factories are often used. - -## Key Store - -The _Key Store_ class checks whether a `KeyStore` is used. - -## Certificate Handling - -The _Certificate Handling_ group checks whether calls to the following certificate classes/interfaces -appear within the codebase: - -- `CertificateFactory` -- `CertPathBuilder` -- `CertPathValidator` -- `CertStore` - -[1]: http://docs.oracle.com/javase/6/docs/technotes/guides/security/crypto/CryptoSpec.html#CoreClasses \ No newline at end of file diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/LambdasAndMethodReferences.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/LambdasAndMethodReferences.markdown deleted file mode 100644 index 6099d2a771..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/LambdasAndMethodReferences.markdown +++ /dev/null @@ -1,479 +0,0 @@ -#MethodReferences -Test cases in the presence of method references. - -##MR1 -[//]: # (MAIN: mr1/Class) -Tests method reference that deals with interface default methods (Java 8 or higher). - -```java -// mr1/Class.java -package mr1; - -import lib.annotations.callgraph.IndirectCall; - -class Class implements Interface { - - @FunctionalInterface public interface FIBoolean { - boolean get(); - } - - @IndirectCall( - name = "method", returnType = boolean.class, line = 17, - resolvedTargets = "Lmr1/Interface;" - ) - public static boolean callViaMethodReference(Interface i) { - FIBoolean bc = i::method; - return bc.get(); - } - - public static void main(String[] args){ - Class cls = new Class(); - callViaMethodReference(cls); - } -} - -interface Interface { - default boolean method() { - return true; - } -} -``` -[//]: # (END) - -##MR2 -[//]: # (MAIN: mr2/Class) -Tests method reference that result in an *INVOKESPECIAL* call issued by calling a private method. - -```java -// mr2/Class.java -package mr2; - -import lib.annotations.callgraph.IndirectCall; - -class Class { - - private String getTypeName() { return "Lmr2/Class;";} - - @IndirectCall( - name = "getTypeName", returnType = String.class, line = 14, - resolvedTargets = "Lmr2/Class;") - public void callViaMethodReference(){ - java.util.function.Supplier stringSupplier = this::getTypeName; - stringSupplier.get(); - } - - public static void main(String[] args){ - Class cls = new Class(); - cls.callViaMethodReference(); - } -} -``` -[//]: # (END) - -##MR3 -[//]: # (MAIN: mr3/Class) -Tests method reference that result in an *INVOKESPECIAL* call issued by calling a protected method -from a super class. - -```java -// mr3/Class.java -package mr3; - -import lib.annotations.callgraph.IndirectCall; - -class Class extends SuperClass { - - @IndirectCall( - name = "getTypeName", returnType = String.class, line = 12, - resolvedTargets = "Lmr3/SuperClass;") - public void callViaMethodReference(){ - java.util.function.Supplier stringSupplier = super::getTypeName; - stringSupplier.get(); - } - - public static void main(String[] args){ - Class cls = new Class(); - cls.callViaMethodReference(); - } -} - -class SuperClass{ - protected String getTypeName() { return "Lmr3/SuperClass;";} -} -``` -[//]: # (END) - -##MR4 -[//]: # (MAIN: mr/Class) -Tests method reference that result in an *INVOKESTATIC* call issued by calling a static method -from a super class. - -```java -// mr/Class.java -package mr4; - -import java.util.function.Supplier; -import lib.annotations.callgraph.IndirectCall; - -class Class { - - @IndirectCall( - name = "getTypeName", returnType = String.class, line = 13, - resolvedTargets = "Lmr/Class;") - public static void main(String[] args){ - Supplier stringSupplier = Class::getTypeName; - stringSupplier.get(); - } - - static String getTypeName() { return "Lmr/Class"; } -} -``` -[//]: # (END) - -##MR5 -[//]: # (MAIN: mr/Class) -Tests method reference dealing with primitive type parameters. -from a super class. - -```java -// mr5/Class.java -package mr5; - -import java.util.function.Supplier; -import lib.annotations.callgraph.IndirectCall; - -class Class { - - public static double sum(double a, double b) { return a + b; } - - @FunctionalInterface public interface FIDoubleDouble { - double apply(double a, double b); - } - - @IndirectCall( - name = "sum", returnType = double.class, line = 19, - resolvedTargets = "Lmr5/Class;") - public static void main(String[] args){ - FIDoubleDouble fidd = Class::sum; - fidd.apply(1d,2d); - } -} -``` -[//]: # (END) - -##MR6 -[//]: # (MAIN: mr6/Class) -Tests method reference that result in a constructor call. - -```java -// mr6/Class.java -package mr6; - -import java.util.function.Supplier; -import lib.annotations.callgraph.IndirectCall; - -class Class { - - public Class(){} - - @IndirectCall( - name = "", line = 14, resolvedTargets = "Lmr6/Class;") - public static void main(String[] args){ - Supplier classSupplier = Class::new; - classSupplier.get(); - } -} -``` -[//]: # (END) - -##MR7 -[//]: # (MAIN: mr7/Class) -Tests method reference that result in a method invocation where the method is defined in a super class. - -```java -// mr7/Class.java -package mr7; - -import lib.annotations.callgraph.IndirectCall; - -class Class extends SuperClass{ - - @IndirectCall( - name = "version", returnType = String.class, line = 13, - resolvedTargets = "Lmr7/SuperClass;") - public static void main(String[] args){ - Class cls = new Class(); - java.util.function.Supplier classSupplier = cls::version; - classSupplier.get(); - } -} - -class SuperClass { - public String version() { return "1.0"; } -} -``` -[//]: # (END) - -##MR8 -[//]: # (MAIN: mr8/Class) -Tests method reference where the method is defined in a super interface. - -```java -// mr8/Class.java -package mr8; - -import lib.annotations.callgraph.IndirectCall; - -class Class implements Interface { - - @IndirectCall( - name = "version", returnType = String.class, line = 13, - resolvedTargets = "Lmr8/Interface;") - public static void main(String[] args){ - Class cls = new Class(); - java.util.function.Supplier classSupplier = cls::version; - classSupplier.get(); - } -} - -interface Interface{ - default String version() { return "0.2"; } -} -``` -[//]: # (END) - -#Lambdas -Test cases in the presence of lambdas. - -##Lambda1 -[//]: # (MAIN: lambda1/Class) -Tests the invocation of a lambda with a integer boxing. - -```java -// lambda1/Class.java -package lambda1; - -import lib.annotations.callgraph.IndirectCall; -import java.util.function.Function; - -class Class { - @IndirectCall( - name = "doSomething", line = 11, resolvedTargets = "Llambda1/Class;") - public static void main(String[] args){ - Function isEven = (Integer a) -> { - doSomething(); - return a % 2 == 0; - }; - isEven.apply(2); - } - - private static void doSomething(){ - // call in lambda - } -} -``` -[//]: # (END) - - -##Lambda4 -[//]: # (MAIN: lambda4/Class) -Tests the invocation on an object receiver captured in a lambda. - -```java -// lambda4/Class.java -package lambda4; - -import lib.annotations.callgraph.IndirectCalls; -import lib.annotations.callgraph.IndirectCall; - -class Class { - - @FunctionalInterface interface Runnable { - void run(); - } - - public static final Class cls = new SubClass(); - public static Class pCls = new SubClass(); - - @IndirectCalls({ - @IndirectCall(name = "doSomething", line = 22, resolvedTargets = "Llambda4/SubClass;"), - @IndirectCall(name = "doSomething", line = 23, resolvedTargets = "Llambda4/SubClass;"), - @IndirectCall(name = "doSomething", line = 24, resolvedTargets = "Llambda4/SubClass;") - }) - public static void main(String[] args){ - Class lCls = new SubClass(); - Runnable psfField = () -> cls.doSomething(); - Runnable psField = () -> pCls.doSomething(); - Runnable localVar = () -> lCls.doSomething(); - - psfField.run(); - psField.run(); - localVar.run(); - } - - public void doSomething(){ } -} - -class SubClass extends Class { - public void doSomething(){ } -} - -class SubSubClass extends SubClass { - public void doSomething(){ } -} -``` -[//]: # (END) - -##Lambda5 -[//]: # (MAIN: lambda5/Class) -Tests the invocation on an object receiver captured in a lambda. - -```java -// lambda5/Class.java -package lambda5; - -import lib.annotations.callgraph.CallSites; -import lib.annotations.callgraph.CallSite; - -class Class { - - @FunctionalInterface interface Runnable { - void run(); - } - - public static void doSomething(){ } - - @CallSites({ - @CallSite(name = "equals", line = 26, resolvedTargets = "Ljava/lang/Object;"), - @CallSite(name = "getClass", line = 27, resolvedTargets = "Ljava/lang/Object;"), - @CallSite(name = "hashCode", line = 28, resolvedTargets = "Ljava/lang/Object;"), - @CallSite(name = "notify", line = 29, resolvedTargets = "Ljava/lang/Object;"), - @CallSite(name = "notifyAll", line = 30, resolvedTargets = "Ljava/lang/Object;"), - @CallSite(name = "toString", line = 31, resolvedTargets = "Ljava/lang/Object;"), - @CallSite(name = "wait", line = 32, resolvedTargets = "Ljava/lang/Object;") - }) - public static void main(String[] args) throws InterruptedException { - Runnable lambda = () -> doSomething(); - Object o = new Object(); - lambda.equals(o); - lambda.getClass(); - lambda.hashCode(); - lambda.notify(); - lambda.notifyAll(); - lambda.toString(); - lambda.wait(); - } -} -``` -[//]: # (END) - -##Lambda6 -[//]: # (MAIN: lambda6/Class) -Tests the invocation on an object receiver captured in a lambda. - -```java -// lambda6/Class.java -package lambda6; - -import lib.annotations.callgraph.IndirectCall; - -class Class { - - @FunctionalInterface interface Runnable { - void run(); - } - - public static void doSomething(){ } - - @IndirectCall(name = "doSomething", line = 17, resolvedTargets = "Llambda6/LambdaProvider;") - public static void main(String[] args) { - Runnable lambda = LambdaProvider.getRunnable(); - - lambda.run(); - } -} - -class LambdaProvider { - - public static void doSomething(){ - /* do something */ - } - - public static Class.Runnable getRunnable(){ - return () -> LambdaProvider.doSomething(); - } -} -``` -[//]: # (END) - -##Lambda7 -[//]: # (MAIN: lambda7/Class) -Tests the invocation of a lambda when it was written to an array and later retrieved and applied. - -```java -// lambda7/Class.java -package lambda7; - -import lib.annotations.callgraph.IndirectCall; - -class Class { - - @FunctionalInterface interface Runnable { - void run(); - } - - public static void doSomething(){ - /* do something */ - } - - public static Runnable[] lambdaArray = new Runnable[10]; - - @IndirectCall(name = "doSomething", line = 25, resolvedTargets = "Llambda7/Class;") - public static void main(String[] args) { - Runnable r1 = () -> doSomething(); - lambdaArray[0] = r1; - Runnable same = lambdaArray[0]; - - same.run(); - } -} - -final class Math { - public static int PI(){ - return 3; - } -} -``` -[//]: # (END) - -##Lambda8 -[//]: # (MAIN: lambda8/Class) -Tests the invocation of an intersection type lambda. - -```java -// lambda8/Class.java -package lambda8; - -import lib.annotations.callgraph.IndirectCall; - -class Class { - - public interface MyMarkerInterface1 {} - public interface MyMarkerInterface2 {} - - public @FunctionalInterface interface Runnable { - void run(); - } - - public static void doSomething(){ - /* do something */ - } - - @IndirectCall(name = "doSomething", line = 21, resolvedTargets = "Llambda8/Class;") - public static void main(String[] args) { - Runnable run = (Runnable & MyMarkerInterface1 & MyMarkerInterface2) () -> doSomething(); - run.run(); - } -} -``` -[//]: # (END) \ No newline at end of file diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/MethodTypes.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/MethodTypes.markdown deleted file mode 100644 index ed8d7fff96..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/MethodTypes.markdown +++ /dev/null @@ -1,45 +0,0 @@ -# Method Types -Extracts the information about the type of the specified methods. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
nativesyntheticbridgesynchronizedvarargs
static✓✓✗✓✓
static { }
<clinit>
✗✗✗✗✗
instance✓✓
(except of Enum.values/valueOf)
✓✓✓
constructor✗✓
(the default constructor is never synthetic)
✗✗✓
diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/MethodsWithoutReturns.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/MethodsWithoutReturns.markdown deleted file mode 100644 index 2d251c4999..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/MethodsWithoutReturns.markdown +++ /dev/null @@ -1,19 +0,0 @@ -# Methods Without Returns - -This analysis derives two features: - - 1. Those methods that neither return normally nor abnormally. - E.g., - - while(true) { - try { ...} - catch { - case t : Throwable => /* ignored */ - } - } - - or - - while (true){;} - - 1. Those methods that – if at all – only return abnormally. diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/Metrics.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/Metrics.markdown deleted file mode 100644 index 36f24756c5..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/Metrics.markdown +++ /dev/null @@ -1,34 +0,0 @@ -# Metrics -Computes basic metrics for a given project. Each given metric is represented in different feature categories. - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
Fields Per Class (FPC)Extracts how often a class has 0, between 1 and 3, between 4 and 10, or more than 10 fields.
Methods Per Class (MPC)Extracts how often a class has 0, between 1 and 3, between 4 and 10, or more than 10 methods.
Classes Per Package (CPP)Extracts how often a package has between 1 and 3, between 4 and 10, or more than 10 classes.
McCabeExtracts how often a method has a complexity of 1 (linear), between 2 and 3, between 4 and 10, or more than 10. - Please note that this analysis also takes exceptions into account.
number of children (NOC)Extracts how often a class has 0, between 1 and 3, between 4 and 10, or more than 10 children. - This metric is the number of direct descendants (subclasses) for each class. Classes with large number of children are considered to be difficult to modify and usually require more testing because of the effects on changes on all the children. They are also considered more complex and fault-prone because a class with numerous children may have to provide services in a larger number of contexts and therefore must be more flexible [1].
- -[1] Basili, V. R., Briand, L. C., and Melo, W. L., "A Validation of Object Orient Design Metrics as -Quality Indicators," IEEE Transactions on Software Engineering, vol. 21, pp. 751-761, 1996. \ No newline at end of file diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/MicroPatterns.md b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/MicroPatterns.md deleted file mode 100644 index 251239641e..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/MicroPatterns.md +++ /dev/null @@ -1,84 +0,0 @@ -# Micro Patterns -Micro Patterns identify special usages of classes, their fields and methods -(cf. [Micro Patterns in Java Code](https://pdfs.semanticscholar.org/ddb5/037b14518890040a170a0fa5199e1d360e18.pdf)). - -### Designator -An interface with absolutely no members. - -### Taxonomy -An empty interface extending another interface. - -### Joiner -An empty interface joining two or more superinterfaces. - -### Pool -A class which declares only static final fields, but no methods. - -### Function Pointer -A class with a single public instance method, but with no fields. - -### Function Object -A class with a single public instance method, and at least one instance field. - -### Cobol Like -A class with a single static method, but no instance members. - -### Stateless -A class with no fields, other than static final ones. - -### Common State -A class in which all fields are static. - -### Immutable -A class with several instance fields, which are assigned exactly once, during instance construction. - -### Restricted Creation -A class with no public constructors, and at least one static field of the same type as the class. - -### Sampler -A class with one or more public constructors, and at least one static field of the same type as the class. - -### Box -A class which has exactly one, mutable, instance field. - -### Compound Box -A class with exactly one non primitive instance field. - -### Canopy -A class with exactly one instance field that it assigned exactly once, during instance construction. - -### Record -A class in which all fields are public, no declared methods. - -### Data Manager -A class where all methods are either setters or getters. - -### Sink -A class whose methods do not propagate calls to any other class. - -### Outline -A class where at least two methods invoke an abstract method on `this`. - -### Trait -An abstract class which has no state. - -### State Machine -An interface whose methods accept no parameters. - -### Pure Type -A class with only abstract methods, and no static members, and no fields. - -### Augmented Type -Only abstract methods and three or more static final fields of the same type. - -### Pseudo Class -A class which can be rewritten as an interface: no concrete methods, only static fields. - -### Implementor -A concrete class, where all the methods override inherited abstract methods. - -### Overrider -A class in which all methods override inherited, non-abstract methods. - -### Extender -A class which extends the inherited protocol, without overriding any methods. diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/RecursiveDataStructures.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/RecursiveDataStructures.markdown deleted file mode 100644 index fe742965ce..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/RecursiveDataStructures.markdown +++ /dev/null @@ -1,28 +0,0 @@ -# Recursive Data Structures - -Identifies recursive data structures. In general, a data structure is recursive if it may contain values of itself. - -One of the simplest examples of a recursive data structure is a linked list. Another one are the nodes of a tree, as, e.g., used by the composite pattern. Next, we see a very simple linked list definition. - - case class List[T](value : T, rest: List[T]) - -Analyzing programs with recursive data-structures is particularly interesting, e.g., when developing field sensitive analyses. - -The query finds self and mutually recursive data structures. The query first builds a kind of **data-type dependency graph** and then searches for strongly connected components in that graph. For example, the graph related to the relations for the following code: - - class U(v : U, i : Int) - class X(v : Y, isDone : Boolean) extends Object - class Y(v1 : Z, v2 : Z) extends U - class Z(v : X, mutex : Object) extends Object - -would be: - - Graph ( - Object -> { } - U -> { U } // ⟲ - X -> { Y } - Y -> { Z } - Z -> { X, Object } - ) - -I.e., though the `mutex` field of `Z` could also reference a Y or Z value, this would not be considered because it is in general not relevant. Overall, the algorithm would identify U and Y as being self-recursive data-structures. diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/ReflectionAPIUsage.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/ReflectionAPIUsage.markdown deleted file mode 100644 index 8a96ea884e..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/ReflectionAPIUsage.markdown +++ /dev/null @@ -1,26 +0,0 @@ -#Reflection API Usage - -This analysis derives which methods/functionality of Java's Reflection API is used how often. - -## Core Reflection API - -The following features are targeted by the analysis: - -- Usage of `Class.forName` -- (reflective) creation of new Instances -- (reflective) field write -- (reflective) field read -- setting the accessibility of `Field`, `Method`, `Constructor`, - or other `AccessibleObject` objects in general -- (reflective) method invocations - -## Reflection with MethodHandles - -Factory methods provided by `java.lang.invoke.MethodHandles.Lookup Lookup` can be used to convert -any class member represented by a Core Reflection API object to a behaviorally equivalent -`MethodHandle`, therefore, `MethodHandle` objects are relevant when assessing reflection usage of -a project. - -- usage of `MethodHandles.Lookup` -- `MethodHandle` invocations over `MethodHandle.invoke`, `MethodHandle.invokeExact`, and `MethodHandle.invokeWithArguments` -- creation of `java.lang.reflect.Proxy` to customize the dispatch method invocations \ No newline at end of file diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/SizeOfInheritanceTree.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/SizeOfInheritanceTree.markdown deleted file mode 100644 index 487c242367..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/SizeOfInheritanceTree.markdown +++ /dev/null @@ -1,5 +0,0 @@ -# Size of Inheritance tree - -Computes for each type the size of the inheritance tree and then assigns the type to one of the complexity categories. If the supertype information is not known to be complete then the overall size is rated as unknown. This generally happens when a project type inherits from a library type and the respective library is not part of the analysis. - -The size of inheritance tree is equal to the number of unique classes and interfaces a given class inherits from. For example, for `java.lang.Object` the size is `0`. For the marker interface `java.io.Serializable` the size is `1`. (In Java every type (ex-/implicitly) inherits from `java.lang.Object.`) diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/SystemAPIUsage.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/SystemAPIUsage.markdown deleted file mode 100644 index a2ac2bbf9d..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/SystemAPIUsage.markdown +++ /dev/null @@ -1,21 +0,0 @@ -# java.lang.System and java.lang.Runtime API - -Represents the usage of core methods of `java.lang.System` or `java.lang.Runtime` that are related to the state of the JVM, Permissions, or to accessing the underlying operating system. - -## Command Execution -Counts method calls that create external processes. In Java that can be either achieved using `Runtime.exec(...)` or using `java.lang.ProcessBuilder`. - -## JVM Exit -Counts calls to JVM methods (`Runtime.halt`/`Runtime.exit`) that stop the JVM. - -## Native Libraries -The *Native Libraries* feature reflects the usage of native libraries loaded using `java.lang.Runtime`. The count shows how many load library calls are found in the project. - -## SecurityManager -Counts usages of the `java.lang.SecurityManager` in a project. This includes getting as well as setting an instance of `java.lang.SecurityManager`. - -## Sound -The Sound group counts how often the sound API to play songs is used. This group only considers the classes in the two packages `javax.sound.sampled` and `javax.scene.media` - -## Environment -Any access or query to the system's environment variables is captured by the `Information` group. diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/ThreadAPIUsage.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/ThreadAPIUsage.markdown deleted file mode 100644 index d181d0fad2..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/ThreadAPIUsage.markdown +++ /dev/null @@ -1,15 +0,0 @@ -# Usage of Thread-related API - -Represents the usage API that is related to thread handling. - -## java.lang.Object - -Usage of notify, notifyAll, wait - -## java.lang.Thread - -Constructor and control method usage. Also includes deprecated methods. - -## java.lang.ThreadGroup - -Constructor and control method usage. Also includes deprecated methods. diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/TrivialReflectionUsage.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/TrivialReflectionUsage.markdown deleted file mode 100644 index 533ba8b03d..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/TrivialReflectionUsage.markdown +++ /dev/null @@ -1,13 +0,0 @@ -#Trivial Reflection Usage - -Counts the number of cases where `Class.forName` calls can be trivially resolved, because the respective String(s) are directly available. E.g., - - String className = "com.here.MyLAF" - Class.forName(className) - -or - - String className = "com.here.DefaultLAF" - if() - className = "com.here.OtherLAF" - Class.forName(className) diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/UnsafeAPIUsage.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/UnsafeAPIUsage.markdown deleted file mode 100644 index a8cc0d71a4..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/UnsafeAPIUsage.markdown +++ /dev/null @@ -1,93 +0,0 @@ -# Unsafe API Usage - -Extracts calls of methods which use `sun.misc.Unsafe`. Closely related methods are grouped according to the mapping given in the paper: -[Use at Your Own Risk: The Java Unsafe API in the Wild][1] **by Lues Mastrangelo et al.** As described in the paper, the analysis supports the following categories: - -## Alloc - The *Alloc* group contains only the `allocateInstance` - method, which allows the developer to allocate a Java - object without executing a constructor. - -## Array - The *Array* group contains methods and fields for computing - relative addresses of array elements. The fields were - added as a simpler and potentially faster alternative in a - more recent version of Unsafe. The value of all fields in - this group are constants initialized with the result of a call - to either `arrayBaseOffset` or `arrayIndexScale` in the Array - group. - -## CompareAndSwap - The *CAS* group contains methods to atomically compare-and-swap - a Java variable. These operations are implemented - using processor-specific atomic instructions. For - instance, on x86 architectures, compareAndSwapInt is - implemented using the `CMPXCHG` machine instruction. - -## Class - Methods of the *Class* group are used to dynamically - load and check Java classes. - -## Fence - The methods of the *Fence* group provide memory fences - to ensure loads and stores are visible to other threads. - These methods are implemented using processor-specific instructions. - -## Fetch & Add - The *Fetch & Add* group, like the CAS group, allows the - programmer to atomically update a Java variable. This - group of methods was also added recently in Java 8. - -## Heap, Heap Get and Heap Put - The *Heap* group methods are used to directly access - memory in the Java heap. The Heap Get and Heap Put - groups allow the developer to load and store a Java variable. - -## Misc - The *Misc* group contains the method getLoadAverage, to - get the load average in the operating system run queue - assigned to the available processors. - -## Monitor - The *Monitor* group contains methods to explicitly manage - Java monitors. - -## Off-Heap - The *Off-Heap* group provides access to unmanaged - memory, enabling explicit memory management. Similarly - to the Heap Get and Heap Put groups, the OffHeap - Get and Off-Heap Put groups allow the developer - to load and store values in Off-Heap memory. The usage - of these methods is non-negligible, with getByte - and putByte dominating the rest. The value of the - *ADDRESS SIZE* field is the result of the method *addressSize()*. - -## Offset - Methods of the *Offset* group are used to compute the location - of fields within Java objects. The offsets are used - in calls to many other sun.misc.Unsafe methods, for instance - those in the Heap Get, Heap Put, and the CAS - groups. - -## Ordered Put - The *Ordered Put* group has methods to store to a Java - variable without emitting any memory barrier but guaranteeing - no reordering across the store. - -## Park - The park and unpark methods are contained in the *Park* - group. With them, it is possible to block and unblock a - thread's execution. - -## Throw - The throwException method is contained in the *Throw* - group, and allows one to throw checked exceptions without - declaring them in the throws clause. - -## Volatile Get & Put - The *Volatile Get and Volatile Put* groups allow - the developer to store a value in a Java variable with - volatile semantics. - - -[1]: http://dl.acm.org/citation.cfm?doid=2814270.2814313 diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/DynamicProxy.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/DynamicProxy.markdown deleted file mode 100644 index 545af5ac09..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/DynamicProxy.markdown +++ /dev/null @@ -1,68 +0,0 @@ -#DynamicProxies -The `java.lang.reflect.InvocationHandler` enables the implementation of dynamic proxies. -##DP1 -[//]: # (MAIN: dp.Main) -Tests a simple proxy implementation. - -```java -// dp/Foo.java -package dp; - -public interface Foo { Object bar(Object obj); } -``` - -```java -// dp/FooImpl.java -package dp; - -public class FooImpl implements Foo { - public Object bar(Object obj) { - return obj; - } -} -``` - -```java -// dp/DebugProxy.java -package dp; - -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.lang.reflect.InvocationHandler; - -public class DebugProxy implements InvocationHandler { - private Object obj; - - public static Object newInstance(Object obj) { - return Proxy.newProxyInstance( - obj.getClass().getClassLoader(),obj.getClass().getInterfaces(), - new DebugProxy(obj)); - } - - private DebugProxy(Object obj) { this.obj = obj; } - - public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { - System.out.println("before method " + m.getName()); - return m.invoke(obj, args); - } -} -``` - -```java -// dp/Main.java -package dp; - -import lib.annotations.callgraph.IndirectCall; - -public class Main { - @IndirectCall( - name = "bar", returnType = String.class, parameterTypes = String.class, line = 12, - resolvedTargets = "Ldp/FooImpl;" - ) - public static void main(String[] args) { - Foo foo = (Foo) DebugProxy.newInstance(new FooImpl()); - foo.bar(null); - } -} -``` -[//]: # (END) \ No newline at end of file diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/JVMCalls.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/JVMCalls.markdown deleted file mode 100644 index ec468d97c2..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/JVMCalls.markdown +++ /dev/null @@ -1,172 +0,0 @@ -#JVMCalls -JVM calls or callbacks must be treated as (on-the-fly) entry points and explicitly modelled for correct -call-graph construction, i.e., when certain operations are performed like creating an object or -adding an ```ShutdownHook```. - -Please note that Java's Serialization feature is a similar mechanism. However, Serialization is a -substantial feature and is thus handled in a separate category. - -##JVMC1 -[//]: # (MAIN: jvmc.Demo) -This tests covers a callback that can be introduced to the program by calling```Runtime.addShutdownHook```. -It allows the program to pass a customizable thread to the JVM that is called on the JVM's shut down. -```java -// jvmc/Demo.java -package jvmc; - -import java.lang.System; -import java.lang.Runtime; - -import lib.annotations.callgraph.DirectCall; - -public class Demo { - - public static void callback(){ /* do something */ } - - public static void main(String[] args){ - Runnable r = new TargetRunnable(); - Runtime.getRuntime().addShutdownHook(new Thread(r)); - } -} - -class TargetRunnable implements Runnable { - - @DirectCall(name = "callback", line = 22, resolvedTargets = "Ljvmc/Demo;") - public void run(){ - Demo.callback(); - } -} -``` -[//]: # (END) - -##JVMC2 -[//]: # (MAIN: jvmc.Demo) -This test case covers the ```finalize``` method which __might__ be called by the JVM during -garbage collection. -```java -// jvmc/Demo.java -package jvmc; - - -import lib.annotations.callgraph.DirectCall; - -public class Demo { - - public static void callback(){}; - - public static void main(String[] args){ - for(int i = -1; i < args.length; i++){ - new Demo(); - } - } - - @DirectCall(name="callback", line=18, resolvedTargets = "Ljvmc/Demo;") - public void finalize() throws java.lang.Throwable { - callback(); - super.finalize(); - } -} -``` -[//]: # (END) - -##JVMC3 -[//]: # (MAIN: jvmc.Demo) -This cases tests the implicitly introduced call edge from ```Thread.start``` to ```Thread.run```. -Please note that this test tests this feature indirectly by validating that the run method of -```TargetRunnable``` is transitively reachable. -```java -// jvmc/Demo.java -package jvmc; - -import lib.annotations.callgraph.IndirectCall; - -public class Demo { - - @IndirectCall(name="run", line = 11, resolvedTargets = "Ljvmc/TargetRunnable;") - public static void main(String[] args) throws InterruptedException { - Runnable r = new TargetRunnable(); - Thread t = new Thread(r); - t.start(); - t.join(); - } -} - -class TargetRunnable implements Runnable { - - public void run(){ - /* Do the hard work */ - } -} -``` -[//]: # (END) - -##JVMC4 -[//]: # (MAIN: jvmc.Demo) -This cases tests the implicitly introduced call edge from ```Thread.start``` to the transitively -reachable ```Thread.exit``` method that is also called by the JVM on a thread's exit. -```java -// jvmc/Demo.java -package jvmc; - -import lib.annotations.callgraph.IndirectCall; - -public class Demo { - - @IndirectCall(name="exit", line = 12, resolvedTargets = "Ljava/lang/Thread;") - public static void main(String[] args) throws InterruptedException { - Runnable r = new TargetRunnable(); - Thread t = new Thread(r); - t.start(); - t.join(); - } -} - -class TargetRunnable implements Runnable { - - public void run(){ - /* Do the hard work */ - } -} -``` -[//]: # (END) - -##JVMC5 -[//]: # (MAIN: jvmc.Demo) -This cases tests the implicitly introduced call edge from ```Thread.setUncaughtExceptionHandler``` -to ```Thread.dispatchUncaughtException``` method that is intended to be called by the JVM. -```java -// jvmc/Demo.java -package jvmc; - -import lib.annotations.callgraph.DirectCall; - -public class Demo { - - public static void main(String[] args) throws InterruptedException { - Runnable r = new TargetRunnable(); - Thread t = new Thread(r); - t.setUncaughtExceptionHandler(new ExceptionalExceptionHandler()); - t.start(); - t.join(); - } -} - -class TargetRunnable implements Runnable { - - public void run(){ - throw new IllegalArgumentException("We don't want this thread to work!"); - } -} - -class ExceptionalExceptionHandler implements Thread.UncaughtExceptionHandler { - - private static void callback() { /* do something */ } - - @DirectCall(name="callback", line= 29, resolvedTargets = "Ljvmc/ExceptionalExceptionHandler;") - public void uncaughtException(Thread t, Throwable e){ - callback(); - // Handle the uncaught Exception (IllegalArgumentException) - } -} -``` -[//]: # (END) \ No newline at end of file diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/Java8InterfaceMethods.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/Java8InterfaceMethods.markdown deleted file mode 100644 index 480d5bb368..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/Java8InterfaceMethods.markdown +++ /dev/null @@ -1,311 +0,0 @@ -#Java8DefaultInterfaceMethods -Tests the resolution of Java 8 default methods. - -##J8DIM1 -[//]: # (MAIN: j8dim.Class) -Tests the resolution of a polymorphic call when a class (cf.```j8dim.Class``` ) implements an -interface (cf. ```j8dim.Interface```) which declares a default method (cf. ```j8dim.Interface.method()```)and -inherits this default method from the inherited interface. A call on ```j8dim.Class.method()``` must -then be resolved to ```j8dim.Interface.method()```. - -```java -// j8dim/Class.java -package j8dim; - -import lib.annotations.callgraph.DirectCall; - -class Class implements Interface { - - @DirectCall(name = "method", line = 10, resolvedTargets = "Lj8dim/Interface;") - public static void main(String[] args){ - Interface i = new Class(); - i.method(); - } -} - -interface Interface { - default void method() { - // do something - } -} -``` -[//]: # (END) - -##J8DIM2 -[//]: # (MAIN: j8dim.SuperClass) -Tests the resolution of a polymorphic call when a class (cf. ```j8dim.SubClass```) implements an -interface with default method (cf. ```j8dim.Interface```) and extends a class (cf. ```j8dim.SuperClass```) -where the interface and the class define a method with the same signature, namely ```method```. -The subclass, inheriting from both, doesn't define a method with that signature, hence, the method -call on that class must be dispatched to the superclass's method when called on ```j8dim.Interface```. - -```java -// j8dim/SuperClass.java -package j8dim; - -import lib.annotations.callgraph.DirectCall; - -class SuperClass { - - public void method(){ - // do something - } - - @DirectCall( - name = "method", - line = 19, - resolvedTargets = "Lj8dim/SuperClass;", - prohibitedTargets = {"Lj8dim/Interface;"} - ) - public static void main(String[] args){ - Interface i = new SubClass(); - i.method(); - } -} - -interface Interface { - default void method() { - // do something - } -} - -class SubClass extends SuperClass implements Interface { - -} -``` -[//]: # (END) - -##J8DIM3 -[//]: # (MAIN: j8dim.SuperClass) -Tests the resolution of a polymorphic call when a class (cf. ```j8dim.SubClass```) implements an -interface with default method (cf. ```j8dim.Interface```) and extends a class (cf. ```j8dim.SuperClass```) -where the interface and the class define a method with the same signature, namely ```method```. -The subclass, inheriting from both, doesn't define a method with that signature, hence, the method -call on that class must be dispatched to the superclass's method when called on ```j8dim.SuperClass```. - - -```java -// j8dim/SuperClass.java -package j8dim; - -import lib.annotations.callgraph.DirectCall; - -class SuperClass { - - public void method(){ - // do something - } - - @DirectCall( - name = "method", - line = 19, - resolvedTargets = "Lj8dim/SuperClass;", - prohibitedTargets = {"Lj8dim/Interface;"} - ) - public static void main(String[] args){ - SuperClass superClass = new SubClass(); - superClass.method(); - } -} - -interface Interface { - default void method() { - // do something - } -} - -class SubClass extends SuperClass implements Interface { - -} -``` -[//]: # (END) - -##J8DIM4 -[//]: # (MAIN: j8dim.SuperClass) -Tests the resolution of a polymorphic call when a class (cf. ```j8dim.SubClass```) implements an -interface with default method (cf. ```j8dim.Interface```) and extends a class (cf. ```j8dim.SuperClass```) -which doesn't provide a method with the same signature as the interface's default method, namely ```method```. -The subclass, inheriting from both, doesn't define a method with that signature, hence, the method -call on that class must be dispatched to the interface's method when called on ```j8dim.SubClass```. - -```java -// j8dim/SuperClass.java -package j8dim; - -import lib.annotations.callgraph.DirectCall; - -class SuperClass { - - @DirectCall( - name = "method", - line = 14, - resolvedTargets = "Lj8dim/Interface;" - ) - public static void main(String[] args){ - SubClass subClass = new SubClass(); - subClass.method(); - } -} - -interface Interface { - default void method() { - // do something - } -} - -class SubClass extends SuperClass implements Interface { - -} -``` -[//]: # (END) - -##J8DIM5 -[//]: # (MAIN: j8dim.SuperClass) -Tests the resolution of a polymorphic call when a class (cf. ```j8dim.Class```) extends an abstract -class (cf. ```j8dim.SuperClass```) that declares a method ```compute()``` and implements -several interfaces that are again in an inheritance relationship where all interfaces define a -default method called ```method```. Respective calls on the ```compute``` and ```method``` methods -on ```j8dim.Class``` must then be dispatched to the correct methods. Since multiple interface define -the same method, the maximally specific methods must be computed (see JVM spec.). - -```java -// j8dim/SuperClass.java -package j8dim; - -import lib.annotations.callgraph.DirectCalls; -import lib.annotations.callgraph.DirectCall; - -abstract class SuperClass { - - public void compute(){ /* do something*/ } - - @DirectCalls({ - @DirectCall( - name = "method", - line = 26, - resolvedTargets = "Lj8dim/DirectInterface;", - prohibitedTargets = {"Lj8dim/Interface1;", "Lj8dim/Interface2;"} - ), - @DirectCall( - name = "compute", - line = 27, - resolvedTargets = "Lj8dim/SuperClass;", - prohibitedTargets = {"Lj8dim/Interface1;","Lj8dim/Interface2;"} - ) - }) - public static void main(String[] args){ - Class cls = new Class(); - cls.method(); - cls.compute(); - } -} -class Class extends SuperClass implements DirectInterface, Interface1, Interface2 { - -} - -interface Interface1 { - - void compute(); - - default void method() { - // do something - } -} - -interface Interface2 { - - void compute(); - - default void method() { - // do something - } -} - -interface DirectInterface extends Interface1, Interface2 { - default void method() { - // do something - } -} -``` -[//]: # (END) - -##J8DIM5 -[//]: # (MAIN: j8dim.Demo) -An interface which extends two other interfaces that declare the same default method, as -```CombinedInterface``` does by extending ```SomeInterface``` and ```AnotherInterface```, -and also declares a default method (i.e. ```CombinedInterface.method```) it is possible to use -qualified super calls of the form ```SomeInterface.super.method()``` that must be resolved to the -respective super class' method implementation. The super calls must be qualified by the targeted -super interface since it is not unique otherwise. -```java -// j8dim/Demo.java -package j8dim; - -import lib.annotations.callgraph.DirectCalls; -import lib.annotations.callgraph.DirectCall; - -class Demo { - - public static void main(String[] args){ - new CombinedInterface(){}.method(); - } -} - -interface SomeInterface { - default void method() { - // do something - } -} - -interface AnotherInterface { - default void method() { - // do something - } -} - -interface CombinedInterface extends SomeInterface, AnotherInterface { - - @DirectCalls({ - @DirectCall(name = "method", line = 32, resolvedTargets = "Lj8dim/SomeInterface;"), - @DirectCall(name = "method", line = 33, resolvedTargets = "Lj8dim/AnotherInterface;") - }) - default void method() { - SomeInterface.super.method(); - AnotherInterface.super.method(); - } -} -``` -[//]: # (END) - -#Java8StaticInterfaceMethods - -Tests the correct method resolution of static interface methods. - -Please note that the ```infrastructure_incompatible_testcases``` are more test cases w.r.t. to -static interface methods pertaining to Java 9 and higher versions. - -##J8SIM1 -[//]: # (MAIN: j8sim.Class) -Tests the invocation of a static interface method ```j8sim.Interface``` in ```j8sim.Class```'s main -method. -```java -// j8sim/Class.java -package j8sim; - -import lib.annotations.callgraph.DirectCall; - -class Class { - - @DirectCall(name = "method", line = 9, resolvedTargets = "Lj8sim/Interface;") - public static void main(String[] args){ - Interface.method(); - } -} - -interface Interface { - static void method() { - // do something - } -} -``` -[//]: # (END) \ No newline at end of file diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/Java8Invokedynamics.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/Java8Invokedynamics.markdown deleted file mode 100644 index 5c5b143548..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/Java8Invokedynamics.markdown +++ /dev/null @@ -1,373 +0,0 @@ -#Java8Invokedynamics -Tests related to the invokedynamic instructions created by Java 8 method references and lambda expressions. -Please note, that the Scala compiler hijacks Java's infrastructure and analyses which support -Java8's invokedynamics, i.e., those using `(Alt)LambdaMetaFactory`) will also support Scala to a -reasonable amount. - -##MR1 -[//]: # (MAIN: id.Class) -Tests method reference that deals with interface default methods (Java 8 or higher) which leads to -invokedynamics that use *INVOKEINTERFACE* methods handles. - -```java -// id/Class.java -package id; - -import lib.annotations.callgraph.IndirectCall; - -class Class implements Interface { - - @FunctionalInterface public interface FIBoolean { - boolean get(); - } - - @IndirectCall( - name = "method", returnType = boolean.class, line = 18, - resolvedTargets = "Lid/Interface;" - ) - public static void main(String[] args){ - Class cls = new Class(); - FIBoolean bc = cls::method; - bc.get(); - } -} - -interface Interface { - default boolean method() { - return true; - } -} -``` -[//]: # (END) - -##MR2 -[//]: # (MAIN: id.Class) -Tests a method reference that results in an invokedynamic that uses an *INVOKESPECIAL* method handle which -is issued by calling a private method. - -```java -// id/Class.java -package id; - -import lib.annotations.callgraph.IndirectCall; - -class Class { - - private String getTypeName() { return "Lid/Class;";} - - @IndirectCall( - name = "getTypeName", returnType = String.class, line = 14, - resolvedTargets = "Lid/Class;") - public void callViaMethodReference(){ - java.util.function.Supplier stringSupplier = this::getTypeName; - stringSupplier.get(); - } - - public static void main(String[] args){ - Class cls = new Class(); - cls.callViaMethodReference(); - } -} -``` -[//]: # (END) - -##MR3 -[//]: # (MAIN: id.Class) -Tests a method reference that results in an invokedynamic that uses an *INVOKESPECIAL* method handle which -is issued by calling a protected method from a super class that is resolved to a syntatic bridge -method compiled in ```id.Class```. - -```java -// id/Class.java -package id; - -import lib.annotations.callgraph.IndirectCall; - -class Class extends SuperClass { - - @IndirectCall( - name = "getTypeName", returnType = String.class, line = 12, - resolvedTargets = "Lid/SuperClass;") - public void callViaMethodReference(){ - java.util.function.Supplier stringSupplier = super::getTypeName; - stringSupplier.get(); - } - - public static void main(String[] args){ - Class cls = new Class(); - cls.callViaMethodReference(); - } -} - -class SuperClass{ - protected String getTypeName() { return "Lid/SuperClass;";} -} -``` -[//]: # (END) - -##MR4 -[//]: # (MAIN: id.Class) -Tests a method reference that results in an *INOVKESTATIC* method handle where the target method doesn't -have any formal arguments. The call is issued by calling a static method from a super class. - -```java -// id/Class.java -package id; - -import java.util.function.Supplier; -import lib.annotations.callgraph.IndirectCall; - -class Class { - - @IndirectCall( - name = "getTypeName", returnType = String.class, line = 13, - resolvedTargets = "Lid/Class;") - public static void main(String[] args){ - Supplier stringSupplier = Class::getTypeName; - stringSupplier.get(); - } - - static String getTypeName() { return "Lid/Class"; } -} -``` -[//]: # (END) - -##MR5 -[//]: # (MAIN: id.Class) -Tests a method reference that results in an *INOVKESTATIC* method handle where the target method has -primitive formal arguments. The call is issued by a call on a functional interface. - -```java -// id/Class.java -package id; - -import java.util.function.Supplier; -import lib.annotations.callgraph.IndirectCall; - -class Class { - - public static double sum(double a, double b) { return a + b; } - - @FunctionalInterface public interface FIDoubleDouble { - double apply(double a, double b); - } - - @IndirectCall( - name = "sum", returnType = double.class, parameterTypes = {double.class, double.class}, line = 19, - resolvedTargets = "Lid/Class;") - public static void main(String[] args){ - FIDoubleDouble fidd = Class::sum; - fidd.apply(1d,2d); - } -} -``` -[//]: # (END) - -##MR6 -[//]: # (MAIN: id.Class) -Tests a method reference that results in an invokedynamic that uses an *NEWINVOKESPECIAL* method handle -which is given by the method reference of ```id.Class::new```. Calling this method references -results in a constructor call to ```id.Class```. - -```java -// id/Class.java -package id; - -import java.util.function.Supplier; -import lib.annotations.callgraph.IndirectCall; - -class Class { - - public Class(){} - - @IndirectCall( - name = "", line = 14, resolvedTargets = "Lid/Class;") - public static void main(String[] args){ - Supplier classSupplier = Class::new; - classSupplier.get(); - } -} -``` -[//]: # (END) - -##MR7 -[//]: # (MAIN: id.Class) -Tests a method reference that results in an invokedynamic that uses an *INVOKEVIRTUAL* method handle -which is given by the method reference of ```cls::version``` where the actually called method is -implentend with ```id.Class```'s superclass ```id.SuperClass```. - -```java -// id/Class.java -package id; - -import lib.annotations.callgraph.IndirectCall; - -class Class extends SuperClass{ - - @IndirectCall( - name = "version", returnType = String.class, line = 13, - resolvedTargets = "Lid/SuperClass;") - public static void main(String[] args){ - Class cls = new Class(); - java.util.function.Supplier classSupplier = cls::version; - classSupplier.get(); - } -} - -class SuperClass { - public String version() { return "1.0"; } -} -``` -[//]: # (END) - -#Lambdas -Test cases in the presence of lambdas. - -##Lambda1 -[//]: # (MAIN: id.Class) -Tests the invocation of a lamdba that results in an invokedynamic with an *INVOKESTATIC* method handle -which points to an synthetic method. Please not that all primitive integers are autoboxed to -```java.lang.Integer``` which then fits the lambdas (cf. ```isEven```) type. -```java -// id/Class.java -package id; - -import lib.annotations.callgraph.IndirectCall; -import java.util.function.Function; - -class Class { - @IndirectCall(name = "doSomething", line = 13, resolvedTargets = "Lid/Class;") - public static void main(String[] args){ - Function isEven = (Integer a) -> { - doSomething(); - return a % 2 == 0; - }; - isEven.apply(2); - } - - private static void doSomething(){ - // call in lambda - } -} -``` -[//]: # (END) - -##Lambda2 -[//]: # (MAIN: id.Class) -Tests an invokedynamic invocation where the object receiver is captured in a lambda function. -Declaring a lambda function in another class (cf. ```id.LambdaProvider```) as it is invoked -(cf. ```id.Class```) leads to an *INVOKESTATIC* method handle where the receiver is not declared -within the same class. - -```java -// id/Class.java -package id; - -import lib.annotations.callgraph.IndirectCall; - -class Class { - - public static void doSomething(){ } - - @IndirectCall(name = "doSomething", line = 12, resolvedTargets = "Lid/LambdaProvider;") - public static void main(String[] args) { - Runnable lambda = LambdaProvider.getRunnable(); - lambda.run(); - } -} - -class LambdaProvider { - - public static void doSomething(){ - /* do something */ - } - - public static id.Runnable getRunnable(){ - return () -> LambdaProvider.doSomething(); - } -} -``` -```java -// id/Runnable.java -package id; - -@FunctionalInterface interface Runnable { - void run(); -} -``` -[//]: # (END) - -##Lambda3 -[//]: # (MAIN: id.Class) -Tests the invocation of a lambda that was first written to and then retrieved from an array. This -case results in an invokedynamic with an *INVOKESTATIC* method handle where the receiver argument -is read by *AASTORE* instruction form an array before the method invocation takes place. - -```java -// id/Class.java -package id; - -import lib.annotations.callgraph.IndirectCall; - -class Class { - - @FunctionalInterface interface Runnable { - void run(); - } - - public static void doSomething(){ - /* do something */ - } - - public static Runnable[] lambdaArray = new Runnable[10]; - - @IndirectCall(name = "doSomething", line = 22, resolvedTargets = "Lid/Class;") - public static void main(String[] args) { - Runnable r1 = () -> doSomething(); - lambdaArray[0] = r1; - Runnable same = lambdaArray[0]; - same.run(); - } -} - -final class Math { - public static int PI(){ - return 3; - } -} -``` -[//]: # (END) - -##Lambda4 -[//]: # (MAIN: id.Class) -Tests the invocation of an intersection type lambda. This is special because the JVM does then use -the ```AltLambdaMetaFactory``` instead of the ```LambdaMetaFactory``` which is used for all the -previously defined cases. - -```java -// id/Class.java -package id; - -import lib.annotations.callgraph.IndirectCall; - -class Class { - - public interface MyMarkerInterface1 {} - public interface MyMarkerInterface2 {} - - public @FunctionalInterface interface Runnable { - void run(); - } - - public static void doSomething(){ - /* do something */ - } - - @IndirectCall(name = "doSomething", line = 21, resolvedTargets = "Lid/Class;") - public static void main(String[] args) { - Runnable run = (Runnable & MyMarkerInterface1 & MyMarkerInterface2) () -> doSomething(); - run.run(); - } -} -``` -[//]: # (END) diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/Library.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/Library.markdown deleted file mode 100644 index db0a27c352..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/Library.markdown +++ /dev/null @@ -1,298 +0,0 @@ -# Library -Tests the call-graph handling for the analysis of software libraries, i.e. partial programs. The main -difference between applications and library software is, that libraries are intended to be used, and, -therefore, to be extended. Library extensions can cause call-graph edges within the library that can -already be detected without a concrete application scenario. For instance, when the library contains yet -independent as well as by client code accessible and inheritable classes and interfaces that declare -a method with exactly the same method signature. - -When the previous conditions are meet, a client can extend such a class as well as implement the -interface respectively without overriding the respective method which leads to interface call sites -that must be additionally resolved to the class' method. We refer to those edges as call-by-signature -(CBS) edges. - -According to [1], all test cases in that category assume that all packages are closed. - -[1] Reif et al., Call Graph Construction for Java Libraries, FSE 2016. - -##LIB1 -[//]: # (LIBRARY) -Tests virtual call resolution in the context of libraries where the calling context is unknown. -The circumstances of the virtual call are as follows: -1) We have a method ```public void libraryEntryPoint(Type type)``` which calls a method on the passed -parameter, -2) A type ```public class Type``` which declares a method ```public void method()```, -3) Another type ```public class Subtype extends Type``` which also declares a method ```public void method()```, -4) An additional type ```public class SomeType``` which also delcares a method ```public void method()```. -Since the calling context of ```Type.method()``` in ```Demo.entrypoint(Type t)``` is unknown. The call-graph -construction must assume that the parameter ```type``` can hold all possible subtypes of ```Type``` . -```java -// lib1/Demo.java -package lib1; - -import lib.annotations.callgraph.DirectCall; - -public class Demo { - - @DirectCall(name = "method", line = 10, resolvedTargets = {"Llib1/Type;", "Llib1/Subtype;"}, - prohibitedTargets = "Llib1/SomeType;") - public void libraryEntryPoint(Type type){ - type.method(); - } -} -``` -```java -// lib1/Type.java -package lib1; - -public class Type { - - public void method(){ - /* do something */ - } -} -``` -```java -// lib1/Subtype.java -package lib1; - -public class Subtype extends Type { - - public void method(){ - /* do something */ - } -} -``` -```java -// lib1/SomeType.java -package lib1; - -public class SomeType { - - public void method(){ - /* do something */ - } -} -``` -[//]: # (END) - -##LIB2 -[//]: # (LIBRARY) -Tests virtual call resolution in the context of libraries where the calling context is unknown. -The circumstances of the virtual call are as follows: -1) We have a method ```public void libraryEntryPoint(Type type)``` which calls a method on the passed -parameter, -2) A type ```public class Type``` which declares a method ```public void method()```, -3) Another type ```public class Subtype extends Type``` which also declares a method ```public void method()```, -4) An additional type ```public class SomeType``` which also delcares a method ```public void method()```. -Since the calling context of ```Type.method()``` in ```Demo.callOnField()``` is unknown, i.e., -the field is public and non-final and, therefore, can be re-assigned by library users. The call-graph -construction must assume that all possible subtypes of ```Type``` can be assigned to the field. -```java -// lib2/Demo.java -package lib2; - -import lib.annotations.callgraph.DirectCall; - -public class Demo { - - public Type field = new Subtype(); - - @DirectCall(name = "method", line = 12, resolvedTargets = {"Llib2/Type;", "Llib2/Subtype;"}, - prohibitedTargets = "Llib2/SomeType;") - public void callOnField(){ - field.method(); - } -} -``` -```java -// lib2/Type.java -package lib2; - -public class Type { - - public void method(){ - /* do something */ - } -} -``` -```java -// lib2/Subtype.java -package lib2; - -public class Subtype extends Type { - - public void method(){ - /* do something */ - } -} -``` -```java -// lib2/SomeType.java -package lib2; - -public class SomeType { - - public void method(){ - /* do something */ - } -} -``` -[//]: # (END) - -##LIB3 -[//]: # (LIBRARY) -Tests library interface invocation for CBS edges under the following circumstances: -1) a ```public class PotentialSuperclass``` that can be inherited, -1) a ```public class DismissedSuperclass``` that cannot be inherited and, therefore, can't be target, -1) a ```public interface``` that can be inherited, -1) all of the previous mentioned classes/interfaces declare the method ```public void method()```. -```java -// lib3/Demo.java -package lib3; - -import lib.annotations.callgraph.DirectCall; - -public class Demo { - - @DirectCall(name = "method", line = 10, resolvedTargets = "Llib3/PotentialSuperclass;", - prohibitedTargets = "Llib3/DismissedSuperlass;") - public static void libraryCallSite(Interface i){ - i.method(); - } -} -``` -```java -// lib3/PotentialSuperclass.java -package lib3; - -public class PotentialSuperclass { - - public void method() { - - } -} -``` -```java -// lib3/DismissedSuperlass.java -package lib3; - -public final class DismissedSuperlass { - - public void method() { - - } -} -``` -```java -// lib3/Interface.java -package lib3; - -public interface Interface { - - void method(); -} -``` -[//]: # (END) - -##LIB4 -[//]: # (LIBRARY) -Tests library interface invocation for CBS edges under the following circumstances: -1) a ```package visible class PotentialSuperclass``` in package ```lib4.collude``` that can be -inherited from a class within the same package, i.e. when a new class is added to the same package, -2) a ```package visible class InternalClass``` in package ```lib4.internal``` that can be inherited -(analogously to 1) ), -3) a ```package visible interface``` in package ```lib4.collude``` that can be inherited from classes in the same package, -4) all of the previous mentioned classes/interfaces declare the method ```public void method()```. -```java -// lib4/collude/Demo.java -package lib4.collude; - -import lib.annotations.callgraph.DirectCall; - -public class Demo { - - @DirectCall(name = "method", line = 10, resolvedTargets = "Llib4/collude/PotentialSuperclass;", - prohibitedTargets = "Llib4/internal/InternalClass;") - public static void interfaceCallSite(PotentialInterface pi){ - pi.method(); - } -} -``` -```java -// lib4/collude/PotentialSuperclass.java -package lib4.collude; - -class PotentialSuperclass { - - public void method(){ - /* do something */ - } -} -``` -```java -// lib4/collude/PotentialInterface.java -package lib4.collude; - -interface PotentialInterface { - - void method(); -} -``` -```java -// lib4/internal/InternalClass.java -package lib4.internal; - -class InternalClass { - - public void method(){ - /* do something */ - } -} -``` -[//]: # (END) - -##LIB5 -[//]: # (LIBRARY) -Tests library interface invocation for CBS edges under the following circumstances: -1) a ```public class PotentialSuperclass``` in package ```lib5.internal``` that can be -inherited from and, therefore, provides the method ```public void method()``` from its superclass, -2) a ```package visible class InternalClass``` in package ```lib5.internal``` that can be inherited -(analogously to 1) ), -3) a ```package visible interface``` in package ```lib5.collude``` that can be inherited from classes in the same package, -4) all of the previous mentioned classes/interfaces declare the method ```public void method()```. -```java -// lib5/collude/Demo.java -package lib5.collude; - -import lib.annotations.callgraph.DirectCall; - -public class Demo { - - @DirectCall(name = "method", line = 9, resolvedTargets = "Llib5/internal/InternalClass;") - public static void interfaceCallSite(PotentialInterface pi){ - pi.method(); - } -} - -interface PotentialInterface { - - void method(); -} -``` -```java -// lib5/internal/PotentialSuperclass.java -package lib5.internal; - -public class PotentialSuperclass extends InternalClass { - -} - -class InternalClass { - - public void method(){ - /* do something */ - } -} -``` -[//]: # (END) \ No newline at end of file diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/ModernReflection.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/ModernReflection.markdown deleted file mode 100644 index f244f71ae0..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/ModernReflection.markdown +++ /dev/null @@ -1,271 +0,0 @@ -#TrivialModernReflection -Reflective calls using the ```java.lang.invoke.*``` APIs and Java 7’s ```MethodHandle``` API which -are not signature polymorphic. - -All strings and constants are directly available and thus no control- or data-flow analysis is required. -##TMR1 -[//]: # (MAIN: tmr.Demo) -Tests modern reflection by performing a ```findStatic``` method handle lookup where the -declaring class type, the static method's name, and the method's return type is given within the -the main method of ```tmr.Demo```. Afterwards, ```invokeExact``` is called on the looked up method -handle which results in a call to ```staticToString```. -```java -// tmr/Demo.java -package tmr; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import lib.annotations.callgraph.IndirectCall; -class Demo { - static String target() { return "Demo"; } - - @IndirectCall( - name = "target", returnType = String.class, line = 18, - resolvedTargets = "Ltmr/Demo;" - ) - public static void main(String[] args) throws Throwable { - MethodType methodType = MethodType.methodType(String.class); - MethodHandle handle = MethodHandles.lookup().findStatic(Demo.class, "target", methodType); - String s = (String) handle.invokeExact(); - } -} -``` -[//]: # (END) - -##TMR2 -[//]: # (MAIN: tmr.Demo) -Tests modern reflection by performing a ```findVirtual``` method handle lookup where the -declaring class type, the instance method's name, and the method's method type object is given within the -the main method of ```tmr.Demo```. Afterwards, ```invokeExact``` is called on the looked up method -handle which results in a call to ```tmr.Demo.target```. -```java -// tmr/Demo.java -package tmr; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import lib.annotations.callgraph.IndirectCall; -class Demo { - public String target() { return "Demo"; } - - @IndirectCall( - name = "target", returnType = String.class, line = 18, - resolvedTargets = "Ltmr/Demo;" - ) - public static void main(String[] args) throws Throwable { - MethodType methodType = MethodType.methodType(String.class); - MethodHandle handle = MethodHandles.lookup().findVirtual(Demo.class, "target", methodType); - String s = (String) handle.invokeExact(new Demo()); - } -} -``` -[//]: # (END) - -##TMR3 -[//]: # (MAIN: tmr.Demo) -Tests modern reflection by performing a ```findConstructor``` method handle lookup where the -declaring class type and the method's method type object is given within the -the main method of ```tmr.Demo```. Afterwards, ```invokeExact``` is called on the looked up method -handle which results in a call to ```tmr.Demo```'s constructor. - -Whether the constructor is called or not is verified by a static method call within the constructor. -```java -// tmr/Demo.java -package tmr; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import lib.annotations.callgraph.DirectCall; -class Demo { - - public static void main(String[] args) throws Throwable { - MethodType methodType = MethodType.methodType(void.class); - MethodHandle handle = MethodHandles.lookup().findConstructor(Demo.class, methodType); - Demo f = (Demo) handle.invokeExact(); - } - - @DirectCall(name="verifyCall", line=18, resolvedTargets = "Ltmr/Demo;") - public Demo() { - Demo.verifyCall(); - } - - public static void verifyCall(){ /* do something */ } -} -``` -[//]: # (END) - -##TMR4 -[//]: # (MAIN: tmr.Demo) -Tests modern reflection by performing a ```findStaticGetter``` method handle lookup where the -declaring class type, the field's name, and the static field's type is given within the -the main method of ```tmr.Demo```. Afterwards, ```invoke``` is called on the looked up method -handle which results in a call to ```Demo.target``` on ```tmr.Demo.field```. -```java -// tmr/Demo.java -package tmr; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; - -import lib.annotations.callgraph.DirectCall; -class Demo { - public String toString() { return "42"; } - - public static Demo field = new Demo(); - - @DirectCall( - name = "toString", returnType = String.class, - line = 18, resolvedTargets = "Ltmr/Demo;" - ) - public static void main(String[] args) throws Throwable { - MethodHandle handle = MethodHandles.lookup().findStaticGetter(Demo.class, "field", Demo.class); - handle.invoke().toString(); - } -} - -``` - -[//]: # (END) - -##TMR5 -[//]: # (MAIN: tmr.Demo) -Tests modern reflection by performing a ```findGetter``` method handle lookup where the -declaring class type, the field's name, and the field's type is given within the -the main method of ```tmr.Demo```. Afterwards, ```invoke``` is called with a new instance of -```tmr.Demo``` on the looked up method handle which results in a call to ```Demo.target``` on -```tmr.Demo.field```. -```java -// tmr/Demo.java -package tmr; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; - -import lib.annotations.callgraph.DirectCall; -class Demo { - public String toString() { return "42"; } - - public Demo field; - - public Demo() { - this.field = this; - } - - @DirectCall( - name = "toString", returnType = String.class, - line = 22, resolvedTargets = "Ltmr/Demo;" - ) - public static void main(String[] args) throws Throwable { - MethodHandle handle = MethodHandles.lookup().findGetter(Demo.class, "field", Demo.class); - handle.invoke(new Demo()).toString(); - } -} -``` - -[//]: # (END) - -##TMR6 -[//]: # (MAIN: tmr.Demo) -Tests modern reflection by performing a ```findSpecial``` method handle lookup where the -declaring class type, the method's name and signature, and the special caller are passed to method. -Afterwards, ```invoke``` is called with a new instance of ```tmr.Demo``` on the looked -up method handle which results in a call to ```Superclass.target``` on an instance of```tmr.Demo```. - -```java -// tmr/Demo.java -package tmr; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import lib.annotations.callgraph.IndirectCall; -class Demo extends Superclass { - protected String target() { return "42!"; } - - @IndirectCall( - name = "target", returnType = String.class, line = 18, - resolvedTargets = "Ltmr/Superclass;" - ) - public static void main(String[] args) throws Throwable { - MethodType methodType = MethodType.methodType(String.class); - MethodHandle handle = MethodHandles.lookup().findSpecial(Superclass.class, "target", methodType, Demo.class); - String s = (String) handle.invokeExact(new Demo()); - } -} - -class Superclass { - protected String target() { return "42"; } -} -``` -[//]: # (END) - -##TMR7 -[//]: # (MAIN: tmr.Demo) -Tests modern reflection by performing a ```findStatic``` method handle lookup where the -declaring class type, the static method's name, and the method's signature is given within the -the main method of ```tmr.Demo```. Afterwards, ```invoke``` is called on the looked up method -handle which results in a call to ```target```. Due to the invocation of ```MethodHandle.invoke``` -it is possible that the return- or parameter types are adapted by the JVM, i.e. types might be -(un)boxed. -```java -// tmr/Demo.java -package tmr; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import lib.annotations.callgraph.IndirectCall; -class Demo { - public static Object target(String param) { return param; } - - @IndirectCall( - name = "target", returnType = Object.class, parameterTypes = String.class, line = 18, - resolvedTargets = "Ltmr/Demo;" - ) - public static void main(String[] args) throws Throwable { - MethodType methodType = MethodType.methodType(Object.class, String.class); - MethodHandle handle = MethodHandles.lookup().findStatic(Demo.class, "target", methodType); - String s = (String) handle.invoke((Object)"Demo"); - } -} -``` -[//]: # (END) - -##TMR8 -[//]: # (MAIN: tmr.Demo) -Tests modern reflection by performing a ```findStatic``` method handle lookup where the -declaring class type, the static method's name, and the method's signature is given within the -the main method of ```tmr.Demo```. Afterwards, ```invokeWithArguments``` is called on the looked up method -handle which results in a call to ```target``` with where the respective parameters are passed. -```java -// tmr/Demo.java -package tmr; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import lib.annotations.callgraph.IndirectCall; -class Demo { - public static String target(String param1, String param2) { return param1 + param2; } - - @IndirectCall( - name = "target", returnType = String.class, parameterTypes = { String.class, String.class }, - line = 18, resolvedTargets = "Ltmr/Demo;" - ) - public static void main(String[] args) throws Throwable { - MethodType methodType = MethodType.methodType(String.class, String.class, String.class); - MethodHandle handle = MethodHandles.lookup().findStatic(Demo.class, "target", methodType); - String s = (String) handle.invokeWithArguments(new Object[]{ "42", "42" }); - } -} -``` -[//]: # (END) diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/NonJavaBytecode1.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/NonJavaBytecode1.markdown deleted file mode 100644 index 729fc5ea27..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/NonJavaBytecode1.markdown +++ /dev/null @@ -1,70 +0,0 @@ -#NonJavaBytecode -This category groups test cases that must be created manually and cannot be created from compiling -valid Java code. However, the resulting bytecode is still valid and does occur in real-world code. - -All test cases within this category **cannot** be compiled using our pipeline. -##NJ1 -[//]: # (MAIN: tr.Demo) -In Java it is not possible to have a subclass that defines a method with the same name and -signature, but which is static. This is, however, possible at the bytecode level and the JVM -will call the default method. - -This test case pertains to an evolution scenario and cannot be compiled out of the box. Please -proceed with the following steps to obtain all class files required by the test case: - -Step one: -Compile the Main.java as is with the method with the markers *METHOD 1* (LINE 33) and *METHOD 3* -(LINE 58) uncommented and the method with the marker *METHOD 2* (LINE 37) commented. Then take the -resulting class files of "C", "Helper", "SuperIntf", "SubIntf", and "Main" and put tom into a -folder named "nj". - -Step two: -Now comment the lines with the markers *METHOD 1* and *METHOD 3* and uncomment the line with the -marker *METHOD 2*. Then compile "Main.java" again and copy "Intf" class' class file to the folder -named "nj". - -Step three: -Next to the folder "nj" add a folder named "META-INF" with the respective "MANIFEST.MF" file which -declares "nj/Main" as main class. - * -Step four: -Compress both folders and create an executeable ".jar" file. - */ -```java -package nj; - -import lib.annotations.callgraph.DirectCall; - -interface SuperIntf { -/* METHOD 1 */ default void m(){ Helper.println("SuperIntf.m"); }; -} - -interface Intf extends SuperIntf { -/* METHOD 2 */ // static void m(){ Helper.println("Intf.m"); }; -} - -interface SubIntf extends Intf {} - -class C implements SubIntf {} - -class Helper { - public static void println(java.lang.String s) { - System.out.println(s); - } - -} - -public class Main { - public static void main(String[] args) { - run(new C()); - } - - @DirectCall(name = "m", line = 58, resolvedTargets = "Lnj/SuperIntf;", prohibitedTargets = "Lnj/Intf;") - public static void run(SubIntf c) { - // This invokes the default method from SuperIntf -/* METHOD 3 */ c.m(); - } - -} -``` -[//]: # (END) \ No newline at end of file diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/NonJavaBytecode2.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/NonJavaBytecode2.markdown deleted file mode 100644 index 1ad12065b9..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/NonJavaBytecode2.markdown +++ /dev/null @@ -1,53 +0,0 @@ -#NonJavaBytecode -This category groups test cases that must be created manually and cannot be created from compiling -valid Java code. However, the resulting bytecode is still valid and does occur in real-world code. - -All test cases within this category **cannot** be compiled using our pipeline. - -##NJ2 -[//]: # (MAIN: nj.Demo) -On Java's source level it is impossible to define two methods with matching names and signatures that -only vary in the method's return type. However, this can be done by crafting bytecode which is then -still valid. - ->Please note: The project ```nonjava_bytecode2```, contained in the -```infrastructure_incompatible_testcases```, provides in ```scala/nj/EngineerBytecode``` a class that -is meant to engineer an instance of this case using OPAL's bytecode engineering DSL. -```java -// nj/Demo.java -package nj; - -import lib.annotations.callgraph.DirectCalls; -import lib.annotations.callgraph.DirectCall; - -/** - * @author Michael Reif - */ -public class Demo { - - @DirectCalls({ - @DirectCall(name="method", line=17, returnType = Object.class, resolvedTargets = "Lnj/Target;"), - @DirectCall(name="method", line=18, returnType = String.class, resolvedTargets = "Lnj/Target;") - }) - public static void main(String[] args) { - Target t = new Target(); - t.method("42"); - t.method("42"); - } -} - -class Target { - - public Object method(String s){ - System.out.println(s); - System.out.println("Object"); - return s; - } - - public String method(String s){ - return s; - } -} - -``` -[//]: # (END) \ No newline at end of file diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/NonVirtualCalls.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/NonVirtualCalls.markdown deleted file mode 100644 index 53a8c97a4d..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/NonVirtualCalls.markdown +++ /dev/null @@ -1,150 +0,0 @@ -#Non-virtual Calls -Tests related to non-virtual methods: static method calls, constructor calls (initializers), -super method calls, and private method calls. - -Static initializers are found in their own category due to the inherent complexity of resolving them -in a meaningful manner. - -##NVC1 -[//]: # (MAIN: nvc.Class) -Tests the resolution of a static method call when another static method with the same name but -different signature is presence. -```java -// nvc/Class.java -package nvc; - -import lib.annotations.callgraph.DirectCall; - -class Class { - - public static void method(){ /* do something*/} - public static void method(int param){ /* do something*/} - - @DirectCall(name = "method", line = 12, resolvedTargets = "Lnvc/Class;") - public static void main(String[] args){ - Class.method(); - } -} -``` -[//]: # (END) - -##NVC2 -[//]: # (MAIN: nvc.Class) -Tests the call resolution of default constructors which is caused by using Java's ```NEW``` keyword. The resulting -bytecode contains a *INVOKESPECIAL* instruction which must be resolved to ```nvc.Class```'s `````` method, i.e., -the default name for a constructor. -```java -// nvc/Class.java -package nvc; - -import lib.annotations.callgraph.DirectCall; - -public class Class { - - public Class(){ - - } - - @DirectCall(name = "", line = 13, resolvedTargets = "Lnvc/Class;") - public static void main(String[] args){ - Class cls = new Class(); - } -} -``` -[//]: # (END) - -##NVC3 -[//]: # (MAIN: nvc.Class) -Tests the resolution of a private method call when another method with the same name but -different signature is presence. -```java -// nvc/Class.java -package nvc; - -import lib.annotations.callgraph.DirectCall; - -class Class { - - private void method(){ /* do something*/} - private void method(int num){ /* do something*/} - - @DirectCall(name = "method", line = 13, resolvedTargets = "Lnvc/Class;") - public static void main(String[] args){ - Class cls = new Class(); - cls.method(); - } -} -``` -[//]: # (END) - -##NVC4 -[//]: # (MAIN: nvc.Class) -Tests the resolution of a super call in the form `super.`. The method call should -only be propagated to the immediate super class. -```java -// nvc/Class.java -package nvc; - -import lib.annotations.callgraph.DirectCall; - -class Class extends Superclass { - - @DirectCall(name = "method", line = 9, resolvedTargets = "Lnvc/Rootclass;") - protected void method(){ - super.method(); - } - - public static void main(String[] args){ - Class cls = new Class(); - cls.method(); - } -} - -class Superclass extends Rootclass { - -} - -class Rootclass { - protected void method(){ /* do something relevant */ } -} -``` -[//]: # (END) - -##NVC5 -[//]: # (MAIN: nvc.Demo) -Tests the resolution of a super call in a larger type hierarchy. In a class hierarchy like below, -with ```nvc.Sub <: nvc.Middle <: nvc.Super```, the super call in ```nvc.Sub.method``` will always invoke -```nvc.Middle.method``` even if ```Sub``` was compiled when ```nvc.Middle``` did not yet have an -implementation of ```method``` and thus the ```invokespecial``` references ```nvc.Super```. -```java -// nvc/Demo.java -package nvc; - -import lib.annotations.callgraph.DirectCall; - -public class Demo { - - public static void main(String[] args){ - new Sub().method(); - } -} - -class Super { - - void method() { /* doSomething */ } -} - -class Middle extends Super { - - void method() { /* doSomething */ } -} - -class Sub extends Middle { - - @DirectCall(name="method", line=25, resolvedTargets = "Lnvc/Middle;") - void method() { - super.method(); - } -} -``` -[//]: # (END) \ No newline at end of file diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/PackageBoundaries.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/PackageBoundaries.markdown deleted file mode 100644 index 935bf09c30..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/PackageBoundaries.markdown +++ /dev/null @@ -1,99 +0,0 @@ -#Package Boundaries - -Visibility modifiers influence the call resolution as other language features (e.g. virtual dispatch). -Especially package visibility sometimes permits the resolution to some target method even if they are -public. The following test cases target mostly the method resolution of inter-package method calls. - -##PB1 -[//]: # (MAIN: a/Main) -Tests the resolution of a call site with a public receiver type (```a.A```) but a package visible method where the -actual receiver type is a class defined in another package (```b```). The receiver type's class -(```b.B```) also declares a package visible method with the exact same signature. However, -due to the access modifier ```package visible```, the call is resolved to the declared type of the -variable. It's prohibited to resolve the call to the package visible method in package ```b```. -```java -// a/Main.java -package a; - -import lib.annotations.callgraph.CallSite; - -public class Main { - - @CallSite(name = "method", line = 10, resolvedTargets = "La/A;", prohibitedTargets = "Lb/B;") - public static void main(String[] args){ - A a = new b.B(); - a.method(); - } -} -``` -```java -// a/A.java -package a; - -public class A { - - void method(){ - /* do something */ - } -} -``` -```java -// b/B.java -package b; - -public class B extends a.A { - - void method(){ - /* do something */ - } -} -``` -[//]: # (END) - -##PB2 -[//]: # (MAIN: a/A) -Tests the resolution of a call site with a public receiver type (```a.A```) but a package visible method where the -actual receiver type is a class defined in same package (```a```). The receiver type's class -(```a.C```) does not declare a method with the same name and inherits from a public type from another -package that declares it. However, due to the access modifier ```package visible```, the call is resolved -to the declared type of the variable and not to the supertype's implementation. -It's prohibited to resolve the call to the package visible method in package ```b```. -```java -// a/A.java -package a; - -import lib.annotations.callgraph.CallSite; - -public class A { - - @CallSite(name = "method", line = 10, resolvedTargets = "La/A;", prohibitedTargets = "Lb/B;") - public static void main(String[] args){ - A a = new C(); - a.method(); - } - - void method(){ - /* do something */ - } -} -``` -```java -// a/C.java -package a; - -public class C extends b.B { - -} -``` -```java -// b/B.java -package b; - -public class B extends a.A { - - void method(){ - /* do something */ - } -} -``` -[//]: # (END) diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/Reflection.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/Reflection.markdown deleted file mode 100644 index 7084406ceb..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/Reflection.markdown +++ /dev/null @@ -1,779 +0,0 @@ -#TrivialReflection -This tests pertain to the Java's reflection API and comprise method call to -```java.lang.Class.getDeclaredMethod```, ```java.lang.Class.getMethod```, -```java.lang.Class.getField```, ```java.lang.reflect.Method.invoke```, and others. Cases that belong -to this category are rather trivially resolvable as all API inputs are directly known and neither -data-flow nor control-flow analyses are required. - -##TR1 -[//]: # (MAIN: tr.Demo) -Tests the reflective call resolution when an object of type ```java.lang.Class``` is used to find a -declared method (```Class.getDeclaredMethod```) of this class. Afterwards, the found method is invoked. -Since the target method ```Demo.target``` is static and has no arguments, neither a receiver nor a -method argument is passed over the ```invoke``` method. -```java -// tr/Demo.java -package tr; - -import lib.annotations.callgraph.IndirectCall; - -class Demo { - static String target() { return "42"; } - - @IndirectCall( - name = "target", returnType = String.class, line = 13, - resolvedTargets = "Ltr/Demo;" - ) - public static void main(String[] args) throws Exception { - Demo.class.getDeclaredMethod("target").invoke(null); - } -} -``` -[//]: # (END) - -##TR2 -[//]: # (MAIN: tr.Demo) -Tests the reflective call resolution when an object of type ```java.lang.Class``` is used to find a -declared method (```Class.getDeclaredMethod```) of this class. Afterwards, the found method is invoked. -Since the target method ```Demo.target``` is an instance method, the receiver (```this```) is passed -```invoke``` such that the method can be called on the actual receiver. -```java -// tr/Demo.java -package tr; - -import lib.annotations.callgraph.IndirectCall; - -class Demo { - public String target() { return "Demo"; } - - @IndirectCall( - name = "target", returnType = String.class, line = 13, - resolvedTargets = "Ltr/Demo;" - ) - void caller() throws Exception { - Demo.class.getDeclaredMethod("target").invoke(this); - } - - public static void main(String[] args) throws Exception { - new Demo().caller(); - } -} -``` -[//]: # (END) - -##TR3 -[//]: # (MAIN: tr.Demo) -Tests the reflective call resolution when an object of type ```java.lang.Class``` is used to find a -method (```Class.getMethod```) of this class. Afterwards, the found method is invoked. -Since the target method ```Demo.target``` is an instance method, the receiver (```this```) is passed -```invoke``` such that the method can be called on the actual receiver. -```java -// tr/Demo.java -package tr; - -import lib.annotations.callgraph.IndirectCall; - -public class Demo { - public String target() { return "Demo"; } - - @IndirectCall( - name = "target", returnType = String.class, line = 13, - resolvedTargets = "Ltr/Demo;" - ) - void caller() throws Exception { - Demo.class.getMethod("target").invoke(this); - } - - public static void main(String[] args) throws Exception { new Demo().caller(); } -} -``` -[//]: # (END) - -##TR4 -[//]: # (MAIN: tr.Demo) -Tests the reflective call resolution when an object of type ```java.lang.Class``` is used to find a -declared method (```Class.getDeclaredMethod```) of this class. Afterwards, the found method is invoked. -Since the target method ```Demo.target``` is static and has one parameter, a ```null``` receiver as -well as a ```String``` matching the method's parameters are passed over the ```invoke``` method. -```java -// tr/Demo.java -package tr; - -import lib.annotations.callgraph.IndirectCall; - -class Demo { - public static String target(String parameter) { return "Value: " + parameter; } - - @IndirectCall( - name = "target", returnType = String.class, parameterTypes = String.class, line = 13, - resolvedTargets = "Ltr/Demo;" - ) - public static void main(String[] args) throws Exception { - Demo.class.getDeclaredMethod("target", String.class).invoke(null, "42"); - } -} -``` -[//]: # (END) - -##TR5 -[//]: # (MAIN: tr.Demo) -Tests the reflective invocation of a constructor by retrieving ```Demo```'s default constructor via -calling ```newInstance``` on ```tr.Demo```'s class object. This call must be resolved to Demo's -`````` method. -```java -// tr/Demo.java -package tr; - -import lib.annotations.callgraph.DirectCall; - -class Demo { - public static void verifyCall(){ /* do something */ } - - @DirectCall(name="verifyCall", line=9, resolvedTargets = "Ltr/Demo;") - public Demo() { Demo.verifyCall(); } - - public static void main(String[] args) throws Exception { - Demo.class.newInstance(); - } -} -``` -[//]: # (END) - -##TR6 -[//]: # (MAIN: tr.Demo) -Tests the reflective invocation of a constructor by retrieving ```Demo```'s constructor with a -String argument via ```java.lang.Class```'s ```getConstructor``` method and then calling ```newInstance``` -of the returned ```java.lang.reflect.Constructor``` object. This call must be resolved to Demo's -```(String)``` method. -```java -// tr/Demo.java -package tr; - -import lib.annotations.callgraph.DirectCall; - -class Demo { - public static void verifyCall(){ /* do something */ } - - @DirectCall(name="verifyCall", line=9, resolvedTargets = "Ltr/Demo;") - public Demo(String s) { Demo.verifyCall(); } - - public static void main(String[] args) throws Exception { - Demo.class.getConstructor(String.class).newInstance("42"); - } -} -``` -[//]: # (END) - -##TR7 -[//]: # (MAIN: tr.Demo) -Tests a reflective method invocation that is performed on a class' private field that is retrieved via the -reflection API. In ```tr.Demo```'s main method a new ```tr.Demo``` object is created and an object -of type ```tr.CallTarget``` is assigned to its field. This field is then retrieved via the reflection -using ```java.lang.Class.getDeclaredField()``` and the field's name, namely ```"field"```. -```java.lang.reflect.Field.get``` is then used to get the object stored within the field of the Demo -instance that has been created previously. Afterwards, the returned instance is used to call -the ```target``` method. -```java -// tr/Demo.java -package tr; - -import java.lang.reflect.Field; -import lib.annotations.callgraph.IndirectCall; - -public class Demo { - private Target field; - - @IndirectCall( - name = "target", line = 18, resolvedTargets = "Ltr/CallTarget;" - ) - public static void main(String[] args) throws Exception { - Demo demo = new Demo(); - demo.field = new CallTarget(); - - Field field = Demo.class.getDeclaredField("field"); - Target target = (Target) field.get(demo); - target.target(); - } -} - -interface Target { - void target(); -} - -class CallTarget implements Target { - public void target(){ /* do something */ } -} - -class NeverInstantiated implements Target { - public void target(){ /* do something */ } -} -``` -[//]: # (END) - -##TR8 -[//]: # (MAIN: tr.Demo) -Tests a reflective method invocation that is performed on a class' public field that is retrieved via the -reflection API. In ```tr.Demo```'s main method a new ```tr.Demo``` object is created and an object -of type ```tr.CallTarget``` is assigned to its field. This field is then retrieved via the reflection -using ```java.lang.Class.getField()``` and the field's name, namely ```"field"```. -```java.lang.reflect.Field.get``` is then used to get the object stored within the field of the Demo -instance that has been created previously. Afterwards, the returned instance is used to call -the ```target``` method. - -```java -// tr/Demo.java -package tr; - -import java.lang.reflect.Field; -import lib.annotations.callgraph.IndirectCall; - -public class Demo { - public Target field; - - @IndirectCall( - name = "target", line = 18, resolvedTargets = "Ltr/CallTarget;" - ) - public static void main(String[] args) throws Exception { - Demo demo = new Demo(); - demo.field = new CallTarget(); - - Field field = Demo.class.getField("field"); - Target t = (Target) field.get(demo); - t.target(); - } -} - -interface Target { - void target(); -} - -class CallTarget implements Target { - public void target(){ /* do something */ } -} - -class NeverInstantiated implements Target { - public void target(){ /* do something */ } -} -``` -[//]: # (END) - -##TR9 -[//]: # (MAIN: tr.Demo) -This test cases concerns the reflection API as well as a class' static initializer. Within the main -method of ```tr.Demo``` ```Class.forName``` is called trying to retrieve an object of -```java.lang.Class``` which is parameterized over ```tr.InitializedClass```. This lookup can trigger -the static initializer of ```tr.InitializedClass``` which must thus be contained in program's call graph. -```java -// tr/Demo.java -package tr; - -import lib.annotations.callgraph.DirectCall; - -class Demo { - public static void verifyCall(){ /* do something */ } - - public static void main(String[] args) throws Exception { - Class.forName("tr.InitializedClass"); - } -} - -class InitializedClass { - - static { - staticInitializerCalled(); - } - - @DirectCall(name="verifyCall", line=21, resolvedTargets = "Ltr/Demo;") - static private void staticInitializerCalled(){ - Demo.verifyCall(); - } -} -``` -[//]: # (END) - -#LocallyResolvableReflection -The complete information is locally (intra-procedurally) available. -##LRR1 -[//]: # (MAIN: lrr.Demo) -This test cases concerns the reflection API as well as a class' static initializer. Within the main -method of ```lrr.Demo``` ```Class.forName``` is called trying to retrieve an object of -```java.lang.Class``` which is either parameterized over ```lrr.Left``` or ```lrr.Right```. This -lookup can trigger the static initializer of ```lrr.Left``` or ```lrr.Right``` which must thus be -contained in program's call graph. -```java -// lrr/Demo.java -package lrr; - -import lib.annotations.callgraph.DirectCall; - -class Demo { - public static void verifyCall(){ /* do something */ } - - public static void main(String[] args) throws Exception { - String className = (args.length % 2 == 0) ? "lrr.Left" : "lrr.Right"; - Class.forName(className); - } -} - -class Left { - - static { - staticInitializerCalled(); - } - - @DirectCall(name="verifyCall", line=22, resolvedTargets = "Llrr/Demo;") - static private void staticInitializerCalled(){ - Demo.verifyCall(); - } -} - - -class Right { - - static { - staticInitializerCalled(); - } - - @DirectCall(name="verifyCall", line=35, resolvedTargets = "Llrr/Demo;") - static private void staticInitializerCalled(){ - Demo.verifyCall(); - } -} -``` -[//]: # (END) - -##LRR2 -[//]: # (MAIN: lrr.Demo) -This test cases concerns the reflection API as well as a class' static initializer. Within the main -method of ```lrr.Demo``` ```Class.forName``` is called trying to retrieve an object of -```java.lang.Class``` which is either parameterized over ```lrr.IsEven``` or ```lrr.IsOdd``` where -both strings are constructed over a StringBuilder. This lookup can then trigger the static initializers -of ```lrr.IsEven``` or ```lrr.IsOdd``` which must thus be contained in program's call graph. -```java -// lrr/Demo.java -package lrr; - -import lib.annotations.callgraph.DirectCall; - -class Demo { - public static void verifyCall(){ /* do something */ } - - public static void main(String[] args) throws Exception { - StringBuilder builder = new StringBuilder("lrr.Is"); - if (args.length % 2 == 0) - builder.append("Even"); - else - builder.append("Odd"); - String className = builder.toString(); - Class.forName(className); - } -} - -class IsEven { - - static { - staticInitializerCalled(); - } - - @DirectCall(name="verifyCall", line=27, resolvedTargets = "Llrr/Demo;") - static private void staticInitializerCalled(){ - Demo.verifyCall(); - } - } - - - class IsOdd { - - static { - staticInitializerCalled(); - } - - @DirectCall(name="verifyCall", line=40, resolvedTargets = "Llrr/Demo;") - static private void staticInitializerCalled(){ - Demo.verifyCall(); - } - } -``` -[//]: # (END) - -##LRR3 -[//]: # (MAIN: lrr.Demo) -This test cases concerns the reflection API as well as a class' static initializer. Within the main -method of ```lrr.Demo``` ```Class.forName``` is called trying to retrieve an object of -```java.lang.Class``` which is parameterized over ```lrr.TargetClass```. The target String which is -passed to the respective ```Class.forName``` call is first assigned to Demo's field and then the -field's value is read and finally passed as parameter. -This lookup can then trigger the static initializer of ```lrr.TargetClass``` which must thus be -contained in program's call graph. -```java -// lrr/Demo.java -package lrr; - -import lib.annotations.callgraph.DirectCall; - -class Demo { - private String className; - - public static void verifyCall(){ /* do something */ } - - public static void main(String[] args) throws Exception { - Demo demo = new Demo(); - demo.className = "lrr.TargetClass"; - Class.forName(demo.className); - } -} - -class TargetClass { - - static { - staticInitializerCalled(); - } - - @DirectCall(name="verifyCall", line=25, resolvedTargets = "Llrr/Demo;") - static private void staticInitializerCalled(){ - Demo.verifyCall(); - } - } -``` -[//]: # (END) - -#ContextSensitiveReflection -The concrete strings require information about the context. - -##CSR1 -[//]: # (MAIN: csr.Demo) -This test cases concerns the reflection API as well as a class' static initializer. Within the main -method of ```csr.Demo``` a static method is called that receives the string constant -which is transitively passed to ```Class.forName``` and then tries to retrieve an object of -```java.lang.Class``` which is parameterized over ```csr.TargetClass```. To infer the parameter that -flows into ```Class.forName``` inter-procedural string tracking is required. This lookup can trigger -the static initializer of ```csr.TargetClass```. -```java -// csr/Demo.java -package csr; - -import lib.annotations.callgraph.DirectCall; -class Demo { - public static void verifyCall(){ /* do something */ } - - static void callForName(String className) throws Exception { - Class.forName(className); - } - - public static void main(String[] args) throws Exception { - Demo.callForName("csr.TargetClass"); - } -} - -class TargetClass { - - static { - staticInitializerCalled(); - } - - @DirectCall(name="verifyCall", line=24, resolvedTargets = "Lcsr/Demo;") - static private void staticInitializerCalled(){ - Demo.verifyCall(); - } - } -``` -[//]: # (END) - -##CSR2 -[//]: # (MAIN: csr.Demo) -This test cases concerns the reflection API as well as a class' static initializer. Within the main -method of ```csr.Demo``` a static method is called that receives the string constant -which is transitively passed to ```Class.forName``` and then tries to retrieve an object of -```java.lang.Class``` which is parameterized over an __unknown__ type. In this test it is impossible -to get any information about the retrieved typed and, therefore, all possible types must be considered -for a sound method resolution. -```java -// csr/Demo.java -package csr; - -import lib.annotations.callgraph.DirectCall; -public class Demo { - public static void verifyCall(){ /* do something */ } - - static void callForName(String className) throws Exception { - Class.forName(className); - } - - public static void main(String[] args) throws Exception { - Demo.callForName(args[0]); - } -} - -class TargetClass { - - static { - staticInitializerCalled(); - } - - @DirectCall(name="verifyCall", line=24, resolvedTargets = "Lcsr/Demo;") - static private void staticInitializerCalled(){ - Demo.verifyCall(); - } - } -``` -[//]: # (END) - -##CSR3 -[//]: # (MAIN: csr.Demo) -This test cases concerns the reflection API as well as a class' static initializer. Within the main -method of ```csr.Demo``` a static method is called reads the value from a static field which is -first written write before the method call to ```Demo.callForName``` and then passed to ```Class.forName```. -```Class.forName``` then tries to retrieve an object of ```java.lang.Class``` which is parameterized -over ```csr.CallTarget```. In this test it is impossible to get any information about the retrieved -typed and, therefore, all possible types must be considered for a sound method resolution. -```java -// csr/Demo.java -package csr; - -import lib.annotations.callgraph.DirectCall; -class Demo { - public static String className; - - public static void verifyCall(){ /* do something */ } - - static void callForName() throws Exception { - Class.forName(Demo.className); - } - - public static void main(String[] args) throws Exception { - Demo.className = "csr.CallTarget"; - Demo.callForName(); - } -} - -class CallTarget { - - static { - staticInitializerCalled(); - } - - @DirectCall(name="verifyCall", line=27, resolvedTargets = "Lcsr/Demo;") - static private void staticInitializerCalled(){ - Demo.verifyCall(); - } - } -``` -[//]: # (END) - -##CSR4 -[//]: # (MAIN: csr.Demo) -This test cases concerns the reflection API as well as a class' static initializer. Within the main -method the methods ```java.lang.System.getProperties``` and ```java.lang.System.setProperties``` are -used to add a ```className``` property with the value ```csr.TargetClass``` to the global system -properties and thus make it globally available throughout the program. Afterwards, -```csr.Demo.callForName``` is called that then uses ```java.lang.System.getProperty("className")``` -to access the stored string which is passed to the ```Class.forName``` call. Modelling system -properties would help to resolve this case soundly and better precision. -```java -// csr/Demo.java -package csr; - -import java.util.Properties; -import lib.annotations.callgraph.DirectCall; - -class Demo { - - public static void verifyCall(){ /* do something */ } - - static void callForName() throws Exception { - String className = System.getProperty("className"); - Class.forName(className); - } - - public static void main(String[] args) throws Exception { - Properties props = System.getProperties(); - props.put("className", "csr.TargetClass"); - System.setProperties(props); - Demo.callForName(); - } -} - -class TargetClass { - - static { - staticInitializerCalled(); - } - - @DirectCall(name="verifyCall", line=31, resolvedTargets = "Lcsr/Demo;") - static private void staticInitializerCalled(){ - Demo.verifyCall(); - } - } -``` -[//]: # (END) - -#ClassForNameExceptions -Test cases w.r.t. to classloading using ```Class.forName(className:String)``` and its respective -exceptions to test whether valid path might be ignored which leads to unsoundness. - -##CFNE1 -[//]: # (MAIN: cfne.Demo) -This test case targets a common try catch pattern when classes are loaded. An existing class is loaded -over ```Class.forName(...)```, instantiated and then casted to another class. Unfortunately, the class -that is instantiated is __incompatible__ with the cast such that the operation results in a -```ClassCastException```. -```java -// cfne/Demo.java -package cfne; - -import lib.annotations.callgraph.DirectCall; - -public class Demo { - - public static void verifyCall(){ /* do something */ } - - @DirectCall(name="verifyCall", line = 15, resolvedTargets = "Lcfne/Demo;") - public static void main(String[] args){ - try { - Class cls = Class.forName("cfne.DeceptiveClass"); - LoadedClass lCls = (LoadedClass) cls.newInstance(); - } catch(ClassCastException cce){ - verifyCall(); - } catch(ClassNotFoundException cnfe){ - // DEAD CODE - } catch(Exception rest){ - // DEAD CODE - } - } -} - -class DeceptiveClass { - -} - -class LoadedClass { - -} -``` -[//]: # (END) - -##CFNE2 -[//]: # (MAIN: cfne.Demo) -This test case targets a common try catch pattern when classes are loaded. An absent class is loaded -over ```Class.forName(...)```. Since the class __can't be found__ the operation results in a ```ClassNotFoundException``` -which is handled in one of the catch blocks. -```java -// cfne/Demo.java -package cfne; - -import lib.annotations.callgraph.DirectCall; - -public class Demo { - - public static void verifyCall(){ /* do something */ } - - @DirectCall(name="verifyCall", line = 18, resolvedTargets = "Lcfne/Demo;") - public static void main(String[] args){ - try { - Class cls = Class.forName("cfne.CatchMeIfYouCan"); - // DEAD CODE - LoadedClass lCls = (LoadedClass) cls.newInstance(); - } catch(ClassCastException cce){ - /* DEAD CODE */ - } catch(ClassNotFoundException cnfe){ - verifyCall(); - } catch(Exception rest){ - //DEAD CODE - } - } -} - -class LoadedClass { - -} -``` -[//]: # (END) - -##CFNE3 -[//]: # (MAIN: cfne.Demo) -This case targets a concerns not only loading of classes but also the execution of their -static initializer. When a class is loaded, its static initializer must be called. -```java -// cfne/Demo.java -package cfne; - -import lib.annotations.callgraph.DirectCall; - -public class Demo { - - public static void verifyCall(){ /* do something */ } - - public static void main(String[] args){ - try { - Class cls = Class.forName("cfne.LoadedClass"); - Object lCls = cls.newInstance(); - } catch(ClassCastException cce){ - // DEAD CODE - } catch(ClassNotFoundException cnfe){ - // DEAD CODE - } catch(Exception rest){ - //DEAD CODE - } - } -} - -class LoadedClass { - - static { - staticInitializerCalled(); - } - - @DirectCall(name="verifyCall", line=31, resolvedTargets = "Lcfne/Demo;") - static private void staticInitializerCalled(){ - Demo.verifyCall(); - } -} -``` -[//]: # (END) - -##CFNE4 -[//]: # (MAIN: cfne.Demo) -This case targets a concerns not only loading of classes but also the execution of their -static initializer. When a class is loaded, its static initializer must be called. Also the static -initializers of potential super classes. -```java -// cfne/Demo.java -package cfne; - -import lib.annotations.callgraph.DirectCall; - -public class Demo { - - public static void verifyCall(){ /* do something */ } - - public static void main(String[] args){ - try { - Class cls = Class.forName("cfne.LoadedClass"); - Object lCls = cls.newInstance(); - } catch(ClassCastException cce){ - // DEAD CODE - } catch(ClassNotFoundException cnfe){ - // DEAD CODE - } catch(Exception rest){ - //DEAD CODE - } - } -} - -class LoadedClass extends RootClass { - -} - -class RootClass { - - static { - staticInitializerCalled(); - } - - @DirectCall(name="verifyCall", line=35, resolvedTargets = "Lcfne/Demo;") - static private void staticInitializerCalled(){ - Demo.verifyCall(); - } -} -``` -[//]: # (END) diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/Serialization.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/Serialization.markdown deleted file mode 100644 index f0cf635922..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/Serialization.markdown +++ /dev/null @@ -1,635 +0,0 @@ -#SerializableClasses -These category comprises test cases that model callbacks that must be handled when dealing with -```java.io.Serializable``` classes. As soon as object (de-)serialization is found within a program -those mechanism can be used and all related methods must therefore be considered as on-the-fly -entry points. -##Ser1 -[//]: # (MAIN: ser.Demo) -This test pertains to the ```writeObject``` callback method that can be implemented when a class -implements ```java.io.Serializable``` as ```ser.Demo``` does. Overriding ```writeObject``` forces -the JVM to call ```writeObject``` instead of the normally used ```defaultWriteObject``` method. In -```ser.Demo```'s main method, an instance of ```ser.Demo``` as well as an ```java.io.ObjectOutputStream``` -is created. The latter is then used to serialize the instance of ```ser.Demo``` that has been created -previously. Serializing this object triggers the JVM which then calls the overridden -```ser.Demo.writeObject``` method. -```java -// ser/Demo.java -package ser; - -import java.io.Serializable; -import java.io.FileOutputStream; -import java.io.ObjectOutputStream; -import java.io.IOException; -import lib.annotations.callgraph.DirectCall; - -public class Demo implements Serializable { - - static final long serialVersionUID = 42L; - - @DirectCall(name = "defaultWriteObject", resolvedTargets = "Ljava/io/ObjectOutputStream;", line = 15) - private void writeObject(java.io.ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - } - - public static void main(String[] args) throws Exception { - Demo serialize = new Demo(); - FileOutputStream fos = new FileOutputStream("test.ser"); - ObjectOutputStream out = new ObjectOutputStream(fos); - out.writeObject(serialize); // triggers serialization - out.close(); - } -} -``` -[//]: # (END) - -##Ser2 -[//]: # (MAIN: ser.Demo) -This test pertains to the ```writeObject``` callback method that can be implemented when a class -implements ```java.io.Serializable``` as ```ser.Demo``` does. Overriding ```writeObject``` forces -the JVM to call ```writeObject``` instead of the normally used ```defaultWriteObject``` method. In -```ser.Demo```'s main method, an instance of ```ser.Demo```, ```ser.AnotherSerializableClass```, -as well as an ```java.io.ObjectOutputStream``` is created. The latter is then used to serialize either -an instance of ```ser.Demo``` or ```ser.AnotherSerializableClass``` that might have been created -previously. Serializing this object triggers the JVM which then either calls the overridden -```ser.Demo.writeObject``` method or the ```defaultWriteObject``` method when -```ser.AnotherSerializableClass``` is serialized. -```java -// ser/Demo.java -package ser; - -import java.io.Serializable; -import java.io.FileOutputStream; -import java.io.ObjectOutputStream; -import java.io.IOException; -import lib.annotations.callgraph.DirectCall; - -public class Demo implements Serializable { - - static final long serialVersionUID = 42L; - - @DirectCall(name = "defaultWriteObject", resolvedTargets = "Ljava/io/ObjectOutputStream;", line = 15) - private void writeObject(java.io.ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - } - - public static void main(String[] args) throws Exception { - Object serialize; - if(args.length == 0) - serialize = new Demo(); - else - serialize = new AnotherSerializableClass(); - FileOutputStream fos = new FileOutputStream("test.ser"); - ObjectOutputStream out = new ObjectOutputStream(fos); - out.writeObject(serialize); - out.close(); - } -} - -class AnotherSerializableClass implements Serializable {} -``` -[//]: # (END) - -##Ser3 -[//]: # (MAIN: ser.Demo) -This test pertains to the ```writeObject``` callback method that can be implemented when a class -implements ```java.io.Serializable``` as ```ser.Demo``` does. Overriding ```writeObject``` forces -the JVM to call ```writeObject``` instead of the normally used ```defaultWriteObject``` method. In -```ser.Demo```'s main method, an instance of ```ser.Demo``` is created and passed to static method. -This method, ```ser.Demo.serialize```, creates an output stream and serializes the object given by the method's parameter. -Without considering inter-procedural information, the test case can only be modeled imprecisely by -taking all ```writeObject``` methods into account. -```java -// ser/Demo.java -package ser; - -import java.io.Serializable; -import java.io.FileOutputStream; -import java.io.ObjectOutputStream; -import java.io.IOException; -import lib.annotations.callgraph.DirectCall; - -public class Demo implements Serializable { - - static final long serialVersionUID = 42L; - - @DirectCall(name = "defaultWriteObject", resolvedTargets = "Ljava/io/ObjectOutputStream;", line = 15) - private void writeObject(java.io.ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - } - - public static void serialize(Object serialize) throws Exception { - FileOutputStream fos = new FileOutputStream("test.ser"); - ObjectOutputStream out = new ObjectOutputStream(fos); - out.writeObject(serialize); - out.close(); - } - - public static void main(String[] args) throws Exception { - Demo serializeIt = new Demo(); - serialize(serializeIt); - } -} -``` -[//]: # (END) - -##Ser4 -[//]: # (MAIN: ser.Demo) -This test pertains to the ```readObject``` callback method that can be implemented when a class -implements ```java.io.Serializable``` as ```ser.Demo``` does. Overriding ```readObject``` forces -the JVM to call ```readObject``` instead of the normally used ```defaultReadObject``` method. In -```ser.Demo```'s main method, an ```ObjectInputStream``` is used to deserialize a previously -serialized object from the ```test.ser``` file. The call to ```ObjectInputStream.readObject``` then -causes the JVM to trigger the deserialization mechanism which in turn calls ```ser.Demo.readObject```. -Without any information about the file's content it is impossible to resolve the call precisely. -```java -// ser/Demo.java -package ser; - -import java.io.Serializable; -import java.io.FileInputStream; -import java.io.ObjectInputStream; -import java.io.IOException; -import lib.annotations.callgraph.DirectCall; - -public class Demo implements Serializable { - - static final long serialVersionUID = 42L; - - @DirectCall(name = "defaultReadObject", resolvedTargets = "Ljava/io/ObjectInputStream;", line = 15) - private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - } - - public static void main(String[] args) throws Exception { - FileInputStream fis = new FileInputStream("test.ser"); - ObjectInputStream in = new ObjectInputStream(fis); - Object obj = in.readObject(); - in.close(); - } -} -``` -[//]: # (END) - -##Ser5 -[//]: # (MAIN: ser.Demo) -This test pertains to the ```readObject``` callback method that can be implemented when a class -implements ```java.io.Serializable``` as ```ser.Demo``` does. Overriding ```readObject``` forces -the JVM to call ```readObject``` instead of the normally used ```defaultReadObject``` method. In -```ser.Demo```'s main method, an ```ObjectInputStream``` is used to deserialize a previously -serialized object from the ```test.ser``` file. The call to ```ObjectInputStream.readObject``` then -causes the JVM to trigger the deserialization mechanism which in turn calls ```ser.Demo.readObject```. -The returned result is then casted to ```ser.Demo``` which either results in a class cast exception or -implies that the deserialized object was from type ```ser.Demo```. -Without any information about the file's content it is impossible to resolve the call precisely. -```java -// ser/Demo.java -package ser; - -import java.io.Serializable; -import java.io.FileInputStream; -import java.io.ObjectInputStream; -import java.io.IOException; -import lib.annotations.callgraph.DirectCall; - -public class Demo implements Serializable { - - static final long serialVersionUID = 42L; - - @DirectCall(name = "defaultReadObject", resolvedTargets = "Ljava/io/ObjectInputStream;", line = 15) - private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - } - - public static void main(String[] args) throws Exception { - FileInputStream fis = new FileInputStream("test.ser"); - ObjectInputStream in = new ObjectInputStream(fis); - Demo obj = (Demo) in.readObject(); - in.close(); - } -} -``` -[//]: # (END) - -##Ser6 -[//]: # (MAIN: ser.Demo) -This test pertains to the ```writeReplace``` callback method that can be implemented when a class -implements ```java.io.Serializable``` as ```ser.Demo``` does. Overriding ```writeReplace``` forces -the JVM to call ```writeReplace``` instead of the normally used ```defaultwriteObject``` method. In -```ser.Demo```'s main method, an ```ObjectOutputStream``` is used to serialize an instance of -```ser.Demo```. The call to ```ObjectInputStream.writeObject``` then causes the JVM to trigger the -serialization mechanism which in turn calls ```ser.Demo.writeReplace```. -```java -// ser/Demo.java -package ser; - -import java.io.Serializable; -import java.io.FileOutputStream; -import java.io.ObjectOutputStream; -import java.io.IOException; -import java.io.ObjectStreamException; -import lib.annotations.callgraph.DirectCall; - -public class Demo implements Serializable { - - static final long serialVersionUID = 42L; - - public Object replace() { return this; } - @DirectCall(name = "replace", returnType = Object.class, resolvedTargets = "Lser/Demo;", line = 17) - private Object writeReplace() throws ObjectStreamException { - return replace(); - } - - public static void main(String[] args) throws Exception { - Demo serialize = new Demo(); - FileOutputStream fos = new FileOutputStream("test.ser"); - ObjectOutputStream out = new ObjectOutputStream(fos); - out.writeObject(serialize); - out.close(); - } -} -``` -[//]: # (END) - -##Ser7 -[//]: # (MAIN: ser.Demo) -This test pertains to the ```readResolve``` callback method that can be implemented when a class -implements ```java.io.Serializable``` as ```ser.Demo``` does. Overriding ```readResolve``` forces -the JVM to call ```readResolve``` instead of the normally used ```defaultReadObject``` method when -```readObject``` is not overridden. In ```ser.Demo```'s main method, an ```ObjectOutputStream``` is -used to serialize an instance of ```ser.Demo```. The call to ```ObjectInputStream.readObject``` then -causes the JVM to trigger the serialization mechanism which in turn calls ```ser.Demo.readResolve```. -```java -// ser/Demo.java -package ser; - -import java.io.Serializable; -import java.io.FileInputStream; -import java.io.ObjectInputStream; -import java.io.IOException; -import java.io.ObjectStreamException; -import lib.annotations.callgraph.DirectCall; - -public class Demo implements Serializable { - - static final long serialVersionUID = 42L; - - public Object replace() { return this; } - @DirectCall(name = "replace", returnType = Object.class, resolvedTargets = "Lser/Demo;", line = 17) - private Object readResolve() throws ObjectStreamException { - return replace(); - } - - public static void main(String[] args) throws Exception { - FileInputStream fis = new FileInputStream("test.ser"); - ObjectInputStream in = new ObjectInputStream(fis); - Demo obj = (Demo) in.readObject(); - in.close(); - } -} -``` -[//]: # (END) - -##Ser8 -[//]: # (MAIN: ser.Demo) -This test pertains to the ```validateObject``` callback method that can be implemented when a class -implements ```java.io.Serializable``` as ```ser.Demo``` does. Overriding ```validateObject``` implies -that it can be called by the JVM after an object is deserialized when a validation procedure has been -registered (see ```registerValidation``` in ```readObject```). -```java -// ser/Demo.java -package ser; - -import java.io.Serializable; -import java.io.FileInputStream; -import java.io.ObjectInputStream; -import java.io.IOException; -import java.io.ObjectStreamException; -import java.io.ObjectInputValidation; -import java.io.InvalidObjectException; -import lib.annotations.callgraph.DirectCall; - -public class Demo implements Serializable, ObjectInputValidation { - - static final long serialVersionUID = 42L; - - public void callback() { } - @DirectCall(name = "callback", resolvedTargets = "Lser/Demo;", line = 19) - public void validateObject() throws InvalidObjectException { - callback(); - } - - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - in.registerValidation(this, 0); - in.defaultReadObject(); - } - - public static void main(String[] args) throws Exception { - FileInputStream fis = new FileInputStream("test.ser"); - ObjectInputStream in = new ObjectInputStream(fis); - Demo obj = (Demo) in.readObject(); - in.close(); - } -} -``` -[//]: # (END) - -##Ser9 -[//]: # (MAIN: ser.Demo) -This scenario tests whether the constructor calls w.r.t. serializable classes are handled soundly. -During deserialization, the JVM calls the first constructor that neither has any formal parameter nor -belongs to ```Serializable``` class of the class which is deserialized. In the scenario below, an -instance of ```ser.Demo``` is going to be deserialized and during this process ```Superclass.``` -is called. -```java -// ser/Superclass.java -package ser; - -import lib.annotations.callgraph.DirectCall; - -public class Superclass { - public void callback() { } - - @DirectCall(name = "callback", resolvedTargets = "Lser/Superclass;", line = 10) - public Superclass() { - callback(); - } -} -``` - -```java -// ser/Demo.java -package ser; - -import java.io.Serializable; -import java.io.FileInputStream; -import java.io.ObjectInputStream; -import java.io.IOException; - -public class Demo extends Superclass implements Serializable { - - static final long serialVersionUID = 42L; - - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - } - - public static void main(String[] args) throws Exception { - FileInputStream fis = new FileInputStream("test.ser"); - ObjectInputStream in = new ObjectInputStream(fis); - Demo obj = (Demo) in.readObject(); - in.close(); - } -} -``` -[//]: # (END) - -#ExternalizableClasses -Callback methods related to ```java.io.Externalizable``` classes. -##ExtSer1 -[//]: # (MAIN: extser.Demo) -This test pertains to the ```writeExternal``` callback method that can be implemented when a class -implements ```java.io.Externalizable``` as ```ser.Demo``` does. Overriding ```writeExternal``` forces -the JVM to call ```writeExternal``` during an object's serialization via ```Externalizable```. In -```ser.Demo```'s main method, an instance of ```ser.Demo``` as well as an ```java.io.ObjectOutputStream``` -is created. The latter is then used to serialize the instance of ```ser.Demo``` that has been created -previously. Serializing this object triggers the JVM's serialization mechanism which then calls the -overridden ```ser.Demo.writeExternal``` method. -```java -// extser/Demo.java -package extser; - -import java.io.Externalizable; -import java.io.FileOutputStream; -import java.io.ObjectOutputStream; -import java.io.ObjectOutput; -import java.io.FileInputStream; -import java.io.ObjectInputStream; -import java.io.ObjectInput; -import java.io.IOException; -import lib.annotations.callgraph.DirectCall; - -public class Demo implements Externalizable { - - @DirectCall(name = "callback", resolvedTargets = "Lextser/Demo;", line = 17) - public void writeExternal(ObjectOutput out) throws IOException { - callback(); - } - - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - callback(); - } - - public void callback() { } - - public static void main(String[] args) throws Exception { - Demo f = new Demo(); - FileOutputStream fos = new FileOutputStream("test.ser"); - ObjectOutputStream out = new ObjectOutputStream(fos); - out.writeObject(f); - out.close(); - } -} -``` -[//]: # (END) - -##ExtSer2 -[//]: # (MAIN: extser.Demo) -This test pertains to the ```readExternal``` callback method that can be implemented when a class -implements ```java.io.Externalizable``` as ```ser.Demo``` does. Overriding ```readExternal``` forces -the JVM to call ```readExternal``` during an object's deserialization via ```Externalizable```. In -```ser.Demo```'s main method, an instance of ```ser.Demo``` as well as an ```java.io.ObjectInputStream``` -is created. The latter is then used to deserialize the instance of ```ser.Demo``` that was written to -```test.ser```previously. Deserializing this object triggers the JVM's deserialization mechanism -which then calls the overridden ```ser.Demo.readExternal``` method. -```java -// extser/Demo.java -package extser; - -import java.io.Externalizable; -import java.io.FileOutputStream; -import java.io.ObjectOutputStream; -import java.io.ObjectOutput; -import java.io.FileInputStream; -import java.io.ObjectInputStream; -import java.io.ObjectInput; -import java.io.IOException; -import lib.annotations.callgraph.DirectCall; - -public class Demo implements Externalizable { - - @DirectCall(name = "callback", resolvedTargets = "Lextser/Demo;", line = 17) - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - callback(); - } - - public void writeExternal(ObjectOutput out) throws IOException { - callback(); - } - - public void callback() { } - - public static void main(String[] args) throws Exception { - FileInputStream fis = new FileInputStream("test.ser"); - ObjectInputStream in = new ObjectInputStream(fis); - Demo obj = (Demo) in.readObject(); - in.close(); - } -} -``` -[//]: # (END) - -##ExtSer3 -[//]: # (MAIN: extser.Demo) -This scenario tests whether the constructor calls w.r.t. externalizable classes are handled soundly. -During deserialization, the JVM calls the no-argument constructor of the ```Externalizable``` class. -```java -// extser/Demo.java -package extser; - -import java.io.Externalizable; -import java.io.FileOutputStream; -import java.io.ObjectOutputStream; -import java.io.ObjectOutput; -import java.io.FileInputStream; -import java.io.ObjectInputStream; -import java.io.ObjectInput; -import java.io.IOException; -import lib.annotations.callgraph.DirectCall; - -public class Demo implements Externalizable { - - public void callback() { } - - @DirectCall(name = "callback", resolvedTargets = "Lextser/Demo;", line = 19) - public Demo() { - callback(); - } - - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - callback(); - } - - public void writeExternal(ObjectOutput out) throws IOException { - callback(); - } - - public static void main(String[] args) throws Exception { - FileInputStream fis = new FileInputStream("test.ser"); - ObjectInputStream in = new ObjectInputStream(fis); - Demo obj = (Demo) in.readObject(); - in.close(); - } -} -``` -[//]: # (END) - -#Serialization and Lambdas -Tests Java's serialization mechanism when Lambdas are (de)serialized, i.e., de(serialization) of Lambdas -causes the JVM to use ```java.lang.invoke.SerializedLambda```. -##SerLam1 -[//]: # (MAIN: serlam.DoSerialization) -Tests whether the serialization of lambdas that implement a functional interface is modelled correctly. -```java -// serlam/DoSerialization.java -package serlam; - -import java.io.Serializable; -import java.lang.invoke.SerializedLambda; -import java.lang.reflect.Method; -import java.util.function.Function; - -import java.io.FileOutputStream; -import java.io.ObjectOutputStream; -import java.io.FileInputStream; -import java.io.ObjectInputStream; - -import lib.annotations.callgraph.IndirectCall; - -public class DoSerialization { - - @FunctionalInterface interface Test extends Serializable{ - String concat(Integer seconds); - } - - @IndirectCall( - name = "writeReplace", - line = 33, - resolvedTargets = "Ljava/lang/invoke/SerializedLambda;") - public static void main(String[] args) throws Exception { - float y = 3.13f; - String s = "bar"; - - Test lambda = (Integer x) -> "Hello World " + x + y + s; - - FileOutputStream fos = new FileOutputStream("serlam1.ser"); - ObjectOutputStream out = new ObjectOutputStream(fos); - out.writeObject(lambda); - out.close(); - - - } -} -``` -[//]: # (END) - -##SerLam2 -[//]: # (MAIN: serlam.DoDeserialization) -Tests whether the deserialization of lambdas that implement a functional interface is modelled correctly. -```java -// serlam/DoSerialization.java -package serlam; - -import java.io.FileOutputStream; -import java.io.ObjectOutputStream; - -public class DoSerialization { - - public static void main(String[] args) throws Exception { - float y = 3.14f; - String s = "foo"; - - Test lambda = (Integer x) -> "Hello World " + x + y + s; - - FileOutputStream fos = new FileOutputStream("serlam2.ser"); - ObjectOutputStream out = new ObjectOutputStream(fos); - out.writeObject(lambda); - out.close(); - } -} -``` -```java -// serlam/Test.java -package serlam; - -import java.io.Serializable; - -public @FunctionalInterface interface Test extends Serializable{ - String concat(Integer seconds); -} -``` - -```java -// serlam/DoDeserialization.java -package serlam; - -import java.io.FileInputStream; -import java.io.ObjectInputStream; - -import lib.annotations.callgraph.IndirectCall; - -public class DoDeserialization { - - @IndirectCall( - name = "readResolve", - line = 18, - resolvedTargets = "Ljava/lang/invoke/SerializedLambda;") - public static void main(String[] args) throws Exception { - DoSerialization.main(args); - FileInputStream fis = new FileInputStream("serlam2.ser"); - ObjectInputStream in = new ObjectInputStream(fis); - Object obj = in.readObject(); - in.close(); - } -} -``` -[//]: # (END) \ No newline at end of file diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/StaticInitializers.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/StaticInitializers.markdown deleted file mode 100644 index 3097ef3915..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/StaticInitializers.markdown +++ /dev/null @@ -1,295 +0,0 @@ -#StaticInitializers -Static initializers have to be treated as (on-the-fly) entry points as they are called by the JVM -when a class is loaded. More details can be found in the JVM spec (§12.4.1 - When initialization occurs). -##SI1 -[//]: # (MAIN: si.Main) -A static initializer should be triggered when a *non-constant static field* is referenced. The scenario below -shows an interface ```si.NonConstantFieldRef``` which declares a static non-constant field that is referenced -once in the ```si.Main```'s main method. When the field is references, the field must be initialized -and in order to do so, the JVM calls ```si.NonConstantFieldRef```'s static initializer (``````). -```java -// si/NonConstantFieldRef.java -package si; - -import lib.annotations.callgraph.DirectCall; -public interface NonConstantFieldRef { - - static String nonConstantField = init(); - - @DirectCall(name = "callback", line = 10, resolvedTargets = "Lsi/Demo;") - static String init() { - callback(); - return "Demo"; - } - - static void callback() {} -} - -class Main { - public static void main(String[] args) { - NonConstantFieldRef.nonConstantField.toString(); - } -} -``` -[//]: # (END) - -##SI2 -[//]: # (MAIN: si.Demo) -A static initializer should be triggered when a *static interface method* is invoked. The scenario below -shows an interface ```si.Interface``` which declares a static method (```callback```) which is called in the -```si.Demo```'s main method. The invocation of ```callback``` causes the JVM to call ```si.Interface```'s - static initializer (``````). - >Please not that this is not directly annotatable and we thus use a static initialized field that - is also initialized within the static initializer and triggers a method invocation that can be tested. -```java -// si/Interface.java -package si; - -import lib.annotations.callgraph.DirectCall; -public interface Interface { - - static String name = init(); - - @DirectCall(name = "callback", line = 10, resolvedTargets = "Lsi/Interface;") - static String init() { - callback(); - return "Demo"; - } - - static void callback() {} -} -class Demo { - - public static void main(String[] args) { - Interface.callback(); - } -} -``` -[//]: # (END) - -##SI3 -[//]: # (MAIN: si.Demo) -Static initializer of an interface with a default method. An interface's static initializer is also -triggered when 1) *a subtype is initialized* and 2) *the interface has a default method*. Where as 1) -is given when a new instance of ```si.Demo``` - it implements the interface ```si.Interface```- is -created in ```si.Demo```'s main method, 2) is given because ```si.Interface``` declares the -default method ```Interface.defaultMethod```. Since both criteria are fulfilled, the JVM will also -initialize ```si.Interface```. -```java -// si/Interface.java -package si; - -import lib.annotations.callgraph.DirectCall; -public interface Interface { - - static String name = init(); - - @DirectCall(name = "callback", line = 10, resolvedTargets = "Lsi/Interface;") - static String init() { - callback(); - return "Demo"; - } - - default String defaultMethod() { return "Demo"; } - - static void callback() {} -} -class Demo implements Interface { - public static void main(String[] args) { - new Demo(); - } -} -``` -[//]: # (END) - -##SI4 -[//]: # (MAIN: si.Demo) -An interface static initializer should be triggered when a *final static field* with a *non-primitive type* -and *non-String type* is referenced. The scenario below shows an interface ```si.Interface``` which -declares a static final field of type ```si.Demo``` that is referenced once in the ```si.Demo```'s -main method. When the field is referenced, the field must be initialized and in order to do so, -the JVM calls ```si.Interface```'s static initializer (``````). -```java -// si/Demo.java -package si; - -import lib.annotations.callgraph.DirectCall; -public class Demo { - public static void main(String[] args) { - Interface.referenceMe.toString(); - } -} - -interface Interface { - - static String testHook = init(); - static final Demo referenceMe = new Demo(); - - @DirectCall(name = "callback", line = 17, resolvedTargets = "Lsi/Interface;") - static String init() { - callback(); - return "Interface"; - } - - static void callback(){} -} -``` -[//]: # (END) - -##SI5 -[//]: # (MAIN: si.Main) -The instantiation of a class should trigger its static initializer. The class ```si.Demo``` has an -explictly defined static initializer. When a new instance of the ```si.Demo``` class is created in -```si.Main```'s main method, the static initializer of ```si.Demo``` must be executed. -```java -// si/Main.java -package si; - -import lib.annotations.callgraph.DirectCall; -public class Main{ - - public static void main(String[] args) { - new Demo(); - } -} - -class Demo { - - static { - init(); - } - - @DirectCall(name = "callback", line = 19, resolvedTargets = "Lsi/Demo;") - static void init() { - callback(); - } - - static void callback() {} -} -``` -[//]: # (END) - -##SI6 -[//]: # (MAIN: si.Main) -```si.Demo``` declares a static method which is called in ```si.Main```'s main method. Calling a -static method on a class receiver triggers the JVM to execute its static initializer when the class -was not used before. -```java -// si/Main.java -package si; - -import lib.annotations.callgraph.DirectCall; -public class Main { - - public static void main(String[] args) { - Demo.callback(); - } -} - -class Demo { - static String name = init(); - - @DirectCall(name = "callback", line = 16, resolvedTargets = "Lsi/Demo;") - static String init() { - callback(); - return "42"; - } - - static void callback() {} -} -``` -[//]: # (END) - -##SI7 -[//]: # (MAIN: si.Main) -Assigning a *non-final static field* (e.g. ```si.Demo.assignMe```) triggers the JVM to execute -```si.Demo``` static initializer. ```si.Demo.assignMe``` is assigned in ```si.Main```'s main method -and thus its static initializer must be contained in the call graph. -```java -// si/Main.java -package si; - -import lib.annotations.callgraph.DirectCall; -public class Main { - - public static void main(String[] args) { - Demo.assignMe = 42; - } -} - -class Demo { - static String name = init(); - - static int assignMe; - - @DirectCall(name = "callback", line = 18, resolvedTargets = "Lsi/Demo;") - static String init() { - callback(); - return "Demo"; - } - - static void callback() {} -} -``` -[//]: # (END) - -##SI8 -[//]: # (MAIN: si.Main) -When initialization of a class occurs during execution, all its super classes must be initialized -beforehand. In ```si.Main```'s main method an instance of ```si.Subclass``` is created. Hence, all -super classes of ```si.Subclass``` must be initialized too. Those are ```si.Superclass``` as direct -super class and ```si.RootClass``` as transitive super class. Since all three classes provide static -initialization routines, calls to all must be included in the call graph. -```java -// si/Main.java -package si; - -import lib.annotations.callgraph.DirectCall; -public class Main { - - public static void main(String[] args) { - new Subclass(); - } -} - -class Subclass extends Superclass { - static String name = init(); - - @DirectCall(name = "callback", line = 16, resolvedTargets = "Lsi/Subclass;") - static String init() { - callback(); - return "Subclass"; - } - - static void callback() {} -} - -class Superclass extends RootClass { - - static { - superInit(); - } - - @DirectCall(name = "callback", line = 31, resolvedTargets = "Lsi/Superclass;") - static void superInit(){ - callback(); - } - - static void callback() {} -} - -class RootClass { - - static { - rootInit(); - } - - @DirectCall(name = "callback", line = 45, resolvedTargets = "Lsi/RootClass;") - static void rootInit(){ - callback(); - } - - static void callback() {} -} -``` -[//]: # (END) \ No newline at end of file diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/Types.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/Types.markdown deleted file mode 100644 index a2ccf7d740..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/Types.markdown +++ /dev/null @@ -1,232 +0,0 @@ -#TypeCasts -Using local information to get better type information. Even if those APIs and instructions do have -no visible effect on the soundness they still must be supported by the frameworks. -##TC1 -[//]: # (MAIN: simplecast.Demo) -This case shows type narrowing due to previous cast. The method ```simplecast.Demo.castToTarget``` takes an -object, casts it to ```simplecast.Target```, and then calls ```target``` on the casted object which -rules out ```simplecast.Demo.target``` as receiver. -```java -// simplecast/Demo.java -package simplecast; - -import lib.annotations.callgraph.DirectCall; -class Demo { - public static void main(String[] args) throws Exception { - if (args.length == 0) - castToTarget(new Target()); - else - castToTarget(new Demo()); - } - - @DirectCall( - name = "target", returnType = String.class, line = 18, - resolvedTargets = "Lsimplecast/Target;" - ) - static void castToTarget(Object o) { - Target b = (Target) o; - b.target(); - } - - public String target() { return "Demo"; } -} -class Target { - public String target() { return "Target"; } -} -``` -[//]: # (END) - -##TC2 -[//]: # (MAIN: castclassapi.Demo) -Type narrowing due to previous cast using Java's class API. The method ```castclassapi.Demo.castToTarget``` -takes a class object that is parameterized over the type the cast should be performed to and an object -that will be casted within the method. It then casts it via ```Class.cast``` to ```castclassapi.Target``` -and then calls ```toString``` on the casted object which rules out ```castclassapi.Demo.toString``` as receiver. -```java -// castclassapi/Demo.java -package castclassapi; - -import lib.annotations.callgraph.DirectCall; -class Demo { - public static void main(String[] args) throws Exception { - if (args.length == 0) - castToTarget(Target.class, new Target()); - else - castToTarget(Demo.class, new Demo()); - } - - @DirectCall( - name = "toString", returnType = String.class, line = 18, - resolvedTargets = "Lcastclassapi/Target;" - ) - static void castToTarget(Class cls, Object o) { - T target = cls.cast(o); - target.toString(); - } - - public String toString() { return "Demo"; } -} -``` -```java -// castclassapi/Target.java -package castclassapi; - -public class Target { - - public String toString(){ - return "Target"; - } -} -``` -[//]: # (END) - -##TC3 -[//]: # (MAIN: classeq.Demo) -Type narrowing due to a equality check of two ```java.lang.Class``` objects. Within the ```this``` -branch of ```classeq.Demo.callIfInstanceOfTarget``` it is thus known that the passed object ```o``` -must be of type ```Target```. Hence, ```o.toString``` must only be resolved to ```Target.toString```. -```java -// classeq/Demo.java -package classeq; - -import lib.annotations.callgraph.DirectCall; -class Demo{ - public static void main(String[] args) throws Exception { - if (args.length == 0) - callIfInstanceOfTarget(new Target()); - else - callIfInstanceOfTarget(new Demo()); - } - - @DirectCall( - name = "toString", returnType = String.class, line = 18, - resolvedTargets = "Lclasseq/Target;" - ) - static void callIfInstanceOfTarget(Object o) { - if (o.getClass() == Target.class) - o.toString(); - } - - public String toString() { return "Demo"; } -} -class Target { - public String toString() { return "Target"; } -} - -``` -[//]: # (END) - - -##TC4 -[//]: # (MAIN: instanceofcheck.Demo) -Type narrowing due to Java's built-in ```instanceof``` check of the given object ```o``` and the -```Target``` class. Within the ```this``` branch of ```Demo.callIfInstanceOfTarget``` it is thus -known that the passed object ```o``` must be of type ```Target```. Hence, ```o.toString``` must only -be resolved to ```Target.toString```. -```java -// instanceofcheck/Demo.java -package instanceofcheck; - -import lib.annotations.callgraph.DirectCall; -class Demo{ - public static void main(String[] args) throws Exception { - if (args.length == 0) - callIfInstanceOfTarget(new Target()); - else - callIfInstanceOfTarget(new Demo()); - } - - @DirectCall( - name = "toString", returnType = String.class, line = 18, - resolvedTargets = "Linstanceofcheck/Target;" - ) - static void callIfInstanceOfTarget(Object o) { - if (o instanceof Target) - o.toString(); - } - - public String toString() { return "Demo"; } -} -class Target { - public String toString() { return "Target"; } -} - -``` -[//]: # (END) - -##TC5 -[//]: # (MAIN: instanceofclassapi.Demo) -Type narrowing due to Java's ```java.lang.Class.isInstance``` API call that checks whether a given -object (i.e. ```o```) is of the same type the class instance is parameterized over, i.e., -```Target.class``` is a shorthand notation for ```java.lang.Class```. Within the ```this``` -branch of ```Demo.callIfInstanceOfTarget``` it is thus known that the passed object ```o``` must be -of type ```Target```. Hence, ```o.toString``` must only be resolved to ```Target.toString```. -```java -// instanceofclassapi/Demo.java -package instanceofclassapi; - -import lib.annotations.callgraph.DirectCall; -class Demo{ - public static void main(String[] args) throws Exception { - if (args.length == 0) - callIfInstanceOfTarget(new Target()); - else - callIfInstanceOfTarget(new Demo()); - } - - @DirectCall( - name = "toString", returnType = String.class, line = 18, - resolvedTargets = "Linstanceofclassapi/Target;" - ) - static void callIfInstanceOfTarget(Object o) { - if (Target.class.isInstance(o)) - o.toString(); - } - - public String toString() { return "Demo"; } -} -class Target { - public String toString() { return "Target"; } -} - -``` -[//]: # (END) - - -##TC6 -[//]: # (MAIN: tc.Demo) -Type narrowing due to Java's ```java.lang.Class.isAssignableFrom``` API call that checks whether a given -object (i.e. ```o```) can be assign to variable of the type the class instance is parameterized over, i.e., -```Target.class``` is a shorthand notation for ```java.lang.Class```. Within the ```this``` -branch of ```Demo.callIfInstanceOfTarget``` it is thus known that the passed object ```o``` must be -a subtype of ```Target```. Hence, ```o.toString``` must only be resolved to ```Target.toString```. -```java -// tc/Demo.java -package tc; - -import lib.annotations.callgraph.DirectCall; -class Demo{ - public static void main(String[] args) throws Exception { - if (args.length == 0) - callIfInstanceOfTarget(new Target()); - else - callIfInstanceOfTarget(new Demo()); - } - - @DirectCall( - name = "toString", returnType = String.class, line = 18, - resolvedTargets = "Ltc/Target;" - ) - static void callIfInstanceOfTarget(Object o) { - if (Target.class.isAssignableFrom(o.getClass())) - o.toString(); - } - - public String toString() { return "Demo"; } -} -class Target { - public String toString() { return "Target"; } -} - -``` -[//]: # (END) \ No newline at end of file diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/Unsafe.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/Unsafe.markdown deleted file mode 100644 index 1db4ad8a61..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/Unsafe.markdown +++ /dev/null @@ -1,367 +0,0 @@ -#Usage of `sun.misc.Unsafe` -Test cases related to the usage of `sun.misc.Unsafe`. This API provided methods to manipulate -the content of fields. - -##Unsafe1 -[//]: # (MAIN: unsafe.Demo) -Using ```Unsafe.compareAndSwapObject``` to set the value in a private field. - -```java -// unsafe/Demo.java -package unsafe; - -import sun.misc.Unsafe; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import lib.annotations.callgraph.DirectCall; - -public class Demo { - private TargetInterface objectVar = null; - - @DirectCall(name = "targetMethod", resolvedTargets = "Lunsafe/UnsafeTarget;", returnType = String.class, line = 22) - public static void main(String[] args) throws Exception { - Constructor unsafeConstructor = Unsafe.class.getDeclaredConstructor(); - unsafeConstructor.setAccessible(true); - Unsafe unsafe = unsafeConstructor.newInstance(); - - Demo o = new Demo(); - Field objectField = Demo.class.getDeclaredField("objectVar"); - long objectOffset = unsafe.objectFieldOffset(objectField); - - unsafe.compareAndSwapObject(o, objectOffset, null, new UnsafeTarget()); - o.objectVar.targetMethod(); - } -} - -interface TargetInterface { - String targetMethod(); -} - -class UnsafeTarget implements TargetInterface{ - public String targetMethod() { - return "UnsafeTarget"; - } -} - -class SafeTarget implements TargetInterface { - public String targetMethod() { - return "SafeTarget"; - } -} -``` -[//]: # (END) - -##Unsafe2 -[//]: # (MAIN: unsafe.Demo) -Using ```Unsafe.putObject``` to set the value in a private field. - -```java -// unsafe/Demo.java -package unsafe; - -import sun.misc.Unsafe; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import lib.annotations.callgraph.DirectCall; - -public class Demo { - private TargetInterface objectVar = null; - - @DirectCall(name = "targetMethod", resolvedTargets = "Lunsafe/UnsafeTarget;", returnType = String.class, line = 22) - public static void main(String[] args) throws Exception { - Constructor unsafeConstructor = Unsafe.class.getDeclaredConstructor(); - unsafeConstructor.setAccessible(true); - Unsafe unsafe = unsafeConstructor.newInstance(); - - Demo o = new Demo(); - Field objectField = Demo.class.getDeclaredField("objectVar"); - long objectOffset = unsafe.objectFieldOffset(objectField); - - unsafe.putObject(o, objectOffset, new UnsafeTarget()); - o.objectVar.targetMethod(); - } -} - -interface TargetInterface { - String targetMethod(); -} - -class UnsafeTarget implements TargetInterface{ - public String targetMethod() { - return "UnsafeTarget"; - } -} - -class SafeTarget implements TargetInterface { - public String targetMethod() { - return "SafeTarget"; - } -} -``` -[//]: # (END) - -##Unsafe3 -[//]: # (MAIN: unsafe.Demo) -Using ```Unsafe.getObject``` to retrieve a value of a private field. - -```java -// unsafe/Demo.java -package unsafe; - -import sun.misc.Unsafe; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import lib.annotations.callgraph.DirectCall; - -public class Demo { - private Object objectVar = null; - - @DirectCall(name = "targetMethod", resolvedTargets = "Lunsafe/UnsafeTarget;", returnType = String.class, line = 23) - public static void main(String[] args) throws Exception { - Constructor unsafeConstructor = Unsafe.class.getDeclaredConstructor(); - unsafeConstructor.setAccessible(true); - Unsafe unsafe = unsafeConstructor.newInstance(); - - Demo o = new Demo(); - Field objectField = Demo.class.getDeclaredField("objectVar"); - long objectOffset = unsafe.objectFieldOffset(objectField); - - o.objectVar = new UnsafeTarget(); - TargetInterface f = (TargetInterface) unsafe.getObject(o, objectOffset); - f.targetMethod(); - } -} - -interface TargetInterface { - String targetMethod(); -} - -class UnsafeTarget implements TargetInterface{ - public String targetMethod() { - return "UnsafeTarget"; - } -} - -class SafeTarget implements TargetInterface { - public String targetMethod() { - return "SafeTarget"; - } -} -``` -[//]: # (END) - -##Unsafe4 -[//]: # (MAIN: unsafe.Demo) -Here, ```Unsafe.getAndSetObject``` is used to retrieve an object from a field and set a new object in parallel. -Using getObject to retrieve a value of a private field. - -```java -// unsafe/Demo.java -package unsafe; - -import sun.misc.Unsafe; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import lib.annotations.callgraph.DirectCall; -import lib.annotations.callgraph.DirectCalls; - -public class Demo { - private Object objectVar = null; - - @DirectCalls({ - @DirectCall(name = "targetMethod", resolvedTargets = "Lunsafe/UnsafeTarget;", returnType = String.class, line = 29), - @DirectCall(name = "targetMethod", resolvedTargets = "Lunsafe/SafeTarget;", returnType = String.class, line = 30) - }) - public static void main(String[] args) throws Exception { - Constructor unsafeConstructor = Unsafe.class.getDeclaredConstructor(); - unsafeConstructor.setAccessible(true); - Unsafe unsafe = unsafeConstructor.newInstance(); - - Demo demo = new Demo(); - Field objectField = Demo.class.getDeclaredField("objectVar"); - long objectOffset = unsafe.objectFieldOffset(objectField); - - demo.objectVar = new SafeTarget(); - UnsafeTarget unsafeTarget = new UnsafeTarget(); - TargetInterface f = (TargetInterface) unsafe.getAndSetObject(demo, objectOffset, unsafeTarget); - - ((TargetInterface)demo.objectVar).targetMethod(); - f.targetMethod(); - } -} - -interface TargetInterface { - String targetMethod(); -} - -class UnsafeTarget implements TargetInterface{ - public String targetMethod() { - return "UnsafeTarget"; - } -} - -class SafeTarget implements TargetInterface { - public String targetMethod() { - return "SafeTarget"; - } -} -``` -[//]: # (END) - -##Unsafe5 -[//]: # (MAIN: unsafe.Demo) -Here, ```Unsafe.putOrderedObject``` is used to put an object into a field. A call on that field must -be resolved to the newly written object. - -```java -// unsafe/Demo.java -package unsafe; - -import sun.misc.Unsafe; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import lib.annotations.callgraph.DirectCall; - -public class Demo { - private Object objectVar = null; - - @DirectCall(name = "targetMethod", resolvedTargets = "Lunsafe/UnsafeTarget;", returnType = String.class, line = 25) - public static void main(String[] args) throws Exception { - Constructor unsafeConstructor = Unsafe.class.getDeclaredConstructor(); - unsafeConstructor.setAccessible(true); - Unsafe unsafe = unsafeConstructor.newInstance(); - - Demo demo = new Demo(); - Field objectField = Demo.class.getDeclaredField("objectVar"); - long objectOffset = unsafe.objectFieldOffset(objectField); - - demo.objectVar = new SafeTarget(); - UnsafeTarget unsafeTarget = new UnsafeTarget(); - unsafe.putOrderedObject(demo, objectOffset, unsafeTarget); - - ((TargetInterface)demo.objectVar).targetMethod(); - } -} - -interface TargetInterface { - String targetMethod(); -} - -class UnsafeTarget implements TargetInterface{ - public String targetMethod() { - return "UnsafeTarget"; - } -} - -class SafeTarget implements TargetInterface { - public String targetMethod() { - return "SafeTarget"; - } -} -``` -[//]: # (END) - -##Unsafe6 -[//]: # (MAIN: unsafe.Demo) -Here, ```Unsafe.getObjectVolatile``` is used to retrieve an object from a class' field. The retrieved -object is then used to call ```toString``` on it. - -```java -// unsafe/Demo.java -package unsafe; - -import sun.misc.Unsafe; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import lib.annotations.callgraph.DirectCall; - -public class Demo { - private Object objectVar = null; - - @DirectCall(name = "targetMethod", resolvedTargets = "Lunsafe/SafeTarget;", returnType = String.class, line = 24) - public static void main(String[] args) throws Exception { - Constructor unsafeConstructor = Unsafe.class.getDeclaredConstructor(); - unsafeConstructor.setAccessible(true); - Unsafe unsafe = unsafeConstructor.newInstance(); - - Demo demo = new Demo(); - Field objectField = Demo.class.getDeclaredField("objectVar"); - long objectOffset = unsafe.objectFieldOffset(objectField); - - demo.objectVar = new SafeTarget(); - TargetInterface target = (TargetInterface) unsafe.getObjectVolatile(demo, objectOffset); - - target.targetMethod(); - } -} - -interface TargetInterface { - String targetMethod(); -} - -class UnsafeTarget implements TargetInterface{ - public String targetMethod() { - return "UnsafeTarget"; - } -} - -class SafeTarget implements TargetInterface { - public String targetMethod() { - return "SafeTarget"; - } -} -``` -[//]: # (END) - -##Unsafe7 -[//]: # (MAIN: unsafe.Demo) -Here, ```Unsafe.putObjectVolatile``` is used to write an object to a class' field. The written -object is then used to call ```toString``` on it. - -```java -// unsafe/Demo.java -package unsafe; - -import sun.misc.Unsafe; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import lib.annotations.callgraph.DirectCall; - -public class Demo { - private Object objectVar = null; - - @DirectCall(name = "targetMethod", resolvedTargets = "Lunsafe/UnsafeTarget;", returnType = String.class, line = 25) - public static void main(String[] args) throws Exception { - Constructor unsafeConstructor = Unsafe.class.getDeclaredConstructor(); - unsafeConstructor.setAccessible(true); - Unsafe unsafe = unsafeConstructor.newInstance(); - - Demo demo = new Demo(); - Field objectField = Demo.class.getDeclaredField("objectVar"); - long objectOffset = unsafe.objectFieldOffset(objectField); - - demo.objectVar = new SafeTarget(); - UnsafeTarget unsafeTarget = new UnsafeTarget(); - unsafe.putObjectVolatile(demo, objectOffset, unsafeTarget); - - ((TargetInterface)demo.objectVar).targetMethod(); - } -} - -interface TargetInterface { - String targetMethod(); -} - -class UnsafeTarget implements TargetInterface{ - public String targetMethod() { - return "UnsafeTarget"; - } -} - -class SafeTarget implements TargetInterface { - public String targetMethod() { - return "SafeTarget"; - } -} -``` -[//]: # (END) \ No newline at end of file diff --git a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/VirtualCalls.markdown b/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/VirtualCalls.markdown deleted file mode 100644 index 9601e8de83..0000000000 --- a/TOOLS/hermes/src/main/resources/org/opalj/hermes/queries/jcg/VirtualCalls.markdown +++ /dev/null @@ -1,118 +0,0 @@ -#VirtualCalls -Tests related to the resolution of standard virtual methods. -##VC1 -[//]: # (MAIN: vc.Class) -Tests a virtually dispatched method call which is in fact monomorphic. -```java -// vc/Class.java -package vc; - -import lib.annotations.callgraph.DirectCall; - -class Class { - - public void target(){ } - - @DirectCall(name = "target", line = 12, resolvedTargets = "Lvc/Class;") - public static void main(String[] args){ - Class cls = new Class(); - cls.target(); - } -} -``` -[//]: # (END) - -##VC2 -[//]: # (MAIN: vc.Class) -Tests a virtually dispatched method call when a simple type hierarchy is present. -```java -// vc/Class.java -package vc; - -import lib.annotations.callgraph.DirectCall; - -class Class { - - public void method(){ } - - @DirectCall(name = "method", line = 11, resolvedTargets = "Lvc/SubClass;") - public static void callMethod(Class cls) { - cls.method(); - } - - public static void main(String[] args){ - callMethod(new SubClass()); - } -} - -class SubClass extends Class { - - public void method() { } -} -``` -[//]: # (END) - -##VC3 -[//]: # (MAIN: vc.Class) -Tests a virtually dispatched method call when the receiver is an interface type. -```java -// vc/Class.java -package vc; - -import lib.annotations.callgraph.DirectCall; - -interface Interface { - void method(); -} - -class Class { - - public void method(){ } - - @DirectCall(name = "method", line = 15, resolvedTargets = {"Lvc/ClassImpl;"}, prohibitedTargets ={"Lvc/Class;"}) - public static void callOnInterface(Interface i){ - i.method(); - } - - public static void main(String[] args){ - callOnInterface(new ClassImpl()); - } -} - -class ClassImpl implements Interface { - public void method(){ } -} -``` -[//]: # (END) - -##VC4 -[//]: # (MAIN: vc.Class) -Tests a virtually dispatched method call when the receiver is loaded from an array. -```java -// vc/Class.java -package vc; - -import lib.annotations.callgraph.DirectCall; - -interface Interface { - void method(); -} - -class Class implements Interface { - - public static Interface[] types = new Interface[]{new Class(), new ClassImpl()}; - - public void method(){ } - - @DirectCall(name = "method", line = 18, resolvedTargets = "Lvc/Class;") - public static void main(String[] args){ - Interface i = types[0]; - i.method(); - } -} - -class ClassImpl implements Interface { - public void method(){ } -} -``` -[//]: # (END) \ No newline at end of file diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/DuplicateFeatureIDException.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/DuplicateFeatureIDException.scala deleted file mode 100644 index c2c87d14bd..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/DuplicateFeatureIDException.scala +++ /dev/null @@ -1,16 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes - -/** - * @param featureID The id of the feature that is reued. - * @param featureQueryA The id of some feature query that derives features with - * the given name. - * @param featureQueryB The id of another feature query that derives features with - * the given name. - */ -case class DuplicateFeatureIDException( - featureID: String, - featureQueryA: FeatureQuery, - featureQueryB: FeatureQuery -) extends Exception(s"$featureID is derived by ${featureQueryA.id} and by ${featureQueryB.id}") diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/Feature.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/Feature.scala deleted file mode 100644 index f2262f91a1..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/Feature.scala +++ /dev/null @@ -1,50 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes - -/** - * Represents the immutable results of a feature query. - * - * @param id A very short identifier of this feature. E.g., `Java8ClassFile` or - * `ProtectedMethod` or `DeadMethod`. The name must not contain spaces or other - * special characters. - * @param count How often the feature was found in a project. - * @param extensions Places where the feature was found. This information is - * primarily useful when exploring the project and is optional. - * I.e., `extensions.size` can be smaller than `count`. The maximum number - * of stored locations is set using the global setting: `org.opalj.hermes.maxLocations`. - * - * @author Michael Eichberg - */ -abstract case class Feature[S] private ( - id: String, - count: Int, - extensions: List[Location[S]] -) { - assert(count >= extensions.size) -} - -/** - * Factory to create features. - * - * @author Michael Eichberg - */ -object Feature { - - def apply[S]( - id: String, - count: Int = 0, - extensions: List[Location[S]] = List.empty[Location[S]] - )( - implicit hermes: HermesConfig - ): Feature[S] = { - new Feature(id, count, extensions.take(hermes.MaxLocations)) {} - } - - def apply[S]( - id: String, - locations: LocationsContainer[S] - ): Feature[S] = { - new Feature(id, locations.size, locations.locations) {} - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/FeatureQuery.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/FeatureQuery.scala deleted file mode 100644 index 0c50d8c70a..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/FeatureQuery.scala +++ /dev/null @@ -1,170 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes - -import java.net.URL -import scala.io.Codec -import scala.io.Source - -import org.opalj.br.analyses.Project -import org.opalj.io.processSource - -import com.github.rjeschke.txtmark.Processor -import javafx.beans.property.LongProperty -import javafx.beans.property.ObjectProperty -import javafx.beans.property.SimpleLongProperty -import javafx.beans.property.SimpleObjectProperty - -/** - * Extracts a feature/a set of closely related features of a given project. - * - * @author Michael Eichberg - */ -abstract class FeatureQuery(implicit hermes: HermesConfig) { - - /** - * Queries should regularly check if they are interrupted using this method. - */ - final def isInterrupted(): Boolean = Thread.currentThread().isInterrupted() - - // ================================ ABSTRACT FUNCTIONALITY ================================ - - /** - * The unique ids of the extracted features. - */ - def featureIDs: Seq[String] - - /** - * The function which analyzes the project and extracts the feature information. - * - * @param project A representation of the project. To speed up queries, intermediate - * information that may also be required by other queries can/should be stored in the - * project using the [[org.opalj.fpcf.PropertyStore]] or using a - * [[org.opalj.br.analyses.ProjectInformationKey]]. - * @param rawClassFiles A direct 1:1 representation of the class files. This makes it possible - * to write queries that need to get an understanding of an unprocessed class file; e.g. - * that need to analyze the constant pool in detail. - * @note '''Every query should regularly check that its thread is not interrupted - * using `isInterrupted`'''. - */ - def apply[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(da.ClassFile, S)] - ): IterableOnce[Feature[S]] - - // =================================== DEFAULT FUNCTIONALITY =================================== - // ==================================== (can be overridden) ==================================== - - /** - * A short descriptive name; by default the simple name of `this` class. - */ - val id: String = { - val simpleClassName = this.getClass.getSimpleName - val dollarPosition = simpleClassName.indexOf('$') - if (dollarPosition == -1) - simpleClassName - else - simpleClassName.substring(0, dollarPosition) - } - - /** - * Returns an explanation of the feature (group) using Markdown as its formatting language. - * - * By default the name of the class is used to lookup the resource "className.markdown" - * which is expected to be found along the extractor. - */ - protected def mdDescription: String = { - var descriptionResourceURL = this.getClass.getResource(s"$id.markdown") - if (descriptionResourceURL == null) - descriptionResourceURL = this.getClass.getResource(s"$id.md") - try { - processSource(Source.fromURL(descriptionResourceURL)(using Codec.UTF8)) { _.mkString } - } catch { - case t: Throwable => s"not available: $descriptionResourceURL; ${t.getMessage}" - } - } - - /** - * Returns an HTML description of this feature query that is targeted at end users; by default - * it calls `mdDescription` to try to find a markdown document that describes this feature and - * then uses TxtMark to convert the document. If a document is returned the web engine's - * user style sheet is set to [[org.opalj.hermes.FeatureQueries.MDCSS]]; in case of an URL no - * stylesheet is set. - * - * @return An HTML document/a link to an HTML document that describes this query. - */ - val htmlDescription: Either[String, URL] = Left(Processor.process(mdDescription)) - - // =============================== HERMES INTERNAL FUNCTIONALITY =============================== - - /** - * The time it took to evaluate the query across all projects in nanoseconds. - */ - private[hermes] val accumulatedAnalysisTime: LongProperty = new SimpleLongProperty() - - private[hermes] def createInitialFeatures[S]: Seq[ObjectProperty[Feature[S]]] = { - featureIDs.map(fid => new SimpleObjectProperty(Feature[S](fid))) - } - -} - -abstract class DefaultFeatureQuery( - implicit hermes: HermesConfig -) extends FeatureQuery { - - def evaluate[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(da.ClassFile, S)] - ): IndexedSeq[LocationsContainer[S]] - - final def apply[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(da.ClassFile, S)] - ): IterableOnce[Feature[S]] = { - val locations = evaluate(projectConfiguration, project, rawClassFiles) - for { (featureID, featureIDIndex) <- featureIDs.iterator.zipWithIndex } yield { - Feature[S](featureID, locations(featureIDIndex)) - } - } -} - -abstract class DefaultGroupedFeaturesQuery( - implicit hermes: HermesConfig -) extends DefaultFeatureQuery { - - def groupedFeatureIDs: Seq[Seq[String]] - - def evaluateFeatureGroups[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(da.ClassFile, S)] - ): IndexedSeq[IterableOnce[LocationsContainer[S]]] - - final def featureIDs: Seq[String] = groupedFeatureIDs.flatten - - override final def evaluate[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(da.ClassFile, S)] - ): IndexedSeq[LocationsContainer[S]] = { - evaluateFeatureGroups(projectConfiguration, project, rawClassFiles).flatten - } - -} - -/** - * Common constants related to feature queries. - * - * @author Michael Eichberg - */ -object FeatureQueries { - - /** - * The URL of the CSS file which used to style the HTML document. - */ - final val MDCSS: URL = this.getClass.getResource("Queries.css") - -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/HermesCLI.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/HermesCLI.scala deleted file mode 100644 index 8abd8e14c1..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/HermesCLI.scala +++ /dev/null @@ -1,106 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes - -import scala.reflect.io.Directory - -import java.io.File -import java.util.concurrent.CountDownLatch -import scala.io.Source - -import org.opalj.io.processSource - -/** - * Executes all analyses to determine the representativeness of the given projects - * ([[https://github.com/opalj/opal/blob/develop/TOOLS/hermes/src/main/resources/org/opalj/hermes/HermesCLI.txt see HermesCLI.txt for further details]]). - * - * @author Michael Eichberg - */ -object HermesCLI { - - object Hermes extends HermesCore { - override def updateProjectData(f: => Unit): Unit = Hermes.synchronized { f } - override def reportProgress(f: => Double): Unit = Hermes.synchronized { f } - } - - final val usage = { - val hermesCLIInputStream = this.getClass.getResourceAsStream("HermesCLI.txt") - processSource(Source.fromInputStream(hermesCLIInputStream)) { s => s.getLines().mkString("\n") } - } - - private def showUsage(): Unit = println(usage) - - def main(args: Array[String]): Unit = { - var configFile: String = null - var statisticsFile: String = null - var mappingFile: Option[String] = None - var noProjectStatistics: Boolean = false - var locationsDir: String = null - - var i = 0 - while (i < args.length) { - args(i) match { - case "-config" => - i += 1 - configFile = args(i) - - case "-statistics" => - i += 1 - statisticsFile = args(i) - - case "-mapping" => - i += 1 - mappingFile = Some(args(i)) - - case "-noProjectStatistics" => - i += 1 - noProjectStatistics = true - - case "-writeLocations" => - i += 1 - locationsDir = args(i) - - case arg => - Console.err.println(s"Unknown parameter $arg.") - showUsage() - System.exit(2) - } - i += 1 - } - if (configFile == null || statisticsFile == null) { - Console.err.println("Missing config file and/or statistics file.") - showUsage() - System.exit(1) - } - - val waitOnFinished = new CountDownLatch(1) - Hermes.analysesFinished.addListener { (_, _, isFinished) => - if (isFinished) { - val theStatisticsFile = new File(statisticsFile).getAbsoluteFile() - Hermes.exportStatistics(theStatisticsFile, !noProjectStatistics) - println("Wrote statistics: " + theStatisticsFile) - - if (locationsDir ne null) { - val theLocationsDir = Directory(new File(locationsDir)) - Hermes.exportLocations(theLocationsDir) - println("Wrote locations: " + theLocationsDir) - } else { - println("Skip writing locations, not specified.") - } - - mappingFile.foreach { mappingFile => - val theMappingFile = new File(mappingFile).getAbsoluteFile() - Hermes.exportMapping(theMappingFile) - println("Wrote mapping: " + theMappingFile) - } - - waitOnFinished.countDown() - } - } - Hermes.initialize(new File(configFile)) - Hermes.analyzeCorpus(runAsDaemons = true) - waitOnFinished.await() // we will not return until we have finished the analysis - println("Hermes finished.") - } - -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/HermesConfig.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/HermesConfig.scala deleted file mode 100644 index 1abbe3bb8d..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/HermesConfig.scala +++ /dev/null @@ -1,98 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes - -import java.io.File - -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory -import com.typesafe.config.ConfigRenderOptions - -/** - * Global configuration settings initialized when the application configuration file is - * read. - * - * @author Michael Eichberg - */ -trait HermesConfig { - - // --------------------------------------------------------------------------------------------- - // - // INITIALIZATION - // - // --------------------------------------------------------------------------------------------- - - private var isInitialized: Boolean = false - - private var config: Config = null - - /** - * The global configuration file. - */ - final lazy val Config: Config = validateInitialized { config } - - /** - * Reads the initial, overall configuration. This method or setConfig has to be called - * before Hermes can be used. - */ - def initialize(configFile: File): Unit = { - import Console.err - if (!configFile.exists || !configFile.canRead()) { - err.println(s"The config file cannot be found or read: $configFile") - err.println("The current folder is: " + System.getProperty("user.dir")) - System.exit(2) - } - try { - val baseConfig = ConfigFactory.load("hermes.conf") - val config = ConfigFactory.parseFile(configFile).withFallback(baseConfig) - setConfig(config) - } catch { - case t: Throwable => - err.println(s"Failed while reading: $configFile; ${t.getMessage()}") - System.exit(3) - // ... if System.exit does not terminate the app; this will at least kill the - // the current call. - throw t; - } - } - - /** - * Sets the used configuration object. - */ - def setConfig(config: Config): Unit = { - if (isInitialized) { - throw new IllegalStateException("configuration is already set"); - } - - this.config = config - isInitialized = true - } - - private def validateInitialized[@specialized(Int, Boolean, Long, Double, Float) T]( - f: => T - ): T = { - if (!isInitialized) - throw new IllegalStateException("configuration is not yet set") - else - f - } - - /** Textual representation of the configuration related to OPAL/Hermes. */ - def renderConfig: String = { - val rendererConfig = ConfigRenderOptions.defaults().setOriginComments(false) - config.getObject("org.opalj").render(rendererConfig) - } - - // --------------------------------------------------------------------------------------------- - // - // ACCESSORS - // - // --------------------------------------------------------------------------------------------- - - /** The config key of the number of locations per feature pre project that is stored. */ - final val MaxLocationsKey: String = "org.opalj.hermes.maxLocations" - - /** The number of locations per feature pre project that is stored. */ - final lazy val MaxLocations: Int = validateInitialized { Config.getInt(MaxLocationsKey) } - -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/HermesCore.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/HermesCore.scala deleted file mode 100644 index db654679a6..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/HermesCore.scala +++ /dev/null @@ -1,399 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes - -import scala.reflect.io.Directory - -import java.io.BufferedWriter -import java.io.File -import java.io.FileWriter -import java.net.URL -import java.util.concurrent.atomic.AtomicInteger -import scala.jdk.CollectionConverters.* - -import org.opalj.br.analyses.Project - -import scala.collection.parallel.CollectionConverters.ImmutableIterableIsParallelizable - -import com.fasterxml.jackson.dataformat.csv.CsvFactory -import com.fasterxml.jackson.dataformat.csv.CsvSchema -import javafx.beans.property.BooleanProperty -import javafx.beans.property.IntegerProperty -import javafx.beans.property.LongProperty -import javafx.beans.property.SimpleBooleanProperty -import javafx.beans.property.SimpleIntegerProperty -import javafx.beans.property.SimpleLongProperty -import javafx.collections.FXCollections -import javafx.collections.ObservableList -import pureconfig._ - -/** - * Implements the core functionality to evaluate a set of feature queries against a set of - * projects; does not provide any UI. The GUI is implemented by the class [[Hermes]] and the - * command-line interface is implemented by the class [[HermesCLI]]. - * - * @author Michael Eichberg - */ -trait HermesCore extends HermesConfig { - - // --------------------------------------------------------------------------------------------- - // - // - // STATIC CONFIGURATION - // - // - // --------------------------------------------------------------------------------------------- - - /** The list of all registered feature queries. */ - lazy val registeredQueries: List[Query] = { - ConfigSource.fromConfig(Config).at("org.opalj.hermes.queries.registered").loadOrThrow[List[Query]] - } - - /** The list of enabled feature queries. */ - lazy val featureQueries: List[FeatureQuery] = { - registeredQueries.flatMap(q => if (q.isEnabled) q.reify(using this) else None) - } - - /** - * The list of unique features derived by enabled feature queries; one ''feature query'' may - * be referenced by multiple unique feature queries. - */ - lazy val featureIDs: List[(String, FeatureQuery)] = { - var featureIDs: List[(String, FeatureQuery)] = List.empty - - for { - featureQuery <- featureQueries - featureID <- featureQuery.featureIDs - } { - if (!featureIDs.exists(_._1 == featureID)) - featureIDs :+= ((featureID, featureQuery)) - else - throw DuplicateFeatureIDException( - featureID, - featureQuery, - featureIDs.collectFirst { case (`featureID`, fq) => fq }.get - ) - } - - featureIDs - } - - /** The set of all project configurations. */ - lazy val projectConfigurations: List[ProjectConfiguration] = { - val pcs = - ConfigSource.fromConfig(Config).at("org.opalj.hermes.projects").loadOrThrow[List[ProjectConfiguration]] - if (pcs.map(_.id).toSet.size != pcs.size) { - throw new RuntimeException("some project names are not unique") - } - pcs - } - - // --------------------------------------------------------------------------------------------- - // - // - // FIELDS FOR STORING QUERY(ING RELATED) RESULTS - // - // - // --------------------------------------------------------------------------------------------- - - /** The matrix containing for each project the extensions of all features. */ - lazy val featureMatrix: ObservableList[ProjectFeatures[URL]] = { - val featureMatrix = FXCollections.observableArrayList[ProjectFeatures[URL]] - for { projectConfiguration <- projectConfigurations } { - val features = featureQueries map { fe => (fe, fe.createInitialFeatures[URL]) } - featureMatrix.add(ProjectFeatures(projectConfiguration, features)) - } - featureMatrix - } - - /** Summary of the number of occurrences of a feature across all projects. */ - lazy val perFeatureCounts: Array[IntegerProperty] = { - val perFeatureCounts = - Array.fill[IntegerProperty](featureIDs.size)(new SimpleIntegerProperty(0)) - featureMatrix.forEach { projectFeatures => - projectFeatures.features.view.zipWithIndex foreach { fi => - val (feature, index) = fi - feature.addListener { (_, oldValue, newValue) => - val change = newValue.count - oldValue.count - if (change != 0) { - perFeatureCounts(index).setValue(perFeatureCounts(index).getValue + change) - } - } - } - } - perFeatureCounts - } - - val analysesFinished: BooleanProperty = new SimpleBooleanProperty(false) - - // some statistics - val corpusAnalysisTime: LongProperty = new SimpleLongProperty - - // --------------------------------------------------------------------------------------------- - // - // - // CORE FUNCTIONALITY - // - // - // --------------------------------------------------------------------------------------------- - - /** - * Executes the queries for all projects. Basically, the queries are executed in parallel - * for each project. - * - * @note This method is only intended to be called once! - */ - def analyzeCorpus(runAsDaemons: Boolean): Thread = { - - def isValid( - projectFeatures: ProjectFeatures[URL], - project: Project[URL], - projectAnalysisStartTime: Long - ): Boolean = { - if (project.projectClassFilesCount == 0) { - updateProjectData { projectFeatures.id.setValue("! " + projectFeatures.id.getValue) } - false - } else { - true - } - } - - val analysesStartTime = System.nanoTime() - val t = new Thread { - override def run(): Unit = { - val totalSteps = (featureQueries.size * projectConfigurations.size).toDouble - val stepsDone = new AtomicInteger(0) - for { - // Using an iterator is required to avoid eager initialization of all projects! - projectFeatures <- featureMatrix.iterator.asScala - if !Thread.currentThread.isInterrupted - projectConfiguration = projectFeatures.projectConfiguration - projectAnalysisStartTime = System.nanoTime() - projectInstantiation = projectConfiguration.instantiate - project = projectInstantiation.project - rawClassFiles = projectInstantiation.rawClassFiles - if isValid(projectFeatures, project, projectAnalysisStartTime) - (featureQuery, features) <- projectFeatures.featureGroups.par - featuresMap = features.map(f => (f.getValue.id, f)).toMap - if !Thread.currentThread.isInterrupted - } { - val featureAnalysisStartTime = System.nanoTime() - val features = featureQuery(projectConfiguration, project, rawClassFiles) - val featureAnalysisEndTime = System.nanoTime() - val featureAnalysisTime = featureAnalysisEndTime - featureAnalysisStartTime - - reportProgress { - featureQuery.accumulatedAnalysisTime.setValue( - featureQuery.accumulatedAnalysisTime.getValue + featureAnalysisTime - ) - corpusAnalysisTime.setValue(featureAnalysisEndTime - analysesStartTime) - // (implicitly) update the feature matrix - features.iterator.foreach { f => featuresMap(f.id).setValue(f) } - - stepsDone.incrementAndGet() / totalSteps - } - } - - // we are done with everything - reportProgress { - val analysesEndTime = System.nanoTime() - corpusAnalysisTime.setValue(analysesEndTime - analysesStartTime) - - analysesFinished.setValue(true) - 1.0d // <=> we are done - } - } - } - t.setDaemon(runAsDaemons) - t.start() - t - } - - /** - * Note that update project data is executed concurrently, but `f` must not be called - * concurrently and may need to be scheduled as part of the UI thread if the affected - * data is visualized. - */ - def updateProjectData(f: => Unit): Unit - - /** - * Called to report the progress. If the double value is 1.0 the analyses has finished. - * Note that report progress is executed concurrently, but `f` must not be called - * concurrently and may need to be scheduled as part of the UI thread if the - * progress is visualized. - */ - // Needs to be implemented by subclasses. - def reportProgress(f: => Double): Unit - - // --------------------------------------------------------------------------------------------- - // - // - // CONVENIENCE FUNCTIONALITY - // - // - // --------------------------------------------------------------------------------------------- - - def exportStatistics(file: File, exportProjectStatistics: Boolean = true): Unit = { - io.process(new BufferedWriter(new FileWriter(file))) { writer => - exportStatistics(writer, exportProjectStatistics) - } - } - - def exportStatistics(writer: BufferedWriter, exportProjectStatistics: Boolean): Unit = { - // Create the set of all names of all project-wide statistics - var projectStatisticsIDs = Set.empty[String] - featureMatrix.forEach { pf => projectStatisticsIDs ++= pf.projectConfiguration.statistics.keySet } - - // Logic to create the csv file: - val csvSchemaBuilder = CsvSchema.builder().addColumn("Project") - if (exportProjectStatistics) { - projectStatisticsIDs.foreach { id => csvSchemaBuilder.addColumn(id) } - } - val csvSchema = - featureIDs.foldLeft(csvSchemaBuilder) { (schema, feature) => - schema.addColumn(feature._1, CsvSchema.ColumnType.NUMBER) - }.setUseHeader(true).build() - - val csvGenerator = new CsvFactory().createGenerator(writer) - csvGenerator.setSchema(csvSchema) - featureMatrix.forEach { pf => - csvGenerator.writeStartArray() - csvGenerator.writeString(pf.id.getValue) - if (exportProjectStatistics) { - projectStatisticsIDs.foreach { id => - pf.projectConfiguration.statistics.get(id) match { - case Some(number) => csvGenerator.writeNumber(number) - case None => csvGenerator.writeString("N/A") - } - } - } - pf.features.foreach { f => csvGenerator.writeNumber(f.getValue.count) } - csvGenerator.flush() - csvGenerator.writeEndArray() - } - csvGenerator.flush() - } - - /** - * Exports the mapping between a feature query class and its feature queries. - * - * For the feature ids the following substitution scheme is used: - * - \\ is replaced by \\\\ - * - new line ('\\n') is replaced by \\n - * - , is replaced by \\, - * - * @param file The file to which the mapping will be written. - */ - def exportMapping(file: File): Unit = { - io.process(new BufferedWriter(new FileWriter(file))) { exportMapping } - } - - def exportMapping(writer: BufferedWriter): Unit = { - registeredQueries.iterator.filter(_.isEnabled) foreach { q => - val fq = q.reify(using this).get - writer.write(q.query) - writer.write("=") - writer.write( - fq.featureIDs.map { fid => fid.replace("\\", "\\\\").replace("\n", "\\n").replace(",", "\\,") } - .mkString(",") - ) - writer.newLine() - } - writer.flush() - } - - def exportLocations(dir: Directory): Unit = { - projectConfigurations.iterator foreach { pc => - featureMatrix.forEach { pf => - val projectFile = new File(s"${dir.path}/${pf.id.getValue}.tsv") - io.process(new BufferedWriter(new FileWriter(projectFile))) { writer => exportLocations(writer, pf) } - } - } - } - - def exportLocations(writer: BufferedWriter, pf: ProjectFeatures[URL]): Unit = { - // Logic to create the csv file: - val csvSchema = CsvSchema.builder() - .addColumn("PID") - .addColumn("FID") - .addColumn("Source") - .addColumn("Package") - .addColumn("FQN") - .addColumn("MethodName") - .addColumn("MethodDescriptor") - .addColumn("PC", CsvSchema.ColumnType.NUMBER) - .addColumn("Field") - .setUseHeader(true) - .setColumnSeparator('\t') - .build() - - val csvGenerator = new CsvFactory().createGenerator(writer) - csvGenerator.setSchema(csvSchema) - - def writeEntry[S]( - source: Option[S], - pn: String, - cls: String = "", - methodName: String = "", - methodDescriptor: String = "", - inst: String = "", - field: String = "" - ): Unit = { - csvGenerator.writeString(source.map(_.toString).getOrElse("")) - csvGenerator.writeString(pn) - csvGenerator.writeString(cls) - csvGenerator.writeString(methodName) - csvGenerator.writeString(methodDescriptor) - csvGenerator.writeString(inst) - csvGenerator.writeString(field) - } - - val projectId = pf.id.getValue - pf.features.foreach { f => - val feature = f.getValue - val fid = feature.id - feature.extensions.foreach { l => - csvGenerator.writeStartArray() - csvGenerator.writeString(projectId) - csvGenerator.writeString(fid) - l match { - case PackageLocation(source, packageName) => - writeEntry(source, packageName) - case ClassFileLocation(source, classFileFQN) => - writeEntry(source, "", s"L${classFileFQN.replace(".", "/")};") - case ml @ MethodLocation(cfl, _, _) => - val jvmTypeName = s"L${ml.classFileFQN.replace(".", "/")};" - writeEntry( - cfl.source, - "", - jvmTypeName, - ml.methodName, - ml.methodDescriptor.toJVMDescriptor - ) - case InstructionLocation(ml, pc) => - val jvmTypeName = s"L${ml.classFileFQN.replace(".", "/")};" - writeEntry( - ml.source, - "", - jvmTypeName, - ml.methodName, - ml.methodDescriptor.toJVMDescriptor, - pc.toString - ) - case FieldLocation(cfl, fieldName, fieldType) => - val fieldEntry = s"$fieldName : ${fieldType.toJava}" - val jvmTypeName = s"L${cfl.classFileFQN.replace(".", "/")};" - writeEntry( - cfl.source, - "", - jvmTypeName, - field = fieldEntry - ) - } - csvGenerator.flush() - csvGenerator.writeEndArray() - } - } - csvGenerator.flush() - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/Location.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/Location.scala deleted file mode 100644 index e8b7b8018c..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/Location.scala +++ /dev/null @@ -1,188 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes - -import org.opalj.br.ClassFile -import org.opalj.br.ClassType -import org.opalj.br.Field -import org.opalj.br.FieldType -import org.opalj.br.Method -import org.opalj.br.MethodDescriptor -import org.opalj.br.analyses.MethodInfo -import org.opalj.br.analyses.Project - -/** - * The location where a specific feature was found. In general, a feature query should always use - * a [[LocationsContainer]] to manage the identified locations. - * - * @tparam S The kind of the source. E.g., `java.net.URL`. - * - * @author Michael Eichberg - */ -sealed abstract class Location[S] { - - /** - * The source location. - */ - def source: Option[S] - -} - -final case class PackageLocation[S]( - override val source: Option[S], - packageName: String -) extends Location[S] { - - override def toString: String = { - source match { - case Some(source) => s"$packageName\n$source" - case None => packageName - } - } -} - -object PackageLocation { - - def apply[S](source: S, packageName: String): PackageLocation[S] = { - new PackageLocation[S](Some(source), packageName) - } - - def apply[S](packageName: String): PackageLocation[S] = { - new PackageLocation[S](None, packageName) - } -} - -final case class ClassFileLocation[S]( - override val source: Option[S], - classFileFQN: String -) extends Location[S] { - - override def toString: String = { - source match { - case Some(source) => s"$classFileFQN\n$source" - case None => classFileFQN - } - } - -} - -object ClassFileLocation { - - def apply[S](classFile: ClassFile): ClassFileLocation[S] = { - new ClassFileLocation[S](None, classFile.thisType.toJava) - } - - def apply[S](classType: ClassType): ClassFileLocation[S] = { - new ClassFileLocation[S](None, classType.toJava) - } - - def apply[S](source: S, classFile: ClassFile): ClassFileLocation[S] = { - new ClassFileLocation[S](Some(source), classFile.thisType.toJava) - } - - def apply[S](project: Project[S], classType: ClassType): ClassFileLocation[S] = { - new ClassFileLocation[S](project.source(classType), classType.toJava) - } - - final def apply[S](project: Project[S], classFile: ClassFile): ClassFileLocation[S] = { - apply(project, classFile.thisType) - } - -} - -final case class FieldLocation[S]( - classFileLocation: ClassFileLocation[S], - fieldName: String, - fieldType: FieldType -) extends Location[S] { - - override def source: Option[S] = classFileLocation.source - - def classFileFQN: String = classFileLocation.classFileFQN - - override def toString: String = { - - val s = s"${classFileLocation.classFileFQN}{ /*field*/ $fieldName : ${fieldType.toJava} }" - val source = classFileLocation.source - if (source.isDefined) - s + s"\n${source.get}" - else - s - } -} - -object FieldLocation { - - def apply[S](classFileLocation: ClassFileLocation[S], field: Field): FieldLocation[S] = { - new FieldLocation[S](classFileLocation, field.name, field.fieldType) - } -} - -final case class MethodLocation[S]( - classFileLocation: ClassFileLocation[S], - methodName: String, - methodDescriptor: MethodDescriptor -) extends Location[S] { - - override def source: Option[S] = classFileLocation.source - - def methodSignature: String = methodDescriptor.toJava(methodName) - - def classFileFQN: String = classFileLocation.classFileFQN - - override def toString: String = { - val s = s"${classFileLocation.classFileFQN}{ /*method*/ $methodSignature }" - val source = classFileLocation.source - if (source.isDefined) - s + s"\n${source.get}" - else - s - } - -} - -object MethodLocation { - - def apply[S](source: S, method: Method): MethodLocation[S] = { - val cf = method.classFile - new MethodLocation(ClassFileLocation(source, cf), method.name, method.descriptor) - } - - def apply[S](classFileLocation: ClassFileLocation[S], method: Method): MethodLocation[S] = { - new MethodLocation(classFileLocation, method.name, method.descriptor) - } - - final def apply[S](methodInfo: MethodInfo[S]): MethodLocation[S] = { - MethodLocation(methodInfo.source, methodInfo.method) - } - -} - -final case class InstructionLocation[S](methodLocation: MethodLocation[S], pc: Int) extends Location[S] { - - override def source: Option[S] = methodLocation.source - - def classFileFQN: String = methodLocation.classFileFQN - - def methodSignature: String = methodLocation.methodSignature - - override def toString: String = { - val classFileLocation = methodLocation.classFileLocation - val source = classFileLocation.source - val s = s"${classFileLocation.classFileFQN}{ $methodSignature { $pc } }" - if (source.isDefined) - s + s"\n${source.get}" - else - s - } -} - -object InstructionLocation { - - def apply[S](source: S, method: Method, pc: Int): InstructionLocation[S] = { - val classFileLocation = ClassFileLocation(source, method.classFile) - val methodLocation = MethodLocation(classFileLocation, method.name, method.descriptor) - new InstructionLocation(methodLocation, pc) - } - -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/LocationsContainer.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/LocationsContainer.scala deleted file mode 100644 index cf8903a77f..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/LocationsContainer.scala +++ /dev/null @@ -1,44 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes - -import scala.language.implicitConversions - -/** - * A collection of up to [[org.opalj.hermes.HermesConfig.MaxLocations]] locations where a specific - * feature was found. - * - * Using a `LocationsContainer` has the advantage that we do not store unwanted locations. - * - * @tparam S The kind of the source. E.g., `java.net.URL`. - * - * @author Michael Eichberg - */ -class LocationsContainer[S](implicit hermes: HermesConfig) { - - private var theLocationsCount = 0 - private var theLocations: List[Location[S]] = List() - - def +=(location: => Location[S]): Unit = { - theLocationsCount += 1 - if (theLocationsCount <= hermes.MaxLocations) { - theLocations = location :: theLocations - } - } - - /** The number of locations that were seen. */ - def size: Int = theLocationsCount - - /** - * The locations that were memorized; this depends on the global settings regarding the - * precision and amount of location information that is kept. - */ - def locations: List[Location[S]] = theLocations -} - -object LocationsContainer { - - implicit def toLocationsChain[S](lc: LocationsContainer[S]): List[Location[S]] = { - lc.locations - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/ProjectConfiguration.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/ProjectConfiguration.scala deleted file mode 100644 index 2653063044..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/ProjectConfiguration.scala +++ /dev/null @@ -1,147 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes - -import java.io.File -import java.net.URL -import scala.collection.Map -import scala.collection.immutable - -import org.opalj.br.analyses.Project -import org.opalj.br.analyses.Project.JavaClassFileReader -import org.opalj.br.analyses.Project.JavaLibraryClassFileReader -import org.opalj.log.GlobalLogContext -import org.opalj.log.OPALLogger.error -import org.opalj.log.OPALLogger.info - -import pureconfig._ - -/** - * Meta-information about a project that belongs to a corpus. - * - * @note Represents one project of the configured using the config key: "org.opalj.hermes.projects". - * - * @author Michael Eichberg - */ -case class ProjectConfiguration( - id: String, - cp: String, - libcp: Option[String], - libcpDefaults: Option[String] -) derives ConfigReader { - - @volatile private var theProjectStatistics: immutable.Map[String, Double] = immutable.Map.empty - - /** - * General statistics about a project. - * See [[org.opalj.br.analyses.Project.statistics]] for further information. - * - * @note This information is only available after instantiate was called. - */ - def statistics: Map[String, Double] = { - theProjectStatistics - } - - /** - * @param key The unique name of the statistic. If the name is not unique an exception will - * be thrown. - */ - def addStatistic(key: String, value: Double): Unit = { - this.synchronized { - if (theProjectStatistics.contains(key)) - throw new IllegalArgumentException(s"$id - $key is already set") - else - theProjectStatistics += ((key, value)) - } - } - - /** - * Instantiates the project and initializes the meta-information. - * - * For the classes belonging to the project the naive bytecode representation is - * also returned to facilitate analyses w.r.t. the representativeness of the bytecode. - */ - def instantiate: ProjectInstantiation = { - - // let's try to garbage collect previous projects - new Thread(() => { System.gc() }).start() - - info( - "project setup", - s"creating new project: $id\n\t\t" + - s"cp=$cp\n\t\tlibcp=$libcp\n\t\tlibcp_defaults=$libcpDefaults" - )(using GlobalLogContext) - - val cpJARs = cp.split(File.pathSeparatorChar).flatMap { jar => - val jarFile = new File(jar) - if (!jarFile.exists || !jarFile.canRead) { - error("project configuration", s"invalid class path: $jarFile")(using GlobalLogContext) - None - } else { - Some(jarFile) - } - } - - // - // SETUP BR PROJECT - // - val noBRClassFiles = Iterable.empty[(br.ClassFile, URL)] - val brProjectClassFiles = cpJARs.foldLeft(noBRClassFiles) { (classFiles, cpJAR) => - classFiles ++ JavaClassFileReader().ClassFiles(cpJAR) - } - val libcpJARs = { - libcp match { - case None => - noBRClassFiles - case Some(libs) => - val libcpJARs = libs.split(File.pathSeparatorChar) - libcpJARs.foldLeft(noBRClassFiles) { (classFiles, libcpJAR) => - val libcpJARFile = new File(libcpJAR) - if (!libcpJARFile.exists || !libcpJARFile.canRead) { - error("project configuration", s"invalid library: $libcpJARFile")(using GlobalLogContext) - classFiles - } else - classFiles ++ JavaLibraryClassFileReader.ClassFiles(libcpJARFile) - } - } - } - val libraryClassFiles: Iterable[(br.ClassFile, URL)] = libcpDefaults match { - case None => libcpJARs - case Some(libraries) => - var predefinedLibrariesClassFiles = Iterable.empty[(br.ClassFile, URL)] - var predefinedLibraries = libraries.split(File.pathSeparatorChar) - while (predefinedLibraries.nonEmpty) { - predefinedLibraries.head match { - case "RTJar" => - predefinedLibrariesClassFiles ++= - br.reader.readRTJarClassFiles()(using reader = JavaLibraryClassFileReader) - case "JRE" => - predefinedLibrariesClassFiles ++= - br.reader.readJREClassFiles()(using reader = JavaLibraryClassFileReader) - case unmatched => - error("project configuration", s"unknown library: $unmatched")(using GlobalLogContext) - - } - predefinedLibraries = predefinedLibraries.tail - } - predefinedLibrariesClassFiles ++ libcpJARs - } - val brProject = Project(brProjectClassFiles, libraryClassFiles, true) - this.synchronized { - theProjectStatistics ++= brProject.statistics.map { kv => - val (k, v) = kv; (k, v.toDouble) - } - } - - // - // SETUP DA CLASS FILE - // - val noDAClassFiles = Iterable.empty[(da.ClassFile, URL)] - val daProjectClassFiles = cpJARs.foldLeft(noDAClassFiles) { (classFiles, cpJAR) => - classFiles ++ da.ClassFileReader.ClassFiles(cpJAR) - } - - ProjectInstantiation(brProject, daProjectClassFiles) - } - -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/ProjectFeatures.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/ProjectFeatures.scala deleted file mode 100644 index b27770efbf..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/ProjectFeatures.scala +++ /dev/null @@ -1,26 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes - -import javafx.beans.property.ObjectProperty -import javafx.beans.property.SimpleStringProperty -import javafx.beans.property.StringProperty - -/** - * The feature objects associated with every project. - * - * @author Michael Eichberg - */ -case class ProjectFeatures[S]( - projectConfiguration: ProjectConfiguration, - featureGroups: Seq[(FeatureQuery, Seq[ObjectProperty[Feature[S]]])] -) { - - /** The project's unique id. */ - final val id: StringProperty = { - new SimpleStringProperty(projectConfiguration, "project", projectConfiguration.id) - } - - final val features: Seq[ObjectProperty[Feature[S]]] = featureGroups.flatMap(_._2) - -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/ProjectInstantiation.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/ProjectInstantiation.scala deleted file mode 100644 index 30bedab5d5..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/ProjectInstantiation.scala +++ /dev/null @@ -1,17 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes - -import java.net.URL - -import org.opalj.br.analyses.Project - -/** - * The resources of the instantiated project. - * - * @author Michael Eichberg - */ -case class ProjectInstantiation( - project: Project[URL], - rawClassFiles: Iterable[(da.ClassFile, URL)] -) diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/Query.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/Query.scala deleted file mode 100644 index 636583b517..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/Query.scala +++ /dev/null @@ -1,44 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes - -import org.opalj.log.GlobalLogContext -import org.opalj.log.OPALLogger.error - -import pureconfig._ - -/** - * Container for feature queries. - * - * @note Used to represent the corresponding information in the general configuration file. - * @param query The name of a concrete class which inherits from `FeatureQuery` and implements - * a default constructor. - * - * @author Michael Eichberg - */ -case class Query(query: String, private var activate: Boolean = true) derives ConfigReader { - - def isEnabled: Boolean = activate - - private var reifiedQuery: Option[FeatureQuery] = null - - def reify(implicit hermes: HermesConfig): Option[FeatureQuery] = this.synchronized { - if (reifiedQuery ne null) { - return reifiedQuery; - } - - reifiedQuery = - try { - val queryClass = Class.forName(query, false, getClass.getClassLoader) - val queryClassConstructor = queryClass.getDeclaredConstructor(classOf[HermesConfig]) - Some(queryClassConstructor.newInstance(hermes).asInstanceOf[FeatureQuery]) - } catch { - case t: Throwable => - error("application configuration", s"failed to load: $query", t)(using GlobalLogContext) - activate = false - None - } - reifiedQuery - } - -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/BytecodeInstructions.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/BytecodeInstructions.scala deleted file mode 100644 index 1763e4ad0b..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/BytecodeInstructions.scala +++ /dev/null @@ -1,61 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries - -import java.net.URI -import java.net.URL - -import org.opalj.br.MethodWithBody -import org.opalj.br.analyses.Project - -/** - * Counts the number of occurrences of each bytecode instruction. - * - * @author Michael Eichberg - */ -class BytecodeInstructions(implicit hermes: HermesConfig) extends FeatureQuery { - - // Let's do some caching... - final val JVMInstructions: List[(Int, String)] = bytecode.JVMInstructions - private final val OpcodesToOrdinalNumbers = new Array[Int](256) - - override val htmlDescription: Either[String, URL] = { - Right(URI.create("https://www.opal-project.de/bi/JVMInstructions.xml").toURL) - } - - override def featureIDs: IndexedSeq[String] = { - var ordinalNumber = 0 - JVMInstructions.map { i => - val (opcode, mnemonic) = i - OpcodesToOrdinalNumbers(opcode) = ordinalNumber - ordinalNumber += 1 - s"$mnemonic (opcode:$opcode)" - }.toIndexedSeq - } - - override def apply[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(da.ClassFile, S)] - ): IterableOnce[Feature[S]] = { - val instructionsLocations = Array.fill(256)(new LocationsContainer[S]) - - for { - (classFile, source) <- project.projectClassFilesWithSources - if !isInterrupted() - classFileLocation = ClassFileLocation(source, classFile) - case method @ MethodWithBody(body) <- classFile.methods - methodLocation = MethodLocation(classFileLocation, method) - pcAndInstruction <- body - } { - val instruction = pcAndInstruction.instruction - val pc = pcAndInstruction.pc - instructionsLocations(instruction.opcode) += InstructionLocation(methodLocation, pc) - } - - for { (locations, opcode) <- instructionsLocations.iterator.zipWithIndex } yield { - Feature[S](featureIDs(OpcodesToOrdinalNumbers(opcode)), locations) - } - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/BytecodeInstrumentationAPIUsage.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/BytecodeInstrumentationAPIUsage.scala deleted file mode 100644 index 3e99300d58..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/BytecodeInstrumentationAPIUsage.scala +++ /dev/null @@ -1,59 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries - -import org.opalj.br.ClassType -import org.opalj.hermes.queries.util.APIFeature -import org.opalj.hermes.queries.util.APIFeatureGroup -import org.opalj.hermes.queries.util.APIFeatureQuery -import org.opalj.hermes.queries.util.InstanceAPIMethod - -/** - * Extracts basic information about the usage of the Java Instrumentation API that has been - * enhanced in Java 6. - * - * @author Michael Reif - */ -class BytecodeInstrumentationAPIUsage(implicit hermes: HermesConfig) extends APIFeatureQuery { - - override def apiFeatures: List[APIFeature] = { - val Instrumentation = ClassType("java/lang/instrument/Instrumentation") - - List( - APIFeatureGroup( - List( - InstanceAPIMethod(Instrumentation, "retransformClasses"), - InstanceAPIMethod(Instrumentation, "addTransformer"), - InstanceAPIMethod(Instrumentation, "isModifiableClass"), - InstanceAPIMethod(Instrumentation, "isRetransformClassesSupported"), - InstanceAPIMethod(Instrumentation, "isRedefineClassesSupported"), - InstanceAPIMethod(Instrumentation, "redefineClasses") - ), - "class file retransformation" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(Instrumentation, "setNativeMethodPrefix"), - InstanceAPIMethod(Instrumentation, "isNativeMethodPrefixSupported") - ), - "instrumenting native methods" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(Instrumentation, "appendToBootstrapClassLoaderSearch"), - InstanceAPIMethod(Instrumentation, "appendToSystemClassLoaderSearch") - ), - "appending class loader search" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(Instrumentation, "getAllLoadedClasses"), - InstanceAPIMethod(Instrumentation, "getInitiatedClasses"), - InstanceAPIMethod(Instrumentation, "getObjectSize") - ), - "retrieve classes information" - ) - ) - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/ClassFileVersion.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/ClassFileVersion.scala deleted file mode 100644 index 4ee295f26d..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/ClassFileVersion.scala +++ /dev/null @@ -1,69 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries - -import org.opalj.bi.Java1MajorVersion -import org.opalj.bi.Java5MajorVersion -import org.opalj.bi.LatestSupportedJavaMajorVersion -import org.opalj.bi.jdkVersion -import org.opalj.br.analyses.Project -import org.opalj.collection.mutable.ArrayMap - -/** - * Counts the number of class files per class file version. - * - * @author Michael Eichberg - */ -class ClassFileVersion(implicit hermes: HermesConfig) extends FeatureQuery { - - def featureId(majorVersion: Int) = s"${jdkVersion(majorVersion)} Class File" - - override val featureIDs: Seq[String] = { - featureId(Java1MajorVersion) +: ( - for (majorVersion <- Java5MajorVersion to LatestSupportedJavaMajorVersion) - yield featureId(majorVersion) - ) - } - - override def apply[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(da.ClassFile, S)] - ): IterableOnce[Feature[S]] = { - - val data = ArrayMap[LocationsContainer[S]](LatestSupportedJavaMajorVersion) - - for { - (classFile, source) <- project.projectClassFilesWithSources - if !isInterrupted() - } { - val version = classFile.majorVersion - val normalizedVersion = if (version < Java5MajorVersion) Java1MajorVersion else version - var locations = data(normalizedVersion) - if (locations eq null) { - locations = new LocationsContainer[S] - data(normalizedVersion) = locations - } - locations += ClassFileLocation[S](source, classFile) - } - - { - val java1MajorVersionFeatureId = this.featureId(Java1MajorVersion) - val extensions = data(Java1MajorVersion) - if (data(Java1MajorVersion) eq null) - Feature[S](java1MajorVersionFeatureId, 0, List.empty) - else - Feature[S](java1MajorVersionFeatureId, extensions.size, extensions) - } +: ( - for (majorVersion <- Java5MajorVersion to LatestSupportedJavaMajorVersion) yield { - val featureId = this.featureId(majorVersion) - val extensions = data(majorVersion) - if (extensions ne null) { - Feature[S](featureId, extensions.size, extensions) - } else - Feature[S](featureId, 0, List.empty) - } - ) - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/ClassLoaderAPIUsage.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/ClassLoaderAPIUsage.scala deleted file mode 100644 index 4fb7c8c085..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/ClassLoaderAPIUsage.scala +++ /dev/null @@ -1,63 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries - -import org.opalj.br.ClassType -import org.opalj.br.MethodDescriptor.JustTakes -import org.opalj.br.MethodDescriptor.NoArgsAndReturnVoid -import org.opalj.hermes.queries.util.APIClassExtension -import org.opalj.hermes.queries.util.APIFeature -import org.opalj.hermes.queries.util.APIFeatureGroup -import org.opalj.hermes.queries.util.APIFeatureQuery -import org.opalj.hermes.queries.util.InstanceAPIMethod -import org.opalj.hermes.queries.util.StaticAPIMethod - -/** - * Extracts calls to the `java.lang.ClassLoader` API. - * - * @author Michael Reif - */ -class ClassLoaderAPIUsage(implicit hermes: HermesConfig) extends APIFeatureQuery { - - override val apiFeatures: List[APIFeature] = { - - val ClassLoader = ClassType.ClassLoader - - List( - APIClassExtension("custom ClassLoader implementation", ClassLoader), - APIFeatureGroup( - List( - StaticAPIMethod(ClassLoader, "getSystemClassLoader"), - InstanceAPIMethod(ClassLoader, "", NoArgsAndReturnVoid) - ), - "Retrieving the SystemClassLoader" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(ClassLoader, "", JustTakes(ClassLoader)), - InstanceAPIMethod(ClassType.Class, "getClassLoader") - ), - "Retrieving some ClassLoader" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(ClassLoader, "defineClass"), - InstanceAPIMethod(ClassLoader, "definePackage") - ), - "define new classes/packages" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(ClassLoader, "getResource"), - InstanceAPIMethod(ClassLoader, "getResourceAsStream"), - InstanceAPIMethod(ClassLoader, "getResources"), - InstanceAPIMethod(ClassLoader, "getSystemResource"), - InstanceAPIMethod(ClassLoader, "getSystemResourceAsStream"), - InstanceAPIMethod(ClassLoader, "getSystemResources") - ), - "accessing resources" - ) - ) - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/ClassTypes.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/ClassTypes.scala deleted file mode 100644 index 2d88ff17be..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/ClassTypes.scala +++ /dev/null @@ -1,96 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries - -import org.opalj.br.analyses.Project - -/** - * Counts which kinds of class types are actually defined. - * - * @author Michael Eichberg - */ -class ClassTypes(implicit hermes: HermesConfig) extends FeatureQuery { - - override val featureIDs: List[String] = { - List( - /*0*/ "(concrete) classes", - /*1*/ "abstract classes", - /*2*/ "annotations", - /*3*/ "enumerations", - /*4*/ "marker interfaces", - /*5*/ "simple functional interfaces\n(single abstract method (SAM) interface)", - /*6*/ "non-functional interface\nwith default methods (Java >8)", - /*7*/ "non-functional interface\nwith static methods (Java >8)", - /*8*/ "(standard) interface", - /*9*/ "module (Java >9)" - ) - } - - override def apply[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(da.ClassFile, S)] - ): IterableOnce[Feature[S]] = { - - val classTypesLocations = Array.fill(10)(new LocationsContainer[S]) - - val functionalInterfaces = project.functionalInterfaces - - for { - (classFile, source) <- project.projectClassFilesWithSources - if !isInterrupted() - } { - val location = ClassFileLocation(source, classFile) - - if (classFile.isClassDeclaration) { - if (!classFile.isAbstract) { - classTypesLocations(0) += location - } else { - classTypesLocations(1) += location - } - - } else if (classFile.isAnnotationDeclaration) { - classTypesLocations(2) += location - - } else if (classFile.isEnumDeclaration) { - classTypesLocations(3) += location - - } else if (classFile.isInterfaceDeclaration) { - - // In the following we try to distinguish the different types of - // interfaces that are usually distinguished from a developer point-of-view. - val explicitlyDefinedMethods = classFile.methods.filter(_.name != "") - - if (explicitlyDefinedMethods.isEmpty && classFile.interfaceTypes.isEmpty) { - // => MarkerInterface (even if it defines complex constants) - classTypesLocations(4) += location - - } else if (functionalInterfaces.contains(classFile.thisType)) { - // we have found a "true" functional interface - classTypesLocations(5) += location - } else { - var isJava8Interface = false - if (explicitlyDefinedMethods.exists(m => !m.isAbstract && !m.isStatic)) { - classTypesLocations(6) += location - isJava8Interface = true - } - if (explicitlyDefinedMethods.exists(m => m.isStatic)) { - classTypesLocations(7) += location - isJava8Interface = true - } - if (!isJava8Interface) { - classTypesLocations(8) += location - } - } - - } else if (classFile.isModuleDeclaration) { - classTypesLocations(9) += location - } - } - - for { (featureID, featureIDIndex) <- featureIDs.iterator.zipWithIndex } yield { - Feature[S](featureID, classTypesLocations(featureIDIndex)) - } - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/DebugInformation.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/DebugInformation.scala deleted file mode 100644 index faf3176d52..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/DebugInformation.scala +++ /dev/null @@ -1,55 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries - -import org.opalj.br.MethodWithBody -import org.opalj.br.analyses.Project - -/** - * Classifies class file elements which contain debug information. - * - * @note The "SourceDebugExtension" attribute is ignored as it is generally not used. - * - * @author Michael Eichberg - */ -class DebugInformation(implicit hermes: HermesConfig) extends FeatureQuery { - - override def featureIDs: IndexedSeq[String] = { - IndexedSeq( - /*0*/ "Class File With\nSource Attribute", - /*1*/ "Method With\nLine Number Table", - /*2*/ "Method With\nLocal Variable Table", - /*3*/ "Method With\nLocal Variable Type Table" - ) - } - - override def apply[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(da.ClassFile, S)] - ): IterableOnce[Feature[S]] = { - val locations = Array.fill(4)(new LocationsContainer[S]) - - for { - (classFile, source) <- project.projectClassFilesWithSources - if !isInterrupted() - classFileLocation = ClassFileLocation(source, classFile) - } { - if (classFile.sourceFile.isDefined) locations(0) += classFileLocation - - for { - case method @ MethodWithBody(body) <- classFile.methods - methodLocation = MethodLocation(classFileLocation, method) - } { - if (body.localVariableTable.isDefined) locations(1) += methodLocation - if (body.localVariableTypeTable.nonEmpty) locations(2) += methodLocation - if (body.lineNumberTable.isDefined) locations(3) += methodLocation - } - } - - for { (locations, index) <- locations.iterator.zipWithIndex } yield { - Feature[S](featureIDs(index), locations) - } - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/FanInFanOut.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/FanInFanOut.scala deleted file mode 100644 index 134257057b..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/FanInFanOut.scala +++ /dev/null @@ -1,301 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries - -import scala.annotation.tailrec - -import scala.collection.mutable - -import com.typesafe.config.Config - -import org.opalj.br.ClassType -import org.opalj.br.FieldType -import org.opalj.br.MethodDescriptor -import org.opalj.br.analyses.Project -import org.opalj.da.ClassFile -import org.opalj.da.CONSTANT_Class_info -import org.opalj.da.CONSTANT_Fieldref_info -import org.opalj.da.CONSTANT_InterfaceMethodref_info -import org.opalj.da.CONSTANT_MethodHandle_info -import org.opalj.da.CONSTANT_Methodref_info -import org.opalj.da.CONSTANT_NameAndType_info -import org.opalj.da.Constant_Pool -import org.opalj.da.Constant_Pool_Entry -import org.opalj.da.CONSTANT_Utf8_info -import org.opalj.log.GlobalLogContext -import org.opalj.log.LogContext -import org.opalj.log.OPALLogger - -/** - * This metric computes the Fan-In and Fan-Out of a class. - * - * @author Michael Reif - */ -class FanInFanOut(implicit hermes: HermesConfig) extends FeatureQuery { - - /// Configuration Keys - final val FanInFanOutConfigPrefix = "org.opalj.hermes.queries.FanInFanOut" - - ///// ############################################################### - ///// ############### Config Parsing Data Structures ################ - ///// ############################################################### - - case class FeatureConfiguration( - featureName: String, - numCategories: Int, - categorySize: Double, - offset: Int - ) { - - private lazy val _maxFeatureIndex = numCategories - 1 - - def featureIndex(value: Int): Int = { - Math.min(value / categorySize.toInt, _maxFeatureIndex) + offset - } - - def featureIndex(value: Double): Int = { - Math.min((value / numCategories).toInt, _maxFeatureIndex) + offset - } - } - - object FeatureConfiguration { - - implicit def logContext: LogContext = GlobalLogContext - - final val logCategory = "Hermes - fan-in/fan-out query" - - def apply( - featureName: String, - categoriesKey: String, - categorySizeKey: String, - categoriesDefault: Int, - categorySizeDefault: Double, - offset: Int - ): FeatureConfiguration = { - implicit val config: Config = hermes.Config.getConfig(FanInFanOutConfigPrefix) - val numCategories = parseNumCategories(categoriesKey).getOrElse(categoriesDefault) - val categorySize = parseCategorySize(categorySizeKey).getOrElse(categorySizeDefault) - - FeatureConfiguration(featureName, numCategories, categorySize, offset) - } - - private def parseNumCategories( - categoriesKey: String - )( - implicit config: Config - ): Option[Int] = { - val numCategories = config.getInt(categoriesKey) - if (numCategories > 0) - Some(numCategories) - else { - val message = - FanInFanOutConfigPrefix + categoriesKey + - " setting invalid - value <= 0; category size has been set to default" - OPALLogger.warn(logCategory, message) - None - } - } - - private def parseCategorySize( - categorySizeKey: String - )( - implicit config: Config - ): Option[Double] = { - val categorySize = config.getDouble(categorySizeKey) - if (categorySize > 0) { - Some(categorySize) - } else { - val message = - FanInFanOutConfigPrefix + categorySizeKey + - " setting invalid - value <= 0; category size has been set to default" - OPALLogger.warn(logCategory, message) - None - } - } - } - - ///// ############################################################### - ///// ####################### CONFIGURATION ######################### - ///// ############################################################### - - val fanoutFeatureName = "FanOut" - val fanoutCategories = "fanout.categories" - val fanoutCategorySize = "fanout.categorySize" - val DEFAULT_fanoutCategories = 4 - val DEFAULT_fanoutCategorySize = 1d - - val faninFeatureName = "FanIn" - val faninCategories = "fanin.categories" - val faninCategorySize = "fanin.categorySize" - val DEFAULT_faninCategories = 4 - val DEFAULT_faninCategorySize = 1d - - val ratioFeatureName = "FanIn/FanOut" - val ratioCategories = "ratio.categories" - val ratioCategorySize = "ratio.categorySize" - val DEFAULT_ratioCategories = 4 - val DEFAULT_ratioCategorySize = 0.5d - - private val fanoutFeature = FeatureConfiguration( - fanoutFeatureName, - fanoutCategories, - fanoutCategorySize, - DEFAULT_fanoutCategories, - DEFAULT_fanoutCategorySize, - offset = 0 - ) - - private val faninFeature = FeatureConfiguration( - faninFeatureName, - faninCategories, - faninCategorySize, - DEFAULT_faninCategories, - DEFAULT_faninCategorySize, - offset = fanoutFeature.numCategories - ) - - private val ratioFeature = FeatureConfiguration( - ratioFeatureName, - ratioCategories, - ratioCategorySize, - DEFAULT_ratioCategories, - DEFAULT_ratioCategorySize, - offset = fanoutFeature.numCategories + faninFeature.numCategories - ) - - private lazy val _featureInfo: Seq[FeatureConfiguration] = Seq( - fanoutFeature, - faninFeature, - ratioFeature - ) - - // Initializes the featureIds from the configuration file. - private lazy val _featureIDs: Seq[String] = { - - val seqBuilder = Seq.newBuilder[String] - - _featureInfo.foreach { featureKind => - val FeatureConfiguration(featureName, numCategories, _, _) = featureKind - var i = 1 - while (i <= numCategories) { - seqBuilder += s"$featureName - Category $i" - i += 1 - } - } - - seqBuilder.result() - } - - ///// ############################################################### - ///// ################## QUERY INTERFACE METHODS #################### - ///// ############################################################### - - override def featureIDs: Seq[String] = _featureIDs - - override def apply[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(ClassFile, S)] - ): IterableOnce[Feature[S]] = { - - val features = Array.fill(featureIDs.size)(new LocationsContainer[S]) - - val fanOutMap = mutable.Map.empty[Int, Int] - val fanInMap = mutable.Map.empty[Int, Int] - - @inline def getClassTypeID = project.classHierarchy.getClassType - - for { - (classFile, source) <- rawClassFiles - classFileType = classFile.thisType.asJVMType - classTypeId = ClassType(classFileType).id - location = ClassFileLocation(Some(source), classFileType) - } { - implicit val constantPool: Constant_Pool = classFile.constant_pool - val cpEntryPredicate: PartialFunction[Constant_Pool_Entry, Constant_Pool_Entry] = { - case CONSTANT_Fieldref_info(_, name_and_type_index) => constantPool(name_and_type_index) - case CONSTANT_NameAndType_info(_, descriptor_index) => constantPool(descriptor_index) - case CONSTANT_MethodHandle_info(_, reference_index) => constantPool(reference_index) - case CONSTANT_Methodref_info(_, class_index) => constantPool(class_index) - case CONSTANT_Class_info(name_index) => constantPool(name_index) - } - - @inline def cpEntries = classFile.constant_pool.filter(cpEntryPredicate.isDefinedAt) - - val referencedTypes = mutable.Set.empty[Int] - cpEntries.foreach { cpEntry => - val typeInfo = getTypeInfo(cpEntry) - if (typeInfo.charAt(0) == '(') { - val md = MethodDescriptor(typeInfo) - referencedTypes ++= md.parameterTypes.foldLeft(Set.empty[Int])((res, p) => - if (p.isClassType) - res + p.asClassType.id - else res - ) - if (md.returnType.isClassType) - referencedTypes += md.returnType.asClassType.id - } else { - try { - val ft = FieldType(typeInfo) - if (ft.isClassType) - referencedTypes += ft.asClassType.id - } catch { - case _: IllegalArgumentException => - referencedTypes += ClassType(typeInfo).id - } - } - } - - val fanOut = referencedTypes.size - 1 - val fanOutIndex = fanoutFeature.featureIndex(fanOut) - features(fanOutIndex) += location - - fanOutMap += (classTypeId -> fanOut) - - referencedTypes.foreach { otId => - if (otId != classTypeId) { - val newRefCount = fanInMap.getOrElse(otId, 0) + 1 - fanInMap += ((otId, newRefCount)) - } - } - } - - fanInMap.foreach { entry => - val (otID, fanIn) = entry - val fanOut = fanOutMap.getOrElse(otID, 1) - val fanInFanOut = fanIn.toDouble / fanOut.toDouble - val l = ClassFileLocation(project, getClassTypeID(otID)) - - val fanInFeatureIndex = faninFeature.featureIndex(fanIn) - val ratioFeatureIndex = ratioFeature.featureIndex(fanInFanOut) - - features(fanInFeatureIndex) += l - features(ratioFeatureIndex) += l - } - - for { (featureID, featureIDIndex) <- featureIDs.iterator.zipWithIndex } yield { - Feature[S](featureID, features(featureIDIndex)) - } - } - - @tailrec private def getTypeInfo( - constant_Pool_Entry: Constant_Pool_Entry - )(implicit constant_pool: Constant_Pool): String = { - constant_Pool_Entry match { - case CONSTANT_Class_info(name_index) => - getTypeInfo(constant_pool(name_index)) - case CONSTANT_Fieldref_info(_, name_and_type_index) => - getTypeInfo(constant_pool(name_and_type_index)) - case CONSTANT_MethodHandle_info(_, reference_index) => - getTypeInfo(constant_pool(reference_index)) - case CONSTANT_Methodref_info(_, class_index) => - getTypeInfo(constant_pool(class_index)) - case CONSTANT_InterfaceMethodref_info(_, class_index) => - getTypeInfo(constant_pool(class_index)) - case CONSTANT_NameAndType_info(_, descriptor_index) => - getTypeInfo(constant_pool(descriptor_index)) - case CONSTANT_Utf8_info(_, value) => value - } - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/FieldAccessStatistics.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/FieldAccessStatistics.scala deleted file mode 100644 index 52b601a6e4..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/FieldAccessStatistics.scala +++ /dev/null @@ -1,87 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries - -import scala.collection.immutable.ArraySeq - -import org.opalj.bi.ACC_PRIVATE -import org.opalj.bi.ACC_PROTECTED -import org.opalj.bi.ACC_PUBLIC -import org.opalj.br.ClassType -import org.opalj.br.analyses.DeclaredFields -import org.opalj.br.analyses.DeclaredFieldsKey -import org.opalj.br.analyses.FieldAccessInformationKey -import org.opalj.br.analyses.Project -import org.opalj.br.fpcf.ContextProviderKey -import org.opalj.br.fpcf.analyses.ContextProvider - -/** - * Counts how often fields are accessed. - * - * @author Michael Eichberg - */ -class FieldAccessStatistics(implicit hermes: HermesConfig) extends DefaultFeatureQuery { - - override def featureIDs: List[String] = { - List( - /*0*/ "unused private fields", - /*1*/ "unused package visible fields", - /*2*/ "unused protected fields", - /*3*/ "unused public fields", - /*4*/ "package visible fields\nonly used by defining type", - /*5*/ "protected fields\nonly used by defining type", - /*6*/ "public fields\nonly used by defininig type " - ) - } - - override def evaluate[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(da.ClassFile, S)] - ): IndexedSeq[LocationsContainer[S]] = { - val locations = Array.fill(featureIDs.size)(new LocationsContainer[S]) - - implicit val declaredFields: DeclaredFields = project.get(DeclaredFieldsKey) - val contextProvider: ContextProvider = project.get(ContextProviderKey) - val fieldAccessInformation = project.get(FieldAccessInformationKey) - import fieldAccessInformation.isAccessed - import fieldAccessInformation.allAccesses - - for { - cf <- project.allProjectClassFiles - classFileLocation = ClassFileLocation(project, cf) - field <- cf.fields - fieldType = field.fieldType - if !fieldType.isBaseType || - (field.fieldType ne ClassType.String) || - !(field.isStatic && field.isFinal) - } { - val category = - if (!isAccessed(field)) { - field.visibilityModifier match { - case Some(ACC_PRIVATE) => 0 - case None => 1 - case Some(ACC_PROTECTED) => 2 - case Some(ACC_PUBLIC) => 3 - } - } else if (!field.isPrivate && allAccesses(field).forall(ac => - contextProvider.contextFromId(ac._1).method.definedMethod.classFile eq cf - ) - ) { - field.visibilityModifier match { - case None => 4 - case Some(ACC_PROTECTED) => 5 - case Some(ACC_PUBLIC) => 6 - - case Some(ACC_PRIVATE) => - throw new UnknownError(s"non private-field $field has private modifier") - } - } else { - -1 - } - if (category != -1) locations(category) += FieldLocation(classFileLocation, field) - } - ArraySeq.unsafeWrapArray(locations) - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/GUIAPIUsage.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/GUIAPIUsage.scala deleted file mode 100644 index 57d2da3b68..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/GUIAPIUsage.scala +++ /dev/null @@ -1,60 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries - -import org.opalj.br.analyses.Project -import org.opalj.da.ClassFile -import org.opalj.da.CONSTANT_Utf8_info - -/** - * Scans a class file's constant pool to check whether it refers to packages that belong to an API - * for graphical user interfaces. The current analysis supports: - * - JavaFX (javafx.) - * - SWT (org.eclipse.swt) - * - Swing (javax.swing) - * - AWT (java.awt) - * - * @author Michael Reif - */ -class GUIAPIUsage(implicit hermes: HermesConfig) extends FeatureQuery { - - override val featureIDs: List[String] = { - List( - /* 0 */ "JavaFX", - /* 1 */ "SWT", - /* 2 */ "Swing", - /* 3 */ "AWT" - ) - } - - override def apply[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(ClassFile, S)] - ): IterableOnce[Feature[S]] = { - - val locations = Array.fill(featureIDs.size)(new LocationsContainer[S]) - - for { - (classFile, source) <- rawClassFiles - location = ClassFileLocation(Some(source), classFile.thisType.asJava) - case CONSTANT_Utf8_info(_, entry) <- classFile.constant_pool - } { - if (entry.startsWith("javafx/")) { - // note: package "javafx" is empty, - locations(0) += location - } else if (entry.startsWith("org/eclipse/swt")) { - locations(1) += location - } else if (entry.startsWith("javax/swing")) { - locations(2) += location - } else if (entry.startsWith("java/awt")) { - locations(3) += location - } - } - - for { (featureID, featureIDIndex) <- featureIDs.iterator.zipWithIndex } yield { - Feature[S](featureID, locations(featureIDIndex)) - } - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/JDBCAPIUsage.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/JDBCAPIUsage.scala deleted file mode 100644 index 0dee2371a7..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/JDBCAPIUsage.scala +++ /dev/null @@ -1,59 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries - -import org.opalj.br.ClassType -import org.opalj.hermes.queries.util.APIFeature -import org.opalj.hermes.queries.util.APIFeatureGroup -import org.opalj.hermes.queries.util.APIFeatureQuery -import org.opalj.hermes.queries.util.InstanceAPIMethod -import org.opalj.hermes.queries.util.StaticAPIMethod - -/** - * Counts the amount of calls to certain JDBC api methods - * - * @author Michael Reif - */ -class JDBCAPIUsage(implicit hermes: HermesConfig) extends APIFeatureQuery { - - override val apiFeatures: List[APIFeature] = { - val DriverManager = ClassType("java/sql/DriverManager") - val Connection = ClassType("java/sql/Connection") - val Statement = ClassType("java/sql/Statement") - val PreparedStatement = ClassType("java/sql/PreparedStatement") - val CallableStatement = ClassType("java/sql/CallableStatement") - - List( - StaticAPIMethod(DriverManager, "getConnection"), - InstanceAPIMethod(Connection, "rollback"), - APIFeatureGroup( - List( - InstanceAPIMethod(Connection, "createStatement"), - InstanceAPIMethod(Statement, "execute"), - InstanceAPIMethod(Statement, "executeQuery"), - InstanceAPIMethod(Statement, "executeUpdate") - ), - "creation and execution of\njava.sql.Statement" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(Connection, "prepareStatement"), - InstanceAPIMethod(PreparedStatement, "execute"), - InstanceAPIMethod(PreparedStatement, "executeQuery"), - InstanceAPIMethod(PreparedStatement, "executeUpdate") - ), - "creation and execution of\njava.sql.PreparedStatement" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(Connection, "prepareCall"), - InstanceAPIMethod(CallableStatement, "execute"), - InstanceAPIMethod(CallableStatement, "executeQuery"), - InstanceAPIMethod(CallableStatement, "executeUpdate") - ), - "creation and execution of\njava.sql.CallableStatement" - ) - ) - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/JavaCryptoArchitectureUsage.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/JavaCryptoArchitectureUsage.scala deleted file mode 100644 index 7c4299dbb5..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/JavaCryptoArchitectureUsage.scala +++ /dev/null @@ -1,116 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries - -import org.opalj.br.ClassType -import org.opalj.hermes.queries.util.APIFeature -import org.opalj.hermes.queries.util.APIFeatureGroup -import org.opalj.hermes.queries.util.APIFeatureQuery -import org.opalj.hermes.queries.util.InstanceAPIMethod -import org.opalj.hermes.queries.util.StaticAPIMethod - -/** - * Captures the usage of the Java Crypto Architecture. - * - * @author Michael Reif - */ -class JavaCryptoArchitectureUsage(implicit hermes: HermesConfig) extends APIFeatureQuery { - - override val apiFeatures: List[APIFeature] = { - - // java.security - val SecureRandom = ClassType("java/security/SecureRandom") - val MessageDigest = ClassType("java/security/MessageDigest") - val Signature = ClassType("java/security/Signature") - val KeyFactory = ClassType("java/security/KeyFactory") - val KeyPairGenerator = ClassType("java/security/KeyPairGenerator") - val KeyStore = ClassType("java/security/KeyStore") - - // java.security.cert - - val CertificateFactory = ClassType("java/security/cert/CertificateFactory") - val CertPathBuilder = ClassType("java/security/cert/CertPathBuilder") - val CertPathValidator = ClassType("java/security/cert/CertPathValidator") - val CertStore = ClassType("java/security/cert/CertStore") - - // javax/crypto - val Cipher = ClassType("javax/crypto/Cipher") - val Mac = ClassType("javax/crypto/Mac") - val SecretKeyFactory = ClassType("javax/crypto/SecretKeyFactory") - val KeyGenerator = ClassType("javax/crypto/KeyGenerator") - val KeyAgreement = ClassType("javax/crypto/KeyAgreement") - - // common methods - val init = "" - val getInstance = "getInstance" - - List( - StaticAPIMethod(Cipher, getInstance), - APIFeatureGroup( - List( - InstanceAPIMethod(SecureRandom, init), - StaticAPIMethod(SecureRandom, getInstance), - StaticAPIMethod(SecureRandom, "getInstanceStrong") - ), - s"using SecureRandom" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(MessageDigest, init), - StaticAPIMethod(MessageDigest, getInstance) - ), - s"using MessageDigest" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(Signature, init), - StaticAPIMethod(Signature, getInstance) - ), - s"using Signature" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(Mac, init), - StaticAPIMethod(Mac, getInstance) - ), - s"using Mac" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(KeyFactory, init), - StaticAPIMethod(KeyFactory, getInstance), - InstanceAPIMethod(SecretKeyFactory, init), - StaticAPIMethod(SecretKeyFactory, getInstance), - InstanceAPIMethod(KeyPairGenerator, init), - StaticAPIMethod(KeyPairGenerator, getInstance), - InstanceAPIMethod(KeyGenerator, init), - StaticAPIMethod(KeyGenerator, getInstance), - InstanceAPIMethod(KeyAgreement, init), - StaticAPIMethod(KeyAgreement, getInstance) - ), - s"cryptographic key handling" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(KeyStore, init), - StaticAPIMethod(KeyStore, getInstance) - ), - s"using KeyStore" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(CertificateFactory, init), - StaticAPIMethod(CertificateFactory, getInstance), - InstanceAPIMethod(CertPathBuilder, init), - StaticAPIMethod(CertPathBuilder, getInstance), - InstanceAPIMethod(CertPathValidator, init), - StaticAPIMethod(CertPathValidator, getInstance), - InstanceAPIMethod(CertStore, init), - StaticAPIMethod(CertStore, getInstance) - ), - s"using Certificates" - ) - ) - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/MethodTypes.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/MethodTypes.scala deleted file mode 100644 index 81207fe1b8..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/MethodTypes.scala +++ /dev/null @@ -1,69 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries - -import org.opalj.br.analyses.Project - -/** - * Counts which types of methods types are found. - * - * @author Michael Eichberg - */ -class MethodTypes(implicit hermes: HermesConfig) extends FeatureQuery { - - override val featureIDs: List[String] = { - List( - /*0*/ "native methods", - /*1*/ "synthetic methods", - /*2*/ "bridge methods", - /*3*/ "synchronized methods", - /*4*/ "varargs methods", - // second category... - /*5*/ "static initializers", - /*6*/ "static methods\n(not including static initializers)", - /*7*/ "constructors", - /*8*/ "instance methods" - ) - } - - override def apply[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(da.ClassFile, S)] - ): IterableOnce[Feature[S]] = { - - val methodLocations = Array.fill(featureIDs.size)(new LocationsContainer[S]) - - for { - (classFile, source) <- project.projectClassFilesWithSources - if !isInterrupted() - classLocation = ClassFileLocation(source, classFile) - m <- classFile.methods - } { - val location = MethodLocation(classLocation, m) - if (m.isNative) methodLocations(0) += location - if (m.isSynthetic) methodLocations(1) += location - if (m.isBridge) methodLocations(2) += location - if (m.isSynchronized) methodLocations(3) += location - if (m.isVarargs) methodLocations(4) += location - - if (m.name == "") - methodLocations(5) += location - else { - if (m.isStatic) { - methodLocations(6) += location - } else { - if (m.name == "") - methodLocations(7) += location - else - methodLocations(8) += location - } - } - } - - for { (featureID, featureIDIndex) <- featureIDs.iterator.zipWithIndex } yield { - Feature[S](featureID, methodLocations(featureIDIndex)) - } - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/MethodsWithoutReturns.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/MethodsWithoutReturns.scala deleted file mode 100644 index 622eb909aa..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/MethodsWithoutReturns.scala +++ /dev/null @@ -1,53 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries - -import org.opalj.br.analyses.Project -import org.opalj.br.cfg.CFGFactory -import org.opalj.br.instructions.ReturnInstruction - -/** - * Counts the number of methods without regular returns. - * - * @author Michael Eichberg - */ -class MethodsWithoutReturns(implicit hermes: HermesConfig) extends FeatureQuery { - - final val AlwaysThrowsExceptionMethodsFeatureId = "Never Returns Normally" - final val InfiniteLoopMethodsFeatureId = "Method with Infinite Loop" - override val featureIDs: List[String] = List( - AlwaysThrowsExceptionMethodsFeatureId, - InfiniteLoopMethodsFeatureId - ) - - override def apply[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(da.ClassFile, S)] - ): IterableOnce[Feature[S]] = { - val infiniteLoopMethods: LocationsContainer[S] = new LocationsContainer[S] - val alwaysThrowsExceptionMethods: LocationsContainer[S] = new LocationsContainer[S] - - for { - (classFile, source) <- project.projectClassFilesWithSources - if !isInterrupted() - classFileLocation = ClassFileLocation(source, classFile) - method <- classFile.methods - body <- method.body - hasReturnInstruction = body.instructionIterator.exists { i => i.isInstanceOf[ReturnInstruction] } - if !hasReturnInstruction - } { - val cfg = CFGFactory(using body, project.classHierarchy) - if (cfg.abnormalReturnNode.predecessors.isEmpty) - infiniteLoopMethods += MethodLocation(classFileLocation, method) - else - alwaysThrowsExceptionMethods += MethodLocation(classFileLocation, method) - } - - List( - Feature[S](AlwaysThrowsExceptionMethodsFeatureId, alwaysThrowsExceptionMethods), - Feature[S](InfiniteLoopMethodsFeatureId, infiniteLoopMethods) - ) - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/Metrics.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/Metrics.scala deleted file mode 100644 index 9dbbc8ff98..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/Metrics.scala +++ /dev/null @@ -1,126 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries - -import scala.collection.mutable - -import org.opalj.br.analyses.Project -import org.opalj.br.cfg.CFGFactory - -/** - * Extracts basic metric information (Fields/Methods per Class; Classes per Package; etc.). - * - * @author Michael Reif - */ -class Metrics(implicit hermes: HermesConfig) extends FeatureQuery { - - /** - * The unique ids of the extracted features. - */ - override val featureIDs: Seq[String] = { - Seq( - "0 FPC", - "1-3 FPC", - "4-10 FPC", - ">10 FPC", // 0, 1, 2, 3 - "0 MPC", - "1-3 MPC", - "4-10 MPC", - ">10 MPC", // 4, 5, 6, 7 - "1-3 CPP", - "4-10 CPP", - ">10 CPP", // 8, 9, 10 - "0 NOC", - "1-3 NOC", - "4-10 NOC", - ">10 NOC", // 11, 12, 13, 14 - "linear methods (McCabe)", - "2-3 McCabe", - "4-10 McCabe", - ">10 McCabe" // 15, 16, 17 ,18 - ) - } - - override def apply[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(da.ClassFile, S)] - ): IterableOnce[Feature[S]] = { - - val classLocations = Array.fill(featureIDs.size)(new LocationsContainer[S]) - - class PackageInfo(var classesCount: Int = 0, val location: PackageLocation[S]) - val packagesInfo = mutable.Map.empty[String, PackageInfo] - - val classHierarchy = project.classHierarchy - - for { - (classFile, source) <- project.projectClassFilesWithSources - classLocation = ClassFileLocation(source, classFile) - } { - // fpc - - classFile.fields.size match { - case 0 => classLocations(0) += classLocation - case x if x <= 3 => classLocations(1) += classLocation - case x if x <= 10 => classLocations(2) += classLocation - case _ => classLocations(3) += classLocation - } - - // mpc - - classFile.methods.size match { - case 0 => classLocations(4) += classLocation - case x if x <= 3 => classLocations(5) += classLocation - case x if x <= 10 => classLocations(6) += classLocation - case _ => classLocations(7) += classLocation - } - - // noc - - classHierarchy.directSubtypesOf(classFile.thisType).size match { - case 0 => classLocations(11) += classLocation - case x if x <= 3 => classLocations(12) += classLocation - case x if x <= 10 => classLocations(13) += classLocation - case _ => classLocations(14) += classLocation - } - - // count the classes per package - val packageName = classFile.thisType.packageName - val packageInfo = packagesInfo.getOrElseUpdate( - packageName, - new PackageInfo(location = PackageLocation(packageName)) - ) - packageInfo.classesCount += 1 - - // McCabe - classFile.methods foreach { method => - CFGFactory(method, project.classHierarchy) foreach { cfg => - val methodLocation = MethodLocation(classLocation, method) - val bbs = cfg.reachableBBs - val edges = bbs.foldLeft(0) { (res, node) => res + node.successors.size } - val mcCabe = edges - bbs.size + 2 - mcCabe match { - case 1 => classLocations(15) += methodLocation - case x if x <= 3 => classLocations(16) += methodLocation - case x if x <= 10 => classLocations(17) += methodLocation - case _ => classLocations(18) += methodLocation - } - } - } - } - - packagesInfo.values foreach { pi => - pi.classesCount match { - case x if x <= 3 => classLocations(8) += pi.location - case x if x <= 10 => classLocations(9) += pi.location - case _ => classLocations(10) += pi.location - } - } - - for { (featureID, featureIDIndex) <- featureIDs.iterator.zipWithIndex } yield { - Feature[S](featureID, classLocations(featureIDIndex)) - } - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/MicroPatterns.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/MicroPatterns.scala deleted file mode 100644 index ca96c78f75..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/MicroPatterns.scala +++ /dev/null @@ -1,521 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries - -import org.opalj.ai.BaseAI -import org.opalj.ai.CorrelationalDomain -import org.opalj.ai.domain -import org.opalj.br.ClassFile -import org.opalj.br.ClassType -import org.opalj.br.Field -import org.opalj.br.LongType -import org.opalj.br.Method -import org.opalj.br.MethodDescriptor -import org.opalj.br.PC -import org.opalj.br.analyses.DeclaredFields -import org.opalj.br.analyses.DeclaredFieldsKey -import org.opalj.br.analyses.FieldAccessInformation -import org.opalj.br.analyses.FieldAccessInformationKey -import org.opalj.br.analyses.Project -import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.ContextProviderKey -import org.opalj.br.fpcf.analyses.ContextProvider -import org.opalj.br.instructions.ArrayLoadInstruction -import org.opalj.br.instructions.FieldReadAccess -import org.opalj.br.instructions.FieldWriteAccess -import org.opalj.br.instructions.Instruction -import org.opalj.br.instructions.LoadConstantInstruction -import org.opalj.br.instructions.MethodInvocationInstruction -import org.opalj.br.instructions.PUTFIELD -import org.opalj.br.instructions.PUTSTATIC -import org.opalj.br.instructions.ReturnValueInstruction -import org.opalj.br.instructions.VirtualMethodInvocationInstruction - -/** - * Counts which kinds of micro patterns are actually available. - * - * @author Leonid Glanz - */ -class MicroPatterns(implicit hermes: HermesConfig) extends FeatureQuery { - - override val featureIDs: List[String] = { - List( - /*0*/ "Designator", - /*1*/ "Taxonomy", - /*2*/ "Joiner", - /*3*/ "Pool", - /*4*/ "Function Pointer", - /*5*/ "Function Object", - /*6*/ "Cobol Like", - /*7*/ "Stateless", - /*8*/ "Common State", - /*9*/ "Immutable", - /*10*/ "Restricted Creation", - /*11*/ "Sampler", - /*12*/ "Box", - /*13*/ "Compound Box", - /*14*/ "Canopy", - /*15*/ "Record", - /*16*/ "Data Manager", - /*17*/ "Sink", - /*18*/ "Outline", - /*19*/ "Trait", - /*20*/ "State Machine", - /*21*/ "Pure Type", - /*22*/ "Augmented Type", - /*23*/ "Pseudo Class", - /*24*/ "Implementor", - /*25*/ "Overrider", - /*26*/ "Extender" - ) - } - - override def apply[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(org.opalj.da.ClassFile, S)] - ): IterableOnce[Feature[S]] = { - implicit val theProject: Project[S] = project - implicit val contextProvider: ContextProvider = project.get(ContextProviderKey) - implicit val declaredFields: DeclaredFields = project.get(DeclaredFieldsKey) - - val fa = project.get(FieldAccessInformationKey) - - val microPatternLocations = Array.fill(featureIDs.size)(new LocationsContainer[S]) - for { - (classFile, source) <- project.projectClassFilesWithSources - if !isInterrupted() - } { - val location = ClassFileLocation(source, classFile) - - if (isDesignator(classFile)) microPatternLocations(0) += location - if (isTaxonomy(classFile)) microPatternLocations(1) += location - if (isJoiner(classFile)) microPatternLocations(2) += location - if (isPool(classFile)) microPatternLocations(3) += location - if (isFunctionPointer(classFile)) microPatternLocations(4) += location - if (isFunctionObject(classFile)) microPatternLocations(5) += location - if (isCobolLike(classFile)) microPatternLocations(6) += location - if (isStateless(classFile)) microPatternLocations(7) += location - if (isCommonState(classFile)) microPatternLocations(8) += location - if (isImmutable(classFile, fa)) microPatternLocations(9) += location - if (isRestrictedCreation(classFile)) microPatternLocations(10) += location - if (isSampler(classFile)) microPatternLocations(11) += location - if (isBox(classFile, fa)) microPatternLocations(12) += location - if (isCompoundBox(classFile, fa)) microPatternLocations(13) += location - if (isCanopy(classFile, fa)) microPatternLocations(14) += location - if (isRecord(classFile)) microPatternLocations(15) += location - if (isDataManager(classFile)) microPatternLocations(16) += location - if (isSink(classFile)) microPatternLocations(17) += location - if (isOutline(classFile)) microPatternLocations(18) += location - if (isTrait(classFile)) microPatternLocations(19) += location - if (isStateMachine(classFile)) microPatternLocations(20) += location - if (isPureType(classFile)) microPatternLocations(21) += location - if (isAugmentedType(classFile)) microPatternLocations(22) += location - if (isPseudoClass(classFile)) microPatternLocations(23) += location - if (isImplementor(classFile, project)) microPatternLocations(24) += location - if (isOverrider(classFile, project)) microPatternLocations(25) += location - if (isExtender(classFile)) microPatternLocations(26) += location - } - for { (featureID, featureIDIndex) <- featureIDs.iterator.zipWithIndex } yield { - Feature[S](featureID, microPatternLocations(featureIDIndex)) - } - } - - def hasExplicitSuperType(cl: ClassFile): Boolean = cl.superclassType.exists(_ ne ClassType.Object) - - /** - * [From the paper] Thus, a Designator micro pattern is an interface which does not - * declare any methods, does not define any static fields or methods, and does not - * inherit such members from any of its superinterfaces. - * - * A class can also be Designator if its definition, as well as the definitions of - * all of its ancestors (other than Object), are empty. - * - * A class can also be Designator if its definition, as well as the definitions of - * all of its ancestors (other than Object), are empty. - */ - def isDesignator(cl: ClassFile)(implicit project: SomeProject): Boolean = { - if (cl.thisType == ClassType.Object) - return false; - - // IMPROVE Cache the results of super interfaces to avoid recomputations or compute it top-down starting with top-level interfaces. - - def isDesignatorType(ct: ClassType): Boolean = { - project.classFile(ct).exists(this.isDesignator) - } - - cl.fields.isEmpty && { - if (cl.isInterfaceDeclaration) - cl.methods.isEmpty && cl.interfaceTypes.forall(isDesignatorType) - else - // we have to filter the always present default constructor (compiler generated, - // if not user defined) - cl.methods.size == 1 && cl.methods.head.descriptor == MethodDescriptor.NoArgsAndReturnVoid && - cl.interfaceTypes.forall(isDesignatorType) && - isDesignatorType(cl.superclassType.get) - } - - } - - /** - * [From the paper:] An empty interface which extends a single interface is - * called a Taxonomy, since it is included, in the subtyping sense, in its parent, - * but otherwise identical to it. - * - * There are also classes which are Taxonomy. Such a class must similarly be empty, - * i.e., add no fields nor methods to its parent. Since constructors are not inherited, - * an empty class may contain constructors. A Taxonomy class may not implement any interfaces. - */ - def isTaxonomy(cl: ClassFile): Boolean = { - cl.fields.isEmpty && { - if (cl.isInterfaceDeclaration) { - cl.interfaceTypes.size == 1 && cl.methods.isEmpty - } else { - cl.thisType != ClassType.Object /*this test is not necessary, but is fast */ && - cl.interfaceTypes.isEmpty && - cl.methods.forall(_.isInitializer) - } - } - } - - /** - * [From the paper:] An empty interface which extends more than one interface is called a - * Joiner, since in effect, it joins together the sets of members of its parents. - * - * An empty class which implements one or more interfaces is also a Joiner. - * ''Here, empty means that the we can have constructors and (optionally) a serialVersionUID - * field.'' - */ - def isJoiner(cl: ClassFile): Boolean = { - if (cl.isInterfaceDeclaration) { - cl.interfaceTypes.size > 1 && cl.fields.isEmpty && cl.methods.isEmpty - } else { - cl.interfaceTypes.nonEmpty && - cl.methods.forall(m => m.isInitializer) && ( - cl.fields match { - case Seq() | Seq(Field(_, "serialVersionUID", LongType)) => true - case _ => false - } - ) - } - } - - /** - * [From the paper:] The most degenerate classes are those which have neither state - * nor behavior. Such a class is distinguished by the requirement that it declares - * no instance fields. Moreover, all of its declared static fields must be final. - * Another requirement is that the class has no methods (other than those inherited - * from Object, or automatically generated constructors). - */ - def isPool(cl: ClassFile): Boolean = { - cl.fields.nonEmpty && cl.fields.forall(f => f.isFinal && f.isStatic) && - // We also (have to) accept a static initializer, because that one will - // initialize the final static fields! - cl.methods.forall(m => m.isInitializer && m.descriptor.parametersCount == 0) - } - - private final val javaLangObjectMethods: Set[String] = - Set("hashCode", "equals", "notify", "notifyAll", "wait", "getClass", "clone", "toString", "finalize") - - def isObjectMethod(method: Method): Boolean = { - // TODO This just checks the name, why don't we check the full signature? - javaLangObjectMethods.contains(method.name) - } - - def isFunctionPointer(cl: ClassFile): Boolean = { - !cl.isInterfaceDeclaration && !cl.isAbstract && cl.methods.count { m => - !isInitMethod(m) - } == 1 && cl.methods.count { m => m.isPublic } == 1 && !cl.methods.exists(m => - m.isStatic && - !m.isStaticInitializer - ) && cl.fields.isEmpty - } - - def isFunctionObject(cl: ClassFile): Boolean = { - cl.fields.nonEmpty && - cl.fields.forall { f => !f.isStatic } && - cl.methods.count { m => !isInitMethod(m) && m.isPublic } == 1 && - !cl.methods.filter(m => !isInitMethod(m)).exists(m => m.isStatic) - } - - def isCobolLike(cl: ClassFile): Boolean = { - !cl.methods.exists { m => !isInitMethod(m) } && cl.methods.count { m => - !isInitMethod(m) && - m.isStatic - } == 1 && - !cl.fields.exists { f => !f.isStatic } && cl.fields.exists(f => f.isStatic) - } - - def isStateless(cl: ClassFile): Boolean = { - !cl.isInterfaceDeclaration && !cl.isAbstract && - !cl.fields.exists { f => !(f.isFinal && f.isStatic) } && - cl.methods.count(m => !isInitMethod(m) && !isObjectMethod(m)) > 1 - } - - def isCommonState(cl: ClassFile): Boolean = { - !cl.isInterfaceDeclaration && - cl.fields.nonEmpty && cl.fields.forall { f => f.isStatic } && - cl.fields.exists { f => !f.isFinal } - } - - def isImmutable(cl: ClassFile, fa: FieldAccessInformation)( - implicit - contextProvider: ContextProvider, - declaredFields: DeclaredFields - ): Boolean = { - !cl.isInterfaceDeclaration && - !cl.isAbstract && cl.fields.count { f => !f.isStatic } > 1 && - cl.fields.forall(f => f.isPrivate && !f.isStatic) && - cl.fields.forall { f => - fa.writeAccesses(f).forall(p => isInitMethod(contextProvider.contextFromId(p._1).method.definedMethod)) - } - } - - def isRestrictedCreation(cl: ClassFile): Boolean = { - !cl.isInterfaceDeclaration && - cl.fields.exists { f => f.isStatic && !f.isFinal && f.fieldType == cl.thisType } && - cl.methods.filter { m => m.isConstructor }.forall { m => m.isPrivate } - } - - def isSampler(cl: ClassFile): Boolean = { - !cl.isInterfaceDeclaration && - cl.methods.filter { m => m.isConstructor }.exists { m => m.isPublic } && - cl.fields.exists { f => - f.isStatic && - f.fieldType.toJava.equals(cl.thisType.toJava) - } - } - - def isBox(cl: ClassFile, fa: FieldAccessInformation)( - implicit - contextProvider: ContextProvider, - declaredFields: DeclaredFields - ): Boolean = { - !cl.isInterfaceDeclaration && - cl.fields.count { f => !f.isStatic } == 1 && - cl.fields.count { f => !f.isFinal } == 1 && - cl.fields.exists(f => - fa.writeAccesses(f).exists(t => - cl.methods.contains(contextProvider.contextFromId(t._1).method.definedMethod) - ) - ) - } - - def isCompoundBox(cl: ClassFile, fa: FieldAccessInformation)( - implicit - contextProvider: ContextProvider, - declaredFields: DeclaredFields - ): Boolean = { - !cl.isInterfaceDeclaration && - cl.fields.count(f => - f.fieldType.isReferenceType && !f.isStatic && - !f.isFinal && fa.writeAccesses(f).exists(t => - cl.methods.contains(contextProvider.contextFromId(t._1).method.definedMethod) - ) - ) == 1 && - cl.fields.count(f => !f.isStatic && !f.fieldType.isReferenceType) + 1 == cl.fields.size - } - - def isCanopy(cl: ClassFile, fa: FieldAccessInformation)( - implicit - contextProvider: ContextProvider, - declaredFields: DeclaredFields - ): Boolean = { - !cl.isInterfaceDeclaration && - cl.fields.count { f => !f.isStatic } == 1 && - cl.fields.count { f => !f.isStatic && !f.isPublic } == 1 && - cl.fields.exists { f => - !f.isStatic && fa.writeAccesses(f).forall(p => - isInitMethod(contextProvider.contextFromId(p._1).method.definedMethod) - ) - } - } - - def isRecord(cl: ClassFile): Boolean = { - !cl.isInterfaceDeclaration && cl.fields.nonEmpty && - cl.fields.forall { f => f.isPublic } && cl.fields.exists(f => !f.isStatic) && cl.methods.forall(m => - isInitMethod(m) || isObjectMethod(m) - ) - } - - def isSink(cl: ClassFile): Boolean = { - !cl.isInterfaceDeclaration && - cl.methods.exists { m => !isInitMethod(m) } && - cl.methods.forall { m => - m.body.isEmpty || - m.body.get.instructions.filter(i => i.isInstanceOf[MethodInvocationInstruction]).forall { i => - !i.asInstanceOf[MethodInvocationInstruction].declaringClass.isClassType || - i.asInstanceOf[MethodInvocationInstruction].declaringClass.asClassType.equals(cl.thisType) - } - } - } - - def isOutline(cl: ClassFile): Boolean = { - !cl.isInterfaceDeclaration && cl.isAbstract && cl.methods.count { m => - m.body.isDefined && - m.body.get.instructions.exists { i => - i.isInstanceOf[VirtualMethodInvocationInstruction] && - i.asInstanceOf[VirtualMethodInvocationInstruction].declaringClass.equals(cl.thisType) && - cl.methods.filter { x => x.isAbstract }.exists { x => - x.name.equals(i.asInstanceOf[VirtualMethodInvocationInstruction].name) && - x.descriptor.equals(i.asInstanceOf[VirtualMethodInvocationInstruction].methodDescriptor) - } - } - } > 1 - } - - def isTrait(cl: ClassFile): Boolean = { - !cl.isInterfaceDeclaration && cl.isAbstract && - cl.fields.isEmpty && - cl.methods.exists(m => m.isAbstract) - } - - def isStateMachine(cl: ClassFile): Boolean = { - cl.methods.count(m => !isInitMethod(m)) > 1 && - cl.fields.nonEmpty && - cl.methods.forall { m => m.descriptor.parametersCount == 0 } - } - - def isPureType(cl: ClassFile): Boolean = { - ( - ( - cl.isAbstract && cl.methods.nonEmpty && - cl.methods.forall { m => m.isAbstract && !m.isStatic } - ) || - ( - cl.isInterfaceDeclaration && cl.methods.nonEmpty && cl.methods.forall { m => !m.isStatic } - ) - ) && - cl.fields.isEmpty - } - - def isAugmentedType(cl: ClassFile): Boolean = { - !cl.isInterfaceDeclaration && cl.isAbstract && - cl.methods.forall { m => m.isAbstract } && cl.fields.size >= 3 && - cl.fields.forall { f => f.isFinal && f.isStatic } && - cl.fields.map { f => f.fieldType }.toSet.size == 1 - } - - def isPseudoClass(cl: ClassFile): Boolean = { - !cl.isInterfaceDeclaration && - cl.fields.forall { f => f.isStatic } && cl.methods.nonEmpty && - cl.methods.forall { m => m.isAbstract || m.isStatic } - } - - def isImplementor[S](cl: ClassFile, theProject: Project[S]): Boolean = { - !cl.isInterfaceDeclaration && - !cl.isAbstract && cl.methods.exists { m => - m.isPublic && - !isInitMethod(m) - } && cl.methods.forall { m => - isInitMethod(m) || !m.isPublic || - (theProject.resolveMethodReference(cl.thisType, m.name, m.descriptor) match { - case Some(a) => (a.isAbstract || a.body.isEmpty) && ( - (hasExplicitSuperType(cl) && a.classFile != null && - a.classFile.thisType == cl.superclassType.get) || - cl.interfaceTypes.exists(it => a.classFile != null && a.classFile.thisType == it) - ) - case None => false - }) - } - } - - def isInitMethod(method: Method): Boolean = method.isInitializer - - def isOverrider[S](cl: ClassFile, theProject: Project[S]): Boolean = { - !cl.isInterfaceDeclaration && !cl.isAbstract && - cl.methods.exists { m => !isInitMethod(m) } && cl.methods.forall { m => - m.isInitializer || - (theProject.resolveMethodReference(cl.thisType, m.name, m.descriptor) match { - case Some(a) => (!a.isAbstract && a.body.isDefined && m.body.nonEmpty) && ( - (hasExplicitSuperType(cl) && a.classFile != null && - a.classFile.thisType == cl.superclassType.get) || - cl.interfaceTypes.exists(it => a.classFile != null && a.classFile.thisType == it) - ) - case None => false - }) - } - } - - def isExtender[S](cl: ClassFile)(implicit theProject: Project[S]): Boolean = { - !cl.isInterfaceDeclaration && - cl.methods.exists(m => !isInitMethod(m)) && hasExplicitSuperType(cl) && cl.methods.forall { m => - isInitMethod(m) || - theProject.resolveMethodReference(cl.thisType, m.name, m.descriptor).isEmpty - } - } - - def isDataManager[S](cl: ClassFile)(implicit theProject: Project[S]): Boolean = { - !cl.isInterfaceDeclaration && - !cl.isAbstract && - cl.fields.nonEmpty && - cl.methods.count(m => !isInitMethod(m) && !isObjectMethod(m)) > 1 && - cl.methods.filter(m => !isInitMethod(m) && !isObjectMethod(m)).forall { m => isSetter(m) || isGetter(m) } - } - - class AnalysisDomain[S](val project: Project[S], val method: Method) - extends CorrelationalDomain - with domain.DefaultHandlingOfMethodResults - with domain.IgnoreSynchronization - with domain.ThrowAllPotentialExceptionsConfiguration - with domain.l0.DefaultTypeLevelFloatValues - with domain.l0.DefaultTypeLevelDoubleValues - with domain.l0.TypeLevelFieldAccessInstructions - with domain.l0.TypeLevelInvokeInstructions - with domain.l0.TypeLevelDynamicLoads - with domain.l1.DefaultReferenceValuesBinding - with domain.l1.DefaultIntegerRangeValues - with domain.l1.DefaultLongValues - with domain.l1.ConcretePrimitiveValuesConversions - with domain.l1.LongValuesShiftOperators - with domain.TheProject - with domain.TheMethod - with domain.RecordDefUse - - def isGetter[S](method: Method)(implicit theProject: Project[S]): Boolean = { - if (!method.isPublic || method.returnType.isVoidType || method.body.isEmpty || - !method.body.get.instructions.exists { i => i.isInstanceOf[FieldReadAccess] } - ) { - return false - } - val instructions = method.body.get.foldLeft(Map.empty[PC, Instruction])((m, pc, i) => m + ((pc, i))) - val result = BaseAI(method, new AnalysisDomain(theProject, method)) - val returns = instructions.filter(i => i._2.isInstanceOf[ReturnValueInstruction]) - - returns.forall(r => - result.domain.operandOrigin(r._1, 0).forall { u => - instructions.contains(u) && (instructions(u).isInstanceOf[FieldReadAccess] || - instructions(u).isInstanceOf[ArrayLoadInstruction] || - instructions(u).isInstanceOf[LoadConstantInstruction[?]]) - } - ) - } - - def isSetter[S](method: Method)(implicit theProject: Project[S]): Boolean = { - if (!method.isPublic || !method.returnType.isVoidType || - method.descriptor.parametersCount == 0 || method.body.isEmpty || - (method.body.isDefined && method.body.isEmpty) || - !method.body.get.instructions.exists { i => i.isInstanceOf[FieldWriteAccess] } - ) { - return false - } - val instructions = method.body.get.foldLeft(Map.empty[PC, Instruction])((m, pc, i) => m + ((pc, i))) - val result = BaseAI(method, new AnalysisDomain(theProject, method)) - val puts = instructions.filter(i => i._2.isInstanceOf[FieldWriteAccess]) - - puts.forall(p => - (p._2.isInstanceOf[PUTSTATIC] && - result.domain.operandOrigin(p._1, 0).forall { x => - x < 0 || (instructions.contains(x) && - instructions(x).isInstanceOf[LoadConstantInstruction[?]]) - }) || - (p._2.isInstanceOf[PUTFIELD] && - result.domain.operandOrigin(p._1, 1).forall { x => - x < 0 || (instructions.contains(x) && - instructions(x).isInstanceOf[LoadConstantInstruction[?]]) - }) - ) - } - -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/RecursiveDataStructures.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/RecursiveDataStructures.scala deleted file mode 100644 index 72f9d0d7f0..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/RecursiveDataStructures.scala +++ /dev/null @@ -1,74 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries - -import org.opalj.br.ClassType -import org.opalj.br.analyses.Project -import org.opalj.graphs.UnidirectionalGraph - -/** - * Identifies recursive data structures. Such data-structure can often significantly limit - * the scalability of analyses. - * - * @author Michael Eichberg - */ -class RecursiveDataStructures(implicit hermes: HermesConfig) extends FeatureQuery { - - override def featureIDs: IndexedSeq[String] = { - IndexedSeq( - /*0*/ "Self-recursive Data Structure", - /*1*/ "Mutually-recursive Data Structure\n2 Types", - /*2*/ "Mutually-recursive Data Structure\n3 Types", - /*3*/ "Mutually-recursive Data Structure\n4 Types", - /*4*/ "Mutually-recursive Data Structure\nmore than 4 Types" - ) - } - - override def apply[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(da.ClassFile, S)] - ): IterableOnce[Feature[S]] = { - - import project.classHierarchy.getClassType - - val g = new UnidirectionalGraph(ClassType.classTypesCount)() - - val locations = Array.fill(featureIDs.size)(new LocationsContainer[S]) - - // 1. create graph - for { - classFile <- project.allProjectClassFiles - if !isInterrupted() - classType = classFile.thisType - field <- classFile.fields - fieldType = field.fieldType - } { - if (fieldType.isClassType) { - g.add(classType.id, fieldType.asClassType.id) - } else if (fieldType.isArrayType) { - val elementType = fieldType.asArrayType.elementType - if (elementType.isClassType) { - g.add(classType.id, elementType.asClassType.id) - } - } - } - - // 2. search for strongly connected components - for { - scc <- g.sccs(filterSingletons = true) - if !isInterrupted() - /* An scc is never empty! */ - sccCategory = Math.min(scc.size, 5) - 1 - classTypeID <- scc - classType = getClassType(classTypeID) - } { - locations(sccCategory) += ClassFileLocation(project, classType) - } - - for { (locations, index) <- locations.iterator.zipWithIndex } yield { - Feature[S](featureIDs(index), locations) - } - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/ReflectionAPIUsage.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/ReflectionAPIUsage.scala deleted file mode 100644 index bb0559ead8..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/ReflectionAPIUsage.scala +++ /dev/null @@ -1,131 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries - -import org.opalj.br.ClassType -import org.opalj.br.MethodDescriptor -import org.opalj.br.MethodDescriptor.JustReturnsObject -import org.opalj.hermes.queries.util.APIFeature -import org.opalj.hermes.queries.util.APIFeatureGroup -import org.opalj.hermes.queries.util.APIFeatureQuery -import org.opalj.hermes.queries.util.InstanceAPIMethod -import org.opalj.hermes.queries.util.StaticAPIMethod - -/** - * Counts the number of certain calls to the Java Reflection API. - * - * @author Michael Reif - */ -class ReflectionAPIUsage(implicit hermes: HermesConfig) extends APIFeatureQuery { - - override val apiFeatures: List[APIFeature] = { - - val Class = ClassType.Class - val Field = ClassType("java/lang/reflect/Field") - val AccessibleObject = ClassType("java/lang/reflect/AccessibleObject") - val Constructor = ClassType("java/lang/reflect/Constructor") - val Method = ClassType("java/lang/reflect/Method") - val MethodHandle = ClassType("java/lang/invoke/MethodHandle") - val MethodHandles = ClassType("java/lang/invoke/MethodHandles") - // TODO val MethodHandles_Lookup = ClassType("java/lang/invoke/MethodHandles$Lookup") - val Proxy = ClassType("java/lang/reflect/Proxy") - - List( - StaticAPIMethod(Class, "forName"), - - // reflective instance creation - APIFeatureGroup( - List( - InstanceAPIMethod(Class, "newInstance", JustReturnsObject), - InstanceAPIMethod(Constructor, "newInstance", "([Ljava/lang/Object;)Ljava/lang/Object;") - ), - "reflective instance creation" - ), - - // reflective field write api - APIFeatureGroup( - List( - InstanceAPIMethod(Field, "set", "(Ljava/lang/Object;Ljava/lang/Object;)V"), - InstanceAPIMethod(Field, "setBoolean", "(Ljava/lang/Object;Z)V"), - InstanceAPIMethod(Field, "setByte", "(Ljava/lang/Object;B)V"), - InstanceAPIMethod(Field, "setChar", "(Ljava/lang/Object;C)V"), - InstanceAPIMethod(Field, "setDouble", "(Ljava/lang/Object;D)V"), - InstanceAPIMethod(Field, "setFloat", "(Ljava/lang/Object;F)V"), - InstanceAPIMethod(Field, "setInt", "(Ljava/lang/Object;I)V"), - InstanceAPIMethod(Field, "setLong", "(Ljava/lang/Object;J)V"), - InstanceAPIMethod(Field, "setShort", "(Ljava/lang/Object;S)V") - ), - "reflective field write" - ), - - // reflective field read api - APIFeatureGroup( - List( - InstanceAPIMethod(Field, "get"), - InstanceAPIMethod(Field, "getBoolean"), - InstanceAPIMethod(Field, "getByte"), - InstanceAPIMethod(Field, "getChar"), - InstanceAPIMethod(Field, "getDouble"), - InstanceAPIMethod(Field, "getFloat"), - InstanceAPIMethod(Field, "getInt"), - InstanceAPIMethod(Field, "getLong"), - InstanceAPIMethod(Field, "getShort") - ), - "reflective field read" - ), - - // making fields accessible using "setAccessible" - APIFeatureGroup( - List( - InstanceAPIMethod(Field, "setAccessible", s"([${AccessibleObject.toJVMTypeName}Z)V"), - InstanceAPIMethod(Field, "setAccessible", "(Z)V") - ), - "makes fields accessible" - ), - - // setting methods or constructors accessible - APIFeatureGroup( - List( - InstanceAPIMethod(Method, "setAccessible", s"([${AccessibleObject.toJVMTypeName}Z)V"), - InstanceAPIMethod(Method, "setAccessible", MethodDescriptor("(Z)V")), - InstanceAPIMethod(Constructor, "setAccessible", s"([${AccessibleObject.toJVMTypeName}Z)V"), - InstanceAPIMethod(Constructor, "setAccessible", MethodDescriptor("(Z)V")) - ), - "makes methods or constructors accessible" - ), - - // set an AccessibleObject accessible - APIFeatureGroup( - List( - InstanceAPIMethod(AccessibleObject, "setAccessible", s"([${AccessibleObject.toJVMTypeName}Z)V"), - InstanceAPIMethod(AccessibleObject, "setAccessible", "(Z)V") - ), - "makes an AccessibleObject accessible\n(exact type unknown)" - ), - - // reflective method invocation - InstanceAPIMethod( - Method, - "invoke", - MethodDescriptor(s"(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;") - ), - - ////////////////////////////////////////////////////////////////////////////////////////////// - //////////////////////////////// new Reflection primitives /////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////////////////////// - - StaticAPIMethod(MethodHandles, "lookup"), - StaticAPIMethod(MethodHandles, "publicLookup"), - APIFeatureGroup( - List( - InstanceAPIMethod(MethodHandle, "invokeExact"), - InstanceAPIMethod(MethodHandle, "invoke"), - InstanceAPIMethod(MethodHandle, "invokeWithArguments") - ), - "method handle invocation" - ), - StaticAPIMethod(Proxy, "newProxyInstance") - ) - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/ReflectiveFieldWrites.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/ReflectiveFieldWrites.scala deleted file mode 100644 index a81596d6b7..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/ReflectiveFieldWrites.scala +++ /dev/null @@ -1,37 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries - -import org.opalj.br.ClassType -import org.opalj.hermes.queries.util.APIFeature -import org.opalj.hermes.queries.util.APIFeatureQuery -import org.opalj.hermes.queries.util.InstanceAPIMethod - -/** - * Groups features that use reflection to modify field values. - * - * @author Dominik Helm - */ -class ReflectiveFieldWrites(implicit hermes: HermesConfig) extends APIFeatureQuery { - - val FieldT = ClassType("java/lang/reflect/Field") - val Lookup = ClassType.MethodHandles$Lookup - - override val apiFeatures: List[APIFeature] = { - List( - InstanceAPIMethod(FieldT, "set", featureID = "FieldSet"), - InstanceAPIMethod(FieldT, "setBoolean", featureID = "FieldSetBoolean"), - InstanceAPIMethod(FieldT, "setByte", featureID = "FieldSetByte"), - InstanceAPIMethod(FieldT, "setChar", featureID = "FieldSetChar"), - InstanceAPIMethod(FieldT, "setDouble", featureID = "FieldSetDouble"), - InstanceAPIMethod(FieldT, "setFloat", featureID = "FieldSetFloat"), - InstanceAPIMethod(FieldT, "setInt", featureID = "FieldSetInt"), - InstanceAPIMethod(FieldT, "setLong", featureID = "FieldSetLong"), - InstanceAPIMethod(FieldT, "setShort", featureID = "FieldSetShort"), - InstanceAPIMethod(Lookup, "findStaticSetter", featureID = "MethodHandleStaticSetter"), - InstanceAPIMethod(Lookup, "findSetter", featureID = "MethodHandleSetter") - ) - } - -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/SizeOfInheritanceTree.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/SizeOfInheritanceTree.scala deleted file mode 100644 index b14b83f9e1..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/SizeOfInheritanceTree.scala +++ /dev/null @@ -1,80 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries - -import org.opalj.br.analyses.Project -import org.opalj.da.ClassFile -import org.opalj.hermes.Feature -import org.opalj.hermes.FeatureQuery -import org.opalj.hermes.ProjectConfiguration - -/** - * Computes the size of the inheritance tree for each class of a project and then assigns the - * class to its respective category. - * - * Here, the size is measured by counting all super types (interfaces and classes!) - * - * @author Ben Hermann - * @author Michael Eichberg - */ -class SizeOfInheritanceTree(implicit hermes: HermesConfig) extends FeatureQuery { - - override val featureIDs: List[String] = { - List( - /*0*/ "Very Small Inheritance Tree", // [0 ... 1 x CategorySize) - /*1*/ "Small Inheritance Tree", // [1 x CategorySize ... 2 x CategorySize) - /*2*/ "Medium Inheritance Tree", // [2 x CategorySize ... 3 x CategorySize) - /*3*/ "Large Inheritance Tree", // [3 x CategorySize ... 4 x CategorySize) - /*4*/ "Very Large Inheritance Tree", // [4 x CategorySize ... 5 x CategorySize) - /*5*/ "Huge Inheritance Tree", // [5 x CategorySize ... 6 x Int.MaxValue) - - /*6*/ "Size of the Inheritance Tree Unknown" // The project is not complete - ) - } - - override def apply[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(ClassFile, S)] - ): IterableOnce[Feature[S]] = { - val classHierarchy = project.classHierarchy - import classHierarchy.isSupertypeInformationComplete - - /* Determines the size for each category. */ - val CategorySize: Int = 3 // TODO read from project config file - - val features = Array.fill[LocationsContainer[S]](featureIDs.size)(new LocationsContainer[S]) - var classCount = 0 - var sumOfSizeOfInheritanceTrees = 0 - classHierarchy.foreachKnownType { t => - if (project.isProjectType(t)) { - val l = ClassFileLocation(project, t) - classHierarchy.supertypeInformation(t) match { - case Some(supertypeInformation) if isSupertypeInformationComplete(t) => - val sizeOfInheritanceTree = supertypeInformation.size - features(Math.min(sizeOfInheritanceTree / CategorySize, 5)) += l - classCount += 1 - sumOfSizeOfInheritanceTrees += sizeOfInheritanceTree - case _ /* None or */ => - features(6) += l - } - } - } - - projectConfiguration.addStatistic( - "⟨SizeOfInheritanceTree⟩", - if (classCount != 0) { - sumOfSizeOfInheritanceTrees.toDouble / classCount.toDouble - } else { - 0d - } - ) - - for { (featureID, featureIDIndex) <- featureIDs.iterator.zipWithIndex } yield { - Feature[S](featureID, features(featureIDIndex)) - } - - } - -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/SystemAPIUsage.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/SystemAPIUsage.scala deleted file mode 100644 index 36d6651381..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/SystemAPIUsage.scala +++ /dev/null @@ -1,130 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries - -import org.opalj.br.ClassType -import org.opalj.hermes.queries.util.APIFeature -import org.opalj.hermes.queries.util.APIFeatureGroup -import org.opalj.hermes.queries.util.APIFeatureQuery -import org.opalj.hermes.queries.util.InstanceAPIMethod -import org.opalj.hermes.queries.util.StaticAPIMethod - -/** - * Counts the calls to specific JVM are system features. - * - * @author Michael Reif - */ -class SystemAPIUsage(implicit hermes: HermesConfig) extends APIFeatureQuery { - - override val apiFeatures: List[APIFeature] = { - - object Sound { - val Clip = ClassType("javax/sound/sampled/Clip") - val DataLine = ClassType("javax/sound/sampled/DataLine") - val TargetDataLine = ClassType("javax/sound/sampled/TargetDataLine") - val SourceDataLine = ClassType("javax/sound/sampled/SourceDataLine") - val MediaPlayer = ClassType("javafx/scene/media/MediaPlayer") - } - - object Network { - val Socket = ClassType("java/net/Socket") - val SSLSocket = ClassType("javax/net/ssl/SSLSocket") - val ServerSocket = ClassType("java/net/ServerSocket") - val SSLServerSocket = ClassType("javax/net/ssl/SSLServerSocket") - val DatagramSocket = ClassType("javax/net/DatagramSocket") - val MulticastSocket = ClassType("javax/net/MulticastSocket") - - val DatagramPacket = ClassType("java/net/DatagramPacket") - val InetAddress = ClassType("java/net/InetAddress") - - val URL = ClassType("java/net/URL") - val URI = ClassType("java/net/URI") - val URLConnection = ClassType("java/net/URLConnection") - } - - val constructor = "" - - val Runtime = ClassType("java/lang/Runtime") - val System = ClassType.System - val ProcessBuilder = ClassType("java/lang/ProcessBuilder") - - List( - // PROCESS - - APIFeatureGroup( - List( - InstanceAPIMethod(Runtime, "exec"), - InstanceAPIMethod(ProcessBuilder, "start") - ), - "Process" - ), - - // JVM EXIT - - APIFeatureGroup( - List( - InstanceAPIMethod(Runtime, "exit"), - InstanceAPIMethod(Runtime, "halt"), - StaticAPIMethod(System, "exit") - ), - "JVM exit" - ), - - // NATIVE LIBRARIES - - APIFeatureGroup( - List( - InstanceAPIMethod(Runtime, "load"), - InstanceAPIMethod(Runtime, "loadLibrary"), - StaticAPIMethod(System, "load"), - StaticAPIMethod(System, "loadLibrary") - ), - "Native Libraries" - ), - - // SECURITY_MANAGER - - StaticAPIMethod(System, "getSecurityManager"), - StaticAPIMethod(System, "setSecurityManager"), - - // ENV - - APIFeatureGroup( - List( - StaticAPIMethod(System, "getenv") - ), - "Environment" - ), - - // SOUND - - APIFeatureGroup( - List( - InstanceAPIMethod(Sound.Clip, "start"), - InstanceAPIMethod(Sound.DataLine, "start"), - InstanceAPIMethod(Sound.TargetDataLine, "start"), - InstanceAPIMethod(Sound.SourceDataLine, "start"), - InstanceAPIMethod(Sound.MediaPlayer, "play") - ), - "Sound" - ), - - // NETWORK - - APIFeatureGroup( - List( - InstanceAPIMethod(Network.Socket, constructor), - InstanceAPIMethod(Network.ServerSocket, constructor), - InstanceAPIMethod(Network.DatagramSocket, constructor), - InstanceAPIMethod(Network.DatagramPacket, constructor), - InstanceAPIMethod(Network.MulticastSocket, constructor), - InstanceAPIMethod(Network.SSLSocket, constructor), - InstanceAPIMethod(Network.SSLServerSocket, constructor), - InstanceAPIMethod(Network.InetAddress, constructor) - ), - "Network sockets" - ) - ) - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/ThreadAPIUsage.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/ThreadAPIUsage.scala deleted file mode 100644 index a878c01b8a..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/ThreadAPIUsage.scala +++ /dev/null @@ -1,69 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries - -import org.opalj.br.ClassType -import org.opalj.hermes.queries.util.APIFeature -import org.opalj.hermes.queries.util.APIFeatureGroup -import org.opalj.hermes.queries.util.APIFeatureQuery -import org.opalj.hermes.queries.util.InstanceAPIMethod -import org.opalj.hermes.queries.util.StaticAPIMethod - -/** - * Captures the usage of Thread-related API usage. - * - * @author Ben Hermann - */ -class ThreadAPIUsage(implicit hermes: HermesConfig) extends APIFeatureQuery { - override val apiFeatures: List[APIFeature] = { - - val Thread = ClassType("java/lang/Thread") - val ThreadGroup = ClassType("java/lang/ThreadGroup") - val Object = ClassType.Object - - val constructor = "" - - List( - // PROCESS - - APIFeatureGroup( - List( - InstanceAPIMethod(Object, "notify"), - InstanceAPIMethod(Object, "notifyAll"), - InstanceAPIMethod(Object, "wait") - ), - "Object-based Thread Notification" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(Thread, constructor), - InstanceAPIMethod(Thread, "interrupt"), - InstanceAPIMethod(Thread, "join"), - InstanceAPIMethod(Thread, "run"), - StaticAPIMethod(Thread, "sleep"), - InstanceAPIMethod(Thread, "start"), - // Deprecated members - InstanceAPIMethod(Thread, "destroy"), - InstanceAPIMethod(Thread, "resume"), - InstanceAPIMethod(Thread, "stop"), - InstanceAPIMethod(Thread, "suspend") - ), - "Usage of Thread API" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(ThreadGroup, constructor), - InstanceAPIMethod(ThreadGroup, "destroy"), - InstanceAPIMethod(ThreadGroup, "interrupt"), - // Deprecated members - InstanceAPIMethod(ThreadGroup, "resume"), - InstanceAPIMethod(ThreadGroup, "stop"), - InstanceAPIMethod(ThreadGroup, "suspend") - ), - "Usage of ThreadGroup API" - ) - ) - } - -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/TrivialReflectionUsage.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/TrivialReflectionUsage.scala deleted file mode 100644 index c75077fc5b..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/TrivialReflectionUsage.scala +++ /dev/null @@ -1,86 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries - -import org.opalj.ai.BaseAI -import org.opalj.ai.domain.l1.DefaultDomainWithCFGAndDefUse -import org.opalj.br.ClassType -import org.opalj.br.MethodDescriptor -import org.opalj.br.MethodWithBody -import org.opalj.br.analyses.MethodInfo -import org.opalj.br.analyses.Project -import org.opalj.br.instructions.Instruction -import org.opalj.br.instructions.INVOKESTATIC -import org.opalj.da.ClassFile - -/** - * Counts (non-)trivial usages of "Class.forName(...)". - * - * @author Michael Reif - * @author Michael Eichberg - */ -class TrivialReflectionUsage(implicit hermes: HermesConfig) extends FeatureQuery { - - final val TrivialForNameUsage = "Trivial Class.forName Usage" - final val NonTrivialForNameUsage = "Nontrivial Class.forName Usage" - - override val featureIDs: List[String] = List(TrivialForNameUsage, NonTrivialForNameUsage) - - override def apply[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(ClassFile, S)] - ): IterableOnce[Feature[S]] = { - val Class = ClassType.Class - val ForName1MD = MethodDescriptor("(Ljava/lang/String;)Ljava/lang/Class;") - val ForName3MD = - MethodDescriptor("(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;") - - val trivialLocations = new LocationsContainer[S] - val nontrivialLocations = new LocationsContainer[S] - - project.parForeachMethodWithBody(isInterrupted = () => this.isInterrupted()) { mi => - val MethodInfo(source, m @ MethodWithBody(body)) = mi: @unchecked - val classForNameCalls = body.collect({ - case i @ INVOKESTATIC(Class, false, "forName", ForName1MD | ForName3MD) => i - }: PartialFunction[Instruction, INVOKESTATIC]) - if (classForNameCalls.nonEmpty) { - val aiResult = BaseAI(m, new DefaultDomainWithCFGAndDefUse(project, m)) - val methodLocation = MethodLocation(source, m) - for { - pcAndInstruction <- classForNameCalls - instruction = pcAndInstruction.value - pc = pcAndInstruction.pc - classNameParameterIndex = instruction.parametersCount - 1 - operands = aiResult.operandsArray(pc) // if i is dead... opeands is null - if operands ne null - classNameParameter = operands(classNameParameterIndex) - } { - classNameParameter match { - case aiResult.domain.StringValue(_) => - trivialLocations += InstructionLocation(methodLocation, pc) - case aiResult.domain.MultipleReferenceValues(classNameParameters) => - val classNames = classNameParameters.collect { - case aiResult.domain.StringValue(className) => className - } - // check if we have a concrete string in all cases.. - val locations = if (classNames.size == classNameParameters.size) { - trivialLocations - } else { - nontrivialLocations - } - locations += InstructionLocation(methodLocation, pc) - case _ => - nontrivialLocations += InstructionLocation(methodLocation, pc) - } - } - } - } - - List( - Feature[S](TrivialForNameUsage, trivialLocations), - Feature[S](NonTrivialForNameUsage, nontrivialLocations) - ) - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/UnsafeAPIUsage.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/UnsafeAPIUsage.scala deleted file mode 100644 index 33269ff7cc..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/UnsafeAPIUsage.scala +++ /dev/null @@ -1,191 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries - -import org.opalj.br.ClassType -import org.opalj.br.MethodDescriptor -import org.opalj.hermes.queries.util.APIFeature -import org.opalj.hermes.queries.util.APIFeatureGroup -import org.opalj.hermes.queries.util.APIFeatureQuery -import org.opalj.hermes.queries.util.InstanceAPIMethod -import org.opalj.hermes.queries.util.StaticAPIMethod - -/** - * Groups features that rely on the Unsafe API. (sun.misc.Unsafe) - * - * @note Feature groups are taken from and are further discussed in the following paper: - * "Use at Your Own Risk: The Java Unsafe API in the Wild" - * by Luis Mastrangelo et al. - * - * @author Michael Reif - */ -class UnsafeAPIUsage(implicit hermes: HermesConfig) extends APIFeatureQuery { - - override val apiFeatures: List[APIFeature] = { - - val Unsafe = ClassType("sun/misc/Unsafe") - - List( - StaticAPIMethod(Unsafe, "getUnsafe", MethodDescriptor("()Lsun/misc/Unsafe;")), - APIFeatureGroup( - List( - InstanceAPIMethod(Unsafe, "allocateInstance") - ), - "Unsafe - Alloc" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(Unsafe, "arrayIndexScale"), - InstanceAPIMethod(Unsafe, "arrayBaseOffset") - ), - "Unsafe - Array" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(Unsafe, "compareAndSwapObject"), - InstanceAPIMethod(Unsafe, "compareAndSwapInt"), - InstanceAPIMethod(Unsafe, "compareAndSwapLong") - ), - "Unsafe - compareAndSwap" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(Unsafe, "shouldBeInitialized"), - InstanceAPIMethod(Unsafe, "defineAnonymousClass"), - InstanceAPIMethod(Unsafe, "ensureClassInitialized"), - InstanceAPIMethod(Unsafe, "defineClass") - ), - "Unsafe - Class" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(Unsafe, "loadFence"), - InstanceAPIMethod(Unsafe, "storeFence"), - InstanceAPIMethod(Unsafe, "fullFence") - ), - "Unsafe - Fence" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(Unsafe, "getAndSetObject"), - InstanceAPIMethod(Unsafe, "getAndSetLong"), - InstanceAPIMethod(Unsafe, "getAndSetInt"), - InstanceAPIMethod(Unsafe, "getAndAddLong"), - InstanceAPIMethod(Unsafe, "getAndAddInt") - ), - "Unsafe - Fetch & Add" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(Unsafe, "setMemory"), - InstanceAPIMethod(Unsafe, "copyMemory") - ), - "Unsafe - Heap" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(Unsafe, "getByte"), - InstanceAPIMethod(Unsafe, "getShort"), - InstanceAPIMethod(Unsafe, "getChar"), - InstanceAPIMethod(Unsafe, "getInt"), - InstanceAPIMethod(Unsafe, "getLong"), - InstanceAPIMethod(Unsafe, "getFloat"), - InstanceAPIMethod(Unsafe, "getDouble"), - InstanceAPIMethod(Unsafe, "getObject"), - InstanceAPIMethod(Unsafe, "getBoolean") - ), - "Unsafe - Heap Get" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(Unsafe, "putByte"), - InstanceAPIMethod(Unsafe, "putShort"), - InstanceAPIMethod(Unsafe, "putChar"), - InstanceAPIMethod(Unsafe, "putInt"), - InstanceAPIMethod(Unsafe, "putLong"), - InstanceAPIMethod(Unsafe, "putFloat"), - InstanceAPIMethod(Unsafe, "putDouble"), - InstanceAPIMethod(Unsafe, "putObject"), - InstanceAPIMethod(Unsafe, "putBoolean") - ), - "Unsafe - Heap Put" - ), - APIFeatureGroup(List(InstanceAPIMethod(Unsafe, "getLoadAverage")), "Misc"), - APIFeatureGroup( - List( - InstanceAPIMethod(Unsafe, "tryMonitorEnter"), - InstanceAPIMethod(Unsafe, "monitorExit"), - InstanceAPIMethod(Unsafe, "monitorEnter") - ), - "Unsafe - Monitor" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(Unsafe, "setMemory"), - InstanceAPIMethod(Unsafe, "reallocateMemory"), - InstanceAPIMethod(Unsafe, "putAddress"), - InstanceAPIMethod(Unsafe, "pageSize"), - InstanceAPIMethod(Unsafe, "getAddress"), - InstanceAPIMethod(Unsafe, "freeMemory"), - InstanceAPIMethod(Unsafe, "copyMemory"), - InstanceAPIMethod(Unsafe, "addressSize") - ), - "Unsafe - Off-Heap" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(Unsafe, "fieldOffset"), - InstanceAPIMethod(Unsafe, "staticFieldOffset"), - InstanceAPIMethod(Unsafe, "staticFieldBase"), - InstanceAPIMethod(Unsafe, "objectFieldOffset") - ), - "Unsafe - Offset" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(Unsafe, "putOrderedObject"), - InstanceAPIMethod(Unsafe, "putOrderedLong"), - InstanceAPIMethod(Unsafe, "putOrderedInt") - ), - "Unsafe - Ordered Put" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(Unsafe, "park"), - InstanceAPIMethod(Unsafe, "unpark") - ), - "Unsafe - Park" - ), - APIFeatureGroup(List(InstanceAPIMethod(Unsafe, "throwException")), "Unsafe - Throw"), - APIFeatureGroup( - List( - InstanceAPIMethod(Unsafe, "getByteVolatile"), - InstanceAPIMethod(Unsafe, "getShortVolatile"), - InstanceAPIMethod(Unsafe, "getCharVolatile"), - InstanceAPIMethod(Unsafe, "getIntVolatile"), - InstanceAPIMethod(Unsafe, "getLongVolatile"), - InstanceAPIMethod(Unsafe, "getFloatVolatile"), - InstanceAPIMethod(Unsafe, "getDoubleVolatile"), - InstanceAPIMethod(Unsafe, "getObjectVolatile"), - InstanceAPIMethod(Unsafe, "getBooleanVolatile") - ), - "Unsafe - Volatile Get" - ), - APIFeatureGroup( - List( - InstanceAPIMethod(Unsafe, "putByteVolatile"), - InstanceAPIMethod(Unsafe, "putShortVolatile"), - InstanceAPIMethod(Unsafe, "putCharVolatile"), - InstanceAPIMethod(Unsafe, "putIntVolatile"), - InstanceAPIMethod(Unsafe, "putLongVolatile"), - InstanceAPIMethod(Unsafe, "putFloatVolatile"), - InstanceAPIMethod(Unsafe, "putDoubleVolatile"), - InstanceAPIMethod(Unsafe, "putObjectVolatile"), - InstanceAPIMethod(Unsafe, "putBooleanVolatile") - ), - "Unsafe - Volatile Put" - ) - ) - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Classloading.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Classloading.scala deleted file mode 100644 index 83efb18b26..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Classloading.scala +++ /dev/null @@ -1,95 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries -package jcg - -import scala.collection.immutable.ArraySeq - -import org.opalj.br.ClassType -import org.opalj.br.MethodDescriptor -import org.opalj.br.MethodWithBody -import org.opalj.br.ReferenceType -import org.opalj.br.analyses.Project -import org.opalj.br.instructions.Instruction -import org.opalj.br.instructions.INVOKEVIRTUAL -import org.opalj.da.ClassFile -import org.opalj.tac.DUVar -import org.opalj.tac.LazyTACUsingAIKey -import org.opalj.value.KnownTypedValue - -/** - * Groups test case features that perform classloading. - * - * @note The features represent the __Classloading__ test cases from the Call Graph Test Project - * (JCG). - * - * @author Dominik Helm - */ -class Classloading(implicit hermes: HermesConfig) extends DefaultFeatureQuery { - - type V = DUVar[KnownTypedValue] - - // required types and descriptors - val ClassLoaderT = ClassType.ClassLoader - - val loadClassMD = MethodDescriptor(ClassType.String, ClassType.Class) - - override def featureIDs: Seq[String] = { - Seq( - "CL1+CL2+CL3", /* 0 --- Standard java classloaders */ - "CL4" /* 1 --- Custom classloaders */ - ) - } - - override def evaluate[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(ClassFile, S)] - ): IndexedSeq[LocationsContainer[S]] = { - - implicit val locations: Array[LocationsContainer[S]] = - Array.fill(featureIDs.size)(new LocationsContainer[S]) - - val tacai = project.get(LazyTACUsingAIKey) - - val classHierarchy = project.classHierarchy - - val hasCustomClassLoaders = - project.allClassFiles exists { cf => - classHierarchy.isSubtypeOf(cf.thisType, ClassLoaderT) && - !(cf.thisType.fqn.startsWith("java/") || - cf.thisType.fqn.startsWith("sun/") || - cf.thisType.fqn.startsWith("com/sun") || - cf.thisType.fqn.startsWith("javax/management/") || - cf.thisType.fqn.startsWith("jdk/nashorn/internal/runtime/")) - } - - for { - (classFile, source) <- project.projectClassFilesWithSources - if !isInterrupted() - classFileLocation = ClassFileLocation(source, classFile) - case method @ MethodWithBody(body) <- classFile.methods - methodLocation = MethodLocation(classFileLocation, method) - pcAndInvocation <- body.collect({ - case i @ INVOKEVIRTUAL(declClass, "loadClass", _) - if classHierarchy.isSubtypeOf(declClass, ClassLoaderT) => i - }: PartialFunction[Instruction, Instruction]) - } { - tacai(method) - val pc = pcAndInvocation.pc - val l = InstructionLocation(methodLocation, pc) - - if (hasCustomClassLoaders) - locations(1) += l // potential custom classloader - else - locations(0) += l // standard java classloader - } - - ArraySeq.unsafeWrapArray(locations) - } - - def isStandardClassLoader(receiverType: ReferenceType): Boolean = { - receiverType.asClassType.fqn.startsWith("java/") - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/DynamicProxy.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/DynamicProxy.scala deleted file mode 100644 index 954b6cf5e8..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/DynamicProxy.scala +++ /dev/null @@ -1,29 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries -package jcg - -import org.opalj.br.ClassType -import org.opalj.hermes.queries.util.APIFeature -import org.opalj.hermes.queries.util.APIFeatureQuery -import org.opalj.hermes.queries.util.StaticAPIMethod - -/** - * Queries the usage of the proxy api (java.lang.reflect.Proxy). - * - * @author Florian Kuebler - */ -class DynamicProxy(implicit hermes: HermesConfig) extends APIFeatureQuery { - - override val apiFeatures: List[APIFeature] = { - - List( - StaticAPIMethod( - ClassType("java/lang/reflect/Proxy"), - "newProxyInstance", - featureID = "DP1" - ) - ) - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/JVMCalls.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/JVMCalls.scala deleted file mode 100644 index 506e429282..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/JVMCalls.scala +++ /dev/null @@ -1,97 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries -package jcg - -import scala.collection.immutable.ArraySeq - -import org.opalj.br.ClassType -import org.opalj.br.analyses.Project -import org.opalj.br.instructions.Instruction -import org.opalj.br.instructions.VirtualMethodInvocationInstruction -import org.opalj.da.ClassFile - -/** - * Groups test case features for features that explicitly must be modeled to imitate the JVM's - * behaviour, i.e., callbacks that are registered and then (potentially) called from the JVM. - * - * @note The features represent the __JVMCalls__ test cases from the Call Graph Test Project (JCG). - * - * @author Michael Reif - */ -class JVMCalls(implicit hermes: HermesConfig) extends DefaultFeatureQuery { - - val Runtime = ClassType("java/lang/Runtime") - val Thread = ClassType("java/lang/Thread") - - override def featureIDs: Seq[String] = { - Seq( - "JVMC1", /* 0 --- Runtime.addShutdownHook */ - "JVMC2", /* 1 --- finalizer */ - "JVMC3", /* 2 --- Thread.start */ - "JVMC4", /* 3 --- Thread.exit */ - "JVMC5" /* 4 ---Thread.setUncaughtExceptionHandler */ - ) - } - - override def evaluate[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(ClassFile, S)] - ): IndexedSeq[LocationsContainer[S]] = { - - val locations = Array.fill(featureIDs.size)(new LocationsContainer[S]) - - // Setup possible types - - val classHierarchy = project.classHierarchy - import classHierarchy.allSubtypes - import project.isProjectType - - val threadSubtypes = allSubtypes(Thread, true).filter(isProjectType) - val relevantTypes = threadSubtypes + Runtime - - for { - (classFile, source) <- project.projectClassFilesWithSources - if !isInterrupted() - classFileLocation = ClassFileLocation(source, classFile) - method <- classFile.methods - methodLocation = MethodLocation(classFileLocation, method) - } { - if (method.isNotStatic && method.isPublic && method.name == "finalize") { - locations(1) += methodLocation - } else if (method.body.nonEmpty) { - val body = method.body.get - val pcAndInvocation = body.collect({ case mii: VirtualMethodInvocationInstruction => - mii - }: PartialFunction[Instruction, VirtualMethodInvocationInstruction]) - pcAndInvocation.foreach { pcAndInvocation => - val pc = pcAndInvocation.pc - val mii = pcAndInvocation.value - val declClass = mii.declaringClass - if (declClass.isClassType - && relevantTypes.contains(declClass.asClassType) - ) { - val name = mii.name - if (name eq "addShutdownHook") { - val instructionLocation = InstructionLocation(methodLocation, pc) - locations(0) += instructionLocation - } else if (name eq "start") { - val instructionLocation = InstructionLocation(methodLocation, pc) - locations(2) += instructionLocation - } else if (name eq "join") { - val instructionLocation = InstructionLocation(methodLocation, pc) - locations(3) += instructionLocation - } else if (name eq "setUncaughtExceptionHandler") { - val instructionLocation = InstructionLocation(methodLocation, pc) - locations(4) += instructionLocation - } - } - } - } - } - - ArraySeq.unsafeWrapArray(locations) - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Java8InterfaceMethods.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Java8InterfaceMethods.scala deleted file mode 100644 index 0802c34416..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Java8InterfaceMethods.scala +++ /dev/null @@ -1,184 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries -package jcg - -import java.util.concurrent.ConcurrentHashMap -import scala.collection.immutable.ArraySeq - -import org.opalj.br.MethodWithBody -import org.opalj.br.analyses.Project -import org.opalj.br.instructions.Instruction -import org.opalj.br.instructions.INVOKEINTERFACE -import org.opalj.br.instructions.INVOKESTATIC -import org.opalj.br.instructions.INVOKEVIRTUAL -import org.opalj.br.instructions.MethodInvocationInstruction -import org.opalj.da.ClassFile - -/** - * Groups test case features that perform a method calls that are related to Java 8 - * interfaces. I.e., method calls to an interface's default method. - * - * @note The features represent the __Java8InterfaceMethods__ test cases from the Call Graph Test Project (JCG). - * - * @author Michael Reif - */ -class Java8InterfaceMethods(implicit hermes: HermesConfig) extends DefaultFeatureQuery { - - case class CacheKey(otID: Int, method: String, opcode: Int) - val relInvokeCache = new ConcurrentHashMap[CacheKey, Boolean]() - - override def featureIDs: Seq[String] = { - Seq( /* IDM = interface default method */ - "J8DIM1", /* 0 --- call on interface which must be resolved to an IDM */ - "J8DIM2", /* 1 --- call on interface (with IDM) that must not be resolved to it */ - "J8DIM3", /* 2 --- call on class which transitively calls an method that potentially could target an IDM */ - "J8DIM4", /* 3 --- call on class type which must be resolved to an IDM */ - "J8DIM5", /* 4 --- call that's dispatched to IDM where the class inherits from multiple interfaces with that idm (sig. wise) */ - "J8SIM1", /* 5 --- call to static interface method */ - "J10SIM2" /* 6 --- call to a private static interface method (from Java 10) */ - ) - } - - override def evaluate[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(ClassFile, S)] - ): IndexedSeq[LocationsContainer[S]] = { - val instructionsLocations = Array.fill(featureIDs.size)(new LocationsContainer[S]) - - for { - (classFile, source) <- project.projectClassFilesWithSources - if !isInterrupted() - classFileLocation = ClassFileLocation(source, classFile) - callerType = classFile.thisType - case method @ MethodWithBody(body) <- classFile.methods - methodLocation = MethodLocation(classFileLocation, method) - pcAndInvocation <- body.collect({ - case iv: INVOKEVIRTUAL if isPotentialCallOnDefaultMethod(iv, project) => iv - case ii: INVOKEINTERFACE if isPotentialCallOnDefaultMethod(ii, project) => ii - case is: INVOKESTATIC if is.isInterface => is - }: PartialFunction[Instruction, Instruction]) - } { - val pc = pcAndInvocation.pc - val invokeKind = pcAndInvocation.value - - val l = InstructionLocation(methodLocation, pc) - - val kindID = invokeKind match { - case INVOKEINTERFACE(dc, name, md) => { - val subtypes = project.classHierarchy.allSubtypes(dc.asClassType, false) - val hasDefaultMethodTarget = subtypes.exists { ct => - val target = project.instanceCall(callerType, ct, name, md) - if (target.hasValue) { - val definingClass = target.value.asVirtualMethod.classType.asClassType - project.classFile(definingClass).exists(_.isInterfaceDeclaration) - } else - false - } - - if (hasDefaultMethodTarget) - 0 /* a default method can be a target */ - else { - 1 /* interface has default method, but it's always overridden */ - } - } - case INVOKEVIRTUAL(dc, name, md) => { - val subtypes = project.classHierarchy.allSubtypes(dc.asClassType, true) - var subtypeWithMultipleInterfaces = false - val hasDefaultMethodTarget = subtypes.exists { ct => - val target = project.instanceCall(callerType, ct, name, md) - if (target.hasValue) { - val definingClass = target.value.asVirtualMethod.classType.asClassType - val isIDM = project.classFile(definingClass).exists(_.isInterfaceDeclaration) - if (isIDM) { - // if the method is resolved to an IDM we have to check whether there are multiple options - // in order to check the linearization order - val typeInheritMultipleIntWithSameIDM = - project.classHierarchy.allSuperinterfacetypes(ct, false).count { it => - val cf = project.classFile(it) - if (cf.nonEmpty) { - val method = cf.get.findMethod(name, md) - method.exists(_.body.nonEmpty) - } else { - false - } - } - subtypeWithMultipleInterfaces |= typeInheritMultipleIntWithSameIDM > 1 - } - isIDM - } else - false - } - - if (hasDefaultMethodTarget) { - if (subtypeWithMultipleInterfaces) - 4 /* the type inherits multiple interface with IDMs */ - else - 3 /* a default method can be a target */ - } else { - 2 /* interface has default method, but it's always overridden */ - } - - } - case INVOKESTATIC(dc, true, name, md) => { - val cf = project.classFile(dc) - if (cf.nonEmpty) { - val method = cf.get.findMethod(name, md) - method.map { m => - if (m.isPrivate) 6 /* call to a private static interface method */ - else 5 /* call to a static interface method */ - }.getOrElse(-1) - } - -1 - } - } - - if (kindID >= 0) { - instructionsLocations(kindID) += l - } - } - - ArraySeq.unsafeWrapArray(instructionsLocations) - } - - // This method determines whether the called interface method might be dispatched to a default method. - private def isPotentialCallOnDefaultMethod[S]( - mii: MethodInvocationInstruction, - project: Project[S] - ): Boolean = { - - val t = mii.declaringClass - if (!t.isClassType) - return false; - - val ct = t.asClassType - val methodName = mii.name - val methodDescriptor = mii.methodDescriptor - - val invokeID = CacheKey(ct.id, methodDescriptor.toJava(methodName), mii.opcode) - relInvokeCache.containsKey(invokeID) - if (relInvokeCache.containsKey(invokeID)) - return relInvokeCache.get(invokeID); - - val ch = project.classHierarchy - var relevantInterfaces = ch.allSuperinterfacetypes(ct, true) - ch.allSubclassTypes(ct, false).foreach { st => - relevantInterfaces = relevantInterfaces ++ ch.allSuperinterfacetypes(st, false) - } - - val isRelevant = relevantInterfaces.exists { si => - val cf = project.classFile(si) - if (cf.isDefined) { - val method = cf.get.findMethod(methodName, methodDescriptor) - method.isDefined && method.get.body.isDefined && method.get.isPublic - } else { - false - } - } - - relInvokeCache.put(invokeID, isRelevant) - isRelevant - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Java8Invokedynamics.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Java8Invokedynamics.scala deleted file mode 100644 index 3d07bae524..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Java8Invokedynamics.scala +++ /dev/null @@ -1,178 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries -package jcg - -import scala.collection.immutable.ArraySeq - -import org.opalj.ai.Domain -import org.opalj.ai.InterruptableAI -import org.opalj.ai.domain.l1.DefaultDomainWithCFGAndDefUse -import org.opalj.bi.REF_invokeInterface -import org.opalj.bi.REF_invokeSpecial -import org.opalj.bi.REF_invokeStatic -import org.opalj.bi.REF_invokeVirtual -import org.opalj.bi.REF_newInvokeSpecial -import org.opalj.br.ClassType.LambdaMetafactory -import org.opalj.br.InvokeSpecialMethodHandle -import org.opalj.br.InvokeStaticMethodHandle -import org.opalj.br.Method -import org.opalj.br.MethodCallMethodHandle -import org.opalj.br.MethodDescriptor -import org.opalj.br.MethodWithBody -import org.opalj.br.analyses.Project -import org.opalj.br.instructions.AASTORE -import org.opalj.br.instructions.ARETURN -import org.opalj.br.instructions.Instruction -import org.opalj.br.instructions.INVOKEDYNAMIC - -/** - * This feature query corresponds to the Java8Invokedynamics.md test cases from the JCG call - * graph test suite as well as some infrastructure incompatabile tests pertaining to JVM-hosted - * languages that also use invokedynamics or Java 10 invokedynamics that are used to concatenate - * Strings. - * - * @author Michael Reif - */ -class Java8Invokedynamics( - implicit val hermesConfig: HermesConfig -) extends DefaultFeatureQuery { - - override def featureIDs: Seq[String] = - /* There are 16 test cases, 7 pertaining to method references and 9 pertaining to lambdas - * Lambda5 = Java10 string concat - * Lambda6 = Scala serialization - * Lambda7 = Scala symbols - * Lambda8 = Scala structural call site - * Lambda9 = Groovy invDyn - no test case available - * - * Note: Scala's method references use the same mechanism as Java 8 and are therefore not - * covered within this query. - * */ - (1 to 7).map(num => s"MR$num") ++ (1 to 9).map(num => s"Lambda$num") - - def evaluate[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(da.ClassFile, S)] - ): IndexedSeq[LocationsContainer[S]] = { - import org.opalj.br.reader.InvokedynamicRewriting.* - - val locations = Array.fill(featureIDs.size)(new LocationsContainer[S]) - - for { - case m @ MethodWithBody(body) <- project.allMethodsWithBody - pcAndInvocation <- body.collect({ - case dynInv: INVOKEDYNAMIC => dynInv - }: PartialFunction[Instruction, Instruction]) - } { - - val pc = pcAndInvocation.pc - val l = InstructionLocation(project.source(m.classFile).get, m, pc) - - val testCaseId = pcAndInvocation.value match { - case invDyn: INVOKEDYNAMIC => { - - if (isJava10StringConcatInvokedynamic(invDyn)) { - 11 /* Lambda5 */ - } else if (isScalaLambdaDeserializeExpression(invDyn)) { - 12 /* Lambda6 */ - } else if (isScalaSymbolExpression(invDyn)) { - 13 /* Lambda7 */ - } else if (m.instructionsOption.nonEmpty - && isScalaStructuralCallSite(invDyn, m.instructionsOption.get, pc) - ) { - 14 /* Lambda8 */ - } else if (isGroovyInvokedynamic(invDyn)) { - 15 /* Lambda9 */ - } else if (isJava8LikeLambdaExpression(invDyn)) { - val bm = invDyn.bootstrapMethod - val handle = bm.arguments(1).asInstanceOf[MethodCallMethodHandle] - - if (bm.handle.isInvokeStaticMethodHandle) { - val InvokeStaticMethodHandle(LambdaMetafactory, false, name, descriptor) = - bm.handle: @unchecked - if (descriptor == MethodDescriptor.LambdaAltMetafactoryDescriptor && - name == "altMetafactory" - ) { - 10 /*Lambda4 */ - } else if (body.pcOfNextInstruction(pc) != -1) { - val nextPC = body.pcOfNextInstruction(pc) - if (body.instructions(nextPC).opcode == ARETURN.opcode) - 8 /* Lambda2*/ - else { - val ai = new InterruptableAI[Domain] - val domain = new DefaultDomainWithCFGAndDefUse(project, m) - val result = ai(m, domain) - val instructions = result.domain.code.instructions - val users = result.domain.usedBy(pc) - if (users.exists(instructions(_).opcode == AASTORE.opcode)) - 9 /* Lambda3 */ - else - handleJava8InvokeDynamic(m, handle) - } - } else - handleJava8InvokeDynamic(m, handle) - } else - handleJava8InvokeDynamic(m, handle) - } else { - // throw new RuntimeException("Unexpected handle Kind." + invDyn) - -1 - } - } - } - - if (testCaseId >= 0 && testCaseId < featureIDs.size) { - locations(testCaseId) += l - } - } - - ArraySeq.unsafeWrapArray(locations) - } - - private def handleJava8InvokeDynamic[S](m: Method, handle: MethodCallMethodHandle) = { - handle.referenceKind match { - case REF_invokeInterface => 0 /* MR1*/ - case REF_invokeStatic => { - val InvokeStaticMethodHandle(_, _, name, descriptor) = handle: @unchecked - // this just the called method is defined in the same class. - // if there is a method in the same class with the same name and descriptor, - // this check is tricked. - val localMethod = m.classFile.findMethod(name, descriptor) - val isLocal = localMethod.isDefined - if (isLocal) { - val callee = localMethod.get - if (callee.isStatic) { - if (callee.isSynthetic) { - 7 /* Lambda1 */ - } else { - if (callee.parameterTypes.isEmpty) { - 3 /* MR4 */ - } else { - 4 /* MR5 */ - } - } - } else { - /* something unexpected */ - -1 - } - } else { - 8 /* Lambda2 */ - } - } - case REF_invokeSpecial => { - val InvokeSpecialMethodHandle(_, isInterface, name, methodDescriptor) = handle: @unchecked - val localMethod = m.classFile.findMethod(name, methodDescriptor) - val isLocal = localMethod.isDefined - if (isLocal) { - val callee = localMethod.get - if (callee.isSynthetic) 2 /* MR3 */ else 1 /* MR2 */ - } else /* something unexpected */ 10 - } - case REF_invokeVirtual => 6 /* MR 7 */ - case REF_newInvokeSpecial => 5 /* MR 6 */ - case hk => throw new RuntimeException("Unexpected handle Kind." + hk) - } - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Library.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Library.scala deleted file mode 100644 index 8583a23b11..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Library.scala +++ /dev/null @@ -1,242 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries -package jcg - -import scala.collection.immutable.ArraySeq -import scala.collection.mutable - -import org.opalj.br.ClassType -import org.opalj.br.Field -import org.opalj.br.Method -import org.opalj.br.MethodDescriptor -import org.opalj.br.MethodWithBody -import org.opalj.br.analyses.Project -import org.opalj.br.analyses.ProjectIndex -import org.opalj.br.analyses.ProjectIndexKey -import org.opalj.br.analyses.SomeProject -import org.opalj.br.instructions.Instruction -import org.opalj.br.instructions.INVOKEINTERFACE -import org.opalj.br.instructions.INVOKEVIRTUAL -import org.opalj.br.instructions.VirtualMethodInvocationInstruction -import org.opalj.da.ClassFile - -/** - * Groups test case features that test the support for libraries/partial programs. All test cases - * assume that all packages are closed!!!! - * - * @note The features represent the __Library__ test cases from the Call Graph Test Project (JCG). - * - * @author Michael Reif - */ -class Library(implicit hermes: HermesConfig) extends DefaultFeatureQuery { - - override def featureIDs: Seq[String] = { - Seq( // CBS = call-by-signature - "LIB1", /* 0 --- parameter of library entry points must be resolved to any subtype */ - "LIB2", /* 1 --- calls on publically writeable fields must resolved to any subtype */ - "LIB3", /* 2 --- cbs with public classes only. */ - "LIB4", /* 3 --- cbs with an internal class */ - "LIB5" /* 4 --- cbs with an internal class that has subclasses */ - ) - } - - override def evaluate[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(ClassFile, S)] - ): IndexedSeq[LocationsContainer[S]] = { - val instructionLocations = Array.fill(featureIDs.size)(new LocationsContainer[S]) - - val projectIndex = project.get(ProjectIndexKey) - var cbsCache = Map.empty[String, Set[Method]] - // val declaredMethods = project.get(DeclaredMethodsKey) - - for { - (classFile, source) <- project.projectClassFilesWithSources - if !isInterrupted() - classFileLocation = ClassFileLocation(source, classFile) - fieldTypes = classFile.fields.filter(f => f.isNotFinal && !f.isPrivate).collect { - case f: Field if f.fieldType.id >= 0 => f.fieldType.id - } - case method @ MethodWithBody(body) <- classFile.methods - paramTypes = method.parameterTypes.map(_.id).filter(_ >= 0) - if (fieldTypes.nonEmpty || paramTypes.nonEmpty) - methodLocation = MethodLocation(classFileLocation, method) - pcAndInvocation <- body.collect({ - case iv: INVOKEVIRTUAL => iv - case ii: INVOKEINTERFACE => ii - }: PartialFunction[Instruction, VirtualMethodInvocationInstruction]) - } { - val pc = pcAndInvocation.pc - val invokeKind = pcAndInvocation.value - - val l = InstructionLocation(methodLocation, pc) - - val receiverType = invokeKind.declaringClass - val otID = receiverType.id - val isFieldType = fieldTypes.contains(otID) - - if (isFieldType) { - instructionLocations(1) += l - } - - val isParameterType = paramTypes.contains(otID) - if (isParameterType) { - if (invokeKind.opcode == INVOKEINTERFACE.opcode) { - val INVOKEINTERFACE(declClass, name, descriptor) = invokeKind: @unchecked - val cfO = project.classFile(declClass) - if (cfO.nonEmpty) { - val cf = cfO.get - val cacheKey = s"${cf.thisType.id}-$name-${descriptor.toJVMDescriptor}" - val targets = cbsCache.get(cacheKey) match { - case None => { - val newTargets = cf.findMethod(name, descriptor) - .map(getCBSTargets(projectIndex, project, _)) - .getOrElse(Set.empty[Method]) - cbsCache = cbsCache + ((cacheKey, newTargets)) - newTargets - } - case Some(result) => result - } - - var publicTarget = false - var packagePrivateTarget = false - var subclassTarget = false - - val itr = targets.iterator - while (itr.hasNext - && !(publicTarget && packagePrivateTarget && subclassTarget) - ) { - - val target = itr.next() - val declClass = target.classFile - - publicTarget |= declClass.isPublic - packagePrivateTarget |= declClass.isPackageVisible - // this is a compiler dependent approximation - subclassTarget |= target.isSynthetic && target.isBridge - } - - if (publicTarget) - instructionLocations(2) += l - - if (packagePrivateTarget) - instructionLocations(3) += l - - if (subclassTarget) - instructionLocations(4) += l - } - } - - instructionLocations(0) += l - } - - } - - ArraySeq.unsafeWrapArray(instructionLocations) - - } - - def getCBSTargets( - projectIndex: ProjectIndex, - project: SomeProject, - method: Method - ): Set[Method] = { - - val methodName = method.name - val methodDescriptor = method.descriptor - - import projectIndex.findMethods - - val interfaceClassFile = method.classFile - val interfaceType = interfaceClassFile.thisType - - val cbsTargets = mutable.Set.empty[Method] - - def analyzePotentialCBSTarget(cbsCallee: Method): Unit = { - if (!cbsCallee.isPublic) - return; - - if (cbsCallee.isAbstract) - return; - - if (!isInheritableMethod(cbsCallee)) - return; - - val cbsCalleeDeclaringClass = cbsCallee.classFile - - if (!cbsCalleeDeclaringClass.isClassDeclaration) - return; - - if (cbsCalleeDeclaringClass.isEffectivelyFinal) - return; - - val cbsCalleeDeclaringType = cbsCalleeDeclaringClass.thisType - - if (cbsCalleeDeclaringType eq ClassType.Object) - return; - - if (project.classHierarchy.isSubtypeOf( - cbsCalleeDeclaringType, - interfaceType - ) - ) - return; - - if (hasSubclassWhichInheritsFromInterface( - cbsCalleeDeclaringType, - interfaceType, - methodName, - methodDescriptor, - project - ).isYes - ) - return; - - cbsTargets += cbsCallee - } - - findMethods(methodName, methodDescriptor) foreach analyzePotentialCBSTarget - - cbsTargets.toSet - } - - @inline private def isInheritableMethod(method: Method): Boolean = { - - !method.isPrivate - } - - private def hasSubclassWhichInheritsFromInterface( - classType: ClassType, - interfaceType: ClassType, - methodName: String, - methodDescriptor: MethodDescriptor, - project: SomeProject - ): Answer = { - - val classHierarchy = project.classHierarchy - - val itr = classHierarchy.allSubclassTypes(classType, reflexive = false) - var isUnknown = false - - while (itr.hasNext) { - val subtype = itr.next() - project.classFile(subtype) match { - case Some(subclassFile) => - if (subclassFile.findMethod(methodName, methodDescriptor).isEmpty - && classHierarchy.isSubtypeOf(subtype, interfaceType) - ) - return Yes; - case None => - isUnknown = false - } - } - - if (isUnknown) - Unknown - else - No - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/ModernReflection.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/ModernReflection.scala deleted file mode 100644 index cc20db13a0..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/ModernReflection.scala +++ /dev/null @@ -1,38 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries -package jcg - -import org.opalj.br.ClassType -import org.opalj.hermes.queries.util.APIFeature -import org.opalj.hermes.queries.util.APIFeatureQuery -import org.opalj.hermes.queries.util.InstanceAPIMethod - -/** - * Queries the usage of the java.lang.invoke API around `MethodHandle`s. - * - * @note The features represent the __MODERN REFLECTION__ test cases from the Call Graph Test - * Project (JCG). - * - * @author Dominik Helm - */ -class ModernReflection(implicit hermes: HermesConfig) extends APIFeatureQuery { - - val Lookup = ClassType.MethodHandles$Lookup - val MethodHandle = ClassType.MethodHandle - - override val apiFeatures: List[APIFeature] = { - List( - InstanceAPIMethod(Lookup, "findStatic", featureID = "TMR1.1"), - InstanceAPIMethod(MethodHandle, "invokeExact", featureID = "TMR1.2"), - InstanceAPIMethod(Lookup, "findVirtual", featureID = "TMR2"), - InstanceAPIMethod(Lookup, "findConstructor", featureID = "TMR3"), - InstanceAPIMethod(Lookup, "findStaticGetter", featureID = "TMR4"), - InstanceAPIMethod(Lookup, "findGetter", featureID = "TMR5"), - InstanceAPIMethod(Lookup, "findSpecial", featureID = "TMR6"), - InstanceAPIMethod(MethodHandle, "invoke", featureID = "TMR7"), - InstanceAPIMethod(MethodHandle, "invokeWithArguments", featureID = "TMR8") - ) - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/NativeMethods.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/NativeMethods.scala deleted file mode 100644 index 454c639530..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/NativeMethods.scala +++ /dev/null @@ -1,39 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries -package jcg - -import scala.collection.immutable.ArraySeq - -import org.opalj.br.analyses.Project -import org.opalj.da.ClassFile - -/** - * Testcase to find applications containing native methods. - * - * @author Dominik Helm - */ -class NativeMethods(implicit hermes: HermesConfig) extends DefaultFeatureQuery { - - override def featureIDs: Seq[String] = Seq("NM") - - override def evaluate[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(ClassFile, S)] - ): IndexedSeq[LocationsContainer[S]] = { - - val locations = new LocationsContainer[S] - - for { - (classFile, source) <- project.projectClassFilesWithSources - method <- classFile.methods - if method.isNative - } { - locations += MethodLocation(source, method) - } - - ArraySeq(locations) - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/NonJavaBytecode1.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/NonJavaBytecode1.scala deleted file mode 100644 index 95fcb4a0a7..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/NonJavaBytecode1.scala +++ /dev/null @@ -1,71 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries -package jcg - -import scala.collection.immutable.ArraySeq - -import org.opalj.br.MethodWithBody -import org.opalj.br.analyses.Project -import org.opalj.br.instructions.INVOKEINTERFACE -import org.opalj.da.ClassFile - -/** - * Test case feature that performs an interface call for a default method where an intermediate - * interface shadows the default method with a static method (not valid java, but valid bytecode). - * - * @author Dominik Helm - */ -class NonJavaBytecode1(implicit hermes: HermesConfig) extends DefaultFeatureQuery { - - override def featureIDs: Seq[String] = { - Seq("NJ1") - } - - override def evaluate[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(ClassFile, S)] - ): IndexedSeq[LocationsContainer[S]] = { - - val instructionsLocations = Array.fill(featureIDs.size)(new LocationsContainer[S]) - - val classHierarchy = project.classHierarchy - - for { - (classFile, source) <- project.projectClassFilesWithSources - if !isInterrupted() - classFileLocation = ClassFileLocation(source, classFile) - case method @ MethodWithBody(body) <- classFile.methods - methodLocation = MethodLocation(classFileLocation, method) - pcAndInstruction <- body - if pcAndInstruction.instruction.opcode == INVOKEINTERFACE.opcode - } { - val INVOKEINTERFACE(declaringClass, name, desc) = pcAndInstruction.instruction: @unchecked - - val targets = project.resolveAllMethodReferences(declaringClass, name, desc) - - if (targets.size == 1) { - val invokedMethod = targets.head - val declIntf = invokedMethod.classFile - if (declIntf.isInterfaceDeclaration && - classHierarchy.allSuperinterfacetypes(declaringClass).exists { sintf => - val sintfCfO = project.classFile(sintf) - sintfCfO.isDefined && - sintfCfO.get.findMethod(name, desc).exists(_.isStatic) && - classHierarchy.allSuperinterfacetypes(sintf).contains(declIntf.thisType) - } - ) { - - val pc = pcAndInstruction.pc - val l = InstructionLocation(methodLocation, pc) - - instructionsLocations(0) += l - } - } - } - - ArraySeq.unsafeWrapArray(instructionsLocations) - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/NonJavaBytecode2.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/NonJavaBytecode2.scala deleted file mode 100644 index 8f4cd941fa..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/NonJavaBytecode2.scala +++ /dev/null @@ -1,56 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries -package jcg - -import scala.collection.immutable.ArraySeq -import scala.collection.mutable - -import org.opalj.br.analyses.Project -import org.opalj.da.ClassFile - -/** - * Test case feature where two methods are defined a class that do only vary in the specified return - * type. This is not possible on Java source level both is still valid in bytecode. - * - * @author Michael Reif - */ -class NonJavaBytecode2(implicit hermes: HermesConfig) extends DefaultFeatureQuery { - - override def featureIDs: Seq[String] = { - Seq("NJ2") - } - - override def evaluate[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(ClassFile, S)] - ): IndexedSeq[LocationsContainer[S]] = { - - val instructionsLocations = Array.fill(featureIDs.size)(new LocationsContainer[S]) - - for { - (classFile, source) <- project.projectClassFilesWithSources - if !isInterrupted() - classFileLocation = ClassFileLocation(source, classFile) - } { - val methodMap = mutable.Map.empty[String, Int] - classFile.methods.filterNot(_.isSynthetic).foreach { m => - val jvmDescriptor = m.descriptor.toJVMDescriptor - val mdWithoutReturn = jvmDescriptor.substring(0, jvmDescriptor.lastIndexOf(')') + 1) - val key = m.name + mdWithoutReturn - - val prev = methodMap.getOrElse(key, 0) - methodMap.put(key, prev + 1) - } - - val hasCase = methodMap.values.exists(_ >= 2) - - if (hasCase) - instructionsLocations(0) += classFileLocation - } - - ArraySeq.unsafeWrapArray(instructionsLocations) - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/NonVirtualCalls.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/NonVirtualCalls.scala deleted file mode 100644 index a103b1988f..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/NonVirtualCalls.scala +++ /dev/null @@ -1,116 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries -package jcg - -import scala.collection.immutable.ArraySeq - -import org.opalj.br.ClassType -import org.opalj.br.MethodWithBody -import org.opalj.br.analyses.Project -import org.opalj.br.instructions.Instruction -import org.opalj.br.instructions.INVOKESPECIAL -import org.opalj.br.instructions.INVOKESTATIC -import org.opalj.da.ClassFile - -/** - * Groups test case features that perform a direct method call. - * - * @note The features represent the __DirectCalls__ test cases from the Call Graph Test Project (JCG). - * - * @author Michael Reif - */ -class NonVirtualCalls(implicit hermes: HermesConfig) extends DefaultFeatureQuery { - - override def featureIDs: Seq[String] = { - Seq( - "NVC1", /* 0 --- static method call */ - "NVC2", /* 1 --- constructor call */ - "NVC3", /* 2 --- private method call */ - "NVC4", /* 3 --- super call on a transitive super method */ - "NVC5", /* 4 --- super call on a direct superclass' target */ - "NVC6" /* 5 --- private method call within an interface */ - ) - } - - override def evaluate[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(ClassFile, S)] - ): IndexedSeq[LocationsContainer[S]] = { - - val instructionsLocations = Array.fill(featureIDs.size)(new LocationsContainer[S]) - - for { - (classFile, source) <- project.projectClassFilesWithSources - if !isInterrupted() - classFileLocation = ClassFileLocation(source, classFile) - case method @ MethodWithBody(body) <- classFile.methods - methodLocation = MethodLocation(classFileLocation, method) - pcAndInvocation <- body.collect({ - case spec: INVOKESPECIAL => spec - case stat: INVOKESTATIC => stat - }: PartialFunction[Instruction, Instruction]) - } { - val pc = pcAndInvocation.pc - val invokeKind = pcAndInvocation.value - val declType = classFile.thisType - - val l = InstructionLocation(methodLocation, pc) - - val kindID = invokeKind match { - case _ @INVOKESTATIC(declaringClass, _, _, _) => { - val cf = project.classFile(declaringClass) - if (cf.isEmpty) - -1 - else if (cf.get.isInterfaceDeclaration) - 4 /* static interface receiver */ - else - 0 /* static call to class type*/ - } - case invSpec @ INVOKESPECIAL(declaringClass, _, name, _) => { - if (name != "") { - val callTargetResult = project.specialCall(classFile.thisType, invSpec) - if (callTargetResult.isEmpty) { - -1 - } else { - val callTarget = callTargetResult.value - if (declType eq declaringClass) { - if (callTarget.isPrivate) { - val cf = project.classFile(declaringClass) - if (cf.isEmpty) - -1 - else if (cf.get.isInterfaceDeclaration) - 5 /* private interface method call */ - else - 2 /* private method call */ - } else { - -1 - } - } else { - if (callTarget.classFile.thisType eq classFile.superclassType.get) { - 4 /* call to direct super class */ - } else { - 3 /* call to transitive super class */ - } - } - } - } else { - val superTypes = project.classHierarchy.directSupertypes(declType) - val isSuperConstructor = superTypes.contains(declaringClass) - if ((declaringClass ne ClassType.Object) && !isSuperConstructor) { - 1 /* constructor call */ - } else -1 - } - } - } - - if (kindID >= 0) { - instructionsLocations(kindID) += l - } - } - - ArraySeq.unsafeWrapArray(instructionsLocations) - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/PackageBoundaries.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/PackageBoundaries.scala deleted file mode 100644 index a06db6e235..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/PackageBoundaries.scala +++ /dev/null @@ -1,142 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries -package jcg - -import scala.collection.immutable.ArraySeq -import scala.collection.mutable.ArrayBuffer - -import org.opalj.br.ClassType -import org.opalj.br.MethodDescriptor -import org.opalj.br.MethodWithBody -import org.opalj.br.analyses.Project -import org.opalj.br.instructions.Instruction -import org.opalj.br.instructions.INVOKEVIRTUAL -import org.opalj.da.ClassFile - -/** - * Groups test case features that perform a polymorphic method calls over package boundaries. This is - * particulary relevant for package visible types and/or package visible methods. - * - * @note The features represent the __PackageBoundaries__ test cases from the Call Graph Test Project (JCG). - * - * @author Michael Reif - */ -class PackageBoundaries(implicit hermes: HermesConfig) extends DefaultFeatureQuery { - - override def featureIDs: Seq[String] = { - Seq( - "PB1", /* 0 --- calls are not allowed to leave the package */ - "PB2" /* 1 --- TODO */ - ) - } - - override def evaluate[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(ClassFile, S)] - ): IndexedSeq[LocationsContainer[S]] = { - val instructionsLocations = Array.fill(2)(new LocationsContainer[S]) - - for { - (classFile, source) <- project.projectClassFilesWithSources - if !isInterrupted() - classFileLocation = ClassFileLocation(source, classFile) - callerType = classFile.thisType - callerPackage = callerType.packageName - case method @ MethodWithBody(body) <- classFile.methods - methodLocation = MethodLocation(classFileLocation, method) - pcAndInvocation <- body.collect({ - case iv: INVOKEVIRTUAL => iv - }: PartialFunction[Instruction, INVOKEVIRTUAL]) - } { - val pc = pcAndInvocation.pc - val invoke = pcAndInvocation.value - - val l = InstructionLocation(methodLocation, pc) - - val receiver = invoke.declaringClass - val name = invoke.name - val methodDescriptor = invoke.methodDescriptor - - if (receiver.isClassType && (receiver.asClassType.packageName eq callerPackage)) { - val rtCt = receiver.asClassType - val rcf = project.classFile(rtCt) - - val isPackageVisibleMethod = rcf.exists(_.findMethod(name, methodDescriptor).exists(_.isPackagePrivate)) - - val matchesPreconditions = isPackageVisibleMethod && - isMethodOverriddenInDiffPackage( - callerPackage, - rtCt, - name, - methodDescriptor, - project - ) - - if (matchesPreconditions) { - if (project.classHierarchy.existsSubclass(rtCt, project) { cf => - val ct = cf.thisType - if (ct.packageName eq callerPackage) { - isMethodOverriddenInDiffPackage(rtCt, ct, name, methodDescriptor, project) - } else false - } - ) { - instructionsLocations(1) += l - /* it exists a subtype `S` within the same package as the declared typed `D` - that inherits transitively from type `D`, such that `S` <: `S'` <: `D` where - `S'` overrides the package private method `D.m` */ - } else { - // it exists an target in another package that overrides the method - instructionsLocations(0) += l - } - } - } - } - - ArraySeq.unsafeWrapArray(instructionsLocations) - } - - private def isMethodOverriddenInDiffPackage[S]( - callerPackage: String, - rtCt: ClassType, - name: String, - methodDescriptor: MethodDescriptor, - project: Project[S] - ) = { - project.classHierarchy.existsSubclass(rtCt, project) { sct => - (sct.thisType.packageName ne callerPackage) && - sct.findMethod(name, methodDescriptor).map(_.isPackagePrivate).getOrElse(false) - } - } - - private def isMethodOverriddenInDiffPackage[S]( - declaredType: ClassType, - targetType: ClassType, - name: String, - methodDescriptor: MethodDescriptor, - project: Project[S] - ): Boolean = { - - val classHierarchy = project.classHierarchy - val callingPackage = declaredType.packageName - - val worklist = ArrayBuffer[Int]() - classHierarchy.directSupertypes(targetType).foreach { sct => worklist.append(sct.id) } - while (worklist.nonEmpty) { - val cur = worklist.remove(0) - val ct = classHierarchy.getClassType(cur) - if (ct.packageName ne callingPackage) { - val cf = project.classFile(ct).get - val mo = cf.findMethod(name, methodDescriptor) - if (mo.isDefined && mo.get.isPackagePrivate) { - return true; - } - } - classHierarchy.directSupertypes(ct).foreach { sct => worklist.append(sct.id) } - } - - false - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Reflection.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Reflection.scala deleted file mode 100644 index 88c23e1c35..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Reflection.scala +++ /dev/null @@ -1,495 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries -package jcg - -import scala.collection.immutable.ArraySeq - -import org.opalj.ai.domain.l1.ArrayValues -import org.opalj.br.* -import org.opalj.br.MethodDescriptor.JustReturnsObject -import org.opalj.br.analyses.Project -import org.opalj.br.analyses.SomeProject -import org.opalj.br.instructions.Instruction -import org.opalj.br.instructions.INVOKESTATIC -import org.opalj.br.instructions.INVOKEVIRTUAL -import org.opalj.br.instructions.LoadClass -import org.opalj.br.instructions.LoadClass_W -import org.opalj.collection.immutable.IntTrieSet -import org.opalj.da.ClassFile -import org.opalj.log.LogContext -import org.opalj.log.OPALLogger -import org.opalj.tac.* -import org.opalj.value.ValueInformation - -/** - * Groups features that use the java reflection API. - * - * @note The features represent the __REFLECTION__ test cases from the Call Graph Test Project - * (JCG). - * - * @author Dominik Helm - */ -class Reflection(implicit hermes: HermesConfig) extends DefaultFeatureQuery { - - type V = DUVar[ValueInformation] - - val ClassT = ClassType.Class - val MethodT = ClassType("java/lang/reflect/Method") - val ConstructorT = ClassType("java/lang/reflect/Constructor") - val FieldT = ClassType("java/lang/reflect/Field") - - val PropertiesT = ClassType("java/util/Properties") - - val Invoke = MethodDescriptor( - ArraySeq(ClassType.Object, ArrayType(ClassType.Object)), - ClassType.Object - ) - val GetMethodMD = MethodDescriptor(ArraySeq(ClassType.String, ArrayType(ClassT)), MethodT) - val NewInstanceMD = MethodDescriptor(ArrayType(ClassType.Object), ClassType.Object) - val GetFieldMD = MethodDescriptor(ClassType.String, FieldT) - val FieldGetMD = MethodDescriptor(ClassType.Object, ClassType.Object) - val ForName1MD = MethodDescriptor("(Ljava/lang/String;)Ljava/lang/Class;") - val ForName3MD = - MethodDescriptor("(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;") - - val GetProperty1MD = MethodDescriptor(ClassType.String, ClassType.String) - val GetProperty2MD = - MethodDescriptor(ArraySeq(ClassType.String, ClassType.String), ClassType.String) - val GetMD = MethodDescriptor(ClassType.Object, ClassType.Object) - - override def featureIDs: Seq[String] = { - Seq( - "TR1", /* 0 --- invoke static method */ - "TR2", /* 1 --- invoke instance method */ - "TR3", /* 2 --- invoke instance method acquired by getMethod */ - "TR4", /* 3 --- invoke method with parameters */ - "TR5", /* 4 --- Constructor.newInstance */ - "TR6", /* 5 --- Class.newInstance */ - "TR7", /* 6 --- call on result of getDeclaredField */ - "TR8", /* 7 --- call on result of getField */ - "TR9", /* 8 --- Class.forName */ - "LRR1", /* 9 -- multiple constants for param */ - "LRR2", /* 10 - constant(s) from StringBuilder for param */ - "CSR1+CSR2", /* 11 - value from unknown source for param */ - "LRR3+CSR3", /* 12 -- value from field for param */ - "CSR4" /* 13 -- value from Properties */ - ) - } - - override def evaluate[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(ClassFile, S)] - ): IndexedSeq[LocationsContainer[S]] = { - - implicit val locations: Array[LocationsContainer[S]] = - Array.fill(featureIDs.size)(new LocationsContainer[S]) - - implicit val p: SomeProject = project - - implicit val tacai: Method => TACode[TACMethodParameter, V] = - project.get(LazyTACUsingAIKey) - - for { - (classFile, source) <- project.projectClassFilesWithSources - if !isInterrupted() - classFileLocation = ClassFileLocation(source, classFile) - case method @ MethodWithBody(body) <- classFile.methods - methodLocation = MethodLocation(classFileLocation, method) - pcAndInstruction <- body.collect({ - case i: LoadClass => i - case i: LoadClass_W => i - case i @ INVOKEVIRTUAL(MethodT, "invoke", Invoke) => i - case i @ INVOKEVIRTUAL(ClassT, "getMethod", GetMethodMD) => i - case i @ INVOKEVIRTUAL(ClassT, "getDeclaredMethod", GetMethodMD) => i - case i @ INVOKEVIRTUAL(ClassT, "newInstance", JustReturnsObject) => i - case i @ INVOKEVIRTUAL(ConstructorT, "newInstance", NewInstanceMD) => i - case i @ INVOKEVIRTUAL(ClassT, "getDeclaredField", GetFieldMD) => i - case i @ INVOKEVIRTUAL(ClassT, "getField", GetFieldMD) => i - case i @ INVOKEVIRTUAL(FieldT, "get", FieldGetMD) => i - case i @ INVOKESTATIC(ClassT, false, "forName", ForName1MD | ForName3MD) => i - }: PartialFunction[Instruction, Instruction]) - } { - val tac = - try { - tacai(method) - } catch { - case e: Exception => - implicit val logContext: LogContext = p.logContext - OPALLogger.error("analysis", s"unable to create 3-address code for: ${method.toJava}") - throw e - } - val pc = pcAndInstruction.pc - val l = InstructionLocation(methodLocation, pc) - - if (pcAndInstruction.value.isMethodInvocationInstruction) { - implicit val body: Array[Stmt[V]] = tac.stmts - - val index = tac.properStmtIndexForPC(pc) - if (index != -1) { - val stmt = tac.stmts(index) - - val call = - if (stmt.astID == Assignment.ASTID) stmt.asAssignment.expr.asFunctionCall - else stmt.asExprStmt.expr.asFunctionCall - - call.declaringClass match { - case MethodT => handleInvoke(call.asVirtualFunctionCall, l) - case ConstructorT => locations(4 /* Constructor.newInstance */ ) += l - case ClassT => - call.name match { - case "getMethod" => - if (stmt.astID == Assignment.ASTID && - methodUsedForInvocation(index, stmt.asAssignment) - ) { - locations(2 /* getMethod */ ) += l // invoke called directly - handleParameterSources(call, l) - } - case "getDeclaredMethod" => - if (stmt.astID == Assignment.ASTID && - methodUsedForInvocation(index, stmt.asAssignment) - ) { - handleParameterSources(call, l) - } - case "newInstance" => locations(5 /* Class.newInstance */ ) += l - case "getDeclaredField" => - if (stmt.astID == Assignment.ASTID && - getFieldUsedForInvokation(index, stmt.asAssignment) - ) { - handleParameterSources(call, l) - } - case "getField" => - if (stmt.astID == Assignment.ASTID && - getFieldUsedForInvokation(index, stmt.asAssignment) - ) { - locations(7 /* getField */ ) += l - handleParameterSources(call, l) - } - case "forName" => - locations(8 /* Class.forName */ ) += l - handleParameterSources(call, l) - } - case FieldT => - if (stmt.astID == Assignment.ASTID) - if (fieldUsedForInvocation(index, stmt.asAssignment)) - locations(6 /* Field.get */ ) += l - case _ => throw new UnknownError("will not happen") - } - } - } - } - - ArraySeq.unsafeWrapArray(locations) - } - - def handleInvoke[S](call: VirtualFunctionCall[V], l: Location[S])( - implicit locations: Array[LocationsContainer[S]] - ): Unit = { - if (call.params.head.asVar.value.asReferenceValue.isNull.isYes) - locations(0 /* static invoke */ ) += l - else - locations(1 /* instance invoke */ ) += l - - val paramTypes = call.params(1).asVar.value - if (paramTypes.asReferenceValue.allValues.exists { v => - v.isNull.isYesOrUnknown || v.asInstanceOf[ArrayValues#DomainArrayValue].length.contains(0) - } - ) - locations(3 /* method with parameters */ ) += l - } - - def handleParameterSources[S](call: Call[V], l: Location[S])( - implicit - project: SomeProject, - locations: Array[LocationsContainer[S]], - stmts: Array[Stmt[V]] - ): Unit = { - def simpleDefinition(definedBy: IntTrieSet): Option[Assignment[V]] = { - if (definedBy.size == 1 && definedBy.head > 0 && - stmts(definedBy.head).astID == Assignment.ASTID - ) - Some(stmts(definedBy.head).asAssignment) - else None - } - - def isNonEscapingStringBuilder(stmt: Assignment[V]): Boolean = { - def isNonEscaping(stringBuilder: V): Boolean = { - stringBuilder.usedBy.forall { useSite => - val use = stmts(useSite) - use.astID match { - case Assignment.ASTID => - use.asAssignment.expr.isVirtualFunctionCall && - (use.asAssignment.expr.asVirtualFunctionCall.name == "append" || - use.asAssignment.expr.asVirtualFunctionCall.name == "toString") - case ExprStmt.ASTID => - use.asExprStmt.expr.isVirtualFunctionCall && - (use.asExprStmt.expr.asVirtualFunctionCall.name == "append" || - use.asExprStmt.expr.asVirtualFunctionCall.name == "toString") - case NonVirtualMethodCall.ASTID => - use.asNonVirtualMethodCall.name == "" - case _ => false // might escape here - } - } - } - - stmt match { - case Assignment(_, sb, _: New) => isNonEscaping(sb) - case Assignment( - _, - sb, - VirtualFunctionCall(_, ClassType.StringBuilder, false, "append", _, receiver, _) - ) => - val stringBuilder = simpleDefinition(receiver.asVar.definedBy) - stringBuilder.exists(isNonEscapingStringBuilder) && isNonEscaping(sb) - case _ => false - } - } - - val definedBy = call.params.head.asVar.definedBy - if (simpleDefinition(definedBy).exists(_.expr.isConst)) { - /* nothing to do, trivial string constant */ - } else { - val ch = project.classHierarchy - definedBy.foreach { defSite => - if (defSite < 0) { - locations(11 /* string string */ ) += l - } else { - val definition = stmts(defSite).asAssignment.expr - if (definition.isConst) { - locations(9 /* multiple constants */ ) += l - } else if (definition.isGetField || definition.isGetStatic) { - locations(12 /* field */ ) += l - } else definition match { - case VirtualFunctionCall( - _, - ClassType.StringBuilder, - false, - "toString", - MethodDescriptor.JustReturnsString, - receiver, - _ - ) => - val stringBuilder = simpleDefinition(receiver.asVar.definedBy) - if (stringBuilder.exists(isNonEscapingStringBuilder)) { - locations(10 /* StringBuilder */ ) += l - } else { - locations(11 /* string unknown */ ) += l - } - case StaticFunctionCall(_, ClassType.System, _, "getProperty", GetProperty1MD, _) => - locations(13 /* string from Properties */ ) += l - case VirtualFunctionCall(_, dc, _, "getProperty", GetProperty1MD, _, _) - if ch.isSubtypeOf(dc, PropertiesT) => - locations(13 /* string from Properties */ ) += l - case VirtualFunctionCall(_, dc, _, "getProperty", GetProperty2MD, _, _) - if ch.isSubtypeOf(dc, PropertiesT) => - locations(13 /* string from Properties */ ) += l - case VirtualFunctionCall(_, dc, _, "get", GetMD, _, _) if ch.isSubtypeOf(dc, PropertiesT) => - locations(13 /* string from Properties */ ) += l - case _ => locations(11 /* string unknown */ ) += l - } - } - } - } - } - - def methodUsedForInvocation[S]( - pc: Int, - stmt: Assignment[V] - )( - implicit - project: SomeProject, - tacai: Method => TACode[TACMethodParameter, V], - stmts: Array[Stmt[V]] - ): Boolean = { - if (stmt.targetVar.usedBy.exists { useSite => - val stmt = stmts(useSite) - stmt.astID match { - case Assignment.ASTID => - stmt.asAssignment.expr.isVirtualFunctionCall && - stmt.asAssignment.expr.asVirtualFunctionCall.name == "invoke" - case ExprStmt.ASTID => - stmt.asExprStmt.expr.isVirtualFunctionCall && - stmt.asExprStmt.expr.asVirtualFunctionCall.name == "invoke" - case _ => false - } - } - ) { - true - } else - stmt.targetVar.usedBy.exists { useSite => - val stmt = stmts(useSite) - stmt.astID match { - case VirtualMethodCall.ASTID | NonVirtualMethodCall.ASTID | StaticMethodCall.ASTID => - mayUse(stmt.asMethodCall.params, pc) && - projectContainsNonLocalCall(MethodT, "invoke") - case Assignment.ASTID => - stmt.asAssignment.expr.astID match { - case VirtualFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | - StaticFunctionCall.ASTID => - mayUse(stmt.asAssignment.expr.asFunctionCall.params, pc) && - projectContainsNonLocalCall(MethodT, "invoke") - case InstanceOf.ASTID | Compare.ASTID => false - case _ => - projectContainsNonLocalCall(MethodT, "invoke") - } - case ExprStmt.ASTID => - stmt.asExprStmt.expr.astID match { - case VirtualFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | - StaticFunctionCall.ASTID => - mayUse(stmt.asExprStmt.expr.asFunctionCall.params, pc) && - projectContainsNonLocalCall(MethodT, "invoke") - case InstanceOf.ASTID | Compare.ASTID => false - case _ => - projectContainsNonLocalCall(MethodT, "invoke") - } - case MonitorEnter.ASTID | MonitorExit.ASTID | If.ASTID | Checkcast.ASTID => false - case _ => - projectContainsNonLocalCall(MethodT, "invoke") - } - } - } - - def fieldUsedForInvocation[S]( - pc: Int, - assignment: Assignment[V] - )(implicit stmts: Array[Stmt[V]]): Boolean = { - if (assignment.targetVar.usedBy.exists { useSite => - val stmt = stmts(useSite) - stmt.astID match { - case VirtualMethodCall.ASTID => - stmt.asVirtualMethodCall.receiver.asVar.definedBy.contains(pc) - case Assignment.ASTID => - stmt.asAssignment.expr.isVirtualFunctionCall && - stmt.asAssignment.expr.asVirtualFunctionCall.receiver.asVar.definedBy.contains(pc) - case ExprStmt.ASTID => - stmt.asExprStmt.expr.isVirtualFunctionCall && - stmt.asExprStmt.expr.asVirtualFunctionCall.receiver.asVar.definedBy.contains(pc) - case _ => false - } - } - ) { - true // direct invocation - } else - // Value loaded from field may escape and may be receiver of a call somewhere in project - assignment.targetVar.usedBy.exists { useSite => - val stmt = stmts(useSite) - stmt.astID match { - case PutField.ASTID => stmt.asPutField.value.asVar.definedBy.contains(pc) - case ArrayStore.ASTID => stmt.asArrayStore.value.asVar.definedBy.contains(pc) - case Assignment.ASTID => - stmt.asAssignment.expr.astID match { - case InstanceOf.ASTID | Compare.ASTID | - GetField.ASTID | ArrayLoad.ASTID => - false - case _ => true - } - case ExprStmt.ASTID => - stmt.asExprStmt.expr.astID match { - case InstanceOf.ASTID | Compare.ASTID | - GetField.ASTID | ArrayLoad.ASTID => - false - case _ => true - } - case MonitorEnter.ASTID | MonitorExit.ASTID | If.ASTID | Checkcast.ASTID => false - case _ => true - } - } - } - - def getFieldUsedForInvokation[S]( - pc: Int, - stmt: Assignment[V] - )( - implicit - project: SomeProject, - tacai: Method => TACode[TACMethodParameter, V], - stmts: Array[Stmt[V]] - ): Boolean = { - if (stmt.targetVar.usedBy.exists { useSite => - val stmt = stmts(useSite) - stmt.astID match { - case Assignment.ASTID => - stmt.asAssignment.expr.isVirtualFunctionCall && - stmt.asAssignment.expr.asVirtualFunctionCall.name == "get" - fieldUsedForInvocation(useSite, stmt.asAssignment) - case ExprStmt.ASTID => - stmt.asExprStmt.expr.isVirtualFunctionCall && - stmt.asExprStmt.expr.asVirtualFunctionCall.name == "get" && - fieldUsedForInvocation(useSite, stmt.asAssignment) - case _ => false - } - } - ) { - true - } else - // Field may escape and there is a non-local Field.get that might lead to an invocation - stmt.targetVar.usedBy.exists { useSite => - val stmt = stmts(useSite) - stmt.astID match { - case VirtualMethodCall.ASTID | NonVirtualMethodCall.ASTID | - StaticMethodCall.ASTID => - mayUse(stmt.asMethodCall.params, pc) && - projectContainsNonLocalCall(FieldT, "get") - case Assignment.ASTID => - stmt.asAssignment.expr.astID match { - case VirtualFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | - StaticFunctionCall.ASTID => - mayUse(stmt.asAssignment.expr.asFunctionCall.params, pc) && - projectContainsNonLocalCall(FieldT, "get") - case InstanceOf.ASTID | Compare.ASTID => false - case _ => - projectContainsNonLocalCall(FieldT, "get") - } - case ExprStmt.ASTID => - stmt.asExprStmt.expr.astID match { - case VirtualFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | - StaticFunctionCall.ASTID => - mayUse(stmt.asExprStmt.expr.asFunctionCall.params, pc) && - projectContainsNonLocalCall(FieldT, "get") - case InstanceOf.ASTID | Compare.ASTID => false - case _ => - projectContainsNonLocalCall(FieldT, "get") - } - case MonitorEnter.ASTID | MonitorExit.ASTID | If.ASTID | Checkcast.ASTID => false - case _ => - projectContainsNonLocalCall(FieldT, "get") - } - } - } - - def mayUse(uvars: Seq[Expr[V]], pc: Int): Boolean = { - uvars.exists(_.asVar.definedBy.contains(pc)) - } - - def projectContainsNonLocalCall( - declType: ClassType, - name: String - )( - implicit - project: SomeProject, - tacai: Method => TACode[TACMethodParameter, V] - ): Boolean = { - project.allMethodsWithBody.exists { method => - val invokes = method.body.get.collect({ - case i @ INVOKEVIRTUAL(`declType`, `name`, _) => i - }: PartialFunction[Instruction, INVOKEVIRTUAL]) - if (invokes.isEmpty) { - false - } else { - val tac = tacai(method) - val stmts = tac.stmts - invokes.exists { pcAndInvocation => - val stmt = stmts(tac.properStmtIndexForPC(pcAndInvocation.pc)) - val call = - if (stmt.astID == Assignment.ASTID) - stmt.asAssignment.expr.asVirtualFunctionCall - else - stmt.asExprStmt.expr.asVirtualFunctionCall - call.receiver.asVar.definedBy.exists { defSite => defSite < 0 || stmts(defSite).astID != New.ASTID } - } - } - } - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Serialization.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Serialization.scala deleted file mode 100644 index d326d78c6b..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Serialization.scala +++ /dev/null @@ -1,331 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries -package jcg - -import scala.collection.immutable.ArraySeq - -import org.opalj.br.ClassHierarchy -import org.opalj.br.ClassType -import org.opalj.br.IntegerType -import org.opalj.br.MethodCallMethodHandle -import org.opalj.br.MethodDescriptor -import org.opalj.br.MethodDescriptor.JustReturnsObject -import org.opalj.br.MethodDescriptor.ReadObjectDescriptor -import org.opalj.br.MethodDescriptor.WriteObjectDescriptor -import org.opalj.br.MethodWithBody -import org.opalj.br.ReferenceType -import org.opalj.br.VoidType -import org.opalj.br.analyses.Project -import org.opalj.br.analyses.SomeProject -import org.opalj.br.instructions.Instruction -import org.opalj.br.instructions.INVOKEVIRTUAL -import org.opalj.da.ClassFile -import org.opalj.tac.Assignment -import org.opalj.tac.Checkcast -import org.opalj.tac.DUVar -import org.opalj.tac.ExprStmt -import org.opalj.tac.InvokedynamicFunctionCall -import org.opalj.tac.LazyTACUsingAIKey -import org.opalj.tac.Stmt -import org.opalj.tac.VirtualMethodCall -import org.opalj.value.ValueInformation - -/** - * Groups test case features that perform serialization. - * - * @note The features represent the __Serialization__ test cases from the Call Graph Test Project - * (JCG). - * - * @author Dominik Helm - */ -class Serialization(implicit hermes: HermesConfig) extends DefaultFeatureQuery { - - // TODO Add Serialization.md from JCG to resources once it is fixed - - type V = DUVar[ValueInformation] - - // required types and descriptors - val OOS = ClassType("java/io/ObjectOutputStream") - val OIS = ClassType("java/io/ObjectInputStream") - val OOutput = ClassType("java/io/") - - val OOSwriteObject = MethodDescriptor.JustTakes(ClassType.Object) - val OISregisterValidation = MethodDescriptor( - ArraySeq(ClassType("java/io/ObjectInputValidation"), IntegerType), - VoidType - ) - val writeExternal = MethodDescriptor.JustTakes(ClassType("java/io/ObjectOutput")) - val readExternal = MethodDescriptor.JustTakes(ClassType("java/io/ObjectInput")) - - override def featureIDs: Seq[String] = { - Seq( - "Ser1", /* 0 --- Trivial writeObject */ - "Ser2", /* 1 --- Local non-trivial writeObject */ - "Ser3", /* 2 --- Non-local writeObject */ - "Ser4", /* 3 --- readObject without cast */ - "Ser5", /* 4 --- readObject with casts */ - "Ser6", /* 5 --- writeReplace */ - "Ser7", /* 6 --- readResolve */ - "Ser8", /* 7 --- registerValidation */ - "Ser9", /* 8 --- general deserialization of Serializiable (not Externalizable) classes */ - "ExtSer1", /* 9 --- writeExternal */ - "ExtSer2+ExtSer3", /* 10 -- readExternal & general deserialization of Externalizable classes */ - "SerLam1+SerLam2" /* 11 --- (de-)serialization of Lambdas */ - ) - } - - override def evaluate[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(ClassFile, S)] - ): IndexedSeq[LocationsContainer[S]] = { - - implicit val locations: Array[LocationsContainer[S]] = - Array.fill(featureIDs.size)(new LocationsContainer[S]) - - val tacai = project.get(LazyTACUsingAIKey) - - implicit val p: Project[?] = project - implicit val classHierarchy: ClassHierarchy = project.classHierarchy - - val serializableTypes: Set[ClassType] = - classHierarchy.allSubtypes(ClassType.Serializable, reflexive = false) - - val externalizableTypes: Set[ClassType] = - classHierarchy.allSubtypes(ClassType.Externalizable, reflexive = false) - - val notExternalizableTypes = serializableTypes diff externalizableTypes - - for { - (classFile, source) <- project.projectClassFilesWithSources - if !isInterrupted() - classFileLocation = ClassFileLocation(source, classFile) - case method @ MethodWithBody(body) <- classFile.methods - methodLocation = MethodLocation(classFileLocation, method) - pcAndInvocation <- body.collect({ - case i @ INVOKEVIRTUAL(declClass, "writeObject", OOSwriteObject) - if classHierarchy.isSubtypeOf(declClass, OOS) => i - case i @ INVOKEVIRTUAL(declClass, "readObject", JustReturnsObject) - if classHierarchy.isSubtypeOf(declClass, OIS) => i - case i @ INVOKEVIRTUAL(declClass, "registerValidation", OISregisterValidation) - if classHierarchy.isSubtypeOf(declClass, OIS) => i - }: PartialFunction[Instruction, INVOKEVIRTUAL]) - tac = tacai(method) - } { - val pc = pcAndInvocation.pc - val l = InstructionLocation(methodLocation, pc) - - val invocation = tac.stmts(tac.properStmtIndexForPC(pc)) - - if (invocation.astID == VirtualMethodCall.ASTID) { - if (invocation.asVirtualMethodCall.name == "writeObject") - handleWriteObject( - invocation.asVirtualMethodCall, - l, - tac.stmts, - serializableTypes, - externalizableTypes, - notExternalizableTypes - ) - else // registerValidation - locations(7) += l - } else { - invocation.astID match { - case Assignment.ASTID => - handleReadObject( - invocation.asAssignment, - l, - tac.stmts, - serializableTypes, - externalizableTypes, - notExternalizableTypes - ) - case ExprStmt.ASTID => - handleReadObjectWithoutCasts( - l, - serializableTypes, - externalizableTypes, - notExternalizableTypes - ) - } - } - } - - ArraySeq.unsafeWrapArray(locations) - } - - def handleWriteObject[S]( - invocation: VirtualMethodCall[V], - l: Location[S], - stmts: Array[Stmt[V]], - serializableTypes: Set[ClassType], - externalizableTypes: Set[ClassType], - notExternalizableTypes: Set[ClassType] - )( - implicit - locations: Array[LocationsContainer[S]], - project: SomeProject, - classHierarchy: ClassHierarchy - ): Unit = { - val paramVar = invocation.params.head.asVar - val param = paramVar.value.asReferenceValue - - if (paramVar.definedBy.exists { defSite => - if (defSite >= 0) { - val expr = stmts(defSite).asAssignment.expr - expr.astID == InvokedynamicFunctionCall.ASTID && isLambdaMetafactoryCall( - expr.asInvokedynamicFunctionCall - ) - } else false - } - ) { - locations(11) += l - } else if (param.isPrecise) { - if (param.isNull.isNo && - hasMethod(param.asReferenceType, "writeObject", WriteObjectDescriptor) && - !classHierarchy.isSubtypeOf(param.asReferenceType, ClassType.Externalizable) - ) - locations(0) += l - else if (param.isNull.isNo && - classHierarchy.isSubtypeOf(param.asReferenceType, ClassType.Externalizable) - ) - locations(9) += l - - if (param.isNull.isNo && - hasMethod(param.asReferenceType, "writeReplace", JustReturnsObject) - ) - locations(5) += l - - } else if (param.allValues.forall(_.isPrecise)) { - if (param.allValues.exists { pt => - pt.isNull.isNo && - hasMethod(pt.asReferenceType, "writeObject", WriteObjectDescriptor) && - !classHierarchy.isSubtypeOf(pt.asReferenceType, ClassType.Externalizable) - } - ) - locations(1) += l - - if (param.allValues.exists { pt => - pt.isNull.isNo && - hasMethod(pt.asReferenceType, "writeReplace", JustReturnsObject) - } - ) - locations(5) += l - - if (param.allValues.exists { pt => - pt.isNull.isNo && - classHierarchy.isSubtypeOf(pt.asReferenceType, ClassType.Externalizable) - } - ) - locations(9) += l - } else { - if (notExternalizableTypes.exists { subtype => hasMethod(subtype, "writeObject", WriteObjectDescriptor) }) - locations(2) += l - - if (serializableTypes.exists { subtype => hasMethod(subtype, "writeReplace", JustReturnsObject) }) - locations(5) += l - - if (externalizableTypes.nonEmpty) - locations(9) += l - } - } - - def isLambdaMetafactoryCall(invokedynamic: InvokedynamicFunctionCall[V]): Boolean = { - val handle = invokedynamic.bootstrapMethod.handle - handle.isInstanceOf[MethodCallMethodHandle] && - handle.asInstanceOf[MethodCallMethodHandle].receiverType == ClassType.LambdaMetafactory - } - - def handleReadObject[S]( - invocation: Assignment[V], - l: Location[S], - stmts: Array[Stmt[V]], - serializableTypes: Set[ClassType], - externalizableTypes: Set[ClassType], - notExternalizableTypes: Set[ClassType] - )( - implicit - locations: Array[LocationsContainer[S]], - project: SomeProject, - classHierarchy: ClassHierarchy - ): Unit = { - val ret = invocation.targetVar - - val castTypes = ret.usedBy.iterator.collect { - case index if stmts(index).astID == Checkcast.ASTID => stmts(index).asCheckcast.cmpTpe - }.toSeq - - if (castTypes.isEmpty) { - handleReadObjectWithoutCasts( - l, - serializableTypes, - externalizableTypes, - notExternalizableTypes - ) - } else { - if (castTypes.exists { cType => - hasMethod(cType, "readObject", ReadObjectDescriptor) && - !classHierarchy.isSubtypeOf(cType, ClassType.Externalizable) - } - ) - locations(4) += l - - if (castTypes.exists { cType => hasMethod(cType, "readResolve", JustReturnsObject) }) - locations(6) += l - - if (castTypes.exists { cType => !classHierarchy.isSubtypeOf(cType, ClassType.Externalizable) }) - locations(8) += l - - if (castTypes.exists { cType => - classHierarchy.isSubtypeOf(cType, ClassType.Externalizable) || - cType.isClassType && - classHierarchy.allSubclassTypes(cType.asClassType, reflexive = false).exists { - classHierarchy.isSubtypeOf(_, ClassType.Externalizable) - } - } - ) { - locations(10) += l - } - } - } - - def handleReadObjectWithoutCasts[S]( - l: Location[S], - serializableTypes: Set[ClassType], - externalizableTypes: Set[ClassType], - notExternalizableTypes: Set[ClassType] - )( - implicit - locations: Array[LocationsContainer[S]], - project: SomeProject - ): Unit = { - if (notExternalizableTypes.exists { subtype => hasMethod(subtype, "readObject", ReadObjectDescriptor) }) - locations(3) += l - - if (serializableTypes.exists { subtype => hasMethod(subtype, "readResolve", JustReturnsObject) }) - locations(6) += l - - if (notExternalizableTypes.nonEmpty) - locations(8) += l - - if (externalizableTypes.nonEmpty) { - locations(10) += l - locations(11) += l - } - } - - def hasMethod( - refType: ReferenceType, - name: String, - descriptor: MethodDescriptor - )(implicit p: SomeProject): Boolean = { - if (refType.isClassType) { - val classType = refType.asClassType - val classFileO = p.classFile(classType) - classFileO.isDefined && classFileO.get.findMethod(name, descriptor).isDefined || - p.instanceCall(classType, classType, name, descriptor).hasValue - } else false - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/StaticInitializer.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/StaticInitializer.scala deleted file mode 100644 index d53ec93515..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/StaticInitializer.scala +++ /dev/null @@ -1,118 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries -package jcg - -import scala.collection.immutable.ArraySeq - -import org.opalj.ai.BaseAI -import org.opalj.ai.domain.l1.DefaultDomainWithCFGAndDefUse -import org.opalj.br.ClassType -import org.opalj.br.PCAndInstruction -import org.opalj.br.analyses.Project -import org.opalj.br.instructions.LDC -import org.opalj.br.instructions.LDC_W -import org.opalj.br.instructions.PUTSTATIC -import org.opalj.da.ClassFile - -/** - * Groups test case features that test the correct recognition of static initializers. - * - * @note The features represent the __StaticInitializer__ test cases from the Call Graph Test Project (JCG). - * - * @author Michael Reif - */ -class StaticInitializer(implicit hermes: HermesConfig) extends DefaultFeatureQuery { - - val Object = ClassType.Object - val String = ClassType.String - - override def featureIDs: Seq[String] = { - Seq( - "SI1", /* 0 --- reference of a non-constant field within an interface. */ - "SI2", /* 1 --- invocation of a static interface method. */ - "SI3", /* 2 --- class creation when class impl. Interface with default method and static fields. */ - "SI4", /* 3 --- reference of a final non-primitive and non-String field within an interface. */ - "SI5", /* 4 --- class creation should trigger the static initializer */ - "SI6", /* 5 --- call of a static method */ - "SI7", /* 6 --- assignment to a static field */ - "SI8" /* 7 --- initialization of a class should cause initialization of super classes */ - ) - } - - override def evaluate[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(ClassFile, S)] - ): IndexedSeq[LocationsContainer[S]] = { - val classLocations = Array.fill(featureIDs.size)(new LocationsContainer[S]) - - for { - (classFile, source) <- project.projectClassFilesWithSources - if classFile.staticInitializer.isDefined - if !isInterrupted() - classFileLocation = ClassFileLocation(source, classFile) - } { - val hasStaticField = classFile.fields.exists(_.isStatic) - val hasStaticMethod = classFile.methods.exists(m => m.isStatic && !m.isStaticInitializer) - - if (classFile.isInterfaceDeclaration) { // index 0 - 3 - - if (hasStaticMethod) { - classLocations(1) += classFileLocation - } - val hasDefaultMethod = classFile.instanceMethods.exists { m => m.body.nonEmpty && m.isPublic } - - if (hasStaticField) { - if (hasDefaultMethod) { - classLocations(2) += classFileLocation - } - - val si = classFile.staticInitializer.get - val putStaticPCs = si.body.get.collectInstructionsWithPC { - case pci @ PCAndInstruction(_, PUTSTATIC(_, _, _)) => pci - } - - val domain = new DefaultDomainWithCFGAndDefUse(project, si) - - putStaticPCs.foreach { pci => - val pc = pci.pc - - val ai = BaseAI - val aiResult = ai.apply(si, domain) - val vo = aiResult.domain.operandOrigin(pc, 0).head - val inst = aiResult.code.instructions(vo) - - if (inst.isInvocationInstruction) { - classLocations(3) += classFileLocation - } else if (inst.opcode != LDC.opcode && inst.opcode != LDC_W.opcode) { - classLocations(0) += classFileLocation - } - } - } - } else { // index 4 - 7 - val hasNonPrivateConstructor = classFile.constructors.exists { !_.isPrivate } - - if (hasNonPrivateConstructor) { - classLocations(4) += classFileLocation - } - - if (hasStaticMethod) { - classLocations(5) += classFileLocation - } - - if (hasStaticField) { - classLocations(6) += classFileLocation - } - - val superclassType = classFile.superclassType - if (superclassType.nonEmpty && superclassType.get != Object) { - classLocations(7) += classFileLocation - } - } - } - - ArraySeq.unsafeWrapArray(classLocations) - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Types.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Types.scala deleted file mode 100644 index b2b734df9d..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Types.scala +++ /dev/null @@ -1,102 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries -package jcg - -import scala.collection.immutable.ArraySeq - -import org.opalj.ai.BaseAI -import org.opalj.ai.domain.l0.BaseDomain -import org.opalj.br.ClassType -import org.opalj.br.MethodWithBody -import org.opalj.br.analyses.Project -import org.opalj.br.instructions.CHECKCAST -import org.opalj.br.instructions.IF_ACMPEQ -import org.opalj.br.instructions.IF_ACMPNE -import org.opalj.br.instructions.INSTANCEOF -import org.opalj.br.instructions.INVOKEVIRTUAL -import org.opalj.da.ClassFile - -/** - * Groups features that somehow rely on Javas type cast API given by either jvm instructions or - * ```java.lang.Class```. - * - * @note The features represent the __TYPES__ test cases from the Call Graph Test Project (JCG). - * - * @author Michael Reif - */ -class Types(implicit hermes: HermesConfig) extends DefaultFeatureQuery { - - val Class = ClassType("java/lang/Class") - val Object = ClassType.Object - - override def featureIDs: Seq[String] = { - Seq( - "TC1", /* 0 --- checkcast (instr.) */ - "TC2", /* 1 --- virutal invocation of java.lang.Class.cast that's not followed by a checkcast instr. */ - "TC3", /* 2 --- equality check between two objects of type java.lang.Class */ - "TC4", /* 3 --- instanceof (instr.) */ - "TC5", /* 4 --- virutal invocation of java.lang.Class.isInstance */ - "TC6" /* 5 --- virutal invocation of java.lang.Class.isAssignableFrom */ - ) - } - - override def evaluate[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(ClassFile, S)] - ): IndexedSeq[LocationsContainer[S]] = { - - val instructionsLocations = Array.fill(featureIDs.size)(new LocationsContainer[S]) - - for { - (classFile, source) <- project.projectClassFilesWithSources - if !isInterrupted() - classFileLocation = ClassFileLocation(source, classFile) - case method @ MethodWithBody(body) <- classFile.methods - methodLocation = MethodLocation(classFileLocation, method) - pcAndInstruction <- body - } { - val instruction = pcAndInstruction.instruction - val pc = pcAndInstruction.pc - - instruction.opcode match { - case CHECKCAST.opcode => instructionsLocations(0) += InstructionLocation(methodLocation, pc) - case INSTANCEOF.opcode => instructionsLocations(3) += InstructionLocation(methodLocation, pc) - case IF_ACMPEQ.opcode | IF_ACMPNE.opcode => - val ai = BaseAI - val aiResult = ai.apply(method, BaseDomain(project, method)) - val operands = aiResult.operandsArray.apply(pc) - if (operands ne null) { - val op1Type = operands(0).asDomainReferenceValue.leastUpperType - val op2Type = operands(1).asDomainReferenceValue.leastUpperType - val isClassRefCheck = op1Type.exists { - op1 => (op1 eq Class) && (op1 eq op2Type.getOrElse(Object)) - } - if (isClassRefCheck) { - instructionsLocations(2) += InstructionLocation(methodLocation, pc) - } - } - case INVOKEVIRTUAL.opcode => - val INVOKEVIRTUAL(declaringClass, name, _) = instruction.asInstanceOf[INVOKEVIRTUAL]; - if (declaringClass.isClassType && (declaringClass.asClassType eq Class)) { - if (name eq "cast") { - val nextPC = body.pcOfNextInstruction(pc) - if (body.instructions(nextPC).opcode != CHECKCAST.opcode) { - instructionsLocations(1) += InstructionLocation(methodLocation, pc) - } - } else if (name eq "isInstance") { - instructionsLocations(4) += InstructionLocation(methodLocation, pc) - } else if (name eq "isAssignableFrom") { - instructionsLocations(5) += InstructionLocation(methodLocation, pc) - } - } - case _ => - } - } - - ArraySeq.unsafeWrapArray(instructionsLocations) - } - -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Unsafe.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Unsafe.scala deleted file mode 100644 index 402e93aed4..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/Unsafe.scala +++ /dev/null @@ -1,37 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries -package jcg - -import org.opalj.br.ClassType -import org.opalj.hermes.queries.util.APIFeature -import org.opalj.hermes.queries.util.APIFeatureQuery -import org.opalj.hermes.queries.util.InstanceAPIMethod - -/** - * Groups features that rely on the Unsafe API. (sun.misc.Unsafe) - * - * @note Feature groups are taken from and are further discussed in the following paper: - * "Use at Your Own Risk: The Java Unsafe API in the Wild" - * by Luis Mastrangelo et al. - * - * @author Michael Reif - */ -class Unsafe(implicit hermes: HermesConfig) extends APIFeatureQuery { - - override val apiFeatures: List[APIFeature] = { - - val Unsafe = ClassType("sun/misc/Unsafe") - - List( - InstanceAPIMethod(Unsafe, "compareAndSwapObject", featureID = "Unsafe1"), - InstanceAPIMethod(Unsafe, "putObject", featureID = "Unsafe2"), - InstanceAPIMethod(Unsafe, "getObject", featureID = "Unsafe3"), - InstanceAPIMethod(Unsafe, "getAndSetObject", featureID = "Unsafe4"), - InstanceAPIMethod(Unsafe, "putOrderedObject", featureID = "Unsafe5"), - InstanceAPIMethod(Unsafe, "getObjectVolatile", featureID = "Unsafe6"), - InstanceAPIMethod(Unsafe, "putObjectVolatile", featureID = "Unsafe7") - ) - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/VirtualCalls.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/VirtualCalls.scala deleted file mode 100644 index 6fefa4b1a4..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/jcg/VirtualCalls.scala +++ /dev/null @@ -1,87 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries -package jcg - -import scala.collection.immutable.ArraySeq - -import org.opalj.br.MethodWithBody -import org.opalj.br.analyses.Project -import org.opalj.br.instructions.Instruction -import org.opalj.br.instructions.INVOKEINTERFACE -import org.opalj.br.instructions.INVOKEVIRTUAL -import org.opalj.br.instructions.VirtualMethodInvocationInstruction -import org.opalj.da.ClassFile - -/** - * Groups test case features that perform a pre Java 8 polymorhpic method call. - * - * @note The features represent the __PolymorphicCalls__ test cases from the Call Graph Test Project (JCG). - * - * @author Michael Reif - */ -class VirtualCalls(implicit hermes: HermesConfig) extends DefaultFeatureQuery { - - override def featureIDs: Seq[String] = { - Seq( - "VC1", /* 0 --- virtual call with single target */ - "VC2", /* 1 --- virtual call with multiple possible targets */ - "VC3", /* 2 --- interface call with single target */ - "VC4" /* 3 --- interface call with multiple targets */ - ) - } - - override def evaluate[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(ClassFile, S)] - ): IndexedSeq[LocationsContainer[S]] = { - val instructionsLocations = Array.fill(featureIDs.size)(new LocationsContainer[S]) - - for { - (classFile, source) <- project.projectClassFilesWithSources - if !isInterrupted() - classFileLocation = ClassFileLocation(source, classFile) - callerType = classFile.thisType - case method @ MethodWithBody(body) <- classFile.methods - methodLocation = MethodLocation(classFileLocation, method) - pcAndInvocation <- body.collect({ - case iv: INVOKEVIRTUAL => iv - case ii: INVOKEINTERFACE => ii - }: PartialFunction[Instruction, VirtualMethodInvocationInstruction]) - } { - val pc = pcAndInvocation.pc - val invokeKind = pcAndInvocation.value - - val l = InstructionLocation(methodLocation, pc) - - val kindID = invokeKind.opcode match { - case INVOKEVIRTUAL.opcode => { - val targets = - project.virtualCall(callerType, invokeKind.asInstanceOf[INVOKEVIRTUAL]) - targets.size match { - case 0 => -1 /* boring call site */ - case 1 => 0 /* single target cs */ - case _ => 1 /* multiple target cs*/ - } - } - case INVOKEINTERFACE.opcode => { - val targets = - project.interfaceCall(callerType, invokeKind.asInstanceOf[INVOKEINTERFACE]) - targets.size match { - case 0 => -1 /* boring call site */ - case 1 => 2 /* single target cs */ - case _ => 3 /* multiple target cs*/ - } - } - } - - if (kindID >= 0) { - instructionsLocations(kindID) += l - } - } - - ArraySeq.unsafeWrapArray(instructionsLocations) - } -} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/util/APIFeature.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/util/APIFeature.scala deleted file mode 100644 index c9e80d9b52..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/util/APIFeature.scala +++ /dev/null @@ -1,185 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries -package util - -import org.opalj.br.ClassType -import org.opalj.br.MethodDescriptor -import org.opalj.br.instructions.MethodInvocationInstruction - -/** - * A common super trait for API related features such as the usage of common or interesting APIs. - * - * @author Michael Reif - */ -sealed abstract class APIFeature { - - /** - * Returns the feature id of the feature. - * - * @note Feature ids have to be unique. - */ - def featureID: String - - /** - * Returns all methods of the API that belong to this feature. - */ - def apiMethods: List[APIMethod] -} - -/** - * Common trait that abstracts over all Class extension scenarios. - */ -sealed abstract class ClassExtension extends APIFeature { - - def declClass: ClassType - - override def apiMethods: List[APIMethod] = List() -} - -/** - * Represents an extension of a specific class - */ -case class APIClassExtension(featureID: String, declClass: ClassType) extends ClassExtension - -/** - * Common trait that abstracts over instance and static api methods. - */ -sealed abstract class APIMethod(private val fID: Option[String] = None) extends APIFeature { - - def declClass: ClassType - - def name: String - - def descriptor: Option[MethodDescriptor] - - def unapply(i: MethodInvocationInstruction): Boolean - - final def matches(i: MethodInvocationInstruction): Boolean = this.unapply(i) - - override final val apiMethods = List(this) - - private def customFeatureID: Option[String] = fID - - /** - * Return the feature id of the feature. - * - * @note Feature ids have to be unique. - */ - override val featureID: String = { - if (customFeatureID.isDefined) { - customFeatureID.get - } else { - val methodName = descriptor.map(_.toJava(name)).getOrElse(name) - val abbreviatedMethodName = methodName.replaceAll("java.lang.Object", "Object") - s"${declClass.toJava}\n$abbreviatedMethodName" - } - } -} - -/** - * Represents an instance API call. - * - * @param declClass ClassType of the receiver. - * @param name Name of the API method. - * @param descriptor Optional method descriptor, is no descriptor assigned, it represents - * all methods with the same name, declared in the same class. - */ -case class InstanceAPIMethod( - declClass: ClassType, - name: String, - descriptor: Option[MethodDescriptor], - fID: Option[String] = None -) extends APIMethod(fID) { - - def unapply(i: MethodInvocationInstruction): Boolean = { - i.isInstanceMethod && - this.declClass == i.declaringClass && - this.name == i.name && - (this.descriptor.isEmpty || this.descriptor.get == i.methodDescriptor) - } -} - -/** - * Factory for InstanceMethods. - */ -object InstanceAPIMethod { - - def apply( - declClass: ClassType, - name: String - ): InstanceAPIMethod = { - InstanceAPIMethod(declClass, name, None) - } - - def apply( - declClass: ClassType, - name: String, - featureID: String - ): InstanceAPIMethod = { - InstanceAPIMethod(declClass, name, None, Some(featureID)) - } - - def apply( - declClass: ClassType, - name: String, - descriptor: MethodDescriptor - ): InstanceAPIMethod = { - InstanceAPIMethod(declClass, name, Some(descriptor)) - } -} - -/** - * Represents a static API call. - * - * @param declClass ClassType of the receiver. - * @param name Name of the API method. - * @param descriptor Optional method descriptor, is no descriptor assigned, it represents - * all methods with the same name, declared in the same class. - */ -case class StaticAPIMethod( - declClass: ClassType, - name: String, - descriptor: Option[MethodDescriptor], - fID: Option[String] = None -) extends APIMethod(fID) { - - def unapply(i: MethodInvocationInstruction): Boolean = { - !i.isInstanceMethod && - this.declClass == i.declaringClass && - this.name == i.name && - (this.descriptor.isEmpty || this.descriptor.get == i.methodDescriptor) - } -} - -/** - * Factory for InstanceMethods. - */ -object StaticAPIMethod { - - def apply(declClass: ClassType, name: String): StaticAPIMethod = { - StaticAPIMethod(declClass, name, None) - } - - def apply(declClass: ClassType, name: String, featureID: String): StaticAPIMethod = { - StaticAPIMethod(declClass, name, None, Some(featureID)) - } - - def apply( - declClass: ClassType, - name: String, - descriptor: MethodDescriptor - ): StaticAPIMethod = { - StaticAPIMethod(declClass, name, Some(descriptor)) - } -} - -/** - * Represents a collection of API methods that can be mapped to a single feature. Most APIs provide - * multiple or slightly different API methods to achieve a single task, hence, it can be helpful to - * group those methods. - * - * @note It is assumed that the passed featureID is unique throughout all feature extractors. - */ -case class APIFeatureGroup(apiMethods: List[APIMethod], featureID: String) extends APIFeature diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/util/APIFeatureQuery.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/util/APIFeatureQuery.scala deleted file mode 100644 index 14cbf5a3ec..0000000000 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/queries/util/APIFeatureQuery.scala +++ /dev/null @@ -1,145 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package hermes -package queries -package util - -import scala.collection.mutable - -import org.opalj.br.ClassType -import org.opalj.br.MethodWithBody -import org.opalj.br.analyses.Project -import org.opalj.br.instructions.Instruction -import org.opalj.br.instructions.MethodInvocationInstruction -import org.opalj.da.ClassFile - -/** - * A predefined query for finding simple API features. It supports - in particular - - * features that check for certain API calls. Subclasses are only required to define - * a `Chain` of `APIFeatures`. - * - * Example of an `apiFeature` declaration in a subclass: - * {{{ - * override def apiFeatures: Chain[APIFeatures] = Chain[APIFeature]( - * val Unsafe = ClassType("sun/misc/Unsafe") - * - * StaticAPIMethod(Unsafe, "getUnsafe", MethodDescriptor("()Lsun/misc/Unsafe;")), - * APIFeatureGroup( - * Chain(InstanceAPIMethod(Unsafe, "allocateInstance")), - * "Unsafe - Alloc" - * ), - * APIFeatureGroup( - * Chain( - * InstanceAPIMethod(Unsafe, "arrayIndexScale"), - * InstanceAPIMethod(Unsafe, "arrayBaseOffset") - * ), - * "Unsafe - Array" - * ) - * ) - * }}} - * - * @author Michael Reif - */ -abstract class APIFeatureQuery(implicit hermes: HermesConfig) extends FeatureQuery { - - def apiFeatures: List[APIFeature] - - /** - * The unique ids of the computed features. - */ - override lazy val featureIDs: Seq[String] = apiFeatures.map(_.featureID) - - /** - * Returns the set of all relevant receiver types. - */ - private final lazy val apiTypes: Set[ClassType] = { - apiFeatures.foldLeft(Set.empty[ClassType])(_ ++ _.apiMethods.map(_.declClass)) - } - - /** - * Analyzes the project and extracts the feature information. - * - * @note '''Every query should regularly check that its thread is not interrupted!''' E.g., - * using `Thread.currentThread().isInterrupted()`. - */ - override def apply[S]( - projectConfiguration: ProjectConfiguration, - project: Project[S], - rawClassFiles: Iterable[(ClassFile, S)] - ): IterableOnce[Feature[S]] = { - - val classHierarchy = project.classHierarchy - import classHierarchy.allSubtypes - import project.isProjectType - - def getClassFileLocation(classType: ClassType): Option[ClassFileLocation[S]] = { - val classFile = project.classFile(classType) - classFile.flatMap { cf => project.source(cf).map(src => ClassFileLocation(src, cf)) } - } - - var occurrencesCount = - apiFeatures.foldLeft(Map.empty[String, Int])((result, feature) => result + ((feature.featureID, 0))) - - // TODO Use LocationsContainer - val locations = mutable.Map.empty[String, List[Location[S]]] - - for { - classFeature <- apiFeatures.collect { case ce: ClassExtension => ce } - featureID = classFeature.featureID - subtypes = allSubtypes(classFeature.declClass, reflexive = false).filter(isProjectType) - size = subtypes.size - if size > 0 - } { - val count = occurrencesCount(featureID) + size - occurrencesCount += ((featureID, count)) - - for { - subtype <- subtypes - if project.isProjectType(subtype) - classFileLocation <- getClassFileLocation(subtype) - } { - locations += (( - featureID, - classFileLocation :: locations.getOrElse(featureID, List.empty) - )) - } - } - - // Checking method API features - - for { - cf <- project.allProjectClassFiles - if !isInterrupted() - source <- project.source(cf) - case m @ MethodWithBody(code) <- cf.methods - pcAndInvocation <- code.collect({ case mii: MethodInvocationInstruction => mii }: PartialFunction[ - Instruction, - MethodInvocationInstruction - ]) - pc = pcAndInvocation.pc - mii = pcAndInvocation.value - declClass = mii.declaringClass - if declClass.isClassType - if apiTypes.contains(declClass.asClassType) - apiFeature <- apiFeatures - featureID = apiFeature.featureID - APIMethod <- apiFeature.apiMethods - if APIMethod.matches(mii) - } { - val l = InstructionLocation(source, m, pc) - locations += ((featureID, l :: locations.getOrElse(featureID, List.empty))) - val count = occurrencesCount(featureID) + 1 - occurrencesCount = occurrencesCount + ((featureID, count)) - } - - apiFeatures.map { apiFeature => - val featureID = apiFeature.featureID - Feature( - featureID, - occurrencesCount(featureID), - locations.getOrElse(featureID, List.empty) - ) - } - } - -} diff --git a/build.sbt b/build.sbt index f620123fc1..44542d0ec7 100644 --- a/build.sbt +++ b/build.sbt @@ -155,7 +155,6 @@ lazy val `OPAL` = (project in file(".")) .disablePlugins(HeaderPlugin) // The root project has no sources and no configured license header .settings( ScalaUnidoc / unidoc / unidocProjectFilter := inAnyProject -- inProjects( - hermes, validate, demos, tools @@ -179,7 +178,6 @@ lazy val `OPAL` = (project in file(".")) framework, // bp, (just temporarily...) tools, - hermes, ce, validate, // Not deployed to maven central demos // Not deployed to maven central @@ -427,18 +425,6 @@ lazy val `BugPicker` = (project in file("TOOLS/bp")) .configs(IntegrationTest) */ -lazy val hermes = `Hermes` - -lazy val `Hermes` = (project in file("TOOLS/hermes")) - .settings(buildSettings *) - .settings( - name := "Hermes", - libraryDependencies ++= Dependencies.hermes, - Compile / doc / scalacOptions ++= Opts.doc.title("OPAL - Hermes") - ) - .dependsOn(framework % "it->it;it->test;test->test;compile->compile") - .configs(IntegrationTest) - lazy val tools = `Tools` lazy val `Tools` = (project in file("DEVELOPING_OPAL/tools")) @@ -478,8 +464,7 @@ lazy val `Validate` = (project in file("DEVELOPING_OPAL/validate")) ) .dependsOn( tools % "it->it;it->test;test->test;compile->compile", - demos % "it->it;it->test;test->test;compile->compile", - hermes % "it->it;test->test;compile->compile" + demos % "it->it;it->test;test->test;compile->compile" ) .configs(IntegrationTest) @@ -518,9 +503,8 @@ lazy val `ConfigurationExplorer` = (project in file("TOOLS/ce")) .dependsOn( br % "compile->compile", apk % "runtime->compile", - demos % "runtime->compile", - // bp % "runtime->compile", - hermes % "runtime->compile" + demos % "runtime->compile" + // bp % "runtime->compile" ) .configs(IntegrationTest) @@ -596,7 +580,6 @@ runProjectDependencyGeneration := { mmd.append(""" | style Common fill:#9cbecc,color:black | style Framework fill:#c0ffc0 - | style Hermes fill:#ffd7cf | |""".stripMargin) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 3d158f2e4e..02d8195cf3 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -60,7 +60,6 @@ object Dependencies { "com.fasterxml.jackson.dataformat" % "jackson-dataformat-csv" % version.jacksonDF withSources () withJavadoc () val fastutil = "it.unimi.dsi" % "fastutil" % version.fastutil withSources () withJavadoc () val scallop = "org.rogach" %% "scallop" % version.scallop - val javafxBase = "org.openjfx" % "javafx-base" % version.openjfx classifier osName val apkparser = "net.dongliu" % "apk-parser" % version.apkparser val scalagraphcore = "org.scala-graph" %% "graph-core" % version.scalagraphcore val scalagraphdot = "org.scala-graph" %% "graph-dot" % version.scalagraphdot @@ -87,7 +86,6 @@ object Dependencies { val ifds = Seq() val ide = Seq() val tools = Seq(txtmark, jacksonDF) - val hermes = Seq(txtmark, jacksonDF, javafxBase) val apk = Seq(apkparser, scalaxml) val ce = Seq(commonstext) }