Fixes for instrumentation of invoke dynamic
This commit is contained in:
parent
d685fbb71f
commit
7cd2a2acf8
|
@ -19,7 +19,9 @@ public class SortingExample {
|
||||||
return strs;
|
return strs;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int id1(int x) { return x; }
|
private static int id0(int x) { return x; }
|
||||||
|
|
||||||
|
private static int id1(int x) { return id0(x); }
|
||||||
|
|
||||||
public String[] sortForward() {
|
public String[] sortForward() {
|
||||||
return sort( (String l, String r) -> id1(l.compareTo(r)));
|
return sort( (String l, String r) -> id1(l.compareTo(r)));
|
||||||
|
|
|
@ -73,4 +73,13 @@ public class DynamicCallGraphTest extends DynamicCallGraphTestBase {
|
||||||
checkEdges(staticCG);
|
checkEdges(staticCG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLambdas() throws IOException, ClassNotFoundException, InvalidClassFileException, FailureException, SecurityException, IllegalArgumentException, ClassHierarchyException, CancelException, InterruptedException {
|
||||||
|
instrument(testJarLocation);
|
||||||
|
run("lambda.SortingExample", null);
|
||||||
|
CallGraph staticCG = staticCG("Llambda/SortingExample", null);
|
||||||
|
System.err.println(staticCG);
|
||||||
|
checkEdges(staticCG);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -208,12 +208,15 @@ public abstract class DynamicCallGraphTestBase extends WalaTestCase {
|
||||||
continue loop;
|
continue loop;
|
||||||
} else {
|
} else {
|
||||||
String callerMethod = edge.nextToken();
|
String callerMethod = edge.nextToken();
|
||||||
|
if (callerMethod.startsWith("lambda$")) {
|
||||||
|
continue loop;
|
||||||
|
}
|
||||||
MethodReference callerRef = MethodReference.findOrCreate(TypeReference.findOrCreate(ClassLoaderReference.Application, "L" + callerClass), Selector.make(callerMethod));
|
MethodReference callerRef = MethodReference.findOrCreate(TypeReference.findOrCreate(ClassLoaderReference.Application, "L" + callerClass), Selector.make(callerMethod));
|
||||||
Set<CGNode> nodes = staticCG.getNodes(callerRef);
|
Set<CGNode> nodes = staticCG.getNodes(callerRef);
|
||||||
if (! filter.test(callerRef)) {
|
if (! filter.test(callerRef)) {
|
||||||
continue loop;
|
continue loop;
|
||||||
}
|
}
|
||||||
Assert.assertEquals(1, nodes.size());
|
Assert.assertEquals(callerMethod, 1, nodes.size());
|
||||||
caller = nodes.iterator().next();
|
caller = nodes.iterator().next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import com.ibm.wala.shrikeBT.Constants;
|
||||||
import com.ibm.wala.shrikeBT.Disassembler;
|
import com.ibm.wala.shrikeBT.Disassembler;
|
||||||
import com.ibm.wala.shrikeBT.IInvokeInstruction;
|
import com.ibm.wala.shrikeBT.IInvokeInstruction;
|
||||||
import com.ibm.wala.shrikeBT.IInvokeInstruction.Dispatch;
|
import com.ibm.wala.shrikeBT.IInvokeInstruction.Dispatch;
|
||||||
|
import com.ibm.wala.shrikeBT.InvokeDynamicInstruction;
|
||||||
import com.ibm.wala.shrikeBT.InvokeInstruction;
|
import com.ibm.wala.shrikeBT.InvokeInstruction;
|
||||||
import com.ibm.wala.shrikeBT.LoadInstruction;
|
import com.ibm.wala.shrikeBT.LoadInstruction;
|
||||||
import com.ibm.wala.shrikeBT.MethodData;
|
import com.ibm.wala.shrikeBT.MethodData;
|
||||||
|
@ -248,7 +249,7 @@ public class OfflineDynamicCallGraph {
|
||||||
this.replaceWith(new MethodEditor.Patch() {
|
this.replaceWith(new MethodEditor.Patch() {
|
||||||
@Override
|
@Override
|
||||||
public void emitTo(final Output w) {
|
public void emitTo(final Output w) {
|
||||||
final String methodSignature = inv.getInvocationCode().hasImplicitThis()?
|
final String methodSignature = inv.getInvocationCode().hasImplicitThis() && !(inv instanceof InvokeDynamicInstruction)?
|
||||||
"(" + inv.getClassType() + inv.getMethodSignature().substring(1):
|
"(" + inv.getClassType() + inv.getMethodSignature().substring(1):
|
||||||
inv.getMethodSignature();
|
inv.getMethodSignature();
|
||||||
Pair<String,Pair<String,String>> key = Pair.make(inv.getClassType(), Pair.make(inv.getMethodName(), methodSignature));
|
Pair<String,Pair<String,String>> key = Pair.make(inv.getClassType(), Pair.make(inv.getMethodName(), methodSignature));
|
||||||
|
@ -274,7 +275,13 @@ public class OfflineDynamicCallGraph {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Dispatch mode = (Dispatch)inv.getInvocationCode();
|
Dispatch mode = (Dispatch)inv.getInvocationCode();
|
||||||
w.emit(InvokeInstruction.make(inv.getMethodSignature(), inv.getClassType(), inv.getMethodName(), mode));
|
if (inv instanceof InvokeDynamicInstruction) {
|
||||||
|
InvokeDynamicInstruction inst = new InvokeDynamicInstruction(((InvokeDynamicInstruction) inv).getOpcode(), ((InvokeDynamicInstruction) inv).getBootstrap(), inv.getMethodName(), inv.getMethodSignature());
|
||||||
|
w.emit(inst);
|
||||||
|
} else {
|
||||||
|
InvokeInstruction inst = InvokeInstruction.make(inv.getMethodSignature(), inv.getClassType(), inv.getMethodName(), mode);
|
||||||
|
w.emit(inst);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
me.applyPatches();
|
me.applyPatches();
|
||||||
|
|
|
@ -19,6 +19,7 @@ import com.ibm.wala.shrikeBT.ConstantInstruction.ClassToken;
|
||||||
import com.ibm.wala.shrikeBT.IBinaryOpInstruction.Operator;
|
import com.ibm.wala.shrikeBT.IBinaryOpInstruction.Operator;
|
||||||
import com.ibm.wala.shrikeBT.analysis.ClassHierarchyProvider;
|
import com.ibm.wala.shrikeBT.analysis.ClassHierarchyProvider;
|
||||||
import com.ibm.wala.shrikeBT.analysis.Verifier;
|
import com.ibm.wala.shrikeBT.analysis.Verifier;
|
||||||
|
import com.ibm.wala.shrikeCT.BootstrapMethodsReader.BootstrapMethod;
|
||||||
import com.ibm.wala.shrikeCT.ConstantPoolParser.ReferenceToken;
|
import com.ibm.wala.shrikeCT.ConstantPoolParser.ReferenceToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -172,8 +173,10 @@ public abstract class Compiler implements Constants {
|
||||||
|
|
||||||
protected abstract int allocateConstantPoolInterfaceMethod(String c, String name, String sig);
|
protected abstract int allocateConstantPoolInterfaceMethod(String c, String name, String sig);
|
||||||
|
|
||||||
protected abstract String createHelperMethod(boolean isStatic, String sig);
|
protected abstract int allocateConstantPoolInvokeDynamic(BootstrapMethod b, String name, String type);
|
||||||
|
|
||||||
|
protected abstract String createHelperMethod(boolean isStatic, String sig);
|
||||||
|
|
||||||
private void collectInstructionInfo() {
|
private void collectInstructionInfo() {
|
||||||
final BitSet s = new BitSet(instructions.length);
|
final BitSet s = new BitSet(instructions.length);
|
||||||
final BitSet localsUsed = new BitSet(32);
|
final BitSet localsUsed = new BitSet(32);
|
||||||
|
@ -955,14 +958,14 @@ public abstract class Compiler implements Constants {
|
||||||
}
|
}
|
||||||
case OP_invokedynamic: {
|
case OP_invokedynamic: {
|
||||||
InvokeDynamicInstruction inv = (InvokeDynamicInstruction) instr;
|
InvokeDynamicInstruction inv = (InvokeDynamicInstruction) instr;
|
||||||
String sig = inv.getMethodSignature();
|
|
||||||
int cpIndex;
|
int cpIndex;
|
||||||
|
|
||||||
if (presetConstants != null && presetConstants == inv.getLazyConstantPool()) {
|
if (presetConstants != null && presetConstants == inv.getLazyConstantPool()) {
|
||||||
cpIndex = ((InvokeDynamicInstruction.Lazy) inv).getCPIndex();
|
cpIndex = ((InvokeDynamicInstruction.Lazy) inv).getCPIndex();
|
||||||
} else {
|
} else {
|
||||||
cpIndex = allocateConstantPoolInterfaceMethod(inv.getClassType(), inv.getMethodName(), sig);
|
cpIndex = allocateConstantPoolInvokeDynamic(inv.getBootstrap(), inv.getMethodName(), inv.getMethodSignature());
|
||||||
}
|
}
|
||||||
|
|
||||||
writeShort(curOffset, cpIndex);
|
writeShort(curOffset, cpIndex);
|
||||||
code[curOffset + 2] = 0;
|
code[curOffset + 2] = 0;
|
||||||
code[curOffset + 3] = 0;
|
code[curOffset + 3] = 0;
|
||||||
|
|
|
@ -26,7 +26,7 @@ public class InvokeDynamicInstruction extends Instruction implements IInvokeInst
|
||||||
protected String methodName;
|
protected String methodName;
|
||||||
protected String methodType;
|
protected String methodType;
|
||||||
|
|
||||||
private InvokeDynamicInstruction(short opcode, BootstrapMethod bootstrap, String methodName, String methodType) {
|
public InvokeDynamicInstruction(short opcode, BootstrapMethod bootstrap, String methodName, String methodType) {
|
||||||
super(opcode);
|
super(opcode);
|
||||||
this.bootstrap = bootstrap;
|
this.bootstrap = bootstrap;
|
||||||
this.methodName = methodName;
|
this.methodName = methodName;
|
||||||
|
|
|
@ -15,6 +15,7 @@ import java.util.Random;
|
||||||
import com.ibm.wala.shrikeBT.Compiler;
|
import com.ibm.wala.shrikeBT.Compiler;
|
||||||
import com.ibm.wala.shrikeBT.MethodData;
|
import com.ibm.wala.shrikeBT.MethodData;
|
||||||
import com.ibm.wala.shrikeCT.ClassWriter;
|
import com.ibm.wala.shrikeCT.ClassWriter;
|
||||||
|
import com.ibm.wala.shrikeCT.BootstrapMethodsReader.BootstrapMethod;
|
||||||
import com.ibm.wala.shrikeCT.ConstantPoolParser.ReferenceToken;
|
import com.ibm.wala.shrikeCT.ConstantPoolParser.ReferenceToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,6 +74,11 @@ final public class CTCompiler extends Compiler {
|
||||||
return cw.addCPMethodHandle(c);
|
return cw.addCPMethodHandle(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int allocateConstantPoolInvokeDynamic(BootstrapMethod b, String name, String type) {
|
||||||
|
return cw.addCPInvokeDynamic(b, name, type);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a JVM type to the internal JVM class name (e.g., Ljava/lang/Object; to java/lang/Object)
|
* Convert a JVM type to the internal JVM class name (e.g., Ljava/lang/Object; to java/lang/Object)
|
||||||
*
|
*
|
||||||
|
|
|
@ -21,7 +21,7 @@ import com.ibm.wala.shrikeCT.ClassReader.AttrIterator;
|
||||||
public class BootstrapMethodsReader extends AttributeReader {
|
public class BootstrapMethodsReader extends AttributeReader {
|
||||||
|
|
||||||
public interface BootstrapMethod {
|
public interface BootstrapMethod {
|
||||||
int invokeType();
|
byte invokeType();
|
||||||
String methodClass();
|
String methodClass();
|
||||||
String methodName();
|
String methodName();
|
||||||
String methodType();
|
String methodType();
|
||||||
|
@ -52,7 +52,7 @@ public class BootstrapMethodsReader extends AttributeReader {
|
||||||
|
|
||||||
final int argumentCount = cr.getUShort(attr + base + 2);
|
final int argumentCount = cr.getUShort(attr + base + 2);
|
||||||
entries[i] = new BootstrapMethod() {
|
entries[i] = new BootstrapMethod() {
|
||||||
private final int invokeType = cp.getCPHandleKind(methodHandleOffset);
|
private final byte invokeType = cp.getCPHandleKind(methodHandleOffset);
|
||||||
private final String methodClass = cp.getCPHandleClass(methodHandleOffset);
|
private final String methodClass = cp.getCPHandleClass(methodHandleOffset);
|
||||||
private final String methodName = cp.getCPHandleName(methodHandleOffset);
|
private final String methodName = cp.getCPHandleName(methodHandleOffset);
|
||||||
private final String methodType = cp.getCPHandleType(methodHandleOffset);
|
private final String methodType = cp.getCPHandleType(methodHandleOffset);
|
||||||
|
@ -63,7 +63,7 @@ public class BootstrapMethodsReader extends AttributeReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int invokeType() {
|
public byte invokeType() {
|
||||||
return invokeType;
|
return invokeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ package com.ibm.wala.shrikeCT;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import com.ibm.wala.shrikeCT.BootstrapMethodsReader.BootstrapMethod;
|
||||||
import com.ibm.wala.shrikeCT.ConstantPoolParser.ReferenceToken;
|
import com.ibm.wala.shrikeCT.ConstantPoolParser.ReferenceToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -202,6 +203,40 @@ public class ClassWriter implements ClassConstants {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class CWInvokeDynamic extends CWItem {
|
||||||
|
final private BootstrapMethod b;
|
||||||
|
|
||||||
|
final private String n;
|
||||||
|
|
||||||
|
final private String t;
|
||||||
|
|
||||||
|
CWInvokeDynamic(BootstrapMethod b, String n, String t) {
|
||||||
|
this.b = b;
|
||||||
|
this.n = n;
|
||||||
|
this.t = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o instanceof CWInvokeDynamic) {
|
||||||
|
CWInvokeDynamic r = (CWInvokeDynamic) o;
|
||||||
|
return r.b.equals(b) && r.n.equals(n) && r.t.equals(t);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return (b.hashCode() << 10) + (n.hashCode() << 3) + t.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
byte getType() {
|
||||||
|
return CONSTANT_InvokeDynamic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy a constant pool from some ClassReader into this class. This must be done before any entries are allocated in this
|
* Copy a constant pool from some ClassReader into this class. This must be done before any entries are allocated in this
|
||||||
* ClassWriter's constant pool, and it can only be done once. If and only if this is done, it is safe to copy "raw" fields,
|
* ClassWriter's constant pool, and it can only be done once. If and only if this is done, it is safe to copy "raw" fields,
|
||||||
|
@ -247,6 +282,9 @@ public class ClassWriter implements ClassConstants {
|
||||||
case CONSTANT_NameAndType:
|
case CONSTANT_NameAndType:
|
||||||
cachedCPEntries.put(new CWNAT(cp.getCPNATName(i), cp.getCPNATType(i)), new Integer(i));
|
cachedCPEntries.put(new CWNAT(cp.getCPNATName(i), cp.getCPNATType(i)), new Integer(i));
|
||||||
break;
|
break;
|
||||||
|
case CONSTANT_InvokeDynamic:
|
||||||
|
cachedCPEntries.put(new CWInvokeDynamic(cp.getCPDynBootstrap(i), cp.getCPDynName(i), cp.getCPDynType(i)), new Integer(i));
|
||||||
|
break;
|
||||||
case CONSTANT_Integer:
|
case CONSTANT_Integer:
|
||||||
cachedCPEntries.put(new Integer(cp.getCPInt(i)), new Integer(i));
|
cachedCPEntries.put(new Integer(cp.getCPInt(i)), new Integer(i));
|
||||||
break;
|
break;
|
||||||
|
@ -433,6 +471,18 @@ public class ClassWriter implements ClassConstants {
|
||||||
return addCPEntry(new CWNAT(n, t), 1);
|
return addCPEntry(new CWNAT(n, t), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an InvokeDynamic to the constant pool if necessary.
|
||||||
|
*
|
||||||
|
* @param n the name
|
||||||
|
* @param t the type, in JVM format
|
||||||
|
* @return the index of a constant pool item with the right value
|
||||||
|
*/
|
||||||
|
public int addCPInvokeDynamic(BootstrapMethod b, String n, String t) {
|
||||||
|
return addCPEntry(new CWInvokeDynamic(b, n, t), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the access flags for the class.
|
* Set the access flags for the class.
|
||||||
*/
|
*/
|
||||||
|
@ -762,6 +812,13 @@ public class ClassWriter implements ClassConstants {
|
||||||
setUShort(buf, offset + 3, addCPUtf8(nat.t));
|
setUShort(buf, offset + 3, addCPUtf8(nat.t));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case CONSTANT_InvokeDynamic: {
|
||||||
|
offset = reserveBuf(5);
|
||||||
|
CWInvokeDynamic inv = (CWInvokeDynamic) item;
|
||||||
|
setUShort(buf, offset+1, inv.b.getIndexInClassFile());
|
||||||
|
setUShort(buf, offset+3, addCPNAT(inv.n, inv.t));
|
||||||
|
break;
|
||||||
|
}
|
||||||
case CONSTANT_MethodHandle: {
|
case CONSTANT_MethodHandle: {
|
||||||
offset = reserveBuf(4);
|
offset = reserveBuf(4);
|
||||||
CWHandle handle = (CWHandle) item;
|
CWHandle handle = (CWHandle) item;
|
||||||
|
|
Loading…
Reference in New Issue