diff options
author | aclement <aclement> | 2007-02-09 10:45:08 +0000 |
---|---|---|
committer | aclement <aclement> | 2007-02-09 10:45:08 +0000 |
commit | 291bb7dbcfcc7ae00047a6d306a7962b5b84e914 (patch) | |
tree | 8e125f2948096a9736bda6e5b48b08d141d9c54c | |
parent | db03e4bbe73821a6fa73b6d96a09e1547e7c3b94 (diff) | |
download | aspectj-verification.tar.gz aspectj-verification.zip |
verification prototype codeverification
56 files changed, 8726 insertions, 150 deletions
diff --git a/bcel-builder/.classpath b/bcel-builder/.classpath index 955afd2c0..8c64eb4d1 100644 --- a/bcel-builder/.classpath +++ b/bcel-builder/.classpath @@ -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> diff --git a/bcel-builder/.fbprefs b/bcel-builder/.fbprefs new file mode 100644 index 000000000..e0032fe06 --- /dev/null +++ b/bcel-builder/.fbprefs @@ -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=|
diff --git a/bcel-builder/.settings/org.eclipse.jdt.core.prefs b/bcel-builder/.settings/org.eclipse.jdt.core.prefs index fd14c0abb..56a224610 100644 --- a/bcel-builder/.settings/org.eclipse.jdt.core.prefs +++ b/bcel-builder/.settings/org.eclipse.jdt.core.prefs @@ -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 diff --git a/bcel-builder/foo.jar b/bcel-builder/foo.jar Binary files differnew file mode 100644 index 000000000..ac306e049 --- /dev/null +++ b/bcel-builder/foo.jar diff --git a/bcel-builder/src/org/aspectj/apache/bcel/Constants.java b/bcel-builder/src/org/aspectj/apache/bcel/Constants.java index 51c139901..2eaebd96b 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/Constants.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/Constants.java @@ -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" }; } diff --git a/bcel-builder/src/org/aspectj/apache/bcel/classfile/Attribute.java b/bcel-builder/src/org/aspectj/apache/bcel/classfile/Attribute.java index 8d7deb62e..386d2802d 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/classfile/Attribute.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/classfile/Attribute.java @@ -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: diff --git a/bcel-builder/src/org/aspectj/apache/bcel/classfile/ConstantNameAndType.java b/bcel-builder/src/org/aspectj/apache/bcel/classfile/ConstantNameAndType.java index f6fb51239..04573c146 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/classfile/ConstantNameAndType.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/classfile/ConstantNameAndType.java @@ -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()); } /** diff --git a/bcel-builder/src/org/aspectj/apache/bcel/classfile/DescendingVisitor.java b/bcel-builder/src/org/aspectj/apache/bcel/classfile/DescendingVisitor.java index 7d61dc2d1..237408ad3 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/classfile/DescendingVisitor.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/classfile/DescendingVisitor.java @@ -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(); diff --git a/bcel-builder/src/org/aspectj/apache/bcel/classfile/EmptyVisitor.java b/bcel-builder/src/org/aspectj/apache/bcel/classfile/EmptyVisitor.java index 1113f0af5..5d4b016fc 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/classfile/EmptyVisitor.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/classfile/EmptyVisitor.java @@ -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) {} diff --git a/bcel-builder/src/org/aspectj/apache/bcel/classfile/GenericSignatureParser.java b/bcel-builder/src/org/aspectj/apache/bcel/classfile/GenericSignatureParser.java index fe45f28da..9c94125b0 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/classfile/GenericSignatureParser.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/classfile/GenericSignatureParser.java @@ -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() { diff --git a/bcel-builder/src/org/aspectj/apache/bcel/classfile/JavaClass.java b/bcel-builder/src/org/aspectj/apache/bcel/classfile/JavaClass.java index e019947f5..7b89379ad 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/classfile/JavaClass.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/classfile/JavaClass.java @@ -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; } } diff --git a/bcel-builder/src/org/aspectj/apache/bcel/classfile/Method.java b/bcel-builder/src/org/aspectj/apache/bcel/classfile/Method.java index 56ff334e5..8408cb416 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/classfile/Method.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/classfile/Method.java @@ -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]; diff --git a/bcel-builder/src/org/aspectj/apache/bcel/classfile/Signature.java b/bcel-builder/src/org/aspectj/apache/bcel/classfile/Signature.java index bb9673103..fea8c4c69 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/classfile/Signature.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/classfile/Signature.java @@ -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); } diff --git a/bcel-builder/src/org/aspectj/apache/bcel/classfile/StackMap.java b/bcel-builder/src/org/aspectj/apache/bcel/classfile/StackMap.java index df976fc69..7c6f765b8 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/classfile/StackMap.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/classfile/StackMap.java @@ -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; } diff --git a/bcel-builder/src/org/aspectj/apache/bcel/classfile/StackMapEntry.java b/bcel-builder/src/org/aspectj/apache/bcel/classfile/StackMapEntry.java index e20d11f5f..3a23e4324 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/classfile/StackMapEntry.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/classfile/StackMapEntry.java @@ -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); } /** diff --git a/bcel-builder/src/org/aspectj/apache/bcel/classfile/StackMapFrame.java b/bcel-builder/src/org/aspectj/apache/bcel/classfile/StackMapFrame.java new file mode 100644 index 000000000..9ae5fe918 --- /dev/null +++ b/bcel-builder/src/org/aspectj/apache/bcel/classfile/StackMapFrame.java @@ -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; + } +} diff --git a/bcel-builder/src/org/aspectj/apache/bcel/classfile/StackMapTable.java b/bcel-builder/src/org/aspectj/apache/bcel/classfile/StackMapTable.java new file mode 100644 index 000000000..030978f36 --- /dev/null +++ b/bcel-builder/src/org/aspectj/apache/bcel/classfile/StackMapTable.java @@ -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(); + } + } +} diff --git a/bcel-builder/src/org/aspectj/apache/bcel/classfile/StackMapType.java b/bcel-builder/src/org/aspectj/apache/bcel/classfile/StackMapType.java index 0dd072d3b..7f986bb64 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/classfile/StackMapType.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/classfile/StackMapType.java @@ -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 ""; diff --git a/bcel-builder/src/org/aspectj/apache/bcel/classfile/Utility.java b/bcel-builder/src/org/aspectj/apache/bcel/classfile/Utility.java index 7e1d4affe..46dc911b4 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/classfile/Utility.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/classfile/Utility.java @@ -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; } diff --git a/bcel-builder/src/org/aspectj/apache/bcel/classfile/Visitor.java b/bcel-builder/src/org/aspectj/apache/bcel/classfile/Visitor.java index 525512d31..4ad116e76 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/classfile/Visitor.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/classfile/Visitor.java @@ -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); diff --git a/bcel-builder/src/org/aspectj/apache/bcel/generic/ArrayType.java b/bcel-builder/src/org/aspectj/apache/bcel/generic/ArrayType.java index e937a7c1d..e0877bba6 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/generic/ArrayType.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/generic/ArrayType.java @@ -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); } diff --git a/bcel-builder/src/org/aspectj/apache/bcel/generic/ConstantPoolGen.java b/bcel-builder/src/org/aspectj/apache/bcel/generic/ConstantPoolGen.java index 686f54e25..42ac0365d 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/generic/ConstantPoolGen.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/generic/ConstantPoolGen.java @@ -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)); } } } diff --git a/bcel-builder/src/org/aspectj/apache/bcel/generic/EmptyVisitor.java b/bcel-builder/src/org/aspectj/apache/bcel/generic/EmptyVisitor.java index ca983400b..8dc3aaf3b 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/generic/EmptyVisitor.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/generic/EmptyVisitor.java @@ -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) { } diff --git a/bcel-builder/src/org/aspectj/apache/bcel/generic/InstructionHandle.java b/bcel-builder/src/org/aspectj/apache/bcel/generic/InstructionHandle.java index df4b4b547..78b134126 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/generic/InstructionHandle.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/generic/InstructionHandle.java @@ -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 diff --git a/bcel-builder/src/org/aspectj/apache/bcel/generic/InstructionList.java b/bcel-builder/src/org/aspectj/apache/bcel/generic/InstructionList.java index cf0fb321a..fcae5795b 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/generic/InstructionList.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/generic/InstructionList.java @@ -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(); } diff --git a/bcel-builder/src/org/aspectj/apache/bcel/generic/MethodGen.java b/bcel-builder/src/org/aspectj/apache/bcel/generic/MethodGen.java index e414b94e7..10b16adc9 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/generic/MethodGen.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/generic/MethodGen.java @@ -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); } } diff --git a/bcel-builder/src/org/aspectj/apache/bcel/generic/Type.java b/bcel-builder/src/org/aspectj/apache/bcel/generic/Type.java index 7ea02cb45..9c7ef94ef 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/generic/Type.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/generic/Type.java @@ -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; diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/Verifier.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/Verifier.java index aa7bb1b3d..d86b150aa 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/verifier/Verifier.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/Verifier.java @@ -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); + } /** diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/statics/DOUBLE_Upper.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/statics/DOUBLE_Upper.java index ed68a24a8..05846d02a 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/verifier/statics/DOUBLE_Upper.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/statics/DOUBLE_Upper.java @@ -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. */ diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/statics/LONG_Upper.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/statics/LONG_Upper.java index 9b1bbeae4..cdf5540d9 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/verifier/statics/LONG_Upper.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/statics/LONG_Upper.java @@ -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. */ diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/statics/StringRepresentation.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/statics/StringRepresentation.java index 24337f155..1039261c5 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/verifier/statics/StringRepresentation.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/statics/StringRepresentation.java @@ -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); } diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/structurals/ControlFlowGraph.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/structurals/ControlFlowGraph.java index d64e6eb38..66a3752c0 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/verifier/structurals/ControlFlowGraph.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/structurals/ControlFlowGraph.java @@ -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"); diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/structurals/ExecutionVisitor.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/structurals/ExecutionVisitor.java index c4951520c..59236033d 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/verifier/structurals/ExecutionVisitor.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/structurals/ExecutionVisitor.java @@ -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){ diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/structurals/InstConstraintVisitor.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/structurals/InstConstraintVisitor.java index 2e39d10fa..2597dc680 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/verifier/structurals/InstConstraintVisitor.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/structurals/InstConstraintVisitor.java @@ -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+"'."); } } diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/structurals/LocalVariables.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/structurals/LocalVariables.java index 44a690886..0e905c1d7 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/verifier/structurals/LocalVariables.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/structurals/LocalVariables.java @@ -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; } diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/structurals/Pass3bVerifier.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/structurals/Pass3bVerifier.java index b2375a9c6..cd344e69d 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/verifier/structurals/Pass3bVerifier.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/structurals/Pass3bVerifier.java @@ -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(){ diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/ControlFlowGraph.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/ControlFlowGraph.java new file mode 100644 index 000000000..5ca4dfd7e --- /dev/null +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/ControlFlowGraph.java @@ -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); + } +} diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/ExceptionHandler.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/ExceptionHandler.java new file mode 100644 index 000000000..62a29e1ff --- /dev/null +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/ExceptionHandler.java @@ -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; + } +} diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/ExceptionHandlers.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/ExceptionHandlers.java new file mode 100644 index 000000000..ee59b587f --- /dev/null +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/ExceptionHandlers.java @@ -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)); + } + } + +} diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/ExecutionVisitor.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/ExecutionVisitor.java new file mode 100644 index 000000000..86b34ba77 --- /dev/null +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/ExecutionVisitor.java @@ -0,0 +1,1143 @@ +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.Constants; +import org.aspectj.apache.bcel.classfile.Constant; +import org.aspectj.apache.bcel.classfile.ConstantClass; +import org.aspectj.apache.bcel.classfile.ConstantDouble; +import org.aspectj.apache.bcel.classfile.ConstantFloat; +import org.aspectj.apache.bcel.classfile.ConstantInteger; +import org.aspectj.apache.bcel.classfile.ConstantLong; +import org.aspectj.apache.bcel.classfile.ConstantString; +import org.aspectj.apache.bcel.generic.*; + +/** + * This Visitor class may be used for a type-based Java Virtual Machine + * simulation. + * It does not check for correct types on the OperandStack or in the + * LocalVariables; nor does it check their sizes are sufficiently big. + * Thus, to use this Visitor for bytecode verifying, you have to make sure + * externally that the type constraints of the Java Virtual Machine instructions + * are satisfied. An InstConstraintVisitor may be used for this. + * Anyway, this Visitor does not mandate it. For example, when you + * visitIADD(IADD o), then there are two stack slots popped and one + * stack slot containing a Type.INT is pushed (where you could also + * pop only one slot if you know there are two Type.INT on top of the + * stack). Monitor-specific behaviour is not simulated. + * + * </P><B>Conventions:</B> + * + * Type.VOID will never be pushed onto the stack. Type.DOUBLE and Type.LONG + * that would normally take up two stack slots (like Double_HIGH and + * Double_LOW) are represented by a simple single Type.DOUBLE or Type.LONG + * object on the stack here. + * If a two-slot type is stored into a local variable, the next variable + * is given the type Type.UNKNOWN. + * + * @version $Id$ + * @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A> + * @see #visitDSTORE(DSTORE o) + * @see InstConstraintVisitor + */ +public class ExecutionVisitor extends EmptyVisitor implements Visitor{ + + /** + * The executionframe we're operating on. + */ + private Frame frame = null; + + /** + * The ConstantPoolGen we're working with. + * @see #setConstantPoolGen(ConstantPoolGen) + */ + private ConstantPoolGen cpg = null; + + /** + * Constructor. Constructs a new instance of this class. + */ + public ExecutionVisitor(){} + public ExecutionVisitor(ConstantPoolGen cpg) {this.cpg=cpg;} + + boolean isJava5OrLater = true; + + private int pos; + /** + * The OperandStack from the current Frame we're operating on. + * @see #setFrame(Frame) + */ + private OperandStack stack(){ + return frame.getStack(); + } + + /** + * The LocalVariables from the current Frame we're operating on. + * @see #setFrame(Frame) + */ + private LocalVariables locals(){ + return frame.getLocals(); + } + + /** + * Sets the ConstantPoolGen needed for symbolic execution. + */ + public void setConstantPoolGen(ConstantPoolGen cpg){ + this.cpg = cpg; + } + + /** + * The only method granting access to the single instance of + * the ExecutionVisitor class. Before actively using this + * instance, <B>SET THE ConstantPoolGen FIRST</B>. + * @see #setConstantPoolGen(ConstantPoolGen) + */ + public void setFrame(Frame f){ + this.frame = f; + } + + ///** Symbolically executes the corresponding Java Virtual Machine instruction. */ + //public void visitWIDE(WIDE o){ + // The WIDE instruction is modelled as a flag + // of the embedded instructions in BCEL. + // Therefore BCEL checks for possible errors + // when parsing in the .class file: We don't + // have even the possibilty to care for WIDE + // here. + //} + + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitAALOAD(AALOAD o){ + stack().pop(); // pop the index int +//System.out.print(stack().peek()); + Type t = stack().pop(); // Pop Array type + if (t == Type.NULL){ + stack().push(Type.NULL); + } // Do nothing stackwise --- a NullPointerException is thrown at Run-Time + else{ + ArrayType at = (ArrayType) t; + stack().push(at.getElementType()); + } + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitAASTORE(AASTORE o){ + stack().pop(); + stack().pop(); + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitACONST_NULL(ACONST_NULL o){ + stack().push(Type.NULL); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitALOAD(ALOAD o){ + stack().push(locals().get(o.getIndex())); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitANEWARRAY(ANEWARRAY o){ + stack().pop(); //count + stack().push( new ArrayType(o.getType(cpg), 1) ); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitARETURN(ARETURN o){ + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitARRAYLENGTH(ARRAYLENGTH o){ + stack().pop(); + stack().push(Type.INT); + } + + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitASTORE(ASTORE o){ + locals().set(o.getIndex(), stack().pop()); +// System.err.println("TODO-DEBUG: set LV '"+o.getIndex()+"' to '"+locals().get(o.getIndex())+"'."); + } + + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitATHROW(ATHROW o){ + Type t = stack().pop(); + stack().clear(); + if (t.equals(Type.NULL)) + stack().push(Type.getType("Ljava/lang/NullPointerException;")); + else + stack().push(t); + } + + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitBALOAD(BALOAD o){ + stack().pop(); + stack().pop(); + stack().push(Type.INT); + } + + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitBASTORE(BASTORE o){ + stack().pop(); + stack().pop(); + stack().pop(); + } + + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitBIPUSH(BIPUSH o){ + stack().push(Type.INT); + } + + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitCALOAD(CALOAD o){ + stack().pop(); + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitCASTORE(CASTORE o){ + stack().pop(); + stack().pop(); + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitCHECKCAST(CHECKCAST o){ + // It's possibly wrong to do so, but SUN's + // ByteCode verifier seems to do (only) this, too. + // TODO: One could use a sophisticated analysis here to check + // if a type cannot possibly be cated to another and by + // so doing predict the ClassCastException at run-time. + stack().pop(); + stack().push(o.getType(cpg)); + } + + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitD2F(D2F o){ + stack().pop(); + stack().push(Type.FLOAT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitD2I(D2I o){ + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitD2L(D2L o){ + stack().pop(); + stack().push(Type.LONG); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitDADD(DADD o){ + stack().pop(); + stack().pop(); + stack().push(Type.DOUBLE); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitDALOAD(DALOAD o){ + stack().pop(); + stack().pop(); + stack().push(Type.DOUBLE); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitDASTORE(DASTORE o){ + stack().pop(); + stack().pop(); + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitDCMPG(DCMPG o){ + stack().pop(); + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitDCMPL(DCMPL o){ + stack().pop(); + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitDCONST(DCONST o){ + stack().push(Type.DOUBLE); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitDDIV(DDIV o){ + stack().pop(); + stack().pop(); + stack().push(Type.DOUBLE); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitDLOAD(DLOAD o){ + stack().push(Type.DOUBLE); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitDMUL(DMUL o){ + stack().pop(); + stack().pop(); + stack().push(Type.DOUBLE); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitDNEG(DNEG o){ + stack().pop(); + stack().push(Type.DOUBLE); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitDREM(DREM o){ + stack().pop(); + stack().pop(); + stack().push(Type.DOUBLE); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitDRETURN(DRETURN o){ + stack().pop(); + } + /** 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.TOP);//UNKNOWN);//fubar + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitDSUB(DSUB o){ + stack().pop(); + stack().pop(); + stack().push(Type.DOUBLE); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitDUP(DUP o){ + Type t = stack().pop(); + stack().push(t); + stack().push(t); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitDUP_X1(DUP_X1 o){ + Type w1 = stack().pop(); + Type w2 = stack().pop(); + stack().push(w1); + stack().push(w2); + stack().push(w1); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitDUP_X2(DUP_X2 o){ + Type w1 = stack().pop(); + Type w2 = stack().pop(); + if (w2.getSize() == 2){ + stack().push(w1); + stack().push(w2); + stack().push(w1); + } + else{ + Type w3 = stack().pop(); + stack().push(w1); + stack().push(w3); + stack().push(w2); + stack().push(w1); + } + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitDUP2(DUP2 o){ + Type t = stack().pop(); + if (t.getSize() == 2){ + stack().push(t); + stack().push(t); + } + else{ // t.getSize() is 1 + Type u = stack().pop(); + stack().push(u); + stack().push(t); + stack().push(u); + stack().push(t); + } + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitDUP2_X1(DUP2_X1 o){ + Type t = stack().pop(); + if (t.getSize() == 2){ + Type u = stack().pop(); + stack().push(t); + stack().push(u); + stack().push(t); + } + else{ //t.getSize() is1 + Type u = stack().pop(); + Type v = stack().pop(); + stack().push(u); + stack().push(t); + stack().push(v); + stack().push(u); + stack().push(t); + } + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitDUP2_X2(DUP2_X2 o){ + Type t = stack().pop(); + if (t.getSize() == 2){ + Type u = stack().pop(); + if (u.getSize() == 2){ + stack().push(t); + stack().push(u); + stack().push(t); + }else{ + Type v = stack().pop(); + stack().push(t); + stack().push(v); + stack().push(u); + stack().push(t); + } + } + else{ //t.getSize() is 1 + Type u = stack().pop(); + Type v = stack().pop(); + if (v.getSize() == 2){ + stack().push(u); + stack().push(t); + stack().push(v); + stack().push(u); + stack().push(t); + }else{ + Type w = stack().pop(); + stack().push(u); + stack().push(t); + stack().push(w); + stack().push(v); + stack().push(u); + stack().push(t); + } + } + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitF2D(F2D o){ + stack().pop(); + stack().push(Type.DOUBLE); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitF2I(F2I o){ + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitF2L(F2L o){ + stack().pop(); + stack().push(Type.LONG); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitFADD(FADD o){ + stack().pop(); + stack().pop(); + stack().push(Type.FLOAT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitFALOAD(FALOAD o){ + stack().pop(); + stack().pop(); + stack().push(Type.FLOAT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitFASTORE(FASTORE o){ + stack().pop(); + stack().pop(); + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitFCMPG(FCMPG o){ + stack().pop(); + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitFCMPL(FCMPL o){ + stack().pop(); + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitFCONST(FCONST o){ + stack().push(Type.FLOAT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitFDIV(FDIV o){ + stack().pop(); + stack().pop(); + stack().push(Type.FLOAT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitFLOAD(FLOAD o){ + stack().push(Type.FLOAT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitFMUL(FMUL o){ + stack().pop(); + stack().pop(); + stack().push(Type.FLOAT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitFNEG(FNEG o){ + stack().pop(); + stack().push(Type.FLOAT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitFREM(FREM o){ + stack().pop(); + stack().pop(); + stack().push(Type.FLOAT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitFRETURN(FRETURN o){ + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitFSTORE(FSTORE o){ + locals().set(o.getIndex(), stack().pop()); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitFSUB(FSUB o){ + stack().pop(); + stack().pop(); + stack().push(Type.FLOAT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitGETFIELD(GETFIELD o){ + stack().pop(); + Type t = o.getFieldType(cpg); + if ( t.equals(Type.BOOLEAN) || + t.equals(Type.CHAR) || + t.equals(Type.BYTE) || + t.equals(Type.SHORT) ) + t = Type.INT; + stack().push(t); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitGETSTATIC(GETSTATIC o){ + Type t = o.getFieldType(cpg); + if ( t.equals(Type.BOOLEAN) || + t.equals(Type.CHAR) || + t.equals(Type.BYTE) || + t.equals(Type.SHORT) ) + t = Type.INT; + stack().push(t); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitGOTO(GOTO o){ + // no stack changes. + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitGOTO_W(GOTO_W o){ + // no stack changes. + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitI2B(I2B o){ + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitI2C(I2C o){ + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitI2D(I2D o){ + stack().pop(); + stack().push(Type.DOUBLE); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitI2F(I2F o){ + stack().pop(); + stack().push(Type.FLOAT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitI2L(I2L o){ + stack().pop(); + stack().push(Type.LONG); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitI2S(I2S o){ + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIADD(IADD o){ + stack().pop(); + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIALOAD(IALOAD o){ + stack().pop(); + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIAND(IAND o){ + stack().pop(); + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIASTORE(IASTORE o){ + stack().pop(); + stack().pop(); + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitICONST(ICONST o){ + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIDIV(IDIV o){ + stack().pop(); + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIF_ACMPEQ(IF_ACMPEQ o){ + if (stack().size()<2) { + int stop = 1; + } + stack().pop(); + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIF_ACMPNE(IF_ACMPNE o){ + stack().pop(); + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIF_ICMPEQ(IF_ICMPEQ o){ + stack().pop(); + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIF_ICMPGE(IF_ICMPGE o){ + stack().pop(); + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIF_ICMPGT(IF_ICMPGT o){ + stack().pop(); + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIF_ICMPLE(IF_ICMPLE o){ + stack().pop(); + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIF_ICMPLT(IF_ICMPLT o){ + stack().pop(); + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIF_ICMPNE(IF_ICMPNE o){ + stack().pop(); + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIFEQ(IFEQ o){ + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIFGE(IFGE o){ + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIFGT(IFGT o){ + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIFLE(IFLE o){ + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIFLT(IFLT o){ + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIFNE(IFNE o){ + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIFNONNULL(IFNONNULL o){ + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIFNULL(IFNULL o){ + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIINC(IINC o){ + // stack is not changed. + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitILOAD(ILOAD o){ + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIMUL(IMUL o){ + stack().pop(); + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitINEG(INEG o){ + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitINSTANCEOF(INSTANCEOF o){ + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitINVOKEINTERFACE(INVOKEINTERFACE o){ + stack().pop(); //objectref +// for (int i=0; i<o.getArgumentTypes(cpg).length; i++){ +// stack().pop(); +// } + Type[] args = o.getArgumentTypes(cpg); + int size=0; + for (int i = 0; i < args.length; i++) { size+=args[i].getSize();} + for (int i=0;i<size;i++) stack().pop(); + // We are sure the invoked method will xRETURN eventually + // We simulate xRETURNs functionality here because we + // don't really "jump into" and simulate the invoked + // method. + if (o.getReturnType(cpg) != Type.VOID){ + Type t = o.getReturnType(cpg); + if ( t.equals(Type.BOOLEAN) || + t.equals(Type.CHAR) || + t.equals(Type.BYTE) || + t.equals(Type.SHORT) ) + t = Type.INT; + stack().push(t); + } + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitINVOKESPECIAL(INVOKESPECIAL o){ + if (o.getMethodName(cpg).equals(Constants.CONSTRUCTOR_NAME)){ + Type[] args = o.getArgumentTypes(cpg); + int size=0; + for (int i = 0; i < args.length; i++) { size+=args[i].getSize();} + UninitializedObjectType t = (UninitializedObjectType) stack().peek(size); + if (t == Frame._this){ + Frame._this = null; + } + stack().initializeObject(t); + locals().initializeObject(t); + } + stack().pop(); //objectref + for (int i=0; i<o.getArgumentTypes(cpg).length; i++){ + stack().pop(); + } + // We are sure the invoked method will xRETURN eventually + // We simulate xRETURNs functionality here because we + // don't really "jump into" and simulate the invoked + // method. + if (o.getReturnType(cpg) != Type.VOID){ + Type t = o.getReturnType(cpg); + if ( t.equals(Type.BOOLEAN) || + t.equals(Type.CHAR) || + t.equals(Type.BYTE) || + t.equals(Type.SHORT) ) + t = Type.INT; + stack().push(t); + } + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitINVOKESTATIC(INVOKESTATIC o){ + for (int i=0; i<o.getArgumentTypes(cpg).length; i++){ + stack().pop(); + } + // We are sure the invoked method will xRETURN eventually + // We simulate xRETURNs functionality here because we + // don't really "jump into" and simulate the invoked + // method. + if (o.getReturnType(cpg) != Type.VOID){ + Type t = o.getReturnType(cpg); + if ( t.equals(Type.BOOLEAN) || + t.equals(Type.CHAR) || + t.equals(Type.BYTE) || + t.equals(Type.SHORT) ) + t = Type.INT; + stack().push(t); + } + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitINVOKEVIRTUAL(INVOKEVIRTUAL o){ + stack().pop(); //objectref + for (int i=0; i<o.getArgumentTypes(cpg).length; i++){ + stack().pop(); + } + // We are sure the invoked method will xRETURN eventually + // We simulate xRETURNs functionality here because we + // don't really "jump into" and simulate the invoked + // method. + if (o.getReturnType(cpg) != Type.VOID){ + Type t = o.getReturnType(cpg); + if ( t.equals(Type.BOOLEAN) || + t.equals(Type.CHAR) || + t.equals(Type.BYTE) || + t.equals(Type.SHORT) ) + t = Type.INT; + stack().push(t); + } + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIOR(IOR o){ + stack().pop(); + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIREM(IREM o){ + stack().pop(); + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIRETURN(IRETURN o){ + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitISHL(ISHL o){ + stack().pop(); + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitISHR(ISHR o){ + stack().pop(); + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitISTORE(ISTORE o){ + locals().set(o.getIndex(), stack().pop()); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitISUB(ISUB o){ + stack().pop(); + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIUSHR(IUSHR o){ + stack().pop(); + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitIXOR(IXOR o){ + stack().pop(); + stack().pop(); + stack().push(Type.INT); + } + + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitJSR(JSR o){ + stack().push(new ReturnaddressType(o.physicalSuccessor())); +//System.err.println("TODO-----------:"+o.physicalSuccessor()); + } + + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitJSR_W(JSR_W o){ + stack().push(new ReturnaddressType(o.physicalSuccessor())); + } + + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitL2D(L2D o){ + stack().pop(); + stack().push(Type.DOUBLE); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitL2F(L2F o){ + stack().pop(); + stack().push(Type.FLOAT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitL2I(L2I o){ + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitLADD(LADD o){ + stack().pop(); + stack().pop(); + stack().push(Type.LONG); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitLALOAD(LALOAD o){ + stack().pop(); + stack().pop(); + stack().push(Type.LONG); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitLAND(LAND o){ + stack().pop(); + stack().pop(); + stack().push(Type.LONG); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitLASTORE(LASTORE o){ + stack().pop(); + stack().pop(); + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitLCMP(LCMP o){ + stack().pop(); + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitLCONST(LCONST o){ + stack().push(Type.LONG); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitLDC(LDC o){ + Constant c = cpg.getConstant(o.getIndex()); + if (c instanceof ConstantInteger){ + stack().push(Type.INT); + } else if (c instanceof ConstantFloat){ + stack().push(Type.FLOAT); + return; + } else if (c instanceof ConstantString){ + stack().push(Type.STRING); + } else if (c instanceof ConstantClass && isJava5OrLater) { + stack().push(Type.CLASS); + } else { + throw new RuntimeException("Incorrect constant type for LDC: "+c); + } + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitLDC_W(LDC_W o){ + Constant c = cpg.getConstant(o.getIndex()); + if (c instanceof ConstantInteger){ + stack().push(Type.INT); + } else if (c instanceof ConstantFloat){ + stack().push(Type.FLOAT); + } else if (c instanceof ConstantString){ + stack().push(Type.STRING); + } else if (c instanceof ConstantClass && isJava5OrLater) { + stack().push(Type.CLASS); + } else { + throw new RuntimeException("Incorrect constant type for LDC_W"); + } + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitLDC2_W(LDC2_W o){ + Constant c = cpg.getConstant(o.getIndex()); + if (c instanceof ConstantLong){ + stack().push(Type.LONG); + } + if (c instanceof ConstantDouble){ + stack().push(Type.DOUBLE); + } + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitLDIV(LDIV o){ + stack().pop(); + stack().pop(); + stack().push(Type.LONG); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitLLOAD(LLOAD o){ + stack().push(locals().get(o.getIndex())); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitLMUL(LMUL o){ + stack().pop(); + stack().pop(); + stack().push(Type.LONG); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitLNEG(LNEG o){ + stack().pop(); + stack().push(Type.LONG); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitLOOKUPSWITCH(LOOKUPSWITCH o){ + stack().pop(); //key + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitLOR(LOR o){ + stack().pop(); + stack().pop(); + stack().push(Type.LONG); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitLREM(LREM o){ + stack().pop(); + stack().pop(); + stack().push(Type.LONG); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitLRETURN(LRETURN o){ + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitLSHL(LSHL o){ + stack().pop(); + stack().pop(); + stack().push(Type.LONG); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitLSHR(LSHR o){ + stack().pop(); + stack().pop(); + stack().push(Type.LONG); + } + /** 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.TOP);//UNKNOWN); //fubar + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitLSUB(LSUB o){ + stack().pop(); + stack().pop(); + stack().push(Type.LONG); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitLUSHR(LUSHR o){ + stack().pop(); + stack().pop(); + stack().push(Type.LONG); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitLXOR(LXOR o){ + stack().pop(); + stack().pop(); + stack().push(Type.LONG); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitMONITORENTER(MONITORENTER o){ + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitMONITOREXIT(MONITOREXIT o){ + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitMULTIANEWARRAY(MULTIANEWARRAY o){ + for (int i=0; i<o.getDimensions(); i++){ + stack().pop(); + } + stack().push(o.getType(cpg)); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitNEW(NEW o){ + stack().push(new UninitializedObjectType((ObjectType) (o.getType(cpg)),false,pos)); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitNEWARRAY(NEWARRAY o){ + stack().pop(); + stack().push(o.getType()); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitNOP(NOP o){ + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitPOP(POP o){ + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitPOP2(POP2 o){ + Type t = stack().pop(); + if (t.getSize() == 1){ + stack().pop(); + } + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitPUTFIELD(PUTFIELD o){ + stack().pop(); + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitPUTSTATIC(PUTSTATIC o){ + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitRET(RET o){ + // do nothing, return address + // is in in the local variables. + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitRETURN(RETURN o){ + // do nothing. + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitSALOAD(SALOAD o){ + stack().pop(); + stack().pop(); + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitSASTORE(SASTORE o){ + stack().pop(); + stack().pop(); + stack().pop(); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitSIPUSH(SIPUSH o){ + stack().push(Type.INT); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitSWAP(SWAP o){ + Type t = stack().pop(); + Type u = stack().pop(); + stack().push(t); + stack().push(u); + } + /** Symbolically executes the corresponding Java Virtual Machine instruction. */ + public void visitTABLESWITCH(TABLESWITCH o){ + stack().pop(); + } + public void setPosition(int position) { + this.pos = position; + } +} diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/Frame.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/Frame.java new file mode 100644 index 000000000..c20bf1b0e --- /dev/null +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/Frame.java @@ -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; + } + +} diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/InstConstraintVisitor.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/InstConstraintVisitor.java new file mode 100644 index 000000000..8e39b621c --- /dev/null +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/InstConstraintVisitor.java @@ -0,0 +1,2667 @@ +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.Constants; +import org.aspectj.apache.bcel.Repository; +import org.aspectj.apache.bcel.classfile.Constant; +import org.aspectj.apache.bcel.classfile.ConstantClass; +import org.aspectj.apache.bcel.classfile.ConstantDouble; +import org.aspectj.apache.bcel.classfile.ConstantInteger; +import org.aspectj.apache.bcel.classfile.ConstantFieldref; +import org.aspectj.apache.bcel.classfile.ConstantFloat; +import org.aspectj.apache.bcel.classfile.ConstantLong; +import org.aspectj.apache.bcel.classfile.ConstantString; +import org.aspectj.apache.bcel.classfile.Field; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.generic.*; +import org.aspectj.apache.bcel.verifier.*; +import org.aspectj.apache.bcel.verifier.exc.*; + + +/** + * A Visitor class testing for valid preconditions of JVM instructions. + * The instance of this class will throw a StructuralCodeConstraintException + * instance if an instruction is visitXXX()ed which has preconditions that are + * not satisfied. + * TODO: Currently, the JVM's behaviour concerning monitors (MONITORENTER, + * MONITOREXIT) is not modeled in JustIce. + * + * @version $Id$ + * @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 + */ +public class InstConstraintVisitor extends EmptyVisitor implements org.aspectj.apache.bcel.generic.Visitor{ + + private static ObjectType GENERIC_ARRAY = new ObjectType("org.aspectj.apache.bcel.verifier.structurals.GenericArray"); + + /** + * The constructor. Constructs a new instance of this class. + */ + public InstConstraintVisitor(){} + public InstConstraintVisitor(ConstantPoolGen cpg ){this.cpg=cpg;} + + /** + * The Execution Frame we're working on. + * + * @see #setFrame(Frame f) + * @see #locals() + * @see #stack() + */ + private Frame frame = null; + + /** + * The ConstantPoolGen we're working on. + * + * @see #setConstantPoolGen(ConstantPoolGen cpg) + */ + private ConstantPoolGen cpg = null; + + /** + * The MethodGen we're working on. + * + * @see #setMethodGen(MethodGen mg) + */ + private MethodGen mg = null; + + /** + * The OperandStack we're working on. + * + * @see #setFrame(Frame f) + */ + private OperandStack stack(){ + return frame.getStack(); + } + + /** + * The LocalVariables we're working on. + * + * @see #setFrame(Frame f) + */ + private LocalVariables locals(){ + return frame.getLocals(); + } + + /** + * This method is called by the visitXXX() to notify the acceptor of this InstConstraintVisitor + * that a constraint violation has occured. This is done by throwing an instance of a + * StructuralCodeConstraintException. + * @throws org.aspectj.apache.bcel.verifier.exc.StructuralCodeConstraintException always. + */ + private void constraintViolated(Instruction violator, String description){ + String fq_classname = violator.getClass().getName(); + throw new StructuralCodeConstraintException("Instruction "+ fq_classname.substring(fq_classname.lastIndexOf('.')+1) +" constraint violated: " + description); + } + + /** + * This returns the single instance of the InstConstraintVisitor class. + * To operate correctly, other values must have been set before actually + * using the instance. + * Use this method for performance reasons. + * + * @see #setConstantPoolGen(ConstantPoolGen cpg) + * @see #setMethodGen(MethodGen mg) + */ + public void setFrame(Frame f){ + this.frame = f; + //if (singleInstance.mg == null || singleInstance.cpg == null) throw new AssertionViolatedException("Forgot to set important values first."); + } + + /** + * Sets the ConstantPoolGen instance needed for constraint + * checking prior to execution. + */ + public void setConstantPoolGen(ConstantPoolGen cpg){ + this.cpg = cpg; + } + + /** + * Sets the MethodGen instance needed for constraint + * checking prior to execution. + */ + public void setMethodGen(MethodGen mg){ + this.mg = mg; + } + + /** + * Assures index is of type INT. + * @throws org.aspectj.apache.bcel.verifier.exc.StructuralCodeConstraintException if the above constraint is not satisfied. + */ + private void indexOfInt(Instruction o, Type index){ + if (! index.equals(Type.INT)) + constraintViolated(o, "The 'index' is not of type int but of type "+index+"."); + } + + /** + * Assures the ReferenceType r is initialized (or Type.NULL). + * Formally, this means (!(r instanceof UninitializedObjectType)), because + * there are no uninitialized array types. + * @throws org.aspectj.apache.bcel.verifier.exc.StructuralCodeConstraintException if the above constraint is not satisfied. + */ + private void referenceTypeIsInitialized(Instruction o, ReferenceType r){ + if (r instanceof UninitializedObjectType){ + constraintViolated(o, "Working on an uninitialized object '"+r+"'."); + } + } + + /** Assures value is of type INT. */ + private void valueOfInt(Instruction o, Type value){ + if (! value.equals(Type.INT)) + constraintViolated(o, "The 'value' is not of type int but of type "+value+"."); + } + + /** + * Assures arrayref is of ArrayType or NULL; + * returns true if and only if arrayref is non-NULL. + * @throws org.aspectj.apache.bcel.verifier.exc.StructuralCodeConstraintException if the above constraint is violated. + */ + private boolean arrayrefOfArrayType(Instruction o, Type arrayref){ + if (! ((arrayref instanceof ArrayType) || arrayref.equals(Type.NULL)) ) + constraintViolated(o, "The 'arrayref' does not refer to an array but is of type "+arrayref+"."); + return (arrayref instanceof ArrayType); + } + + /***************************************************************/ + /* MISC */ + /***************************************************************/ + /** + * Ensures the general preconditions of an instruction that accesses the stack. + * This method is here because BCEL has no such superinterface for the stack + * accessing instructions; and there are funny unexpected exceptions in the + * semantices of the superinterfaces and superclasses provided. + * E.g. SWAP is a StackConsumer, but DUP_X1 is not a StackProducer. + * Therefore, this method is called by all StackProducer, StackConsumer, + * and StackInstruction instances via their visitXXX() method. + * Unfortunately, as the superclasses and superinterfaces overlap, some instructions + * cause this method to be called two or three times. [TODO: Fix this.] + * + * @see #visitStackConsumer(StackConsumer o) + * @see #visitStackProducer(StackProducer o) + * @see #visitStackInstruction(StackInstruction o) + */ + private void _visitStackAccessor(Instruction o){ + int consume = o.consumeStack(cpg); // Stack values are always consumed first; then produced. + if (consume > stack().slotsUsed()){ + constraintViolated( o, "Cannot consume "+consume+" stack slots: only "+stack().slotsUsed()+" slot(s) left on stack!\nStack:\n"+stack()); + } + + int produce = o.produceStack(cpg) - o.consumeStack(cpg); // Stack values are always consumed first; then produced. + if ( produce + stack().slotsUsed() > stack().maxStack() ){ + constraintViolated( o, "Cannot produce "+produce+" stack slots: only "+(stack().maxStack()-stack().slotsUsed())+" free stack slot(s) left.\nStack:\n"+stack()); + } + } + + /***************************************************************/ + /* "generic"visitXXXX methods where XXXX is an interface */ + /* therefore, we don't know the order of visiting; but we know */ + /* these methods are called before the visitYYYY methods below */ + /***************************************************************/ + + /** + * Assures the generic preconditions of a LoadClass instance. + * The referenced class is loaded and pass2-verified. + */ + public void visitLoadClass(LoadClass o){ + ObjectType t = o.getLoadClassType(cpg); +// this really means we can't verify a class by itself... +// if (t != null){// null means "no class is loaded" +// Verifier v = VerifierFactory.getVerifier(t.getClassName()); +// VerificationResult vr = v.doPass2(); +// if (vr.getStatus() != VerificationResult.VERIFIED_OK){ +// constraintViolated((Instruction) o, "Class '"+o.getLoadClassType(cpg).getClassName()+"' is referenced, but cannot be loaded and resolved: '"+vr+"'."); +// } +// } + } + + /** + * Ensures the general preconditions of a StackConsumer instance. + */ + public void visitStackConsumer(StackConsumer o){ + _visitStackAccessor((Instruction) o); + } + + /** + * Ensures the general preconditions of a StackProducer instance. + */ + public void visitStackProducer(StackProducer o){ + _visitStackAccessor((Instruction) o); + } + + + /***************************************************************/ + /* "generic" visitYYYY methods where YYYY is a superclass. */ + /* therefore, we know the order of visiting; we know */ + /* these methods are called after the visitXXXX methods above. */ + /***************************************************************/ + /** + * Ensures the general preconditions of a CPInstruction instance. + */ + public void visitCPInstruction(CPInstruction o){ + int idx = o.getIndex(); + if ((idx < 0) || (idx >= cpg.getSize())){ + throw new AssertionViolatedException("Huh?! Constant pool index of instruction '"+o+"' illegal? Pass 3a should have checked this!"); + } + } + + /** + * Ensures the general preconditions of a FieldInstruction instance. + */ + public void visitFieldInstruction(FieldInstruction o){ + // visitLoadClass(o) has been called before: Every FieldOrMethod + // implements LoadClass. + // visitCPInstruction(o) has been called before. + // A FieldInstruction may be: GETFIELD, GETSTATIC, PUTFIELD, PUTSTATIC + Constant c = cpg.getConstant(o.getIndex()); + if (!(c instanceof ConstantFieldref)){ + constraintViolated(o, "Index '"+o.getIndex()+"' should refer to a CONSTANT_Fieldref_info structure, but refers to '"+c+"'."); + } + // the o.getClassType(cpg) type has passed pass 2; see visitLoadClass(o). + Type t = o.getType(cpg); + if (t instanceof ObjectType){ + String name = ((ObjectType)t).getClassName(); + 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+"'."); + } + } + } + + /** + * Ensures the general preconditions of an InvokeInstruction instance. + */ + public void visitInvokeInstruction(InvokeInstruction o){ + // visitLoadClass(o) has been called before: Every FieldOrMethod + // implements LoadClass. + // visitCPInstruction(o) has been called before. + //TODO + } + + /** + * Ensures the general preconditions of a StackInstruction instance. + */ + public void visitStackInstruction(StackInstruction o){ + _visitStackAccessor(o); + } + + /** + * Assures the generic preconditions of a LocalVariableInstruction instance. + * That is, the index of the local variable must be valid. + */ + public void visitLocalVariableInstruction(LocalVariableInstruction o){ + if (locals().maxLocals() <= (o.getType(cpg).getSize()==1? o.getIndex() : o.getIndex()+1) ){ + constraintViolated(o, "The 'index' is not a valid index into the local variable array."); + } + } + + /** + * Assures the generic preconditions of a LoadInstruction instance. + */ + public void visitLoadInstruction(LoadInstruction o){ + //visitLocalVariableInstruction(o) is called before, because it is more generic. + + // LOAD instructions must not read Type.UNKNOWN + if (locals().get(o.getIndex()) == Type.UNKNOWN){ + constraintViolated(o, "Read-Access on local variable "+o.getIndex()+" with unknown content."); + } + + // LOAD instructions, two-slot-values at index N must have Type.UNKNOWN + // as a symbol for the higher halve at index N+1 + // [suppose some instruction put an int at N+1--- our double at N is defective] + if (o.getType(cpg).getSize() == 2){ + if (locals().get(o.getIndex()+1) != Type.UNKNOWN){ + constraintViolated(o, "Reading a two-locals value from local variables "+o.getIndex()+" and "+(o.getIndex()+1)+" where the latter one is destroyed."); + } + } + + // LOAD instructions must read the correct type. + if (!(o instanceof ALOAD)){ + if (locals().get(o.getIndex()) != o.getType(cpg) ){ + constraintViolated(o, "Local Variable type and LOADing Instruction type mismatch: Local Variable: '"+locals().get(o.getIndex())+"'; Instruction type: '"+o.getType(cpg)+"'."); + } + } + else{ // we deal with an ALOAD + if (!(locals().get(o.getIndex()) instanceof ReferenceType)){ + constraintViolated(o, "Local Variable type and LOADing Instruction type mismatch: Local Variable: '"+locals().get(o.getIndex())+"'; Instruction expects a ReferenceType."); + } + // ALOAD __IS ALLOWED__ to put uninitialized objects onto the stack! + //referenceTypeIsInitialized(o, (ReferenceType) (locals().get(o.getIndex()))); + } + + // LOAD instructions must have enough free stack slots. + if ((stack().maxStack() - stack().slotsUsed()) < o.getType(cpg).getSize()){ + constraintViolated(o, "Not enough free stack slots to load a '"+o.getType(cpg)+"' onto the OperandStack."); + } + } + + /** + * Assures the generic preconditions of a StoreInstruction instance. + */ + public void visitStoreInstruction(StoreInstruction o){ + //visitLocalVariableInstruction(o) is called before, because it is more generic. + + if (stack().isEmpty()){ // Don't bother about 1 or 2 stack slots used. This check is implicitely done below while type checking. + constraintViolated(o, "Cannot STORE: Stack to read from is empty."); + } + + if ( (!(o instanceof ASTORE)) ){ + if (! (stack().peek() == o.getType(cpg)) ){// the other xSTORE types are singletons in BCEL. + constraintViolated(o, "Stack top type and STOREing Instruction type mismatch: Stack top: '"+stack().peek()+"'; Instruction type: '"+o.getType(cpg)+"'."); + } + } + else{ // we deal with ASTORE + Type stacktop = stack().peek(); + if ( (!(stacktop instanceof ReferenceType)) && (!(stacktop instanceof ReturnaddressType)) ){ + constraintViolated(o, "Stack top type and STOREing Instruction type mismatch: Stack top: '"+stack().peek()+"'; Instruction expects a ReferenceType or a ReturnadressType."); + } + if (stacktop instanceof ReferenceType){ + referenceTypeIsInitialized(o, (ReferenceType) stacktop); + } + } + } + + /** + * Assures the generic preconditions of a ReturnInstruction instance. + */ + public void visitReturnInstruction(ReturnInstruction o){ + if (o instanceof RETURN){ + return; + } + if (o instanceof ARETURN){ + if (stack().peek() == Type.NULL){ + return; + } + else{ + if (! (stack().peek() instanceof ReferenceType)){ + constraintViolated(o, "Reference type expected on top of stack, but is: '"+stack().peek()+"'."); + } + referenceTypeIsInitialized(o, (ReferenceType) (stack().peek())); + //ReferenceType objectref = (ReferenceType) (stack().peek()); + // TODO: This can only be checked if using Staerk-et-al's "set of object types" instead of a + // "wider cast object type" created during verification. + //if (! (objectref.isAssignmentCompatibleWith(mg.getType())) ){ + // constraintViolated(o, "Type on stack top which should be returned is a '"+stack().peek()+"' which is not assignment compatible with the return type of this method, '"+mg.getType()+"'."); + //} + } + } + else{ + Type method_type = mg.getType(); + if (method_type == Type.BOOLEAN || + method_type == Type.BYTE || + method_type == Type.SHORT || + method_type == Type.CHAR){ + method_type = Type.INT; + } + if (! ( method_type.equals( stack().peek() ))){ + constraintViolated(o, "Current method has return type of '"+mg.getType()+"' expecting a '"+method_type+"' on top of the stack. But stack top is a '"+stack().peek()+"'."); + } + } + } + + /***************************************************************/ + /* "special"visitXXXX methods for one type of instruction each */ + /***************************************************************/ + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitAALOAD(AALOAD o){ + Type arrayref = stack().peek(1); + Type index = stack().peek(0); + + indexOfInt(o, index); + if (arrayrefOfArrayType(o, arrayref)){ + if (! (((ArrayType) arrayref).getElementType() instanceof ReferenceType)){ + constraintViolated(o, "The 'arrayref' does not refer to an array with elements of a ReferenceType but to an array of "+((ArrayType) arrayref).getElementType()+"."); + } + referenceTypeIsInitialized(o, (ReferenceType) (((ArrayType) arrayref).getElementType())); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitAASTORE(AASTORE o){ + Type arrayref = stack().peek(2); + Type index = stack().peek(1); + Type value = stack().peek(0); + + indexOfInt(o, index); + if (!(value instanceof ReferenceType)){ + constraintViolated(o, "The 'value' is not of a ReferenceType but of type "+value+"."); + }else{ + referenceTypeIsInitialized(o, (ReferenceType) value); + } + // Don't bother further with "referenceTypeIsInitialized()", there are no arrays + // of an uninitialized object type. + if (arrayrefOfArrayType(o, arrayref)){ + if (! (((ArrayType) arrayref).getElementType() instanceof ReferenceType)){ + constraintViolated(o, "The 'arrayref' does not refer to an array with elements of a ReferenceType but to an array of "+((ArrayType) arrayref).getElementType()+"."); + } + if (! ((ReferenceType)value).isAssignmentCompatibleWith((ReferenceType) ((ArrayType) arrayref).getElementType())){ + constraintViolated(o, "The type of 'value' ('"+value+"') is not assignment compatible to the components of the array 'arrayref' refers to. ('"+((ArrayType) arrayref).getElementType()+"')"); + } + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitACONST_NULL(ACONST_NULL o){ + // Nothing needs to be done here. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitALOAD(ALOAD o){ + //visitLoadInstruction(LoadInstruction) is called before. + + // Nothing else needs to be done here. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitANEWARRAY(ANEWARRAY o){ + if (!stack().peek().equals(Type.INT)) + constraintViolated(o, "The 'count' at the stack top is not of type '"+Type.INT+"' but of type '"+stack().peek()+"'."); + // The runtime constant pool item at that index must be a symbolic reference to a class, + // array, or interface type. See Pass 3a. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitARETURN(ARETURN o){ + if (! (stack().peek() instanceof ReferenceType) ){ + constraintViolated(o, "The 'objectref' at the stack top is not of a ReferenceType but of type '"+stack().peek()+"'."); + } + ReferenceType objectref = (ReferenceType) (stack().peek()); + referenceTypeIsInitialized(o, objectref); + + // The check below should already done via visitReturnInstruction(ReturnInstruction), see there. + // It cannot be done using Staerk-et-al's "set of object types" instead of a + // "wider cast object type", anyway. + //if (! objectref.isAssignmentCompatibleWith(mg.getReturnType() )){ + // constraintViolated(o, "The 'objectref' type "+objectref+" at the stack top is not assignment compatible with the return type '"+mg.getReturnType()+"' of the method."); + //} + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitARRAYLENGTH(ARRAYLENGTH o){ + Type arrayref = stack().peek(0); + arrayrefOfArrayType(o, arrayref); + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitASTORE(ASTORE o){ + if (! ( (stack().peek() instanceof ReferenceType) || (stack().peek() instanceof ReturnaddressType) ) ){ + constraintViolated(o, "The 'objectref' is not of a ReferenceType or of ReturnaddressType but of "+stack().peek()+"."); + } + if (stack().peek() instanceof ReferenceType){ + referenceTypeIsInitialized(o, (ReferenceType) (stack().peek()) ); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitATHROW(ATHROW o){ + // It's stated that 'objectref' must be of a ReferenceType --- but since Throwable is + // not derived from an ArrayType, it follows that 'objectref' must be of an ObjectType or Type.NULL. + if (! ((stack().peek() instanceof ObjectType) || (stack().peek().equals(Type.NULL))) ){ + constraintViolated(o, "The 'objectref' is not of an (initialized) ObjectType but of type "+stack().peek()+"."); + } + + // NULL is a subclass of every class, so to speak. + if (stack().peek().equals(Type.NULL)) return; + + ObjectType exc = (ObjectType) (stack().peek()); + ObjectType throwable = (ObjectType) (Type.getType("Ljava/lang/Throwable;")); + if ( (! (exc.subclassOf(throwable)) ) && (! (exc.equals(throwable))) ){ + constraintViolated(o, "The 'objectref' is not of class Throwable or of a subclass of Throwable, but of '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitBALOAD(BALOAD o){ + Type arrayref = stack().peek(1); + Type index = stack().peek(0); + indexOfInt(o, index); + if (arrayrefOfArrayType(o, arrayref)){ + if (! ( (((ArrayType) arrayref).getElementType().equals(Type.BOOLEAN)) || + (((ArrayType) arrayref).getElementType().equals(Type.BYTE)) ) ){ + constraintViolated(o, "The 'arrayref' does not refer to an array with elements of a Type.BYTE or Type.BOOLEAN but to an array of '"+((ArrayType) arrayref).getElementType()+"'."); + } + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitBASTORE(BASTORE o){ + Type arrayref = stack().peek(2); + Type index = stack().peek(1); + Type value = stack().peek(0); + + indexOfInt(o, index); + valueOfInt(o, value); + if (arrayrefOfArrayType(o, arrayref)){ + if (! ( (((ArrayType) arrayref).getElementType().equals(Type.BOOLEAN)) || + (((ArrayType) arrayref).getElementType().equals(Type.BYTE)) ) ) + constraintViolated(o, "The 'arrayref' does not refer to an array with elements of a Type.BYTE or Type.BOOLEAN but to an array of '"+((ArrayType) arrayref).getElementType()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitBIPUSH(BIPUSH o){ + // Nothing to do... + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitBREAKPOINT(BREAKPOINT o){ + throw new AssertionViolatedException("In this JustIce verification pass there should not occur an illegal instruction such as BREAKPOINT."); + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitCALOAD(CALOAD o){ + Type arrayref = stack().peek(1); + Type index = stack().peek(0); + + indexOfInt(o, index); + arrayrefOfArrayType(o, arrayref); + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitCASTORE(CASTORE o){ + Type arrayref = stack().peek(2); + Type index = stack().peek(1); + Type value = stack().peek(0); + + indexOfInt(o, index); + valueOfInt(o, value); + if (arrayrefOfArrayType(o, arrayref)){ + if (! ((ArrayType) arrayref).getElementType().equals(Type.CHAR) ){ + constraintViolated(o, "The 'arrayref' does not refer to an array with elements of type char but to an array of type "+((ArrayType) arrayref).getElementType()+"."); + } + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitCHECKCAST(CHECKCAST o){ + // The objectref must be of type reference. + Type objectref = stack().peek(0); + if (!(objectref instanceof ReferenceType)){ + constraintViolated(o, "The 'objectref' is not of a ReferenceType but of type "+objectref+"."); + } + else{ + referenceTypeIsInitialized(o, (ReferenceType) objectref); + } + // The unsigned indexbyte1 and indexbyte2 are used to construct an index into the runtime constant pool of the + // current class (§3.6), where the value of the index is (indexbyte1 << 8) | indexbyte2. The runtime constant + // pool item at the index must be a symbolic reference to a class, array, or interface type. + Constant c = cpg.getConstant(o.getIndex()); + if (! (c instanceof ConstantClass)){ + constraintViolated(o, "The Constant at 'index' is not a ConstantClass, but '"+c+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitD2F(D2F o){ + if (stack().peek() != Type.DOUBLE){ + constraintViolated(o, "The value at the stack top is not of type 'double', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitD2I(D2I o){ + if (stack().peek() != Type.DOUBLE){ + constraintViolated(o, "The value at the stack top is not of type 'double', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitD2L(D2L o){ + if (stack().peek() != Type.DOUBLE){ + constraintViolated(o, "The value at the stack top is not of type 'double', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitDADD(DADD o){ + if (stack().peek() != Type.DOUBLE){ + constraintViolated(o, "The value at the stack top is not of type 'double', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.DOUBLE){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'double', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitDALOAD(DALOAD o){ + indexOfInt(o, stack().peek()); + if (stack().peek(1) == Type.NULL){ + return; + } + if (! (stack().peek(1) instanceof ArrayType)){ + constraintViolated(o, "Stack next-to-top must be of type double[] but is '"+stack().peek(1)+"'."); + } + Type t = ((ArrayType) (stack().peek(1))).getBasicType(); + if (t != Type.DOUBLE){ + constraintViolated(o, "Stack next-to-top must be of type double[] but is '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitDASTORE(DASTORE o){ + if (stack().peek() != Type.DOUBLE){ + constraintViolated(o, "The value at the stack top is not of type 'double', but of type '"+stack().peek()+"'."); + } + indexOfInt(o, stack().peek(1)); + if (stack().peek(2) == Type.NULL){ + return; + } + if (! (stack().peek(2) instanceof ArrayType)){ + constraintViolated(o, "Stack next-to-next-to-top must be of type double[] but is '"+stack().peek(2)+"'."); + } + Type t = ((ArrayType) (stack().peek(2))).getBasicType(); + if (t != Type.DOUBLE){ + constraintViolated(o, "Stack next-to-next-to-top must be of type double[] but is '"+stack().peek(2)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitDCMPG(DCMPG o){ + if (stack().peek() != Type.DOUBLE){ + constraintViolated(o, "The value at the stack top is not of type 'double', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.DOUBLE){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'double', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitDCMPL(DCMPL o){ + if (stack().peek() != Type.DOUBLE){ + constraintViolated(o, "The value at the stack top is not of type 'double', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.DOUBLE){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'double', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitDCONST(DCONST o){ + // There's nothing to be done here. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitDDIV(DDIV o){ + if (stack().peek() != Type.DOUBLE){ + constraintViolated(o, "The value at the stack top is not of type 'double', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.DOUBLE){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'double', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitDLOAD(DLOAD o){ + //visitLoadInstruction(LoadInstruction) is called before. + + // Nothing else needs to be done here. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitDMUL(DMUL o){ + if (stack().peek() != Type.DOUBLE){ + constraintViolated(o, "The value at the stack top is not of type 'double', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.DOUBLE){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'double', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitDNEG(DNEG o){ + if (stack().peek() != Type.DOUBLE){ + constraintViolated(o, "The value at the stack top is not of type 'double', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitDREM(DREM o){ + if (stack().peek() != Type.DOUBLE){ + constraintViolated(o, "The value at the stack top is not of type 'double', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.DOUBLE){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'double', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitDRETURN(DRETURN o){ + if (stack().peek() != Type.DOUBLE){ + constraintViolated(o, "The value at the stack top is not of type 'double', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitDSTORE(DSTORE o){ + //visitStoreInstruction(StoreInstruction) is called before. + + // Nothing else needs to be done here. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitDSUB(DSUB o){ + if (stack().peek() != Type.DOUBLE){ + constraintViolated(o, "The value at the stack top is not of type 'double', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.DOUBLE){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'double', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitDUP(DUP o){ + if (stack().peek().getSize() != 1){ + constraintViolated(o, "Won't DUP type on stack top '"+stack().peek()+"' because it must occupy exactly one slot, not '"+stack().peek().getSize()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitDUP_X1(DUP_X1 o){ + if (stack().peek().getSize() != 1){ + constraintViolated(o, "Type on stack top '"+stack().peek()+"' should occupy exactly one slot, not '"+stack().peek().getSize()+"'."); + } + if (stack().peek(1).getSize() != 1){ + constraintViolated(o, "Type on stack next-to-top '"+stack().peek(1)+"' should occupy exactly one slot, not '"+stack().peek(1).getSize()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitDUP_X2(DUP_X2 o){ + if (stack().peek().getSize() != 1){ + constraintViolated(o, "Stack top type must be of size 1, but is '"+stack().peek()+"' of size '"+stack().peek().getSize()+"'."); + } + if (stack().peek(1).getSize() == 2){ + return; // Form 2, okay. + } + else{ //stack().peek(1).getSize == 1. + if (stack().peek(2).getSize() != 1){ + constraintViolated(o, "If stack top's size is 1 and stack next-to-top's size is 1, stack next-to-next-to-top's size must also be 1, but is: '"+stack().peek(2)+"' of size '"+stack().peek(2).getSize()+"'."); + } + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitDUP2(DUP2 o){ + if (stack().peek().getSize() == 2){ + return; // Form 2, okay. + } + else{ //stack().peek().getSize() == 1. + if (stack().peek(1).getSize() != 1){ + constraintViolated(o, "If stack top's size is 1, then stack next-to-top's size must also be 1. But it is '"+stack().peek(1)+"' of size '"+stack().peek(1).getSize()+"'."); + } + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitDUP2_X1(DUP2_X1 o){ + if (stack().peek().getSize() == 2){ + if (stack().peek(1).getSize() != 1){ + constraintViolated(o, "If stack top's size is 2, then stack next-to-top's size must be 1. But it is '"+stack().peek(1)+"' of size '"+stack().peek(1).getSize()+"'."); + } + else{ + return; // Form 2 + } + } + else{ // stack top is of size 1 + if ( stack().peek(1).getSize() != 1 ){ + constraintViolated(o, "If stack top's size is 1, then stack next-to-top's size must also be 1. But it is '"+stack().peek(1)+"' of size '"+stack().peek(1).getSize()+"'."); + } + if ( stack().peek(2).getSize() != 1 ){ + constraintViolated(o, "If stack top's size is 1, then stack next-to-next-to-top's size must also be 1. But it is '"+stack().peek(2)+"' of size '"+stack().peek(2).getSize()+"'."); + } + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitDUP2_X2(DUP2_X2 o){ + + if (stack().peek(0).getSize() == 2){ + if (stack().peek(1).getSize() == 2){ + return; // Form 4 + } + else{// stack top size is 2, next-to-top's size is 1 + if ( stack().peek(2).getSize() != 1 ){ + constraintViolated(o, "If stack top's size is 2 and stack-next-to-top's size is 1, then stack next-to-next-to-top's size must also be 1. But it is '"+stack().peek(2)+"' of size '"+stack().peek(2).getSize()+"'."); + } + else{ + return; // Form 2 + } + } + } + else{// stack top is of size 1 + if (stack().peek(1).getSize() == 1){ + if ( stack().peek(2).getSize() == 2 ){ + return; // Form 3 + } + else{ + if ( stack().peek(3).getSize() == 1){ + return; // Form 1 + } + } + } + } + constraintViolated(o, "The operand sizes on the stack do not match any of the four forms of usage of this instruction."); + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitF2D(F2D o){ + if (stack().peek() != Type.FLOAT){ + constraintViolated(o, "The value at the stack top is not of type 'float', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitF2I(F2I o){ + if (stack().peek() != Type.FLOAT){ + constraintViolated(o, "The value at the stack top is not of type 'float', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitF2L(F2L o){ + if (stack().peek() != Type.FLOAT){ + constraintViolated(o, "The value at the stack top is not of type 'float', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitFADD(FADD o){ + if (stack().peek() != Type.FLOAT){ + constraintViolated(o, "The value at the stack top is not of type 'float', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.FLOAT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'float', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitFALOAD(FALOAD o){ + indexOfInt(o, stack().peek()); + if (stack().peek(1) == Type.NULL){ + return; + } + if (! (stack().peek(1) instanceof ArrayType)){ + constraintViolated(o, "Stack next-to-top must be of type float[] but is '"+stack().peek(1)+"'."); + } + Type t = ((ArrayType) (stack().peek(1))).getBasicType(); + if (t != Type.FLOAT){ + constraintViolated(o, "Stack next-to-top must be of type float[] but is '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitFASTORE(FASTORE o){ + if (stack().peek() != Type.FLOAT){ + constraintViolated(o, "The value at the stack top is not of type 'float', but of type '"+stack().peek()+"'."); + } + indexOfInt(o, stack().peek(1)); + if (stack().peek(2) == Type.NULL){ + return; + } + if (! (stack().peek(2) instanceof ArrayType)){ + constraintViolated(o, "Stack next-to-next-to-top must be of type float[] but is '"+stack().peek(2)+"'."); + } + Type t = ((ArrayType) (stack().peek(2))).getBasicType(); + if (t != Type.FLOAT){ + constraintViolated(o, "Stack next-to-next-to-top must be of type float[] but is '"+stack().peek(2)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitFCMPG(FCMPG o){ + if (stack().peek() != Type.FLOAT){ + constraintViolated(o, "The value at the stack top is not of type 'float', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.FLOAT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'float', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitFCMPL(FCMPL o){ + if (stack().peek() != Type.FLOAT){ + constraintViolated(o, "The value at the stack top is not of type 'float', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.FLOAT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'float', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitFCONST(FCONST o){ + // nothing to do here. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitFDIV(FDIV o){ + if (stack().peek() != Type.FLOAT){ + constraintViolated(o, "The value at the stack top is not of type 'float', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.FLOAT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'float', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitFLOAD(FLOAD o){ + //visitLoadInstruction(LoadInstruction) is called before. + + // Nothing else needs to be done here. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitFMUL(FMUL o){ + if (stack().peek() != Type.FLOAT){ + constraintViolated(o, "The value at the stack top is not of type 'float', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.FLOAT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'float', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitFNEG(FNEG o){ + if (stack().peek() != Type.FLOAT){ + constraintViolated(o, "The value at the stack top is not of type 'float', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitFREM(FREM o){ + if (stack().peek() != Type.FLOAT){ + constraintViolated(o, "The value at the stack top is not of type 'float', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.FLOAT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'float', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitFRETURN(FRETURN o){ + if (stack().peek() != Type.FLOAT){ + constraintViolated(o, "The value at the stack top is not of type 'float', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitFSTORE(FSTORE o){ + //visitStoreInstruction(StoreInstruction) is called before. + + // Nothing else needs to be done here. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitFSUB(FSUB o){ + if (stack().peek() != Type.FLOAT){ + constraintViolated(o, "The value at the stack top is not of type 'float', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.FLOAT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'float', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + + private Field findField(String field_name,String classname) { + JavaClass jc = Repository.lookupClass(classname); + Field[] fields = jc.getFields(); + Field f = null; + for (int i=0; i<fields.length; i++){ + if (fields[i].getName().equals(field_name)){ + return fields[i]; + } + } + if (jc.getSuperClass()!=null) return findField(field_name,jc.getSuperClass().getClassName()); + return null; + } + public void visitGETFIELD(GETFIELD o){ + Type objectref = stack().peek(); + if (! ( (objectref instanceof ObjectType) || (objectref == Type.NULL) ) ){ + constraintViolated(o, "Stack top should be an object reference that's not an array reference, but is '"+objectref+"'."); + } + + String field_name = o.getFieldName(cpg); + Field f = findField(field_name,o.getClassType(cpg).getClassName()); + +// JavaClass jc = Repository.lookupClass(o.getClassType(cpg).getClassName()); +// Field[] fields = jc.getFields(); +// Field f = null; +// for (int i=0; i<fields.length; i++){ +// if (fields[i].getName().equals(field_name)){ +// f = fields[i]; +// break; +// } +// } + if (f == null){ + throw new AssertionViolatedException("Field not found?!?"); + } + + if (f.isProtected()){ + ObjectType classtype = o.getClassType(cpg); + ObjectType curr = new ObjectType(mg.getClassName()); + + if ( classtype.equals(curr) || + curr.subclassOf(classtype) ){ + Type t = stack().peek(); + if (t == Type.NULL){ + return; + } + if (! (t instanceof ObjectType) ){ + constraintViolated(o, "The 'objectref' must refer to an object that's not an array. Found instead: '"+t+"'."); + } + ObjectType objreftype = (ObjectType) t; + if (! ( objreftype.equals(curr) || + objreftype.subclassOf(curr) ) ){ + //TODO: One day move to Staerk-et-al's "Set of object types" instead of "wider" object types + // created during the verification. + // "Wider" object types don't allow us to check for things like that below. + //constraintViolated(o, "The referenced field has the ACC_PROTECTED modifier, and it's a member of the current class or a superclass of the current class. However, the referenced object type '"+stack().peek()+"' is not the current class or a subclass of the current class."); + } + } + } + + // TODO: Could go into Pass 3a. + if (f.isStatic()){ + constraintViolated(o, "Referenced field '"+f+"' is static which it shouldn't be."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitGETSTATIC(GETSTATIC o){ + // Field must be static: see Pass 3a. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitGOTO(GOTO o){ + // nothing to do here. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitGOTO_W(GOTO_W o){ + // nothing to do here. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitI2B(I2B o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitI2C(I2C o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitI2D(I2D o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitI2F(I2F o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitI2L(I2L o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitI2S(I2S o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIADD(IADD o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.INT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'int', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIALOAD(IALOAD o){ + indexOfInt(o, stack().peek()); + if (stack().peek(1) == Type.NULL){ + return; + } + if (! (stack().peek(1) instanceof ArrayType)){ + constraintViolated(o, "Stack next-to-top must be of type int[] but is '"+stack().peek(1)+"'."); + } + Type t = ((ArrayType) (stack().peek(1))).getBasicType(); + if (t != Type.INT){ + constraintViolated(o, "Stack next-to-top must be of type int[] but is '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIAND(IAND o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.INT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'int', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIASTORE(IASTORE o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + indexOfInt(o, stack().peek(1)); + if (stack().peek(2) == Type.NULL){ + return; + } + if (! (stack().peek(2) instanceof ArrayType)){ + constraintViolated(o, "Stack next-to-next-to-top must be of type int[] but is '"+stack().peek(2)+"'."); + } + Type t = ((ArrayType) (stack().peek(2))).getBasicType(); + if (t != Type.INT){ + constraintViolated(o, "Stack next-to-next-to-top must be of type int[] but is '"+stack().peek(2)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitICONST(ICONST o){ + //nothing to do here. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIDIV(IDIV o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.INT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'int', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIF_ACMPEQ(IF_ACMPEQ o){ + if (!(stack().peek() instanceof ReferenceType)){ + constraintViolated(o, "The value at the stack top is not of a ReferenceType, but of type '"+stack().peek()+"'."); + } + referenceTypeIsInitialized(o, (ReferenceType) (stack().peek()) ); + + if (!(stack().peek(1) instanceof ReferenceType)){ + constraintViolated(o, "The value at the stack next-to-top is not of a ReferenceType, but of type '"+stack().peek(1)+"'."); + } + referenceTypeIsInitialized(o, (ReferenceType) (stack().peek(1)) ); + + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIF_ACMPNE(IF_ACMPNE o){ + if (!(stack().peek() instanceof ReferenceType)){ + constraintViolated(o, "The value at the stack top is not of a ReferenceType, but of type '"+stack().peek()+"'."); + referenceTypeIsInitialized(o, (ReferenceType) (stack().peek()) ); + } + if (!(stack().peek(1) instanceof ReferenceType)){ + constraintViolated(o, "The value at the stack next-to-top is not of a ReferenceType, but of type '"+stack().peek(1)+"'."); + referenceTypeIsInitialized(o, (ReferenceType) (stack().peek(1)) ); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIF_ICMPEQ(IF_ICMPEQ o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.INT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'int', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIF_ICMPGE(IF_ICMPGE o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.INT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'int', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIF_ICMPGT(IF_ICMPGT o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.INT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'int', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIF_ICMPLE(IF_ICMPLE o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.INT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'int', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIF_ICMPLT(IF_ICMPLT o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.INT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'int', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIF_ICMPNE(IF_ICMPNE o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.INT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'int', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIFEQ(IFEQ o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIFGE(IFGE o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIFGT(IFGT o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIFLE(IFLE o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIFLT(IFLT o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIFNE(IFNE o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIFNONNULL(IFNONNULL o){ + if (!(stack().peek() instanceof ReferenceType)){ + constraintViolated(o, "The value at the stack top is not of a ReferenceType, but of type '"+stack().peek()+"'."); + } + referenceTypeIsInitialized(o, (ReferenceType) (stack().peek()) ); + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIFNULL(IFNULL o){ + if (!(stack().peek() instanceof ReferenceType)){ + constraintViolated(o, "The value at the stack top is not of a ReferenceType, but of type '"+stack().peek()+"'."); + } + referenceTypeIsInitialized(o, (ReferenceType) (stack().peek()) ); + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIINC(IINC o){ + // Mhhh. In BCEL, at this time "IINC" is not a LocalVariableInstruction. + if (locals().maxLocals() <= (o.getType(cpg).getSize()==1? o.getIndex() : o.getIndex()+1) ){ + constraintViolated(o, "The 'index' is not a valid index into the local variable array."); + } + + indexOfInt(o, locals().get(o.getIndex())); + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitILOAD(ILOAD o){ + // All done by visitLocalVariableInstruction(), visitLoadInstruction() + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIMPDEP1(IMPDEP1 o){ + throw new AssertionViolatedException("In this JustIce verification pass there should not occur an illegal instruction such as IMPDEP1."); + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIMPDEP2(IMPDEP2 o){ + throw new AssertionViolatedException("In this JustIce verification pass there should not occur an illegal instruction such as IMPDEP2."); + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIMUL(IMUL o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.INT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'int', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitINEG(INEG o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitINSTANCEOF(INSTANCEOF o){ + // The objectref must be of type reference. + Type objectref = stack().peek(0); + if (!(objectref instanceof ReferenceType)){ + constraintViolated(o, "The 'objectref' is not of a ReferenceType but of type "+objectref+"."); + } + else{ + referenceTypeIsInitialized(o, (ReferenceType) objectref); + } + // The unsigned indexbyte1 and indexbyte2 are used to construct an index into the runtime constant pool of the + // current class (§3.6), where the value of the index is (indexbyte1 << 8) | indexbyte2. The runtime constant + // pool item at the index must be a symbolic reference to a class, array, or interface type. + Constant c = cpg.getConstant(o.getIndex()); + if (! (c instanceof ConstantClass)){ + constraintViolated(o, "The Constant at 'index' is not a ConstantClass, but '"+c+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitINVOKEINTERFACE(INVOKEINTERFACE o){ + // Method is not native, otherwise pass 3 would not happen. + + int count = o.getCount(); + if (count == 0){ + constraintViolated(o, "The 'count' argument must not be 0."); + } + // It is a ConstantInterfaceMethodref, Pass 3a made it sure. + // TODO: Do we want to do anything with it? + //ConstantInterfaceMethodref cimr = (ConstantInterfaceMethodref) (cpg.getConstant(o.getIndex())); + + // the o.getClassType(cpg) type has passed pass 2; see visitLoadClass(o). + + Type t = o.getType(cpg); + if (t instanceof ObjectType){ + String name = ((ObjectType)t).getClassName(); + 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+"'."); + } + } + + + Type[] argtypes = o.getArgumentTypes(cpg); + int nargs = argtypes.length; + + for (int i=nargs-1; i>=0; i--){ + Type fromStack = stack().peek( (nargs-1) - i ); // 0 to nargs-1 + Type fromDesc = argtypes[i]; + if (fromDesc == Type.BOOLEAN || + fromDesc == Type.BYTE || + fromDesc == Type.CHAR || + fromDesc == Type.SHORT){ + fromDesc = Type.INT; + } + if (! fromStack.equals(fromDesc)){ + if (fromStack instanceof ReferenceType && fromDesc instanceof ReferenceType){ + //ReferenceType rFromStack = (ReferenceType) fromStack; + //ReferenceType rFromDesc = (ReferenceType) fromDesc; + // TODO: This can only be checked when using Staerk-et-al's "set of object types" + // instead of a "wider cast object type" created during verification. + //if ( ! rFromStack.isAssignmentCompatibleWith(rFromDesc) ){ + // constraintViolated(o, "Expecting a '"+fromDesc+"' but found a '"+fromStack+"' on the stack (which is not assignment compatible)."); + //} + } + else{ + constraintViolated(o, "Expecting a '"+fromDesc+"' but found a '"+fromStack+"' on the stack."); + } + } + } + + Type objref = stack().peek(nargs); + if (objref == Type.NULL){ + return; + } + if (! (objref instanceof ReferenceType) ){ + constraintViolated(o, "Expecting a reference type as 'objectref' on the stack, not a '"+objref+"'."); + } + referenceTypeIsInitialized(o, (ReferenceType) objref); + if (!(objref instanceof ObjectType)){ + if (!(objref instanceof ArrayType)){ + constraintViolated(o, "Expecting an ObjectType as 'objectref' on the stack, not a '"+objref+"'."); // could be a ReturnaddressType + } + else{ + objref = GENERIC_ARRAY; + } + } + + // String objref_classname = ((ObjectType) objref).getClassName(); + // String theInterface = o.getClassName(cpg); + // TODO: This can only be checked if we're using Staerk-et-al's "set of object types" + // instead of "wider cast object types" generated during verification. + //if ( ! Repository.implementationOf(objref_classname, theInterface) ){ + // constraintViolated(o, "The 'objref' item '"+objref+"' does not implement '"+theInterface+"' as expected."); + //} + + int counted_count = 1; // 1 for the objectref + for (int i=0; i<nargs; i++){ + counted_count += argtypes[i].getSize(); + } + if (count != counted_count){ + constraintViolated(o, "The 'count' argument should probably read '"+counted_count+"' but is '"+count+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitINVOKESPECIAL(INVOKESPECIAL o){ + // Don't init an object twice. + if ( (o.getMethodName(cpg).equals(Constants.CONSTRUCTOR_NAME)) && (!(stack().peek(o.getArgumentTypes(cpg).length) instanceof UninitializedObjectType)) ){ + constraintViolated(o, "Possibly initializing object twice. A valid instruction sequence must not have an uninitialized object on the operand stack or in a local variable during a backwards branch, or in a local variable in code protected by an exception handler. Please see The Java Virtual Machine Specification, Second Edition, 4.9.4 (pages 147 and 148) for details."); + } + + // the o.getClassType(cpg) type has passed pass 2; see visitLoadClass(o). + + Type t = o.getType(cpg); + if (t instanceof ObjectType){ + String name = ((ObjectType)t).getClassName(); + 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+"'."); + } + } + + + Type[] argtypes = o.getArgumentTypes(cpg); + int nargs = argtypes.length; + + for (int i=nargs-1; i>=0; i--){ + Type fromStack = stack().peek( (nargs-1) - i ); // 0 to nargs-1 + Type fromDesc = argtypes[i]; + if (fromDesc == Type.BOOLEAN || + fromDesc == Type.BYTE || + fromDesc == Type.CHAR || + fromDesc == Type.SHORT){ + fromDesc = Type.INT; + } + if (! fromStack.equals(fromDesc)){ + if (fromStack instanceof ReferenceType && fromDesc instanceof ReferenceType){ + ReferenceType rFromStack = (ReferenceType) fromStack; + ReferenceType rFromDesc = (ReferenceType) fromDesc; + // TODO: This can only be checked using Staerk-et-al's "set of object types", not + // using a "wider cast object type". + if ( ! rFromStack.isAssignmentCompatibleWith(rFromDesc) ){ + constraintViolated(o, "Expecting a '"+fromDesc+"' but found a '"+fromStack+"' on the stack (which is not assignment compatible)."); + } + } + else{ + constraintViolated(o, "Expecting a '"+fromDesc+"' but found a '"+fromStack+"' on the stack."); + } + } + } + + Type objref = stack().peek(nargs); + if (objref == Type.NULL){ + return; + } + if (! (objref instanceof ReferenceType) ){ + constraintViolated(o, "Expecting a reference type as 'objectref' on the stack, not a '"+objref+"'."); + } + String objref_classname = null; + if ( !(o.getMethodName(cpg).equals(Constants.CONSTRUCTOR_NAME))){ + referenceTypeIsInitialized(o, (ReferenceType) objref); + if (!(objref instanceof ObjectType)){ + if (!(objref instanceof ArrayType)){ + constraintViolated(o, "Expecting an ObjectType as 'objectref' on the stack, not a '"+objref+"'."); // could be a ReturnaddressType + } + else{ + objref = GENERIC_ARRAY; + } + } + + objref_classname = ((ObjectType) objref).getClassName(); + } + else{ + if (!(objref instanceof UninitializedObjectType)){ + constraintViolated(o, "Expecting an UninitializedObjectType as 'objectref' on the stack, not a '"+objref+"'. Otherwise, you couldn't invoke a method since an array has no methods (not to speak of a return address)."); + } + objref_classname = ((UninitializedObjectType) objref).getInitialized().getClassName(); + } + + + String theClass = o.getClassName(cpg); + if ( ! Repository.instanceOf(objref_classname, theClass) ){ + constraintViolated(o, "The 'objref' item '"+objref+"' does not implement '"+theClass+"' as expected."); + } + + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitINVOKESTATIC(INVOKESTATIC o){ + // Method is not native, otherwise pass 3 would not happen. + + Type t = o.getType(cpg); + if (t instanceof ObjectType){ + String name = ((ObjectType)t).getClassName(); + 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+"'."); + } + } + + Type[] argtypes = o.getArgumentTypes(cpg); + int nargs = argtypes.length; + + for (int i=nargs-1; i>=0; i--){ + Type fromStack = stack().peek( (nargs-1) - i ); // 0 to nargs-1 + Type fromDesc = argtypes[i]; + if (fromDesc == Type.BOOLEAN || + fromDesc == Type.BYTE || + fromDesc == Type.CHAR || + fromDesc == Type.SHORT){ + fromDesc = Type.INT; + } + if (! fromStack.equals(fromDesc)){ + if (fromStack instanceof ReferenceType && fromDesc instanceof ReferenceType){ + ReferenceType rFromStack = (ReferenceType) fromStack; + ReferenceType rFromDesc = (ReferenceType) fromDesc; + // TODO: This check can possibly only be done using Staerk-et-al's "set of object types" + // instead of a "wider cast object type" created during verification. + if ( ! rFromStack.isAssignmentCompatibleWith(rFromDesc) ){ + constraintViolated(o, "Expecting a '"+fromDesc+"' but found a '"+fromStack+"' on the stack (which is not assignment compatible)."); + } + } + else{ + constraintViolated(o, "Expecting a '"+fromDesc+"' but found a '"+fromStack+"' on the stack."); + } + } + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitINVOKEVIRTUAL(INVOKEVIRTUAL o){ + // the o.getClassType(cpg) type has passed pass 2; see visitLoadClass(o). + + Type t = o.getType(cpg); + if (t instanceof ObjectType){ + String name = ((ObjectType)t).getClassName(); + 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+"'."); + } + } + + + Type[] argtypes = o.getArgumentTypes(cpg); + int nargs = argtypes.length; + + for (int i=nargs-1; i>=0; i--){ + Type fromStack = stack().peek( (nargs-1) - i ); // 0 to nargs-1 + Type fromDesc = argtypes[i]; + if (fromDesc == Type.BOOLEAN || + fromDesc == Type.BYTE || + fromDesc == Type.CHAR || + fromDesc == Type.SHORT){ + fromDesc = Type.INT; + } + if (! fromStack.equals(fromDesc)){ + if (fromStack instanceof ReferenceType && fromDesc instanceof ReferenceType){ + ReferenceType rFromStack = (ReferenceType) fromStack; + ReferenceType rFromDesc = (ReferenceType) fromDesc; + // TODO: This can possibly only be checked when using Staerk-et-al's "set of object types" instead + // of a single "wider cast object type" created during verification. + if ( ! rFromStack.isAssignmentCompatibleWith(rFromDesc) ){ + constraintViolated(o, "Expecting a '"+fromDesc+"' but found a '"+fromStack+"' on the stack (which is not assignment compatible)."); + } + } + else{ + constraintViolated(o, "Expecting a '"+fromDesc+"' but found a '"+fromStack+"' on the stack."); + } + } + } + + Type objref = stack().peek(nargs); + if (objref == Type.NULL){ + return; + } + if (! (objref instanceof ReferenceType) ){ + constraintViolated(o, "Expecting a reference type as 'objectref' on the stack, not a '"+objref+"'."); + } + referenceTypeIsInitialized(o, (ReferenceType) objref); + if (!(objref instanceof ObjectType)){ + if (!(objref instanceof ArrayType)){ + constraintViolated(o, "Expecting an ObjectType as 'objectref' on the stack, not a '"+objref+"'."); // could be a ReturnaddressType + } + else{ + objref = GENERIC_ARRAY; + } + } + + String objref_classname = ((ObjectType) objref).getClassName(); + + String theClass = o.getClassName(cpg); + + if ( ! Repository.instanceOf(objref_classname, theClass) ){ + constraintViolated(o, "The 'objref' item '"+objref+"' does not implement '"+theClass+"' as expected."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIOR(IOR o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.INT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'int', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIREM(IREM o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.INT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'int', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIRETURN(IRETURN o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitISHL(ISHL o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.INT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'int', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitISHR(ISHR o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.INT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'int', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitISTORE(ISTORE o){ + //visitStoreInstruction(StoreInstruction) is called before. + + // Nothing else needs to be done here. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitISUB(ISUB o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.INT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'int', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIUSHR(IUSHR o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.INT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'int', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitIXOR(IXOR o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.INT){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'int', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitJSR(JSR o){ + // nothing to do here. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitJSR_W(JSR_W o){ + // nothing to do here. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitL2D(L2D o){ + if (stack().peek() != Type.LONG){ + constraintViolated(o, "The value at the stack top is not of type 'long', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitL2F(L2F o){ + if (stack().peek() != Type.LONG){ + constraintViolated(o, "The value at the stack top is not of type 'long', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitL2I(L2I o){ + if (stack().peek() != Type.LONG){ + constraintViolated(o, "The value at the stack top is not of type 'long', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitLADD(LADD o){ + if (stack().peek() != Type.LONG){ + constraintViolated(o, "The value at the stack top is not of type 'long', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.LONG){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'long', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitLALOAD(LALOAD o){ + indexOfInt(o, stack().peek()); + if (stack().peek(1) == Type.NULL){ + return; + } + if (! (stack().peek(1) instanceof ArrayType)){ + constraintViolated(o, "Stack next-to-top must be of type long[] but is '"+stack().peek(1)+"'."); + } + Type t = ((ArrayType) (stack().peek(1))).getBasicType(); + if (t != Type.LONG){ + constraintViolated(o, "Stack next-to-top must be of type long[] but is '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitLAND(LAND o){ + if (stack().peek() != Type.LONG){ + constraintViolated(o, "The value at the stack top is not of type 'long', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.LONG){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'long', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitLASTORE(LASTORE o){ + if (stack().peek() != Type.LONG){ + constraintViolated(o, "The value at the stack top is not of type 'long', but of type '"+stack().peek()+"'."); + } + indexOfInt(o, stack().peek(1)); + if (stack().peek(2) == Type.NULL){ + return; + } + if (! (stack().peek(2) instanceof ArrayType)){ + constraintViolated(o, "Stack next-to-next-to-top must be of type long[] but is '"+stack().peek(2)+"'."); + } + Type t = ((ArrayType) (stack().peek(2))).getBasicType(); + if (t != Type.LONG){ + constraintViolated(o, "Stack next-to-next-to-top must be of type long[] but is '"+stack().peek(2)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitLCMP(LCMP o){ + if (stack().peek() != Type.LONG){ + constraintViolated(o, "The value at the stack top is not of type 'long', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.LONG){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'long', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitLCONST(LCONST o){ + // Nothing to do here. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitLDC(LDC o){ + // visitCPInstruction is called first. + + Constant c = cpg.getConstant(o.getIndex()); + if (! ( ( c instanceof ConstantInteger) || + ( c instanceof ConstantFloat ) || + ( c instanceof ConstantString ) ) ){ + constraintViolated(o, "Referenced constant should be a CONSTANT_Integer, a CONSTANT_Float or a CONSTANT_String, but is '"+c+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitLDC_W(LDC_W o){ + // visitCPInstruction is called first. + + Constant c = cpg.getConstant(o.getIndex()); + if (! ( ( c instanceof ConstantInteger) || + ( c instanceof ConstantFloat ) || + ( c instanceof ConstantString ) ) ){ + constraintViolated(o, "Referenced constant should be a CONSTANT_Integer, a CONSTANT_Float or a CONSTANT_String, but is '"+c+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitLDC2_W(LDC2_W o){ + // visitCPInstruction is called first. + + Constant c = cpg.getConstant(o.getIndex()); + if (! ( ( c instanceof ConstantLong) || + ( c instanceof ConstantDouble ) ) ){ + constraintViolated(o, "Referenced constant should be a CONSTANT_Integer, a CONSTANT_Float or a CONSTANT_String, but is '"+c+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitLDIV(LDIV o){ + if (stack().peek() != Type.LONG){ + constraintViolated(o, "The value at the stack top is not of type 'long', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.LONG){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'long', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitLLOAD(LLOAD o){ + //visitLoadInstruction(LoadInstruction) is called before. + + // Nothing else needs to be done here. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitLMUL(LMUL o){ + if (stack().peek() != Type.LONG){ + constraintViolated(o, "The value at the stack top is not of type 'long', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.LONG){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'long', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitLNEG(LNEG o){ + if (stack().peek() != Type.LONG){ + constraintViolated(o, "The value at the stack top is not of type 'long', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitLOOKUPSWITCH(LOOKUPSWITCH o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + // See also pass 3a. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitLOR(LOR o){ + if (stack().peek() != Type.LONG){ + constraintViolated(o, "The value at the stack top is not of type 'long', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.LONG){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'long', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitLREM(LREM o){ + if (stack().peek() != Type.LONG){ + constraintViolated(o, "The value at the stack top is not of type 'long', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.LONG){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'long', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitLRETURN(LRETURN o){ + if (stack().peek() != Type.LONG){ + constraintViolated(o, "The value at the stack top is not of type 'long', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitLSHL(LSHL o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.LONG){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'long', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitLSHR(LSHR o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.LONG){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'long', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitLSTORE(LSTORE o){ + //visitStoreInstruction(StoreInstruction) is called before. + + // Nothing else needs to be done here. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitLSUB(LSUB o){ + if (stack().peek() != Type.LONG){ + constraintViolated(o, "The value at the stack top is not of type 'long', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.LONG){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'long', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitLUSHR(LUSHR o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.LONG){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'long', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitLXOR(LXOR o){ + if (stack().peek() != Type.LONG){ + constraintViolated(o, "The value at the stack top is not of type 'long', but of type '"+stack().peek()+"'."); + } + if (stack().peek(1) != Type.LONG){ + constraintViolated(o, "The value at the stack next-to-top is not of type 'long', but of type '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitMONITORENTER(MONITORENTER o){ + if (! ((stack().peek()) instanceof ReferenceType)){ + constraintViolated(o, "The stack top should be of a ReferenceType, but is '"+stack().peek()+"'."); + } + referenceTypeIsInitialized(o, (ReferenceType) (stack().peek()) ); + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitMONITOREXIT(MONITOREXIT o){ + if (! ((stack().peek()) instanceof ReferenceType)){ + constraintViolated(o, "The stack top should be of a ReferenceType, but is '"+stack().peek()+"'."); + } + referenceTypeIsInitialized(o, (ReferenceType) (stack().peek()) ); + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitMULTIANEWARRAY(MULTIANEWARRAY o){ + int dimensions = o.getDimensions(); + // Dimensions argument is okay: see Pass 3a. + for (int i=0; i<dimensions; i++){ + if (stack().peek(i) != Type.INT){ + constraintViolated(o, "The '"+dimensions+"' upper stack types should be 'int' but aren't."); + } + } + // The runtime constant pool item at that index must be a symbolic reference to a class, + // array, or interface type. See Pass 3a. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitNEW(NEW o){ + //visitCPInstruction(CPInstruction) has been called before. + //visitLoadClass(LoadClass) has been called before. + + Type t = o.getType(cpg); + if (! (t instanceof ReferenceType)){ + throw new AssertionViolatedException("NEW.getType() returning a non-reference type?!"); + } + if (! (t instanceof ObjectType)){ + constraintViolated(o, "Expecting a class type (ObjectType) to work on. Found: '"+t+"'."); + } + ObjectType obj = (ObjectType) t; + + //e.g.: Don't instantiate interfaces + if (! obj.referencesClass()){ + constraintViolated(o, "Expecting a class type (ObjectType) to work on. Found: '"+obj+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitNEWARRAY(NEWARRAY o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitNOP(NOP o){ + // nothing is to be done here. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitPOP(POP o){ + if (stack().peek().getSize() != 1){ + constraintViolated(o, "Stack top size should be 1 but stack top is '"+stack().peek()+"' of size '"+stack().peek().getSize()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitPOP2(POP2 o){ + if (stack().peek().getSize() != 2){ + constraintViolated(o, "Stack top size should be 2 but stack top is '"+stack().peek()+"' of size '"+stack().peek().getSize()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitPUTFIELD(PUTFIELD o){ + + Type objectref = stack().peek(1); + if (! ( (objectref instanceof ObjectType) || (objectref == Type.NULL) ) ){ + constraintViolated(o, "Stack next-to-top should be an object reference that's not an array reference, but is '"+objectref+"'."); + } + + String field_name = o.getFieldName(cpg); + Field f = findField(field_name, o.getClassType(cpg).getClassName()); + +// JavaClass jc = Repository.lookupClass(o.getClassType(cpg).getClassName()); +// Field[] fields = jc.getFields(); +// Field f = null; +// for (int i=0; i<fields.length; i++){ +// if (fields[i].getName().equals(field_name)){ +// f = fields[i]; +// break; +// } +// } + if (f == null){ + throw new AssertionViolatedException("Field not found?!?"); + } + + Type value = stack().peek(); + Type t = Type.getType(f.getSignature()); + Type shouldbe = t; + if (shouldbe == Type.BOOLEAN || + shouldbe == Type.BYTE || + shouldbe == Type.CHAR || + shouldbe == Type.SHORT){ + shouldbe = Type.INT; + } + if (t instanceof ReferenceType){ + ReferenceType rvalue = null; + if (value instanceof ReferenceType){ + rvalue = (ReferenceType) value; + referenceTypeIsInitialized(o, rvalue); + } + else{ + constraintViolated(o, "The stack top type '"+value+"' is not of a reference type as expected."); + } + // TODO: This can possibly only be checked using Staerk-et-al's "set-of-object types", not + // using "wider cast object types" created during verification. + // Comment it out if you encounter problems. See also the analogon at visitPUTSTATIC. + if (!(rvalue.isAssignmentCompatibleWith(shouldbe))){ + constraintViolated(o, "The stack top type '"+value+"' is not assignment compatible with '"+shouldbe+"'."); + } + } + else{ + if (shouldbe != value){ + constraintViolated(o, "The stack top type '"+value+"' is not of type '"+shouldbe+"' as expected."); + } + } + + if (f.isProtected()){ + ObjectType classtype = o.getClassType(cpg); + ObjectType curr = new ObjectType(mg.getClassName()); + + if ( classtype.equals(curr) || + curr.subclassOf(classtype) ){ + Type tp = stack().peek(1); + if (tp == Type.NULL){ + return; + } + if (! (tp instanceof ObjectType) ){ + constraintViolated(o, "The 'objectref' must refer to an object that's not an array. Found instead: '"+tp+"'."); + } + ObjectType objreftype = (ObjectType) tp; + if (! ( objreftype.equals(curr) || + objreftype.subclassOf(curr) ) ){ + constraintViolated(o, "The referenced field has the ACC_PROTECTED modifier, and it's a member of the current class or a superclass of the current class. However, the referenced object type '"+stack().peek()+"' is not the current class or a subclass of the current class."); + } + } + } + + // TODO: Could go into Pass 3a. + if (f.isStatic()){ + constraintViolated(o, "Referenced field '"+f+"' is static which it shouldn't be."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitPUTSTATIC(PUTSTATIC o){ + String field_name = o.getFieldName(cpg); + JavaClass jc = Repository.lookupClass(o.getClassType(cpg).getClassName()); + Field[] fields = jc.getFields(); + Field f = null; + for (int i=0; i<fields.length; i++){ + if (fields[i].getName().equals(field_name)){ + f = fields[i]; + break; + } + } + if (f == null){ + throw new AssertionViolatedException("Field not found?!?"); + } + Type value = stack().peek(); + Type t = Type.getType(f.getSignature()); + Type shouldbe = t; + if (shouldbe == Type.BOOLEAN || + shouldbe == Type.BYTE || + shouldbe == Type.CHAR || + shouldbe == Type.SHORT){ + shouldbe = Type.INT; + } + if (t instanceof ReferenceType){ + ReferenceType rvalue = null; + if (value instanceof ReferenceType){ + rvalue = (ReferenceType) value; + referenceTypeIsInitialized(o, rvalue); + } + else{ + constraintViolated(o, "The stack top type '"+value+"' is not of a reference type as expected."); + } + // TODO: This can possibly only be checked using Staerk-et-al's "set-of-object types", not + // using "wider cast object types" created during verification. + // Comment it out if you encounter problems. See also the analogon at visitPUTFIELD. + if (!(rvalue.isAssignmentCompatibleWith(shouldbe))){ + constraintViolated(o, "The stack top type '"+value+"' is not assignment compatible with '"+shouldbe+"'."); + } + } + else{ + if (shouldbe != value){ + constraintViolated(o, "The stack top type '"+value+"' is not of type '"+shouldbe+"' as expected."); + } + } + // TODO: Interface fields may be assigned to only once. (Hard to implement in + // JustIce's execution model). This may only happen in <clinit>, see Pass 3a. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitRET(RET o){ + if (! (locals().get(o.getIndex()) instanceof ReturnaddressType)){ + constraintViolated(o, "Expecting a ReturnaddressType in local variable "+o.getIndex()+"."); + } + if (locals().get(o.getIndex()) == ReturnaddressType.NO_TARGET){ + throw new AssertionViolatedException("Oops: RET expecting a target!"); + } + // Other constraints such as non-allowed overlapping subroutines are enforced + // while building the Subroutines data structure. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitRETURN(RETURN o){ + if (mg.getName().equals(Constants.CONSTRUCTOR_NAME)){// If we leave an <init> method + if ((Frame._this != null) && (!(mg.getClassName().equals(Type.OBJECT.getClassName()))) ) { + constraintViolated(o, "Leaving a constructor that itself did not call a constructor."); + } + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitSALOAD(SALOAD o){ + indexOfInt(o, stack().peek()); + if (stack().peek(1) == Type.NULL){ + return; + } + if (! (stack().peek(1) instanceof ArrayType)){ + constraintViolated(o, "Stack next-to-top must be of type short[] but is '"+stack().peek(1)+"'."); + } + Type t = ((ArrayType) (stack().peek(1))).getBasicType(); + if (t != Type.SHORT){ + constraintViolated(o, "Stack next-to-top must be of type short[] but is '"+stack().peek(1)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitSASTORE(SASTORE o){ + if (stack().peek() != Type.INT){ + constraintViolated(o, "The value at the stack top is not of type 'int', but of type '"+stack().peek()+"'."); + } + indexOfInt(o, stack().peek(1)); + if (stack().peek(2) == Type.NULL){ + return; + } + if (! (stack().peek(2) instanceof ArrayType)){ + constraintViolated(o, "Stack next-to-next-to-top must be of type short[] but is '"+stack().peek(2)+"'."); + } + Type t = ((ArrayType) (stack().peek(2))).getBasicType(); + if (t != Type.SHORT){ + constraintViolated(o, "Stack next-to-next-to-top must be of type short[] but is '"+stack().peek(2)+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitSIPUSH(SIPUSH o){ + // nothing to do here. Generic visitXXX() methods did the trick before. + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitSWAP(SWAP o){ + if (stack().peek().getSize() != 1){ + constraintViolated(o, "The value at the stack top is not of size '1', but of size '"+stack().peek().getSize()+"'."); + } + if (stack().peek(1).getSize() != 1){ + constraintViolated(o, "The value at the stack next-to-top is not of size '1', but of size '"+stack().peek(1).getSize()+"'."); + } + } + + /** + * Ensures the specific preconditions of the said instruction. + */ + public void visitTABLESWITCH(TABLESWITCH o){ + indexOfInt(o, stack().peek()); + // See Pass 3a. + } + +} + diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/InstructionContext.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/InstructionContext.java new file mode 100644 index 000000000..dea064ceb --- /dev/null +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/InstructionContext.java @@ -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(); +} diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/InstructionContextImpl.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/InstructionContextImpl.java new file mode 100644 index 000000000..c4d8f8124 --- /dev/null +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/InstructionContextImpl.java @@ -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; + } + +}
\ No newline at end of file diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/InstructionContextQueue.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/InstructionContextQueue.java new file mode 100644 index 000000000..3c8e133d3 --- /dev/null +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/InstructionContextQueue.java @@ -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); } +}
\ No newline at end of file diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/LocalVariables.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/LocalVariables.java new file mode 100644 index 000000000..cd6e07ad7 --- /dev/null +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/LocalVariables.java @@ -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; +// } + + +} diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/OperandStack.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/OperandStack.java new file mode 100644 index 000000000..8cf9e8772 --- /dev/null +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/OperandStack.java @@ -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()); + } + } + } + +} diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/StackMapHelper.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/StackMapHelper.java new file mode 100644 index 000000000..a9275fe1b --- /dev/null +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/StackMapHelper.java @@ -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; + } +} diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/Subroutine.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/Subroutine.java new file mode 100644 index 000000000..211b06b3c --- /dev/null +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/Subroutine.java @@ -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(); +} diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/SubroutineImpl.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/SubroutineImpl.java new file mode 100644 index 000000000..8412ddd50 --- /dev/null +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/SubroutineImpl.java @@ -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; + } + } + +}
\ No newline at end of file diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/Subroutines.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/Subroutines.java new file mode 100644 index 000000000..d40a1387c --- /dev/null +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/Subroutines.java @@ -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(); + } +} diff --git a/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/UninitializedObjectType.java b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/UninitializedObjectType.java new file mode 100644 index 000000000..3b8814555 --- /dev/null +++ b/bcel-builder/src/org/aspectj/apache/bcel/verifier/utility/UninitializedObjectType.java @@ -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); + } +} diff --git a/bcel-builder/testdata/stackmap/Code.class b/bcel-builder/testdata/stackmap/Code.class Binary files differnew file mode 100644 index 000000000..81f7e8cc1 --- /dev/null +++ b/bcel-builder/testdata/stackmap/Code.class diff --git a/bcel-builder/testdata/stackmap/Code.java b/bcel-builder/testdata/stackmap/Code.java new file mode 100644 index 000000000..53a9202bb --- /dev/null +++ b/bcel-builder/testdata/stackmap/Code.java @@ -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"); + } + + } +} diff --git a/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/preverifier/PreverifierBaseTestCase.java b/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/preverifier/PreverifierBaseTestCase.java new file mode 100644 index 000000000..746383342 --- /dev/null +++ b/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/preverifier/PreverifierBaseTestCase.java @@ -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); + } + + + +} diff --git a/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/preverifier/PreverifierTest.java b/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/preverifier/PreverifierTest.java new file mode 100644 index 000000000..730d3a8a9 --- /dev/null +++ b/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/preverifier/PreverifierTest.java @@ -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); + } + } + +} |