Download The OPAL Framework - A Tutorial.key

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
Prerequisites
•
Install Scala IDE for Scala 2.11.2 from www.scala-ide.org
•
Install Scala 2.11.2 from www.scala-lang.org
•
Install SBT 0.13.6 from www.scala-sbt.org
•
Create a clone of the master branch of OPAL
www.bitbucket.org/delors/opal
•
To test that everything works smoothly compile the project:
•
•
•
Test the project
•
sbt test
•
sbt it:test
Create and then import the Eclipse projects
•
•
sbt clean copyResources test:compile
sbt eclipse
Run again (sometimes some resources are forgotten and something won’t work (tools etc.)…)
•
sbt copyResources
The OPAL Framework
A Tutorial
Michael Eichberg
Oktober 2014
2
Architecture
Abstract Interpretation Framework!
Analyses that are concerned with a program’s control and data-flow (and which provides
reasonable abstractions thereof)
“resolved” Bytecode Representation!
Object-oriented representation of Java class files
Well suited for lightweight static analyses (e.g., to analyze an application’s static structure, to
find instances of bug patterns.)
Bytecode Infrastructure!
Generic infrastructure for parsing Java class files.
Abstract Interpretation Framework!
Analyses that are concerned with a program’s control and data-flow (and which provides
reasonable abstractions thereof)
“resolved” Bytecode Representation!
Lightweight static analyses (e.g., to analyze an application’s static structure, to find instances
of bug patterns.)
Bytecode Infrastructure!
Low-level “analyses” directly on top the bytecode representation (e.g., to do bytecode
verification, to create other representations of Java bytecode, to find instances of bug patterns
(sequence of bytecode instructions).)
The Bytecode
Representation
Part I
The package: org.opalj.br
•
Classes used for the representation of Java class files. Basically,
each major element of a class file (Method Declaration, Field
Declaration, each “Attribute”,…) is represented using one class.
(The code base and the JVM specification are well-aligned.)
•
By default all information is represented.
(Except of the BootstrapMethodTable, which is resolved; the
information is directly attached to the respective
Invokedynamic instructions.)
•
Traversal of class files is primarily facilitated by Pattern
Matching.
(Signatures also support the traversal using a Visitor)
The package: org.opalj.br
•
Main classes:
•
ClassFile
(java.lang.Object is the superclass of all classes except of itself.)
•
•
Method
•
MethodDescriptor (Parameter types and return type.)
•
Code (If the method is non-native and non-abstract.)
The instructions array may contain “null” values.
Defines many helper methods to facilitate the traversal of the
instructions array.
Field
The package: org.opalj.br
•
Main classes:
•
Type
Types can be compared using reference comparison (eq).
All types are associated with a unique id.
•
ReferenceType
•
ObjectType
The name uses the binary notation. Not java.lang.Object, but java/lang/Object)
Common ObjectTypes are predefined.
•
ArrayType
The package: org.opalj.br.instructions
•
Representation of a method’s instructions.
The type hierarchy and many extractors facilitate the matching of byte code
sequences/facilitate the abstraction over different types of byte code
instructions.
•
Important Concepts
•
In Java Bytecode we have four(five) different instructions to invoke a method
(Invokespecial, Invokestatic, Invokevirtual,
Invokeinterface)
•
Creating a class is a two step process:
•
allocating the memory (using New)
•
invoking the constructor (using Invokespecial)
Example - Matching Instructions
The package: org.opalj.br.instructions
val superClass : ObjectType = …
for {
method @ MethodWithBody(body) ← classFile.methods
body.instructions.exists {
case INVOKESPECIAL(
`superClass`,
"clone",
MethodDescriptor(Seq(), ObjectType.Object))
case _
}
} yield …
false;
true;
The package: org.opalj.br.reader
•
•
Classes to read in Java Class Files. Main classes:
•
Java8Framework
Represents “all” information. (Attributes that are not supported are discarded.)
•
Java8LibraryFramework
Only represents those information that describe the
public interface.
In most cases, however, it is more useful to load class
files (implicitly) using a Project.
Adhoc Analyses
Prototyping Analyses
Using the Scala REPL
http://www.blackrocktools.com
Tekton 3030, 16oz Wood Rip Hammer - 3030
The Scala REPL
OPAL is easy to explore using Scala’s REPL
OPAL facilitates prototyping (parts of) analyses using the REPL
Get the number of abstract methods defined in the rt.jar
pc-eichberg:OPAL eichberg$ sbt!
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=384m; support was removed in 8.0!
[info] Loading project definition from /Users/eichberg/Code/OPAL/project!
[info] Set current project to OPAL Library (in build file:/Users/eichberg/Code/OPAL/)!
> project BytecodeRepresentation!
[info] Set current project to Bytecode Representation (in build file:/Users/eichberg/Code/OPAL/)!
> console!
[info] Starting scala interpreter...!
[info] !
Welcome to Scala version 2.11.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_05).!
Type in expressions to have them evaluated.!
Type :help for more information.!
!
scala> import org.opalj.br.reader.Java8Framework._!
import org.opalj.br.reader.Java8Framework._!
!
val cfs = ClassFiles(new java.io.File("/Library/Java/JavaVirtualMachines/
jdk1.8.0_05.jdk/Contents/Home/jre/lib/rt.jar"))!
scala>
cfs: Seq[(org.opalj.br.reader.Java8Framework.ClassFile, java.net.URL)] =!
List((ClassFile(!
!
public [SUPER] java.lang.SecurityManager!
!
extends java.lang.Object!
!
{version=52.0}!
),jar:file:/Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/jre/lib/rt.jar!/java/lang/
Short$ShortCache.class), (ClassFile(!
!
[SUPER] java.lang.Shut...!
scala> cfs.size!
res0: Int = 20526!
scala> cfs.map(cf => cf._1.methods.filter(_.isAbstract).size).sum!
res3: Int = 16793!
!
scala>
:q!
Leave the console and return to sbt.
Exercise
•
Get the number of abstract methods defined in abstract
classes (not interfaces).
•
Help:
•
www.opal-project.de/library/api/SNAPSHOT
•
www.scala-lang.org/api/2.11.2
scala> import org.opalj.br.reader.Java8Framework._!
import org.opalj.br.reader.Java8Framework._!
!
val cfs = ClassFiles(new java.io.File(“/Library/Java/
JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/jre/lib/rt.jar"))!
scala>
!
cfs.filter(cf => !cf._1.isInterfaceDeclaration).map(cf =>
cf._1.methods.filter(_.isAbstract).size).sum!
scala>
Solution
res3: Int = 4587
The package: org.opalj.br.analyses
•
Commonly useful analyses or classes that support the development of analyses.
•
Project
Primary abstraction of a Java Project. Serves as a container for global information (e.g. the call graph, the class
hierarchy). Enables the navigation from Methods, Fields and ObjectTypes to
ClassFiles.
•
ClassHierarchy
Representation of the class hierarchy. Support methods to calculate least upper
type bounds and to resolve method and field references. Supports partial class
hierarchies.
•
AnalysisExecutor
Template class that facilitates the development of new analyses.
Get all classes that extend java.util.AbstractList
(including anonymous classes.)
> console!
[info] Starting scala interpreter...!
[info] !
Welcome to Scala version 2.11.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_05).!
Type in expressions to have them evaluated.!
Type :help for more information.!
!
val p = org.opalj.br.analyses.Project(new java.io.File("/Library/Java/
JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/jre/lib/rt.jar"))!
scala>
p: org.opalj.br.analyses.Project[java.net.URL] =!
Project(!
!
java.awt.geom.Rectangle2D « jar:file:/Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/
Home/jre/lib/rt.jar!/java/awt/geom/Rectangle2...!
scala> p.classHierarchy.allSubtypes(org.opalj.br.ObjectType("java/util/
AbstractList"),false).filter(_.packageName.startsWith("java/")).mkString("\n")!
res0: String =!
ObjectType(java/util/Collections$EmptyList)!
ObjectType(java/util/RandomAccessSubList)!
ObjectType(java/util/ArrayList)!
ObjectType(java/util/concurrent/CopyOnWriteArrayList$COWSubList)!
ObjectType(java/util/LinkedList)!
ObjectType(java/util/Vector)!
ObjectType(java/util/SubList)!
ObjectType(java/util/ArrayList$SubList)!
ObjectType(java/util/Stack)!
ObjectType(java/util/AbstractSequentialList)!
ObjectType(java/util/Collections$CopiesList)!
ObjectType(java/util/Collections$SingletonList)!
ObjectType(java/util/Arrays$ArrayList)!
!
scala>
AnalysisExecutor
The package: org.opalj.br.analyses
package org.opalj.ai.tutorial.base
!
import
import
import
import
import
java.net.URL
org.opalj._
org.opalj.br._
org.opalj.br.analyses._
org.opalj.ai._
!
object AnalysisTemplate extends AnalysisExecutor {
!
val analysis = new OneStepAnalysis[URL, BasicReport] {
!
override def doAnalyze(
theProject: Project[URL],
parameters: Seq[String],
isInterrupted: ()
Boolean) = {
// HERE GOES YOUR ANALYSIS
BasicReport(theProject.statistics.mkString("\n"))
}
}
}
The Bytecode Representation
•
General Design Decisions
•
All classes are (effectively) immutable.
(Supports parallelization.)
•
Extensive support for pattern matching.
If available, always use the accessor methods offered by the
class that manages the collection and not the collection itself.
E.g., to find a method with a specific name, use the respective method
defined by Method and do not use the collection’s find method.
•
No “null” values.
(There is only one exception: the instructions array)
Example - clone method calls super.clone()
The Bytecode Representation
for {
classFile ← classFiles
if !classFile.isInterfaceDeclaration && !classFile.isAnnotationDeclaration
superClass ← classFile.superclassType.toList
method @ Method(_, "clone", MethodDescriptor(Seq(), ObjectType.Object)) ← classFile.methods
if !method.isAbstract
if !method.body.get.instructions.exists {
case INVOKESPECIAL(
`superClass`,
"clone",
MethodDescriptor(Seq(), ObjectType.Object))
true;
case _
false;
}
} yield (classFile /*.thisClass.className*/ , method /*.name*/ )
Exercise
•
Develop an analysis that collects all constructor calls in all
methods that create an instance of java.io.File
using the constructor (File(String s)).
•
The analysis should return a triple of type:
( /*Declaring Class*/ ClassFile,
/*Caller*/ Method,
/*PCs of constructor calls*/ Set[PC])
I.e., for each method the set of program counters that
call the respective constructor should be returned.
•
Execute the analysis in parallel.
import
import
import
import
import
import
!
java.net.URL
org.opalj._
org.opalj.br._
org.opalj.br.analyses._
org.opalj.br.instructions._
org.opalj.ai._
object IdentifyResourcesAnalysis extends AnalysisExecutor {
!
!
val analysis = new OneStepAnalysis[URL, BasicReport] {
override def title: String = "File Object Creation Using Constant Strings"
!
override def description: String =
"Identifies java.io.File object instantiations using constant strings."
!
override def doAnalyze(theProject: Project[URL], parameters: Seq[String], isInterrupted: ()
Boolean) = {
Solution
val callSites = (for {
cf ← theProject.classFiles.par
m @ MethodWithBody(body) ← cf.methods
} yield {
val pcs = for {
pc ← body.collectWithIndex {
case (
pc,
INVOKESPECIAL(
ObjectType("java/io/File"),
"<init>",
SingleArgumentMethodDescriptor((ObjectType.String, VoidType)))
)
pc
}
} yield pc
(cf, m, pcs)
}).filter(_._3.size > 0)
BasicReport(…)
}
}
}
Abstract Interpretation Framework!
Analyses that are concerned with a program’s control and data-flow (and which provides
reasonable abstractions thereof)
“resolved” Bytecode Representation!
Lightweight static analyses (e.g., to analyze an application’s static structure, to find instances
of bug patterns.)
Bytecode Infrastructure!
Low-level “analyses” directly on top the bytecode representation (e.g., to do bytecode
verification, to create other representations of Java bytecode, to find instances of bug patterns
(sequence of bytecode instructions).)
The Abstract
Interpretation Framework
Part II
The Abstract Interpretation
Framework
•
Static analysis framework that is inspired by Model Checking/
Symbolic Execution and Abstract Interpretation
•
Works directly on top of Java bytecode to reduce overhead (I.e., OPAL does not use an intermediate representation, such as SSA.)
•
Complexity of Java bytecode is completely hidden within OPAL
•
"Functional Style" to facilitate parallelization/to avoid hard to find
bugs (most objects cannot be mutated and those that can be
mutated are not expected to be mutated)
•
User defined precision (no framework inherent limitations)
An analysis that reports all return instructions of all methods
that return an integer value where the return value is
bounded.
public void myMethod(int i) {
if (i < 0)
return -1; // <= bounded; should be returned.
else
return i;
// <= unbounded
}
!
It basically (re)uses the predefined abstract domains.
!
The analysis does not consider calls to further methods.
Identifying ireturn instructions that return bounded integer
values.
> console!
import
import
import
import
import
import
!
org.opalj._!
org.opalj.br._!
org.opalj.br.analyses._!
org.opalj.ai._!
org.opalj.ai.domain._!
org.opalj.ai.domain.l1._!
val p = Project(new java.io.File("/Library/Java/JavaVirtualMachines/
jdk1.8.0_05.jdk/Contents/Home/jre/lib/rt.jar"))!
!
val methods = p.methods.filter(m => m.body.isDefined && m.descriptor.returnType ==
IntegerType)!
!
class TheDomain(p:Project[java.net.URL],cf : ClassFile,m : Method) extends
DefaultDomain(p,cf,m) with RecordLastReturnedValues!
!
methods.foreach{m => !
val cf = p.classFile(m)!
val r = BaseAI(cf,m,new TheDomain(p,p.classFile(m),m)).domain!
== 1)!
r.allReturnedValues.foreach(_ if(r.allReturnedValues.size
match {!
(if you want to get all methods with a single return instruction)
case (pc,r.IntegerRange(x,y))
=> !
println(cf.thisType.toJava+"{ "+m.toJava+"; pc="+pc+":["+x+","+y+"]"+" }")!
case _ => /***/!
})!
}
sun.awt.image.ImagingLib{ int getNativeOpIndex(java.lang.Class); pc=30:[-1,2] }
Identifying ireturn instructions that return bounded integer
values.
> console!
org.opalj._!
org.opalj.br._!
137 private static int getNativeOpIndex(Class opClass) {
org.opalj.br.analyses._!
138 //
org.opalj.ai._!
139 // Search for this class in cached list of
org.opalj.ai.domain._!
140 // classes supplying native acceleration
org.opalj.ai.domain.l1._!
141 //
!
142 int opIndex = -1;
val p = Project(new
java.io.File("/Library/Java/JavaVirtualMachines/
143 for (int i=0; i<NUM_NATIVE_OPS; i++) {
jdk1.8.0_05.jdk/Contents/Home/jre/lib/rt.jar"))!
144 if (opClass == nativeOpClass[i]) {
!
145 opIndex = i;
val methods
= p.methods.filter(m => m.body.isDefined
&& m.descriptor.returnType ==
146 break;
IntegerType)!
147 }
!
148 }
class TheDomain(p:Project[java.net.URL],cf
: ClassFile,m : Method) extends
DefaultDomain(p,cf,m)
with RecordLastReturnedValues!
149 return opIndex;
!
150 }
methods.foreach{m => !
val cf = p.classFile(m)!
val r = BaseAI(cf,m,new TheDomain(p,p.classFile(m),m)).domain!
r.allReturnedValues.foreach(_ match {!
case (pc,r.IntegerRange(x,y)) => !
println(cf.thisType.toJava+"{ "+m.toJava+"; pc="+pc+":["+x+","+y+"]"+" }")!
case _ => /***/!
})!
}
this constant is “3”
jd
k
1.
8.
0_
05
import
import
import
import
import
import
sun.awt.image.ImagingLib{ int getNativeOpIndex(java.lang.Class); pc=30:[-1,2] }
Identifying areturn instructions that return null values.
> console!
import
import
import
import
import
import
!
org.opalj._!
org.opalj.br._!
org.opalj.br.analyses._!
org.opalj.ai._!
org.opalj.ai.domain._!
org.opalj.ai.domain.l1._!
val p = Project(new java.io.File("/Library/Java/JavaVirtualMachines/
jdk1.8.0_05.jdk/Contents/Home/jre/lib/rt.jar"))!
!
val methods = p.methods.filter(m => m.body.isDefined &&
m.descriptor.returnType.isObjectType)!
!
class TheDomain(p:Project[java.net.URL],cf : ClassFile,m : Method) extends
DefaultDomain(p,cf,m) with RecordLastReturnedValues!
!
methods.foreach{m => !
val cf = p.classFile(m)!
val r = BaseAI(cf,m,new TheDomain(p,p.classFile(m),m)).domain!
r.allReturnedValues.foreach(_ match {!
case (pc,r.NullValue()) => !
println(cf.thisType.toJava+"{ "+m.toJava+"; pc="+pc)!
case _ => /***/!
})!
}
Understanding
Available Domains
OPAL comes with several tools
that facilitate the development of
analyses using the abstract
interpretation framework.
http://www.blackrocktools.com
Tekton 3030, 16oz Wood Rip Hammer - 3030
Understanding the result of an abstract interpretation of a method.
static void cfDependentValues4() {
int b = 0;
int c = 0;
int i = 0;
int j = i; // j is just an alias for i
while (j < 2) {
int a = anInt();
if (i == 1)
b = a;
else
c = a;
i++;
j = i;
}
if (i == 2) {
doIt(j); // j == 2
doIt(b); // some value (but not "c")
doIt(c); // some value (but not "b")
}
}
The XHTML Tracer (0.1)
an instruction where two or
more paths join
The XHTML Tracer (0.1)
10
1
3
2
9
5
4
6
8
7
The AI performs a strict depth-first evaluation.
The XHTML Tracer (0.1)
The AI performs a strict depth-first evaluation.
Four Different Approaches to Develop Analyses
Using the AI Framework can be Distinguished
•
by reusing a pre-configured Domain to pre-analyze the
method and then analyzing the result
•
by configuring a Domain w.r.t. the analysis task at hand
(By means of mixin-composition of existing partial domains.)
•
by adapting/configuring a Domain (By means of subclassing a Domain.)
•
by developing a new Domain
Custom analysis are built by tailoring the “Domain”.
The Abstract
Interpretation
Framework
Domain
The Analysis’
Domain
Base Domains
FieldAccessesDomain
VMLevelExceptionsFactory
DoubleValuesDomain
DoubleValuesFactory
CoreDomain
FloatValuesDomain
FloatValuesFactory
LongValuesDomain
LongValuesFactory
IntegerValuesDomain
IntegerValuesFactory
PrimitiveValuesConversionsDomain
JoinStabilization
PrimitiveValuesFactory
ReturnInstructionsDomain
ReferenceValuesFactory
TypedValuesFactory
ReferenceValuesDomain
MonitorInstructionsDomain
MethodCallsDomain
DefaultDomainValueBinding
The Core Domains
Domain
Commonly Useful Helper Domains
General Support
ConcreteIntegerValues
ConcreteLongValues
Origin
TheProject
ProjectBasedClassHierarchy
ClassHierarchy
TheCode
TheMethod
Configuration
Base Domains
FieldAccessesDomain
VMLevelExceptionsFactory
DoubleValuesDomain
DoubleValuesFactory
CoreDomain
FloatValuesDomain
FloatValuesFactory
LongValuesDomain
LongValuesFactory
IntegerValuesDomain
IntegerValuesFactory
PrimitiveValuesConversionsDomain
JoinStabilization
PrimitiveValuesFactory
ReturnInstructionsDomain
ReferenceValuesFactory
TypedValuesFactory
ReferenceValuesDomain
MonitorInstructionsDomain
MethodCallsDomain
DefaultDomainValueBinding
Domain
Currently Available Domains
ArrayValues, BaseConfigurableDomain, BaseDomain, BaseDomain,
CHACallGraphDomain, CallGraphDomain, CalledTaintAnalysisDomain, ClassValues,
ConstantFieldValuesResolution, DefaultArrayValuesBinding,
DefaultCHACallGraphDomain, DefaultClassValuesBinding,
DefaultConfigurableDomain, DefaultConfigurableDomain, DefaultDomain,
DefaultDomain, DefaultDomainValueBinding, DefaultIntegerRangeValues,
DefaultLongValues, DefaultPreciseIntegerValues, DefaultPreciseLongValues,
DefaultReferenceValuesBinding, DefaultReferenceValuesBinding,
DefaultStringValuesBinding, DefaultTypeLevelDoubleValues,
DefaultTypeLevelFloatValues, DefaultTypeLevelIntegerValues,
DefaultTypeLevelLongValues, DefaultTypeLevelReferenceValues,
DefaultVTACallGraphDomain, Domain, IincTracingDomain, JoinStabilization,
PerInstructionPostProcessing, PropertyTracing, RecordConstraints,
RecordVoidReturns, ReferenceValues, ReifiedConstraints, RootTaintAnalysisDomain,
SimpleBooleanPropertyTracing, StringValues, TaintAnalysisDomain,
TypeLevelDomain, TypeLevelIntegerValues, VTACallGraphDomain,
ValuesCoordinatingDomain
The Domain used to calculate a Call Graph
class DefaultCHACallGraphDomain[Source](
val project: Project[Source],
val cache: CallGraphCache[MethodSignature, Set[Method]],
val classFile: ClassFile,
val method: Method)
extends Domain
with DefaultDomainValueBinding
with ThrowAllPotentialExceptionsConfiguration
with TheProject[Source]
with TheMethod
with ProjectBasedClassHierarchy
with DefaultHandlingOfMethodResults
with IgnoreSynchronization
with l0.DefaultTypeLevelIntegerValues
with l0.DefaultTypeLevelLongValues
with l0.DefaultTypeLevelFloatValues
with l0.DefaultTypeLevelDoubleValues
with l0.DefaultReferenceValuesBinding
with l0.TypeLevelFieldAccessInstructions
with l0.TypeLevelInvokeInstructions
with CHACallGraphDomain
Most Relevant Classes of the AI Framework
•
AI (BaseAI)
The abstract interpreter.
•
(Core)Domain
The interface between the abstract interpreter and the
user-defined/user-provided domain.
•
DomainValue
Abstraction of a concrete runtime-value.
A DomainValue is bound to its domain; the type system
will ensure that domain values are not passed between
different Domain instances.
Parallelization
•
The abstract interpretation framework supports
parallelization at the level of methods; i.e., it supports the
concurrent abstract interpretation of multiple methods. Requirement:
•
the Domain contains no state that is dependent on the
evaluation context
•
every Method is analyzed using its own Domain
object (default model)
A Simple Dead-Code Analysis - Idea
•
First, identify all if instructions.
•
Second, check if every branch is taken. (A branch is never taken if no state is associated with the operands/
locals on the respective branch.)
A Simple Dead-Code Analysis
class AnalysisDomain(
override val project: Project[java.net.URL],
val method: Method)
extends Domain
with domain.DefaultDomainValueBinding
with domain.ThrowAllPotentialExceptionsConfiguration
with domain.l0.DefaultTypeLevelFloatValues
with domain.l0.DefaultTypeLevelDoubleValues
with domain.l0.TypeLevelFieldAccessInstructions
with domain.l0.TypeLevelInvokeInstructions
with domain.l1.DefaultReferenceValuesBinding
with domain.l1.DefaultIntegerRangeValues
with domain.l1.DefaultLongValues
with domain.l1.LongValuesShiftOperators
with domain.l1.DefaultConcretePrimitiveValuesConversions
with domain.DefaultHandlingOfMethodResults
with domain.IgnoreSynchronization
with domain.TheProject[java.net.URL]
with domain.TheMethod
with domain.ProjectBasedClassHierarchy
executed in parallel
A Simple Dead-Code Analysis
In most cases you want to create a new
domain object per analyzed method.
val methodsWithDeadCode = {
val results = new java.util.concurrent.ConcurrentLinkedQueue[DeadCode]()
for {
classFile ← theProject.classFiles.par
method @ MethodWithBody(body) ← classFile.methods
domain = new AnalysisDomain(theProject, method)
result = BaseAI(classFile, method, domain)
operandsArray = result.operandsArray
(ctiPC, instruction, branchTargetPCs) ← body collectWithIndex {
case (ctiPC, instruction @ ConditionalControlTransferInstruction()) if operandsArray(ctiPC) != null
(ctiPC, instruction, instruction.nextInstructions(ctiPC, body))
}
branchTarget ← branchTargetPCs
if operandsArray(branchTarget) == null
} {
val operands = operandsArray(ctiPC).take(2)
results.add(
DeadCode(classFile, method, ctiPC, body.lineNumber(ctiPC), instruction, operands)
)
}
scala.collection.JavaConversions.collectionAsScalaIterable(results)
}
thr
ea
sa d
fe!
Scala Voodoo - Path-Dependent Types
From “Programming in Scala”
… As the term “path-dependent type” says, the type depends on the path: in
general, different paths give rise to different types….
•
The abstract interpretation framework makes heavy use
of path-dependent types to ensure that (domain) values
are not accidentally passed from domain to another
domain/compared across domain boundaries.
Scala Voodoo - Path-Dependent Types
From “Programming in Scala”
… As the term “path-dependent type” says, the type depends on the path: in
general, different paths give rise to different types….
OPAL
Example
trait CoreDomain {
!
trait Value { …
}
}
val d1 = new MyDomain()
val d1v = d1.IntegerRange(0,1)
!
val d2 = new MyDomain()
val d2v = d2.IntegerRange(0,1)
!
d1v.join(d2v)
If you need to compare/transfer values between domains perform an adaptation using adapt. (E.g., d1v.adapt(..d2)). It may be useful to create one (central/shared/common) Domain for this purpose.
Scala Voodoo - Path-Dependent Types
From “Programming in Scala”
… As the term “path-dependent type” says, the type depends on the path: in
general, different paths give rise to different types….
Pattern Matching on the Result of an Abstract Interpretation
methods.foreach{m => val cf = p.classFile(m)
val
d
= BaseAI(cf,m,new TheDomain(p,p.classFile(m),m)).
domain
r.allReturnedValues.foreach(_ match {
case (pc,
d.IntegerRange(x,y))
println(…)
case _ => /***/
})
}
=> the domain used to
perform the abstract
interpretation
Scala Voodoo - Self Types
From “Programming in Scala”
Technically, a self type is an assumed type for this whenever this is mentioned within the class.
Pragmatically, a self type specifies the requirements on any concrete class the trait is mixed into.
•
The abstract interpretation framework uses self types to
specify inter-domain dependencies.
Example
trait DefaultStringValuesBinding extends DefaultReferenceValuesBinding with StringValues {
!
domain:
}
…
IntegerValuesDomain with TypedValuesFactory with Configuration with ClassHierarchy
Exercise
•
•
Develop an analysis that collects all constructor calls in all
methods that create an instance of java.io.File using the
constructor (File(String s)) and where the parameter is a
constant string. (Consider the intra-procedural case only)
•
The analysis should print out the name of the methods
identified by the analysis.
•
Execute the analysis in parallel.
Hint: You need to do an abstract interpretation using the
domain “l1.DefaultStringValuesBinding”.
import
import
import
import
import
import
!
java.net.URL
org.opalj._
org.opalj.br._
org.opalj.br.analyses._
org.opalj.br.instructions._
org.opalj.ai._
object IdentifyResourcesAnalysis extends AnalysisExecutor {
!
!
val analysis = new OneStepAnalysis[URL, BasicReport] {
override def title: String = "File Object Creation Using Constant Strings"
!
override def description: String =
"Identifies java.io.File object instantiations using constant strings."
Previous Solution
!
override def doAnalyze(theProject: Project[URL], parameters: Seq[String], isInterrupted: ()
Boolean) = {
val callSites = (for {
cf ← theProject.classFiles.par
m @ MethodWithBody(body) ← cf.methods
} yield {
val pcs = for {
pc ← body.collectWithIndex {
case (
pc,
INVOKESPECIAL(
ObjectType("java/io/File"),
"<init>",
SingleArgumentMethodDescriptor((ObjectType.String, VoidType)))
)
pc
}
} yield pc
(cf, m, pcs)
}).filter(_._3.size > 0)
BasicReport(…)
}
}
}
!
class AnalysisDomain(
override val project: Project[URL],
val method: Method)
extends Domain with domain.TheProject[URL] with domain.TheMethod
with domain.DefaultDomainValueBinding
with domain.ThrowAllPotentialExceptionsConfiguration
with domain.l0.DefaultTypeLevelIntegerValues
with domain.l0.DefaultTypeLevelLongValues
with domain.l0.DefaultTypeLevelFloatValues
with domain.l0.DefaultTypeLevelDoubleValues
with domain.l0.TypeLevelFieldAccessInstructions
with domain.l0.TypeLevelInvokeInstructions
with domain.l1.DefaultStringValuesBinding
with domain.DefaultHandlingOfMethodResults
with domain.IgnoreSynchronization
with domain.ProjectBasedClassHierarchy
val callSitesWithConstantStringParameter =
for {
(cf, m, pcs) ← callSites
result = BaseAI(cf, m, new AnalysisDomain(theProject, m))
(pc, value) ← pcs.map(pc
(pc, result.operandsArray(pc))).collect {
case (pc, result.domain.StringValue(value) :: _)
Solution
!
(pc, value)
}
} yield (cf, m, pc, value)
def callSiteToString(callSite: (ClassFile, Method, PC, String)): String = {
val (cf, m, pc, v) = callSite
cf.thisType.toJava+"{ "+m.toJava+"{"+pc+": \""+v+"\" } }"
}
BasicReport(
callSitesWithConstantStringParameter.map(callSiteToString(_)).
mkString("Methods:\n", "\n", ".\n"))
}
}
}
The OPAL Framework
Static Analysis is Easy