Fixes for instrumentation of invoke dynamic

This commit is contained in:
Julian Dolby 2017-12-01 20:01:16 +08:00
parent d685fbb71f
commit 7cd2a2acf8
9 changed files with 99 additions and 12 deletions

View File

@ -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)));

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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)
*

View File

@ -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;
}

View File

@ -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;