-
Notifications
You must be signed in to change notification settings - Fork 58
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#376 Implement more efficient Class Hierarchy Analysis for method de-…
…virtualization (#377) * #376 Implement more efficient Class Hierarchy Analysis for method de-virtualization
- Loading branch information
1 parent
bd9d1ff
commit a6ba5bf
Showing
11 changed files
with
236 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
63 changes: 63 additions & 0 deletions
63
core/src/main/java/de/mirkosertic/bytecoder/core/Statistics.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
/* | ||
* Copyright 2018 Mirko Sertic | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package de.mirkosertic.bytecoder.core; | ||
|
||
import de.mirkosertic.bytecoder.api.Logger; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
public class Statistics { | ||
|
||
public static class Counter { | ||
private int value; | ||
|
||
public void increment() { | ||
value++; | ||
} | ||
} | ||
|
||
public static class Context { | ||
private final Map<String, Counter> counter; | ||
|
||
Context() { | ||
counter = new HashMap<>(); | ||
} | ||
|
||
public Counter counter(final String name) { | ||
return counter.computeIfAbsent(name, t -> new Counter()); | ||
} | ||
|
||
} | ||
|
||
private final Map<String, Context> contexts; | ||
|
||
Statistics() { | ||
contexts = new HashMap<>(); | ||
} | ||
|
||
public Context context(final String name) { | ||
return contexts.computeIfAbsent(name, t -> new Context()); | ||
} | ||
|
||
public void writeTo(final Logger logger) { | ||
for (final Map.Entry<String, Context> ctx : contexts.entrySet()) { | ||
for (final Map.Entry<String, Counter> ctn : ctx.getValue().counter.entrySet()) { | ||
logger.info("[Statistics] {}::{} = {}", ctx.getKey(), ctn.getKey(), ctn.getValue().value); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
102 changes: 102 additions & 0 deletions
102
core/src/main/java/de/mirkosertic/bytecoder/ssa/ClassHierarchyAnalysis.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
/* | ||
* Copyright 2020 Mirko Sertic | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package de.mirkosertic.bytecoder.ssa; | ||
|
||
import de.mirkosertic.bytecoder.classlib.Array; | ||
import de.mirkosertic.bytecoder.core.BytecodeLinkedClass; | ||
import de.mirkosertic.bytecoder.core.BytecodeLinkerContext; | ||
import de.mirkosertic.bytecoder.core.BytecodeMethod; | ||
import de.mirkosertic.bytecoder.core.BytecodeMethodSignature; | ||
import de.mirkosertic.bytecoder.core.BytecodeObjectTypeRef; | ||
import de.mirkosertic.bytecoder.core.BytecodeTypeRef; | ||
import de.mirkosertic.bytecoder.graph.Edge; | ||
|
||
import java.util.HashSet; | ||
import java.util.Optional; | ||
import java.util.Set; | ||
import java.util.function.Predicate; | ||
|
||
public class ClassHierarchyAnalysis { | ||
|
||
private final BytecodeLinkerContext linkerContext; | ||
|
||
public ClassHierarchyAnalysis(final BytecodeLinkerContext linkerContext) { | ||
this.linkerContext = linkerContext; | ||
} | ||
|
||
public Optional<BytecodeLinkedClass> classProvidingInvokableMethod(final String aMethodName, | ||
final BytecodeMethodSignature aSignature, | ||
final BytecodeTypeRef aInvocationTarget, | ||
final Value aReceiver, | ||
final Predicate<BytecodeLinkedClass> aClassFilter, | ||
final Predicate<BytecodeMethod> aMethodFilter) { | ||
final BytecodeLinkedClass theInvocationTarget; | ||
if (aInvocationTarget.isArray()) { | ||
theInvocationTarget = linkerContext.resolveClass(BytecodeObjectTypeRef.fromRuntimeClass(Array.class)); | ||
} else { | ||
theInvocationTarget = linkerContext.resolveClass((BytecodeObjectTypeRef) aInvocationTarget); | ||
} | ||
|
||
if (aClassFilter.test(theInvocationTarget)) { | ||
// We don't have to check the type hierarchy for final classes | ||
// Finding the implementation type can be done faster in this case | ||
if (theInvocationTarget.getBytecodeClass().getAccessFlags().isFinal()) { | ||
BytecodeLinkedClass theCurrent = theInvocationTarget; | ||
while (theCurrent != null) { | ||
final BytecodeMethod m = theCurrent.getBytecodeClass().methodByNameAndSignatureOrNull(aMethodName, aSignature); | ||
if (m != null && aMethodFilter.test(m)) { | ||
return Optional.of(theCurrent); | ||
} | ||
theCurrent = theCurrent.getSuperClass(); | ||
} | ||
return Optional.empty(); | ||
} | ||
|
||
// We have to check the whole type hierarchy | ||
final Set<BytecodeLinkedClass> theResult = new HashSet<>(); | ||
linkerContext.linkedClasses() | ||
.map(Edge::targetNode) | ||
.filter(aClassFilter) | ||
.filter(t -> { | ||
final Set<BytecodeLinkedClass> theImplementingTypes = t.getImplementingTypes(); | ||
return theImplementingTypes.contains(theInvocationTarget); | ||
}) | ||
.forEach(clz -> { | ||
BytecodeLinkedClass theCurrent = clz; | ||
test: | ||
while (theCurrent != null) { | ||
final BytecodeMethod m = theCurrent.getBytecodeClass().methodByNameAndSignatureOrNull(aMethodName, aSignature); | ||
if (m != null && aMethodFilter.test(m)) { | ||
theResult.add(theCurrent); | ||
break test; | ||
} | ||
theCurrent = theCurrent.getSuperClass(); | ||
} | ||
}); | ||
|
||
if (theResult.size() == 1) { | ||
return Optional.of(theResult.iterator().next()); | ||
} else if (theResult.size() > 1) { | ||
// We might check the type of the receiver here, | ||
// but i haven't found a stable solution for this problem | ||
// This happens when calling abstract methods in base types and there | ||
// are multiple implementation types available. | ||
// Bytecoder Dataflow Analysis cannot do this | ||
} | ||
} | ||
return Optional.empty(); | ||
} | ||
} |
Oops, something went wrong.