more support for method handles, particularly for ones created with
reflection.
This commit is contained in:
parent
0ad11fefc3
commit
7e6cfe84f6
|
@ -10,6 +10,7 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul
|
|||
org.eclipse.jdt.core.compiler.annotation.nullable.secondary=
|
||||
org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=1.8
|
||||
|
|
|
@ -21,3 +21,4 @@ Export-Package: com.ibm.wala.cast.java.analysis.typeInference,
|
|||
com.ibm.wala.cast.java.types
|
||||
Bundle-ClassPath: .
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Automatic-Module-Name: com.ibm.wala.cast.java
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
buildscript {
|
||||
repositories { mavenCentral() }
|
||||
dependencies { classpath 'org.ajoberstar:gradle-git:0.2.3' }
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'eclipse'
|
||||
}
|
||||
|
@ -6,6 +11,121 @@ eclipse.project.natures 'org.eclipse.pde.PluginNature'
|
|||
|
||||
sourceSets.test.java.srcDirs = ['src']
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// download and extract kawa 3.0 "kawa.jar"
|
||||
//
|
||||
|
||||
task downloadKawa(type: Exec) {
|
||||
commandLine "wget", "ftp://ftp.gnu.org/pub/gnu/kawa/kawa-3.0.zip"
|
||||
workingDir "$temporaryDir"
|
||||
def destination = file("$temporaryDir/kawa-3.0.zip")
|
||||
enabled = !destination.exists() //to clone only once
|
||||
}
|
||||
|
||||
task extractKawa(type: Copy, dependsOn: downloadKawa) {
|
||||
from(zipTree("${downloadKawa.temporaryDir}/kawa-3.0.zip")) {
|
||||
include "kawa-3.0/lib/kawa.jar"
|
||||
eachFile {
|
||||
relativePath new RelativePath(!directory, relativePath.lastName)
|
||||
}
|
||||
}
|
||||
into projectDir
|
||||
includeEmptyDirs false
|
||||
outputs.file "kawa.jar"
|
||||
}
|
||||
|
||||
task cleanExtractKawa(type: Delete) {
|
||||
delete files(extractKawa)[0]
|
||||
}
|
||||
|
||||
clean.dependsOn cleanExtractKawa
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// build kawa chess
|
||||
//
|
||||
import org.ajoberstar.gradle.git.tasks.*
|
||||
|
||||
task getKawaChess(type: GitClone) {
|
||||
def destination = file("$temporaryDir/kawachess")
|
||||
uri = "https://github.com/ttu-fpclub/kawa-chess"
|
||||
destinationPath = destination
|
||||
bare = false
|
||||
enabled = !destination.exists() //to clone only once
|
||||
}
|
||||
|
||||
task compileChessFiles(type: JavaExec) {
|
||||
def kawaImg = file('kawa-chess/img.scm')
|
||||
def kawaPos = file('kawa-chess/pos.scm')
|
||||
def kawaChess = file('kawa-chess/chess.scm')
|
||||
def kawaGui = file('kawa-chess/gui.scm')
|
||||
inputs.file kawaImg
|
||||
inputs.file kawaPos
|
||||
inputs.file kawaChess
|
||||
inputs.file kawaGui
|
||||
|
||||
workingDir = file('kawa-chess')
|
||||
|
||||
def kawaJar = new File("kawa.jar")
|
||||
inputs.file kawaJar
|
||||
classpath kawaJar
|
||||
|
||||
main 'kawa.repl'
|
||||
args '-C', kawaImg, kawaPos, kawaChess, kawaGui
|
||||
}
|
||||
|
||||
compileChessFiles.dependsOn extractKawa
|
||||
compileChessFiles.dependsOn getKawaChess
|
||||
|
||||
task compileChessMain(type: JavaExec, dependsOn: compileChessFiles) {
|
||||
def kawaMain = file('kawa-chess/main.scm')
|
||||
inputs.file kawaMain
|
||||
|
||||
workingDir = file('kawa-chess')
|
||||
|
||||
def kawaJar = new File("kawa.jar")
|
||||
inputs.file kawaJar
|
||||
classpath kawaJar
|
||||
|
||||
main 'kawa.repl'
|
||||
args '--main', '-C', kawaMain
|
||||
}
|
||||
|
||||
task buildChessJar(type: Jar, dependsOn: compileChessMain) {
|
||||
from file('kawa-chess')
|
||||
baseName 'kawachess'
|
||||
version null
|
||||
destinationDir projectDir
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// build the kawa test jar
|
||||
//
|
||||
|
||||
task compileKawaTestMain(type: JavaExec, dependsOn: extractKawa) {
|
||||
def kawaMain = file('kawasrc/test.scm')
|
||||
inputs.file kawaMain
|
||||
|
||||
workingDir = file('kawasrc')
|
||||
|
||||
def kawaJar = new File("kawa.jar")
|
||||
inputs.file kawaJar
|
||||
classpath kawaJar
|
||||
|
||||
main 'kawa.repl'
|
||||
args '--main', '-C', kawaMain
|
||||
}
|
||||
|
||||
task buildKawaTestJar(type: Jar, dependsOn: compileKawaTestMain) {
|
||||
from file('kawasrc')
|
||||
baseName 'kawatest'
|
||||
version null
|
||||
destinationDir projectDir
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -186,5 +306,7 @@ afterEclipseBuildshipImport {
|
|||
downloadJavaCup,
|
||||
extractBcel,
|
||||
generateHelloHashJar,
|
||||
buildChessJar,
|
||||
buildKawaTestJar,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@ source.. = src/
|
|||
output.. = bin/test
|
||||
bin.includes = META-INF/,\
|
||||
.,\
|
||||
kawa.jar,\
|
||||
kawatest.jar,\
|
||||
kawachess.jar,\
|
||||
hello_hash.jar,\
|
||||
JLex.jar,\
|
||||
bcel-5.2.jar,\
|
||||
|
|
|
@ -88,13 +88,64 @@
|
|||
<delete dir="${temp.folder}"/>
|
||||
</target>
|
||||
|
||||
<target name="copyJars" depends="fetchJLex,fetchJavaCup,fetchBcel,buildOCamlHelloHash">
|
||||
<target name="copyJars" depends="fetchJLex,fetchJavaCup,fetchBcel,buildOCamlHelloHash,compileKawaTest,compileKawaChess">
|
||||
<copy todir="${plugin.destination}/bin">
|
||||
<fileset dir="${plugin.destination}" includes="*.jar" />
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
|
||||
<target name="KawaChessPresent" depends="init">
|
||||
<available file="${plugin.destination}/kawa-chess/chess.scm" property="kawa.chess.present"/>
|
||||
</target>
|
||||
|
||||
<target name="getKawaChess" depends="KawaChessPresent" unless="kawa.chess.present" >
|
||||
<exec executable="git"
|
||||
dir="${plugin.destination}">
|
||||
<arg value="clone"/>
|
||||
<arg value="http://github.com/ttu-fpclub/kawa-chess" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
|
||||
<target name="KawaPresent" depends="init">
|
||||
<available file="${plugin.destination}/kawa-3.0/lib/kawa.jar" property="kawa.present"/>
|
||||
</target>
|
||||
|
||||
<target name="fetchKawa" depends="KawaPresent" unless="kawa.present">
|
||||
<delete dir="${temp.folder}"/>
|
||||
<mkdir dir="${temp.folder}"/>
|
||||
<get src="ftp://ftp.gnu.org/pub/gnu/kawa/kawa-latest.zip" dest="${temp.folder}/kawa.zip"/>
|
||||
<unzip src="${temp.folder}/kawa.zip" dest="${plugin.destination}"/>
|
||||
<delete dir="${temp.folder}"/>
|
||||
</target>
|
||||
|
||||
<target name="KawaTestCompiled" depends="init">
|
||||
<available file="${plugin.destination}/kawatest.jar" property="kawa.test.compiled"/>
|
||||
</target>
|
||||
|
||||
<target name="compileKawaTest" depends="KawaTestCompiled,fetchKawa" unless="kawa.test.compiled" >
|
||||
<exec executable="java" dir="${plugin.destination}/kawasrc">
|
||||
<arg line="-cp ${plugin.destination}/kawa-3.0/lib/kawa.jar kawa.repl --main -C test.scm"/>
|
||||
</exec>
|
||||
<jar destfile="${plugin.destination}/kawatest.jar" basedir="${plugin.destination}/kawasrc" />
|
||||
</target>
|
||||
|
||||
<target name="KawaChessCompiled" depends="init">
|
||||
<available file="${plugin.destination}/kawachess.jar" property="kawa.chess.compiled"/>
|
||||
</target>
|
||||
|
||||
<target name="compileKawaChess" depends="KawaChessCompiled,fetchKawa,getKawaChess" unless="kawa.chess.compiled" >
|
||||
<exec executable="java" dir="${plugin.destination}/kawa-chess">
|
||||
<arg line="-cp ${plugin.destination}/kawa-3.0/lib/kawa.jar kawa.repl -C img.scm pos.scm chess.scm gui.scm"/>
|
||||
</exec>
|
||||
<exec executable="java" dir="${plugin.destination}/kawa-chess">
|
||||
<arg line="-cp ${plugin.destination}/kawa-3.0/lib/kawa.jar kawa.repl --main -C main.scm"/>
|
||||
</exec>
|
||||
<jar destfile="${plugin.destination}/kawachess.jar" basedir="${plugin.destination}/kawa-chess" />
|
||||
</target>
|
||||
|
||||
|
||||
<target name="init" depends="properties">
|
||||
<condition property="pluginTemp" value="${buildTempFolder}/plugins">
|
||||
<isset property="buildTempFolder"/>
|
||||
|
|
|
@ -33,6 +33,9 @@ processTestResources {
|
|||
from testdata.downloadJavaCup
|
||||
from files(testdata.extractBcel)[0]
|
||||
from testdata.generateHelloHashJar
|
||||
from testdata.extractKawa
|
||||
from testdata.buildChessJar
|
||||
from testdata.buildKawaTestJar
|
||||
}
|
||||
|
||||
test {
|
||||
|
|
|
@ -59,8 +59,11 @@ public class Java7CallGraphTest extends DynamicCallGraphTestBase {
|
|||
|
||||
SSAPropagationCallGraphBuilder builder = Util.makeZeroOneContainerCFABuilder(options, cache, cha, scope);
|
||||
|
||||
options.setSelector(new MethodHandles.InvokeExactTargetSelector(options.getMethodTargetSelector()));
|
||||
|
||||
builder.setContextSelector(new MethodHandles.ContextSelectorImpl(builder.getContextSelector()));
|
||||
builder.setContextInterpreter(new DelegatingSSAContextInterpreter(new MethodHandles.ContextInterpreterImpl(), builder.getCFAContextInterpreter()));
|
||||
builder.setContextInterpreter(new DelegatingSSAContextInterpreter(new MethodHandles.InvokeContextInterpreterImpl(), builder.getCFAContextInterpreter()));
|
||||
builder.setContextInterpreter(new DelegatingSSAContextInterpreter(new MethodHandles.FindContextInterpreterImpl(), builder.getCFAContextInterpreter()));
|
||||
|
||||
CallGraph cg = builder.makeCallGraph(options, null);
|
||||
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2002 - 2014 IBM Corporation.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* IBM Corporation - initial API and implementation
|
||||
*****************************************************************************/
|
||||
|
||||
package com.ibm.wala.core.tests.callGraph;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.ibm.wala.analysis.reflection.java7.MethodHandles;
|
||||
import com.ibm.wala.classLoader.Language;
|
||||
import com.ibm.wala.classLoader.Module;
|
||||
import com.ibm.wala.classLoader.ResourceJarFileModule;
|
||||
import com.ibm.wala.core.tests.shrike.DynamicCallGraphTestBase;
|
||||
import com.ibm.wala.ipa.callgraph.AnalysisCacheImpl;
|
||||
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
|
||||
import com.ibm.wala.ipa.callgraph.AnalysisOptions.ReflectionOptions;
|
||||
import com.ibm.wala.ipa.callgraph.AnalysisScope;
|
||||
import com.ibm.wala.ipa.callgraph.CGNode;
|
||||
import com.ibm.wala.ipa.callgraph.CallGraph;
|
||||
import com.ibm.wala.ipa.callgraph.Entrypoint;
|
||||
import com.ibm.wala.ipa.callgraph.IAnalysisCacheView;
|
||||
import com.ibm.wala.ipa.callgraph.impl.Util;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.SSAPropagationCallGraphBuilder;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.cfa.DelegatingSSAContextInterpreter;
|
||||
import com.ibm.wala.ipa.cha.ClassHierarchy;
|
||||
import com.ibm.wala.ipa.cha.ClassHierarchyException;
|
||||
import com.ibm.wala.ipa.cha.ClassHierarchyFactory;
|
||||
import com.ibm.wala.types.ClassLoaderReference;
|
||||
import com.ibm.wala.types.Descriptor;
|
||||
import com.ibm.wala.types.MethodReference;
|
||||
import com.ibm.wala.types.TypeReference;
|
||||
import com.ibm.wala.util.CancelException;
|
||||
import com.ibm.wala.util.strings.Atom;
|
||||
|
||||
public class KawaCallGraphTest extends DynamicCallGraphTestBase {
|
||||
|
||||
@Test
|
||||
public void testKawaChess() throws ClassHierarchyException, IllegalArgumentException, CancelException, IOException, SecurityException {
|
||||
CallGraph CG = testKawa(new ResourceJarFileModule(getClass().getClassLoader().getResource("kawachess.jar")), "main");
|
||||
|
||||
Set<CGNode> status = getNodes(CG, "Lchess", "startingStatus", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
|
||||
assert ! status.isEmpty();
|
||||
|
||||
Set<CGNode> color = getNodes(CG, "Lchess", "startingColor", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
|
||||
assert ! color.isEmpty();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKawaTest() throws ClassHierarchyException, IllegalArgumentException, CancelException, IOException, SecurityException {
|
||||
CallGraph CG = testKawa(new ResourceJarFileModule(getClass().getClassLoader().getResource("kawatest.jar")), "test");
|
||||
|
||||
Set<CGNode> nodes = getNodes(CG, "Ltest", "plusish$V", "(Lgnu/lists/LList;)Ljava/lang/Object;");
|
||||
assert ! nodes.isEmpty();
|
||||
}
|
||||
|
||||
private static Set<CGNode> getNodes(CallGraph CG, String cls, String method, String descr) {
|
||||
Set<CGNode> nodes = CG.getNodes(MethodReference.findOrCreate(TypeReference.find(ClassLoaderReference.Application, cls), Atom.findOrCreateUnicodeAtom(method), Descriptor.findOrCreateUTF8(descr)));
|
||||
return nodes;
|
||||
}
|
||||
|
||||
public CallGraph testKawa(Module code, String main) throws ClassHierarchyException, IllegalArgumentException, CancelException, IOException, SecurityException {
|
||||
AnalysisScope scope = CallGraphTestUtil.makeJ2SEAnalysisScope("base.txt", CallGraphTestUtil.REGRESSION_EXCLUSIONS_FOR_GUI);
|
||||
scope.addToScope(ClassLoaderReference.Application, new ResourceJarFileModule(getClass().getClassLoader().getResource("kawa.jar")));
|
||||
scope.addToScope(ClassLoaderReference.Application, code);
|
||||
|
||||
ClassHierarchy cha = ClassHierarchyFactory.make(scope);
|
||||
Iterable<Entrypoint> entrypoints = com.ibm.wala.ipa.callgraph.impl.Util.makeMainEntrypoints(scope, cha, "L" + main);
|
||||
AnalysisOptions options = CallGraphTestUtil.makeAnalysisOptions(scope, entrypoints);
|
||||
IAnalysisCacheView cache = new AnalysisCacheImpl();
|
||||
|
||||
options.setReflectionOptions(ReflectionOptions.NONE);
|
||||
options.setTraceStringConstants(true);
|
||||
options.setUseConstantSpecificKeys(true);
|
||||
|
||||
SSAPropagationCallGraphBuilder builder = Util.makeZeroCFABuilder(Language.JAVA, options, cache, cha, scope);
|
||||
|
||||
options.setSelector(new MethodHandles.InvokeExactTargetSelector(options.getMethodTargetSelector()));
|
||||
|
||||
builder.setContextSelector(new MethodHandles.ContextSelectorImpl(builder.getContextSelector()));
|
||||
builder.setContextInterpreter(new DelegatingSSAContextInterpreter(new MethodHandles.InvokeContextInterpreterImpl(), builder.getCFAContextInterpreter()));
|
||||
builder.setContextInterpreter(new DelegatingSSAContextInterpreter(new MethodHandles.FindContextInterpreterImpl(), builder.getCFAContextInterpreter()));
|
||||
|
||||
return builder.makeCallGraph(options, null);
|
||||
}
|
||||
|
||||
}
|
|
@ -20,11 +20,13 @@ import com.ibm.wala.classLoader.CallSiteReference;
|
|||
import com.ibm.wala.classLoader.IClass;
|
||||
import com.ibm.wala.classLoader.IMethod;
|
||||
import com.ibm.wala.classLoader.NewSiteReference;
|
||||
import com.ibm.wala.classLoader.SyntheticMethod;
|
||||
import com.ibm.wala.ipa.callgraph.CGNode;
|
||||
import com.ibm.wala.ipa.callgraph.Context;
|
||||
import com.ibm.wala.ipa.callgraph.ContextItem;
|
||||
import com.ibm.wala.ipa.callgraph.ContextKey;
|
||||
import com.ibm.wala.ipa.callgraph.ContextSelector;
|
||||
import com.ibm.wala.ipa.callgraph.MethodTargetSelector;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.ConstantKey;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
|
||||
|
@ -53,8 +55,10 @@ import com.ibm.wala.util.intset.IntSetUtil;
|
|||
|
||||
public class MethodHandles {
|
||||
|
||||
private static final IntSet params = IntSetUtil.make(new int[] {1, 2});
|
||||
|
||||
private static final IntSet self = IntSetUtil.make(new int[0]);
|
||||
|
||||
|
||||
private static ContextKey METHOD_KEY = new ContextKey() {
|
||||
@Override
|
||||
public String toString() {
|
||||
|
@ -62,23 +66,32 @@ public class MethodHandles {
|
|||
}
|
||||
};
|
||||
|
||||
public static class MethodItem implements ContextItem {
|
||||
private final MethodReference method;
|
||||
|
||||
public MethodItem(MethodReference method) {
|
||||
super();
|
||||
this.method = method;
|
||||
private static ContextKey CLASS_KEY = new ContextKey() {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CLASS_KEY";
|
||||
}
|
||||
};
|
||||
|
||||
private static ContextKey NAME_KEY = new ContextKey() {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NAME_KEY";
|
||||
}
|
||||
};
|
||||
|
||||
private static class HandlesItem<T> implements ContextItem {
|
||||
private final T item;
|
||||
|
||||
public MethodReference getMethod() {
|
||||
return method;
|
||||
public HandlesItem(T method) {
|
||||
this.item = method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((method == null) ? 0 : method.hashCode());
|
||||
result = prime * result + ((item == null) ? 0 : item.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -90,15 +103,78 @@ public class MethodHandles {
|
|||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
MethodItem other = (MethodItem) obj;
|
||||
if (method == null) {
|
||||
if (other.method != null)
|
||||
HandlesItem other = (HandlesItem) obj;
|
||||
if (item == null) {
|
||||
if (other.item != null)
|
||||
return false;
|
||||
} else if (!method.equals(other.method))
|
||||
} else if (!item.equals(other.item))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class FindContext implements Context {
|
||||
private final Context base;
|
||||
private final TypeReference cls;
|
||||
private final String selector;
|
||||
|
||||
public FindContext(Context base, TypeReference cls, String methodName) {
|
||||
this.base = base;
|
||||
this.cls = cls;
|
||||
this.selector = methodName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContextItem get(ContextKey name) {
|
||||
if (CLASS_KEY.equals(name)) {
|
||||
return new HandlesItem<>(cls);
|
||||
} else if (NAME_KEY.equals(name)) {
|
||||
return new HandlesItem<>(selector);
|
||||
} else {
|
||||
|
||||
return base.get(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((base == null) ? 0 : base.hashCode());
|
||||
result = prime * result + ((cls == null) ? 0 : cls.hashCode());
|
||||
result = prime * result + ((selector == null) ? 0 : selector.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
FindContext other = (FindContext) obj;
|
||||
if (base == null) {
|
||||
if (other.base != null)
|
||||
return false;
|
||||
} else if (!base.equals(other.base))
|
||||
return false;
|
||||
if (cls == null) {
|
||||
if (other.cls != null)
|
||||
return false;
|
||||
} else if (!cls.equals(other.cls))
|
||||
return false;
|
||||
if (selector == null) {
|
||||
if (other.selector != null)
|
||||
return false;
|
||||
} else if (!selector.equals(other.selector))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static class MethodContext implements Context {
|
||||
private final Context base;
|
||||
|
@ -112,7 +188,7 @@ public class MethodHandles {
|
|||
@Override
|
||||
public ContextItem get(ContextKey name) {
|
||||
if (METHOD_KEY.equals(name)) {
|
||||
return new MethodItem(method);
|
||||
return new HandlesItem<>(method);
|
||||
} else {
|
||||
return base.get(name);
|
||||
}
|
||||
|
@ -165,7 +241,7 @@ public class MethodHandles {
|
|||
@Override
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] actualParameters) {
|
||||
Context baseContext = base.getCalleeTarget(caller, site, callee, actualParameters);
|
||||
if ((isInvoke(callee) || isType(callee)) && callee.getDeclaringClass().getReference().equals(TypeReference.JavaLangInvokeMethodHandle)) {
|
||||
if ((isInvoke(callee) || isType(callee)) && callee.getReference().getDeclaringClass().getName().equals(TypeReference.JavaLangInvokeMethodHandle.getName())) {
|
||||
if (actualParameters != null && actualParameters.length > 0) {
|
||||
InstanceKey selfKey = actualParameters[0];
|
||||
if (selfKey instanceof ConstantKey && ((ConstantKey)selfKey).getConcreteType().getReference().equals(TypeReference.JavaLangInvokeMethodHandle)) {
|
||||
|
@ -174,15 +250,64 @@ public class MethodHandles {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isFindStatic(callee) && callee.getDeclaringClass().getReference().equals(TypeReference.JavaLangInvokeMethodHandlesLookup)) {
|
||||
if (actualParameters != null && actualParameters.length > 2) {
|
||||
InstanceKey classKey = actualParameters[1];
|
||||
InstanceKey nameKey = actualParameters[2];
|
||||
if (classKey instanceof ConstantKey && ((ConstantKey)classKey).getConcreteType().getReference().equals(TypeReference.JavaLangClass) &&
|
||||
nameKey instanceof ConstantKey && ((ConstantKey)nameKey).getConcreteType().getReference().equals(TypeReference.JavaLangString)) {
|
||||
return new FindContext(baseContext, ((IClass)((ConstantKey)classKey).getValue()).getReference(), (String)((ConstantKey)nameKey).getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return baseContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
return self;
|
||||
return isFindStatic(site.getDeclaredTarget())? params: self;
|
||||
}
|
||||
}
|
||||
|
||||
public static class InvokeExactTargetSelector implements MethodTargetSelector {
|
||||
private final MethodTargetSelector base;
|
||||
private final Map<MethodReference, SyntheticMethod> impls = HashMapFactory.make();
|
||||
|
||||
public InvokeExactTargetSelector(MethodTargetSelector base) {
|
||||
this.base = base;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMethod getCalleeTarget(CGNode caller, CallSiteReference site, IClass receiver) {
|
||||
MethodReference target = site.getDeclaredTarget();
|
||||
if (isInvokeExact(target)) {
|
||||
if (! impls.containsKey(target)) {
|
||||
SyntheticMethod invokeExactTrampoline = new SyntheticMethod(target, receiver.getClassHierarchy().lookupClass(TypeReference.JavaLangInvokeMethodHandle), false, false);
|
||||
impls.put(target, invokeExactTrampoline);
|
||||
}
|
||||
|
||||
return impls.get(target);
|
||||
}
|
||||
|
||||
return base.getCalleeTarget(caller, site, receiver);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isInvokeExact(MethodReference target) {
|
||||
return target.getDeclaringClass().getName().equals(TypeReference.JavaLangInvokeMethodHandle.getName()) &&
|
||||
target.getName().toString().equals("invokeExact");
|
||||
}
|
||||
|
||||
private static boolean isFindStatic(MethodReference node) {
|
||||
return node.getName().toString().startsWith("findStatic");
|
||||
}
|
||||
|
||||
private static boolean isFindStatic(IMethod node) {
|
||||
return isFindStatic(node.getReference());
|
||||
}
|
||||
|
||||
private static boolean isInvoke(IMethod node) {
|
||||
return node.getName().toString().startsWith("invoke");
|
||||
}
|
||||
|
@ -199,8 +324,12 @@ public class MethodHandles {
|
|||
return isType(node.getMethod());
|
||||
}
|
||||
|
||||
public static class ContextInterpreterImpl implements SSAContextInterpreter {
|
||||
private final Map<CGNode, SoftReference<IR>> irs = HashMapFactory.make();
|
||||
private static boolean isFindStatic(CGNode node) {
|
||||
return isFindStatic(node.getMethod());
|
||||
}
|
||||
|
||||
private abstract static class HandlersContextInterpreterImpl implements SSAContextInterpreter {
|
||||
protected final Map<CGNode, SoftReference<IR>> irs = HashMapFactory.make();
|
||||
|
||||
@Override
|
||||
public Iterator<NewSiteReference> iterateNewSites(CGNode node) {
|
||||
|
@ -229,52 +358,11 @@ public class MethodHandles {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean understands(CGNode node) {
|
||||
return (isInvoke(node) || isType(node)) && node.getContext() instanceof MethodContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<CallSiteReference> iterateCallSites(CGNode node) {
|
||||
return getIR(node).iterateCallSites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IR getIR(CGNode node) {
|
||||
if (!irs.containsKey(node) || irs.get(node).get() == null) {
|
||||
MethodSummary code = new MethodSummary(node.getMethod().getReference());
|
||||
SummarizedMethod m = new SummarizedMethod(node.getMethod().getReference(), code, node.getMethod().getDeclaringClass());
|
||||
SSAInstructionFactory insts = node.getMethod().getDeclaringClass().getClassLoader().getLanguage().instructionFactory();
|
||||
assert node.getContext() instanceof MethodContext;
|
||||
MethodReference ref = ((MethodContext)node.getContext()).method;
|
||||
boolean isStatic = node.getClassHierarchy().resolveMethod(ref).isStatic();
|
||||
if (isInvoke(node)) {
|
||||
String name = node.getMethod().getName().toString();
|
||||
if ("invokeWithArguments".equals(name)) {
|
||||
int nargs = ref.getNumberOfParameters();
|
||||
int params[] = new int[nargs];
|
||||
for(int i = 0; i < nargs; i++) {
|
||||
code.addConstant(i+nargs+3, new ConstantValue(i));
|
||||
code.addStatement(insts.ArrayLoadInstruction(code.getNextProgramCounter(), i+3, 1, i+nargs+3, TypeReference.JavaLangObject));
|
||||
params[i] = i+3;
|
||||
}
|
||||
CallSiteReference site = CallSiteReference.make(nargs+1, ref, isStatic? Dispatch.STATIC: Dispatch.SPECIAL);
|
||||
code.addStatement(insts.InvokeInstruction(code.getNextProgramCounter(), 2*nargs+3, params, 2*nargs+4, site, null));
|
||||
code.addStatement(insts.ReturnInstruction(code.getNextProgramCounter(), 2*nargs+3, false));
|
||||
} else {
|
||||
// int nargs = node.getMethod().getNumberOfParameters();
|
||||
}
|
||||
} else {
|
||||
assert isType(node);
|
||||
code.addStatement(insts.LoadMetadataInstruction(code.getNextProgramCounter(), 2, TypeReference.JavaLangInvokeMethodType, ref.getDescriptor()));
|
||||
code.addStatement(insts.ReturnInstruction(code.getNextProgramCounter(), 2, false));
|
||||
}
|
||||
irs.put(node, new SoftReference<>(m.makeIR(node.getContext(), SSAOptions.defaultOptions())));
|
||||
}
|
||||
|
||||
return irs.get(node).get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IRView getIRView(CGNode node) {
|
||||
return getIR(node);
|
||||
|
@ -294,6 +382,100 @@ public class MethodHandles {
|
|||
public ControlFlowGraph<SSAInstruction, ISSABasicBlock> getCFG(CGNode n) {
|
||||
return getIR(n).getControlFlowGraph();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class FindContextInterpreterImpl extends HandlersContextInterpreterImpl {
|
||||
|
||||
@Override
|
||||
public boolean understands(CGNode node) {
|
||||
return isFindStatic(node) && node.getContext() instanceof FindContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IR getIR(CGNode node) {
|
||||
if (!irs.containsKey(node) || irs.get(node).get() == null) {
|
||||
MethodSummary code = new MethodSummary(node.getMethod().getReference());
|
||||
SummarizedMethod m = new SummarizedMethod(node.getMethod().getReference(), code, node.getMethod().getDeclaringClass());
|
||||
SSAInstructionFactory insts = node.getMethod().getDeclaringClass().getClassLoader().getLanguage().instructionFactory();
|
||||
assert node.getContext() instanceof FindContext;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
IClass cls = node.getClassHierarchy().lookupClass(((HandlesItem<TypeReference>)node.getContext().get(CLASS_KEY)).item);
|
||||
@SuppressWarnings("unchecked")
|
||||
String selector = ((HandlesItem<String>)node.getContext().get(NAME_KEY)).item;
|
||||
|
||||
int vn = 10;
|
||||
for(IMethod handleMethod : cls.getAllMethods()) {
|
||||
if (handleMethod.getName().toString().contains(selector)) {
|
||||
code.addStatement(insts.LoadMetadataInstruction(code.getNextProgramCounter(), vn, TypeReference.JavaLangInvokeMethodHandle, handleMethod.getReference()));
|
||||
code.addStatement(insts.ReturnInstruction(code.getNextProgramCounter(), vn , false));
|
||||
vn++;
|
||||
}
|
||||
}
|
||||
irs.put(node, new SoftReference<>(m.makeIR(node.getContext(), SSAOptions.defaultOptions())));
|
||||
}
|
||||
|
||||
return irs.get(node).get();
|
||||
}
|
||||
}
|
||||
|
||||
public static class InvokeContextInterpreterImpl extends HandlersContextInterpreterImpl {
|
||||
|
||||
@Override
|
||||
public boolean understands(CGNode node) {
|
||||
return (isInvoke(node) || isType(node)) && node.getContext() instanceof MethodContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IR getIR(CGNode node) {
|
||||
if (!irs.containsKey(node) || irs.get(node).get() == null) {
|
||||
MethodSummary code = new MethodSummary(node.getMethod().getReference());
|
||||
SummarizedMethod m = new SummarizedMethod(node.getMethod().getReference(), code, node.getMethod().getDeclaringClass());
|
||||
SSAInstructionFactory insts = node.getMethod().getDeclaringClass().getClassLoader().getLanguage().instructionFactory();
|
||||
assert node.getContext() instanceof MethodContext;
|
||||
MethodReference ref = ((MethodContext)node.getContext()).method;
|
||||
boolean isStatic = node.getClassHierarchy().resolveMethod(ref).isStatic();
|
||||
boolean isVoid = ref.getReturnType().equals(TypeReference.Void);
|
||||
if (isInvoke(node)) {
|
||||
String name = node.getMethod().getName().toString();
|
||||
if ("invokeWithArguments".equals(name)) {
|
||||
int nargs = ref.getNumberOfParameters();
|
||||
int params[] = new int[nargs];
|
||||
for(int i = 0; i < nargs; i++) {
|
||||
code.addConstant(i+nargs+3, new ConstantValue(i));
|
||||
code.addStatement(insts.ArrayLoadInstruction(code.getNextProgramCounter(), i+3, 1, i+nargs+3, TypeReference.JavaLangObject));
|
||||
params[i] = i+3;
|
||||
}
|
||||
CallSiteReference site = CallSiteReference.make(nargs+1, ref, isStatic? Dispatch.STATIC: Dispatch.SPECIAL);
|
||||
code.addStatement(insts.InvokeInstruction(code.getNextProgramCounter(), 2*nargs+3, params, 2*nargs+4, site, null));
|
||||
code.addStatement(insts.ReturnInstruction(code.getNextProgramCounter(), 2*nargs+3, false));
|
||||
} else if ("invokeExact".equals(name)) {
|
||||
int nargs = node.getMethod().getReference().getNumberOfParameters();
|
||||
int params[] = new int[nargs];
|
||||
if (nargs == ref.getNumberOfParameters() + (isStatic? 0: 1)) {
|
||||
for(int i = 0; i < nargs; i++) {
|
||||
params[i] = i+2;
|
||||
}
|
||||
CallSiteReference site = CallSiteReference.make(0, ref, isStatic? Dispatch.STATIC: Dispatch.SPECIAL);
|
||||
if (isVoid) {
|
||||
code.addStatement(insts.InvokeInstruction(code.getNextProgramCounter(), params, nargs+2, site, null));
|
||||
} else {
|
||||
code.addStatement(insts.InvokeInstruction(code.getNextProgramCounter(), nargs+2, params, nargs+3, site, null));
|
||||
code.addStatement(insts.ReturnInstruction(code.getNextProgramCounter(), nargs+2, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert isType(node);
|
||||
code.addStatement(insts.LoadMetadataInstruction(code.getNextProgramCounter(), 2, TypeReference.JavaLangInvokeMethodType, ref.getDescriptor()));
|
||||
code.addStatement(insts.ReturnInstruction(code.getNextProgramCounter(), 2, false));
|
||||
}
|
||||
irs.put(node, new SoftReference<>(m.makeIR(node.getContext(), SSAOptions.defaultOptions())));
|
||||
}
|
||||
|
||||
return irs.get(node).get();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -711,7 +711,6 @@ public abstract class PropagationCallGraphBuilder implements CallGraphBuilder<In
|
|||
* @return the CGNode to which this particular call should dispatch.
|
||||
*/
|
||||
protected CGNode getTargetForCall(CGNode caller, CallSiteReference site, IClass recv, InstanceKey iKey[]) {
|
||||
|
||||
IMethod targetMethod = options.getMethodTargetSelector().getCalleeTarget(caller, site, recv);
|
||||
|
||||
// this most likely indicates an exclusion at work; the target selector
|
||||
|
|
|
@ -1112,7 +1112,7 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
|
|||
if (DEBUG) {
|
||||
System.err.println("visitInvoke: " + instruction);
|
||||
}
|
||||
|
||||
|
||||
PointerKey uniqueCatch = null;
|
||||
if (hasUniqueCatchBlock(instruction, ir)) {
|
||||
uniqueCatch = getBuilder().getUniqueCatchKey(instruction, ir, node);
|
||||
|
@ -1731,12 +1731,14 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
|
|||
IntSet currentObjs = rhs[rhsIndex].getValue();
|
||||
if (currentObjs != null) {
|
||||
final IntSet oldObjs = previousPtrs[rhsIndex];
|
||||
currentObjs.foreachExcluding(oldObjs, x -> new CrossProductRec(constParams, call, node,
|
||||
|
||||
currentObjs.foreachExcluding(oldObjs, x -> new CrossProductRec(constParams, call, node,
|
||||
v -> {
|
||||
IClass recv = null;
|
||||
if (call.getCallSite().isDispatch()) {
|
||||
recv = v[0].getConcreteType();
|
||||
}
|
||||
|
||||
CGNode target = getTargetForCall(node, call.getCallSite(), recv, v);
|
||||
if (target != null) {
|
||||
changed.b = true;
|
||||
|
|
|
@ -158,6 +158,10 @@ public final class TypeReference implements Serializable {
|
|||
|
||||
public final static TypeReference JavaLangInvokeMethodHandle = findOrCreate(ClassLoaderReference.Primordial, JavaLangInvokeMethodHandleName);
|
||||
|
||||
private final static TypeName JavaLangInvokeMethodHandlesLookupName = TypeName.string2TypeName("Ljava/lang/invoke/MethodHandles$Lookup");
|
||||
|
||||
public final static TypeReference JavaLangInvokeMethodHandlesLookup = findOrCreate(ClassLoaderReference.Primordial, JavaLangInvokeMethodHandlesLookupName);
|
||||
|
||||
private final static TypeName JavaLangInvokeMethodTypeName = TypeName.string2TypeName("Ljava/lang/invoke/MethodType");
|
||||
|
||||
public final static TypeReference JavaLangInvokeMethodType = findOrCreate(ClassLoaderReference.Primordial, JavaLangInvokeMethodTypeName);
|
||||
|
|
Loading…
Reference in New Issue