@@ -4,6 +4,6 @@ | |||
<classpathentry kind="src" path="testsrc"/> | |||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> | |||
<classpathentry kind="lib" path="/lib/regexp/jakarta-regexp-1.2.jar"/> | |||
<classpathentry sourcepath="/lib/junit/junit-src.jar" kind="lib" path="/lib/junit/junit.jar"/> | |||
<classpathentry kind="lib" path="/lib/junit/junit.jar" sourcepath="/lib/junit/junit-src.jar"/> | |||
<classpathentry kind="output" path="bin"/> | |||
</classpath> |
@@ -0,0 +1,105 @@ | |||
#FindBugs User Preferences | |||
#Fri Feb 09 09:32:57 GMT 2007 | |||
default_directory=C\:\\e33m4\\eclipse | |||
detectorBadAppletConstructor=BadAppletConstructor|false | |||
detectorBadResultSetAccess=BadResultSetAccess|true | |||
detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true | |||
detectorBadUseOfReturnValue=BadUseOfReturnValue|true | |||
detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true | |||
detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true | |||
detectorCloneIdiom=CloneIdiom|true | |||
detectorComparatorIdiom=ComparatorIdiom|true | |||
detectorConfusedInheritance=ConfusedInheritance|true | |||
detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true | |||
detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true | |||
detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true | |||
detectorDroppedException=DroppedException|true | |||
detectorDumbMethodInvocations=DumbMethodInvocations|true | |||
detectorDumbMethods=DumbMethods|true | |||
detectorDuplicateBranches=DuplicateBranches|true | |||
detectorEmptyZipFileEntry=EmptyZipFileEntry|true | |||
detectorFindBadCast2=FindBadCast2|true | |||
detectorFindBadForLoop=FindBadForLoop|true | |||
detectorFindCircularDependencies=FindCircularDependencies|false | |||
detectorFindDeadLocalStores=FindDeadLocalStores|true | |||
detectorFindDoubleCheck=FindDoubleCheck|true | |||
detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true | |||
detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true | |||
detectorFindFinalizeInvocations=FindFinalizeInvocations|true | |||
detectorFindFloatEquality=FindFloatEquality|true | |||
detectorFindHEmismatch=FindHEmismatch|true | |||
detectorFindInconsistentSync2=FindInconsistentSync2|true | |||
detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true | |||
detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true | |||
detectorFindMaskedFields=FindMaskedFields|true | |||
detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true | |||
detectorFindNakedNotify=FindNakedNotify|true | |||
detectorFindNonSerializableStoreIntoSession=FindNonSerializableStoreIntoSession|true | |||
detectorFindNonSerializableValuePassedToWriteObject=FindNonSerializableValuePassedToWriteObject|true | |||
detectorFindNonShortCircuit=FindNonShortCircuit|true | |||
detectorFindNullDeref=FindNullDeref|true | |||
detectorFindOpenStream=FindOpenStream|true | |||
detectorFindPuzzlers=FindPuzzlers|true | |||
detectorFindRefComparison=FindRefComparison|true | |||
detectorFindReturnRef=FindReturnRef|true | |||
detectorFindRunInvocations=FindRunInvocations|true | |||
detectorFindSelfComparison=FindSelfComparison|true | |||
detectorFindSelfComparison2=FindSelfComparison2|true | |||
detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true | |||
detectorFindSpinLoop=FindSpinLoop|true | |||
detectorFindSqlInjection=FindSqlInjection|true | |||
detectorFindTwoLockWait=FindTwoLockWait|true | |||
detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true | |||
detectorFindUnconditionalWait=FindUnconditionalWait|true | |||
detectorFindUninitializedGet=FindUninitializedGet|true | |||
detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true | |||
detectorFindUnreleasedLock=FindUnreleasedLock|true | |||
detectorFindUnsyncGet=FindUnsyncGet|true | |||
detectorFindUselessControlFlow=FindUselessControlFlow|true | |||
detectorHugeSharedStringConstants=HugeSharedStringConstants|true | |||
detectorIDivResultCastToDouble=IDivResultCastToDouble|true | |||
detectorIncompatMask=IncompatMask|true | |||
detectorInefficientMemberAccess=InefficientMemberAccess|false | |||
detectorInefficientToArray=InefficientToArray|true | |||
detectorInfiniteLoop=InfiniteLoop|true | |||
detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true | |||
detectorInfiniteRecursiveLoop2=InfiniteRecursiveLoop2|false | |||
detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true | |||
detectorInitializationChain=InitializationChain|true | |||
detectorInstantiateStaticClass=InstantiateStaticClass|true | |||
detectorInvalidJUnitTest=InvalidJUnitTest|true | |||
detectorIteratorIdioms=IteratorIdioms|true | |||
detectorLazyInit=LazyInit|true | |||
detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true | |||
detectorMethodReturnCheck=MethodReturnCheck|true | |||
detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true | |||
detectorMutableLock=MutableLock|true | |||
detectorMutableStaticFields=MutableStaticFields|true | |||
detectorNaming=Naming|true | |||
detectorNumberConstructor=NumberConstructor|true | |||
detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true | |||
detectorPublicSemaphores=PublicSemaphores|false | |||
detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true | |||
detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true | |||
detectorRedundantInterfaces=RedundantInterfaces|true | |||
detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true | |||
detectorSerializableIdiom=SerializableIdiom|true | |||
detectorStartInConstructor=StartInConstructor|true | |||
detectorStringConcatenation=StringConcatenation|true | |||
detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true | |||
detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true | |||
detectorSwitchFallthrough=SwitchFallthrough|true | |||
detectorURLProblems=URLProblems|true | |||
detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true | |||
detectorUnnecessaryMath=UnnecessaryMath|true | |||
detectorUnreadFields=UnreadFields|true | |||
detectorUseObjectEquals=UseObjectEquals|false | |||
detectorUselessSubclassMethod=UselessSubclassMethod|false | |||
detectorVarArgsProblems=VarArgsProblems|true | |||
detectorVolatileUsage=VolatileUsage|true | |||
detectorWaitInLoop=WaitInLoop|true | |||
detectorWrongMapIterator=WrongMapIterator|true | |||
detectorXMLFactoryBypass=XMLFactoryBypass|true | |||
detector_threshold=2 | |||
filter_settings=Medium|PERFORMANCE,CORRECTNESS,I18N,MT_CORRECTNESS,BAD_PRACTICE,MALICIOUS_CODE,STYLE|false | |||
filter_settings_neg=| |
@@ -1,6 +1,6 @@ | |||
#Fri Nov 25 19:12:57 GMT 2005 | |||
#Wed Dec 06 15:02:55 GMT 2006 | |||
eclipse.preferences.version=1 | |||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=disabled | |||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled | |||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.1 | |||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve | |||
org.eclipse.jdt.core.compiler.compliance=1.3 | |||
@@ -10,7 +10,7 @@ org.eclipse.jdt.core.compiler.debug.sourceFile=generate | |||
org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning | |||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=ignore | |||
org.eclipse.jdt.core.compiler.problem.autoboxing=ignore | |||
org.eclipse.jdt.core.compiler.problem.deprecation=warning | |||
org.eclipse.jdt.core.compiler.problem.deprecation=ignore | |||
org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled | |||
org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled | |||
org.eclipse.jdt.core.compiler.problem.discouragedReference=warning | |||
@@ -46,12 +46,13 @@ org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore | |||
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore | |||
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore | |||
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore | |||
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled | |||
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled | |||
org.eclipse.jdt.core.compiler.problem.unusedImport=warning | |||
org.eclipse.jdt.core.compiler.problem.unusedLocal=ignore | |||
org.eclipse.jdt.core.compiler.problem.unusedLabel=warning | |||
org.eclipse.jdt.core.compiler.problem.unusedLocal=warning | |||
org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore | |||
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled | |||
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled | |||
org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=ignore | |||
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=enabled | |||
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=enabled | |||
org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning | |||
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning | |||
org.eclipse.jdt.core.compiler.source=1.3 |
@@ -57,7 +57,7 @@ package org.aspectj.apache.bcel; | |||
/** | |||
* Constants for the project, mostly defined in the JVM specification. | |||
* | |||
* @version $Id: Constants.java,v 1.3 2006/05/04 11:28:26 aclement Exp $ | |||
* @version $Id: Constants.java,v 1.3.4.1 2007/02/09 10:45:09 aclement Exp $ | |||
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> | |||
*/ | |||
public interface Constants { | |||
@@ -431,6 +431,7 @@ public interface Constants { | |||
public static final byte T_REFERENCE = 14; // Deprecated | |||
public static final byte T_UNKNOWN = 15; | |||
public static final byte T_ADDRESS = 16; | |||
public static final byte T_TOP = 17; | |||
/** The primitive type names corresponding to the T_XX constants, | |||
* e.g., TYPE_NAMES[T_INT] = "int" | |||
@@ -439,6 +440,7 @@ public interface Constants { | |||
ILLEGAL_TYPE, ILLEGAL_TYPE, ILLEGAL_TYPE, ILLEGAL_TYPE, | |||
"boolean", "char", "float", "double", "byte", "short", "int", "long", | |||
"void", "array", "object", "unknown" // Non-standard | |||
,"address","top" | |||
}; | |||
/** The primitive class names corresponding to the T_XX constants, | |||
@@ -766,8 +768,8 @@ public interface Constants { | |||
public static final byte ATTR_SYNTHETIC = 7; | |||
public static final byte ATTR_DEPRECATED = 8; | |||
public static final byte ATTR_PMG = 9; | |||
public static final byte ATTR_SIGNATURE = 10; //J5TODO: Is this the same as a Java5 signature attribute? | |||
public static final byte ATTR_STACK_MAP = 11; | |||
public static final byte ATTR_SIGNATURE = 10; | |||
public static final byte ATTR_STACK_MAP_TABLE = 11; | |||
// J5SUPPORT: | |||
public static final byte ATTR_RUNTIME_VISIBLE_ANNOTATIONS = 12; | |||
@@ -784,27 +786,28 @@ public interface Constants { | |||
"SourceFile", "ConstantValue", "Code", "Exceptions", | |||
"LineNumberTable", "LocalVariableTable", | |||
"InnerClasses", "Synthetic", "Deprecated", | |||
"PMGClass", "Signature", "StackMap", | |||
"PMGClass", "Signature", "StackMapTable", | |||
// J5SUPPORT: | |||
"RuntimeVisibleAnnotations","RuntimeInvisibleAnnotations", | |||
"RuntimeVisibleParameterAnnotations","RuntimeInvisibleParameterAnnotations", | |||
"LocalVariableTypeTable","EnclosingMethod","AnnotationDefault" | |||
}; | |||
/** Constants used in the StackMap attribute. | |||
/** | |||
* Constants used in the StackMap attribute. | |||
*/ | |||
public static final byte ITEM_Bogus = 0; | |||
public static final byte ITEM_Top = 0; | |||
public static final byte ITEM_Integer = 1; | |||
public static final byte ITEM_Float = 2; | |||
public static final byte ITEM_Double = 3; | |||
public static final byte ITEM_Long = 4; | |||
public static final byte ITEM_Null = 5; | |||
public static final byte ITEM_InitObject = 6; | |||
public static final byte ITEM_Object = 7; | |||
public static final byte ITEM_NewObject = 8; | |||
public static final byte ITEM_Null = 5; | |||
public static final byte ITEM_UninitializedThis = 6; | |||
public static final byte ITEM_Object = 7; | |||
public static final byte ITEM_Uninitialized = 8; | |||
public static final String[] ITEM_NAMES = { | |||
"Bogus", "Integer", "Float", "Double", "Long", | |||
"Null", "InitObject", "Object", "NewObject" | |||
"Top", "Integer", "Float", "Double", "Long", | |||
"Null", "UninitializedThis", "Object", "Uninitalized" | |||
}; | |||
} |
@@ -71,7 +71,7 @@ import java.util.HashMap; | |||
* <em>Synthetic</em> attributes are supported. The | |||
* <em>Unknown</em> attribute stands for non-standard-attributes. | |||
* | |||
* @version $Id: Attribute.java,v 1.2 2004/11/19 16:45:18 aclement Exp $ | |||
* @version $Id: Attribute.java,v 1.2.8.1 2007/02/09 10:45:09 aclement Exp $ | |||
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> | |||
* @see ConstantValue | |||
* @see SourceFile | |||
@@ -168,15 +168,14 @@ public abstract class Attribute implements Cloneable, Node, Serializable { | |||
c = (ConstantUtf8)constant_pool.getConstant(name_index, | |||
Constants.CONSTANT_Utf8); | |||
name = c.getBytes(); | |||
// Length of data in bytes | |||
length = file.readInt(); | |||
// Compare strings to find known attribute | |||
for(byte i=0; i < Constants.KNOWN_ATTRIBUTES; i++) { | |||
for (byte i=0; i < Constants.KNOWN_ATTRIBUTES; i++) { | |||
if(name.equals(Constants.ATTRIBUTE_NAMES[i])) { | |||
tag = i; // found! | |||
break; | |||
tag = i; // found! | |||
break; | |||
} | |||
} | |||
@@ -186,7 +185,7 @@ public abstract class Attribute implements Cloneable, Node, Serializable { | |||
AttributeReader r = (AttributeReader)readers.get(name); | |||
if(r != null) | |||
return r.createAttribute(name_index, length, file, constant_pool); | |||
return r.createAttribute(name_index, length, file, constant_pool); | |||
else | |||
return new Unknown(name_index, length, file, constant_pool); | |||
@@ -223,8 +222,8 @@ public abstract class Attribute implements Cloneable, Node, Serializable { | |||
case Constants.ATTR_SIGNATURE: | |||
return new Signature(name_index, length, file, constant_pool); | |||
case Constants.ATTR_STACK_MAP: | |||
return new StackMap(name_index, length, file, constant_pool); | |||
case Constants.ATTR_STACK_MAP_TABLE: | |||
return new StackMapTable(name_index, length, file, constant_pool); | |||
// J5SUPPORT: | |||
case Constants.ATTR_RUNTIME_VISIBLE_ANNOTATIONS: |
@@ -63,7 +63,7 @@ import java.io.*; | |||
* and represents a reference to the name and signature | |||
* of a field or method. | |||
* | |||
* @version $Id: ConstantNameAndType.java,v 1.2 2004/11/19 16:45:18 aclement Exp $ | |||
* @version $Id: ConstantNameAndType.java,v 1.2.8.1 2007/02/09 10:45:09 aclement Exp $ | |||
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> | |||
* @see Constant | |||
*/ | |||
@@ -86,7 +86,7 @@ public final class ConstantNameAndType extends Constant { | |||
*/ | |||
ConstantNameAndType(DataInputStream file) throws IOException | |||
{ | |||
this((int)file.readUnsignedShort(), (int)file.readUnsignedShort()); | |||
this(file.readUnsignedShort(), file.readUnsignedShort()); | |||
} | |||
/** |
@@ -66,7 +66,7 @@ import org.aspectj.apache.bcel.classfile.annotation.RuntimeVisibleParameterAnnot | |||
* class supplies the traversal strategy, other classes can make use | |||
* of it. | |||
* | |||
* @version $Id: DescendingVisitor.java,v 1.2 2004/11/19 16:45:18 aclement Exp $ | |||
* @version $Id: DescendingVisitor.java,v 1.2.8.1 2007/02/09 10:45:09 aclement Exp $ | |||
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> | |||
*/ | |||
public class DescendingVisitor implements Visitor { | |||
@@ -89,7 +89,7 @@ public class DescendingVisitor implements Visitor { | |||
if((size < 2) || (level < 0)) | |||
return null; | |||
else | |||
return stack.elementAt(size - (level + 2)); // size - 1 == current | |||
} | |||
@@ -212,18 +212,18 @@ public class DescendingVisitor implements Visitor { | |||
stack.pop(); | |||
} | |||
public void visitStackMap(StackMap table) { | |||
public void visitStackMap(StackMapTable table) { | |||
stack.push(table); | |||
table.accept(visitor); | |||
StackMapEntry[] vars = table.getStackMap(); | |||
StackMapFrame[] vars = table.getStackMap(); | |||
for(int i=0; i < vars.length; i++) | |||
vars[i].accept(this); | |||
stack.pop(); | |||
} | |||
public void visitStackMapEntry(StackMapEntry var) { | |||
public void visitStackMapEntry(StackMapFrame var) { | |||
stack.push(var); | |||
var.accept(visitor); | |||
stack.pop(); |
@@ -66,7 +66,7 @@ import org.aspectj.apache.bcel.classfile.annotation.RuntimeVisibleParameterAnnot | |||
* By courtesy of David Spencer. | |||
* | |||
* @see DescendingVisitor | |||
* @version $Id: EmptyVisitor.java,v 1.3 2004/11/19 16:45:18 aclement Exp $ | |||
* @version $Id: EmptyVisitor.java,v 1.3.8.1 2007/02/09 10:45:09 aclement Exp $ | |||
* | |||
*/ | |||
public class EmptyVisitor implements Visitor { | |||
@@ -102,8 +102,8 @@ public class EmptyVisitor implements Visitor { | |||
public void visitSourceFile(SourceFile obj) {} | |||
public void visitSynthetic(Synthetic obj) {} | |||
public void visitUnknown(Unknown obj) {} | |||
public void visitStackMap(StackMap obj) {} | |||
public void visitStackMapEntry(StackMapEntry obj) {} | |||
public void visitStackMap(StackMapTable obj) {} | |||
public void visitStackMapEntry(StackMapFrame obj) {} | |||
// J5SUPPORT: | |||
public void visitEnclosingMethod(EnclosingMethod obj) {} |
@@ -170,10 +170,9 @@ public class GenericSignatureParser { | |||
FieldTypeSignature fieldType = parseFieldTypeSignature(true); | |||
if (fieldType != null) { | |||
return new ArrayTypeSignature(fieldType); | |||
} else { | |||
// must be BaseType array | |||
return new ArrayTypeSignature(new BaseTypeSignature(eatIdentifier())); | |||
} | |||
// must be BaseType array | |||
return new ArrayTypeSignature(new BaseTypeSignature(eatIdentifier())); | |||
} | |||
// L PackageSpecifier* SimpleClassTypeSignature ClassTypeSignature* ; | |||
@@ -264,9 +263,8 @@ public class GenericSignatureParser { | |||
TypeArgument[] tArgs = new TypeArgument[typeArgs.size()]; | |||
typeArgs.toArray(tArgs); | |||
return tArgs; | |||
} else { | |||
return null; | |||
} | |||
return null; | |||
} | |||
private TypeVariableSignature parseTypeVariableSignature() { |
@@ -77,7 +77,7 @@ import java.util.StringTokenizer; | |||
* class file. Those interested in programatically generating classes | |||
* should see the <a href="../generic/ClassGen.html">ClassGen</a> class. | |||
* @version $Id: JavaClass.java,v 1.9 2005/12/02 06:56:14 acolyer Exp $ | |||
* @version $Id: JavaClass.java,v 1.9.6.1 2007/02/09 10:45:09 aclement Exp $ | |||
* @see org.aspectj.apache.bcel.generic.ClassGen | |||
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> | |||
*/ | |||
@@ -971,9 +971,8 @@ public class JavaClass extends AccessFlags implements Cloneable, Node { | |||
loadGenericSignatureInfoIfNecessary(); | |||
if (signatureAttribute != null) { | |||
return signatureAttribute.asClassSignature(); | |||
} else { | |||
return null; | |||
} | |||
return null; | |||
} | |||
} |
@@ -69,7 +69,7 @@ import org.aspectj.apache.bcel.generic.Type; | |||
* for a method in the class. See JVM specification for details. | |||
* A method has access flags, a name, a signature and a number of attributes. | |||
* | |||
* @version $Id: Method.java,v 1.2 2004/11/19 16:45:18 aclement Exp $ | |||
* @version $Id: Method.java,v 1.2.8.1 2007/02/09 10:45:09 aclement Exp $ | |||
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> | |||
*/ | |||
public final class Method extends FieldOrMethod { | |||
@@ -169,7 +169,6 @@ public final class Method extends FieldOrMethod { | |||
if(code != null) | |||
return code.getLocalVariableTable(); | |||
else | |||
return null; | |||
} | |||
@@ -181,8 +180,8 @@ public final class Method extends FieldOrMethod { | |||
if(code != null) | |||
return code.getLineNumberTable(); | |||
else | |||
return null; | |||
return null; | |||
} | |||
/** | |||
@@ -252,7 +251,7 @@ public final class Method extends FieldOrMethod { | |||
if (parameterAnnotationsOutOfDate) { | |||
// Find attributes that contain annotation data | |||
Attribute[] attrs = getAttributes(); | |||
List accumulatedAnnotations = new ArrayList(); | |||
// List accumulatedAnnotations = new ArrayList(); | |||
for (int i = 0; i < attrs.length; i++) { | |||
Attribute attribute = attrs[i]; |
@@ -69,7 +69,7 @@ import org.aspectj.apache.bcel.Constants; | |||
* This class is derived from <em>Attribute</em> and represents a reference | |||
* to a <href="http://wwwipd.ira.uka.de/~pizza/gj/">GJ</a> attribute. | |||
* | |||
* @version $Id: Signature.java,v 1.6 2005/07/12 13:59:06 aclement Exp $ | |||
* @version $Id: Signature.java,v 1.6.6.1 2007/02/09 10:45:09 aclement Exp $ | |||
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> | |||
* @see Attribute | |||
*/ | |||
@@ -119,7 +119,6 @@ public final class Signature extends Attribute { | |||
* @param v Visitor object | |||
*/ | |||
public void accept(Visitor v) { | |||
System.err.println("Visiting non-standard Signature object"); | |||
v.visitSignature(this); | |||
} | |||
@@ -54,8 +54,9 @@ package org.aspectj.apache.bcel.classfile; | |||
* <http://www.apache.org/>. | |||
*/ | |||
import org.aspectj.apache.bcel.Constants; | |||
import java.io.*; | |||
import java.io.DataInputStream; | |||
import java.io.DataOutputStream; | |||
import java.io.IOException; | |||
/** | |||
* This class represents a stack map attribute used for | |||
@@ -66,7 +67,7 @@ import java.io.*; | |||
* within the Code attribute of a method. See CLDC specification | |||
* §5.3.1.2 | |||
* | |||
* @version $Id: StackMap.java,v 1.2 2004/11/19 16:45:18 aclement Exp $ | |||
* @version $Id: StackMap.java,v 1.2.8.1 2007/02/09 10:45:09 aclement Exp $ | |||
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> | |||
* @see Code | |||
* @see StackMapEntry | |||
@@ -85,7 +86,7 @@ public final class StackMap extends Attribute implements Node { | |||
public StackMap(int name_index, int length, StackMapEntry[] map, | |||
ConstantPool constant_pool) | |||
{ | |||
super(Constants.ATTR_STACK_MAP, name_index, length, constant_pool); | |||
super(/*dunno - do we want to support the 'old style' ? */(byte)12/*Constants.ATTR_STACK_MAP*/, name_index, length, constant_pool); | |||
setStackMap(map); | |||
} | |||
@@ -178,7 +179,7 @@ public final class StackMap extends Attribute implements Node { | |||
* @param v Visitor object | |||
*/ | |||
public void accept(Visitor v) { | |||
v.visitStackMap(this); | |||
//v.visitStackMap(this); | |||
} | |||
public final int getMapLength() { return map_length; } |
@@ -63,7 +63,7 @@ import java.io.IOException; | |||
* local variables and the the of stack items at a given byte code offset. | |||
* See CLDC specification §5.3.1.2 | |||
* | |||
* @version $Id: StackMapEntry.java,v 1.4 2004/11/22 08:31:27 aclement Exp $ | |||
* @version $Id: StackMapEntry.java,v 1.4.8.1 2007/02/09 10:45:09 aclement Exp $ | |||
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> | |||
* @see StackMap | |||
* @see StackMapType | |||
@@ -193,7 +193,7 @@ public final class StackMapEntry implements Cloneable { | |||
* @param v Visitor object | |||
*/ | |||
public void accept(Visitor v) { | |||
v.visitStackMapEntry(this); | |||
// v.visitStackMapEntry(this); | |||
} | |||
/** |
@@ -0,0 +1,333 @@ | |||
package org.aspectj.apache.bcel.classfile; | |||
/* ==================================================================== | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2001 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, | |||
* if any, must include the following acknowledgment: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowledgment may appear in the software itself, | |||
* if and wherever such third-party acknowledgments normally appear. | |||
* | |||
* 4. The names "Apache" and "Apache Software Foundation" and | |||
* "Apache BCEL" must not be used to endorse or promote products | |||
* derived from this software without prior written permission. For | |||
* written permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache", | |||
* "Apache BCEL", nor may "Apache" appear in their name, without | |||
* prior written permission of the Apache Software Foundation. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
import java.io.DataInputStream; | |||
import java.io.DataOutputStream; | |||
import java.io.IOException; | |||
import org.aspectj.apache.bcel.generic.ConstantPoolGen; | |||
import org.aspectj.apache.bcel.verifier.utility.Frame; | |||
/** | |||
* This class represents a stack map entry recording the types of | |||
* local variables and the the of stack items at a given byte code offset. | |||
* See CLDC specification §5.3.1.2 | |||
* | |||
* @version $Id$ | |||
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> | |||
* @see StackMapTable | |||
* @see StackMapType | |||
*/ | |||
public final class StackMapFrame implements Cloneable, Node { | |||
// XXX move to bcel constants | |||
public static final byte SameFrameKind = 0; | |||
public static final byte SameLocalsOneStackItemFrameKind = 1; | |||
public static final byte RESERVEDKind = 2; | |||
public static final byte SameLocalsOneStackItemFrameExtendedKind = 3; | |||
public static final byte ChopFrameKind = 4; | |||
public static final byte SameFrameExtendedKind = 5; | |||
public static final byte AppendFrameKind = 6; | |||
public static final byte FullFrameKind = 7; | |||
public static final byte UnknownKind = 8; | |||
private byte kind = UnknownKind; | |||
private static String[] ALL_KINDS = { | |||
"SameFrame","SameLocalsOneStackItemFrame","RESERVED", | |||
"SameLocalsOneStackItemFrameExtended","ChopFrame", | |||
"SameFrameExtended","AppendFrame","FullFrame","???" | |||
}; | |||
/** | |||
* 000..063 => SameFrame | |||
* 064..127 => SameLocalsOneStackItemFrame | |||
* 128..246 => RESERVED | |||
* 247 => SameLocalsOneStackItemFrameExtended | |||
* 248..250 => ChopFrame | |||
* 251 => SameFrameExtended | |||
* 252..254 => AppendFrame | |||
* 255 => FullFrame | |||
*/ | |||
private int tag; | |||
private int offsetDelta; | |||
private int localCount=0; | |||
private int stackCount=0; | |||
private StackMapType[] localTypes; | |||
private StackMapType[] stackTypes; | |||
private ConstantPool cpool; | |||
// temporary | |||
boolean built = false; | |||
/** | |||
* Build a StackMapFrame from the file stream contents | |||
* @param is Data input stream | |||
* @throws IOException | |||
*/ | |||
StackMapFrame(DataInputStream is, ConstantPool constantPool) throws IOException { | |||
cpool = constantPool; | |||
tag = is.readUnsignedByte(); | |||
if (tag<64) { // SameFrame | |||
// same locals as before, stack height is zero | |||
kind = SameFrameKind; | |||
offsetDelta=tag; | |||
if (debug) System.err.println("Loading SameFrame: offset="+offsetDelta); | |||
} else if (tag<128) { // SameLocalsOneStackItemFrame | |||
kind = SameLocalsOneStackItemFrameKind; | |||
offsetDelta = tag-64; | |||
if (debug) System.err.println("Loading SameLocalsOneStackItemFrame: offset="+offsetDelta); | |||
stackCount=1; | |||
stackTypes=new StackMapType[1]; | |||
stackTypes[0]=new StackMapType(is,constantPool); | |||
} else if (tag<247) { // RESERVED | |||
kind=RESERVEDKind; | |||
if (debug) System.err.println("Loading reserved"); | |||
} else if (tag==247) { // SameLocalsOneStackItemFrameExtended | |||
kind=SameLocalsOneStackItemFrameExtendedKind; | |||
offsetDelta = is.readUnsignedShort(); | |||
if (debug) System.err.println("Loading SameLocalsOneStackItemFrameExtended: offset="+offsetDelta); | |||
stackCount=1; | |||
stackTypes=new StackMapType[1]; | |||
stackTypes[0]=new StackMapType(is,constantPool); | |||
} else if (tag<251) { // ChopFrame | |||
kind=ChopFrameKind; | |||
if (debug) System.err.println("Loading ChopFrame"); | |||
offsetDelta = is.readUnsignedShort(); | |||
localCount=251-tag; // locals removed from previous frame | |||
} else if (tag==251) { // SameFrameExtended | |||
kind=SameFrameExtendedKind; | |||
// same locals as before, stack height is zero | |||
offsetDelta = is.readUnsignedShort(); | |||
if (debug) System.err.println("Loading SameFrameExtended: offset="+offsetDelta); | |||
} else if (tag<255) { // AppendFrame | |||
kind=AppendFrameKind; | |||
offsetDelta = is.readUnsignedShort(); | |||
localCount = tag - 251; | |||
stackCount = 0; // implied | |||
if (debug) System.err.println("Loading AppendFrame, number of locals="+localCount+" offset="+offsetDelta); | |||
localTypes = new StackMapType[localCount]; | |||
for(int i=0;i<localCount;i++) { | |||
localTypes[i] = new StackMapType(is,constantPool); | |||
if (debug) System.err.println(" Position ["+i+"] = "+localTypes[i]); | |||
} | |||
} else if (tag==255) { // FullFrame | |||
kind=FullFrameKind; | |||
if (debug) System.err.println("Loading FullFrame..."); | |||
offsetDelta = is.readUnsignedShort(); | |||
localCount = is.readUnsignedShort(); | |||
localTypes = new StackMapType[localCount]; | |||
for(int i=0;i<localCount;i++) { | |||
localTypes[i] = new StackMapType(is,constantPool); | |||
if (debug) System.err.println(" Locals ["+i+"] = "+localTypes[i]); | |||
} | |||
stackCount = is.readUnsignedShort(); | |||
stackTypes = new StackMapType[stackCount]; | |||
for(int i=0;i<stackCount;i++) { | |||
stackTypes[i] = new StackMapType(is,constantPool); | |||
if (debug) System.err.println(" Stack ["+i+"] = "+stackTypes[i]); | |||
} | |||
if (debug) System.err.println("Loaded FullFrame, offset="+offsetDelta+" number of locals="+localCount+" number of stack items="+stackCount); | |||
} | |||
} | |||
private boolean debug = false; | |||
public StackMapFrame(int byte_code_offset, int number_of_locals, | |||
StackMapType[] types_of_locals, | |||
int number_of_stack_items, | |||
StackMapType[] types_of_stack_items, | |||
ConstantPool constant_pool) { | |||
this.offsetDelta = byte_code_offset; | |||
this.localCount = number_of_locals; | |||
this.localTypes = types_of_locals; | |||
this.stackCount = number_of_stack_items; | |||
this.stackTypes = types_of_stack_items; | |||
this.cpool = constant_pool; | |||
} | |||
/** | |||
* Dump stack map entry | |||
* | |||
* @param file Output file stream | |||
* @throws IOException | |||
*/ | |||
public final void dump(DataOutputStream file) throws IOException | |||
{ | |||
// all frames are full ones! | |||
file.writeByte(255); | |||
file.writeShort(offsetDelta); | |||
file.writeShort(localTypes.length);//localCount); | |||
for(int i=0; i < localTypes.length; i++) { | |||
localTypes[i].dump(file); | |||
} | |||
file.writeShort(stackTypes.length); | |||
for(int i=0; i < stackTypes.length; i++) | |||
stackTypes[i].dump(file); | |||
} | |||
/** | |||
* @return String representation. | |||
*/ | |||
public final String toString() { | |||
// if (!built) return "Unfinished StackMapFrame, tag="+Integer.toString(tag); | |||
StringBuffer buf = new StringBuffer(getKindString()+" (offset=" + offsetDelta); | |||
if (localCount!=0 && localTypes==null && kind!=ChopFrameKind) { | |||
System.err.println("BADLY FORMED STACKMAPFRAME: "+getKindString()+" locals size is "+localCount+" but localtypes is null"); | |||
} | |||
// For a 'ChopFrame' - the localCount is the number to remove, so the localTypes field stays null. | |||
if (kind==ChopFrameKind) { | |||
buf.append(" chopping "+localCount+" local").append(localCount>1?"s":""); | |||
} else { | |||
if(localCount > 0) { | |||
buf.append(", LOCALS["+localCount+"]={"); | |||
for(int i=0; i < localCount; i++) { | |||
buf.append(localTypes[i]); | |||
if(i < localCount - 1) | |||
buf.append(", "); | |||
} | |||
buf.append("}"); | |||
} | |||
} | |||
if(stackCount > 0) { | |||
buf.append(", STACK["+stackCount+"]={"); | |||
for(int i=0; i < stackCount; i++) { | |||
buf.append(stackTypes[i]); | |||
if(i < stackCount - 1) | |||
buf.append(", "); | |||
} | |||
buf.append("}"); | |||
} | |||
buf.append(")"); | |||
return buf.toString(); | |||
} | |||
public void setByteCodeOffset(int b) { offsetDelta = b; } | |||
public int getByteCodeOffset() { return offsetDelta; } | |||
public void setNumberOfLocals(int n) { localCount = n; } | |||
public int getNumberOfLocals() { return localCount; } | |||
public void setTypesOfLocals(StackMapType[] t) { localTypes = t; } | |||
public StackMapType[] getTypesOfLocals() { return localTypes; } | |||
public void setNumberOfStackItems(int n) { stackCount = n; } | |||
public int getNumberOfStackItems() { return stackCount; } | |||
public void setTypesOfStackItems(StackMapType[] t) { stackTypes = t; } | |||
public StackMapType[] getTypesOfStackItems() { return stackTypes; } | |||
/** | |||
* @return deep copy of this object | |||
*/ | |||
public StackMapFrame copy() { | |||
try { | |||
return (StackMapFrame)clone(); | |||
} catch(CloneNotSupportedException e) {} | |||
return null; | |||
} | |||
/** | |||
* Called by objects that are traversing the nodes of the tree implicitely | |||
* defined by the contents of a Java class. I.e., the hierarchy of methods, | |||
* fields, attributes, etc. spawns a tree of objects. | |||
* | |||
* @param v Visitor object | |||
*/ | |||
public void accept(Visitor v) { | |||
v.visitStackMapEntry(this); | |||
} | |||
public String getKindString() { | |||
return ALL_KINDS[kind]; | |||
} | |||
/** | |||
* @return Constant pool used by this object. | |||
*/ | |||
public final ConstantPool getConstantPool() { return cpool; } | |||
/** | |||
* @param constant_pool Constant pool to be used for this object. | |||
*/ | |||
public final void setConstantPool(ConstantPool constant_pool) { | |||
this.cpool = constant_pool; | |||
} | |||
public int getKind() { | |||
return kind; | |||
} | |||
public static StackMapFrame forFrame(int offset, Frame frame, ConstantPoolGen cpg) { | |||
//System.out.println("? StackMapFrame.forFrame(): new frame at offset "+offset+": locals="+frame.getLocals().toCompactString()); | |||
StackMapFrame smf = | |||
new StackMapFrame(offset, | |||
frame.getLocals().maxLocals(), | |||
frame.getLocalsAsStackMapTypes(cpg), | |||
frame.getStack().maxStack(), | |||
frame.getStackAsStackMapTypes(cpg), | |||
cpg.getConstantPool()); | |||
return smf; | |||
} | |||
} |
@@ -0,0 +1,282 @@ | |||
package org.aspectj.apache.bcel.classfile; | |||
/* ==================================================================== | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2001 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, | |||
* if any, must include the following acknowledgment: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowledgment may appear in the software itself, | |||
* if and wherever such third-party acknowledgments normally appear. | |||
* | |||
* 4. The names "Apache" and "Apache Software Foundation" and | |||
* "Apache BCEL" must not be used to endorse or promote products | |||
* derived from this software without prior written permission. For | |||
* written permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache", | |||
* "Apache BCEL", nor may "Apache" appear in their name, without | |||
* prior written permission of the Apache Software Foundation. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
import org.aspectj.apache.bcel.Constants; | |||
import org.aspectj.apache.bcel.generic.ConstantPoolGen; | |||
import org.aspectj.apache.bcel.verifier.utility.Frame; | |||
import java.io.*; | |||
/** | |||
* This class represents a stack map attribute used for | |||
* preverification of Java classes for the <a | |||
* href="http://java.sun.com/j2me/"> Java 2 Micro Edition</a> | |||
* (J2ME). This attribute is used by the <a | |||
* href="http://java.sun.com/products/cldc/">KVM</a> and contained | |||
* within the Code attribute of a method. See CLDC specification | |||
* §5.3.1.2 | |||
* | |||
* @version $Id$ | |||
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> | |||
* @see Code | |||
* @see StackMapFrame | |||
* @see StackMapType | |||
* | |||
* overhauled - Andy Clement 4th April 2006 | |||
*/ | |||
public final class StackMapTable extends Attribute implements Node { | |||
private StackMapFrame[] entries; // Table of stack map entries | |||
private int numberOfEntries; | |||
// if 'isInPackedState' then the data array needs unpacking into the entries array before use | |||
private boolean isInPackedState = false; | |||
private byte[] data; | |||
// temporary | |||
String dataString; | |||
/** | |||
* @param nameIdx Index of name | |||
* @param len Content length in bytes | |||
* @param map Table of stack map entries | |||
* @param constantPool Array of constants | |||
*/ | |||
public StackMapTable(int nameIdx, int len, StackMapFrame[] map, ConstantPool constantPool) { | |||
super(Constants.ATTR_STACK_MAP_TABLE, nameIdx, len, constantPool); | |||
setStackMap(map); | |||
isInPackedState=false; | |||
} | |||
/** | |||
* Construct object from file stream. | |||
* @param nameIdx Index of name | |||
* @param len Content length in bytes | |||
* @param file Input stream | |||
* @throws IOException | |||
* @param constant_pool Array of constants | |||
*/ | |||
StackMapTable(int nameIdx, int len, DataInputStream file, ConstantPool constantPool) throws IOException { | |||
this(nameIdx, len, (StackMapFrame[])null, constantPool); | |||
numberOfEntries = file.readUnsignedShort(); | |||
// entries = new StackMapFrame[numberOfEntries]; | |||
data = new byte[length-2]; | |||
int byteReads = file.read(data); | |||
isInPackedState = true; | |||
// assert(bytesRead==length) | |||
// for (int i=0; i < numberOfEntries; i++) { | |||
// entries[i] = new StackMapFrame(file, constantPool); | |||
// } | |||
} | |||
private void ensureUnpacked() { | |||
if (!isInPackedState) return; | |||
try { | |||
// temporary | |||
// construct an int representation for debugging | |||
StringBuffer sb = new StringBuffer(); | |||
sb.append("StackMapTable ["); | |||
String zeroes = "000"; | |||
for (int i = 0; i < data.length; i++) { | |||
int b = (int) data[i]; | |||
if (b<0) b+=256; | |||
StringBuffer sb2 = new StringBuffer().append(zeroes).append(Integer.toString(b)); | |||
sb.append(sb2.substring(sb2.length()-3)).append(" "); | |||
if ((sb.length()%60)>55) sb.append("\n"); | |||
} | |||
dataString = sb.append("]").toString(); | |||
ByteArrayInputStream bs = new ByteArrayInputStream(data); | |||
DataInputStream dis = new DataInputStream(bs); | |||
// numberOfEntries = (dis.readUnsignedShort()); | |||
entries = new StackMapFrame[numberOfEntries]; | |||
if (debug) System.err.println("Unpacking "+numberOfEntries+" StackFrames"); | |||
int bcoffset=0; | |||
for (int i=0; i < numberOfEntries; i++) { | |||
entries[i] = new StackMapFrame(dis, constant_pool); | |||
bcoffset+=(entries[i].getByteCodeOffset()+(i==0?0:1)); | |||
if (debug) System.err.println("That was at bytecode offset "+bcoffset); | |||
} | |||
dis.close(); | |||
data = null; // throw it away now | |||
} catch (IOException e) { | |||
throw new RuntimeException("Unpacking of StackMapTable attribute failed"); | |||
} | |||
isInPackedState=false; | |||
} | |||
private static boolean debug = false; | |||
/** | |||
* Dump line number table attribute to file stream in binary format. | |||
* | |||
* @param file Output file stream | |||
* @throws IOException | |||
*/ | |||
public final void dump(DataOutputStream file) throws IOException | |||
{ | |||
super.dump(file); | |||
file.writeShort(numberOfEntries); | |||
if (isInPackedState) { | |||
file.write(data); | |||
} else { | |||
for(int i=0; i < numberOfEntries; i++) | |||
entries[i].dump(file); | |||
} | |||
} | |||
/** | |||
* @return Array of stack map entries | |||
*/ | |||
public final StackMapFrame[] getStackMap() { ensureUnpacked();return entries; } | |||
/** | |||
* @param map Array of stack map entries | |||
*/ | |||
public final void setStackMap(StackMapFrame[] map) { | |||
this.data = null; | |||
this.isInPackedState=false; | |||
entries = map; | |||
numberOfEntries = (map == null)? 0 : map.length; | |||
} | |||
/** | |||
* @return String representation. | |||
*/ | |||
public final String toString() { | |||
ensureUnpacked(); | |||
return dataString; | |||
// StringBuffer buf = new StringBuffer("StackMapTable("); | |||
// | |||
// for(int i=0; i < numberOfEntries; i++) { | |||
// buf.append(entries[i].toString()); | |||
// | |||
// if(i < numberOfEntries - 1) | |||
// buf.append(", "); | |||
// } | |||
// | |||
// buf.append(')'); | |||
// | |||
// return buf.toString(); | |||
} | |||
/** | |||
* @return deep copy of this attribute | |||
*/ | |||
public Attribute copy(ConstantPool constant_pool) { | |||
ensureUnpacked(); | |||
StackMapTable c = (StackMapTable)clone(); | |||
c.entries = new StackMapFrame[numberOfEntries]; | |||
for(int i=0; i < numberOfEntries; i++) | |||
c.entries[i] = entries[i].copy(); | |||
c.constant_pool = constant_pool; | |||
return c; | |||
} | |||
/** | |||
* Called by objects that are traversing the nodes of the tree implicitely | |||
* defined by the contents of a Java class. I.e., the hierarchy of methods, | |||
* fields, attributes, etc. spawns a tree of objects. | |||
* | |||
* @param v Visitor object | |||
*/ | |||
public void accept(Visitor v) { | |||
ensureUnpacked(); | |||
v.visitStackMap(this); | |||
} | |||
public final int getMapLength() { return numberOfEntries; } | |||
/** | |||
* Takes a sequence of execution frames and builds a StackMapTable attribute for them | |||
*/ | |||
public static StackMapTable forFrames(Frame[] frames,/* int[] offsets,*/ ConstantPoolGen cpg) { | |||
if (debug) System.out.println("> StackMapTable.forFrames(): Building table from a set of frames"); | |||
if (debug) System.out.print("Offsets:"); | |||
for (int i = 0; i < frames.length; i++) { | |||
if (debug) System.out.print(frames[i].position+" "); | |||
} | |||
if (debug) System.out.println(); | |||
try { | |||
StackMapFrame[] smf = new StackMapFrame[frames.length]; // don't need the first frame, calculated from descriptor | |||
int offset = 0; | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||
DataOutputStream dos = new DataOutputStream(baos); | |||
for (int i = 0; i < frames.length; i++) { | |||
if (i==0) offset= frames[0].position; else offset = frames[i].position-frames[i-1].position-1; | |||
if (offset<0) { | |||
throw new RuntimeException(); | |||
} | |||
smf[i] = StackMapFrame.forFrame(offset,frames[i],cpg); | |||
smf[i].dump(dos); | |||
} | |||
dos.flush();dos.close(); | |||
int len = baos.toByteArray().length+2; | |||
int nameIndex = cpg.addUtf8(Constants.ATTRIBUTE_NAMES[Constants.ATTR_STACK_MAP_TABLE]); | |||
StackMapTable table = new StackMapTable(nameIndex,len,smf,cpg.getConstantPool()); | |||
return table; | |||
} catch (IOException e) { | |||
e.printStackTrace(); | |||
throw new RuntimeException(); | |||
} | |||
} | |||
} |
@@ -61,11 +61,12 @@ import java.io.*; | |||
* This class represents the type of a local variable or item on stack | |||
* used in the StackMap entries. | |||
* | |||
* @version $Id: StackMapType.java,v 1.2 2004/11/19 16:45:18 aclement Exp $ | |||
* @version $Id: StackMapType.java,v 1.2.8.1 2007/02/09 10:45:09 aclement Exp $ | |||
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> | |||
* @see StackMapEntry | |||
* @see StackMap | |||
* @see StackMapFrame | |||
* @see StackMapTable | |||
* @see Constants | |||
* | |||
*/ | |||
public final class StackMapType implements Cloneable { | |||
private byte type; | |||
@@ -77,13 +78,9 @@ public final class StackMapType implements Cloneable { | |||
* @param file Input stream | |||
* @throws IOException | |||
*/ | |||
StackMapType(DataInputStream file, ConstantPool constant_pool) throws IOException | |||
{ | |||
StackMapType(DataInputStream file, ConstantPool constant_pool) throws IOException { | |||
this(file.readByte(), -1, constant_pool); | |||
if(hasIndex()) | |||
setIndex(file.readShort()); | |||
if(hasIndex()) setIndex(file.readShort()); | |||
setConstantPool(constant_pool); | |||
} | |||
@@ -98,7 +95,7 @@ public final class StackMapType implements Cloneable { | |||
} | |||
public void setType(byte t) { | |||
if((t < Constants.ITEM_Bogus) || (t > Constants.ITEM_NewObject)) | |||
if ((t < Constants.ITEM_Top) || (t > Constants.ITEM_Uninitialized)) | |||
throw new RuntimeException("Illegal type for StackMapType: " + t); | |||
type = t; | |||
} | |||
@@ -128,13 +125,13 @@ public final class StackMapType implements Cloneable { | |||
*/ | |||
public final boolean hasIndex() { | |||
return ((type == Constants.ITEM_Object) || | |||
(type == Constants.ITEM_NewObject)); | |||
(type == Constants.ITEM_Uninitialized)); | |||
} | |||
private String printIndex() { | |||
if(type == Constants.ITEM_Object) | |||
return ", class=" + constant_pool.constantToString(index, Constants.CONSTANT_Class); | |||
else if(type == Constants.ITEM_NewObject) | |||
else if(type == Constants.ITEM_Uninitialized) | |||
return ", offset=" + index; | |||
else | |||
return ""; |
@@ -86,7 +86,7 @@ import org.aspectj.apache.bcel.util.ByteSequence; | |||
/** | |||
* Utility functions that do not really belong to any class in particular. | |||
* | |||
* @version $Id: Utility.java,v 1.5 2006/07/19 12:06:15 aclement Exp $ | |||
* @version $Id: Utility.java,v 1.5.2.1 2007/02/09 10:45:09 aclement Exp $ | |||
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> | |||
* | |||
* modified: Andy Clement 2-mar-05 Removed unnecessary static and optimized | |||
@@ -458,11 +458,10 @@ public abstract class Utility { | |||
if(vars != null) { | |||
LocalVariable l = vars.getLocalVariable(var_index); | |||
if(l != null) | |||
buf.append(" " + l.getName()); | |||
} else | |||
if(l != null) buf.append(" " + l.getName()); | |||
} else { | |||
buf.append(" arg" + var_index); | |||
} | |||
if("double".equals(param_type) || "long".equals(param_type)) | |||
var_index += 2; | |||
@@ -778,7 +777,7 @@ public abstract class Utility { | |||
if(left_justify) | |||
return str + new String(buf); | |||
else | |||
return new String(buf) + str; | |||
} | |||
@@ -67,7 +67,7 @@ import org.aspectj.apache.bcel.classfile.annotation.RuntimeVisibleParameterAnnot | |||
* Implemented by wish of | |||
* <A HREF="http://www.inf.fu-berlin.de/~bokowski">Boris Bokowski</A>. | |||
* | |||
* @version $Id: Visitor.java,v 1.2 2004/11/19 16:45:18 aclement Exp $ | |||
* @version $Id: Visitor.java,v 1.2.8.1 2007/02/09 10:45:09 aclement Exp $ | |||
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> | |||
*/ | |||
public interface Visitor { | |||
@@ -101,8 +101,8 @@ public interface Visitor { | |||
public void visitSourceFile(SourceFile obj); | |||
public void visitSynthetic(Synthetic obj); | |||
public void visitUnknown(Unknown obj); | |||
public void visitStackMap(StackMap obj); | |||
public void visitStackMapEntry(StackMapEntry obj); | |||
public void visitStackMap(StackMapTable obj); | |||
public void visitStackMapEntry(StackMapFrame obj); | |||
// Java5 | |||
public void visitEnclosingMethod(EnclosingMethod obj); |
@@ -58,7 +58,7 @@ import org.aspectj.apache.bcel.Constants; | |||
/** | |||
* Denotes array type, such as int[][] | |||
* | |||
* @version $Id: ArrayType.java,v 1.2 2004/11/19 16:45:19 aclement Exp $ | |||
* @version $Id: ArrayType.java,v 1.2.8.1 2007/02/09 10:45:09 aclement Exp $ | |||
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> | |||
*/ | |||
public final class ArrayType extends ReferenceType { | |||
@@ -132,7 +132,7 @@ public final class ArrayType extends ReferenceType { | |||
public Type getElementType() { | |||
if(dimensions == 1) | |||
return basic_type; | |||
else | |||
return new ArrayType(basic_type, dimensions - 1); | |||
} | |||
@@ -82,7 +82,7 @@ import org.aspectj.apache.bcel.classfile.ConstantUtf8; | |||
* Constants.MAX_SHORT entries. Note that the first (0) is used by the | |||
* JVM and that Double and Long constants need two slots. | |||
* | |||
* @version $Id: ConstantPoolGen.java,v 1.3 2004/11/22 08:31:27 aclement Exp $ | |||
* @version $Id: ConstantPoolGen.java,v 1.3.8.1 2007/02/09 10:45:09 aclement Exp $ | |||
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> | |||
* @see Constant | |||
*/ | |||
@@ -160,8 +160,9 @@ public class ConstantPoolGen implements java.io.Serializable { | |||
delim = IMETHODREF_DELIM; | |||
else if(c instanceof ConstantFieldref) | |||
delim = FIELDREF_DELIM; | |||
cp_table.put(class_name + delim + method_name + delim + signature, new Index(i)); | |||
StringBuffer key = new StringBuffer(class_name); | |||
key.append(delim).append(method_name).append(delim).append(signature); | |||
cp_table.put(key.toString(), new Index(i)); | |||
} | |||
} | |||
} |
@@ -57,10 +57,10 @@ package org.aspectj.apache.bcel.generic; | |||
/** | |||
* Supplies empty method bodies to be overridden by subclasses. | |||
* | |||
* @version $Id: EmptyVisitor.java,v 1.2 2004/11/19 16:45:18 aclement Exp $ | |||
* @version $Id: EmptyVisitor.java,v 1.2.8.1 2007/02/09 10:45:09 aclement Exp $ | |||
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> | |||
*/ | |||
public abstract class EmptyVisitor implements Visitor { | |||
public class EmptyVisitor implements Visitor { | |||
public void visitStackInstruction(StackInstruction obj) { } | |||
public void visitLocalVariableInstruction(LocalVariableInstruction obj) { } | |||
public void visitBranchInstruction(BranchInstruction obj) { } |
@@ -58,6 +58,7 @@ import org.aspectj.apache.bcel.classfile.Utility; | |||
import java.util.HashSet; | |||
import java.util.Collection; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
/** | |||
* Instances of this class give users a handle to the instructions contained in | |||
@@ -71,7 +72,7 @@ import java.util.HashMap; | |||
* can traverse the list via an Enumeration returned by | |||
* InstructionList.elements(). | |||
* | |||
* @version $Id: InstructionHandle.java,v 1.2 2004/11/19 16:45:19 aclement Exp $ | |||
* @version $Id: InstructionHandle.java,v 1.2.8.1 2007/02/09 10:45:09 aclement Exp $ | |||
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> | |||
* @see Instruction | |||
* @see BranchHandle | |||
@@ -83,6 +84,10 @@ public class InstructionHandle implements java.io.Serializable { | |||
protected int i_position = -1; // byte code offset of instruction | |||
private HashSet targeters; | |||
private HashMap attributes; | |||
private int flags = 0; | |||
private static int IS_JUMP_DESTINATION_CALCULATED = 0x0001; | |||
private static int IS_JUMP_DESTINATION = 0x0002; | |||
public final InstructionHandle getNext() { return next; } | |||
public final InstructionHandle getPrev() { return prev; } | |||
@@ -136,6 +141,34 @@ public class InstructionHandle implements java.io.Serializable { | |||
return ih; | |||
} | |||
} | |||
/** | |||
* Return true if the instruction referenced is a jump destination, meaning it is targeted by a branch instruction or | |||
*/ | |||
public boolean isJumpDestination() { | |||
if ((flags&IS_JUMP_DESTINATION_CALCULATED)!=0) return ((flags&IS_JUMP_DESTINATION)!=0); | |||
if (targeters!=null) { | |||
for (Iterator iter = targeters.iterator(); iter.hasNext();) { | |||
InstructionTargeter targeter = (InstructionTargeter) iter.next(); | |||
if (targeter instanceof BranchInstruction || | |||
targeter instanceof CodeExceptionGen) { | |||
boolean catchBlockEntry =false; | |||
// Skip it if targeted just because exception handler catch block entry | |||
if (targeter instanceof CodeExceptionGen) { | |||
CodeExceptionGen ceg = (CodeExceptionGen)targeter; | |||
if (ceg.getHandlerPC().getPosition()!=getPosition()) catchBlockEntry=true; | |||
} | |||
if (!catchBlockEntry) { | |||
flags|=(IS_JUMP_DESTINATION_CALCULATED|IS_JUMP_DESTINATION); | |||
return true; | |||
} | |||
} | |||
} | |||
} | |||
flags|=IS_JUMP_DESTINATION_CALCULATED; | |||
return false; | |||
} | |||
/** | |||
* Called by InstructionList.setPositions when setting the position for every |
@@ -75,7 +75,7 @@ import java.util.ArrayList; | |||
* A list is finally dumped to a byte code array with <a | |||
* href="#getByteCode()">getByteCode</a>. | |||
* | |||
* @version $Id: InstructionList.java,v 1.4 2006/05/12 08:17:43 aclement Exp $ | |||
* @version $Id: InstructionList.java,v 1.4.4.1 2007/02/09 10:45:09 aclement Exp $ | |||
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> | |||
* @see Instruction | |||
* @see InstructionHandle | |||
@@ -956,6 +956,7 @@ public class InstructionList implements Serializable { | |||
Instruction i = ih.instruction; | |||
ih.setPosition(index); | |||
pos[count++] = index; | |||
index += i.getLength(); | |||
} |
@@ -70,11 +70,13 @@ import org.aspectj.apache.bcel.classfile.LineNumberTable; | |||
import org.aspectj.apache.bcel.classfile.LocalVariable; | |||
import org.aspectj.apache.bcel.classfile.LocalVariableTable; | |||
import org.aspectj.apache.bcel.classfile.Method; | |||
import org.aspectj.apache.bcel.classfile.StackMapTable; | |||
import org.aspectj.apache.bcel.classfile.Utility; | |||
import org.aspectj.apache.bcel.classfile.annotation.Annotation; | |||
import org.aspectj.apache.bcel.classfile.annotation.RuntimeAnnotations; | |||
import org.aspectj.apache.bcel.classfile.annotation.RuntimeParameterAnnotations; | |||
import org.aspectj.apache.bcel.generic.annotation.AnnotationGen; | |||
import org.aspectj.apache.bcel.verifier.utility.StackMapHelper; | |||
/** | |||
* Template class for building up a method. This is done by defining exception | |||
@@ -86,7 +88,7 @@ import org.aspectj.apache.bcel.generic.annotation.AnnotationGen; | |||
* use the `removeNOPs' method to get rid off them. | |||
* The resulting method object can be obtained via the `getMethod()' method. | |||
* | |||
* @version $Id: MethodGen.java,v 1.7 2006/02/21 10:49:15 aclement Exp $ | |||
* @version $Id: MethodGen.java,v 1.7.4.1 2007/02/09 10:45:09 aclement Exp $ | |||
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> | |||
* @author <A HREF="http://www.vmeng.com/beard">Patrick C. Beard</A> [setMaxStack()] | |||
* @see InstructionList | |||
@@ -703,6 +705,7 @@ public class MethodGen extends FieldGenOrMethodGen { | |||
if(il != null) | |||
byte_code = il.getByteCode(); | |||
// calculateStackMapTable(); | |||
LineNumberTable lnt = null; | |||
LocalVariableTable lvt = null; | |||
//J5TODO: LocalVariableTypeTable support! | |||
@@ -1144,5 +1147,41 @@ public class MethodGen extends FieldGenOrMethodGen { | |||
l.add(annotation); | |||
param_annotations[parameterIndex] = l; | |||
} | |||
} | |||
private Attribute findAttribute(String name, Attribute[] all) { | |||
for (int i = 0; i < all.length; i++) { | |||
if (all[i].getName().equals(name)) return all[i]; | |||
} | |||
return null; | |||
} | |||
private boolean debug = true; | |||
public void calculateStackMapTable() { | |||
if (il==null) return; // native method? | |||
// if (debug) System.out.println("> MethodGen.calculateStackMapTable() for "+getName()); | |||
// jsr check | |||
InstructionHandle iHandle = il.getStart(); | |||
boolean hasJsr = false; | |||
while (iHandle!=null && !hasJsr) { | |||
Instruction ins = iHandle.getInstruction(); | |||
if (ins instanceof JsrInstruction || ins instanceof JSR_W) { | |||
hasJsr = true; | |||
} | |||
iHandle = iHandle.getNext(); | |||
} | |||
if (hasJsr) { | |||
System.out.println("SKIPPING METHOD - CONTAINS JSR"); | |||
return; | |||
} | |||
StackMapTable stackmap = (StackMapTable)findAttribute("StackMapTable", getCodeAttributes()); | |||
//StackMapFrame[] smfs = stackmap.getStackMap(); // causes unpacking | |||
if (stackmap!=null) removeCodeAttribute(stackmap); | |||
StackMapTable smt = new StackMapHelper().produceStackMapTableAttribute(this); | |||
addCodeAttribute(smt); | |||
} | |||
} |
@@ -65,7 +65,7 @@ import org.aspectj.apache.bcel.classfile.Utility; | |||
* Abstract super class for all possible java types, namely basic types | |||
* such as int, object types like String and array types, e.g. int[] | |||
* | |||
* @version $Id: Type.java,v 1.7 2006/07/19 12:06:17 aclement Exp $ | |||
* @version $Id: Type.java,v 1.7.2.1 2007/02/09 10:45:09 aclement Exp $ | |||
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> | |||
* | |||
* modified: | |||
@@ -89,9 +89,11 @@ public abstract class Type implements java.io.Serializable { | |||
public static final ObjectType STRING = new ObjectType("java.lang.String"); | |||
public static final ObjectType STRINGBUFFER = new ObjectType("java.lang.StringBuffer"); | |||
public static final ObjectType THROWABLE = new ObjectType("java.lang.Throwable"); | |||
public static final ObjectType CLASS = new ObjectType("java.lang.Class"); | |||
public static final Type[] NO_ARGS = new Type[0]; | |||
public static final ReferenceType NULL = new ReferenceType(){}; | |||
public static final Type UNKNOWN = new Type(Constants.T_UNKNOWN,"<unknown object>"){}; | |||
public static final Type TOP = new Type(Constants.T_TOP,"<top>"){}; | |||
protected Type(byte t, String s) { | |||
type = t; |
@@ -59,6 +59,7 @@ import java.util.HashMap; | |||
import java.util.Iterator; | |||
import org.aspectj.apache.bcel.classfile.JavaClass; | |||
import org.aspectj.apache.bcel.classfile.Method; | |||
import org.aspectj.apache.bcel.verifier.statics.Pass1Verifier; | |||
import org.aspectj.apache.bcel.verifier.statics.Pass2Verifier; | |||
import org.aspectj.apache.bcel.verifier.statics.Pass3aVerifier; | |||
@@ -75,7 +76,7 @@ import org.aspectj.apache.bcel.verifier.structurals.Pass3bVerifier; | |||
* A Verifier creates PassVerifier instances to perform the actual verification. | |||
* Verifier instances are usually generated by the VerifierFactory. | |||
* | |||
* @version $Id: Verifier.java,v 1.4 2004/11/22 08:31:27 aclement Exp $ | |||
* @version $Id: Verifier.java,v 1.4.8.1 2007/02/09 10:45:09 aclement Exp $ | |||
* @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A> | |||
* @see org.aspectj.apache.bcel.verifier.VerifierFactory | |||
* @see org.aspectj.apache.bcel.verifier.PassVerifier | |||
@@ -122,6 +123,16 @@ public class Verifier{ | |||
} | |||
return p3av.verify(); | |||
} | |||
public VerificationResult doPass3a(String methodName) { | |||
Method[] ms = org.aspectj.apache.bcel.Repository.lookupClass(classname).getMethods(); | |||
int found=-1; | |||
for (int i = 0; i < ms.length; i++) { | |||
if (ms[i].getName().equals(methodName)) {found =i;break;} | |||
} | |||
if (found==-1) return null; | |||
return doPass3a(found); | |||
} | |||
/** Returns the VerificationResult for the given pass. */ | |||
public VerificationResult doPass3b(int method_no){ | |||
@@ -134,6 +145,16 @@ public class Verifier{ | |||
} | |||
return p3bv.verify(); | |||
} | |||
public VerificationResult doPass3b(String methodName) { | |||
Method[] ms = org.aspectj.apache.bcel.Repository.lookupClass(classname).getMethods(); | |||
int found=-1; | |||
for (int i = 0; i < ms.length; i++) { | |||
if (ms[i].getName().equals(methodName)) {found =i;break;} | |||
} | |||
if (found==-1) return null; | |||
return doPass3b(found); | |||
} | |||
/** |
@@ -59,7 +59,7 @@ import org.aspectj.apache.bcel.generic.Type; | |||
/** | |||
* This class represents the upper half of a DOUBLE variable. | |||
* @version $Id: DOUBLE_Upper.java,v 1.2 2004/11/19 16:45:19 aclement Exp $ | |||
* @version $Id: DOUBLE_Upper.java,v 1.2.8.1 2007/02/09 10:45:09 aclement Exp $ | |||
* @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A> | |||
*/ | |||
public final class DOUBLE_Upper extends Type{ | |||
@@ -69,7 +69,7 @@ public final class DOUBLE_Upper extends Type{ | |||
/** The constructor; this class must not be instantiated from the outside. */ | |||
private DOUBLE_Upper(){ | |||
super(Constants.T_UNKNOWN, "Long_Upper"); | |||
super(Constants.T_TOP, "Long_Upper"); | |||
} | |||
/** Use this method to get the single instance of this class. */ |
@@ -59,7 +59,7 @@ import org.aspectj.apache.bcel.generic.Type; | |||
/** | |||
* This class represents the upper half of a LONG variable. | |||
* @version $Id: LONG_Upper.java,v 1.2 2004/11/19 16:45:19 aclement Exp $ | |||
* @version $Id: LONG_Upper.java,v 1.2.8.1 2007/02/09 10:45:09 aclement Exp $ | |||
* @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A> | |||
*/ | |||
public final class LONG_Upper extends Type{ | |||
@@ -69,7 +69,7 @@ public final class LONG_Upper extends Type{ | |||
/** The constructor; this class must not be instantiated from the outside. */ | |||
private LONG_Upper(){ | |||
super(Constants.T_UNKNOWN, "Long_Upper"); | |||
super(Constants.T_TOP, "Long_Upper"); | |||
} | |||
/** Use this method to get the single instance of this class. */ |
@@ -71,6 +71,7 @@ import org.aspectj.apache.bcel.classfile.ConstantString; | |||
import org.aspectj.apache.bcel.classfile.ConstantUtf8; | |||
import org.aspectj.apache.bcel.classfile.ConstantValue; | |||
import org.aspectj.apache.bcel.classfile.Deprecated; | |||
import org.aspectj.apache.bcel.classfile.EnclosingMethod; | |||
import org.aspectj.apache.bcel.classfile.ExceptionTable; | |||
import org.aspectj.apache.bcel.classfile.Field; | |||
import org.aspectj.apache.bcel.classfile.InnerClass; | |||
@@ -85,7 +86,8 @@ import org.aspectj.apache.bcel.classfile.Method; | |||
import org.aspectj.apache.bcel.classfile.Node; | |||
import org.aspectj.apache.bcel.classfile.Signature; | |||
import org.aspectj.apache.bcel.classfile.SourceFile; | |||
import org.aspectj.apache.bcel.classfile.StackMap; | |||
import org.aspectj.apache.bcel.classfile.StackMapFrame; | |||
import org.aspectj.apache.bcel.classfile.StackMapTable; | |||
import org.aspectj.apache.bcel.classfile.Synthetic; | |||
import org.aspectj.apache.bcel.classfile.Unknown; | |||
import org.aspectj.apache.bcel.classfile.Visitor; | |||
@@ -107,7 +109,7 @@ import org.aspectj.apache.bcel.verifier.exc.AssertionViolatedException; | |||
* Note that this class also serves as a placeholder for more sophisticated message | |||
* handling in future versions of JustIce. | |||
* | |||
* @version $Id: StringRepresentation.java,v 1.4 2006/07/04 16:57:42 aclement Exp $ | |||
* @version $Id: StringRepresentation.java,v 1.4.2.1 2007/02/09 10:45:09 aclement Exp $ | |||
* @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A> | |||
*/ | |||
public class StringRepresentation extends org.aspectj.apache.bcel.classfile.EmptyVisitor implements Visitor{ | |||
@@ -249,9 +251,9 @@ public class StringRepresentation extends org.aspectj.apache.bcel.classfile.Empt | |||
public void visitSourceFile(SourceFile obj){ | |||
tostring = toString(obj); | |||
} | |||
public void visitStackMap(StackMap obj){ | |||
tostring = toString(obj); | |||
} | |||
public void visitStackMap(StackMapTable obj){ | |||
tostring = toString(obj); | |||
} | |||
public void visitSynthetic(Synthetic obj){ | |||
tostring = toString(obj); | |||
} |
@@ -74,7 +74,7 @@ import org.aspectj.apache.bcel.verifier.exc.StructuralCodeConstraintException; | |||
/** | |||
* This class represents a control flow graph of a method. | |||
* | |||
* @version $Id: ControlFlowGraph.java,v 1.4 2004/11/22 08:31:27 aclement Exp $ | |||
* @version $Id: ControlFlowGraph.java,v 1.4.8.1 2007/02/09 10:45:08 aclement Exp $ | |||
* @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A> | |||
*/ | |||
public class ControlFlowGraph{ | |||
@@ -213,7 +213,8 @@ public class ControlFlowGraph{ | |||
// instruction, but does not modify the workingFrame object. | |||
//InstConstraintVisitor icv = InstConstraintVisitor.getInstance(VerifierFactory.getVerifier(method_gen.getClassName())); | |||
icv.setFrame(workingFrame); | |||
getInstruction().accept(icv); | |||
// will go bang... | |||
// getInstruction().accept(icv); | |||
} | |||
catch(StructuralCodeConstraintException ce){ | |||
ce.extendMessage("","\nInstructionHandle: "+getInstruction()+"\n"); |
@@ -86,7 +86,7 @@ import org.aspectj.apache.bcel.generic.*; | |||
* If a two-slot type is stored into a local variable, the next variable | |||
* is given the type Type.UNKNOWN. | |||
* | |||
* @version $Id: ExecutionVisitor.java,v 1.5 2005/02/02 09:11:39 aclement Exp $ | |||
* @version $Id: ExecutionVisitor.java,v 1.5.6.1 2007/02/09 10:45:08 aclement Exp $ | |||
* @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A> | |||
* @see #visitDSTORE(DSTORE o) | |||
* @see InstConstraintVisitor | |||
@@ -335,7 +335,7 @@ public class ExecutionVisitor extends EmptyVisitor implements Visitor{ | |||
/** Symbolically executes the corresponding Java Virtual Machine instruction. */ | |||
public void visitDSTORE(DSTORE o){ | |||
locals().set(o.getIndex(), stack().pop()); | |||
locals().set(o.getIndex()+1, Type.UNKNOWN); | |||
locals().set(o.getIndex()+1, Type.TOP); | |||
} | |||
/** Symbolically executes the corresponding Java Virtual Machine instruction. */ | |||
public void visitDSUB(DSUB o){ | |||
@@ -1014,7 +1014,7 @@ public class ExecutionVisitor extends EmptyVisitor implements Visitor{ | |||
/** Symbolically executes the corresponding Java Virtual Machine instruction. */ | |||
public void visitLSTORE(LSTORE o){ | |||
locals().set(o.getIndex(), stack().pop()); | |||
locals().set(o.getIndex()+1, Type.UNKNOWN); | |||
locals().set(o.getIndex()+1, Type.TOP); | |||
} | |||
/** Symbolically executes the corresponding Java Virtual Machine instruction. */ | |||
public void visitLSUB(LSUB o){ |
@@ -79,7 +79,7 @@ import org.aspectj.apache.bcel.verifier.exc.*; | |||
* TODO: Currently, the JVM's behaviour concerning monitors (MONITORENTER, | |||
* MONITOREXIT) is not modeled in JustIce. | |||
* | |||
* @version $Id: InstConstraintVisitor.java,v 1.4 2005/02/02 09:11:39 aclement Exp $ | |||
* @version $Id: InstConstraintVisitor.java,v 1.4.6.1 2007/02/09 10:45:08 aclement Exp $ | |||
* @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A> | |||
* @see org.aspectj.apache.bcel.verifier.exc.StructuralCodeConstraintException | |||
* @see org.aspectj.apache.bcel.verifier.exc.LinkingConstraintException | |||
@@ -234,12 +234,12 @@ public class InstConstraintVisitor extends EmptyVisitor implements org.aspectj.a | |||
private void _visitStackAccessor(Instruction o){ | |||
int consume = o.consumeStack(cpg); // Stack values are always consumed first; then produced. | |||
if (consume > stack().slotsUsed()){ | |||
constraintViolated((Instruction) o, "Cannot consume "+consume+" stack slots: only "+stack().slotsUsed()+" slot(s) left on stack!\nStack:\n"+stack()); | |||
constraintViolated(o, "Cannot consume "+consume+" stack slots: only "+stack().slotsUsed()+" slot(s) left on stack!\nStack:\n"+stack()); | |||
} | |||
int produce = o.produceStack(cpg) - ((Instruction) o).consumeStack(cpg); // Stack values are always consumed first; then produced. | |||
int produce = o.produceStack(cpg) - (o).consumeStack(cpg); // Stack values are always consumed first; then produced. | |||
if ( produce + stack().slotsUsed() > stack().maxStack() ){ | |||
constraintViolated((Instruction) o, "Cannot produce "+produce+" stack slots: only "+(stack().maxStack()-stack().slotsUsed())+" free stack slot(s) left.\nStack:\n"+stack()); | |||
constraintViolated(o, "Cannot produce "+produce+" stack slots: only "+(stack().maxStack()-stack().slotsUsed())+" free stack slot(s) left.\nStack:\n"+stack()); | |||
} | |||
} | |||
@@ -1645,7 +1645,7 @@ public class InstConstraintVisitor extends EmptyVisitor implements org.aspectj.a | |||
Verifier v = VerifierFactory.getVerifier( name ); | |||
VerificationResult vr = v.doPass2(); | |||
if (vr.getStatus() != VerificationResult.VERIFIED_OK){ | |||
constraintViolated((Instruction) o, "Class '"+name+"' is referenced, but cannot be loaded and resolved: '"+vr+"'."); | |||
constraintViolated(o, "Class '"+name+"' is referenced, but cannot be loaded and resolved: '"+vr+"'."); | |||
} | |||
} | |||
@@ -1729,7 +1729,7 @@ public class InstConstraintVisitor extends EmptyVisitor implements org.aspectj.a | |||
Verifier v = VerifierFactory.getVerifier( name ); | |||
VerificationResult vr = v.doPass2(); | |||
if (vr.getStatus() != VerificationResult.VERIFIED_OK){ | |||
constraintViolated((Instruction) o, "Class '"+name+"' is referenced, but cannot be loaded and resolved: '"+vr+"'."); | |||
constraintViolated( o, "Class '"+name+"' is referenced, but cannot be loaded and resolved: '"+vr+"'."); | |||
} | |||
} | |||
@@ -1810,7 +1810,7 @@ public class InstConstraintVisitor extends EmptyVisitor implements org.aspectj.a | |||
Verifier v = VerifierFactory.getVerifier( name ); | |||
VerificationResult vr = v.doPass2(); | |||
if (vr.getStatus() != VerificationResult.VERIFIED_OK){ | |||
constraintViolated((Instruction) o, "Class '"+name+"' is referenced, but cannot be loaded and resolved: '"+vr+"'."); | |||
constraintViolated(o, "Class '"+name+"' is referenced, but cannot be loaded and resolved: '"+vr+"'."); | |||
} | |||
} | |||
@@ -1855,7 +1855,7 @@ public class InstConstraintVisitor extends EmptyVisitor implements org.aspectj.a | |||
Verifier v = VerifierFactory.getVerifier( name ); | |||
VerificationResult vr = v.doPass2(); | |||
if (vr.getStatus() != VerificationResult.VERIFIED_OK){ | |||
constraintViolated((Instruction) o, "Class '"+name+"' is referenced, but cannot be loaded and resolved: '"+vr+"'."); | |||
constraintViolated( o, "Class '"+name+"' is referenced, but cannot be loaded and resolved: '"+vr+"'."); | |||
} | |||
} | |||
@@ -62,7 +62,7 @@ import org.aspectj.apache.bcel.verifier.exc.*; | |||
* This class implements an array of local variables used for symbolic JVM | |||
* simulation. | |||
* | |||
* @version $Id: LocalVariables.java,v 1.2 2004/11/19 16:45:19 aclement Exp $ | |||
* @version $Id: LocalVariables.java,v 1.2.8.1 2007/02/09 10:45:08 aclement Exp $ | |||
* @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A> | |||
*/ | |||
public class LocalVariables{ | |||
@@ -75,7 +75,7 @@ public class LocalVariables{ | |||
public LocalVariables(int maxLocals){ | |||
locals = new Type[maxLocals]; | |||
for (int i=0; i<maxLocals; i++){ | |||
locals[i] = Type.UNKNOWN; | |||
locals[i] = Type.TOP; | |||
} | |||
} | |||
@@ -122,6 +122,9 @@ public class LocalVariables{ | |||
if (type == Type.BYTE || type == Type.SHORT || type == Type.BOOLEAN || type == Type.CHAR){ | |||
throw new AssertionViolatedException("LocalVariables do not know about '"+type+"'. Use Type.INT instead."); | |||
} | |||
if (type==Type.TOP) { | |||
int stop =1; | |||
} | |||
locals[i] = type; | |||
} | |||
@@ -57,7 +57,6 @@ package org.aspectj.apache.bcel.verifier.structurals; | |||
import java.io.PrintWriter; | |||
import java.io.StringWriter; | |||
import java.util.ArrayList; | |||
import java.util.Random; | |||
import java.util.Vector; | |||
import org.aspectj.apache.bcel.Constants; | |||
@@ -86,7 +85,7 @@ import org.aspectj.apache.bcel.verifier.exc.VerifierConstraintViolatedException; | |||
* More detailed information is to be found at the do_verify() method's | |||
* documentation. | |||
* | |||
* @version $Id: Pass3bVerifier.java,v 1.5 2005/02/02 09:11:39 aclement Exp $ | |||
* @version $Id: Pass3bVerifier.java,v 1.5.6.1 2007/02/09 10:45:08 aclement Exp $ | |||
* @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A> | |||
* @see #do_verify() | |||
*/ | |||
@@ -155,44 +154,34 @@ public final class Pass3bVerifier extends PassVerifier{ | |||
myOwner = owner; | |||
this.method_no = method_no; | |||
} | |||
public Pass3bVerifier() {} | |||
/** | |||
* Whenever the outgoing frame | |||
* situation of an InstructionContext changes, all its successors are | |||
* put [back] into the queue [as if they were unvisited]. | |||
* The proof of termination is about the existence of a | |||
* fix point of frame merging. | |||
* Whenever the outgoing frame situation of an InstructionContext changes, | |||
* all its successors are put [back] into the queue [as if they were unvisited]. | |||
* The proof of termination is about the existence of a fix point of frame merging. | |||
*/ | |||
private void circulationPump(ControlFlowGraph cfg, InstructionContext start, Frame vanillaFrame, InstConstraintVisitor icv, ExecutionVisitor ev){ | |||
final Random random = new Random(); | |||
InstructionContextQueue icq = new InstructionContextQueue(); | |||
private void circulationPump(ControlFlowGraph cfg, InstructionContext start, | |||
Frame vanillaFrame, InstConstraintVisitor icv, ExecutionVisitor ev){ | |||
InstructionContextQueue contextQueue = new InstructionContextQueue(); | |||
start.execute(vanillaFrame, new ArrayList(), icv, ev); // new ArrayList() <=> no Instruction was executed before | |||
// => Top-Level routine (no jsr call before) | |||
icq.add(start, new ArrayList()); | |||
// => Top-Level routine (no jsr call before) | |||
contextQueue.add(start, new ArrayList()); | |||
// LOOP! | |||
while (!icq.isEmpty()){ | |||
InstructionContext u; | |||
ArrayList ec; | |||
if (!DEBUG){ | |||
int r = random.nextInt(icq.size()); | |||
u = icq.getIC(r); | |||
ec = icq.getEC(r); | |||
icq.remove(r); | |||
} | |||
else{ | |||
u = icq.getIC(0); | |||
ec = icq.getEC(0); | |||
icq.remove(0); | |||
} | |||
while (!contextQueue.isEmpty()){ | |||
InstructionContext u = contextQueue.getIC(0); | |||
ArrayList ec = contextQueue.getEC(0); | |||
contextQueue.remove(0); | |||
ArrayList oldchain = (ArrayList) (ec.clone()); | |||
ArrayList newchain = (ArrayList) (ec.clone()); | |||
newchain.add(u); | |||
if ((u.getInstruction().getInstruction()) instanceof RET){ | |||
//System.err.println(u); | |||
//System.err.println(u); | |||
// We can only follow _one_ successor, the one after the | |||
// JSR that was recently executed. | |||
RET ret = (RET) (u.getInstruction().getInstruction()); | |||
@@ -206,7 +195,7 @@ public final class Pass3bVerifier extends PassVerifier{ | |||
if (skip_jsr < 0){ | |||
throw new AssertionViolatedException("More RET than JSR in execution chain?!"); | |||
} | |||
//System.err.println("+"+oldchain.get(ss)); | |||
//System.err.println("+"+oldchain.get(ss)); | |||
if (((InstructionContext) oldchain.get(ss)).getInstruction().getInstruction() instanceof JsrInstruction){ | |||
if (skip_jsr == 0){ | |||
lastJSR = (InstructionContext) oldchain.get(ss); | |||
@@ -229,7 +218,7 @@ public final class Pass3bVerifier extends PassVerifier{ | |||
} | |||
if (theSuccessor.execute(u.getOutFrame(oldchain), newchain, icv, ev)){ | |||
icq.add(theSuccessor, (ArrayList) newchain.clone()); | |||
contextQueue.add(theSuccessor, (ArrayList) newchain.clone()); | |||
} | |||
} | |||
else{// "not a ret" | |||
@@ -239,7 +228,7 @@ public final class Pass3bVerifier extends PassVerifier{ | |||
for (int s=0; s<succs.length; s++){ | |||
InstructionContext v = succs[s]; | |||
if (v.execute(u.getOutFrame(oldchain), newchain, icv, ev)){ | |||
icq.add(v, (ArrayList) newchain.clone()); | |||
contextQueue.add(v, (ArrayList) newchain.clone()); | |||
} | |||
} | |||
}// end "not a ret" | |||
@@ -260,7 +249,7 @@ public final class Pass3bVerifier extends PassVerifier{ | |||
//if (v.execute(new Frame(u.getOutFrame(oldchain).getLocals(), new OperandStack (u.getOutFrame().getStack().maxStack(), (exc_hds[s].getExceptionType()==null? Type.THROWABLE : exc_hds[s].getExceptionType())) ), newchain), icv, ev){ | |||
//icq.add(v, (ArrayList) newchain.clone()); | |||
if (v.execute(new Frame(u.getOutFrame(oldchain).getLocals(), new OperandStack (u.getOutFrame(oldchain).getStack().maxStack(), (exc_hds[s].getExceptionType()==null? Type.THROWABLE : exc_hds[s].getExceptionType())) ), new ArrayList(), icv, ev)){ | |||
icq.add(v, new ArrayList()); | |||
contextQueue.add(v, new ArrayList()); | |||
} | |||
} | |||
@@ -370,6 +359,8 @@ public final class Pass3bVerifier extends PassVerifier{ | |||
} | |||
return VerificationResult.VR_OK; | |||
} | |||
/** Returns the method number as supplied when instantiating. */ | |||
public int getMethodNo(){ |
@@ -0,0 +1,140 @@ | |||
package org.aspectj.apache.bcel.verifier.utility; | |||
/* ==================================================================== | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2001 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, | |||
* if any, must include the following acknowledgment: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowledgment may appear in the software itself, | |||
* if and wherever such third-party acknowledgments normally appear. | |||
* | |||
* 4. The names "Apache" and "Apache Software Foundation" and | |||
* "Apache BCEL" must not be used to endorse or promote products | |||
* derived from this software without prior written permission. For | |||
* written permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache", | |||
* "Apache BCEL", nor may "Apache" appear in their name, without | |||
* prior written permission of the Apache Software Foundation. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
import java.util.Hashtable; | |||
import org.aspectj.apache.bcel.generic.InstructionHandle; | |||
import org.aspectj.apache.bcel.generic.MethodGen; | |||
import org.aspectj.apache.bcel.verifier.exc.AssertionViolatedException; | |||
/** | |||
* This class represents a control flow graph of a method. | |||
* | |||
* @version $Id$ | |||
* @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A> | |||
*/ | |||
public class ControlFlowGraph { | |||
// The MethodGen object we're working on | |||
private final MethodGen method; | |||
// The Subroutines for the method | |||
final Subroutines subroutines; | |||
// The ExceptionHandlers object for the method | |||
final ExceptionHandlers exceptionhandlers; | |||
// All InstructionContext instances of this ControlFlowGraph | |||
private Hashtable /*InstructionHandle > InstructionContextImpl*/ instructionContexts = new Hashtable(); | |||
// --- | |||
public Subroutines getSubroutines() { | |||
return subroutines; | |||
} | |||
public ControlFlowGraph(MethodGen method){ | |||
subroutines = new Subroutines(method); | |||
exceptionhandlers = new ExceptionHandlers(method); | |||
InstructionHandle[] instructionhandles = method.getInstructionList().getInstructionHandles(); | |||
for (int i=0; i<instructionhandles.length; i++) { | |||
instructionContexts.put(instructionhandles[i], new InstructionContextImpl(this, instructionhandles[i])); | |||
} | |||
this.method = method; | |||
} | |||
/** | |||
* Returns the InstructionContext of a given instruction. | |||
*/ | |||
public InstructionContext contextOf(InstructionHandle inst){ | |||
InstructionContext ic = (InstructionContext) instructionContexts.get(inst); | |||
if (ic == null){ | |||
throw new AssertionViolatedException("InstructionContext requested for an InstructionHandle that's not known!"); | |||
} | |||
return ic; | |||
} | |||
/** | |||
* Returns the InstructionContext[] of a given InstructionHandle[], | |||
* in a naturally ordered manner. | |||
*/ | |||
public InstructionContext[] contextsOf(InstructionHandle[] insts){ | |||
InstructionContext[] ret = new InstructionContext[insts.length]; | |||
for (int i=0; i<insts.length; i++){ | |||
ret[i] = contextOf(insts[i]); | |||
} | |||
return ret; | |||
} | |||
/** | |||
* Returns an InstructionContext[] with all the InstructionContext instances | |||
* for the method whose control flow is represented by this ControlFlowGraph | |||
* <B>(NOT ORDERED!)</B>. | |||
*/ | |||
public InstructionContext[] getInstructionContexts(){ | |||
InstructionContext[] ret = new InstructionContext[instructionContexts.values().size()]; | |||
return (InstructionContext[]) instructionContexts.values().toArray(ret); | |||
} | |||
/** | |||
* Returns true, if and only if the said instruction is not reachable; that means, | |||
* if it not part of this ControlFlowGraph. | |||
*/ | |||
public boolean isDead(InstructionHandle i){ | |||
return instructionContexts.containsKey(i); | |||
} | |||
} |
@@ -0,0 +1,93 @@ | |||
package org.aspectj.apache.bcel.verifier.utility; | |||
/* ==================================================================== | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2001 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, | |||
* if any, must include the following acknowledgment: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowledgment may appear in the software itself, | |||
* if and wherever such third-party acknowledgments normally appear. | |||
* | |||
* 4. The names "Apache" and "Apache Software Foundation" and | |||
* "Apache BCEL" must not be used to endorse or promote products | |||
* derived from this software without prior written permission. For | |||
* written permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache", | |||
* "Apache BCEL", nor may "Apache" appear in their name, without | |||
* prior written permission of the Apache Software Foundation. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
import org.aspectj.apache.bcel.generic.*; | |||
/** | |||
* This class represents an exception handler; that is, an ObjectType | |||
* representing a subclass of java.lang.Throwable and the instruction | |||
* the handler starts off (represented by an InstructionContext). | |||
* | |||
* @version $Id$ | |||
* @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A> | |||
*/ | |||
public class ExceptionHandler{ | |||
/** The type of the exception to catch. NULL means ANY. */ | |||
private ObjectType catchtype; | |||
/** The InstructionHandle where the handling begins. */ | |||
private InstructionHandle handlerpc; | |||
/** Leave instance creation to JustIce. */ | |||
ExceptionHandler(ObjectType catch_type, InstructionHandle handler_pc){ | |||
catchtype = catch_type; | |||
handlerpc = handler_pc; | |||
} | |||
/** | |||
* Returns the type of the exception that's handled. <B>'null' means 'ANY'.</B> | |||
*/ | |||
public ObjectType getExceptionType(){ | |||
return catchtype; | |||
} | |||
/** | |||
* Returns the InstructionHandle where the handler starts off. | |||
*/ | |||
public InstructionHandle getHandlerStart(){ | |||
return handlerpc; | |||
} | |||
} |
@@ -0,0 +1,112 @@ | |||
package org.aspectj.apache.bcel.verifier.utility; | |||
/* ==================================================================== | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2001 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, | |||
* if any, must include the following acknowledgment: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowledgment may appear in the software itself, | |||
* if and wherever such third-party acknowledgments normally appear. | |||
* | |||
* 4. The names "Apache" and "Apache Software Foundation" and | |||
* "Apache BCEL" must not be used to endorse or promote products | |||
* derived from this software without prior written permission. For | |||
* written permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache", | |||
* "Apache BCEL", nor may "Apache" appear in their name, without | |||
* prior written permission of the Apache Software Foundation. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
import org.aspectj.apache.bcel.generic.*; | |||
import java.util.HashSet; | |||
import java.util.Hashtable; | |||
/** | |||
* This class allows easy access to ExceptionHandler objects. | |||
* | |||
* @version $Id$ | |||
* @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A> | |||
*/ | |||
public class ExceptionHandlers{ | |||
/** | |||
* The ExceptionHandler instances. | |||
* Key: InstructionHandle objects, Values: HashSet<ExceptionHandler> instances. | |||
*/ | |||
private Hashtable exceptionhandlers; | |||
/** | |||
* Constructor. Creates a new ExceptionHandlers instance. | |||
*/ | |||
public ExceptionHandlers(MethodGen mg){ | |||
exceptionhandlers = new Hashtable(); | |||
CodeExceptionGen[] cegs = mg.getExceptionHandlers(); | |||
for (int i=0; i<cegs.length; i++){ | |||
ExceptionHandler eh = new ExceptionHandler(cegs[i].getCatchType(), cegs[i].getHandlerPC()); | |||
for (InstructionHandle ih=cegs[i].getStartPC(); ih != cegs[i].getEndPC().getNext(); ih=ih.getNext()) { | |||
if (ih==cegs[i].getStartPC() || | |||
ih.getInstruction() instanceof ExceptionThrower || | |||
ih.getInstruction() instanceof ASTORE) { | |||
HashSet hs; | |||
hs = (HashSet) exceptionhandlers.get(ih); | |||
if (hs == null){ | |||
hs = new HashSet(); | |||
exceptionhandlers.put(ih, hs); | |||
} | |||
hs.add(eh); | |||
} | |||
} | |||
} | |||
// System.out.println("ExceptionHandler size is "+exceptionhandlers.keySet().size()); | |||
} | |||
/** | |||
* Returns all the ExceptionHandler instances representing exception | |||
* handlers that protect the instruction ih. | |||
*/ | |||
public ExceptionHandler[] getExceptionHandlers(InstructionHandle ih){ | |||
HashSet hs = (HashSet) exceptionhandlers.get(ih); | |||
if (hs == null) return new ExceptionHandler[0]; | |||
else{ | |||
ExceptionHandler[] ret = new ExceptionHandler[hs.size()]; | |||
return (ExceptionHandler[]) (hs.toArray(ret)); | |||
} | |||
} | |||
} |
@@ -0,0 +1,375 @@ | |||
package org.aspectj.apache.bcel.verifier.utility; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.aspectj.apache.bcel.Constants; | |||
import org.aspectj.apache.bcel.classfile.ConstantClass; | |||
import org.aspectj.apache.bcel.classfile.ConstantPool; | |||
import org.aspectj.apache.bcel.classfile.StackMapFrame; | |||
import org.aspectj.apache.bcel.classfile.StackMapType; | |||
import org.aspectj.apache.bcel.generic.ArrayType; | |||
import org.aspectj.apache.bcel.generic.BasicType; | |||
import org.aspectj.apache.bcel.generic.ConstantPoolGen; | |||
import org.aspectj.apache.bcel.generic.ObjectType; | |||
import org.aspectj.apache.bcel.generic.Type; | |||
import org.aspectj.apache.bcel.generic.Type.TypeHolder; | |||
/* ==================================================================== | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2001 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, | |||
* if any, must include the following acknowledgment: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowledgment may appear in the software itself, | |||
* if and wherever such third-party acknowledgments normally appear. | |||
* | |||
* 4. The names "Apache" and "Apache Software Foundation" and | |||
* "Apache BCEL" must not be used to endorse or promote products | |||
* derived from this software without prior written permission. For | |||
* written permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache", | |||
* "Apache BCEL", nor may "Apache" appear in their name, without | |||
* prior written permission of the Apache Software Foundation. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
/** | |||
* This class represents a JVM execution frame; that means, a local variable array and an operand stack. | |||
* | |||
* @version $Id$ | |||
* @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A> | |||
*/ | |||
public class Frame { | |||
/** | |||
* For instance initialization methods, it is important to remember | |||
* which instance it is that is not initialized yet. It will be | |||
* initialized invoking another constructor later. | |||
* NULL means the instance already *is* initialized. | |||
*/ | |||
protected static UninitializedObjectType _this; | |||
private LocalVariables locals; | |||
private OperandStack stack; | |||
public int position; // used for stackmapframe creation | |||
// --- | |||
public Frame(int maxLocals, int maxStack){ | |||
locals = new LocalVariables(maxLocals); | |||
stack = new OperandStack(maxStack); | |||
} | |||
public Frame(LocalVariables locals, OperandStack stack){ | |||
this.locals = locals; | |||
this.stack = stack; | |||
} | |||
// === | |||
protected Object clone() { | |||
Frame f = new Frame(locals.getClone(), stack.getClone()); | |||
return f; | |||
} | |||
public Frame getClone() { | |||
return (Frame) clone(); | |||
} | |||
public LocalVariables getLocals() { return locals; } | |||
public OperandStack getStack() { return stack; } | |||
public boolean equals(Object o){ | |||
if (!(o instanceof Frame)) return false; // implies "null" is non-equal. | |||
Frame f = (Frame) o; | |||
return this.stack.equals(f.stack) && this.locals.equals(f.locals); | |||
} | |||
/** | |||
* Returns a String representation of the Frame instance. | |||
*/ | |||
public String toString(){ | |||
StringBuffer sb = new StringBuffer(); | |||
sb.append("FRAMELOCS"+locals.toCompactString()).append("\n"); | |||
sb.append("FRAMESTAK"+stack.toCompactString()).append("\n"); | |||
return sb.toString(); | |||
} | |||
public String toString(String prefix){ | |||
StringBuffer sb = new StringBuffer(); | |||
sb.append(prefix).append("FRAMELOCS"+locals.toCompactString()).append("\n"); | |||
sb.append(prefix).append("FRAMESTAK"+stack.toCompactString()); | |||
return sb.toString(); | |||
} | |||
private int addLocal(int pos,StackMapType smType) { | |||
byte itemkey = smType.getType(); | |||
ConstantPool cp = smType.getConstantPool(); | |||
if (debug) System.out.println(">>"+Constants.ITEM_NAMES[itemkey]); | |||
switch (itemkey) { | |||
// single slot items | |||
case Constants.ITEM_Integer: | |||
locals.set(pos++, BasicType.INT); | |||
return 1; | |||
case Constants.ITEM_Object: | |||
ConstantClass c = (ConstantClass)cp.getConstant(smType.getIndex(), Constants.CONSTANT_Class); | |||
String sig = c.getBytes(cp); | |||
if (sig.charAt(0)!='[') sig = "L"+sig+";"; | |||
TypeHolder th = Type.getTypeInternal(sig); | |||
locals.set(pos++,th.getType());//new ObjectType(c.getBytes(cp))); | |||
return 1; | |||
case Constants.ITEM_Top: | |||
locals.set(pos++,Type.TOP/*Top - why is it not Top? */);return 1; | |||
case Constants.ITEM_Float: | |||
case Constants.ITEM_Null: | |||
case Constants.ITEM_UninitializedThis: | |||
case Constants.ITEM_Uninitialized: | |||
// double slot items | |||
case Constants.ITEM_Double: | |||
case Constants.ITEM_Long: | |||
throw new RuntimeException("Frame.addLocal() - need to implement support for "+Constants.ITEM_NAMES[itemkey]); | |||
} | |||
return 0; | |||
} | |||
private int addStack(StackMapType smType) { | |||
byte itemkey = smType.getType(); | |||
ConstantPool cp = smType.getConstantPool(); | |||
if (debug) System.out.println(">>"+Constants.ITEM_NAMES[itemkey]); | |||
switch (itemkey) { | |||
// single slot items | |||
case Constants.ITEM_Integer: | |||
stack.push(BasicType.INT); | |||
return 1; | |||
case Constants.ITEM_Object: | |||
ConstantClass c = (ConstantClass)cp.getConstant(smType.getIndex(), Constants.CONSTANT_Class); | |||
stack.push(new ObjectType(c.getBytes(cp))); | |||
return 1; | |||
case Constants.ITEM_Top: | |||
case Constants.ITEM_Float: | |||
case Constants.ITEM_Null: | |||
case Constants.ITEM_UninitializedThis: | |||
case Constants.ITEM_Uninitialized: | |||
// double slot items | |||
case Constants.ITEM_Double: | |||
case Constants.ITEM_Long: | |||
throw new RuntimeException("Frame.addStack() - need to implement support for "+Constants.ITEM_NAMES[itemkey]); | |||
} | |||
return 0; | |||
} | |||
private boolean debug=true; | |||
/** Applies a StackMapFrame to this Frame */ | |||
public boolean apply(StackMapFrame frame) { | |||
if (debug) System.out.println(">> Applying a stackmapframe: "+frame); | |||
int newLocalEntryCount,newStackEntryCount; | |||
StackMapType[] localEntries,stackEntries; | |||
switch (frame.getKind()) { | |||
// Same locals, empty stack | |||
case StackMapFrame.SameFrameKind: stack.clear();return true; | |||
// Same locals, empty stack | |||
case StackMapFrame.SameFrameExtendedKind: stack.clear();return true; | |||
// Empty stack, additional locals specified | |||
case StackMapFrame.AppendFrameKind: | |||
newLocalEntryCount = frame.getNumberOfLocals(); | |||
localEntries = frame.getTypesOfLocals(); | |||
if (debug) System.out.println("AppendFrame - "+newLocalEntryCount+" new locals"); | |||
int pos = locals.getNextUnset(); | |||
locals.haveSet(newLocalEntryCount+pos); | |||
for (int i = 0; i < newLocalEntryCount; i++) pos+=addLocal(pos,localEntries[i]); | |||
stack.clear(); | |||
return true; | |||
case StackMapFrame.FullFrameKind: | |||
newLocalEntryCount = frame.getNumberOfLocals(); | |||
newStackEntryCount = frame.getNumberOfStackItems(); | |||
localEntries = frame.getTypesOfLocals(); | |||
locals.flush(); | |||
stackEntries = frame.getTypesOfStackItems(); | |||
if (debug) System.out.println("FullFrame - "+newLocalEntryCount+" new locals"); | |||
if (debug) System.out.println("Fixing locals"); | |||
pos=0; | |||
for (int i = 0; i < newLocalEntryCount; i++) pos+=addLocal(pos,localEntries[i]); | |||
stack.clear(); | |||
locals.haveSet(newLocalEntryCount); | |||
if (debug) System.out.println("Fixing stack"); | |||
for (int i = 0; i < newStackEntryCount; i++) pos+=addStack(stackEntries[i]); | |||
return true; | |||
case StackMapFrame.SameLocalsOneStackItemFrameKind: | |||
stack.clear(); | |||
stackEntries = frame.getTypesOfStackItems(); | |||
if (stackEntries.length!=1) throw new RuntimeException("Assertion failed: should be length 1 but is length "+stackEntries.length); | |||
// assert(stackEntries.length==1) | |||
addStack(stackEntries[0]); | |||
return true; | |||
case StackMapFrame.ChopFrameKind: | |||
stack.clear(); | |||
// XXX is this the last X number of known ones or the last X of the maxLocals() number? | |||
// From methodTypesToSignature in Utility - there are two chop frames, so it it must be chopping 'the last X we currently know about' | |||
pos = locals.getNextUnset(); | |||
if (pos==-1) pos=locals.maxLocals(); | |||
locals.haveSet(locals.getNextUnset()-frame.getNumberOfLocals()); | |||
for (int j=0;j<frame.getNumberOfLocals();j++) { | |||
locals.set(pos-j-1, Type.UNKNOWN); | |||
} | |||
return true; | |||
default: | |||
throw new RuntimeException("Frame.apply() - you need to implement support for "+frame.getKindString()); | |||
// System.err.println("DUNNO > "+frame.getKindString()); | |||
} | |||
} | |||
private StackMapType getTheType(Type type,ConstantPoolGen cpg) { | |||
// System.out.println("> Frame.getTheType(): type = "+type); | |||
byte tag = type.getType(); | |||
StackMapType smt = null; | |||
int pos = -1; | |||
switch (tag) { | |||
case Constants.T_INT: | |||
smt = new StackMapType(Constants.ITEM_Integer,-1,cpg.getConstantPool()); | |||
break; | |||
case Constants.T_OBJECT: | |||
if (type==Type.NULL) { | |||
smt = new StackMapType(Constants.ITEM_Null,-1,cpg.getConstantPool()); | |||
} else { | |||
pos = cpg.addClass((ObjectType)type); | |||
smt = new StackMapType(Constants.ITEM_Object,pos,cpg.getConstantPool()); | |||
} | |||
break; | |||
case Constants.T_UNKNOWN: | |||
UninitializedObjectType uot = (UninitializedObjectType)type; | |||
if (uot.isThis()) { | |||
smt = new StackMapType(Constants.ITEM_UninitializedThis,-1,cpg.getConstantPool()); | |||
} else { | |||
// pos = cpg.addClass(uot.getInitialized()); | |||
smt = new StackMapType(Constants.ITEM_Uninitialized,uot.getLoc(),cpg.getConstantPool()); | |||
} | |||
break; | |||
case Constants.T_ARRAY: | |||
ArrayType aType = (ArrayType)type; | |||
pos = cpg.addClass(type.getSignature()); | |||
smt = new StackMapType(Constants.ITEM_Object,pos,cpg.getConstantPool()); | |||
break; | |||
case Constants.T_TOP: | |||
smt = new StackMapType(Constants.ITEM_Top,-1,cpg.getConstantPool()); | |||
break; | |||
case Constants.T_FLOAT: | |||
smt = new StackMapType(Constants.ITEM_Float,-1,cpg.getConstantPool()); | |||
break; | |||
case Constants.T_LONG: | |||
smt = new StackMapType(Constants.ITEM_Long,-1,cpg.getConstantPool()); | |||
break; | |||
case Constants.T_DOUBLE: | |||
smt = new StackMapType(Constants.ITEM_Double,-1,cpg.getConstantPool()); | |||
break; | |||
default: | |||
throw new RuntimeException("Frame.getTheType(): Dont know about tag "+tag); | |||
} | |||
return smt; | |||
} | |||
public StackMapType[] getLocalsAsStackMapTypes(ConstantPoolGen cpg) { | |||
// StackMapType[] smts = new StackMapType[locals.maxLocals()]; | |||
List l = new ArrayList(); | |||
for (int i = 0; i < locals.maxLocals(); i++) { | |||
Type type = (Type)locals.get(i); | |||
l.add(getTheType(type, cpg)); | |||
if (type.getSize()==2) { | |||
// can skip the TOP that follows ... yes? | |||
i++; | |||
if (locals.get(i)!=Type.TOP) { | |||
throw new RuntimeException("Bang! should be TOP but is "+locals.get(i)); | |||
} | |||
} | |||
} | |||
return (StackMapType[])l.toArray(new StackMapType[]{}); | |||
// return smts; | |||
} | |||
public StackMapType[] getStackAsStackMapTypes(ConstantPoolGen cpg) { | |||
List l = new ArrayList(); | |||
for (int i = 0; i < stack.slotsUsed(); i++) { | |||
Type type = (Type)stack.get(i); | |||
l.add(getTheType(type, cpg)); | |||
if (type.getSize()==2) { | |||
// can skip the TOP that follows ... yes? | |||
i++; | |||
try { | |||
if (stack.get(i)!=Type.TOP) { | |||
throw new RuntimeException("Bang! should be TOP but is "+locals.get(i)); | |||
} | |||
} catch (IndexOutOfBoundsException iiobe) { | |||
int stop = 1; | |||
} | |||
} | |||
} | |||
return (StackMapType[])l.toArray(new StackMapType[]{}); | |||
// StackMapType[] smts = new StackMapType[stack.slotsUsed()]; | |||
// for (int i = 0; i < stack.size(); i++) { | |||
// smts[i] = getTheType((Type)stack.get(i),cp); | |||
// } | |||
// return smts; | |||
} | |||
} |
@@ -0,0 +1,113 @@ | |||
package org.aspectj.apache.bcel.verifier.utility; | |||
/* ==================================================================== | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2001 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, | |||
* if any, must include the following acknowledgment: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowledgment may appear in the software itself, | |||
* if and wherever such third-party acknowledgments normally appear. | |||
* | |||
* 4. The names "Apache" and "Apache Software Foundation" and | |||
* "Apache BCEL" must not be used to endorse or promote products | |||
* derived from this software without prior written permission. For | |||
* written permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache", | |||
* "Apache BCEL", nor may "Apache" appear in their name, without | |||
* prior written permission of the Apache Software Foundation. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
import java.util.ArrayList; | |||
import org.aspectj.apache.bcel.generic.InstructionHandle; | |||
/** | |||
* An InstructionContext offers convenient access to information like control flow successors | |||
* | |||
* @version $Id$ | |||
* @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A> | |||
*/ | |||
public interface InstructionContext{ | |||
/** | |||
* This method symbolically executes the Instruction held in the | |||
* InstructionContext. It "merges in" the incoming execution frame | |||
* situation (see The JVM Specification, 2nd edition, page 146). | |||
* By so doing, the outgoing execution frame situation is calculated. | |||
* | |||
* This method is JustIce-specific and is usually of no sense for | |||
* users of the ControlFlowGraph class. They should use | |||
* getInstruction().accept(Visitor), possibly in conjunction with the ExecutionVisitor. | |||
* | |||
* @see #getOutFrame(ArrayList) | |||
* @return true - if and only if the "outgoing" frame situation | |||
* changed from the one before execute()ing. | |||
*/ | |||
boolean execute(Frame inFrame, ArrayList executionPredecessors, InstConstraintVisitor icv, ExecutionVisitor ev); | |||
boolean execute(Frame inFrame, ArrayList executionPredecessors, ExecutionVisitor ev); | |||
/** | |||
* This method returns the outgoing execution frame situation; | |||
* therefore <B>it has to be calculated by execute(Frame, ArrayList) | |||
* first.</B> | |||
* | |||
* @see #execute(Frame, ArrayList, InstConstraintVisitor, ExecutionVisitor) | |||
*/ | |||
Frame getOutFrame(ArrayList executionPredecessors); | |||
/** | |||
* Returns the InstructionHandle this InstructionContext is wrapped around. | |||
* | |||
* @return The InstructionHandle this InstructionContext is wrapped around. | |||
*/ | |||
InstructionHandle getInstruction(); | |||
/** | |||
* Returns the usual control flow successors. | |||
* @see #getExceptionHandlers() | |||
*/ | |||
InstructionContext[] getSuccessors(); | |||
/** | |||
* Returns the exception handlers that protect this instruction. | |||
* They are special control flow successors. | |||
*/ | |||
ExceptionHandler[] getExceptionHandlers(); | |||
} |
@@ -0,0 +1,395 @@ | |||
package org.aspectj.apache.bcel.verifier.utility; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import org.aspectj.apache.bcel.generic.ATHROW; | |||
import org.aspectj.apache.bcel.generic.BranchInstruction; | |||
import org.aspectj.apache.bcel.generic.GotoInstruction; | |||
import org.aspectj.apache.bcel.generic.Instruction; | |||
import org.aspectj.apache.bcel.generic.InstructionConstants; | |||
import org.aspectj.apache.bcel.generic.InstructionHandle; | |||
import org.aspectj.apache.bcel.generic.JsrInstruction; | |||
import org.aspectj.apache.bcel.generic.RET; | |||
import org.aspectj.apache.bcel.generic.ReturnInstruction; | |||
import org.aspectj.apache.bcel.generic.Select; | |||
import org.aspectj.apache.bcel.verifier.exc.AssertionViolatedException; | |||
import org.aspectj.apache.bcel.verifier.exc.StructuralCodeConstraintException; | |||
/** | |||
* Objects of this class represent a node in a ControlFlowGraph. | |||
* These nodes are instructions, not basic blocks. | |||
*/ | |||
class InstructionContextImpl implements InstructionContext{ | |||
private final ControlFlowGraph graph; | |||
// The InstructionHandle this InstructionContext is wrapped around. | |||
private InstructionHandle instruction; | |||
// The 'incoming' execution Frames. | |||
private HashMap /* last executing JSR */ inFrames; | |||
// The 'outgoing' execution Frames. | |||
private HashMap /* last executed JSR */ outFrames; | |||
/** | |||
* The 'execution predecessors' - a list of type InstructionContext | |||
* of those instances that have been execute()d before in that order. | |||
*/ | |||
private List /*InstructionContext*/ executionPredecessors = null; | |||
// --- | |||
/** | |||
* Creates an InstructionHandleImpl object from an InstructionHandle. | |||
* Creation of one per InstructionHandle suffices. Don't create more. | |||
*/ | |||
public InstructionContextImpl(ControlFlowGraph graph, InstructionHandle inst){ | |||
if (inst == null) throw new AssertionViolatedException("Cannot instantiate InstructionContextImpl from NULL."); | |||
this.graph = graph; | |||
instruction = inst; | |||
inFrames = new HashMap(); | |||
outFrames = new HashMap(); | |||
} | |||
/** | |||
* Returns the exception handlers of this instruction. | |||
*/ | |||
public ExceptionHandler[] getExceptionHandlers(){ | |||
return this.graph.exceptionhandlers.getExceptionHandlers(getInstruction()); | |||
} | |||
/** | |||
* Returns a clone of the "outgoing" frame situation with respect to the given ExecutionChain. | |||
*/ | |||
public Frame getOutFrame(ArrayList execChain){ | |||
executionPredecessors = execChain; | |||
Frame org; | |||
InstructionContext jsr = lastExecutionJSR(); | |||
org = (Frame) outFrames.get(jsr); | |||
if (org == null){ | |||
throw new AssertionViolatedException("outFrame not set! This:\n"+this+"\nExecutionChain: "+getExecutionChain()+"\nOutFrames: '"+outFrames+"'."); | |||
} | |||
return org.getClone(); | |||
} | |||
/** | |||
* "Merges in" (vmspec2, page 146) the "incoming" frame situation; | |||
* executes the instructions symbolically | |||
* and therefore calculates the "outgoing" frame situation. | |||
* Returns: True iff the "incoming" frame situation changed after | |||
* merging with "inFrame". | |||
* The execPreds ArrayList must contain the InstructionContext | |||
* objects executed so far in the correct order. This is just | |||
* one execution path [out of many]. This is needed to correctly | |||
* "merge" in the special case of a RET's successor. | |||
* <B>The InstConstraintVisitor and ExecutionVisitor instances | |||
* must be set up correctly.</B> | |||
* @return true - if and only if the "outgoing" frame situation | |||
* changed from the one before execute()ing. | |||
*/ | |||
private List subsetIt(List l) { | |||
List toKeep = new ArrayList(); | |||
for (Iterator iter = l.iterator(); iter.hasNext();) { | |||
InstructionContext iCtx = (InstructionContext) iter.next(); | |||
Instruction i = iCtx.getInstruction().getInstruction(); | |||
if (i instanceof JsrInstruction || i instanceof RET) { | |||
toKeep.add(iCtx); | |||
} | |||
} | |||
if (toKeep.size()>0) { | |||
System.err.println("keep>0!"); | |||
return toKeep; | |||
} else { | |||
return Collections.EMPTY_LIST; | |||
} | |||
} | |||
/** | |||
* Copy of other execute method but doesnt worry about constraint visitor | |||
*/ | |||
public boolean execute(Frame inFrame, ArrayList execPreds, ExecutionVisitor executionVisitor) { | |||
executionPredecessors = subsetIt(execPreds);//(ArrayList) execPreds.clone(); | |||
//sanity check | |||
if ( (lastExecutionJSR() == null) && (this.graph.subroutines.subroutineOf(getInstruction()) != this.graph.subroutines.getTopLevel() ) ){ | |||
throw new AssertionViolatedException("Huh?! Am I '"+this+"' part of a subroutine or not?"); | |||
} | |||
if ( (lastExecutionJSR() != null) && (this.graph.subroutines.subroutineOf(getInstruction()) == this.graph.subroutines.getTopLevel() ) ){ | |||
throw new AssertionViolatedException("Huh?! Am I '"+this+"' part of a subroutine or not?"); | |||
} | |||
Frame inF = (Frame) inFrames.get(lastExecutionJSR()); | |||
if (inF == null) {// no incoming frame was set, so set it. | |||
inFrames.put(lastExecutionJSR(), inFrame); | |||
inF = inFrame; | |||
} else {// if there was an "old" inFrame | |||
if (inF.equals(inFrame)){ //shortcut: no need to merge equal frames. | |||
return false; | |||
} | |||
if (! mergeInFrames(inFrame)){ | |||
return false; | |||
} | |||
} | |||
// Now we're sure the inFrame has changed! | |||
// new inFrame is already merged in, see above. | |||
Frame workingFrame = inF.getClone(); | |||
// This executes the Instruction - modifying the workingFrame | |||
executionVisitor.setFrame(workingFrame); | |||
executionVisitor.setPosition(getInstruction().getPosition()); | |||
getInstruction().accept(executionVisitor); | |||
outFrames.put(lastExecutionJSR(), workingFrame); | |||
return true; // new inFrame was different from old inFrame so merging them | |||
// yielded a different this.inFrame. | |||
} | |||
public boolean execute(Frame inFrame, ArrayList execPreds, InstConstraintVisitor icv, ExecutionVisitor ev){ | |||
executionPredecessors = subsetIt(execPreds);//(ArrayList) execPreds.clone(); | |||
//sanity check | |||
if ( (lastExecutionJSR() == null) && (this.graph.subroutines.subroutineOf(getInstruction()) != this.graph.subroutines.getTopLevel() ) ){ | |||
throw new AssertionViolatedException("Huh?! Am I '"+this+"' part of a subroutine or not?"); | |||
} | |||
if ( (lastExecutionJSR() != null) && (this.graph.subroutines.subroutineOf(getInstruction()) == this.graph.subroutines.getTopLevel() ) ){ | |||
throw new AssertionViolatedException("Huh?! Am I '"+this+"' part of a subroutine or not?"); | |||
} | |||
Frame inF = (Frame) inFrames.get(lastExecutionJSR()); | |||
if (inF == null){// no incoming frame was set, so set it. | |||
inFrames.put(lastExecutionJSR(), inFrame); | |||
inF = inFrame; | |||
} | |||
else{// if there was an "old" inFrame | |||
if (inF.equals(inFrame)){ //shortcut: no need to merge equal frames. | |||
return false; | |||
} | |||
if (! mergeInFrames(inFrame)){ | |||
return false; | |||
} | |||
} | |||
// Now we're sure the inFrame has changed! | |||
// new inFrame is already merged in, see above. | |||
Frame workingFrame = inF.getClone(); | |||
try{ | |||
// This verifies the InstructionConstraint for the current | |||
// instruction, but does not modify the workingFrame object. | |||
//InstConstraintVisitor icv = InstConstraintVisitor.getInstance(VerifierFactory.getVerifier(method_gen.getClassName())); | |||
icv.setFrame(workingFrame); | |||
// will go bang... | |||
getInstruction().accept(icv); | |||
} | |||
catch(StructuralCodeConstraintException ce){ | |||
ce.extendMessage("","\nInstructionHandle: "+getInstruction()+"\n"); | |||
ce.extendMessage("","\nExecution Frame:\n"+workingFrame); | |||
extendMessageWithFlow(ce); | |||
throw ce; | |||
} | |||
// This executes the Instruction. | |||
// Therefore the workingFrame object is modified. | |||
//ExecutionVisitor ev = ExecutionVisitor.getInstance(VerifierFactory.getVerifier(method_gen.getClassName())); | |||
ev.setFrame(workingFrame); | |||
ev.setPosition(getInstruction().getPosition()); | |||
getInstruction().accept(ev); | |||
//getInstruction().accept(ExecutionVisitor.withFrame(workingFrame)); | |||
outFrames.put(lastExecutionJSR(), workingFrame); | |||
return true; // new inFrame was different from old inFrame so merging them | |||
// yielded a different this.inFrame. | |||
} | |||
/** | |||
* Returns a simple String representation of this InstructionContext. | |||
*/ | |||
public String toString(){ | |||
//TODO: Put information in the brackets, e.g. | |||
// Is this an ExceptionHandler? Is this a RET? Is this the start of | |||
// a subroutine? | |||
String ret = getInstruction().toString(false)+"\t[InstructionContext]"; | |||
return ret; | |||
} | |||
/** | |||
* Does the actual merging (vmspec2, page 146). | |||
* Returns true IFF this.inFrame was changed in course of merging with inFrame. | |||
*/ | |||
private boolean mergeInFrames(Frame inFrame){ | |||
// TODO: Can be performance-improved. | |||
Frame inF = (Frame) inFrames.get(lastExecutionJSR()); | |||
OperandStack oldstack = inF.getStack().getClone(); | |||
LocalVariables oldlocals = inF.getLocals().getClone(); | |||
try{ | |||
inF.getStack().merge(inFrame.getStack()); | |||
inF.getLocals().merge(inFrame.getLocals()); | |||
} | |||
catch (StructuralCodeConstraintException sce){ | |||
extendMessageWithFlow(sce); | |||
throw sce; | |||
} | |||
if ( oldstack.equals(inF.getStack()) && | |||
oldlocals.equals(inF.getLocals()) ){ | |||
return false; | |||
} | |||
else{ | |||
return true; | |||
} | |||
} | |||
/** | |||
* Returns the control flow execution chain. This is built | |||
* while execute(Frame, ArrayList)-ing the code represented | |||
* by the surrounding ControlFlowGraph. | |||
*/ | |||
private String getExecutionChain(){ | |||
String s = this.toString(); | |||
for (int i=executionPredecessors.size()-1; i>=0; i--){ | |||
s = executionPredecessors.get(i)+"\n" + s; | |||
} | |||
return s; | |||
} | |||
/** | |||
* Extends the StructuralCodeConstraintException ("e") object with an at-the-end-extended message. | |||
* This extended message will then reflect the execution flow needed to get to the constraint | |||
* violation that triggered the throwing of the "e" object. | |||
*/ | |||
private void extendMessageWithFlow(StructuralCodeConstraintException e){ | |||
String s = "Execution flow:\n"; | |||
e.extendMessage("", s+getExecutionChain()); | |||
} | |||
/* | |||
* Fulfils the contract of InstructionContext.getInstruction(). | |||
*/ | |||
public InstructionHandle getInstruction(){ | |||
return instruction; | |||
} | |||
/** | |||
* Returns the InstructionContextImpl with an JSR/JSR_W | |||
* that was last in the ExecutionChain, without | |||
* a corresponding RET, i.e. | |||
* we were called by this one. | |||
* Returns null if we were called from the top level. | |||
*/ | |||
private InstructionContextImpl lastExecutionJSR(){ | |||
int size = executionPredecessors.size(); | |||
int retcount = 0; | |||
for (int i=size-1; i>=0; i--){ | |||
InstructionContextImpl current = (InstructionContextImpl) (executionPredecessors.get(i)); | |||
Instruction currentlast = current.getInstruction().getInstruction(); | |||
if (currentlast instanceof RET) retcount++; | |||
if (currentlast instanceof JsrInstruction){ | |||
retcount--; | |||
if (retcount == -1) { | |||
throw new RuntimeException(); | |||
// return current; | |||
} | |||
} | |||
} | |||
return null; | |||
} | |||
/* Satisfies InstructionContext.getSuccessors(). */ | |||
public InstructionContext[] getSuccessors(){ | |||
return this.graph.contextsOf(_getSuccessors()); | |||
} | |||
/** | |||
* A utility method that calculates the successors of a given InstructionHandle | |||
* That means, a RET does have successors as defined here. | |||
* A JsrInstruction has its target as its successor | |||
* (opposed to its physical successor) as defined here. | |||
*/ | |||
// TODO: implement caching! | |||
private InstructionHandle[] _getSuccessors(){ | |||
final InstructionHandle[] empty = new InstructionHandle[0]; | |||
final InstructionHandle[] single = new InstructionHandle[1]; | |||
final InstructionHandle[] pair = new InstructionHandle[2]; | |||
Instruction inst = getInstruction().getInstruction(); | |||
if (inst instanceof RET){ | |||
Subroutine s = this.graph.subroutines.subroutineOf(getInstruction()); | |||
if (s==null){ //return empty; // RET in dead code. "empty" would be the correct answer, but we know something about the surrounding project... | |||
throw new AssertionViolatedException("Asking for successors of a RET in dead code?!"); | |||
} | |||
//TODO: remove | |||
throw new AssertionViolatedException("DID YOU REALLY WANT TO ASK FOR RET'S SUCCS?"); | |||
/* | |||
InstructionHandle[] jsrs = s.getEnteringJsrInstructions(); | |||
InstructionHandle[] ret = new InstructionHandle[jsrs.length]; | |||
for (int i=0; i<jsrs.length; i++){ | |||
ret[i] = jsrs[i].getNext(); | |||
} | |||
return ret; | |||
*/ | |||
} | |||
// Terminates method normally. | |||
if (inst instanceof ReturnInstruction){ | |||
return empty; | |||
} | |||
// Terminates method abnormally, because JustIce mandates | |||
// subroutines not to be protected by exception handlers. | |||
if (inst instanceof ATHROW){ | |||
return empty; | |||
} | |||
// See method comment. | |||
if (inst instanceof JsrInstruction){ | |||
single[0] = ((JsrInstruction) inst).getTarget(); | |||
return single; | |||
} | |||
if (inst instanceof GotoInstruction){ | |||
single[0] = ((GotoInstruction) inst).getTarget(); | |||
return single; | |||
} | |||
if (inst instanceof BranchInstruction){ | |||
if (inst instanceof Select){ | |||
// BCEL's getTargets() returns only the non-default targets, | |||
// thanks to Eli Tilevich for reporting. | |||
InstructionHandle[] matchTargets = ((Select) inst).getTargets(); | |||
InstructionHandle[] ret = new InstructionHandle[matchTargets.length+1]; | |||
ret[0] = ((Select) inst).getTarget(); | |||
System.arraycopy(matchTargets, 0, ret, 1, matchTargets.length); | |||
return ret; | |||
} | |||
else{ | |||
pair[0] = getInstruction().getNext(); | |||
pair[1] = ((BranchInstruction) inst).getTarget(); | |||
return pair; | |||
} | |||
} | |||
// default case: Fall through. | |||
single[0] = getInstruction().getNext(); | |||
return single; | |||
} | |||
} |
@@ -0,0 +1,34 @@ | |||
package org.aspectj.apache.bcel.verifier.utility; | |||
import java.util.ArrayList; | |||
import java.util.Vector; | |||
/** | |||
* An InstructionContextQueue is a utility class that holds | |||
* (InstructionContext, ArrayList) pairs in a Queue data structure. | |||
* This is used to hold information about InstructionContext objects | |||
* externally --- i.e. that information is not saved inside the | |||
* InstructionContext object itself. This is useful to save the | |||
* execution path of the symbolic execution of the | |||
* Pass3bVerifier - this is not information | |||
* that belongs into the InstructionContext object itself. | |||
* Only at "execute()"ing | |||
* time, an InstructionContext object will get the current information | |||
* we have about its symbolic execution predecessors. | |||
*/ | |||
final class InstructionContextQueue{ | |||
private Vector instructionContexts = new Vector(); // Type: InstructionContext | |||
private Vector executionChains = new Vector(); // Type: ArrayList (of InstructionContext) | |||
public void add(InstructionContext ic, ArrayList executionChain){ | |||
instructionContexts.add(ic); | |||
executionChains.add(executionChain); | |||
} | |||
public InstructionContext getIC(int i) { return (InstructionContext) instructionContexts.get(i); } | |||
public ArrayList getEC(int i) { return (ArrayList) executionChains.get(i); } | |||
public int size() { return instructionContexts.size(); } | |||
public boolean isEmpty() { return instructionContexts.isEmpty(); } | |||
public void remove() { this.remove(0); } | |||
public void remove(int i){ instructionContexts.remove(i); executionChains.remove(i); } | |||
} |
@@ -0,0 +1,268 @@ | |||
package org.aspectj.apache.bcel.verifier.utility; | |||
/* ==================================================================== | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2001 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, | |||
* if any, must include the following acknowledgment: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowledgment may appear in the software itself, | |||
* if and wherever such third-party acknowledgments normally appear. | |||
* | |||
* 4. The names "Apache" and "Apache Software Foundation" and | |||
* "Apache BCEL" must not be used to endorse or promote products | |||
* derived from this software without prior written permission. For | |||
* written permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache", | |||
* "Apache BCEL", nor may "Apache" appear in their name, without | |||
* prior written permission of the Apache Software Foundation. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
import org.aspectj.apache.bcel.generic.Type; | |||
import org.aspectj.apache.bcel.generic.ReferenceType; | |||
import org.aspectj.apache.bcel.verifier.exc.*; | |||
/** | |||
* This class implements an array of local variables used for symbolic JVM simulation. | |||
* | |||
* @version $Id$ | |||
* @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A> | |||
*/ | |||
public class LocalVariables { | |||
// The Type[] containing the local variable slots | |||
private Type[] locals; | |||
// Creates a new LocalVariables object | |||
public LocalVariables(int maxLocals){ | |||
locals = new Type[maxLocals]; | |||
for (int i=0; i<maxLocals; i++) locals[i] = Type.TOP; | |||
} | |||
/** Returns the type of the local variable slot i */ | |||
public Type get(int i) { | |||
return locals[i]; | |||
} | |||
/** | |||
* Returns a (correctly typed) clone of this object. | |||
* This is equivalent to ((LocalVariables) this.clone()). | |||
*/ | |||
public LocalVariables getClone(){ | |||
return (LocalVariables) this.clone(); | |||
} | |||
/** | |||
* Returns the number of local variable slots this | |||
* LocalVariables instance has. | |||
*/ | |||
public int maxLocals(){ | |||
return locals.length; | |||
} | |||
/** | |||
* Sets a new Type for the given local variable slot. | |||
*/ | |||
public void set(int i, Type type){ | |||
if (type == Type.BYTE || type == Type.SHORT || type == Type.BOOLEAN || type == Type.CHAR){ | |||
throw new AssertionViolatedException("LocalVariables do not know about '"+type+"'. Use Type.INT instead."); | |||
} | |||
locals[i] = type; | |||
// if (type!=Type.UNKNOWN) numberThatHaveBeenSet=i; | |||
} | |||
/* | |||
* Fulfills the general contract of Object.equals(). | |||
*/ | |||
public boolean equals(Object o){ | |||
if (!(o instanceof LocalVariables)) return false; | |||
LocalVariables lv = (LocalVariables) o; | |||
if (this.locals.length != lv.locals.length) return false; | |||
for (int i=0; i<this.locals.length; i++){ | |||
if (!this.locals[i].equals(lv.locals[i])){ | |||
//System.out.println(this.locals[i]+" is not "+lv.locals[i]); | |||
return false; | |||
} | |||
} | |||
return true; | |||
} | |||
/** | |||
* Merges two local variables sets as described in the Java Virtual Machine Specification, | |||
* Second Edition, section 4.9.2, page 146. | |||
*/ | |||
public void merge(LocalVariables lv){ | |||
if (this.locals.length != lv.locals.length){ | |||
throw new AssertionViolatedException("Merging LocalVariables of different size?!? From different methods or what?!?"); | |||
} | |||
for (int i=0; i<locals.length; i++){ | |||
merge(lv, i); | |||
} | |||
} | |||
/** | |||
* Merges a single local variable. | |||
* | |||
* @see #merge(LocalVariables) | |||
*/ | |||
private void merge(LocalVariables lv, int i){ | |||
// We won't accept an unitialized object if we know it was initialized; | |||
// compare vmspec2, 4.9.4, last paragraph. | |||
if ( (!(locals[i] instanceof UninitializedObjectType)) && (lv.locals[i] instanceof UninitializedObjectType) ){ | |||
throw new StructuralCodeConstraintException("Backwards branch with an uninitialized object in the local variables detected."); | |||
} | |||
// Even harder, what about _different_ uninitialized object types?! | |||
if ( (!(locals[i].equals(lv.locals[i]))) && (locals[i] instanceof UninitializedObjectType) && (lv.locals[i] instanceof UninitializedObjectType) ){ | |||
throw new StructuralCodeConstraintException("Backwards branch with an uninitialized object in the local variables detected."); | |||
} | |||
// If we just didn't know that it was initialized, we have now learned. | |||
if (locals[i] instanceof UninitializedObjectType){ | |||
if (! (lv.locals[i] instanceof UninitializedObjectType)){ | |||
locals[i] = ((UninitializedObjectType) locals[i]).getInitialized(); | |||
} | |||
} | |||
if ((locals[i] instanceof ReferenceType) && (lv.locals[i] instanceof ReferenceType)){ | |||
if (! locals[i].equals(lv.locals[i])){ // needed in case of two UninitializedObjectType instances | |||
Type sup = ((ReferenceType) locals[i]).getFirstCommonSuperclass((ReferenceType) (lv.locals[i])); | |||
if (sup != null){ | |||
locals[i] = sup; | |||
} | |||
else{ | |||
// We should have checked this in Pass2! | |||
throw new AssertionViolatedException("Could not load all the super classes of '"+locals[i]+"' and '"+lv.locals[i]+"'."); | |||
} | |||
} | |||
} | |||
else{ | |||
if (! (locals[i].equals(lv.locals[i])) ){ | |||
/*TODO | |||
if ((locals[i] instanceof org.aspectj.apache.bcel.generic.ReturnaddressType) && (lv.locals[i] instanceof org.aspectj.apache.bcel.generic.ReturnaddressType)){ | |||
//System.err.println("merging "+locals[i]+" and "+lv.locals[i]); | |||
throw new AssertionViolatedException("Merging different ReturnAddresses: '"+locals[i]+"' and '"+lv.locals[i]+"'."); | |||
} | |||
*/ | |||
locals[i] = Type.TOP; | |||
} | |||
} | |||
} | |||
/** | |||
* Returns a String representation of this object. | |||
*/ | |||
public String toString(){ | |||
String s = new String(); | |||
for (int i=0; i<locals.length; i++){ | |||
s += Integer.toString(i)+": "+locals[i]+"\n"; | |||
} | |||
return s; | |||
} | |||
/** | |||
* Return a more compact string than toString() | |||
*/ | |||
public String toCompactString() { | |||
StringBuffer sb = new StringBuffer(); | |||
sb.append("[").append(maxLocals()).append(":"); | |||
for (int i=0;i<locals.length;i++) sb.append(i).append("=").append(compactName(locals[i])).append(" "); | |||
sb.append("]"); | |||
return sb.toString(); | |||
} | |||
public String compactName(Type type) { | |||
if (type==Type.UNKNOWN) return "<Unknown>"; | |||
String s = type.toString(); | |||
int pos = s.lastIndexOf("."); | |||
if (pos==-1) return s; | |||
return s.substring(pos+1); | |||
} | |||
/** | |||
* Replaces all occurences of u in this local variables set | |||
* with an "initialized" ObjectType. | |||
*/ | |||
public void initializeObject(UninitializedObjectType u){ | |||
for (int i=0; i<locals.length; i++){ | |||
if (locals[i] == u){ | |||
locals[i] = u.getInitialized(); | |||
} | |||
} | |||
} | |||
/** | |||
* Returns a deep copy of this object; i.e. the clone | |||
* operates on a new local variable array. | |||
* However, the Type objects in the array are shared. | |||
*/ | |||
protected Object clone(){ | |||
LocalVariables lvs = new LocalVariables(locals.length); | |||
for (int i=0; i<locals.length; i++){ | |||
lvs.locals[i] = this.locals[i]; | |||
} | |||
return lvs; | |||
} | |||
public void flush() { | |||
// reset them all back to unknown! | |||
for (int i=0; i<locals.length; i++) locals[i] = Type.TOP; | |||
} | |||
public int getNextUnset() { | |||
return numberThatHaveBeenSet; | |||
} | |||
public void haveSet(int i) { | |||
// Called so we can remember where the end is | |||
numberThatHaveBeenSet=i; | |||
} | |||
private int numberThatHaveBeenSet; | |||
// public int getFirstUnknown() { | |||
// for (int i = 0; i < locals.length; i++) { | |||
// if (locals[i].equals(Type.UNKNOWN)) return i; | |||
// } | |||
// return -1; | |||
// } | |||
} |
@@ -0,0 +1,300 @@ | |||
package org.aspectj.apache.bcel.verifier.utility; | |||
/* ==================================================================== | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2001 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, | |||
* if any, must include the following acknowledgment: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowledgment may appear in the software itself, | |||
* if and wherever such third-party acknowledgments normally appear. | |||
* | |||
* 4. The names "Apache" and "Apache Software Foundation" and | |||
* "Apache BCEL" must not be used to endorse or promote products | |||
* derived from this software without prior written permission. For | |||
* written permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache", | |||
* "Apache BCEL", nor may "Apache" appear in their name, without | |||
* prior written permission of the Apache Software Foundation. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
import org.aspectj.apache.bcel.generic.*; | |||
import org.aspectj.apache.bcel.verifier.exc.*; | |||
import java.util.*; | |||
/** | |||
* This class implements a stack used for symbolic JVM stack simulation. | |||
* [It's used an an operand stack substitute.] | |||
* Elements of this stack are org.aspectj.apache.bcel.generic.Type objects. | |||
* | |||
* @version $Id$ | |||
* @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A> | |||
*/ | |||
public class OperandStack{ | |||
/** We hold the stack information here. */ | |||
private ArrayList stack = new ArrayList(); | |||
/** The maximum number of stack slots this OperandStack instance may hold. */ | |||
private int maxStack; | |||
/** | |||
* Creates an empty stack with a maximum of maxStack slots. | |||
*/ | |||
public OperandStack(int maxStack){ | |||
this.maxStack = maxStack; | |||
} | |||
/** | |||
* Creates an otherwise empty stack with a maximum of maxStack slots and | |||
* the ObjectType 'obj' at the top. | |||
*/ | |||
public OperandStack(int maxStack, ObjectType obj){ | |||
this.maxStack = maxStack; | |||
this.push(obj); | |||
} | |||
/** | |||
* Returns a deep copy of this object; that means, the clone operates | |||
* on a new stack. However, the Type objects on the stack are | |||
* shared. | |||
*/ | |||
protected Object clone(){ | |||
OperandStack newstack = new OperandStack(this.maxStack); | |||
newstack.stack = (ArrayList) this.stack.clone(); | |||
return newstack; | |||
} | |||
/** | |||
* There will be TOP entries for double slot values! | |||
*/ | |||
public Type get(int i) { return (Type)stack.get(i);} | |||
/** | |||
* Clears the stack. | |||
*/ | |||
public void clear(){ | |||
stack = new ArrayList(); | |||
} | |||
/** | |||
* Returns true if and only if this OperandStack | |||
* equals another, meaning equal lengths and equal | |||
* objects on the stacks. | |||
*/ | |||
public boolean equals(Object o){ | |||
if (!(o instanceof OperandStack)) return false; | |||
OperandStack s = (OperandStack) o; | |||
return this.stack.equals(s.stack); | |||
} | |||
/** | |||
* Returns a (typed!) clone of this. | |||
* | |||
* @see #clone() | |||
*/ | |||
public OperandStack getClone(){ | |||
return (OperandStack) this.clone(); | |||
} | |||
/** | |||
* Returns true IFF this OperandStack is empty. | |||
*/ | |||
public boolean isEmpty(){ | |||
return stack.isEmpty(); | |||
} | |||
/** | |||
* Returns the number of stack slots this stack can hold. | |||
*/ | |||
public int maxStack(){ | |||
return this.maxStack; | |||
} | |||
/** | |||
* Returns the element on top of the stack. The element is not popped off the stack! | |||
*/ | |||
public Type peek(){ | |||
return peek(0); | |||
} | |||
/** | |||
* Returns the element that's i elements below the top element; that means, | |||
* iff i==0 the top element is returned. The element is not popped off the stack! | |||
*/ | |||
public Type peek(int i){ | |||
Type t = (Type) stack.get(size()-i-1); | |||
if (t==Type.TOP) { | |||
t = (Type) stack.get(size()-i-2); | |||
} | |||
return t; | |||
} | |||
/** | |||
* Returns the element on top of the stack. The element is popped off the stack. | |||
*/ | |||
public Type pop(){ | |||
Type e = (Type) stack.remove(size()-1); | |||
if (e==Type.TOP) { | |||
e = (Type) stack.remove(size()-1); | |||
} | |||
return e; | |||
} | |||
/** | |||
* Pops i elements off the stack. ALWAYS RETURNS "null"!!! | |||
*/ | |||
public Type pop(int i){ | |||
for (int j=0; j<i; j++){ | |||
pop(); | |||
} | |||
return null; | |||
} | |||
/** | |||
* Pushes a Type object onto the stack. | |||
*/ | |||
public void push(Type type){ | |||
if (type == null) throw new AssertionViolatedException("Cannot push NULL onto OperandStack."); | |||
if (type == Type.BOOLEAN || type == Type.CHAR || type == Type.BYTE || type == Type.SHORT){ | |||
throw new AssertionViolatedException("The OperandStack does not know about '"+type+"'; use Type.INT instead."); | |||
} | |||
if (slotsUsed() >= maxStack){ | |||
throw new AssertionViolatedException("OperandStack too small, should have thrown proper Exception elsewhere. Stack: "+this); | |||
} | |||
stack.add(type); | |||
if (type.getSize()==2) stack.add(Type.TOP); | |||
} | |||
/** | |||
* Returns the size of this OperandStack; that means, how many Type objects there are. | |||
*/ | |||
public int size(){ | |||
return stack.size(); | |||
} | |||
/** | |||
* Returns the number of stack slots used. | |||
* @see #maxStack() | |||
*/ | |||
public int slotsUsed(){ | |||
return stack.size(); | |||
/* XXX change this to a better implementation using a variable | |||
that keeps track of the actual slotsUsed()-value monitoring | |||
all push()es and pop()s. | |||
*/ | |||
// int slots = 0; | |||
// for (int i=0; i<stack.size(); i++){ | |||
// slots += peek(i).getSize(); | |||
// } | |||
// return slots; | |||
} | |||
/** | |||
* Returns a String representation of this OperandStack instance. | |||
*/ | |||
public String toString(){ | |||
String s = "Slots used: "+slotsUsed()+" MaxStack: "+maxStack+".\n"; | |||
for (int i=0; i<size(); i++){ | |||
s+=peek(i)+" (Size: "+peek(i).getSize()+")\n"; | |||
} | |||
return s; | |||
} | |||
/** | |||
* Return a more compact string than toString() | |||
*/ | |||
public String toCompactString() { | |||
StringBuffer sb = new StringBuffer(); | |||
sb.append("[").append(size()).append(":"); | |||
for (int i=0;i<size();i++) sb.append(i).append("=").append(peek(i)).append("(").append(peek(i).getSize()).append(") "); | |||
sb.append("]"); | |||
return sb.toString(); | |||
} | |||
/** | |||
* Merges another stack state into this instance's stack state. | |||
* See the Java Virtual Machine Specification, Second Edition, page 146: 4.9.2 | |||
* for details. | |||
*/ | |||
public void merge(OperandStack s){ | |||
if ( (slotsUsed() != s.slotsUsed()) || (size() != s.size()) ) | |||
throw new StructuralCodeConstraintException("Cannot merge stacks of different size:\nOperandStack A:\n"+this+"\nOperandStack B:\n"+s); | |||
for (int i=0; i<size(); i++){ | |||
// If the object _was_ initialized and we're supposed to merge | |||
// in some uninitialized object, we reject the code (see vmspec2, 4.9.4, last paragraph). | |||
if ( (! (stack.get(i) instanceof UninitializedObjectType)) && (s.stack.get(i) instanceof UninitializedObjectType) ){ | |||
throw new StructuralCodeConstraintException("Backwards branch with an uninitialized object on the stack detected."); | |||
} | |||
// Even harder, we're not initialized but are supposed to broaden | |||
// the known object type | |||
if ( (!(stack.get(i).equals(s.stack.get(i)))) && (stack.get(i) instanceof UninitializedObjectType) && (!(s.stack.get(i) instanceof UninitializedObjectType))){ | |||
throw new StructuralCodeConstraintException("Backwards branch with an uninitialized object on the stack detected."); | |||
} | |||
// on the other hand... | |||
if (stack.get(i) instanceof UninitializedObjectType){ //if we have an uninitialized object here | |||
if (! (s.stack.get(i) instanceof UninitializedObjectType)){ //that has been initialized by now | |||
stack.set(i, ((UninitializedObjectType) (stack.get(i))).getInitialized() ); //note that. | |||
} | |||
} | |||
if (! stack.get(i).equals(s.stack.get(i))){ | |||
if ( (stack.get(i) instanceof ReferenceType) && | |||
(s.stack.get(i) instanceof ReferenceType) ){ | |||
stack.set(i, ((ReferenceType) stack.get(i)).getFirstCommonSuperclass((ReferenceType) (s.stack.get(i)))); | |||
} | |||
else{ | |||
throw new StructuralCodeConstraintException("Cannot merge stacks of different types:\nStack A:\n"+this+"\nStack B:\n"+s); | |||
} | |||
} | |||
} | |||
} | |||
/** | |||
* Replaces all occurences of u in this OperandStack instance | |||
* with an "initialized" ObjectType. | |||
*/ | |||
public void initializeObject(UninitializedObjectType u){ | |||
for (int i=0; i<stack.size(); i++){ | |||
if (stack.get(i) == u){ | |||
stack.set(i, u.getInitialized()); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,225 @@ | |||
package org.aspectj.apache.bcel.verifier.utility; | |||
import java.util.ArrayList; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import org.aspectj.apache.bcel.Constants; | |||
import org.aspectj.apache.bcel.classfile.StackMapFrame; | |||
import org.aspectj.apache.bcel.classfile.StackMapTable; | |||
import org.aspectj.apache.bcel.generic.BranchInstruction; | |||
import org.aspectj.apache.bcel.generic.CodeExceptionGen; | |||
import org.aspectj.apache.bcel.generic.ConstantPoolGen; | |||
import org.aspectj.apache.bcel.generic.InstructionHandle; | |||
import org.aspectj.apache.bcel.generic.InstructionTargeter; | |||
import org.aspectj.apache.bcel.generic.MethodGen; | |||
import org.aspectj.apache.bcel.generic.ObjectType; | |||
import org.aspectj.apache.bcel.generic.RET; | |||
import org.aspectj.apache.bcel.generic.ReturnaddressType; | |||
import org.aspectj.apache.bcel.generic.Type; | |||
public class StackMapHelper { | |||
public static int count = 0; | |||
private Map framesAtBranchDestinations /*<InstructionHandle,Frame>*/= new HashMap(); | |||
private boolean debugPumping=false; | |||
public StackMapHelper() {} | |||
/** Merge the new frame with anything we already know */ | |||
private void storeFrameAtBranchDestination(InstructionHandle iHandle,Object newFrame) { | |||
if (!iHandle.isJumpDestination()) return; | |||
Frame existingFrame = (Frame)framesAtBranchDestinations.get(iHandle); | |||
if (debugPumping) System.out.println(((Frame)newFrame).toString("Storing at "+iHandle.getPosition()+":")); | |||
if (existingFrame==null) { | |||
framesAtBranchDestinations.put(iHandle,newFrame); | |||
} else { // merge | |||
Frame f = (Frame)newFrame; | |||
existingFrame.getLocals().merge(f.getLocals()); | |||
existingFrame.getStack().merge(f.getStack()); | |||
} | |||
count++; // number of frames stored | |||
} | |||
/** | |||
* For everything targeted as a jump destination, create the frame. needs to return offsets to | |||
*/ | |||
private Frame[] getFramesAtBranchDestinations(InstructionHandle iHandle) { | |||
Frame[] outputFrames = new Frame[framesAtBranchDestinations.size()]; | |||
int i=0; | |||
while (iHandle!=null) { | |||
if (iHandle.isJumpDestination()) { | |||
Frame f = (Frame)framesAtBranchDestinations.get(iHandle); | |||
f.position = iHandle.getPosition(); // fix it for StackMapFrame creation | |||
outputFrames[i++]=f; | |||
} | |||
iHandle = iHandle.getNext(); | |||
} | |||
return outputFrames; | |||
} | |||
/** | |||
* Drive the lightweight verifier process to produce the Frames that will be used | |||
* to construct the StackMapTable attribute. | |||
*/ | |||
public StackMapTable produceStackMapTableAttribute(MethodGen mg) { | |||
// Don't bother if there is no code | |||
if (mg.isAbstract() || mg.isNative() ) return null; | |||
ConstantPoolGen cpg = mg.getConstantPool(); | |||
mg.getInstructionList().setPositions(); // fixes up the stupid bloody optimizations of LDC | |||
ExecutionVisitor executionVisitor = new ExecutionVisitor(cpg); | |||
ControlFlowGraph cflowGraph = new ControlFlowGraph(mg); | |||
Frame initialFrame = createInitialFrame(mg); | |||
CodeExceptionGen[] handlers = mg.getExceptionHandlers(); | |||
// now the initial frame is setup and we can start 'running the code' | |||
if (debugPumping) System.err.println("Initial frame at start of method: "+initialFrame.toString()); | |||
InstructionContext contextOfFirstInstruction = cflowGraph.contextOf(mg.getInstructionList().getStart()); | |||
processInstructions(cflowGraph,contextOfFirstInstruction,initialFrame,executionVisitor,handlers); | |||
Frame[] frames = getFramesAtBranchDestinations(mg.getInstructionList().getStart()); | |||
StackMapTable table = StackMapTable.forFrames(frames,cpg); | |||
return table; | |||
} | |||
private boolean isRETInstruction(InstructionContext ic) { | |||
return (ic.getInstruction().getInstruction() instanceof RET); | |||
} | |||
/** | |||
* Whenever the outgoing frame situation of an InstructionContext changes, | |||
* all its successors are put [back] into the queue [as if they were unvisited]. | |||
* The proof of termination is about the existence of a fix point of frame merging. | |||
*/ | |||
private void processInstructions( ControlFlowGraph flowGraph, InstructionContext start, Frame initialFrame, ExecutionVisitor executionVis, CodeExceptionGen[] handlers) { | |||
InstructionContextQueue queue = new InstructionContextQueue(); | |||
// Passing in an empty array list here indicates no instruction was executed previously, | |||
// which is interpreted as this being a 'top level' routine - no previous Jsr | |||
start.execute(initialFrame, new ArrayList(), executionVis); | |||
storeFrameAtBranchDestination(start.getInstruction(),initialFrame.clone()); | |||
queue.add(start, new ArrayList()); | |||
while (!queue.isEmpty()){ | |||
// Retrieve the next instruction context to look at and the execution chain we followed to get to it | |||
InstructionContext instructionToProcess = queue.getIC(0); | |||
ArrayList executionChain = queue.getEC(0); | |||
queue.remove(0); | |||
Frame incomingFrame = instructionToProcess.getOutFrame(executionChain); | |||
executionChain.add(instructionToProcess); | |||
// Frame outgoingFrame = null; | |||
if (isRETInstruction(instructionToProcess)) { | |||
// We can only follow *one* successor, the one after the JSR that was recently executed. | |||
RET ret = (RET) (instructionToProcess.getInstruction().getInstruction()); | |||
ReturnaddressType t = (ReturnaddressType) incomingFrame.getLocals().get(ret.getIndex()); | |||
InstructionContext theSuccessor = flowGraph.contextOf(t.getTarget()); | |||
if (theSuccessor.execute((Frame)incomingFrame.clone(), executionChain, executionVis)) { | |||
queue.add(theSuccessor, executionChain); | |||
} | |||
} else { // not a RET instruction | |||
// Normal successors - add them to the queue of successors | |||
InstructionContext[] successors = instructionToProcess.getSuccessors(); | |||
for (int s=0; s<successors.length; s++) { | |||
InstructionContext nextSuccessor = successors[s]; | |||
Frame outgoingFrame = (Frame)incomingFrame.clone(); | |||
// System.out.println("Processed: "+instructionToProcess); | |||
storeFrameAtBranchDestination(nextSuccessor.getInstruction(),outgoingFrame.clone()); | |||
if (nextSuccessor.execute(outgoingFrame, executionChain, executionVis)){ | |||
queue.add(nextSuccessor, executionChain); | |||
} | |||
} | |||
} | |||
// Exception Handlers. Add them to the queue of successors. | |||
// [subroutines are never protected; mandated by JustIce] | |||
ExceptionHandler[] exc_hds = instructionToProcess.getExceptionHandlers(); | |||
for (int s=0; s<exc_hds.length; s++){ | |||
// TODO: the "oldchain" and "newchain" is used to determine the subroutine | |||
// we're in (by searching for the last JSR) by the InstructionContext | |||
// implementation. Therefore, we should not use this chain mechanism | |||
// when dealing with exception handlers. | |||
// Example: a JSR with an exception handler as its successor does not | |||
// mean we're in a subroutine if we go to the exception handler. | |||
// We should address this problem later; by now we simply "cut" the chain | |||
// by using an empty chain for the exception handlers. | |||
InstructionContext theExceptionHandlerBlock = flowGraph.contextOf(exc_hds[s].getHandlerStart()); | |||
Frame f = new Frame(incomingFrame.getLocals(), new OperandStack (incomingFrame.getStack().maxStack(), (exc_hds[s].getExceptionType()==null? Type.THROWABLE : exc_hds[s].getExceptionType())) ); | |||
// System.out.println("Processed: "+instructionToProcess); | |||
storeFrameAtBranchDestination(theExceptionHandlerBlock.getInstruction(), f.clone()); | |||
if (theExceptionHandlerBlock.execute(f, new ArrayList(), executionVis)){ | |||
queue.add(theExceptionHandlerBlock, new ArrayList()); | |||
} | |||
} | |||
} | |||
} | |||
// --- | |||
public static Frame createInitialFrame(MethodGen mg) { | |||
Frame initialFrame = new Frame(mg.getMaxLocals(),mg.getMaxStack()); | |||
int pos=0; | |||
if ( !mg.isStatic() ) { | |||
if (mg.getName().equals(Constants.CONSTRUCTOR_NAME)){ | |||
Frame._this = new UninitializedObjectType(new ObjectType(mg.getClassName()),true,-1); // was clazz.getClassName | |||
initialFrame.getLocals().set(pos++, Frame._this); | |||
} else { | |||
Frame._this = null; | |||
initialFrame.getLocals().set(pos++, new ObjectType(mg.getClassName())); | |||
} | |||
} | |||
Type[] argtypes = mg.getArgumentTypes(); | |||
int twoslotoffset = 0; | |||
for (int j=0; j<argtypes.length; j++){ | |||
if (argtypes[j] == Type.SHORT || argtypes[j] == Type.BYTE || argtypes[j] == Type.CHAR || argtypes[j] == Type.BOOLEAN){ | |||
argtypes[j] = Type.INT; | |||
} | |||
initialFrame.getLocals().set(twoslotoffset + j + (mg.isStatic()?0:1), argtypes[j]); | |||
if (argtypes[j].getSize() == 2) { | |||
twoslotoffset++; | |||
initialFrame.getLocals().set(twoslotoffset + j + (mg.isStatic()?0:1), Type.TOP); // should that be 'top' too (as in filler...) | |||
} | |||
pos = twoslotoffset + j + (mg.isStatic()?0:1); | |||
} | |||
initialFrame.getLocals().haveSet(pos+1); | |||
return initialFrame; | |||
} | |||
/** | |||
* Constructs the initial frame for a method then applies each of the table entries in turn, the return value is | |||
* an array of complete 'unpacked' frames. | |||
*/ | |||
public static Frame[] reconstructFrames(MethodGen method,StackMapTable table) { | |||
boolean debug=true; | |||
StringBuffer offsets = new StringBuffer(); | |||
Frame theFrame = StackMapHelper.createInitialFrame(method); | |||
if (debug) System.out.println("Initial frame is "+theFrame); | |||
StackMapFrame[] frames = table.getStackMap(); | |||
int offset=0; | |||
Frame[] fullFrames = new Frame[frames.length+1]; | |||
fullFrames[0] = (Frame)theFrame.clone(); | |||
for (int f = 0; f < frames.length; f++) { | |||
StackMapFrame frame = frames[f]; | |||
if (f==0) offset=frame.getByteCodeOffset(); | |||
else offset+=1+frame.getByteCodeOffset(); | |||
theFrame.apply(frame); | |||
fullFrames[f+1]=(Frame)theFrame.clone(); | |||
offsets.append(offset+" "); | |||
if (debug) System.out.println("Current frame (offset="+offset+") now "+theFrame); | |||
} | |||
System.err.println("Frame offsets: "+offsets.toString()); | |||
return fullFrames; | |||
} | |||
} |
@@ -0,0 +1,127 @@ | |||
package org.aspectj.apache.bcel.verifier.utility; | |||
/* ==================================================================== | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2001 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, | |||
* if any, must include the following acknowledgment: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowledgment may appear in the software itself, | |||
* if and wherever such third-party acknowledgments normally appear. | |||
* | |||
* 4. The names "Apache" and "Apache Software Foundation" and | |||
* "Apache BCEL" must not be used to endorse or promote products | |||
* derived from this software without prior written permission. For | |||
* written permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache", | |||
* "Apache BCEL", nor may "Apache" appear in their name, without | |||
* prior written permission of the Apache Software Foundation. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
import org.aspectj.apache.bcel.generic.*; | |||
/** | |||
* This interface defines properties of JVM bytecode subroutines. | |||
* Note that it is 'abused' to maintain the top-level code in a | |||
* consistent fashion, too. | |||
* | |||
* @version $Id$ | |||
* @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A> | |||
*/ | |||
public interface Subroutine{ | |||
/** | |||
* Returns all the JsrInstructions that have the | |||
* first instruction of this subroutine as their target. | |||
* <B>Must not be invoked on the 'top-level subroutine'.</B> | |||
*/ | |||
public InstructionHandle[] getEnteringJsrInstructions(); | |||
/** | |||
* Returns the one and only RET that leaves the subroutine. | |||
* Note that JustIce has a pretty rigid notion of a subroutine. | |||
* <B>Must not be invoked on the 'top-level subroutine'.</B> | |||
* | |||
* @see org.aspectj.apache.bcel.verifier.structurals.Subroutines | |||
*/ | |||
public InstructionHandle getLeavingRET(); | |||
/** | |||
* Returns all instructions that together form this subroutine. | |||
* Note that an instruction is part of exactly one subroutine | |||
* (the top-level code is considered to be a special subroutine) - | |||
* else it is not reachable at all (dead code). | |||
*/ | |||
public InstructionHandle[] getInstructions(); | |||
/** | |||
* Returns if the given InstructionHandle refers to an instruction | |||
* that is part of this subroutine. This is a convenience method | |||
* that saves iteration over the InstructionHandle objects returned | |||
* by getInstructions(). | |||
* | |||
* @see #getInstructions() | |||
*/ | |||
public boolean contains(InstructionHandle inst); | |||
/** | |||
* Returns an int[] containing the indices of the local variable slots | |||
* accessed by this Subroutine (read-accessed, write-accessed or both); | |||
* local variables referenced by subroutines of this subroutine are | |||
* not included. | |||
* | |||
* @see #getRecursivelyAccessedLocalsIndices() | |||
*/ | |||
public int[] getAccessedLocalsIndices(); | |||
/** | |||
* Returns an int[] containing the indices of the local variable slots | |||
* accessed by this Subroutine (read-accessed, write-accessed or both); | |||
* local variables referenced by subroutines of this subroutine are | |||
* included. | |||
* | |||
* @see #getAccessedLocalsIndices() | |||
*/ | |||
public int[] getRecursivelyAccessedLocalsIndices(); | |||
/** | |||
* Returns the subroutines that are directly called from this subroutine. | |||
*/ | |||
public Subroutine[] subSubs(); | |||
} |
@@ -0,0 +1,293 @@ | |||
package org.aspectj.apache.bcel.verifier.utility; | |||
import java.util.HashSet; | |||
import java.util.Iterator; | |||
import org.aspectj.apache.bcel.generic.ASTORE; | |||
import org.aspectj.apache.bcel.generic.IndexedInstruction; | |||
import org.aspectj.apache.bcel.generic.Instruction; | |||
import org.aspectj.apache.bcel.generic.InstructionHandle; | |||
import org.aspectj.apache.bcel.generic.JsrInstruction; | |||
import org.aspectj.apache.bcel.generic.LocalVariableInstruction; | |||
import org.aspectj.apache.bcel.generic.RET; | |||
import org.aspectj.apache.bcel.verifier.exc.AssertionViolatedException; | |||
import org.aspectj.apache.bcel.verifier.exc.StructuralCodeConstraintException; | |||
class SubroutineImpl implements Subroutine { | |||
private final Subroutines subroutines; | |||
/** | |||
* UNSET, a symbol for an uninitialized localVariable | |||
* field. This is used for the "top-level" Subroutine; | |||
* i.e. no subroutine. | |||
*/ | |||
private static final int UNSET = -1; | |||
/** The instructions that belong to this subroutine. */ | |||
private HashSet instructions = new HashSet(); // Elements: InstructionHandle | |||
/** | |||
* The Local Variable slot where the first | |||
* instruction of this subroutine (an ASTORE) stores | |||
* the JsrInstruction's ReturnAddress in and | |||
* the RET of this subroutine operates on. | |||
*/ | |||
int localVariable = UNSET; | |||
/** | |||
* The JSR or JSR_W instructions that define this | |||
* subroutine by targeting it. | |||
*/ | |||
HashSet theJSRs = new HashSet(); | |||
/** | |||
* The RET instruction that leaves this subroutine. | |||
*/ | |||
InstructionHandle theRET; | |||
/** | |||
* The default constructor. | |||
* @param subroutines TODO | |||
*/ | |||
public SubroutineImpl(Subroutines subroutines){ | |||
this.subroutines = subroutines; | |||
} | |||
/** | |||
* Adds a new JSR or JSR_W that has this subroutine as its target. | |||
*/ | |||
public void addEnteringJsrInstruction(InstructionHandle jsrInst){ | |||
if ( (jsrInst == null) || (! (jsrInst.getInstruction() instanceof JsrInstruction))){ | |||
throw new AssertionViolatedException("Expecting JsrInstruction InstructionHandle."); | |||
} | |||
if (localVariable == UNSET){ | |||
throw new AssertionViolatedException("Set the localVariable first!"); | |||
} | |||
else{ | |||
// Something is wrong when an ASTORE is targeted that does not operate on the same local variable than the rest of the | |||
// JsrInstruction-targets and the RET. | |||
// (We don't know out leader here so we cannot check if we're really targeted!) | |||
if (localVariable != ((ASTORE) (((JsrInstruction) jsrInst.getInstruction()).getTarget().getInstruction())).getIndex()){ | |||
throw new AssertionViolatedException("Setting a wrong JsrInstruction."); | |||
} | |||
} | |||
theJSRs.add(jsrInst); | |||
} | |||
// --- | |||
/* | |||
* Refer to the Subroutine interface for documentation. | |||
*/ | |||
public boolean contains(InstructionHandle inst){ | |||
return instructions.contains(inst); | |||
} | |||
/* | |||
* Satisfies Subroutine.getAccessedLocalIndices(). | |||
*/ | |||
public int[] getAccessedLocalsIndices(){ | |||
//TODO: Implement caching. | |||
HashSet acc = new HashSet(); | |||
if (theRET == null && this != this.subroutines.TOPLEVEL){ | |||
throw new AssertionViolatedException("This subroutine object must be built up completely before calculating accessed locals."); | |||
} | |||
Iterator i = instructions.iterator(); | |||
while (i.hasNext()){ | |||
InstructionHandle ih = (InstructionHandle) i.next(); | |||
// RET is not a LocalVariableInstruction in the current version of BCEL. | |||
if (ih.getInstruction() instanceof LocalVariableInstruction || ih.getInstruction() instanceof RET){ | |||
int idx = ((IndexedInstruction) (ih.getInstruction())).getIndex(); | |||
acc.add(new Integer(idx)); | |||
// LONG? DOUBLE?. | |||
try{ | |||
// LocalVariableInstruction instances are typed without the need to look into | |||
// the constant pool. | |||
if (ih.getInstruction() instanceof LocalVariableInstruction){ | |||
int s = ((LocalVariableInstruction) ih.getInstruction()).getType(null).getSize(); | |||
if (s==2) acc.add(new Integer(idx+1)); | |||
} | |||
} | |||
catch(RuntimeException re){ | |||
throw new AssertionViolatedException("Oops. BCEL did not like NULL as a ConstantPoolGen object."); | |||
} | |||
} | |||
} | |||
int[] ret = new int[acc.size()]; | |||
i = acc.iterator(); | |||
int j=-1; | |||
while (i.hasNext()){ | |||
j++; | |||
ret[j] = ((Integer) i.next()).intValue(); | |||
} | |||
return ret; | |||
} | |||
/* | |||
* Refer to the Subroutine interface for documentation. | |||
*/ | |||
public InstructionHandle[] getEnteringJsrInstructions(){ | |||
if (this == this.subroutines.TOPLEVEL) { | |||
throw new AssertionViolatedException("getLeavingRET() called on top level pseudo-subroutine."); | |||
} | |||
InstructionHandle[] jsrs = new InstructionHandle[theJSRs.size()]; | |||
return (InstructionHandle[]) (theJSRs.toArray(jsrs)); | |||
} | |||
/* | |||
* Refer to the Subroutine interface for documentation. | |||
*/ | |||
public InstructionHandle[] getInstructions(){ | |||
InstructionHandle[] ret = new InstructionHandle[instructions.size()]; | |||
return (InstructionHandle[]) instructions.toArray(ret); | |||
} | |||
/* | |||
* Refer to the Subroutine interface for documentation. | |||
*/ | |||
public InstructionHandle getLeavingRET(){ | |||
if (this == this.subroutines.TOPLEVEL) { | |||
throw new AssertionViolatedException("getLeavingRET() called on top level pseudo-subroutine."); | |||
} | |||
return theRET; | |||
} | |||
/* Satisfies Subroutine.getRecursivelyAccessedLocalsIndices(). */ | |||
public int[] getRecursivelyAccessedLocalsIndices(){ | |||
HashSet s = new HashSet(); | |||
int[] lvs = getAccessedLocalsIndices(); | |||
for (int j=0; j<lvs.length; j++){ | |||
s.add(new Integer(lvs[j])); | |||
} | |||
_getRecursivelyAccessedLocalsIndicesHelper(s, this.subSubs()); | |||
int[] ret = new int[s.size()]; | |||
Iterator i = s.iterator(); | |||
int j=-1; | |||
while (i.hasNext()){ | |||
j++; | |||
ret[j] = ((Integer) i.next()).intValue(); | |||
} | |||
return ret; | |||
} | |||
/* | |||
* Satisfies Subroutine.subSubs(). | |||
*/ | |||
public Subroutine[] subSubs(){ | |||
HashSet h = new HashSet(); | |||
Iterator i = instructions.iterator(); | |||
while (i.hasNext()){ | |||
Instruction inst = ((InstructionHandle) i.next()).getInstruction(); | |||
if (inst instanceof JsrInstruction){ | |||
InstructionHandle targ = ((JsrInstruction) inst).getTarget(); | |||
h.add(this.subroutines.getSubroutine(targ)); | |||
} | |||
} | |||
Subroutine[] ret = new Subroutine[h.size()]; | |||
return (Subroutine[]) h.toArray(ret); | |||
} | |||
/** | |||
* Returns a String representation of this object, merely | |||
* for debugging purposes. | |||
* (Internal) Warning: Verbosity on a problematic subroutine may cause | |||
* stack overflow errors due to recursive subSubs() calls. | |||
* Don't use this, then. | |||
*/ | |||
public String toString(){ | |||
String ret = "Subroutine (size:"+instructions.size()+"instructions): Local variable is '"+localVariable+"', JSRs are '"+theJSRs+"', RET is '"+theRET+"', Instructions: '"+instructions.toString()+"'."; | |||
ret += " Accessed local variable slots: '"; | |||
int[] alv = getAccessedLocalsIndices(); | |||
for (int i=0; i<alv.length; i++){ | |||
ret += alv[i]+" "; | |||
} | |||
ret+="'."; | |||
ret += " Recursively (via subsub...routines) accessed local variable slots: '"; | |||
alv = getRecursivelyAccessedLocalsIndices(); | |||
for (int i=0; i<alv.length; i++){ | |||
ret += alv[i]+" "; | |||
} | |||
ret+="'."; | |||
return ret; | |||
} | |||
/** | |||
* A recursive helper method for getRecursivelyAccessedLocalsIndices(). | |||
* @see #getRecursivelyAccessedLocalsIndices() | |||
*/ | |||
private void _getRecursivelyAccessedLocalsIndicesHelper(HashSet s, Subroutine[] subs){ | |||
for (int i=0; i<subs.length; i++){ | |||
int[] lvs = subs[i].getAccessedLocalsIndices(); | |||
for (int j=0; j<lvs.length; j++){ | |||
s.add(new Integer(lvs[j])); | |||
} | |||
if(subs[i].subSubs().length != 0){ | |||
_getRecursivelyAccessedLocalsIndicesHelper(s, subs[i].subSubs()); | |||
} | |||
} | |||
} | |||
/* | |||
* Adds an instruction to this subroutine. | |||
* All instructions must have been added before invoking setLeavingRET(). | |||
* @see #setLeavingRET | |||
*/ | |||
void addInstruction(InstructionHandle ih){ | |||
if (theRET != null){ | |||
throw new AssertionViolatedException("All instructions must have been added before invoking setLeavingRET()."); | |||
} | |||
instructions.add(ih); | |||
} | |||
/** | |||
* Sets the leaving RET instruction. Must be invoked after all instructions are added. | |||
* Must not be invoked for top-level 'subroutine'. | |||
*/ | |||
void setLeavingRET(){ | |||
if (localVariable == UNSET){ | |||
throw new AssertionViolatedException("setLeavingRET() called for top-level 'subroutine' or forgot to set local variable first."); | |||
} | |||
Iterator iter = instructions.iterator(); | |||
InstructionHandle ret = null; | |||
while(iter.hasNext()){ | |||
InstructionHandle actual = (InstructionHandle) iter.next(); | |||
if (actual.getInstruction() instanceof RET){ | |||
if (ret != null){ | |||
throw new StructuralCodeConstraintException("Subroutine with more then one RET detected: '"+ret+"' and '"+actual+"'."); | |||
} | |||
else{ | |||
ret = actual; | |||
} | |||
} | |||
} | |||
if (ret == null){ | |||
throw new StructuralCodeConstraintException("Subroutine without a RET detected."); | |||
} | |||
if (((RET) ret.getInstruction()).getIndex() != localVariable){ | |||
throw new StructuralCodeConstraintException("Subroutine uses '"+ret+"' which does not match the correct local variable '"+localVariable+"'."); | |||
} | |||
theRET = ret; | |||
} | |||
/* | |||
* Sets the local variable slot the ASTORE that is targeted | |||
* by the JsrInstructions of this subroutine operates on. | |||
* This subroutine's RET operates on that same local variable | |||
* slot, of course. | |||
*/ | |||
void setLocalVariable(int i){ | |||
if (localVariable != UNSET){ | |||
throw new AssertionViolatedException("localVariable set twice."); | |||
} | |||
else{ | |||
localVariable = i; | |||
} | |||
} | |||
} |
@@ -0,0 +1,395 @@ | |||
package org.aspectj.apache.bcel.verifier.utility; | |||
/* ==================================================================== | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2001 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, | |||
* if any, must include the following acknowledgment: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowledgment may appear in the software itself, | |||
* if and wherever such third-party acknowledgments normally appear. | |||
* | |||
* 4. The names "Apache" and "Apache Software Foundation" and | |||
* "Apache BCEL" must not be used to endorse or promote products | |||
* derived from this software without prior written permission. For | |||
* written permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache", | |||
* "Apache BCEL", nor may "Apache" appear in their name, without | |||
* prior written permission of the Apache Software Foundation. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
import org.aspectj.apache.bcel.generic.*; | |||
import org.aspectj.apache.bcel.verifier.exc.*; | |||
import java.awt.Color; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.Enumeration; | |||
import java.util.HashSet; | |||
import java.util.Hashtable; | |||
import java.util.Iterator; | |||
/** | |||
* Instances of this class contain information about the subroutines | |||
* found in a code array of a method. | |||
* This implementation considers the top-level (the instructions | |||
* reachable without a JSR or JSR_W starting off from the first | |||
* instruction in a code array of a method) being a special subroutine; | |||
* see getTopLevel() for that. | |||
* Please note that the definition of subroutines in the Java Virtual | |||
* Machine Specification, Second Edition is somewhat incomplete. | |||
* Therefore, JustIce uses an own, more rigid notion. | |||
* Basically, a subroutine is a piece of code that starts at the target | |||
* of a JSR of JSR_W instruction and ends at a corresponding RET | |||
* instruction. Note also that the control flow of a subroutine | |||
* may be complex and non-linear; and that subroutines may be nested. | |||
* JustIce also mandates subroutines not to be protected by exception | |||
* handling code (for the sake of control flow predictability). | |||
* To understand JustIce's notion of subroutines, please read | |||
* | |||
* TODO: refer to the paper. | |||
* | |||
* @version $Id$ | |||
* @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A> | |||
* @see #getTopLevel() | |||
*/ | |||
public class Subroutines{ | |||
/** | |||
* The Hashtable containing the subroutines found. | |||
* Key: InstructionHandle of the leader of the subroutine. | |||
* Elements: SubroutineImpl objects. | |||
*/ | |||
private Hashtable subroutines = new Hashtable(); | |||
/** | |||
* This is referring to a special subroutine, namely the | |||
* top level. This is not really a subroutine but we use | |||
* it to distinguish between top level instructions and | |||
* unreachable instructions. | |||
*/ | |||
public final Subroutine TOPLEVEL; | |||
public Subroutines(MethodGen mg){ | |||
InstructionHandle[] all = mg.getInstructionList().getInstructionHandles(); | |||
CodeExceptionGen[] handlers = mg.getExceptionHandlers(); | |||
// Define our "Toplevel" fake subroutine. | |||
TOPLEVEL = new SubroutineImpl(this); | |||
// Calculate "real" subroutines. | |||
HashSet sub_leaders = new HashSet(); // Elements: InstructionHandle | |||
InstructionHandle ih = all[0]; | |||
for (int i=0; i<all.length; i++){ | |||
Instruction inst = all[i].getInstruction(); | |||
if (inst instanceof JsrInstruction){ | |||
sub_leaders.add(((JsrInstruction) inst).getTarget()); | |||
} | |||
} | |||
// Build up the database. | |||
Iterator iter = sub_leaders.iterator(); | |||
while (iter.hasNext()){ | |||
SubroutineImpl sr = new SubroutineImpl(this); | |||
InstructionHandle astore = (InstructionHandle) (iter.next()); | |||
sr.setLocalVariable( ((ASTORE) (astore.getInstruction())).getIndex() ); | |||
subroutines.put(astore, sr); | |||
} | |||
// Fake it a bit. We want a virtual "TopLevel" subroutine. | |||
subroutines.put(all[0], TOPLEVEL); | |||
sub_leaders.add(all[0]); | |||
// Tell the subroutines about their JsrInstructions. | |||
// Note that there cannot be a JSR targeting the top-level | |||
// since "Jsr 0" is disallowed in Pass 3a. | |||
// Instructions shared by a subroutine and the toplevel are | |||
// disallowed and checked below, after the BFS. | |||
for (int i=0; i<all.length; i++){ | |||
Instruction inst = all[i].getInstruction(); | |||
if (inst instanceof JsrInstruction){ | |||
InstructionHandle leader = ((JsrInstruction) inst).getTarget(); | |||
((SubroutineImpl) getSubroutine(leader)).addEnteringJsrInstruction(all[i]); | |||
} | |||
} | |||
// Now do a BFS from every subroutine leader to find all the | |||
// instructions that belong to a subroutine. | |||
HashSet instructions_assigned = new HashSet(); // we don't want to assign an instruction to two or more Subroutine objects. | |||
Hashtable colors = new Hashtable(); //Graph colouring. Key: InstructionHandle, Value: java.awt.Color . | |||
iter = sub_leaders.iterator(); | |||
while (iter.hasNext()){ | |||
// Do some BFS with "actual" as the root of the graph. | |||
InstructionHandle actual = (InstructionHandle) (iter.next()); | |||
// Init colors | |||
for (int i=0; i<all.length; i++){ | |||
colors.put(all[i], Color.white); | |||
} | |||
colors.put(actual, Color.gray); | |||
// Init Queue | |||
ArrayList Q = new ArrayList(); | |||
Q.add(actual); // add(Obj) adds to the end, remove(0) removes from the start. | |||
/* BFS ALGORITHM MODIFICATION: Start out with multiple "root" nodes, as exception handlers are starting points of top-level code, too. [why top-level? TODO: Refer to the special JustIce notion of subroutines.]*/ | |||
if (actual == all[0]){ | |||
for (int j=0; j<handlers.length; j++){ | |||
colors.put(handlers[j].getHandlerPC(), Color.gray); | |||
Q.add(handlers[j].getHandlerPC()); | |||
} | |||
} | |||
/* CONTINUE NORMAL BFS ALGORITHM */ | |||
// Loop until Queue is empty | |||
while (Q.size() != 0){ | |||
InstructionHandle u = (InstructionHandle) Q.remove(0); | |||
InstructionHandle[] successors = getSuccessors(u); | |||
for (int i=0; i<successors.length; i++){ | |||
if (((Color) colors.get(successors[i])) == Color.white){ | |||
colors.put(successors[i], Color.gray); | |||
Q.add(successors[i]); | |||
} | |||
} | |||
colors.put(u, Color.black); | |||
} | |||
// BFS ended above. | |||
for (int i=0; i<all.length; i++){ | |||
if (colors.get(all[i]) == Color.black){ | |||
((SubroutineImpl) (actual==all[0]?getTopLevel():getSubroutine(actual))).addInstruction(all[i]); | |||
if (instructions_assigned.contains(all[i])){ | |||
throw new StructuralCodeConstraintException("Instruction '"+all[i]+"' is part of more than one subroutine (or of the top level and a subroutine)."); | |||
} | |||
else{ | |||
instructions_assigned.add(all[i]); | |||
} | |||
} | |||
} | |||
if (actual != all[0]){// If we don't deal with the top-level 'subroutine' | |||
((SubroutineImpl) getSubroutine(actual)).setLeavingRET(); | |||
} | |||
} | |||
// Now make sure no instruction of a Subroutine is protected by exception handling code | |||
// as is mandated by JustIces notion of subroutines. | |||
for (int i=0; i<handlers.length; i++){ | |||
InstructionHandle _protected = handlers[i].getStartPC(); | |||
while (_protected != handlers[i].getEndPC().getNext()){// Note the inclusive/inclusive notation of "generic API" exception handlers! | |||
Enumeration subs = subroutines.elements(); | |||
while (subs.hasMoreElements()){ | |||
Subroutine sub = (Subroutine) subs.nextElement(); | |||
if (sub != subroutines.get(all[0])){ // We don't want to forbid top-level exception handlers. | |||
if (sub.contains(_protected)){ | |||
throw new StructuralCodeConstraintException("Subroutine instruction '"+_protected+"' is protected by an exception handler, '"+handlers[i]+"'. This is forbidden by the JustIce verifier due to its clear definition of subroutines."); | |||
} | |||
} | |||
} | |||
_protected = _protected.getNext(); | |||
} | |||
} | |||
// Now make sure no subroutine is calling a subroutine | |||
// that uses the same local variable for the RET as themselves | |||
// (recursively). | |||
// This includes that subroutines may not call themselves | |||
// recursively, even not through intermediate calls to other | |||
// subroutines. | |||
noRecursiveCalls(getTopLevel(), new HashSet()); | |||
} | |||
/** | |||
* This (recursive) utility method makes sure that | |||
* no subroutine is calling a subroutine | |||
* that uses the same local variable for the RET as themselves | |||
* (recursively). | |||
* This includes that subroutines may not call themselves | |||
* recursively, even not through intermediate calls to other | |||
* subroutines. | |||
* | |||
* @throws StructuralCodeConstraintException if the above constraint is not satisfied. | |||
*/ | |||
private void noRecursiveCalls(Subroutine sub, HashSet set){ | |||
Subroutine[] subs = sub.subSubs(); | |||
for (int i=0; i<subs.length; i++){ | |||
int index = ((RET) (subs[i].getLeavingRET().getInstruction())).getIndex(); | |||
if (!set.add(new Integer(index))){ | |||
// Don't use toString() here because of possibly infinite recursive subSubs() calls then. | |||
SubroutineImpl si = (SubroutineImpl) subs[i]; | |||
throw new StructuralCodeConstraintException("Subroutine with local variable '"+si.localVariable+"', JSRs '"+si.theJSRs+"', RET '"+si.theRET+"' is called by a subroutine which uses the same local variable index as itself; maybe even a recursive call? JustIce's clean definition of a subroutine forbids both."); | |||
} | |||
noRecursiveCalls(subs[i], set); | |||
set.remove(new Integer(index)); | |||
} | |||
} | |||
/** | |||
* Returns the Subroutine object associated with the given | |||
* leader (that is, the first instruction of the subroutine). | |||
* You must not use this to get the top-level instructions | |||
* modeled as a Subroutine object. | |||
* | |||
* @see #getTopLevel() | |||
*/ | |||
public Subroutine getSubroutine(InstructionHandle leader){ | |||
Subroutine ret = (Subroutine) subroutines.get(leader); | |||
if (ret == null){ | |||
throw new AssertionViolatedException("Subroutine requested for an InstructionHandle that is not a leader of a subroutine."); | |||
} | |||
if (ret == TOPLEVEL){ | |||
throw new AssertionViolatedException("TOPLEVEL special subroutine requested; use getTopLevel()."); | |||
} | |||
return ret; | |||
} | |||
/** | |||
* Returns the subroutine object associated with the | |||
* given instruction. This is a costly operation, you | |||
* should consider using getSubroutine(InstructionHandle). | |||
* Returns 'null' if the given InstructionHandle lies | |||
* in so-called 'dead code', i.e. code that can never | |||
* be executed. | |||
* | |||
* @see #getSubroutine(InstructionHandle) | |||
* @see #getTopLevel() | |||
*/ | |||
public Subroutine subroutineOf(InstructionHandle any){ | |||
Iterator i = subroutines.values().iterator(); | |||
while (i.hasNext()){ | |||
Subroutine s = (Subroutine) i.next(); | |||
if (s.contains(any)) return s; | |||
} | |||
System.err.println("DEBUG: Please verify '"+any+"' lies in dead code."); | |||
return null; | |||
//throw new AssertionViolatedException("No subroutine for InstructionHandle found (DEAD CODE?)."); | |||
} | |||
/** | |||
* For easy handling, the piece of code that is <B>not</B> a | |||
* subroutine, the top-level, is also modeled as a Subroutine | |||
* object. | |||
* It is a special Subroutine object where <B>you must not invoke | |||
* getEnteringJsrInstructions() or getLeavingRET()</B>. | |||
* | |||
* @see Subroutine#getEnteringJsrInstructions() | |||
* @see Subroutine#getLeavingRET() | |||
*/ | |||
public Subroutine getTopLevel(){ | |||
return TOPLEVEL; | |||
} | |||
/** | |||
* A utility method that calculates the successors of a given InstructionHandle | |||
* <B>in the same subroutine</B>. That means, a RET does not have any successors | |||
* as defined here. A JsrInstruction has its physical successor as its successor | |||
* (opposed to its target) as defined here. | |||
*/ | |||
private static InstructionHandle[] getSuccessors(InstructionHandle instruction){ | |||
final InstructionHandle[] empty = new InstructionHandle[0]; | |||
final InstructionHandle[] single = new InstructionHandle[1]; | |||
final InstructionHandle[] pair = new InstructionHandle[2]; | |||
Instruction inst = instruction.getInstruction(); | |||
if (inst instanceof RET){ | |||
return empty; | |||
} | |||
// Terminates method normally. | |||
if (inst instanceof ReturnInstruction){ | |||
return empty; | |||
} | |||
// Terminates method abnormally, because JustIce mandates | |||
// subroutines not to be protected by exception handlers. | |||
if (inst instanceof ATHROW){ | |||
return empty; | |||
} | |||
// See method comment. | |||
if (inst instanceof JsrInstruction){ | |||
single[0] = instruction.getNext(); | |||
return single; | |||
} | |||
if (inst instanceof GotoInstruction){ | |||
single[0] = ((GotoInstruction) inst).getTarget(); | |||
return single; | |||
} | |||
if (inst instanceof BranchInstruction){ | |||
if (inst instanceof Select){ | |||
// BCEL's getTargets() returns only the non-default targets, | |||
// thanks to Eli Tilevich for reporting. | |||
InstructionHandle[] matchTargets = ((Select) inst).getTargets(); | |||
InstructionHandle[] ret = new InstructionHandle[matchTargets.length+1]; | |||
ret[0] = ((Select) inst).getTarget(); | |||
System.arraycopy(matchTargets, 0, ret, 1, matchTargets.length); | |||
return ret; | |||
} | |||
else{ | |||
pair[0] = instruction.getNext(); | |||
pair[1] = ((BranchInstruction) inst).getTarget(); | |||
return pair; | |||
} | |||
} | |||
// default case: Fall through. | |||
single[0] = instruction.getNext(); | |||
return single; | |||
} | |||
/** | |||
* Returns a String representation of this object; merely for debugging puposes. | |||
*/ | |||
public String toString(){ | |||
return "---\n"+subroutines.toString()+"\n---\n"; | |||
} | |||
public Collection getIndividualSubroutines() { | |||
return subroutines.values(); | |||
} | |||
} |
@@ -0,0 +1,104 @@ | |||
package org.aspectj.apache.bcel.verifier.utility; | |||
/* ==================================================================== | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2001 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, | |||
* if any, must include the following acknowledgment: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowledgment may appear in the software itself, | |||
* if and wherever such third-party acknowledgments normally appear. | |||
* | |||
* 4. The names "Apache" and "Apache Software Foundation" and | |||
* "Apache BCEL" must not be used to endorse or promote products | |||
* derived from this software without prior written permission. For | |||
* written permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache", | |||
* "Apache BCEL", nor may "Apache" appear in their name, without | |||
* prior written permission of the Apache Software Foundation. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
import org.aspectj.apache.bcel.*; | |||
import org.aspectj.apache.bcel.generic.*; | |||
/** | |||
* This class represents an uninitialized object type; see The Java | |||
* Virtual Machine Specification, Second Edition, page 147: 4.9.4 for | |||
* more details. | |||
* | |||
* @version $Id$ | |||
* @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A> | |||
*/ | |||
public class UninitializedObjectType extends ReferenceType implements Constants{ | |||
/** The "initialized" version. */ | |||
private ObjectType initialized; | |||
private int loc; | |||
/** Creates a new instance. */ | |||
public UninitializedObjectType(ObjectType t,boolean isThis,int loc){ | |||
super(T_UNKNOWN, "<UNINITIALIZED OBJECT OF TYPE '"+t.getClassName()+"'>"); | |||
initialized = t; | |||
this.loc = loc; | |||
this.isthis = isThis; | |||
} | |||
private boolean isthis; | |||
/** | |||
* Returns the ObjectType of the same class as the one of the uninitialized object | |||
* represented by this UninitializedObjectType instance. | |||
*/ | |||
public ObjectType getInitialized(){ | |||
return initialized; | |||
} | |||
public boolean isThis() { return isthis; } | |||
public int getLoc() { return loc; } | |||
/** | |||
* Returns true on equality of this and o. | |||
* Equality means the ObjectType instances of "initialized" | |||
* equal one another in this and the o instance. | |||
* | |||
*/ | |||
public boolean equals(Object o){ | |||
if (! (o instanceof UninitializedObjectType)) return false; | |||
return initialized.equals(((UninitializedObjectType)o).initialized); | |||
} | |||
} |
@@ -0,0 +1,23 @@ | |||
public class Code { | |||
public void method1() { | |||
int i = 1; | |||
if (true) { | |||
System.out.println("x"); | |||
} | |||
i=2; | |||
try { | |||
int j = 5; | |||
i=3; | |||
System.out.println("y"); | |||
System.out.println("z"); | |||
} catch (Throwable t) { | |||
System.out.println("e"); | |||
int k=3; | |||
} | |||
if (true) { | |||
i = 4; | |||
System.out.println("a"); | |||
} | |||
} | |||
} |
@@ -0,0 +1,189 @@ | |||
/* ******************************************************************* | |||
* Copyright (c) 2004 IBM | |||
* All rights reserved. | |||
* This program and the accompanying materials are made available | |||
* under the terms of the Common Public License v1.0 | |||
* which accompanies this distribution and is available at | |||
* http://www.eclipse.org/legal/cpl-v10.html | |||
* | |||
* Contributors: | |||
* Andy Clement - initial implementation {date} | |||
* ******************************************************************/ | |||
package org.aspectj.apache.bcel.classfile.tests.preverifier; | |||
import java.io.File; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.aspectj.apache.bcel.Repository; | |||
import org.aspectj.apache.bcel.classfile.Attribute; | |||
import org.aspectj.apache.bcel.classfile.JavaClass; | |||
import org.aspectj.apache.bcel.classfile.Method; | |||
import org.aspectj.apache.bcel.classfile.annotation.Annotation; | |||
import org.aspectj.apache.bcel.generic.ClassGen; | |||
import org.aspectj.apache.bcel.generic.ConstantPoolGen; | |||
import org.aspectj.apache.bcel.generic.ObjectType; | |||
import org.aspectj.apache.bcel.generic.annotation.AnnotationGen; | |||
import org.aspectj.apache.bcel.generic.annotation.ElementNameValuePairGen; | |||
import org.aspectj.apache.bcel.generic.annotation.ElementValueGen; | |||
import org.aspectj.apache.bcel.generic.annotation.SimpleElementValueGen; | |||
import org.aspectj.apache.bcel.util.ClassPath; | |||
import org.aspectj.apache.bcel.util.SyntheticRepository; | |||
import junit.framework.TestCase; | |||
/** | |||
* Super class for the Java5 tests, includes various helper methods. | |||
*/ | |||
public class PreverifierBaseTestCase extends TestCase { | |||
private boolean verbose = false; | |||
protected File createTestdataFile(String name) { | |||
return new File("testdata"+File.separator+name); | |||
} | |||
protected JavaClass getClassFromJar(String clazzname) throws ClassNotFoundException { | |||
SyntheticRepository repos = createRepos("Java6BuiltCode.jar"); | |||
Repository.setRepository(repos); | |||
return repos.loadClass(clazzname); | |||
} | |||
protected JavaClass getClassFromDir(String dir,String clazzname) throws ClassNotFoundException { | |||
SyntheticRepository repos = createReposForDirectory(dir); | |||
Repository.setRepository(repos); | |||
return repos.loadClass(clazzname); | |||
} | |||
protected Method getMethod(JavaClass cl,String methodname) { | |||
Method[] methods = cl.getMethods(); | |||
for (int i = 0; i < methods.length; i++) { | |||
Method m = methods[i]; | |||
if (m.getName().equals(methodname)) { | |||
return m; | |||
} | |||
} | |||
return null; | |||
} | |||
protected Method getMethod(JavaClass cl,String methodname,int count) { | |||
Method[] methods = cl.getMethods(); | |||
for (int i = 0; i < methods.length; i++) { | |||
Method m = methods[i]; | |||
if (m.getName().equals(methodname)) { | |||
if (count==0) return m; | |||
count--; | |||
} | |||
} | |||
return null; | |||
} | |||
protected Method getMethod(ClassGen cg,String methodname,int count) { | |||
Method[] methods = cg.getMethods(); | |||
for (int i = 0; i < methods.length; i++) { | |||
Method m = methods[i]; | |||
if (m.getName().equals(methodname)) { | |||
if (count==0) return m; | |||
count--; | |||
} | |||
} | |||
return null; | |||
} | |||
protected boolean wipe(String name) { | |||
return new File("testdata"+File.separator+name).delete(); | |||
} | |||
protected boolean wipe(String dir, String name) { | |||
boolean b = wipe(dir+File.separator+name); | |||
String[] files = new File(dir).list(); | |||
if (files==null || files.length==0) { | |||
new File(dir).delete(); // Why does this not succeed? stupid thing | |||
} | |||
return b; | |||
} | |||
public SyntheticRepository createRepos(String cpentry) { | |||
ClassPath cp = new ClassPath( | |||
"testdata"+File.separator+cpentry+File.pathSeparator+"testdata"+File.separator+"stackmap"+File.pathSeparator+ | |||
System.getProperty("sun.boot.class.path")); | |||
return SyntheticRepository.getInstance(cp); | |||
} | |||
public SyntheticRepository createReposForDirectory(String dir) { | |||
ClassPath cp = new ClassPath(dir+File.pathSeparator+ | |||
System.getProperty("sun.boot.class.path")); | |||
return SyntheticRepository.getInstance(cp); | |||
} | |||
protected Attribute[] findAttribute(String name, JavaClass clazz) { | |||
Attribute[] all = clazz.getAttributes(); | |||
List chosenAttrsList = new ArrayList(); | |||
for (int i = 0; i < all.length; i++) { | |||
if (verbose) System.err.println("Attribute: "+all[i].getName()); | |||
if (all[i].getName().equals(name)) chosenAttrsList.add(all[i]); | |||
} | |||
return (Attribute[])chosenAttrsList.toArray(new Attribute[]{}); | |||
} | |||
protected Attribute findAttribute(String name, Attribute[] all) { | |||
List chosenAttrsList = new ArrayList(); | |||
for (int i = 0; i < all.length; i++) { | |||
if (verbose) System.err.println("Attribute: "+all[i].getName()); | |||
if (all[i].getName().equals(name)) chosenAttrsList.add(all[i]); | |||
} | |||
assertTrue("Should be one match: "+chosenAttrsList.size(),chosenAttrsList.size()==1); | |||
return (Attribute)chosenAttrsList.get(0); | |||
} | |||
protected String dumpAnnotations(Annotation[] as) { | |||
StringBuffer result = new StringBuffer(); | |||
result.append("["); | |||
for (int i = 0; i < as.length; i++) { | |||
Annotation annotation = as[i]; | |||
result.append(annotation.toShortString()); | |||
if (i+1<as.length) result.append(","); | |||
} | |||
result.append("]"); | |||
return result.toString(); | |||
} | |||
protected String dumpAnnotations(AnnotationGen[] as) { | |||
StringBuffer result = new StringBuffer(); | |||
result.append("["); | |||
for (int i = 0; i < as.length; i++) { | |||
AnnotationGen annotation = as[i]; | |||
result.append(annotation.toShortString()); | |||
if (i+1<as.length) result.append(","); | |||
} | |||
result.append("]"); | |||
return result.toString(); | |||
} | |||
protected String dumpAttributes(Attribute[] as) { | |||
StringBuffer result = new StringBuffer(); | |||
result.append("AttributeArray:["); | |||
for (int i = 0; i < as.length; i++) { | |||
Attribute attr = as[i]; | |||
result.append(attr.toString()); | |||
if (i+1<as.length) result.append(","); | |||
} | |||
result.append("]"); | |||
return result.toString(); | |||
} | |||
public AnnotationGen createFruitAnnotation(ConstantPoolGen cp, String aFruit, boolean visibility) { | |||
SimpleElementValueGen evg = new SimpleElementValueGen(ElementValueGen.STRING,cp,aFruit); | |||
ElementNameValuePairGen nvGen = new ElementNameValuePairGen("fruit",evg,cp); | |||
ObjectType t = new ObjectType("SimpleStringAnnotation"); | |||
List elements = new ArrayList(); | |||
elements.add(nvGen); | |||
return new AnnotationGen(t,elements,visibility,cp); | |||
} | |||
} |
@@ -0,0 +1,771 @@ | |||
/* ******************************************************************* | |||
* Copyright (c) 2006 Contributors | |||
* All rights reserved. | |||
* This program and the accompanying materials are made available | |||
* under the terms of the Common Public License v1.0 | |||
* which accompanies this distribution and is available at | |||
* http://www.eclipse.org/legal/cpl-v10.html | |||
* | |||
* Contributors: | |||
* Andy Clement - IBM - 3rd April 2006 - initial implementation | |||
* ******************************************************************/ | |||
package org.aspectj.apache.bcel.classfile.tests.preverifier; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.File; | |||
import java.io.FileNotFoundException; | |||
import java.io.FileOutputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.net.URL; | |||
import java.net.URLClassLoader; | |||
import java.util.Enumeration; | |||
import java.util.zip.ZipEntry; | |||
import java.util.zip.ZipFile; | |||
import org.aspectj.apache.bcel.Repository; | |||
import org.aspectj.apache.bcel.classfile.Attribute; | |||
import org.aspectj.apache.bcel.classfile.ClassFormatException; | |||
import org.aspectj.apache.bcel.classfile.ClassParser; | |||
import org.aspectj.apache.bcel.classfile.JavaClass; | |||
import org.aspectj.apache.bcel.classfile.Method; | |||
import org.aspectj.apache.bcel.classfile.StackMapFrame; | |||
import org.aspectj.apache.bcel.classfile.StackMapTable; | |||
import org.aspectj.apache.bcel.generic.ClassGen; | |||
import org.aspectj.apache.bcel.generic.ConstantPoolGen; | |||
import org.aspectj.apache.bcel.generic.MethodGen; | |||
import org.aspectj.apache.bcel.generic.Type; | |||
import org.aspectj.apache.bcel.util.ClassPath; | |||
import org.aspectj.apache.bcel.util.SyntheticRepository; | |||
import org.aspectj.apache.bcel.verifier.utility.Frame; | |||
import org.aspectj.apache.bcel.verifier.utility.LocalVariables; | |||
import org.aspectj.apache.bcel.verifier.utility.OperandStack; | |||
import org.aspectj.apache.bcel.verifier.utility.StackMapHelper; | |||
/** | |||
* gotchas | |||
* - stack at an exception entry point - is it the stack from the try{} with the exception on? | |||
* - what is Top? | |||
* - sometimes if we haven't merged 'routes' at the start of a chain, we may commence the chain without quite | |||
* the right set of types?!? do we re-stack (the start of it) and pick another and come back later? | |||
* - is the frame for a 'try' the frame before or after the instruction? (i.e. the one we hack for the exception handler) - ANSWER: its the 'before' but we may need to include lvar types 'learned' during the block in the catch block frame | |||
*/ | |||
public class PreverifierTest extends PreverifierBaseTestCase { | |||
private boolean debug = true; | |||
// /** Check StackMapTable can be retrieved and is the right length */ | |||
// public void testLookingAtTheStackMap() throws ClassNotFoundException { | |||
// StackMapTable stackmap = fetchStackMapTable("org.aspectj.apache.bcel.classfile.Utility","codeToString"); | |||
// assertTrue("Should be 8 entries in the stack map but found: "+stackmap.getMapLength(),stackmap.getMapLength()==8); | |||
// } | |||
// | |||
// | |||
// | |||
// /** Are the frames correct */ | |||
// public void testTheFrames() throws ClassNotFoundException { | |||
// StackMapTable stackmap = fetchStackMapTable("org.aspectj.apache.bcel.classfile.Utility","codeToString"); | |||
// StackMapFrame[] frames = stackmap.getStackMap(); | |||
// String[] expectedKinds = new String[]{ | |||
// "AppendFrame","SameFrame","SameFrame","SameFrame","SameFrameExtended", | |||
// "SameFrame","FullFrame","SameFrame" | |||
// }; | |||
// for (int i = 0; i < frames.length; i++) { | |||
// StackMapFrame frame = frames[i]; | |||
// //System.out.println("f"+i+" is "+frame); | |||
// assertTrue("Frame at position "+i+" should be "+expectedKinds[i]+" but is "+frame.getKindString(),frame.getKindString().equals(expectedKinds[i])); | |||
// } | |||
// } | |||
// | |||
// | |||
// | |||
// /** Reconstructing complete frames - applying the frames to the initial */ | |||
// public void testFrameReconstruction() throws ClassNotFoundException { | |||
// MethodGen method = fetchMethod("org.aspectj.apache.bcel.classfile.Utility","codeToString"); | |||
// StackMapTable table = fetchStackMapTable(method); | |||
// StackMapFrame[] frames = table.getStackMap(); | |||
// Frame[] fullFrames = StackMapper.reconstructFrames(method,table); | |||
// int offset = 0; | |||
// for (int f = 0; f < fullFrames.length; f++) { | |||
// StackMapFrame frame = frames[f]; | |||
// if (f==0) offset=frame.getByteCodeOffset(); | |||
// else offset+=1+frame.getByteCodeOffset(); | |||
// System.out.println("Current frame (offset="+offset+") now "+fullFrames[f]); | |||
// } | |||
// } | |||
// | |||
// | |||
// | |||
// /** Creates a set of frames and also retrieves them from a method - compares them */ | |||
// public void testFrameComparison() throws ClassNotFoundException { | |||
// JavaClass clazz = getClassFromJar("org.aspectj.apache.bcel.classfile.Utility"); | |||
// Method m = getMethod(clazz,"codeToString"); | |||
// MethodGen method = new MethodGen(m,clazz.getClassName(),new ConstantPoolGen(clazz.getConstantPool())); | |||
// | |||
// // We are comparing the full frames here, *not* the StackMapFrames | |||
// | |||
// // First retrieve them from the method attribute | |||
// StackMapTable table = fetchStackMapTable(method); | |||
// StackMapFrame[] frames = table.getStackMap(); | |||
// Frame[] fullFramesRead = StackMapper.reconstructFrames(method,table); | |||
// | |||
// // Now calculate them | |||
// long stime = System.currentTimeMillis(); | |||
// Frame[] fullFramesCalculated = new StackMapper(clazz).createStackMapTableAttribute(method); | |||
// | |||
// // tada !!! compare the bloody things | |||
// assertTrue("Expected same number of frames in each case? read="+fullFramesRead.length+" calc="+fullFramesCalculated.length, | |||
// fullFramesRead.length==fullFramesCalculated.length); | |||
// | |||
// int pos = 0; | |||
// for (int i = 0; i < fullFramesCalculated.length; i++) { | |||
// if (i==0) pos=frames[i].getByteCodeOffset(); | |||
// else pos+=frames[i].getByteCodeOffset()+1; | |||
// assertTrue("Offset "+pos+" frames should be equal? ["+i+"] "+differences(fullFramesRead[i],fullFramesCalculated[i])+ | |||
// "\n READ="+fullFramesRead[i]+"\n CALC="+fullFramesCalculated[i], | |||
// equivalent(fullFramesRead[i],fullFramesCalculated[i])); | |||
// } | |||
// } | |||
// | |||
// private boolean debug = true; | |||
// /** | |||
// * Creates a set of frames and also retrieves them from a method - compares them. | |||
// * From the 'test a load of stuff' tests, individual problematic methods are promoted to having their own testcase | |||
// * | |||
// * Fun facts: | |||
// * ChopFrame occurs in this one! | |||
// */ | |||
// public void testFrameComparison2() throws ClassNotFoundException { | |||
// JavaClass clazz = getClassFromJar("org.aspectj.apache.bcel.classfile.Utility"); | |||
// Method m = getMethod(clazz,"methodTypeToSignature"); | |||
// MethodGen method = new MethodGen(m,clazz.getClassName(),new ConstantPoolGen(clazz.getConstantPool())); | |||
// | |||
// // We are comparing the full frames here, *not* the StackMapFrames | |||
// | |||
// // First retrieve them from the method attribute | |||
// StackMapTable table = fetchStackMapTable(method); | |||
// StackMapFrame[] frames = table.getStackMap(); | |||
// Frame[] fullFramesRead = StackMapper.reconstructFrames(method,table); | |||
// if (debug) { | |||
// System.err.println("READ FRAMES - START"); | |||
// for (int i = 0; i < fullFramesRead.length; i++) { | |||
// Frame frame = fullFramesRead[i]; | |||
// System.err.println(fullFramesRead[i]); | |||
// } | |||
// System.err.println("READ FRAMES - END"); | |||
// } | |||
// // Now calculate them | |||
// long stime = System.currentTimeMillis(); | |||
// Frame[] fullFramesCalculated = new StackMapper(clazz).createStackMapTableAttribute(method); | |||
// if (debug) { | |||
// System.err.println("CALC FRAMES - START"); | |||
// for (int i = 0; i < fullFramesCalculated.length; i++) { | |||
// Frame frame = fullFramesCalculated[i]; | |||
// System.err.println(fullFramesCalculated[i]); | |||
// } | |||
// System.err.println("CALC FRAMES - END"); | |||
// } | |||
// | |||
// // tada !!! compare the bloody things | |||
// assertTrue("Expected same number of frames in each case? read="+fullFramesRead.length+" calc="+fullFramesCalculated.length, | |||
// fullFramesRead.length==fullFramesCalculated.length); | |||
// | |||
// int pos = 0; | |||
// for (int i = 0; i < fullFramesCalculated.length; i++) { | |||
// if (i==0) pos=frames[i].getByteCodeOffset(); | |||
// else pos+=frames[i].getByteCodeOffset()+1; | |||
// assertTrue("Offset "+pos+" frames should be equal? ["+i+"] "+differences(fullFramesRead[i],fullFramesCalculated[i])+ | |||
// "\n READ="+fullFramesRead[i]+"\n CALC="+fullFramesCalculated[i], | |||
// equivalent(fullFramesRead[i],fullFramesCalculated[i])); | |||
// } | |||
// } | |||
private int[] toOffsets(StackMapFrame[] smfs) { | |||
int[] offsets = new int[smfs.length+1]; | |||
int idx=0; | |||
offsets[idx++]=0; | |||
for (int i = 0; i < smfs.length; i++) { | |||
offsets[idx]=offsets[idx-1]+smfs[i].getByteCodeOffset()+(i>0?1:0); | |||
idx++; | |||
} | |||
return offsets; | |||
} | |||
public void testSimpleFrameComparison() throws ClassNotFoundException, ClassFormatException, IOException { | |||
String cname = "Code"; | |||
String mname = "method1"; | |||
JavaClass clazz = getClassFromJar(cname); | |||
Method interestingMethod = getMethod(clazz,mname,0); | |||
System.out.println(interestingMethod); | |||
MethodGen method = new MethodGen(interestingMethod,clazz.getClassName(),new ConstantPoolGen(clazz.getConstantPool())); | |||
// We are comparing the full frames here, *not* the StackMapFrames | |||
// First retrieve them from the method attribute - this is what the COMPILER generated | |||
StackMapTable table = fetchStackMapTable(method); | |||
StackMapFrame[] frames = table.getStackMap(); | |||
int[] offsets = toOffsets(frames); | |||
Frame[] compilerFrames = StackMapHelper.reconstructFrames(method,table); | |||
if (debug) printFrames("Javac",compilerFrames,offsets); | |||
if (true) return; | |||
// Now calculate them | |||
long stime = System.currentTimeMillis(); | |||
// Frame[] fullFramesCalculated = new StackMapper(clazz).createStackMapTableAttribute(method); | |||
// Frame[] calculatedFrames = new StackMapper(clazz).produceStackMapTableAttribute(clazz, method); | |||
// if (debug) printFrames("Calculated",calculatedFrames,offsets); | |||
// compareFrames("Compiler and Bcel frames",compilerFrames,calculatedFrames,frames); | |||
} | |||
public void testComplexFrameComparison() throws ClassNotFoundException, ClassFormatException, IOException { | |||
String cname = "org.aspectj.apache.bcel.classfile.Utility"; | |||
String mname = "methodSignatureToString"; | |||
int count = 2; | |||
JavaClass clazz = getClassFromJar(cname); | |||
ClassGen cg = new ClassGen(clazz); | |||
Method interestingMethod = getMethod(cg,mname,2); | |||
System.out.println(interestingMethod); | |||
MethodGen method = new MethodGen(interestingMethod,clazz.getClassName(),new ConstantPoolGen(clazz.getConstantPool())); | |||
method.calculateStackMapTable(); | |||
cg.replaceMethod(interestingMethod, method.getMethod()); | |||
// method.calculateStackMapTable(); | |||
String path = "c:/temp/"+clazz.getPackageName().replaceAll("\\.","/"); | |||
String name = clazz.getClassName(); | |||
if (name.indexOf(".")!=-1) { | |||
name = name.substring(name.lastIndexOf(".")+1); | |||
} | |||
String file = path+"/"+name+".class"; | |||
System.err.println(file); | |||
new File(path).mkdirs(); | |||
cg.getJavaClass().dump(new FileOutputStream(file)); | |||
URLClassLoader cl = new URLClassLoader(new URL[]{new File("c:/temp").toURL(),new File("c:/testcode.jar").toURL()},null);//this.getClass().getClassLoader()); | |||
System.err.println(cl.getURLs()[0]); | |||
System.err.println("loading "+clazz.getClassName()); | |||
Class c = Class.forName(clazz.getClassName(),true,cl); | |||
// | |||
// // First retrieve them from the method attribute - this is what the COMPILER generated | |||
// StackMapTable table = fetchStackMapTable(method); | |||
// StackMapFrame[] frames = table.getStackMap(); | |||
// int[] offsets = toOffsets(frames); | |||
// Frame[] compilerFrames = StackMapper.reconstructFrames(method,table); | |||
// if (debug) printFrames("Javac",compilerFrames,offsets); | |||
// | |||
// // Now calculate them | |||
// long stime = System.currentTimeMillis(); | |||
// Frame[] fullFramesCalculated = new StackMapper(clazz).createStackMapTableAttribute(method); | |||
// Frame[] calculatedFrames = new StackMapper(clazz).produceStackMapTableAttribute(clazz, method); | |||
//// if (debug) printFrames("Calculated",calculatedFrames,offsets); | |||
//// | |||
//// compareFrames("Compiler and Bcel frames",compilerFrames,calculatedFrames,frames); | |||
//// | |||
//// // let's try serializing the new version of the method with this on... | |||
//// method.addStackMap(calculatedFrames); | |||
} | |||
public void testProblemClassOne() throws ClassNotFoundException, ClassFormatException, IOException { | |||
// Uncovered two problems surfaced: | |||
// - Bcel not understanding LDC changes to support Class Constants in the ExecutionVisitor | |||
// - Bcel 'optimizing' wide instructions that don't use double byte indices - the stackmap is left unchanged and so incorrect | |||
checkOneClass("org.aspectj.apache.bcel.generic.InstructionHandle",true,3); | |||
} | |||
public void testProblemClassTwo() throws ClassNotFoundException, ClassFormatException, IOException { | |||
// simple problem revealed in test system with not avoiding abstract methods | |||
checkOneClass("org.aspectj.apache.bcel.classfile.annotation.ElementValue",true,3); | |||
} | |||
public void testProblemClassThree() throws ClassNotFoundException, ClassFormatException, IOException { | |||
// problem in deserializing a stackmaptable - incorrectly used the localcount when creating the stack array ! | |||
// problem with uninitialized - where the index should be the index of the related 'new' instruction | |||
// - enhanced execution visitor to remember where it is | |||
// problem with methods that have no stackmap (readVersion in this class) | |||
// lots of problems where 'unknown' was being used when it should have been 'top' | |||
checkOneClass("org.aspectj.apache.bcel.classfile.ClassParser",true,3); | |||
} | |||
public void testEveryMethodInAClass() throws ClassNotFoundException, ClassFormatException, IOException { | |||
checkOneClass("org.aspectj.apache.bcel.classfile.Utility",false); | |||
} | |||
public void testProblemClassFour() throws ClassNotFoundException, ClassFormatException, IOException { | |||
// Problem with incorrect handling of Type.NULL - it is a special ObjectType constant | |||
checkOneClass("org.aspectj.apache.bcel.classfile.GenericSignatureParser",true); | |||
} | |||
public void testProblemClassFive() throws ClassNotFoundException, ClassFormatException, IOException { | |||
// outofmemory on the clinit... it is 8870 bytes long | |||
// ended up fixing the exceptionhandler creation logic - so it doesnt build a list for every instruction | |||
// of the exceptions that might occur, just for those instructions that actually throw exceptions! | |||
long timer = System.currentTimeMillis(); | |||
checkOneClass("org.aspectj.apache.bcel.Constants",true); | |||
System.out.println("took "+(System.currentTimeMillis()-timer)+"ms"); | |||
// 3926/2684ms! 7/12/06 | |||
} | |||
public void testProblemClassSix() throws ClassNotFoundException, ClassFormatException, IOException { | |||
// Problem here was that after optimizing the exception code so that it didn't persue so many execution | |||
// chains, i forgot that astores alter the local variable table for the catch block - and in this | |||
// case, if I forgot that the catch block thought LV1 was <null> when in fact it was Object | |||
// fix is to be a little less aggressive - and recognize ASTOREs need to be followed through the | |||
// catch block | |||
checkOneClass("org.aspectj.apache.bcel.classfile.Attribute",true,14); // clone method | |||
} | |||
public void testProblemClassSeven() throws ClassNotFoundException, ClassFormatException, IOException { | |||
// Now we had a problem with double slot values (long/double) - they are internally represented but when | |||
// the frame is dumped the duplicate TOPs are removed. | |||
// Changes were to StackMapHelper.createInitialFrame() and StackMapFrame.dump() and Frame.getLocalsAsStackMapTypes() | |||
checkOneClass("org.aspectj.apache.bcel.generic.ConstantPoolGen",true,17); | |||
} | |||
public void testProblemClassEight() throws ClassNotFoundException, ClassFormatException, IOException { | |||
// java.lang.VerifyError: Instruction type does not match stack map in method org.aspectj.apache.bcel.verifier.utility.StackMapper.chaseChains()V at offset 0 | |||
// interesting! The method code ends with a 'goto 0' causing us to jump back to the start - because I didn't store the initialframe for the | |||
// method, we didn't do a merge after the jump back to 0, we just used what the contents were. A merge would have told us we knew much | |||
// less than we thought. The fix was to store the initial frame as we start | |||
checkOneClass("org.aspectj.apache.bcel.verifier.utility.StackMapper",true,11); | |||
} | |||
// rt.jar classes | |||
public void testProblemClass9() throws ClassNotFoundException, ClassFormatException, IOException { | |||
// Revealed that the OperandStack wasn't correctly working for double slot entries like LONG | |||
checkOneClass("com.sun.corba.se.impl.corba.AnyImpl",true,34); | |||
} | |||
public void testProblemClass10() throws ClassNotFoundException, ClassFormatException, IOException { | |||
// method 2 - internalGetServant has jsrs, yey! | |||
checkOneClass("com.sun.corba.se.impl.oa.poa.POAPolicyMediatorImpl_R_USM",true,2); | |||
} | |||
public void testProblemClass11() throws ClassNotFoundException, ClassFormatException, IOException { | |||
checkOneClass("com.sun.corba.se.impl.corba.AnyImplHelper",false); | |||
} | |||
public void testJar() throws ClassNotFoundException, ClassFormatException, IOException { | |||
StackMapHelper.count=0; | |||
verifyJar("testdata/Java6BuiltCode.jar"); | |||
System.out.println("Stored: "+StackMapHelper.count+" frames"); | |||
} | |||
public void testJar2() throws ClassNotFoundException, ClassFormatException, IOException { | |||
verifyJar("C:/Program Files/Java/jdk1.6.0/jre/lib/rt.jar"); | |||
} | |||
public void verifyJar(String where) throws ClassNotFoundException, ClassFormatException, IOException { | |||
long timer = System.currentTimeMillis(); | |||
ZipFile zf = new ZipFile(where); | |||
Enumeration e = zf.entries(); | |||
int count=0,verified=0; | |||
while (e.hasMoreElements()) { | |||
ZipEntry zfe = (ZipEntry)e.nextElement(); | |||
if (zfe.getName().endsWith(".class")) { | |||
String n = zfe.getName(); | |||
if (debug) System.out.println("Processing class #"+count+": "+n); | |||
n = n.substring(0,n.indexOf(".class")); | |||
if (n.indexOf("OperatingSystem")==-1 && n.indexOf("JdbcOdbc")==-1) { | |||
boolean b = checkOneClass(n.replaceAll("/","."),false); | |||
count++; | |||
if (b) verified++; | |||
} | |||
} | |||
} | |||
System.out.println("Tested entire jar, took "+(System.currentTimeMillis()-timer)+"ms for "+count+" classes (verified: "+verified+")"); | |||
} | |||
public File getTempDir() { | |||
String s = "c:/temp";//System.getProperty("java.io.tmpdir"); | |||
File f = new File(s+File.separator+"verifier"); | |||
f.mkdir(); | |||
return f; | |||
} | |||
public boolean checkOneClass(String cname,boolean verifyAsYouGoAlong) throws ClassNotFoundException, ClassFormatException, IOException { | |||
return checkOneClass(cname,verifyAsYouGoAlong,0); | |||
} | |||
// public static void main(String []argv) throws ClassNotFoundException, FileNotFoundException, IOException { | |||
// // remove one | |||
// ClassPath cp = new ClassPath( | |||
// "c:/igor/fails/"+File.pathSeparator+ | |||
// System.getProperty("sun.boot.class.path")); | |||
// SyntheticRepository repos = SyntheticRepository.getInstance(cp); | |||
// Repository.setRepository(repos); | |||
// JavaClass clazz = repos.loadClass("com.ibm.tivoli.itcam.toolkit.ai.aspectj.captureJDBC.CaptureDataSource"); | |||
// ClassGen cg = new ClassGen(clazz); | |||
// Attributes[] as = cg.getAttributes(); | |||
// for (int i = 0; i < as.length; i++) { | |||
// | |||
// } | |||
// File f = new File("c:/igor/CaptureDataSource.class"); | |||
// cg.getJavaClass().dump(new FileOutputStream(f)); | |||
// | |||
// } | |||
public boolean checkOneClass(String cname,boolean verifyAsYouGoAlong,int startMethod) throws ClassNotFoundException, ClassFormatException, IOException { | |||
if (debug) System.out.println("? PreverifierTest.testClass(): testing class '"+cname+"'"); | |||
long timer = System.currentTimeMillis(); | |||
JavaClass clazz = getClassFromJar(cname); | |||
ClassGen cg = new ClassGen(clazz); | |||
cg.setMajor(50); // upgrade it so it gets verified! | |||
// if (cg.getMajor()<50) { | |||
// System.out.println("? skipping this class, it is major version "+cg.getMajor()); | |||
// return false; | |||
// } | |||
File tempDir = getTempDir(); | |||
String destination = clazz.getPackageName().replaceAll("\\.","/"); | |||
String name = clazz.getClassName(); | |||
if (name.indexOf(".")!=-1) name = name.substring(name.lastIndexOf(".")+1); | |||
File outputDir = new File(tempDir,destination); | |||
outputDir.mkdirs(); | |||
File file = new File (outputDir,name+".class"); | |||
if (debug) System.out.println("? New file will be dumped to "+file); | |||
Method[] methods = cg.getMethods(); | |||
for (int i = startMethod; i <methods.length; i++) { | |||
Method originalMethod = methods[i]; | |||
if (originalMethod.isAbstract()) { | |||
if (debug) System.out.println("? skipping abstract method "+i+"/"+methods.length); | |||
} else { | |||
if (debug) System.out.println("? processing method "+i+"/"+methods.length+": "+originalMethod.getName()); | |||
ConstantPoolGen cpg = cg.getConstantPool(); | |||
MethodGen newMethod = new MethodGen(originalMethod,clazz.getClassName(),cpg); | |||
newMethod.calculateStackMapTable(); | |||
cg.replaceMethod(originalMethod, newMethod.getMethod()); | |||
if (verifyAsYouGoAlong) { | |||
cg.getJavaClass().dump(new FileOutputStream(file)); | |||
URLClassLoader cl = new URLClassLoader(new URL[]{tempDir.toURL(),new File("c:/testcode.jar").toURL()},null);//this.getClass().getClassLoader()); | |||
Class c = Class.forName(clazz.getClassName(),true,cl); | |||
} | |||
} | |||
} | |||
if (!verifyAsYouGoAlong) { // Just do this once at the end | |||
cg.getJavaClass().dump(new FileOutputStream(file)); | |||
URLClassLoader cl = new URLClassLoader(new URL[]{tempDir.toURL(),new File("c:/testcode.jar").toURL()},null);//this.getClass().getClassLoader()); | |||
Class c = Class.forName(clazz.getClassName(),true,cl); | |||
} | |||
if (debug) System.out.println("Time taken testing "+cname+": "+(System.currentTimeMillis()-timer)+"ms"); | |||
return true; | |||
} | |||
private void printFrames(String id,Frame[] fs,int[] offsets) { | |||
System.err.println(id+": start ("+fs.length+" frames)"); | |||
for (int i = 0; i < fs.length; i++) System.err.println(fs[i].toString(i+"(o"+offsets[i]+")")); | |||
System.err.println(id+": end"); | |||
} | |||
/** Compare two sets of frames - the final parameter is just used for offset reporting*/ | |||
public void compareFrames(String description, Frame[] compilerFrames,Frame[] calculatedFrames,StackMapFrame[] frames) { | |||
System.out.println(description); | |||
assertTrue("Expected same number of frames in each case? read="+compilerFrames.length+" calc="+calculatedFrames.length, | |||
compilerFrames.length==calculatedFrames.length); | |||
int pos = 0; | |||
for (int i = 0; i < calculatedFrames.length; i++) { | |||
System.out.println("Comparing frames at offset "+pos); | |||
assertTrue("Offset "+pos+" frames should be equal? ["+i+"] "+differences(compilerFrames[i],calculatedFrames[i])+ | |||
"\n READ="+compilerFrames[i]+"\n CALC="+calculatedFrames[i],equivalent(compilerFrames[i],calculatedFrames[i])); | |||
if (i<calculatedFrames.length-1) pos+=frames[i].getByteCodeOffset()+(i>0?1:0); | |||
} | |||
System.out.println("comparison successful"); | |||
} | |||
/** | |||
* Creates a set of frames and also retrieves them from a method - compares them. | |||
* | |||
* Victim: org.aspectj.apache.bcel.classfile.Utility.methodSignatureToString() (the 2nd one) | |||
*/ | |||
// public void testFrameComparison() throws ClassNotFoundException, ClassFormatException, IOException { | |||
// JavaClass clazz = getClassFromJar("org.aspectj.apache.bcel.classfile.Utility"); | |||
// Method interestingMethod = getMethod(clazz,"methodSignatureToString",2); | |||
// System.out.println(interestingMethod); | |||
// | |||
// MethodGen method = new MethodGen(interestingMethod,clazz.getClassName(),new ConstantPoolGen(clazz.getConstantPool())); | |||
// | |||
// // We are comparing the full frames here, *not* the StackMapFrames | |||
// | |||
// | |||
// // First retrieve them from the method attribute - this is what the COMPILER generated | |||
// StackMapTable table = fetchStackMapTable(method); | |||
// StackMapFrame[] frames = table.getStackMap(); | |||
// Frame[] fullFrames = StackMapper.reconstructFrames(method,table); | |||
// if (debug) { | |||
// System.out.println("Javac: start ("+fullFrames.length+" frames)"); | |||
// for (int i = 0; i < fullFrames.length; i++) System.out.println(fullFrames[i].toString(i+") ")); | |||
// System.out.println("Javac: end"); | |||
// } | |||
// | |||
// | |||
// | |||
// | |||
// | |||
// // Now calculate them | |||
// long stime = System.currentTimeMillis(); | |||
// // Frame[] fullFramesCalculated = new StackMapper(clazz).createStackMapTableAttribute(method); | |||
// Frame[] fullFramesCalculated = new StackMapper(clazz).produceStackMapTableAttribute(clazz, method);//(method); | |||
// if (debug) { | |||
// System.out.println("Calculated: start ("+fullFramesCalculated.length+" frames)"); | |||
// for (int i = 0; i < fullFramesCalculated.length; i++) | |||
// System.out.println(fullFramesCalculated[i]); | |||
// System.out.println("Calculated: end"); | |||
// } | |||
// | |||
// // tada !!! compare the bloody things | |||
//// assertTrue("Expected same number of frames in each case? read="+fullFramesRead.length+" calc="+fullFramesCalculated.length, | |||
//// fullFramesRead.length==fullFramesCalculated.length); | |||
// | |||
// int pos = 0; | |||
// for (int i = 0; i < fullFramesCalculated.length; i++) { | |||
// if (i==0) pos=frames[i].getByteCodeOffset(); | |||
// else pos+=frames[i].getByteCodeOffset()+1; | |||
// assertTrue("Offset "+pos+" frames should be equal? ["+i+"] "+differences(fullFrames[i],fullFramesCalculated[i])+ | |||
// "\n READ="+fullFrames[i]+"\n CALC="+fullFramesCalculated[i], | |||
// equivalent(fullFrames[i],fullFramesCalculated[i])); | |||
// } | |||
// } | |||
// public void testTheChuffinLot() throws ClassNotFoundException { | |||
// // Load a whole class and test all its frames for every method. | |||
// int compared=0; | |||
// JavaClass clazz = getClassFromJar("org.aspectj.apache.bcel.classfile.Utility"); | |||
// ClassGen cg = new ClassGen(clazz); | |||
// Method[] ms = cg.getMethods(); | |||
// System.err.println("Class "+clazz.getClassName()+" has "+ms.length+" methods to compare"); | |||
// for (int i = 0; i < ms.length; i++) { | |||
// Method m = ms[i]; | |||
// MethodGen method = new MethodGen(m,clazz.getClassName(),cg.getConstantPool()); | |||
// if (!hasStackMapTableAttribute(method)) {System.err.println("No StackMapAttribute for "+method.getName());continue;} | |||
// System.err.println("===================================================="); | |||
// System.err.println("Method "+i+" is '"+m+"': commencing frame comparison"); | |||
// // First retrieve them from the method attribute | |||
// StackMapTable table = fetchStackMapTable(method); | |||
// StackMapFrame[] frames = table.getStackMap(); | |||
// Frame[] fullFramesRead = StackMapper.reconstructFrames(method,table); | |||
// | |||
// // Now calculate them | |||
// long stime = System.currentTimeMillis(); | |||
// Frame[] fullFramesCalculated = new StackMapper(clazz).createStackMapTableAttribute(method); | |||
// | |||
// // tada !!! compare the bloody things | |||
// assertTrue("Expected same number of frames in each case? read="+fullFramesRead.length+" calc="+fullFramesCalculated.length, | |||
// fullFramesRead.length==fullFramesCalculated.length); | |||
// | |||
// int pos = 0; | |||
// for (int ii = 0; ii < fullFramesCalculated.length; ii++) { | |||
// if (ii==0) pos=frames[ii].getByteCodeOffset(); | |||
// else pos+=frames[ii].getByteCodeOffset()+1; | |||
// assertTrue("Offset "+pos+" frames should be equal? ["+ii+"] "+differences(fullFramesRead[ii],fullFramesCalculated[ii])+ | |||
// "\n READ="+fullFramesRead[ii]+"\n CALC="+fullFramesCalculated[ii], | |||
// equivalent(fullFramesRead[ii],fullFramesCalculated[ii])); | |||
// } | |||
// compared++; | |||
// } | |||
// System.err.println("Successfully compared attributes for "+compared+" methods"); | |||
// } | |||
// ZipFile zf = new ZipFile(f); | |||
// int i = 0; | |||
// long stime = System.currentTimeMillis(); | |||
// Enumeration entries = zf.entries(); | |||
// while (entries.hasMoreElements()) { | |||
// ZipEntry zfe = (ZipEntry) entries.nextElement(); | |||
// String classfileName = zfe.getName(); | |||
// if (classfileName.endsWith(".class")) { | |||
// String clazzname = classfileName.substring(0, | |||
// classfileName.length() - 6).replace('/', '.'); | |||
// ReferenceType b = (ReferenceType) slowWorld | |||
// .resolve(clazzname); | |||
// i++; | |||
// } | |||
// } | |||
// public void testDumbVerifier() throws ClassNotFoundException { | |||
// MethodGen method = fetchMethod("org.aspectj.apache.bcel.classfile.Utility","codeToString"); | |||
// | |||
// JavaClass clazz = getClassFromJar("org.aspectj.apache.bcel.classfile.Utility"); | |||
// Method m = getMethod(clazz,"codeToString"); | |||
// Attribute a = findAttribute("Code", m.getAttributes()); | |||
// assertTrue("Should be of type 'Code' but is "+a.getClass(),a instanceof Code); | |||
// Code c = (Code)a; | |||
// Attribute[] codeAttributes = c.getAttributes(); | |||
// StackMapTable stackmap = (StackMapTable)findAttribute("StackMapTable", c.getAttributes()); | |||
// assertTrue("Should be 8 entries in the stack map but found: "+stackmap.getMapLength(),stackmap.getMapLength()==8); | |||
// try { | |||
// MethodGen mg = new MethodGen(m,clazz.getClassName(),new ConstantPoolGen(clazz.getConstantPool())); | |||
// long stime = System.currentTimeMillis(); | |||
// new StackMapper().createStackMapTableAttribute(mg); | |||
// System.err.println("Took "+(System.currentTimeMillis()-stime)+"ms"); | |||
// } catch (Exception e) {e.printStackTrace();} | |||
// } | |||
/** | |||
* Retrieves a stack map then asks for verification of the method in the class which spits out | |||
* stackmapframes we can compare against... | |||
* this checks: | |||
* - the verifier works | |||
* - the stackmap loading is correct | |||
* - the process for having our own cut down verifier | |||
*/ | |||
// | |||
// | |||
// | |||
// public void testVerifyTheMap() throws ClassNotFoundException { | |||
//// JavaClass clazz = getClassFromJar("org.aspectj.apache.bcel.classfile.Utility"); | |||
//// Method m = getMethod(clazz,"codeToString"); | |||
// JavaClass clazz = getClassFromJar("org.aspectj.apache.bcel.classfile.Utility"); | |||
// Method m = getMethod(clazz,"methodSignatureToString",2); | |||
// Attribute a = findAttribute("Code", m.getAttributes()); | |||
// assertTrue("Should be of type 'Code' but is "+a.getClass(),a instanceof Code); | |||
// Code c = (Code)a; | |||
// Attribute[] codeAttributes = c.getAttributes(); | |||
// StackMapTable stackmap = (StackMapTable)findAttribute("StackMapTable", c.getAttributes()); | |||
//// assertTrue("Should be 8 entries in the stack map but found: "+stackmap.getMapLength(),stackmap.getMapLength()==8); | |||
// try { | |||
// MethodGen mg = new MethodGen(m,clazz.getClassName(),new ConstantPoolGen(clazz.getConstantPool())); | |||
// long stime = System.currentTimeMillis(); | |||
// Frame[] o = new StackMapper(clazz).produceStackMapTableAttribute(clazz,mg); | |||
// System.err.println("Took "+(System.currentTimeMillis()-stime)+"ms"); | |||
// } catch (Exception e) {e.printStackTrace();} | |||
// } | |||
// | |||
// | |||
// | |||
// /** Let's test the creation of subroutines for a method */ | |||
// public void testSubroutines() throws ClassNotFoundException { | |||
// JavaClass clazz = getClassFromJar("org.aspectj.apache.bcel.classfile.Utility"); | |||
// Method m = getMethod(clazz,"methodSignatureToString",2); | |||
// MethodGen mg = new MethodGen(m,clazz.getClassName(),new ConstantPoolGen(clazz.getConstantPool())); | |||
// long stime = System.currentTimeMillis(); | |||
// Subroutines subs = new Subroutines(mg); | |||
// System.err.println("Took "+(System.currentTimeMillis()-stime)+"ms"); | |||
// Collection c = subs.getIndividualSubroutines(); | |||
// System.err.println("End of analysis: "+c.size()+" subroutines"); | |||
// for (Iterator iter = c.iterator(); iter.hasNext();) { | |||
// Subroutine element = (Subroutine) iter.next(); | |||
// System.err.println(element); | |||
// } | |||
// } | |||
// | |||
// | |||
// --- | |||
protected void setUp() throws Exception { | |||
super.setUp(); | |||
} | |||
protected void tearDown() throws Exception { | |||
super.tearDown(); | |||
} | |||
// --- | |||
// private StackMapTable fetchStackMapTable(String classname,String methodname) throws ClassNotFoundException { | |||
// JavaClass clazz = getClassFromJar(classname); | |||
// Method m = getMethod(clazz,methodname); | |||
// Attribute a = findAttribute("Code", m.getAttributes()); | |||
// Code c = (Code)a; | |||
// Attribute[] codeAttributes = c.getAttributes(); | |||
// CodeException[] ces = c.getExceptionTable(); | |||
// StackMapTable stackmap = (StackMapTable)findAttribute("StackMapTable", c.getAttributes()); | |||
// return stackmap; | |||
// } | |||
// | |||
// private boolean hasStackMapTableAttribute(MethodGen method) { | |||
// Attribute[] all = method.getCodeAttributes(); | |||
// for (int i = 0; i < all.length; i++) { | |||
// if (all[i].getName().equals("StackMapTable")) return true; | |||
// } | |||
// return false; | |||
// } | |||
private StackMapTable fetchStackMapTable(MethodGen method) throws ClassNotFoundException { | |||
Attribute[] codeAttributes = method.getCodeAttributes(); | |||
StackMapTable stackmap = (StackMapTable)findAttribute("StackMapTable", codeAttributes); | |||
return stackmap; | |||
} | |||
private MethodGen fetchMethod(String classname,String methodname) throws ClassNotFoundException { | |||
JavaClass clazz = getClassFromJar(classname); | |||
Method m = getMethod(clazz,methodname); | |||
MethodGen mg = new MethodGen(m,clazz.getClassName(),new ConstantPoolGen(clazz.getConstantPool())); | |||
return mg; | |||
} | |||
/** For frames that aren't the same (equivalent()) this method will return why */ | |||
private String differences(Frame f1,Frame f2) { | |||
LocalVariables f1Locals = f1.getLocals(); | |||
LocalVariables f2Locals = f2.getLocals(); | |||
OperandStack f1Stack = f1.getStack(); | |||
OperandStack f2Stack = f2.getStack(); | |||
if (f1Locals.maxLocals()!=f2Locals.maxLocals()) return "Different number of locals"; | |||
if (f1Stack.maxStack()!=f2Stack.maxStack()) return "Different max stack sizes"; | |||
if (f1Stack.size()!=f2Stack.size()) return "Difference 'current' stack depths"; | |||
StringBuffer sb = new StringBuffer(); | |||
for (int i = 0; i < f1Locals.maxLocals(); i++) { | |||
Type f1Type = f1Locals.get(i); | |||
Type f2Type = f2Locals.get(i); | |||
if (!f1Type.equals(f2Type)) return "Different types at local variable position "+i+" "+f1Type+"!="+f2Type; | |||
} | |||
for (int i = 0; i < f1Stack.size(); i++) { | |||
Type f1Type = f1Stack.peek(i); | |||
Type f2Type = f2Stack.peek(i); | |||
if (!f1Type.equals(f2Type)) return "Different types at stack depth "+i+" "+f1Type+"!="+f2Type; | |||
} | |||
return ""; | |||
} | |||
/** Are two frames the same? */ | |||
private boolean equivalent(Frame f1,Frame f2) { | |||
LocalVariables f1Locals = f1.getLocals(); | |||
LocalVariables f2Locals = f2.getLocals(); | |||
OperandStack f1Stack = f1.getStack(); | |||
OperandStack f2Stack = f2.getStack(); | |||
if (f1Locals.maxLocals()!=f2Locals.maxLocals()) return false; | |||
if (f1Stack.maxStack()!=f2Stack.maxStack()) return false; | |||
if (f1Stack.size()!=f2Stack.size()) return false; | |||
for (int i = 0; i < f1Locals.maxLocals(); i++) { | |||
Type f1Type = f1Locals.get(i); | |||
Type f2Type = f2Locals.get(i); | |||
if (!f1Type.equals(f2Type)) return false; | |||
} | |||
for (int i = 0; i < f1Stack.size(); i++) { | |||
Type f1Type = f1Stack.peek(i); | |||
Type f2Type = f2Stack.peek(i); | |||
if (!f1Type.equals(f2Type)) return false; | |||
} | |||
return true; | |||
} | |||
private void dump(Attribute[] mAttributes) { | |||
for (int i = 0; i < mAttributes.length; i++) { | |||
Attribute attribute = mAttributes[i]; | |||
System.err.println(attribute); | |||
} | |||
} | |||
} |