diff --git a/com.ibm.wala.cast.java/.settings/org.eclipse.jdt.core.prefs b/com.ibm.wala.cast.java/.settings/org.eclipse.jdt.core.prefs index 5465e174f..169bc4376 100644 --- a/com.ibm.wala.cast.java/.settings/org.eclipse.jdt.core.prefs +++ b/com.ibm.wala.cast.java/.settings/org.eclipse.jdt.core.prefs @@ -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 diff --git a/com.ibm.wala.cast.java/META-INF/MANIFEST.MF b/com.ibm.wala.cast.java/META-INF/MANIFEST.MF index 686671850..f638eae6f 100644 --- a/com.ibm.wala.cast.java/META-INF/MANIFEST.MF +++ b/com.ibm.wala.cast.java/META-INF/MANIFEST.MF @@ -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 diff --git a/com.ibm.wala.core.testdata/build.gradle b/com.ibm.wala.core.testdata/build.gradle index b3581630f..8a28e09c9 100644 --- a/com.ibm.wala.core.testdata/build.gradle +++ b/com.ibm.wala.core.testdata/build.gradle @@ -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, ) } diff --git a/com.ibm.wala.core.testdata/build.properties b/com.ibm.wala.core.testdata/build.properties index d61143dc0..c7386c4f6 100644 --- a/com.ibm.wala.core.testdata/build.properties +++ b/com.ibm.wala.core.testdata/build.properties @@ -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,\ diff --git a/com.ibm.wala.core.testdata/build.xml b/com.ibm.wala.core.testdata/build.xml index 7342fea11..09929b2c6 100644 --- a/com.ibm.wala.core.testdata/build.xml +++ b/com.ibm.wala.core.testdata/build.xml @@ -88,13 +88,64 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/com.ibm.wala.core.tests/build.gradle b/com.ibm.wala.core.tests/build.gradle index d3ed7bf15..9b356cb96 100644 --- a/com.ibm.wala.core.tests/build.gradle +++ b/com.ibm.wala.core.tests/build.gradle @@ -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 { diff --git a/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/callGraph/Java7CallGraphTest.java b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/callGraph/Java7CallGraphTest.java index bff08aab2..d2c4b2b13 100644 --- a/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/callGraph/Java7CallGraphTest.java +++ b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/callGraph/Java7CallGraphTest.java @@ -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); diff --git a/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/callGraph/KawaCallGraphTest.java b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/callGraph/KawaCallGraphTest.java new file mode 100644 index 000000000..46a163b20 --- /dev/null +++ b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/callGraph/KawaCallGraphTest.java @@ -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 status = getNodes(CG, "Lchess", "startingStatus", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); + assert ! status.isEmpty(); + + Set 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 nodes = getNodes(CG, "Ltest", "plusish$V", "(Lgnu/lists/LList;)Ljava/lang/Object;"); + assert ! nodes.isEmpty(); + } + + private static Set getNodes(CallGraph CG, String cls, String method, String descr) { + Set 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 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); + } + +} diff --git a/com.ibm.wala.core/src/com/ibm/wala/analysis/reflection/java7/MethodHandles.java b/com.ibm.wala.core/src/com/ibm/wala/analysis/reflection/java7/MethodHandles.java index fb8544a0a..cfe234635 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/analysis/reflection/java7/MethodHandles.java +++ b/com.ibm.wala.core/src/com/ibm/wala/analysis/reflection/java7/MethodHandles.java @@ -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 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 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> irs = HashMapFactory.make(); + private static boolean isFindStatic(CGNode node) { + return isFindStatic(node.getMethod()); + } + + private abstract static class HandlersContextInterpreterImpl implements SSAContextInterpreter { + protected final Map> irs = HashMapFactory.make(); @Override public Iterator 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 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 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)node.getContext().get(CLASS_KEY)).item); + @SuppressWarnings("unchecked") + String selector = ((HandlesItem)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(); + } } } diff --git a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/PropagationCallGraphBuilder.java b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/PropagationCallGraphBuilder.java index 9b8d8a7d2..8d403695c 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/PropagationCallGraphBuilder.java +++ b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/PropagationCallGraphBuilder.java @@ -711,7 +711,6 @@ public abstract class PropagationCallGraphBuilder implements CallGraphBuilder 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; diff --git a/com.ibm.wala.core/src/com/ibm/wala/types/TypeReference.java b/com.ibm.wala.core/src/com/ibm/wala/types/TypeReference.java index 563c68275..eb37e8a2d 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/types/TypeReference.java +++ b/com.ibm.wala.core/src/com/ibm/wala/types/TypeReference.java @@ -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);