Fixes for instrumentation of invoke dynamic
This commit is contained in:
parent
d685fbb71f
commit
7cd2a2acf8
|
@ -19,7 +19,9 @@ public class SortingExample {
|
|||
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() {
|
||||
return sort( (String l, String r) -> id1(l.compareTo(r)));
|
||||
|
|
|
@ -73,4 +73,13 @@ public class DynamicCallGraphTest extends DynamicCallGraphTestBase {
|
|||
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;
|
||||
} else {
|
||||
String callerMethod = edge.nextToken();
|
||||
if (callerMethod.startsWith("lambda$")) {
|
||||
continue loop;
|
||||
}
|
||||
MethodReference callerRef = MethodReference.findOrCreate(TypeReference.findOrCreate(ClassLoaderReference.Application, "L" + callerClass), Selector.make(callerMethod));
|
||||
Set<CGNode> nodes = staticCG.getNodes(callerRef);
|
||||
if (! filter.test(callerRef)) {
|
||||
continue loop;
|
||||
}
|
||||
Assert.assertEquals(1, nodes.size());
|
||||
Assert.assertEquals(callerMethod, 1, nodes.size());
|
||||
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.IInvokeInstruction;
|
||||
import com.ibm.wala.shrikeBT.IInvokeInstruction.Dispatch;
|
||||
import com.ibm.wala.shrikeBT.InvokeDynamicInstruction;
|
||||
import com.ibm.wala.shrikeBT.InvokeInstruction;
|
||||
import com.ibm.wala.shrikeBT.LoadInstruction;
|
||||
import com.ibm.wala.shrikeBT.MethodData;
|
||||
|
@ -248,7 +249,7 @@ public class OfflineDynamicCallGraph {
|
|||
this.replaceWith(new MethodEditor.Patch() {
|
||||
@Override
|
||||
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.getMethodSignature();
|
||||
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();
|
||||
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();
|
||||
|
|
|
@ -19,6 +19,7 @@ import com.ibm.wala.shrikeBT.ConstantInstruction.ClassToken;
|
|||
import com.ibm.wala.shrikeBT.IBinaryOpInstruction.Operator;
|
||||
import com.ibm.wala.shrikeBT.analysis.ClassHierarchyProvider;
|
||||
import com.ibm.wala.shrikeBT.analysis.Verifier;
|
||||
import com.ibm.wala.shrikeCT.BootstrapMethodsReader.BootstrapMethod;
|
||||
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 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() {
|
||||
final BitSet s = new BitSet(instructions.length);
|
||||
final BitSet localsUsed = new BitSet(32);
|
||||
|
@ -955,14 +958,14 @@ public abstract class Compiler implements Constants {
|
|||
}
|
||||
case OP_invokedynamic: {
|
||||
InvokeDynamicInstruction inv = (InvokeDynamicInstruction) instr;
|
||||
String sig = inv.getMethodSignature();
|
||||
|
||||
int cpIndex;
|
||||
|
||||
if (presetConstants != null && presetConstants == inv.getLazyConstantPool()) {
|
||||
cpIndex = ((InvokeDynamicInstruction.Lazy) inv).getCPIndex();
|
||||
} else {
|
||||
cpIndex = allocateConstantPoolInterfaceMethod(inv.getClassType(), inv.getMethodName(), sig);
|
||||
cpIndex = allocateConstantPoolInvokeDynamic(inv.getBootstrap(), inv.getMethodName(), inv.getMethodSignature());
|
||||
}
|
||||
|
||||
writeShort(curOffset, cpIndex);
|
||||
code[curOffset + 2] = 0;
|
||||
code[curOffset + 3] = 0;
|
||||
|
|
|
@ -26,7 +26,7 @@ public class InvokeDynamicInstruction extends Instruction implements IInvokeInst
|
|||
protected String methodName;
|
||||
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);
|
||||
this.bootstrap = bootstrap;
|
||||
this.methodName = methodName;
|
||||
|
|
|
@ -15,6 +15,7 @@ import java.util.Random;
|
|||
import com.ibm.wala.shrikeBT.Compiler;
|
||||
import com.ibm.wala.shrikeBT.MethodData;
|
||||
import com.ibm.wala.shrikeCT.ClassWriter;
|
||||
import com.ibm.wala.shrikeCT.BootstrapMethodsReader.BootstrapMethod;
|
||||
import com.ibm.wala.shrikeCT.ConstantPoolParser.ReferenceToken;
|
||||
|
||||
/**
|
||||
|
@ -73,6 +74,11 @@ final public class CTCompiler extends Compiler {
|
|||
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)
|
||||
*
|
||||
|
|
|
@ -21,7 +21,7 @@ import com.ibm.wala.shrikeCT.ClassReader.AttrIterator;
|
|||
public class BootstrapMethodsReader extends AttributeReader {
|
||||
|
||||
public interface BootstrapMethod {
|
||||
int invokeType();
|
||||
byte invokeType();
|
||||
String methodClass();
|
||||
String methodName();
|
||||
String methodType();
|
||||
|
@ -52,7 +52,7 @@ public class BootstrapMethodsReader extends AttributeReader {
|
|||
|
||||
final int argumentCount = cr.getUShort(attr + base + 2);
|
||||
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 methodName = cp.getCPHandleName(methodHandleOffset);
|
||||
private final String methodType = cp.getCPHandleType(methodHandleOffset);
|
||||
|
@ -63,7 +63,7 @@ public class BootstrapMethodsReader extends AttributeReader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int invokeType() {
|
||||
public byte invokeType() {
|
||||
return invokeType;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ package com.ibm.wala.shrikeCT;
|
|||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.ibm.wala.shrikeCT.BootstrapMethodsReader.BootstrapMethod;
|
||||
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
|
||||
* 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:
|
||||
cachedCPEntries.put(new CWNAT(cp.getCPNATName(i), cp.getCPNATType(i)), new Integer(i));
|
||||
break;
|
||||
case CONSTANT_InvokeDynamic:
|
||||
cachedCPEntries.put(new CWInvokeDynamic(cp.getCPDynBootstrap(i), cp.getCPDynName(i), cp.getCPDynType(i)), new Integer(i));
|
||||
break;
|
||||
case CONSTANT_Integer:
|
||||
cachedCPEntries.put(new Integer(cp.getCPInt(i)), new Integer(i));
|
||||
break;
|
||||
|
@ -433,6 +471,18 @@ public class ClassWriter implements ClassConstants {
|
|||
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.
|
||||
*/
|
||||
|
@ -762,6 +812,13 @@ public class ClassWriter implements ClassConstants {
|
|||
setUShort(buf, offset + 3, addCPUtf8(nat.t));
|
||||
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: {
|
||||
offset = reserveBuf(4);
|
||||
CWHandle handle = (CWHandle) item;
|
||||
|
|
Loading…
Reference in New Issue