java 7 support, with much pain for stack maps :)
This commit is contained in:
parent
c006dbcdd4
commit
36709b9d1a
@ -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>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -22,7 +22,8 @@
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.ibm.wala.cast.js.test.data/examples-src" path="3" type="2"/> "/>
|
||||
</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>
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
eclipse.preferences.version=1
|
||||
encoding//harness-src/com/ibm/wala/cast/js/test/TestCorrelatedPairExtraction.java=UTF-8
|
||||
@ -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>
|
||||
|
||||
@ -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) {}
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ package dynamicCG;
|
||||
|
||||
public class MainClass {
|
||||
private final Object x;
|
||||
|
||||
|
||||
private MainClass(Object x) {
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
41
com.ibm.wala.core.testdata/src/shrike/StackMaps.java
Normal file
41
com.ibm.wala.core.testdata/src/shrike/StackMaps.java
Normal 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;
|
||||
}
|
||||
}
|
||||
2
com.ibm.wala.core.tests/dat/base.txt
Normal file
2
com.ibm.wala.core.tests/dat/base.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Primordial,Java,stdlib,none
|
||||
Primordial,Java,jarFile,primordial.jar.model
|
||||
3
com.ibm.wala.core.tests/dat/ocaml_compr.txt
Normal file
3
com.ibm.wala.core.tests/dat/ocaml_compr.txt
Normal file
@ -0,0 +1,3 @@
|
||||
Primordial,Java,stdlib,none
|
||||
Primordial,Java,jarFile,primordial.jar.model
|
||||
Application,Java,jarFile,compr
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
source.. = source/
|
||||
source.. = source/,\
|
||||
data/
|
||||
output.. = bin/
|
||||
bin.includes = META-INF/,\
|
||||
.
|
||||
|
||||
@ -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"/>
|
||||
|
||||
@ -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"/>
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
@ -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.
|
||||
*
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
@ -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.
|
||||
*/
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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!");
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user