java 7 support, with much pain for stack maps :)

This commit is contained in:
Julian Dolby 2014-10-06 15:34:16 -04:00
parent c006dbcdd4
commit 36709b9d1a
45 changed files with 1178 additions and 95 deletions

View File

@ -2,6 +2,6 @@
<classpath>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@ -6,9 +6,9 @@ org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annota
org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
@ -105,7 +105,7 @@ org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disa
org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
org.eclipse.jdt.core.compiler.source=1.6
org.eclipse.jdt.core.compiler.source=1.7
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0

View File

@ -3,6 +3,7 @@ package com.ibm.wala.cast.js.rhino.callgraph.fieldbased.test;
import java.io.IOException;
import java.net.URL;
import org.junit.Ignore;
import org.junit.Test;
import com.ibm.wala.cast.ir.translator.TranslatorToCAst.Error;

View File

@ -22,7 +22,8 @@
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.ibm.wala.cast.js.test.data/examples-src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.launching.macosx.MacOSXType/Java SE 7/"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.ibm.wala.cast.js.rhino.test"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx800M -verbose:gc -ea"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048M -verbose:gc -ea"/>
</launchConfiguration>

View File

@ -0,0 +1,2 @@
eclipse.preferences.version=1
encoding//harness-src/com/ibm/wala/cast/js/test/TestCorrelatedPairExtraction.java=UTF-8

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.ibm.wala.cast.js.test/harness-src/com/ibm/wala/cast/js/test/TestSimpleCallGraphShape.java"/>
<listEntry value="/com.ibm.wala.cast.js.rhino.test/harness-src/com/ibm/wala/cast/js/test/TestSimpleCallGraphShapeRhino.java"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="1"/>
@ -10,8 +10,8 @@
<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value=""/>
<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit3"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.ibm.wala.cast.js.test.TestSimpleCallGraphShape"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.ibm.wala.cast.js.test"/>
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.ibm.wala.cast.js.test.TestSimpleCallGraphShapeRhino"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.ibm.wala.cast.js.rhino.test"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx256M -Dcom.ibm.wala.tracefile=/tmp/jssimplecg.out -ea"/>
</launchConfiguration>

View File

@ -312,7 +312,7 @@ function Referrer() {
this.toString = function () { return new String();}
}
document.referrer = new Referrer();
document.evaluate = function evaluate(a, b, c, d, e) {
document.evaluate = function documentEvaluate(a, b, c, d, e) {
}
document.execCommand = function execCommand(a,b,c) {}

View File

@ -74,6 +74,7 @@ import com.ibm.wala.classLoader.SourceModule;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeBT.IBinaryOpInstruction.IOperator;
import com.ibm.wala.shrikeBT.IComparisonInstruction.Operator;
import com.ibm.wala.shrikeCT.BootstrapMethodsReader.BootstrapMethod;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.ssa.SSAAddressOfInstruction;
import com.ibm.wala.ssa.SSAArrayLengthInstruction;
@ -603,6 +604,11 @@ public class JavaScriptLoader extends CAstAbstractModuleLoader {
return new SetPrototype(object, prototype);
}
@Override
public SSAInstruction InvokeInstruction(int i, int[] js, int j, CallSiteReference site, BootstrapMethod bootstrap) {
throw new UnsupportedOperationException();
}
};
}

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.ibm.wala.cast.test"/>
@ -11,6 +11,7 @@
<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.launching.macosx.MacOSXType/Java SE 7"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.ibm.wala.cast.test"/>
</launchConfiguration>

View File

@ -6,9 +6,9 @@ org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annota
org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
@ -105,7 +105,7 @@ org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disa
org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
org.eclipse.jdt.core.compiler.source=1.6
org.eclipse.jdt.core.compiler.source=1.7
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0

View File

@ -2,9 +2,11 @@ package dynamicCG;
public class ExtraClass {
private final Object x;
private final long l;
public ExtraClass(Object x) {
this.x = x;
this.l = (x==null)? 0: x.hashCode();
}
public ExtraClass() {
@ -21,6 +23,12 @@ public class ExtraClass {
@Override
public String toString() {
return getName(x);
String s = getName(x);
long t = l;
String s2 = getName(x);
if (t < 0) {
t = 0;
}
return s + ":" + t + ":" + s2;
}
}

View File

@ -2,7 +2,7 @@ package dynamicCG;
public class MainClass {
private final Object x;
private MainClass(Object x) {
this.x = x;
}

View File

@ -0,0 +1,41 @@
package shrike;
public class StackMaps {
private int field1;
private int field2;
private int min;
public StackMaps(int field1, int field2) {
setMin(field1 > field2? field2: field1);
this.field1 = field1;
this.field2 = field2;
}
private void setMin(int x) {
this.min = x;
}
public static void main(String[] args) {
String result = "success";
try {
if ("max".equals(args[0])) {
System.err.println(new StackMaps(3, 2).max());
} else {
System.err.println(new StackMaps("min" == args[0]? 7: 5, 2).min());
}
} catch (RuntimeException e) {
result = "bad";
throw e;
} finally {
System.err.println(result);
}
}
public int max() {
return field1 > field2? field1: field2;
}
public int min() {
return min;
}
}

View File

@ -0,0 +1,2 @@
Primordial,Java,stdlib,none
Primordial,Java,jarFile,primordial.jar.model

View File

@ -0,0 +1,3 @@
Primordial,Java,stdlib,none
Primordial,Java,jarFile,primordial.jar.model
Application,Java,jarFile,compr

View File

@ -18,6 +18,7 @@ import org.junit.Assert;
import com.ibm.wala.core.tests.util.WalaTestCase;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.CallGraph;
import com.ibm.wala.properties.WalaProperties;
import com.ibm.wala.shrike.cg.DynamicCallGraph;
import com.ibm.wala.shrikeBT.analysis.Analyzer.FailureException;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
@ -60,7 +61,17 @@ public abstract class DynamicCallGraphTestBase extends WalaTestCase {
assert new File(instrumentedJarLocation).delete();
}
DynamicCallGraph.main(new String[]{testJarLocation, "-o", instrumentedJarLocation});
String rtJar = null;
for(String jar : WalaProperties.getJ2SEJarFiles()) {
if (jar.endsWith("rt.jar") || jar.endsWith("classes.jar")) {
rtJar = jar;
}
}
DynamicCallGraph.main(
rtJar == null?
new String[]{testJarLocation, "-o", instrumentedJarLocation}:
new String[]{testJarLocation, "-o", instrumentedJarLocation, "--rt-jar", rtJar});
Assert.assertTrue("expected to create /tmp/test.jar", new File(instrumentedJarLocation).exists());
instrumentedJarBuilt = true;
}
@ -82,11 +93,19 @@ public abstract class DynamicCallGraphTestBase extends WalaTestCase {
System.setProperty("dynamicCGFilter", tmpFile.getCanonicalPath());
}
try {
testMain.invoke(null, (Object)new String[0]);
testMain.invoke(null, (Object)new String[0]);
} catch (Throwable e) {
// exceptions here are from program being instrumented
// this is fine, since we are collecting its call graph
// and exceptions are possible behavior.
// well, most errors are fine. On the other hand, low-level
// class loading errors likely indicate a bug in instrumentation,
// which is often tested with this test.
while (e.getCause() != null) {
Assert.assertFalse(String.valueOf(e.getCause()), e.getCause() instanceof LinkageError);
e = e.getCause();
}
}
// the VM is not exiting, so stop tracing explicitly

View File

@ -146,13 +146,10 @@ public class ShrikeCFG extends AbstractCFG<IInstruction, ShrikeCFG.BasicBlock> {
Assertions.UNREACHABLE();
handlers = null;
}
boolean[] r = new boolean[getInstructions().length];
boolean[] catchers = new boolean[getInstructions().length];
// we initially start with both the entry and exit block.
int blockCount = 2;
// Compute r so r[i] == true iff instruction i begins a basic block.
// While doing so count the number of blocks.
boolean[] r = new boolean[getInstructions().length];
boolean[] catchers = new boolean[getInstructions().length];
r[0] = true;
IInstruction[] instructions = getInstructions();
for (int i = 0; i < instructions.length; i++) {
@ -163,14 +160,12 @@ public class ShrikeCFG extends AbstractCFG<IInstruction, ShrikeCFG.BasicBlock> {
if (targets.length > 0 || !instructions[i].isFallThrough()) {
if (i + 1 < instructions.length && !r[i + 1]) {
r[i + 1] = true;
blockCount++;
}
}
for (int j = 0; j < targets.length; j++) {
if (!r[targets[j]]) {
r[targets[j]] = true;
blockCount++;
}
}
if (instructions[i].isPEI()) {
@ -178,7 +173,6 @@ public class ShrikeCFG extends AbstractCFG<IInstruction, ShrikeCFG.BasicBlock> {
// break the basic block here.
if (i + 1 < instructions.length && !r[i + 1]) {
r[i + 1] = true;
blockCount++;
}
if (hs != null && hs.length > 0) {
for (int j = 0; j < hs.length; j++) {
@ -187,7 +181,6 @@ public class ShrikeCFG extends AbstractCFG<IInstruction, ShrikeCFG.BasicBlock> {
// we have not discovered the catch block yet.
// form a new basic block
r[hs[j].getHandler()] = true;
blockCount++;
}
catchers[hs[j].getHandler()] = true;
}

View File

@ -30,6 +30,7 @@ import com.ibm.wala.shrikeBT.IConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IInstruction;
import com.ibm.wala.shrikeBT.IUnaryOpInstruction;
import com.ibm.wala.shrikeBT.Instruction;
import com.ibm.wala.shrikeCT.BootstrapMethodsReader.BootstrapMethod;
import com.ibm.wala.shrikeCT.ConstantPoolParser.ReferenceToken;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.ssa.SSAAddressOfInstruction;
@ -47,6 +48,7 @@ import com.ibm.wala.ssa.SSAGotoInstruction;
import com.ibm.wala.ssa.SSAInstanceofInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAInvokeDynamicInstruction;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSALoadIndirectInstruction;
import com.ibm.wala.ssa.SSALoadMetadataInstruction;
@ -238,6 +240,19 @@ public class JavaLanguage extends LanguageImpl implements BytecodeLanguage, Cons
};
}
public SSAInvokeDynamicInstruction InvokeInstruction(int result, int[] params, int exception, CallSiteReference site, BootstrapMethod bootstrap) {
return new SSAInvokeDynamicInstruction(result, params, exception, site, bootstrap) {
@Override
public Collection<TypeReference> getExceptionTypes() {
if (!isStatic()) {
return getNullPointerException();
} else {
return Collections.emptySet();
}
}
};
}
@Override
public SSAInvokeInstruction InvokeInstruction(int[] params, int exception, CallSiteReference site) {
return new SSAInvokeInstruction(params, exception, site) {

View File

@ -17,6 +17,7 @@ import com.ibm.wala.shrikeBT.IBinaryOpInstruction;
import com.ibm.wala.shrikeBT.IComparisonInstruction;
import com.ibm.wala.shrikeBT.IConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IUnaryOpInstruction;
import com.ibm.wala.shrikeCT.BootstrapMethodsReader.BootstrapMethod;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.TypeReference;
@ -99,4 +100,6 @@ public interface SSAInstructionFactory {
SSAUnaryOpInstruction UnaryOpInstruction(IUnaryOpInstruction.IOperator operator, int result, int val);
SSAInstruction InvokeInstruction(int i, int[] js, int j, CallSiteReference site, BootstrapMethod bootstrap);
}

View File

@ -0,0 +1,36 @@
/*******************************************************************************
* Copyright (c) 2007 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.ssa;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.shrikeCT.BootstrapMethodsReader.BootstrapMethod;
public class SSAInvokeDynamicInstruction extends SSAInvokeInstruction {
private final BootstrapMethod bootstrap;
public SSAInvokeDynamicInstruction(int result, int[] params, int exception, CallSiteReference site, BootstrapMethod bootstrap) {
super(result, params, exception, site);
this.bootstrap = bootstrap;
}
public SSAInvokeDynamicInstruction(int[] params, int exception, CallSiteReference site, BootstrapMethod bootstrap) {
super(params, exception, site);
this.bootstrap = bootstrap;
}
@Override
public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
return insts.InvokeInstruction(defs == null || result == -1 ? result : defs[0], uses == null ? params : uses,
defs == null ? exception : defs[result == -1 ? 0 : 1], site, bootstrap);
}
}

View File

@ -18,13 +18,13 @@ import com.ibm.wala.types.TypeReference;
*/
public abstract class SSAInvokeInstruction extends SSAAbstractInvokeInstruction {
private final int result;
protected final int result;
/**
* The value numbers of the arguments passed to the call. For non-static methods, params[0] == this. If params == null, this
* should be a static method with no parameters.
*/
private final int[] params;
protected final int[] params;
protected SSAInvokeInstruction(int result, int[] params, int exception, CallSiteReference site) {
super(exception, site);

View File

@ -40,10 +40,9 @@ public abstract class SSAPutInstruction extends SSAFieldAccessInstruction {
@Override
public String toString(SymbolTable symbolTable) {
if (isStatic()) {
return "putstatic " + getValueString(symbolTable, val) + " " + getDeclaredField();
return "putstatic " + getDeclaredField() + " = " + getValueString(symbolTable, val);
} else {
return "putfield " + getValueString(symbolTable, getRef()) + " = " + getValueString(symbolTable, val) + " "
+ getDeclaredField();
return "putfield " + getValueString(symbolTable, getRef()) + "." + getDeclaredField() + " = " + getValueString(symbolTable, val);
}
}

View File

@ -1,4 +1,5 @@
source.. = source/
source.. = source/,\
data/
output.. = bin/
bin.includes = META-INF/,\
.

View File

@ -25,7 +25,7 @@
<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.ibm.wala.cast.java.test.JDTJava15IRTests"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl}"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.ibm.wala.ide.jdt.test"/>

View File

@ -25,7 +25,7 @@
<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.ibm.wala.cast.java.test.JDTJavaIRTests"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl}"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.ibm.wala.ide.jdt.test"/>

View File

@ -53,7 +53,7 @@ final class TypeInferenceAssertion implements IRAssertion {
private IR getIR(CallGraph cg, String fullyQualifiedTypeName, String methodName, String methodParameter, String methodReturnType) {
IClassHierarchy classHierarchy = cg.getClassHierarchy();
MethodReference methodRef = JDTJava15IRTests
MethodReference methodRef = IRTests
.descriptorToMethodRef(
String.format("Source#%s#%s#(%s)%s", fullyQualifiedTypeName, methodName, methodParameter, methodReturnType),
classHierarchy);

View File

@ -196,6 +196,7 @@ public class DemandCastChecker {
return Pair.make(retCG, retPA);
}
@SuppressWarnings("unused")
private static RefinementPolicyFactory chooseRefinePolicyFactory(ClassHierarchy cha) {
if (true) {
return new TunedRefinementPolicy.Factory(cha);

View File

@ -11,15 +11,16 @@
package com.ibm.wala.shrike.cg;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Map;
import com.ibm.wala.shrikeBT.ConstantInstruction;
import com.ibm.wala.shrikeBT.Constants;
import com.ibm.wala.shrikeBT.Disassembler;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.shrikeBT.LoadInstruction;
import com.ibm.wala.shrikeBT.MethodData;
import com.ibm.wala.shrikeBT.MethodEditor;
@ -28,12 +29,16 @@ import com.ibm.wala.shrikeBT.ReturnInstruction;
import com.ibm.wala.shrikeBT.ThrowInstruction;
import com.ibm.wala.shrikeBT.Util;
import com.ibm.wala.shrikeBT.analysis.Analyzer.FailureException;
import com.ibm.wala.shrikeBT.analysis.ClassHierarchyStore;
import com.ibm.wala.shrikeBT.analysis.Verifier;
import com.ibm.wala.shrikeBT.shrikeCT.CTUtils;
import com.ibm.wala.shrikeBT.shrikeCT.ClassInstrumenter;
import com.ibm.wala.shrikeBT.shrikeCT.OfflineInstrumenter;
import com.ibm.wala.shrikeCT.ClassReader;
import com.ibm.wala.shrikeCT.ClassWriter;
import com.ibm.wala.shrikeCT.ConstantPoolParser;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.config.FileOfClasses;
import com.ibm.wala.util.config.SetOfClasses;
@ -61,11 +66,13 @@ public class DynamicCallGraph {
private static SetOfClasses filter;
private static ClassHierarchyStore cha = new ClassHierarchyStore();
public static void main(String[] args) throws IOException, ClassNotFoundException, InvalidClassFileException, FailureException {
instrumenter = new OfflineInstrumenter();
ClassInstrumenter ci;
Writer w = new BufferedWriter(new FileWriter("report", false));
instrumenter = new OfflineInstrumenter();
args = instrumenter.parseStandardArgs(args);
for(int i = 0; i < args.length - 1; i++) {
@ -73,15 +80,30 @@ public class DynamicCallGraph {
runtime = Class.forName(args[i+1]);
} else if ("--exclusions".equals(args[i])) {
filter = new FileOfClasses(new FileInputStream(args[i+1]));
} else if ("--rt-jar".equals(args[i])) {
System.err.println("using " + args[i+1] + " as stdlib");
OfflineInstrumenter libReader = new OfflineInstrumenter();
libReader.addInputJar(new File(args[i+1]));
while ((ci = libReader.nextClass()) != null) {
CTUtils.addClassToHierarchy(cha, ci.getReader());
}
}
}
instrumenter.setPassUnmodifiedClasses(true);
instrumenter.beginTraversal();
while ((ci = instrumenter.nextClass()) != null) {
CTUtils.addClassToHierarchy(cha, ci.getReader());
}
instrumenter.setClassHierarchyProvider(cha);
instrumenter.beginTraversal();
ClassInstrumenter ci;
while ((ci = instrumenter.nextClass()) != null) {
doClass(ci, w);
}
instrumenter.close();
}
@ -93,7 +115,8 @@ public class DynamicCallGraph {
w.write("Class: " + className + "\n");
w.flush();
ClassReader r = ci.getReader();
final ClassReader r = ci.getReader();
for (int m = 0; m < ci.getReader().getMethodCount(); m++) {
final MethodData d = ci.visitMethod(m);
@ -114,6 +137,7 @@ public class DynamicCallGraph {
if (verify) {
Verifier v = new Verifier(d);
v.setClassHierarchy(cha);
v.verify();
}
@ -124,6 +148,7 @@ public class DynamicCallGraph {
final String theMethod = r.getMethodName(m).concat(r.getMethodType(m));
final boolean isConstructor = theMethod.contains("<init>");
final boolean nonStatic = !java.lang.reflect.Modifier.isStatic(r.getMethodAccessFlags(m));
me.insertAtStart(new MethodEditor.Patch() {
@Override
public void emitTo(MethodEditor.Output w) {
@ -173,8 +198,7 @@ public class DynamicCallGraph {
});
}
});
// this updates the data d
me.applyPatches();
@ -186,13 +210,67 @@ public class DynamicCallGraph {
if (verify) {
Verifier v = new Verifier(d);
v.setClassHierarchy(cha);
v.verify();
}
}
}
if (ci.isChanged()) {
ClassWriter cw = ci.emitClass();
ClassWriter cw = new ClassWriter() {
private final Map<Object, Integer> entries = HashMapFactory.make();
{
ConstantPoolParser p = r.getCP();
for(int i = 1; i < p.getItemCount(); i++) {
switch (p.getItemType(i)) {
case CONSTANT_Integer:
entries.put(new Integer(p.getCPInt(i)), i);
break;
case CONSTANT_Long:
entries.put(new Long(p.getCPLong(i)), i);
break;
case CONSTANT_Float:
entries.put(new Float(p.getCPFloat(i)), i);
break;
case CONSTANT_Double:
entries.put(new Double(p.getCPDouble(i)), i);
break;
case CONSTANT_Utf8:
entries.put(p.getCPUtf8(i), i);
break;
case CONSTANT_String:
entries.put(new CWString(p.getCPString(i)), i);
break;
case CONSTANT_Class:
entries.put(new CWClass(p.getCPClass(i)), i);
break;
case CONSTANT_MethodHandle:
case CONSTANT_MethodType:
case CONSTANT_InvokeDynamic:
}
}
}
private int findExistingEntry(Object o) {
if (entries.containsKey(o)) {
return entries.get(o);
} else {
return -1;
}
}
@Override
protected int addCPEntry(Object o, int size) {
int entry = findExistingEntry(o);
if (entry != -1) {
return entry;
} else {
return super.addCPEntry(o, size);
}
}
};
ci.emitClass(cw);
instrumenter.outputModifiedClass(ci, cw);
}
}

View File

@ -30,6 +30,8 @@ import com.ibm.wala.shrikeBT.analysis.Verifier;
*/
public abstract class Compiler implements Constants {
// input
final private boolean isConstructor;
final private boolean isStatic;
final private String classType;
@ -90,7 +92,7 @@ public abstract class Compiler implements Constants {
* @throws IllegalArgumentException if instructions is null
* @throws IllegalArgumentException if instructionsToBytecodes is null
*/
public Compiler(boolean isStatic, String classType, String signature, IInstruction[] instructions, ExceptionHandler[][] handlers,
public Compiler(boolean isConstructor, boolean isStatic, String classType, String signature, IInstruction[] instructions, ExceptionHandler[][] handlers,
int[] instructionsToBytecodes) {
if (instructionsToBytecodes == null) {
throw new IllegalArgumentException("instructionsToBytecodes is null");
@ -108,6 +110,7 @@ public abstract class Compiler implements Constants {
throw new IllegalArgumentException("Instructions/handlers length mismatch");
}
this.isConstructor = isConstructor;
this.isStatic = isStatic;
this.classType = classType;
this.signature = signature;
@ -120,7 +123,7 @@ public abstract class Compiler implements Constants {
* Extract the data for the method to be compiled from the MethodData container.
*/
protected Compiler(MethodData info) {
this(info.getIsStatic(), info.getClassType(), info.getSignature(), info.getInstructions(), info.getHandlers(), info
this(info.getName().equals("<init>"), info.getIsStatic(), info.getClassType(), info.getSignature(), info.getInstructions(), info.getHandlers(), info
.getInstructionsToBytecodes());
}
@ -935,6 +938,22 @@ public abstract class Compiler implements Constants {
curOffset += 2;
break;
}
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);
}
writeShort(curOffset, cpIndex);
code[curOffset + 2] = 0;
code[curOffset + 3] = 0;
curOffset += 4;
break;
}
case OP_invokeinterface: {
InvokeInstruction inv = (InvokeInstruction) instr;
String sig = inv.getMethodSignature();
@ -951,7 +970,7 @@ public abstract class Compiler implements Constants {
curOffset += 4;
break;
}
case OP_new:
case OP_new:
writeShort(curOffset, allocateConstantPoolClassType(((NewInstruction) instr).getType()));
curOffset += 2;
break;
@ -1365,7 +1384,7 @@ public abstract class Compiler implements Constants {
int[] rawHandlers = buildRawHandlers(start, start + len);
int[] bytecodeMap = buildBytecodeMap(start, start + len);
auxMethods.add(new Output(name, sig, newCode, rawHandlers, bytecodeMap, maxLocals, maxStack, true));
auxMethods.add(new Output(name, sig, newCode, rawHandlers, bytecodeMap, maxLocals, maxStack, true, null));
maxStack = savedMaxStack;
@ -1662,7 +1681,7 @@ public abstract class Compiler implements Constants {
}
private void makeTypes() {
Verifier v = new Verifier(isStatic, classType, signature, instructions, handlers);
Verifier v = new Verifier(isConstructor, isStatic, classType, signature, instructions, handlers, instructionsToBytecodes);
if (hierarchy != null) {
v.setClassHierarchy(hierarchy);
}
@ -1700,7 +1719,7 @@ public abstract class Compiler implements Constants {
}
}
mainMethod = new Output(null, null, code, buildRawHandlers(0, instructions.length), buildBytecodeMap(0, instructions.length),
maxLocals, maxStack, isStatic);
maxLocals, maxStack, isStatic, instructionsToOffsets);
instructionsToOffsets = null;
branchTargets = null;
@ -1750,8 +1769,10 @@ public abstract class Compiler implements Constants {
final private int maxStack;
final private int[] instructionsToOffsets;
Output(String name, String signature, byte[] code, int[] rawHandlers, int[] newBytecodesToOldBytecodes, int maxLocals,
int maxStack, boolean isStatic) {
int maxStack, boolean isStatic, int[] instructionsToOffsets) {
this.code = code;
this.name = name;
this.signature = signature;
@ -1760,6 +1781,7 @@ public abstract class Compiler implements Constants {
this.isStatic = isStatic;
this.maxLocals = maxLocals;
this.maxStack = maxStack;
this.instructionsToOffsets = instructionsToOffsets;
}
/**
@ -1769,6 +1791,10 @@ public abstract class Compiler implements Constants {
return code;
}
public int[] getInstructionOffsets() {
return instructionsToOffsets;
}
/**
* @return the name of the method; either "null", if this code takes the place of the original method, or some string
* representing the name of a helper method

View File

@ -2,7 +2,9 @@ package com.ibm.wala.shrikeBT;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@ -20,6 +22,10 @@ public class InvokeDynamicInstruction extends Instruction implements IInvokeInst
this.methodType = methodType;
}
ConstantPoolReader getLazyConstantPool() {
return null;
}
@Override
public boolean isPEI() {
return true;
@ -127,22 +133,35 @@ public class InvokeDynamicInstruction extends Instruction implements IInvokeInst
}
return methodType;
}
@Override
ConstantPoolReader getLazyConstantPool() {
return cp;
}
}
public CallSite bootstrap(Class cl) throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
public CallSite bootstrap(Class cl) throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException {
ClassLoader bootstrapCL = cl.getClassLoader();
Class<?> bootstrapClass = Class.forName(getBootstrap().methodClass().replace('/', '.'), false, bootstrapCL);
MethodType bt = MethodType.fromMethodDescriptorString(bootstrap.methodType(), bootstrapCL);
Method bootstrap = bootstrapClass.getMethod(this.bootstrap.methodName(), bt.parameterList().toArray(new Class[ bt.parameterCount() ]));
Object[] args = new Object[ bt.parameterCount() ];
args[0] = MethodHandles.lookup().in(cl);
Lookup myLookup = MethodHandles.lookup().in(cl);
Field impl_lookup = Lookup.class.getDeclaredField("IMPL_LOOKUP"); // get the required field via reflections
impl_lookup.setAccessible(true); // set it accessible
Lookup lutrusted = (Lookup) impl_lookup.get(myLookup); // get the value of IMPL_LOOKUP from the Lookup instance and save it in a new Lookup object
args[0] = lutrusted;
args[1] = getMethodName();
args[2] = MethodType.fromMethodDescriptorString(getMethodSignature(), cl.getClassLoader());
for(int i = 3; i < bt.parameterCount(); i++) {
args[i] = getBootstrap().callArgument(i-3);
args[i] = getBootstrap().callArgument(bootstrapCL,i-3);
}
//bootstrap.setAccessible(true);
return (CallSite) bootstrap.invoke(null, args);
}

View File

@ -19,6 +19,7 @@ import java.util.Arrays;
import java.util.HashMap;
import com.ibm.wala.shrikeBT.IInvokeInstruction.Dispatch;
import com.ibm.wala.util.collections.Pair;
/**
* This class contains miscellaneous useful functions.
@ -612,4 +613,50 @@ public final class Util {
} while (true);
}
}
public static Pair<boolean[], boolean[]> computeBasicBlocks(IInstruction[] instructions, ExceptionHandler[][] handlers) {
// Compute r so r[i] == true iff instruction i begins a basic block.
boolean[] r = new boolean[instructions.length];
boolean[] catchers = new boolean[instructions.length];
r[0] = true;
for (int i = 0; i < instructions.length; i++) {
int[] targets = instructions[i].getBranchTargets();
// if there are any targets, then break the basic block here.
// also break the basic block after a return
if (targets.length > 0 || !instructions[i].isFallThrough()) {
if (i + 1 < instructions.length && !r[i + 1]) {
r[i + 1] = true;
}
}
for (int j = 0; j < targets.length; j++) {
if (!r[targets[j]]) {
r[targets[j]] = true;
}
}
if (instructions[i].isPEI()) {
ExceptionHandler[] hs = handlers[i];
// break the basic block here.
if (i + 1 < instructions.length && !r[i + 1]) {
r[i + 1] = true;
}
if (hs != null && hs.length > 0) {
for (int j = 0; j < hs.length; j++) {
// exceptionHandlers.add(hs[j]);
if (!r[hs[j].getHandler()]) {
// we have not discovered the catch block yet.
// form a new basic block
r[hs[j].getHandler()] = true;
}
catchers[hs[j].getHandler()] = true;
}
}
}
}
return Pair.make(r, catchers);
}
}

View File

@ -16,13 +16,17 @@ import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import com.ibm.wala.shrikeBT.Constants;
import com.ibm.wala.shrikeBT.DupInstruction;
import com.ibm.wala.shrikeBT.ExceptionHandler;
import com.ibm.wala.shrikeBT.IInstruction;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.shrikeBT.IInvokeInstruction.Dispatch;
import com.ibm.wala.shrikeBT.ILoadInstruction;
import com.ibm.wala.shrikeBT.IStoreInstruction;
import com.ibm.wala.shrikeBT.LoadInstruction;
import com.ibm.wala.shrikeBT.MethodData;
import com.ibm.wala.shrikeBT.NewInstruction;
import com.ibm.wala.shrikeBT.StoreInstruction;
import com.ibm.wala.shrikeBT.SwapInstruction;
import com.ibm.wala.shrikeBT.Util;
@ -32,7 +36,13 @@ import com.ibm.wala.shrikeBT.Util;
*/
public class Analyzer {
public static final String thisType = "THIS";
public static final String topType = "TOP";
// inputs
final protected boolean isConstructor;
final protected boolean isStatic;
final protected String classType;
@ -60,11 +70,13 @@ public class Analyzer {
protected int[][] backEdges;
protected int[] instToBC;
protected final static String[] noStrings = new String[0];
protected final static int[] noEdges = new int[0];
public Analyzer(boolean isStatic, String classType, String signature, IInstruction[] instructions, ExceptionHandler[][] handlers) {
public Analyzer(boolean isConstructor, boolean isStatic, String classType, String signature, IInstruction[] instructions, ExceptionHandler[][] handlers, int[] instToBC) {
if (instructions == null) {
throw new IllegalArgumentException("null instructions");
}
@ -77,10 +89,12 @@ public class Analyzer {
}
}
this.classType = classType;
this.isConstructor = isConstructor;
this.isStatic = isStatic;
this.signature = signature;
this.instructions = instructions;
this.handlers = handlers;
this.instToBC = instToBC;
}
/**
@ -166,12 +180,44 @@ public class Analyzer {
return backEdges;
}
private String patchType(String t) {
if (t == thisType) {
return classType;
} else if (t != null && t.startsWith("#")) {
return stripSharp(t);
} else {
return t;
}
}
final public boolean isSubtypeOf(String t1, String t2) {
return ClassHierarchy.isSubtypeOf(hierarchy, t1, t2) != ClassHierarchy.NO;
return ClassHierarchy.isSubtypeOf(hierarchy, patchType(t1), patchType(t2)) != ClassHierarchy.NO;
}
private boolean isPrimitive(String type) {
return type != null && (!type.startsWith("L") && !type.startsWith("["));
}
final public String findCommonSupertype(String t1, String t2) {
return ClassHierarchy.findCommonSupertype(hierarchy, t1, t2);
if (String.valueOf(t1).equals(String.valueOf(t2))) {
return t1;
}
if (t1 == thisType || t2 == thisType || t1 == topType || t2 == topType) {
return topType;
}
if (isPrimitive(t1) != isPrimitive(t2)) {
return topType;
}
String x = ClassHierarchy.findCommonSupertype(hierarchy, patchType(t1), patchType(t2));
if ("L?;".equals(x) && ("J".equals(t1) || "J".equals(t2))) {
System.err.println(t1 + " -- " + t2);
}
return x;
}
final public BitSet getBasicBlockStarts() {
@ -474,6 +520,16 @@ public class Analyzer {
private boolean mergeTypes(int i, String[] curStack, int curStackSize, String[] curLocals, int curLocalsSize,
List<PathElement> path) throws FailureException {
boolean a = mergeStackTypes(i, curStack, curStackSize, path);
boolean b = mergeLocalTypes(i, curLocals, curLocalsSize, path);
return a||b;
}
private boolean longType(String type) {
return Constants.TYPE_long.equals(type) || Constants.TYPE_double.equals(type);
}
private boolean mergeStackTypes(int i, String[] curStack, int curStackSize, List<PathElement> path) throws FailureException {
boolean changed = false;
if (stacks[i] == null) {
@ -496,15 +552,27 @@ public class Analyzer {
}
}
return changed;
}
private boolean mergeLocalTypes(int i, String[] curLocals, int curLocalsSize, List<PathElement> path) throws FailureException {
boolean changed = false;
if (locals[i] == null) {
locals[i] = cutArray(curLocals, curLocalsSize);
changed = true;
} else {
String[] ls = locals[i];
for (int j = 0; j < ls.length; j++) {
String t = findCommonSupertype(ls[j], curLocals[j]);
if (t != ls[j]) {
ls[j] = t;
for (int lj = 0, cj = 0; lj < ls.length; lj++, cj++) {
String t = findCommonSupertype(ls[lj], curLocals[cj]);
if (t != ls[lj]) {
ls[lj] = t;
if (longType(curLocals[lj]) != longType(ls[cj])) {
System.err.println("merging " + curLocals[cj] + " and " + ls[lj] + " to " + t);
if (ls.length > lj+1) {
System.err.println("next " + curLocals[cj+1] + " and " + ls[lj+1]);
}
}
changed = true;
}
}
@ -513,6 +581,10 @@ public class Analyzer {
return changed;
}
public static String stripSharp(String type) {
return type.substring(type.lastIndexOf('#')+1);
}
/**
* A PathElement describes a point where a value is moved from one location to another.
*/
@ -532,6 +604,34 @@ public class Analyzer {
System.arraycopy(locals[i], 0, curLocals, 0, curLocalsSize[0]);
IInstruction.Visitor localsUpdate = new IInstruction.Visitor() {
@Override
public void visitInvoke(IInvokeInstruction instruction) {
if (instruction.getInvocationCode() == Dispatch.SPECIAL) {
if (instruction.getMethodName().equals("<init>")) {
int sz = Util.getParamsTypes(instruction.getClassType(), instruction.getMethodSignature()).length;
if (isConstructor) {
if (thisType.equals(curStack[ sz-1 ])) {
for(int i = 0; i < curLocals.length; i++) {
if (thisType.equals(curLocals[i])) {
curLocals[i] = classType;
}
}
for(int i = 0; i < curStack.length; i++) {
if (thisType.equals(curStack[i])) {
curStack[i] = classType;
}
}
}
}
if (curStack.length > sz && curStack[sz] != null && curStack[sz].startsWith("#")) {
curStack[sz] = stripSharp(curStack[sz]);
}
}
}
}
@Override
public void visitLocalLoad(ILoadInstruction instruction) {
String t = curLocals[instruction.getVarIndex()];
@ -541,9 +641,13 @@ public class Analyzer {
@Override
public void visitLocalStore(IStoreInstruction instruction) {
int index = instruction.getVarIndex();
curLocals[index] = curStack[0];
String t = curStack[0];
curLocals[index] = t;
if (longType(t) && curLocals.length > index+1) {
curLocals[index+1] = null;
}
if (index >= curLocalsSize[0]) {
curLocalsSize[0] = index + 1;
curLocalsSize[0] = index + (longType(t) && curLocals.length > index+1? 2: 1);
}
}
};
@ -578,6 +682,9 @@ public class Analyzer {
curStack[1] = s;
} else {
String pushed = instr.getPushedType(curStack);
if (instr instanceof NewInstruction && ! pushed.startsWith("[")) {
pushed = "#" + instToBC[i] + "#" + pushed;
}
if (pushed != null) {
System.arraycopy(curStack, popped, curStack, 1, curStackSize - popped);
curStack[0] = Util.getStackType(pushed);
@ -590,6 +697,19 @@ public class Analyzer {
}
}
ExceptionHandler[] handler = handlers[i];
for(int h = 0; h < handler.length; h++) {
int target = handler[h].getHandler();
String cls = handler[h].getCatchClass();
if (cls == null) {
cls = "Ljava/lang/Throwable;";
}
String[] catchStack = new String[]{ cls };
if (mergeTypes(target, catchStack, 1, curLocals, curLocalsSize[0], path)) {
computeTypes(target, visitor, makeTypesAt, path);
}
}
int[] targets = instr.getBranchTargets();
for (int j = 0; j < targets.length; j++) {
if (mergeTypes(targets[j], curStack, curStackSize, curLocals, curLocalsSize[0], path)) {
@ -653,8 +773,22 @@ public class Analyzer {
stacks = new String[instructions.length][];
locals = new String[instructions.length][];
String thisType;
if (isConstructor) {
thisType = Analyzer.thisType;
} else {
thisType = classType;
}
stacks[0] = noStrings;
locals[0] = Util.getParamsTypesInLocals(isStatic ? null : classType, signature);
locals[0] = Util.getParamsTypesInLocals(isStatic ? null : thisType, signature);
if (isConstructor) {
for(int i = 0; i < locals[0].length; i++) {
if (classType.equals(locals[0][i])) {
locals[0][i] = thisType;
}
}
}
int[] stackSizes = getStackSizes();
maxStack = 0;
for (int i = 0; i < stackSizes.length; i++) {
@ -696,7 +830,11 @@ public class Analyzer {
}
protected Analyzer(MethodData info) {
this(info.getIsStatic(), info.getClassType(), info.getSignature(), info.getInstructions(), info.getHandlers());
this(info.getName().equals("<init>"), info.getIsStatic(), info.getClassType(), info.getSignature(), info.getInstructions(), info.getHandlers(), info.getInstructionsToBytecodes());
}
protected Analyzer(MethodData info, int[] instToBC) {
this(info.getName().equals("<init>"), info.getIsStatic(), info.getClassType(), info.getSignature(), info.getInstructions(), info.getHandlers(), instToBC);
}
public static Analyzer createAnalyzer(MethodData info) {

View File

@ -35,7 +35,6 @@ import com.ibm.wala.shrikeBT.IShiftInstruction;
import com.ibm.wala.shrikeBT.IStoreInstruction;
import com.ibm.wala.shrikeBT.ITypeTestInstruction;
import com.ibm.wala.shrikeBT.IUnaryOpInstruction;
import com.ibm.wala.shrikeBT.InvokeDynamicInstruction;
import com.ibm.wala.shrikeBT.MethodData;
import com.ibm.wala.shrikeBT.MonitorInstruction;
import com.ibm.wala.shrikeBT.NewInstruction;
@ -288,8 +287,8 @@ public final class Verifier extends Analyzer {
/**
* Initialize a verifier.
*/
public Verifier(boolean isStatic, String classType, String signature, IInstruction[] instructions, ExceptionHandler[][] handlers) {
super(isStatic, classType, signature, instructions, handlers);
public Verifier(boolean isConstructor, boolean isStatic, String classType, String signature, IInstruction[] instructions, ExceptionHandler[][] handlers, int[] instToBC) {
super(isConstructor, isStatic, classType, signature, instructions, handlers, instToBC);
}
/**
@ -301,6 +300,10 @@ public final class Verifier extends Analyzer {
super(info);
}
public Verifier(MethodData info, int[] instToBC) throws NullPointerException {
super(info, instToBC);
}
/**
* Try to verify the method. If verification is unsuccessful, we throw an exception.
*

View File

@ -39,6 +39,9 @@ public class CTUtils {
superInterfaces[i] = CTDecoder.convertClassToType(cr.getInterfaceName(i));
}
String superName = cr.getSuperName();
if ("java/io/File".equals(cr.getName()) || "java/lang/Throwable".equals(cr.getName())) {
System.err.println(superName);
}
store.setClassInfo(CTDecoder.convertClassToType(cr.getName()), (cr.getAccessFlags() & Constants.ACC_INTERFACE) != 0, (cr
.getAccessFlags() & Constants.ACC_FINAL) != 0, superName != null ? CTDecoder.convertClassToType(superName) : null,
superInterfaces);

View File

@ -10,7 +10,9 @@
*******************************************************************************/
package com.ibm.wala.shrikeBT.shrikeCT;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import com.ibm.wala.shrikeBT.Compiler;
import com.ibm.wala.shrikeBT.ConstantPoolReader;
@ -21,6 +23,8 @@ import com.ibm.wala.shrikeBT.Instruction;
import com.ibm.wala.shrikeBT.MethodData;
import com.ibm.wala.shrikeBT.ReturnInstruction;
import com.ibm.wala.shrikeBT.Util;
import com.ibm.wala.shrikeBT.analysis.Analyzer.FailureException;
import com.ibm.wala.shrikeBT.analysis.ClassHierarchyProvider;
import com.ibm.wala.shrikeCT.ClassConstants;
import com.ibm.wala.shrikeCT.ClassReader;
import com.ibm.wala.shrikeCT.ClassWriter;
@ -31,6 +35,9 @@ import com.ibm.wala.shrikeCT.LineNumberTableReader;
import com.ibm.wala.shrikeCT.LineNumberTableWriter;
import com.ibm.wala.shrikeCT.LocalVariableTableReader;
import com.ibm.wala.shrikeCT.LocalVariableTableWriter;
import com.ibm.wala.shrikeCT.StackMapConstants.StackMapFrame;
import com.ibm.wala.shrikeCT.StackMapTableReader;
import com.ibm.wala.shrikeCT.StackMapTableWriter;
/**
* This class provides a convenient way to instrument every method in a class. It assumes you are using ShrikeCT to read and write
@ -54,11 +61,13 @@ final public class ClassInstrumenter {
private final String inputName;
private final ClassHierarchyProvider cha;
/**
* Create a class instrumenter from raw bytes.
*/
public ClassInstrumenter(String inputName, byte[] bytes) throws InvalidClassFileException {
this(inputName, new ClassReader(bytes));
public ClassInstrumenter(String inputName, byte[] bytes, ClassHierarchyProvider cha) throws InvalidClassFileException {
this(inputName, new ClassReader(bytes), cha);
}
/**
@ -82,11 +91,12 @@ final public class ClassInstrumenter {
*
* @throws IllegalArgumentException if cr is null
*/
public ClassInstrumenter(String inputName, ClassReader cr) throws InvalidClassFileException {
public ClassInstrumenter(String inputName, ClassReader cr, ClassHierarchyProvider cha) throws InvalidClassFileException {
if (cr == null) {
throw new IllegalArgumentException("cr is null");
}
this.cr = cr;
this.cha = cha;
methods = new MethodData[cr.getMethodCount()];
oldCode = new CodeReader[methods.length];
cpr = CTDecoder.makeConstantPoolReader(cr);
@ -249,7 +259,10 @@ final public class ClassInstrumenter {
* We fix up any debug information to be consistent with the changes to the code.
*/
public ClassWriter emitClass() throws InvalidClassFileException {
ClassWriter w = new ClassWriter();
return emitClass(new ClassWriter());
}
public ClassWriter emitClass(ClassWriter w) throws InvalidClassFileException {
emitClassInto(w);
return w;
}
@ -296,12 +309,12 @@ final public class ClassInstrumenter {
int flags = cr.getMethodAccessFlags(i);
// we're not installing a native method here
flags &= ~ClassConstants.ACC_NATIVE;
w.addMethod(flags, cr.getMethodNameIndex(i), cr.getMethodTypeIndex(i), makeMethodAttributes(i, w, oc, comp.getOutput()));
w.addMethod(flags, cr.getMethodNameIndex(i), cr.getMethodTypeIndex(i), makeMethodAttributes(i, w, oc, comp.getOutput(), md));
Compiler.Output[] aux = comp.getAuxiliaryMethods();
if (aux != null) {
for (int j = 0; j < aux.length; j++) {
Compiler.Output a = aux[j];
w.addMethod(a.getAccessFlags(), a.getMethodName(), a.getMethodSignature(), makeMethodAttributes(i, w, oc, a));
w.addMethod(a.getAccessFlags(), a.getMethodName(), a.getMethodSignature(), makeMethodAttributes(i, w, oc, a, md));
}
}
}
@ -389,13 +402,14 @@ final public class ClassInstrumenter {
}
}
private ClassWriter.Element[] makeMethodAttributes(int m, ClassWriter w, CodeReader oldCode, Compiler.Output output)
private ClassWriter.Element[] makeMethodAttributes(int m, ClassWriter w, CodeReader oldCode, Compiler.Output output, MethodData md)
throws InvalidClassFileException {
CodeWriter code = makeNewCode(w, output);
int codeAttrCount = 0;
LineNumberTableWriter lines = null;
LocalVariableTableWriter locals = null;
StackMapTableWriter stacks = null;
if (oldCode != null) {
lines = makeNewLines(w, oldCode, output);
if (lines != null) {
@ -405,15 +419,32 @@ final public class ClassInstrumenter {
if (locals != null) {
codeAttrCount++;
}
if (oldCode.getClassReader().getMajorVersion() > 50) {
try { /*
List<StackMapFrame> sm = StackMapTableReader.readStackMap(oldCode);
if (sm != null) {
stacks = new StackMapTableWriter(w, sm, output.getNewBytecodesToOldBytecodes());
codeAttrCount++;
} else { */
stacks = new StackMapTableWriter(w, md, output, cha);
codeAttrCount++;
// }
} catch (IOException | FailureException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
ClassWriter.Element[] codeAttributes = new ClassWriter.Element[codeAttrCount];
int codeAttrIndex = 0;
if (lines != null) {
codeAttributes[0] = lines;
codeAttrIndex++;
codeAttributes[codeAttrIndex++] = lines;
}
if (locals != null) {
codeAttributes[codeAttrIndex] = locals;
codeAttributes[codeAttrIndex++] = locals;
}
if (stacks != null) {
codeAttributes[codeAttrIndex++] = stacks;
}
code.setAttributes(codeAttributes);

View File

@ -15,6 +15,7 @@ import java.io.IOException;
import java.io.OutputStream;
import com.ibm.wala.shrikeBT.Util;
import com.ibm.wala.shrikeBT.analysis.ClassHierarchyProvider;
import com.ibm.wala.shrikeBT.tools.OfflineInstrumenterBase;
import com.ibm.wala.shrikeCT.ClassWriter;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
@ -28,6 +29,7 @@ final public class OfflineInstrumenter extends OfflineInstrumenterBase {
* Create an empty collection of classes to instrument.
*/
public OfflineInstrumenter() {
}
@Override
@ -35,7 +37,7 @@ final public class OfflineInstrumenter extends OfflineInstrumenterBase {
byte[] bytes = new byte[s.available()];
Util.readFully(s, bytes);
try {
return new ClassInstrumenter(inputName, bytes);
return new ClassInstrumenter(inputName, bytes, cha);
} catch (InvalidClassFileException e) {
throw new IOException("Class is invalid: " + e.getMessage());
}

View File

@ -72,7 +72,7 @@ public class BootstrapDumper {
}
private void dumpAttributes(Class cl, ClassReader cr, int i, ClassReader.AttrIterator attrs) throws InvalidClassFileException,
InvalidBytecodeException, IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
InvalidBytecodeException, IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException {
for (; attrs.isValid(); attrs.advance()) {
String name = attrs.getName();
if (name.equals("Code")) {
@ -111,8 +111,9 @@ public class BootstrapDumper {
* @throws ClassNotFoundException
*
* @throws IllegalArgumentException if cr is null
* @throws NoSuchFieldException
*/
public void doClass(ClassLoader image, final ClassReader cr) throws InvalidClassFileException, InvalidBytecodeException, IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
public void doClass(ClassLoader image, final ClassReader cr) throws InvalidClassFileException, InvalidBytecodeException, IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException {
if (cr == null) {
throw new IllegalArgumentException("cr is null");
}

View File

@ -28,6 +28,8 @@ import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;
import com.ibm.wala.shrikeBT.analysis.ClassHierarchyProvider;
/**
* This class provides functionality for performing offline instrumentation. It is subclassed with class-toolkit-specific
* functionality.
@ -53,6 +55,8 @@ public abstract class OfflineInstrumenterBase {
private ManifestBuilder manifestBuilder;
protected ClassHierarchyProvider cha;
/**
* This installs a ManifestBuilder callback that this class will notify whenever an entry has been added to the output zip file.
*/
@ -200,8 +204,13 @@ public abstract class OfflineInstrumenterBase {
}
protected OfflineInstrumenterBase() {
}
public void setClassHierarchyProvider(ClassHierarchyProvider cha) {
this.cha = cha;
}
/**
* Set the file in which instrumented classes will be deposited.
*/

View File

@ -1,7 +1,11 @@
package com.ibm.wala.shrikeCT;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import com.ibm.wala.shrikeCT.ClassReader.AttrIterator;
import com.ibm.wala.shrikeCT.ConstantPoolParser.ReferenceToken;
public class BootstrapMethodsReader extends AttributeReader {
@ -10,7 +14,7 @@ public class BootstrapMethodsReader extends AttributeReader {
String methodClass();
String methodName();
String methodType();
Object callArgument(int i);
Object callArgument(ClassLoader cl, int i);
int callArgumentKind(int i);
}
@ -70,7 +74,7 @@ public class BootstrapMethodsReader extends AttributeReader {
}
@Override
public Object callArgument(int i) {
public Object callArgument(ClassLoader cl, int i) {
try {
int index = cr.getUShort(argsBase + (2*i));
int t = callArgumentKind(i);
@ -91,11 +95,16 @@ public class BootstrapMethodsReader extends AttributeReader {
return cp.getCPLong(index);
case ClassConstants.CONSTANT_MethodHandle:
String className = cp.getCPHandleClass(index);
Class<?> cls = Class.forName(className, false, cl);
String eltName = cp.getCPHandleName(index);
String eltDesc = cp.getCPHandleType(index);
return new ReferenceToken(className, eltName, eltDesc);
MethodType type = MethodType.fromMethodDescriptorString(eltDesc, cl);
Method m = cls.getDeclaredMethod(eltName, type.parameterList().toArray(new Class[type.parameterCount()]));
Lookup lk = MethodHandles.lookup().in(cls);
m.setAccessible(true);
return lk.unreflect(m);
case ClassConstants.CONSTANT_MethodType:
return cp.getCPMethodType(index);
return MethodType.fromMethodDescriptorString(cp.getCPMethodType(index), this.getClass().getClassLoader());
default:
assert false : "invalid type " + t;
}
@ -103,6 +112,14 @@ public class BootstrapMethodsReader extends AttributeReader {
assert false : e;
} catch (InvalidClassFileException e) {
assert false : e;
} catch (ClassNotFoundException e) {
assert false : e;
} catch (NoSuchMethodException e) {
assert false : e;
} catch (SecurityException e) {
assert false : e;
} catch (IllegalAccessException e) {
assert false : e;
}
return null;
}

View File

@ -64,7 +64,7 @@ public final class ClassReader implements ClassConstants {
if (magic != MAGIC) {
throw new InvalidClassFileException(offset, "bad magic number: " + magic);
}
if (majorVersion < 45 || majorVersion > 51) {
if (majorVersion < 45 || majorVersion > 52) {
throw new InvalidClassFileException(offset, "unknown class file version: " + majorVersion + "." + minorVersion);
}

View File

@ -16,7 +16,7 @@ import java.util.HashMap;
/**
* This class formats and writes class data into JVM format.
*/
public final class ClassWriter implements ClassConstants {
public class ClassWriter implements ClassConstants {
// input
private int majorVersion = 46;
@ -82,10 +82,10 @@ public final class ClassWriter implements ClassConstants {
abstract byte getType();
}
static class CWString extends CWItem {
public static class CWString extends CWItem {
final private String s;
CWString(String s) {
public CWString(String s) {
this.s = s;
}
@ -105,10 +105,10 @@ public final class ClassWriter implements ClassConstants {
}
}
static class CWClass extends CWItem {
public static class CWClass extends CWItem {
final private String c;
CWClass(String c) {
public CWClass(String c) {
this.c = c;
}
@ -265,7 +265,7 @@ public final class ClassWriter implements ClassConstants {
forceAddCPEntries = force;
}
private int addCPEntry(Object o, int size) {
protected int addCPEntry(Object o, int size) {
if (cachedCPEntries == null) {
throw new IllegalArgumentException("Cannot add a new constant pool entry during makeBytes() processing!");
}

View File

@ -95,7 +95,13 @@ public final class ConstantPoolParser implements ClassConstants {
ClassReader thisClass = new ClassReader(bytes);
AttrIterator attrs = new AttrIterator();
thisClass.initClassAttributeIterator(attrs);
invokeDynamicBootstraps = new BootstrapMethodsReader(attrs);
for (; attrs.isValid(); attrs.advance()) {
if (attrs.getName().equals("BootstrapMethods")) {
invokeDynamicBootstraps = new BootstrapMethodsReader(attrs);
break;
}
}
assert invokeDynamicBootstraps != null;
}
return invokeDynamicBootstraps;

View File

@ -0,0 +1,225 @@
package com.ibm.wala.shrikeCT;
import static com.ibm.wala.shrikeCT.StackMapTableWriter.writeUByte;
import static com.ibm.wala.shrikeCT.StackMapTableWriter.writeUShort;
import java.io.IOException;
import java.io.OutputStream;
import com.ibm.wala.shrikeBT.analysis.Analyzer;
public class StackMapConstants {
interface StackMapType {
void write(OutputStream s, ClassWriter writer) throws IOException;
int size();
}
public enum Item implements StackMapType {
ITEM_Top(0),
ITEM_Integer(1),
ITEM_Float(2),
ITEM_Double(3) {
@Override
public int size() {
return 2;
}
},
ITEM_Long(4) {
@Override
public int size() {
return 2;
}
},
ITEM_Null(5),
ITEM_UninitializedThis(6),
ITEM_Object(7) {
@Override
public boolean isObject() {
return true;
}
},
ITEM_Uninitalized(8) {
@Override
public boolean isObject() {
return true;
}
};
private final byte code;
Item(int code) {
this.code = (byte)code;
}
public boolean isObject() {
return false;
}
@Override
public int size() {
return 1;
}
@Override
public void write(OutputStream s, ClassWriter writer) throws IOException {
writeUByte(s, code);
}
}
public static class UninitializedType implements StackMapType {
private final String type;
private final int offset;
public UninitializedType(String type) {
assert type.startsWith("#");
this.type = Analyzer.stripSharp(type);
this.offset = Integer.parseInt(type.substring(1, type.lastIndexOf('#')));
}
@Override
public void write(OutputStream s, ClassWriter writer) throws IOException {
Item.ITEM_Uninitalized.write(s, writer);
writeUShort(s, offset);
}
@Override
public int size() {
return Item.ITEM_Uninitalized.size();
}
@Override
public String toString() {
return "uninit:" + type;
}
}
public static class ObjectType implements StackMapType {
private final String type;
ObjectType(ClassReader cr, int typeIndex) throws IllegalArgumentException, InvalidClassFileException {
this(cr.getCP().getCPString(typeIndex));
}
ObjectType(String type) {
this.type = type;
}
@Override
public int size() {
return Item.ITEM_Object.size();
}
@Override
public String toString() {
return "obj:" + type;
}
@Override
public void write(OutputStream s, ClassWriter writer) throws IOException {
Item.ITEM_Object.write(s, writer);
if ("L;".equals(type)) {
writeUShort(s, writer.addCPClass("java/lang/Object"));
} else if (type.startsWith("L")) {
writeUShort(s, writer.addCPClass(type.substring(1, type.length()-1)));
} else {
writeUShort(s, writer.addCPClass(type));
}
}
}
public static Item items[] = {
Item.ITEM_Top,
Item.ITEM_Integer,
Item.ITEM_Float,
Item.ITEM_Double,
Item.ITEM_Long,
Item.ITEM_Null,
Item.ITEM_UninitializedThis,
Item.ITEM_Object,
Item.ITEM_Uninitalized
};
public static class StackMapFrame {
public StackMapFrame(StackMapFrame frame, int newOffset) {
this(frame.frameType, newOffset, frame.localTypes, frame.stackTypes);
}
public StackMapFrame(int frameType, int offset, StackMapType[] localTypes, StackMapType[] stackTypes) {
this.frameType = frameType;
this.offset = offset;
this.localTypes = localTypes;
this.stackTypes = stackTypes;
}
private final int frameType;
private final int offset;
private final StackMapType[] localTypes;
private final StackMapType[] stackTypes;
public int getFrameType() {
return frameType;
}
public int getOffset() {
return offset;
}
public StackMapType[] getLocalTypes() {
return localTypes;
}
public StackMapType[] getStackTypes() {
return stackTypes;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("frame type: ").append(frameType).append("\n");
sb.append(" offset: ").append(offset).append("\n");
sb.append(" locals\n");
for(int i = 0; i < localTypes.length; i++) {
sb.append(" ").append(localTypes[i]).append("\n");
}
sb.append(" stack\n");
for(int i = 0; i < stackTypes.length; i++) {
sb.append(" ").append(stackTypes[i]).append("\n");
}
return sb.toString();
}
public void write(OutputStream out, ClassWriter writer) throws IOException {
// frame type
writeUByte(out, frameType);
// offset delta
writeUShort(out, offset);
// locals
if (localTypes != null) {
writeUShort(out, localTypes.length);
for(StackMapType type : localTypes) {
type.write(out, writer);
}
} else {
writeUShort(out, 0);
}
// stack
if (stackTypes != null) {
writeUShort(out, stackTypes.length);
for(int j = stackTypes.length; j > 0; ) {
stackTypes[--j].write(out, writer);
}
} else {
writeUShort(out, 0);
}
}
}
}

View File

@ -0,0 +1,96 @@
package com.ibm.wala.shrikeCT;
import java.util.ArrayList;
import java.util.List;
import com.ibm.wala.shrikeCT.ClassReader.AttrIterator;
import com.ibm.wala.shrikeCT.StackMapConstants.Item;
import com.ibm.wala.shrikeCT.StackMapConstants.ObjectType;
import com.ibm.wala.shrikeCT.StackMapConstants.StackMapFrame;
import com.ibm.wala.shrikeCT.StackMapConstants.StackMapType;
public class StackMapTableReader extends AttributeReader {
private List<StackMapFrame> frames;
public List<StackMapFrame> frames() {
return frames;
}
private StackMapType item(int offset) throws InvalidClassFileException {
Item item = StackMapConstants.items[cr.getByte(offset)];
if (item.isObject()) {
return new ObjectType(cr.getCP().getCPClass(cr.getUShort(offset+1)));
} else {
return item;
}
}
public StackMapTableReader(AttrIterator iter) throws InvalidClassFileException {
super(iter, "StackMapTable");
frames = new ArrayList<StackMapFrame>();
int entries = cr.getUShort(attr+6);
int ptr = attr + 8;
for(int i = 0; i < entries; i++) {
int frameType = (0x000000ff & cr.getByte(ptr++));
if (frameType < 63) {
int offset = frameType;
frames.add(new StackMapFrame(frameType, offset, new StackMapType[0], new StackMapType[0]));
} else if (frameType < 128) {
int offset = frameType - 64;
StackMapType stack1 = item(ptr);
ptr += (stack1 instanceof ObjectType)? 3: 1;
frames.add(new StackMapFrame(frameType, offset, new StackMapType[0], new StackMapType[]{ stack1 }));
} else if (frameType == 247) {
int offset = cr.getUShort(ptr); ptr += 2;
StackMapType stack1 = item(ptr);
ptr += (stack1 instanceof ObjectType)? 3: 1;
frames.add(new StackMapFrame(frameType, offset, new StackMapType[0], new StackMapType[]{ stack1 }));
} else if (frameType >= 248 && frameType <= 250) {
int offset = cr.getUShort(ptr); ptr += 2;
frames.add(new StackMapFrame(frameType, offset, new StackMapType[0], new StackMapType[0]));
} else if (frameType == 251) {
int offset = cr.getUShort(ptr); ptr += 2;
frames.add(new StackMapFrame(frameType, offset, new StackMapType[0], new StackMapType[0]));
} else if (frameType >= 252 && frameType <= 254) {
StackMapType[] locals = new StackMapType[ frameType - 251 ];
int offset = cr.getUShort(ptr); ptr += 2;
for(int j = 0; j < locals.length; j++) {
locals[j] = item(ptr);
ptr += (locals[j] instanceof ObjectType)? 3: 1;
}
frames.add(new StackMapFrame(frameType, offset, locals, new StackMapType[0]));
} else if (frameType == 255) {
int offset = cr.getUShort(ptr); ptr += 2;
int numLocals = cr.getUShort(ptr); ptr += 2;
StackMapType[] locals = new StackMapType[ numLocals ];
for(int j = 0; j < numLocals; j++) {
locals[j] = item(ptr);
ptr += (locals[j] instanceof ObjectType)? 3: 1;
}
int numStack = cr.getUShort(ptr); ptr += 2;
StackMapType[] stack = new StackMapType[ numStack ];
for(int j = 0; j < numStack; j++) {
stack[j] = item(ptr);
ptr += (stack[j] instanceof ObjectType)? 3: 1;
}
frames.add(new StackMapFrame(frameType, offset, locals, stack));
}
}
}
public static List<StackMapFrame> readStackMap(CodeReader code) throws InvalidClassFileException, IllegalArgumentException {
ClassReader.AttrIterator iter = new ClassReader.AttrIterator();
code.initAttributeIterator(iter);
for (; iter.isValid(); iter.advance()) {
if (iter.getName().equals("StackMapTable")) {
StackMapTableReader r = new StackMapTableReader(iter);
return r.frames();
}
}
return null;
}
}

View File

@ -0,0 +1,250 @@
package com.ibm.wala.shrikeCT;
import static com.ibm.wala.shrikeBT.Constants.TYPE_double;
import static com.ibm.wala.shrikeBT.Constants.TYPE_float;
import static com.ibm.wala.shrikeBT.Constants.TYPE_int;
import static com.ibm.wala.shrikeBT.Constants.TYPE_long;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.ibm.wala.shrikeBT.Compiler.Output;
import com.ibm.wala.shrikeBT.GotoInstruction;
import com.ibm.wala.shrikeBT.IInstruction;
import com.ibm.wala.shrikeBT.MethodData;
import com.ibm.wala.shrikeBT.analysis.Analyzer;
import com.ibm.wala.shrikeBT.analysis.Analyzer.FailureException;
import com.ibm.wala.shrikeBT.analysis.ClassHierarchyProvider;
import com.ibm.wala.shrikeBT.analysis.Verifier;
import com.ibm.wala.shrikeCT.ClassWriter.Element;
import com.ibm.wala.shrikeCT.StackMapConstants.Item;
import com.ibm.wala.shrikeCT.StackMapConstants.ObjectType;
import com.ibm.wala.shrikeCT.StackMapConstants.StackMapFrame;
import com.ibm.wala.shrikeCT.StackMapConstants.StackMapType;
import com.ibm.wala.shrikeCT.StackMapConstants.UninitializedType;
import com.ibm.wala.util.collections.HashMapFactory;
public class StackMapTableWriter extends Element {
private final byte[] data;
public StackMapTableWriter(ClassWriter writer, List<StackMapFrame> frames) throws IOException {
this.data = serialize(writer, frames);
}
private byte[] serialize(ClassWriter writer, List<StackMapFrame> frames) throws IOException {
ByteArrayOutputStream data = new ByteArrayOutputStream();
for(StackMapFrame frame : frames) {
frame.write(data, writer);
}
ByteArrayOutputStream bs = new ByteArrayOutputStream();
writeUShort(bs, writer.addCPUtf8("StackMapTable"));
writeInt(bs, data.size() + 2);
writeUShort(bs, frames.size());
data.writeTo(bs);
return bs.toByteArray();
}
public StackMapTableWriter(ClassWriter writer, MethodData method, Output output, ClassHierarchyProvider cha) throws FailureException, IOException {
this(writer, stackMapTable(writer, method, output, cha));
}
private static List<StackMapFrame> remapStackFrames(List<StackMapFrame> sm, int[] newBytecodesToOldBytecodes) {
// mapping to new bytecode
Map<Integer,Integer> oldToNew = HashMapFactory.make();
for(int i = newBytecodesToOldBytecodes.length - 1; i >= 0; i--) {
oldToNew.put(newBytecodesToOldBytecodes[i], i);
}
// positions of frames
int i = 1;
int positions[] = new int[ sm.size()];
Iterator<StackMapFrame> sms = sm.iterator();
int position = sms.next().getOffset();
positions[0] = oldToNew.get(position);
while (sms.hasNext()) {
position = position + sms.next().getOffset() + 1;
positions[i++] = oldToNew.get(position);
}
// positions turned into offsets
for(i = positions.length-1; i > 0; i--) {
positions[i] = positions[i] - positions[i-1] - 1;
}
// frames with new offsets
List<StackMapFrame> newFrames = new ArrayList<StackMapFrame>(sm.size());
for(i = 0; i < sm.size(); i++) {
newFrames.add(new StackMapFrame(sm.get(i), positions[i]));
}
return newFrames;
}
public StackMapTableWriter(ClassWriter w, List<StackMapFrame> sm, int[] newBytecodesToOldBytecodes) throws IOException {
this(w, remapStackFrames(sm, newBytecodesToOldBytecodes));
}
static StackMapType item(String type) {
if (type == null) {
return Item.ITEM_Top;
} else if (type.equals(Analyzer.topType)) {
return Item.ITEM_Top;
} else if (type.equals(Analyzer.thisType)) {
return Item.ITEM_UninitializedThis;
} else if (type.equals(TYPE_int)) {
return Item.ITEM_Integer;
} else if (type.equals(TYPE_float)) {
return Item.ITEM_Float;
} else if (type.equals(TYPE_double)) {
return Item.ITEM_Double;
} else if (type.equals(TYPE_long)) {
return Item.ITEM_Long;
} else {
if (type.startsWith("#")) {
return new UninitializedType(type);
} else {
return new ObjectType(type);
}
}
}
static void writeUByte(OutputStream s, int v) throws IOException {
byte bytes[] = new byte[1];
ClassWriter.setUByte(bytes, 0, v);
s.write(bytes);
}
static void writeUShort(OutputStream s, int v) throws IOException {
byte bytes[] = new byte[2];
ClassWriter.setUShort(bytes, 0, v);
s.write(bytes);
}
static void writeInt(OutputStream s, int v) throws IOException {
byte bytes[] = new byte[4];
ClassWriter.setInt(bytes, 0, v);
s.write(bytes);
}
static StackMapType[] trim(StackMapType[] types) {
int i = types.length-1;
while (i >= 0 && (types[i] == null || types[i] == Item.ITEM_Null)) {
i--;
}
if (i < 0) {
return new StackMapType[0];
} else if (i < types.length-1) {
StackMapType[] trimmed = new StackMapType[ i+1 ];
System.arraycopy(types, 0, trimmed, 0, i+1);
return trimmed;
} else {
return types;
}
}
static StackMapType[] types(String[] types, boolean locals) {
StackMapType[] stackTypes = new StackMapType[ types.length ];
int x = 0;
for(int j = 0; j < types.length; j++) {
StackMapType stackType = item("L?;".equals(types[j])? "Ljava/lang/Object;": types[j]);
stackTypes[x++] = stackType;
if (locals && stackType.size() == 2) {
j++;
}
}
return trim(stackTypes);
}
private static boolean isUselessGoto(IInstruction inst, int index) {
if (inst instanceof GotoInstruction) {
if (((GotoInstruction)inst).getBranchTargets()[0] == index+1) {
return true;
}
}
return false;
}
public static List<StackMapFrame> stackMapTable(ClassWriter writer, MethodData method, Output output, ClassHierarchyProvider cha) throws FailureException, IOException {
List<StackMapFrame> frames = new ArrayList<StackMapFrame>();
int[] instructionToBytecode = output.getInstructionOffsets();
IInstruction[] insts = method.getInstructions();
Verifier typeChecker = new Verifier(method, instructionToBytecode);
if (cha != null) {
typeChecker.setClassHierarchy(cha);
}
typeChecker.verifyCollectAll();
BitSet bbs = typeChecker.getBasicBlockStarts();
int offset = 0;
for(int i = 1; i < insts.length; i++) {
if (bbs.get(i)) {
// Shrike does not generate goto i+1
if (isUselessGoto(insts[i], i)) {
continue;
}
// full frame
byte frameType = (byte)255;
// offset delta
int position = instructionToBytecode[i];
assert position - offset > 0 || offset == 0;
int frameOffset = offset==0? position: position - offset - 1;
offset = position;
// locals
String[] localTypes = typeChecker.getLocalTypes()[i];
StackMapType[] localWriteTypes;
if (localTypes != null) {
localWriteTypes = types(localTypes, true);
} else {
localWriteTypes = new StackMapType[0];
}
// stack
String[] stackTypes = typeChecker.getStackTypes()[i];
StackMapType[] stackWriteTypes;
if (stackTypes != null) {
stackWriteTypes = types(stackTypes, false);
} else {
stackWriteTypes = new StackMapType[0];
}
frames.add(new StackMapFrame(frameType, frameOffset, localWriteTypes, stackWriteTypes));
}
}
return frames;
}
@Override
public int getSize() {
return data.length;
}
@Override
public int copyInto(byte[] buf, int offset) {
for(int i = 0; i < data.length; i++) {
buf[offset+i] = data[i];
}
return data.length+offset;
}
}