From 46e7dbf647b3ae5653e242218f7723f0b0dd5e89 Mon Sep 17 00:00:00 2001 From: Patrick Linskey Date: Wed, 28 Jun 2006 19:46:13 +0000 Subject: [PATCH] added some openjpa-lib test, pom files, and serp fork. We may move the changes in serp back into the serp source tree and add a maven layout to serp, but for now, let's just get things rolling here. git-svn-id: https://svn.apache.org/repos/asf/incubator/openjpa/trunk@417860 13f79535-47bb-0310-9956-ffa450edef68 --- .../lib/conf/NoneConfigurationProvider.java | 81 - openjpa-lib/pom.xml | 113 + .../apache/openjpa/lib/ant/AbstractTask.java | 0 .../org/apache/openjpa/lib/ant/package.html | 0 .../apache/openjpa/lib/conf/BooleanValue.java | 0 .../apache/openjpa/lib/conf/Configurable.java | 0 .../openjpa/lib/conf/Configuration.java | 0 .../openjpa/lib/conf/ConfigurationImpl.java | 0 .../lib/conf/ConfigurationProvider.java | 0 .../openjpa/lib/conf/Configurations.java | 0 .../apache/openjpa/lib/conf/DoubleValue.java | 0 .../apache/openjpa/lib/conf/FileValue.java | 0 .../openjpa/lib/conf/GenericConfigurable.java | 0 .../org/apache/openjpa/lib/conf/IntValue.java | 0 .../lib/conf/MapConfigurationProvider.java | 0 .../apache/openjpa/lib/conf/ObjectValue.java | 0 .../openjpa/lib/conf/PluginListValue.java | 0 .../apache/openjpa/lib/conf/PluginValue.java | 0 .../openjpa/lib/conf/StringListValue.java | 0 .../apache/openjpa/lib/conf/StringValue.java | 0 .../org/apache/openjpa/lib/conf/Value.java | 0 .../openjpa/lib/conf/ValueListener.java | 0 .../org/apache/openjpa/lib/conf/package.html | 0 .../lib/jdbc/AbstractJDBCListener.java | 0 .../jdbc/ConfiguringConnectionDecorator.java | 0 .../openjpa/lib/jdbc/ConnectionDecorator.java | 0 .../openjpa/lib/jdbc/DataSourceLogs.java | 0 .../lib/jdbc/DecoratingDataSource.java | 0 .../lib/jdbc/DelegatingCallableStatement.java | 0 .../lib/jdbc/DelegatingConnection.java | 0 .../lib/jdbc/DelegatingDataSource.java | 0 .../lib/jdbc/DelegatingDatabaseMetaData.java | 0 .../lib/jdbc/DelegatingPreparedStatement.java | 0 .../openjpa/lib/jdbc/DelegatingResultSet.java | 0 .../openjpa/lib/jdbc/DelegatingStatement.java | 0 .../apache/openjpa/lib/jdbc/JDBCEvent.java | 0 .../jdbc/JDBCEventConnectionDecorator.java | 0 .../apache/openjpa/lib/jdbc/JDBCListener.java | 0 .../lib/jdbc/LoggingConnectionDecorator.java | 0 .../lib/jdbc/ReportingSQLException.java | 0 .../apache/openjpa/lib/jdbc/SQLFormatter.java | 0 .../org/apache/openjpa/lib/jdbc/package.html | 0 .../apache/openjpa/lib/log/AbstractLog.java | 0 .../openjpa/lib/log/CommonsLogFactory.java | 0 .../java/org/apache/openjpa/lib/log/Log.java | 0 .../openjpa/lib/log/Log4JLogFactory.java | 0 .../apache/openjpa/lib/log/LogFactory.java | 0 .../openjpa/lib/log/LogFactoryAdapter.java | 0 .../openjpa/lib/log/LogFactoryImpl.java | 0 .../openjpa/lib/log/LogOutputStream.java | 0 .../openjpa/lib/log/MultiLogFactory.java | 0 .../openjpa/lib/log/NoneLogFactory.java | 0 .../org/apache/openjpa/lib/log/package.html | 0 .../openjpa/lib/meta/CFMetaDataParser.java | 0 .../lib/meta/CFMetaDataSerializer.java | 0 .../meta/ClassAnnotationMetaDataFilter.java | 0 .../openjpa/lib/meta/ClassArgParser.java | 0 .../lib/meta/ClassMetaDataIterator.java | 0 .../lib/meta/ClasspathMetaDataIterator.java | 0 .../lib/meta/FileMetaDataIterator.java | 0 .../openjpa/lib/meta/MetaDataFilter.java | 0 .../openjpa/lib/meta/MetaDataIterator.java | 0 .../lib/meta/MetaDataIteratorChain.java | 0 .../openjpa/lib/meta/MetaDataParser.java | 0 .../openjpa/lib/meta/MetaDataSerializer.java | 0 .../lib/meta/ResourceMetaDataIterator.java | 0 .../openjpa/lib/meta/SourceTracker.java | 0 .../openjpa/lib/meta/SourceTrackers.java | 0 .../lib/meta/SuffixMetaDataFilter.java | 0 .../openjpa/lib/meta/URLMetaDataIterator.java | 0 .../openjpa/lib/meta/XMLMetaDataParser.java | 0 .../lib/meta/XMLMetaDataSerializer.java | 0 .../lib/meta/ZipFileMetaDataIterator.java | 0 .../lib/meta/ZipStreamMetaDataIterator.java | 0 .../org/apache/openjpa/lib/meta/package.html | 0 .../openjpa/lib/rop/AbstractListIterator.java | 0 .../rop/AbstractNonSequentialResultList.java | 0 .../openjpa/lib/rop/AbstractResultList.java | 0 .../lib/rop/AbstractSequentialResultList.java | 0 .../openjpa/lib/rop/EagerResultList.java | 0 .../lib/rop/LazyForwardResultList.java | 0 .../openjpa/lib/rop/ListResultList.java | 0 .../lib/rop/ListResultObjectProvider.java | 0 .../lib/rop/MergedResultObjectProvider.java | 0 .../lib/rop/RandomAccessResultList.java | 0 .../lib/rop/RangeResultObjectProvider.java | 0 .../apache/openjpa/lib/rop/ResultList.java | 0 .../openjpa/lib/rop/ResultListIterator.java | 0 .../openjpa/lib/rop/ResultObjectProvider.java | 0 .../lib/rop/ResultObjectProviderIterator.java | 0 .../openjpa/lib/rop/SimpleResultList.java | 0 .../lib/rop/SoftRandomAccessResultList.java | 0 .../openjpa/lib/rop/WindowResultList.java | 0 .../org/apache/openjpa/lib/rop/package.html | 0 .../lib/util/AbstractEventManager.java | 0 .../openjpa/lib/util/Base16Encoder.java | 0 .../openjpa/lib/util/BytecodeWriter.java | 0 .../apache/openjpa/lib/util/Closeable.java | 0 .../apache/openjpa/lib/util/CodeFormat.java | 0 .../apache/openjpa/lib/util/EventManager.java | 0 .../org/apache/openjpa/lib/util/Files.java | 0 .../lib/util/FormatPreservingProperties.java | 0 .../apache/openjpa/lib/util/JavaVersions.java | 0 .../org/apache/openjpa/lib/util/LRUMap.java | 0 .../apache/openjpa/lib/util/Localizer.java | 0 .../openjpa/lib/util/MultiClassLoader.java | 0 .../org/apache/openjpa/lib/util/Options.java | 0 .../openjpa/lib/util/ParameterTemplate.java | 0 .../openjpa/lib/util/ParseException.java | 0 .../openjpa/lib/util/ReferenceHashMap.java | 0 .../openjpa/lib/util/ReferenceHashSet.java | 0 .../apache/openjpa/lib/util/ReferenceMap.java | 0 .../lib/util/ResourceBundleProvider.java | 0 .../org/apache/openjpa/lib/util/Services.java | 0 .../apache/openjpa/lib/util/SimpleRegex.java | 0 .../util/SimpleResourceBundleProvider.java | 0 .../org/apache/openjpa/lib/util/SizedMap.java | 0 .../util/StreamResourceBundleProvider.java | 0 .../openjpa/lib/util/StringDistance.java | 0 .../lib/util/TemporaryClassLoader.java | 0 .../openjpa/lib/util/TypedProperties.java | 0 .../openjpa/lib/util/UUIDGenerator.java | 0 .../lib/util/ZipResourceBundleProvider.java | 0 .../util/concurrent/AbstractCollection.java | 0 .../AbstractConcurrentEventManager.java | 0 .../lib/util/concurrent/AbstractQueue.java | 0 .../lib/util/concurrent/AbstractSet.java | 0 .../openjpa/lib/util/concurrent/Arrays.java | 0 .../util/concurrent/ConcurrentHashMap.java | 0 .../util/concurrent/ConcurrentHashSet.java | 0 .../concurrent/ConcurrentLinkedQueue.java | 0 .../lib/util/concurrent/ConcurrentMap.java | 0 .../ConcurrentReferenceHashMap.java | 0 .../ConcurrentReferenceHashSet.java | 0 .../openjpa/lib/util/concurrent/CondVar.java | 0 .../lib/util/concurrent/Condition.java | 0 .../util/concurrent/CopyOnWriteArrayList.java | 0 .../util/concurrent/CopyOnWriteArraySet.java | 0 .../lib/util/concurrent/FIFOCondVar.java | 0 .../lib/util/concurrent/FIFOWaitQueue.java | 0 .../openjpa/lib/util/concurrent/Lock.java | 0 .../lib/util/concurrent/NanoTimer.java | 0 .../openjpa/lib/util/concurrent/Queue.java | 0 .../lib/util/concurrent/ReentrantLock.java | 0 .../openjpa/lib/util/concurrent/TimeUnit.java | 0 .../openjpa/lib/util/concurrent/Utils.java | 0 .../lib/util/concurrent/WaitQueue.java | 0 .../openjpa/lib/util/concurrent/package.html | 0 .../org/apache/openjpa/lib/util/package.html | 0 .../apache/openjpa/lib/xml/Commentable.java | 0 .../apache/openjpa/lib/xml/DocTypeReader.java | 0 .../org/apache/openjpa/lib/xml/Location.java | 0 .../lib/xml/ValidatingErrorHandler.java | 0 .../apache/openjpa/lib/xml/XMLFactory.java | 0 .../org/apache/openjpa/lib/xml/XMLWriter.java | 0 .../org/apache/openjpa/lib/xml/package.html | 0 .../openjpa/lib/ant/localizer.properties | 0 .../openjpa/lib/conf/localizer.properties | 0 .../openjpa/lib/jdbc/localizer.properties | 0 .../openjpa/lib/log/localizer.properties | 0 .../openjpa/lib/meta/localizer.properties | 0 .../openjpa/lib/rop/localizer.properties | 0 .../openjpa/lib/util/localizer.properties | 0 .../openjpa/lib/xml/localizer.properties | 0 .../org/apache/openjpa/lib/util/Duration.java | 162 + .../lib/util/TestAbstractEventManager.java | 95 + .../openjpa/lib/util/TestLocalizer.java | 125 + .../lib/util/TestMultiClassLoader.java | 163 + .../apache/openjpa/lib/util/TestOptions.java | 218 ++ .../lib/util/TestParameterTemplate.java | 62 + .../lib/util/TestPropertiesParser.java | 406 +++ .../openjpa/lib/util/TestReferenceSet.java | 135 + .../openjpa/lib/util/TestSimpleRegex.java | 70 + .../lib/util/TestTemporaryClassLoader.java | 41 + .../openjpa/lib/util/TestTypedProperties.java | 110 + .../openjpa/lib/util/TestUUIDGenerator.java | 42 + .../util/concurrent/TestConcurrentMap.java | 155 + .../testlocalizer/LocalizerTestHelper.java | 28 + .../src/test/resources/localizer.properties | 1 + .../lib/conf/test/localizer.properties | 12 + .../util/testlocalizer/localizer.properties | 2 + .../testlocalizer/localizer_de_DE.properties | 2 + pom.xml | 179 ++ serp/pom.xml | 41 + .../java/serp/bytecode/ArrayInstruction.java | 37 + .../serp/bytecode/ArrayLoadInstruction.java | 131 + .../main/java/serp/bytecode/ArrayState.java | 94 + .../serp/bytecode/ArrayStoreInstruction.java | 133 + .../main/java/serp/bytecode/Attribute.java | 148 + .../main/java/serp/bytecode/Attributes.java | 217 ++ serp/src/main/java/serp/bytecode/BCClass.java | 1878 +++++++++++ .../java/serp/bytecode/BCClassLoader.java | 83 + .../src/main/java/serp/bytecode/BCEntity.java | 49 + serp/src/main/java/serp/bytecode/BCField.java | 166 + .../src/main/java/serp/bytecode/BCMember.java | 399 +++ .../src/main/java/serp/bytecode/BCMethod.java | 498 +++ .../bytecode/ClassConstantInstruction.java | 223 ++ .../java/serp/bytecode/ClassInstruction.java | 135 + .../java/serp/bytecode/CmpInstruction.java | 190 ++ serp/src/main/java/serp/bytecode/Code.java | 2847 +++++++++++++++++ .../main/java/serp/bytecode/CodeEntry.java | 27 + .../serp/bytecode/ConstantInstruction.java | 531 +++ .../java/serp/bytecode/ConstantValue.java | 263 ++ .../main/java/serp/bytecode/Constants.java | 324 ++ .../serp/bytecode/ConvertInstruction.java | 440 +++ .../main/java/serp/bytecode/Deprecated.java | 35 + .../java/serp/bytecode/ExceptionHandler.java | 310 ++ .../main/java/serp/bytecode/Exceptions.java | 330 ++ .../java/serp/bytecode/FieldInstruction.java | 499 +++ .../serp/bytecode/GetFieldInstruction.java | 64 + .../java/serp/bytecode/IIncInstruction.java | 97 + .../java/serp/bytecode/IfInstruction.java | 81 + .../main/java/serp/bytecode/InnerClass.java | 466 +++ .../main/java/serp/bytecode/InnerClasses.java | 246 ++ .../main/java/serp/bytecode/Instruction.java | 191 ++ .../java/serp/bytecode/InstructionPtr.java | 48 + .../serp/bytecode/InstructionPtrStrategy.java | 120 + .../java/serp/bytecode/JumpInstruction.java | 186 ++ .../main/java/serp/bytecode/LineNumber.java | 162 + .../java/serp/bytecode/LineNumberTable.java | 227 ++ .../java/serp/bytecode/LoadInstruction.java | 299 ++ serp/src/main/java/serp/bytecode/Local.java | 272 ++ .../main/java/serp/bytecode/LocalTable.java | 238 ++ .../java/serp/bytecode/LocalVariable.java | 95 + .../bytecode/LocalVariableInstruction.java | 177 + .../serp/bytecode/LocalVariableTable.java | 121 + .../java/serp/bytecode/LocalVariableType.java | 43 + .../serp/bytecode/LocalVariableTypeTable.java | 103 + .../bytecode/LookupSwitchInstruction.java | 255 ++ .../java/serp/bytecode/MathInstruction.java | 294 ++ .../java/serp/bytecode/MethodInstruction.java | 777 +++++ .../bytecode/MonitorEnterInstruction.java | 37 + .../serp/bytecode/MonitorExitInstruction.java | 35 + .../serp/bytecode/MonitorInstruction.java | 36 + .../bytecode/MultiANewArrayInstruction.java | 110 + .../main/java/serp/bytecode/NameCache.java | 296 ++ .../serp/bytecode/NewArrayInstruction.java | 173 + .../main/java/serp/bytecode/ObjectState.java | 143 + .../java/serp/bytecode/PrimitiveState.java | 96 + serp/src/main/java/serp/bytecode/Project.java | 451 +++ .../serp/bytecode/PutFieldInstruction.java | 68 + .../java/serp/bytecode/RetInstruction.java | 64 + .../java/serp/bytecode/ReturnInstruction.java | 128 + .../main/java/serp/bytecode/SourceFile.java | 135 + .../java/serp/bytecode/StackInstruction.java | 177 + serp/src/main/java/serp/bytecode/State.java | 208 ++ .../java/serp/bytecode/StoreInstruction.java | 281 ++ .../java/serp/bytecode/SwitchInstruction.java | 247 ++ .../main/java/serp/bytecode/Synthetic.java | 36 + .../serp/bytecode/TableSwitchInstruction.java | 272 ++ .../java/serp/bytecode/TypedInstruction.java | 157 + .../java/serp/bytecode/UnknownAttribute.java | 75 + .../java/serp/bytecode/WideInstruction.java | 427 +++ .../serp/bytecode/lowlevel/ClassEntry.java | 101 + .../serp/bytecode/lowlevel/ComplexEntry.java | 115 + .../serp/bytecode/lowlevel/ConstantEntry.java | 35 + .../serp/bytecode/lowlevel/ConstantPool.java | 725 +++++ .../bytecode/lowlevel/ConstantPoolTable.java | 207 ++ .../serp/bytecode/lowlevel/DoubleEntry.java | 90 + .../java/serp/bytecode/lowlevel/Entry.java | 188 ++ .../serp/bytecode/lowlevel/FieldEntry.java | 52 + .../serp/bytecode/lowlevel/FloatEntry.java | 86 + .../java/serp/bytecode/lowlevel/IntEntry.java | 88 + .../lowlevel/InterfaceMethodEntry.java | 52 + .../serp/bytecode/lowlevel/LongEntry.java | 92 + .../serp/bytecode/lowlevel/MethodEntry.java | 52 + .../bytecode/lowlevel/NameAndTypeEntry.java | 124 + .../serp/bytecode/lowlevel/StringEntry.java | 98 + .../serp/bytecode/lowlevel/UTF8Entry.java | 90 + .../java/serp/bytecode/lowlevel/package.html | 11 + serp/src/main/java/serp/bytecode/package.html | 139 + .../java/serp/bytecode/visitor/BCVisitor.java | 415 +++ .../bytecode/visitor/PrettyPrintVisitor.java | 459 +++ .../serp/bytecode/visitor/VisitAcceptor.java | 34 + .../java/serp/bytecode/visitor/package.html | 10 + serp/src/main/java/serp/util/Numbers.java | 69 + serp/src/main/java/serp/util/Strings.java | 417 +++ serp/src/main/java/serp/util/package.html | 8 + .../java/serp/bytecode/AbstractStateTest.java | 207 ++ .../test/java/serp/bytecode/TestArray.java | 87 + .../bytecode/TestArrayLoadInstruction.java | 96 + .../bytecode/TestArrayStoreInstruction.java | 96 + .../java/serp/bytecode/TestAttributes.java | 155 + .../test/java/serp/bytecode/TestBCClass.java | 269 ++ .../src/test/java/serp/bytecode/TestCode.java | 142 + .../bytecode/TestConstantInstruction.java | 125 + .../serp/bytecode/TestConvertInstruction.java | 99 + .../serp/bytecode/TestLoadInstruction.java | 179 ++ .../serp/bytecode/TestMathInstruction.java | 347 ++ .../java/serp/bytecode/TestNameCache.java | 217 ++ .../java/serp/bytecode/TestPrimitive.java | 74 + .../test/java/serp/bytecode/TestProject.java | 333 ++ .../serp/bytecode/TestStoreInstruction.java | 179 ++ .../bytecode/lowlevel/TestConstantPool.java | 360 +++ serp/src/test/java/serp/util/TestStrings.java | 128 + 295 files changed, 27213 insertions(+), 81 deletions(-) delete mode 100755 openjpa-lib/main/java/org/apache/openjpa/lib/conf/NoneConfigurationProvider.java create mode 100755 openjpa-lib/pom.xml rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/ant/AbstractTask.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/ant/package.html (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/conf/BooleanValue.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/conf/Configurable.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/conf/Configuration.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/conf/ConfigurationImpl.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/conf/ConfigurationProvider.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/conf/Configurations.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/conf/DoubleValue.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/conf/FileValue.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/conf/GenericConfigurable.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/conf/IntValue.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/conf/MapConfigurationProvider.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/conf/ObjectValue.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/conf/PluginListValue.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/conf/PluginValue.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/conf/StringListValue.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/conf/StringValue.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/conf/Value.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/conf/ValueListener.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/conf/package.html (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/jdbc/AbstractJDBCListener.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/jdbc/ConfiguringConnectionDecorator.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/jdbc/ConnectionDecorator.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/jdbc/DataSourceLogs.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/jdbc/DecoratingDataSource.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/jdbc/DelegatingCallableStatement.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/jdbc/DelegatingConnection.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/jdbc/DelegatingDataSource.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/jdbc/DelegatingDatabaseMetaData.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/jdbc/DelegatingPreparedStatement.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/jdbc/DelegatingResultSet.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/jdbc/DelegatingStatement.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/jdbc/JDBCEvent.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/jdbc/JDBCEventConnectionDecorator.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/jdbc/JDBCListener.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/jdbc/LoggingConnectionDecorator.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/jdbc/ReportingSQLException.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/jdbc/SQLFormatter.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/jdbc/package.html (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/log/AbstractLog.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/log/CommonsLogFactory.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/log/Log.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/log/Log4JLogFactory.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/log/LogFactory.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/log/LogFactoryAdapter.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/log/LogFactoryImpl.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/log/LogOutputStream.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/log/MultiLogFactory.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/log/NoneLogFactory.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/log/package.html (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/meta/CFMetaDataParser.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/meta/CFMetaDataSerializer.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/meta/ClassAnnotationMetaDataFilter.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/meta/ClassArgParser.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/meta/ClassMetaDataIterator.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/meta/ClasspathMetaDataIterator.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/meta/FileMetaDataIterator.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/meta/MetaDataFilter.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/meta/MetaDataIterator.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/meta/MetaDataIteratorChain.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/meta/MetaDataParser.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/meta/MetaDataSerializer.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/meta/ResourceMetaDataIterator.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/meta/SourceTracker.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/meta/SourceTrackers.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/meta/SuffixMetaDataFilter.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/meta/URLMetaDataIterator.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/meta/XMLMetaDataParser.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/meta/XMLMetaDataSerializer.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/meta/ZipFileMetaDataIterator.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/meta/ZipStreamMetaDataIterator.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/meta/package.html (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/rop/AbstractListIterator.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/rop/AbstractNonSequentialResultList.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/rop/AbstractResultList.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/rop/AbstractSequentialResultList.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/rop/EagerResultList.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/rop/LazyForwardResultList.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/rop/ListResultList.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/rop/ListResultObjectProvider.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/rop/MergedResultObjectProvider.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/rop/RandomAccessResultList.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/rop/RangeResultObjectProvider.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/rop/ResultList.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/rop/ResultListIterator.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/rop/ResultObjectProvider.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/rop/ResultObjectProviderIterator.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/rop/SimpleResultList.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/rop/SoftRandomAccessResultList.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/rop/WindowResultList.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/rop/package.html (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/AbstractEventManager.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/Base16Encoder.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/BytecodeWriter.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/Closeable.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/CodeFormat.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/EventManager.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/Files.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/FormatPreservingProperties.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/JavaVersions.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/LRUMap.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/Localizer.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/MultiClassLoader.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/Options.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/ParameterTemplate.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/ParseException.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/ReferenceHashMap.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/ReferenceHashSet.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/ReferenceMap.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/ResourceBundleProvider.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/Services.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/SimpleRegex.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/SimpleResourceBundleProvider.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/SizedMap.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/StreamResourceBundleProvider.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/StringDistance.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/TemporaryClassLoader.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/TypedProperties.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/UUIDGenerator.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/ZipResourceBundleProvider.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/AbstractCollection.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/AbstractConcurrentEventManager.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/AbstractQueue.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/AbstractSet.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/Arrays.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentHashMap.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentHashSet.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentLinkedQueue.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentMap.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentReferenceHashMap.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentReferenceHashSet.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/CondVar.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/Condition.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/CopyOnWriteArrayList.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/CopyOnWriteArraySet.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/FIFOCondVar.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/FIFOWaitQueue.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/Lock.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/NanoTimer.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/Queue.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/ReentrantLock.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/TimeUnit.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/Utils.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/WaitQueue.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/concurrent/package.html (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/util/package.html (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/xml/Commentable.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/xml/DocTypeReader.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/xml/Location.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/xml/ValidatingErrorHandler.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/xml/XMLFactory.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/xml/XMLWriter.java (100%) rename openjpa-lib/{ => src}/main/java/org/apache/openjpa/lib/xml/package.html (100%) rename openjpa-lib/{ => src}/main/resources/org/apache/openjpa/lib/ant/localizer.properties (100%) rename openjpa-lib/{ => src}/main/resources/org/apache/openjpa/lib/conf/localizer.properties (100%) rename openjpa-lib/{ => src}/main/resources/org/apache/openjpa/lib/jdbc/localizer.properties (100%) rename openjpa-lib/{ => src}/main/resources/org/apache/openjpa/lib/log/localizer.properties (100%) rename openjpa-lib/{ => src}/main/resources/org/apache/openjpa/lib/meta/localizer.properties (100%) rename openjpa-lib/{ => src}/main/resources/org/apache/openjpa/lib/rop/localizer.properties (100%) rename openjpa-lib/{ => src}/main/resources/org/apache/openjpa/lib/util/localizer.properties (100%) rename openjpa-lib/{ => src}/main/resources/org/apache/openjpa/lib/xml/localizer.properties (100%) create mode 100755 openjpa-lib/src/test/java/org/apache/openjpa/lib/util/Duration.java create mode 100755 openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestAbstractEventManager.java create mode 100755 openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestLocalizer.java create mode 100755 openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestMultiClassLoader.java create mode 100755 openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestOptions.java create mode 100755 openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestParameterTemplate.java create mode 100755 openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestPropertiesParser.java create mode 100755 openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestReferenceSet.java create mode 100755 openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestSimpleRegex.java create mode 100755 openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestTemporaryClassLoader.java create mode 100755 openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestTypedProperties.java create mode 100755 openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestUUIDGenerator.java create mode 100755 openjpa-lib/src/test/java/org/apache/openjpa/lib/util/concurrent/TestConcurrentMap.java create mode 100755 openjpa-lib/src/test/java/org/apache/openjpa/lib/util/testlocalizer/LocalizerTestHelper.java create mode 100755 openjpa-lib/src/test/resources/localizer.properties create mode 100755 openjpa-lib/src/test/resources/org/apache/openjpa/lib/conf/test/localizer.properties create mode 100755 openjpa-lib/src/test/resources/org/apache/openjpa/lib/util/testlocalizer/localizer.properties create mode 100755 openjpa-lib/src/test/resources/org/apache/openjpa/lib/util/testlocalizer/localizer_de_DE.properties create mode 100755 pom.xml create mode 100755 serp/pom.xml create mode 100755 serp/src/main/java/serp/bytecode/ArrayInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/ArrayLoadInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/ArrayState.java create mode 100755 serp/src/main/java/serp/bytecode/ArrayStoreInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/Attribute.java create mode 100755 serp/src/main/java/serp/bytecode/Attributes.java create mode 100755 serp/src/main/java/serp/bytecode/BCClass.java create mode 100755 serp/src/main/java/serp/bytecode/BCClassLoader.java create mode 100755 serp/src/main/java/serp/bytecode/BCEntity.java create mode 100755 serp/src/main/java/serp/bytecode/BCField.java create mode 100755 serp/src/main/java/serp/bytecode/BCMember.java create mode 100755 serp/src/main/java/serp/bytecode/BCMethod.java create mode 100755 serp/src/main/java/serp/bytecode/ClassConstantInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/ClassInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/CmpInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/Code.java create mode 100755 serp/src/main/java/serp/bytecode/CodeEntry.java create mode 100755 serp/src/main/java/serp/bytecode/ConstantInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/ConstantValue.java create mode 100755 serp/src/main/java/serp/bytecode/Constants.java create mode 100755 serp/src/main/java/serp/bytecode/ConvertInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/Deprecated.java create mode 100755 serp/src/main/java/serp/bytecode/ExceptionHandler.java create mode 100755 serp/src/main/java/serp/bytecode/Exceptions.java create mode 100755 serp/src/main/java/serp/bytecode/FieldInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/GetFieldInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/IIncInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/IfInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/InnerClass.java create mode 100755 serp/src/main/java/serp/bytecode/InnerClasses.java create mode 100755 serp/src/main/java/serp/bytecode/Instruction.java create mode 100755 serp/src/main/java/serp/bytecode/InstructionPtr.java create mode 100755 serp/src/main/java/serp/bytecode/InstructionPtrStrategy.java create mode 100755 serp/src/main/java/serp/bytecode/JumpInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/LineNumber.java create mode 100755 serp/src/main/java/serp/bytecode/LineNumberTable.java create mode 100755 serp/src/main/java/serp/bytecode/LoadInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/Local.java create mode 100755 serp/src/main/java/serp/bytecode/LocalTable.java create mode 100755 serp/src/main/java/serp/bytecode/LocalVariable.java create mode 100755 serp/src/main/java/serp/bytecode/LocalVariableInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/LocalVariableTable.java create mode 100755 serp/src/main/java/serp/bytecode/LocalVariableType.java create mode 100755 serp/src/main/java/serp/bytecode/LocalVariableTypeTable.java create mode 100755 serp/src/main/java/serp/bytecode/LookupSwitchInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/MathInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/MethodInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/MonitorEnterInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/MonitorExitInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/MonitorInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/MultiANewArrayInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/NameCache.java create mode 100755 serp/src/main/java/serp/bytecode/NewArrayInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/ObjectState.java create mode 100755 serp/src/main/java/serp/bytecode/PrimitiveState.java create mode 100755 serp/src/main/java/serp/bytecode/Project.java create mode 100755 serp/src/main/java/serp/bytecode/PutFieldInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/RetInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/ReturnInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/SourceFile.java create mode 100755 serp/src/main/java/serp/bytecode/StackInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/State.java create mode 100755 serp/src/main/java/serp/bytecode/StoreInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/SwitchInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/Synthetic.java create mode 100755 serp/src/main/java/serp/bytecode/TableSwitchInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/TypedInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/UnknownAttribute.java create mode 100755 serp/src/main/java/serp/bytecode/WideInstruction.java create mode 100755 serp/src/main/java/serp/bytecode/lowlevel/ClassEntry.java create mode 100755 serp/src/main/java/serp/bytecode/lowlevel/ComplexEntry.java create mode 100755 serp/src/main/java/serp/bytecode/lowlevel/ConstantEntry.java create mode 100755 serp/src/main/java/serp/bytecode/lowlevel/ConstantPool.java create mode 100755 serp/src/main/java/serp/bytecode/lowlevel/ConstantPoolTable.java create mode 100755 serp/src/main/java/serp/bytecode/lowlevel/DoubleEntry.java create mode 100755 serp/src/main/java/serp/bytecode/lowlevel/Entry.java create mode 100755 serp/src/main/java/serp/bytecode/lowlevel/FieldEntry.java create mode 100755 serp/src/main/java/serp/bytecode/lowlevel/FloatEntry.java create mode 100755 serp/src/main/java/serp/bytecode/lowlevel/IntEntry.java create mode 100755 serp/src/main/java/serp/bytecode/lowlevel/InterfaceMethodEntry.java create mode 100755 serp/src/main/java/serp/bytecode/lowlevel/LongEntry.java create mode 100755 serp/src/main/java/serp/bytecode/lowlevel/MethodEntry.java create mode 100755 serp/src/main/java/serp/bytecode/lowlevel/NameAndTypeEntry.java create mode 100755 serp/src/main/java/serp/bytecode/lowlevel/StringEntry.java create mode 100755 serp/src/main/java/serp/bytecode/lowlevel/UTF8Entry.java create mode 100755 serp/src/main/java/serp/bytecode/lowlevel/package.html create mode 100755 serp/src/main/java/serp/bytecode/package.html create mode 100755 serp/src/main/java/serp/bytecode/visitor/BCVisitor.java create mode 100755 serp/src/main/java/serp/bytecode/visitor/PrettyPrintVisitor.java create mode 100755 serp/src/main/java/serp/bytecode/visitor/VisitAcceptor.java create mode 100755 serp/src/main/java/serp/bytecode/visitor/package.html create mode 100755 serp/src/main/java/serp/util/Numbers.java create mode 100755 serp/src/main/java/serp/util/Strings.java create mode 100755 serp/src/main/java/serp/util/package.html create mode 100755 serp/src/test/java/serp/bytecode/AbstractStateTest.java create mode 100755 serp/src/test/java/serp/bytecode/TestArray.java create mode 100755 serp/src/test/java/serp/bytecode/TestArrayLoadInstruction.java create mode 100755 serp/src/test/java/serp/bytecode/TestArrayStoreInstruction.java create mode 100755 serp/src/test/java/serp/bytecode/TestAttributes.java create mode 100755 serp/src/test/java/serp/bytecode/TestBCClass.java create mode 100755 serp/src/test/java/serp/bytecode/TestCode.java create mode 100755 serp/src/test/java/serp/bytecode/TestConstantInstruction.java create mode 100755 serp/src/test/java/serp/bytecode/TestConvertInstruction.java create mode 100755 serp/src/test/java/serp/bytecode/TestLoadInstruction.java create mode 100755 serp/src/test/java/serp/bytecode/TestMathInstruction.java create mode 100755 serp/src/test/java/serp/bytecode/TestNameCache.java create mode 100755 serp/src/test/java/serp/bytecode/TestPrimitive.java create mode 100755 serp/src/test/java/serp/bytecode/TestProject.java create mode 100755 serp/src/test/java/serp/bytecode/TestStoreInstruction.java create mode 100755 serp/src/test/java/serp/bytecode/lowlevel/TestConstantPool.java create mode 100755 serp/src/test/java/serp/util/TestStrings.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/conf/NoneConfigurationProvider.java b/openjpa-lib/main/java/org/apache/openjpa/lib/conf/NoneConfigurationProvider.java deleted file mode 100755 index 57b10968d..000000000 --- a/openjpa-lib/main/java/org/apache/openjpa/lib/conf/NoneConfigurationProvider.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2006 The Apache Software Foundation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.conf; - - -import java.io.*; -import java.util.*; - -import org.apache.commons.collections.*; - - -/** - *

No-op configuration provider.

- * - * @author Abe White - * @nojavadoc - */ -public class NoneConfigurationProvider - implements ConfigurationProvider -{ - private static final NoneConfigurationProvider _instance = - new NoneConfigurationProvider (); - - - /** - * Singleton. - */ - public static NoneConfigurationProvider getInstance () - { - return _instance; - } - - - public boolean loadDefaults (ClassLoader loader) - { - return false; - } - - - public boolean load (String resource, ClassLoader loader) - { - return false; - } - - - public boolean load (File file) - { - return false; - } - - - public Map getProperties () - { - return MapUtils.EMPTY_MAP; - } - - - public void addProperties (Map props) - { - if (props != null && !props.isEmpty ()) - throw new UnsupportedOperationException (); - } - - - public void setInto (Configuration conf) - { - } -} diff --git a/openjpa-lib/pom.xml b/openjpa-lib/pom.xml new file mode 100755 index 000000000..13bbe1e43 --- /dev/null +++ b/openjpa-lib/pom.xml @@ -0,0 +1,113 @@ + + 4.0.0 + org.apache.openjpa + openjpa-lib + jar + + Utilities + Utilities + http://incubator.apache.org/projects/openjpa + + + org.apache.openjpa + openjpa + 0.0.1 + + + + + + + org.apache.openjpa + serp + 0.0.1 + compile + + + + + commons-logging + commons-logging + 1.0.4 + compile + + + + + log4j + log4j + 1.2.13 + compile + + + + + javax.servlet + servlet-api + 2.5 + compile + + + + + commons-lang + commons-lang + 2.1 + compile + + + + + ant + ant + 1.6.5 + compile + + + + + commons-collections + commons-collections + 3.1 + compile + + + + + commons-pool + commons-pool + 1.2 + compile + + + + + backport-util-concurrent + backport-util-concurrent + 2.1 + compile + + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.3 + 1.3 + + + + + + + + + + diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/ant/AbstractTask.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/ant/AbstractTask.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/ant/AbstractTask.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/ant/AbstractTask.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/ant/package.html b/openjpa-lib/src/main/java/org/apache/openjpa/lib/ant/package.html similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/ant/package.html rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/ant/package.html diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/conf/BooleanValue.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/BooleanValue.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/conf/BooleanValue.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/BooleanValue.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/conf/Configurable.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/Configurable.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/conf/Configurable.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/Configurable.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/conf/Configuration.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/Configuration.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/conf/Configuration.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/Configuration.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/conf/ConfigurationImpl.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ConfigurationImpl.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/conf/ConfigurationImpl.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ConfigurationImpl.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/conf/ConfigurationProvider.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ConfigurationProvider.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/conf/ConfigurationProvider.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ConfigurationProvider.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/conf/Configurations.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/Configurations.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/conf/Configurations.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/Configurations.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/conf/DoubleValue.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/DoubleValue.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/conf/DoubleValue.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/DoubleValue.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/conf/FileValue.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/FileValue.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/conf/FileValue.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/FileValue.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/conf/GenericConfigurable.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/GenericConfigurable.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/conf/GenericConfigurable.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/GenericConfigurable.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/conf/IntValue.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/IntValue.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/conf/IntValue.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/IntValue.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/conf/MapConfigurationProvider.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/MapConfigurationProvider.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/conf/MapConfigurationProvider.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/MapConfigurationProvider.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/conf/ObjectValue.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ObjectValue.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/conf/ObjectValue.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ObjectValue.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/conf/PluginListValue.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/PluginListValue.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/conf/PluginListValue.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/PluginListValue.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/conf/PluginValue.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/PluginValue.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/conf/PluginValue.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/PluginValue.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/conf/StringListValue.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/StringListValue.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/conf/StringListValue.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/StringListValue.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/conf/StringValue.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/StringValue.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/conf/StringValue.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/StringValue.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/conf/Value.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/Value.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/conf/Value.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/Value.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/conf/ValueListener.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ValueListener.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/conf/ValueListener.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ValueListener.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/conf/package.html b/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/package.html similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/conf/package.html rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/package.html diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/AbstractJDBCListener.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/AbstractJDBCListener.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/AbstractJDBCListener.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/AbstractJDBCListener.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/ConfiguringConnectionDecorator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/ConfiguringConnectionDecorator.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/ConfiguringConnectionDecorator.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/ConfiguringConnectionDecorator.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/ConnectionDecorator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/ConnectionDecorator.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/ConnectionDecorator.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/ConnectionDecorator.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/DataSourceLogs.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/DataSourceLogs.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/DataSourceLogs.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/DataSourceLogs.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/DecoratingDataSource.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/DecoratingDataSource.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/DecoratingDataSource.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/DecoratingDataSource.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/DelegatingCallableStatement.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/DelegatingCallableStatement.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/DelegatingCallableStatement.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/DelegatingCallableStatement.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/DelegatingConnection.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/DelegatingConnection.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/DelegatingConnection.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/DelegatingConnection.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/DelegatingDataSource.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/DelegatingDataSource.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/DelegatingDataSource.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/DelegatingDataSource.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/DelegatingDatabaseMetaData.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/DelegatingDatabaseMetaData.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/DelegatingDatabaseMetaData.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/DelegatingDatabaseMetaData.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/DelegatingPreparedStatement.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/DelegatingPreparedStatement.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/DelegatingPreparedStatement.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/DelegatingPreparedStatement.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/DelegatingResultSet.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/DelegatingResultSet.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/DelegatingResultSet.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/DelegatingResultSet.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/DelegatingStatement.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/DelegatingStatement.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/DelegatingStatement.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/DelegatingStatement.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/JDBCEvent.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/JDBCEvent.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/JDBCEvent.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/JDBCEvent.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/JDBCEventConnectionDecorator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/JDBCEventConnectionDecorator.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/JDBCEventConnectionDecorator.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/JDBCEventConnectionDecorator.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/JDBCListener.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/JDBCListener.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/JDBCListener.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/JDBCListener.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/LoggingConnectionDecorator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/LoggingConnectionDecorator.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/LoggingConnectionDecorator.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/LoggingConnectionDecorator.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/ReportingSQLException.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/ReportingSQLException.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/ReportingSQLException.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/ReportingSQLException.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/SQLFormatter.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/SQLFormatter.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/SQLFormatter.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/SQLFormatter.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/package.html b/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/package.html similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/jdbc/package.html rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/package.html diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/log/AbstractLog.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/log/AbstractLog.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/log/AbstractLog.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/log/AbstractLog.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/log/CommonsLogFactory.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/log/CommonsLogFactory.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/log/CommonsLogFactory.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/log/CommonsLogFactory.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/log/Log.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/log/Log.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/log/Log.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/log/Log.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/log/Log4JLogFactory.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/log/Log4JLogFactory.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/log/Log4JLogFactory.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/log/Log4JLogFactory.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/log/LogFactory.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/log/LogFactory.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/log/LogFactory.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/log/LogFactory.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/log/LogFactoryAdapter.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/log/LogFactoryAdapter.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/log/LogFactoryAdapter.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/log/LogFactoryAdapter.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/log/LogFactoryImpl.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/log/LogFactoryImpl.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/log/LogFactoryImpl.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/log/LogFactoryImpl.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/log/LogOutputStream.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/log/LogOutputStream.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/log/LogOutputStream.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/log/LogOutputStream.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/log/MultiLogFactory.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/log/MultiLogFactory.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/log/MultiLogFactory.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/log/MultiLogFactory.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/log/NoneLogFactory.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/log/NoneLogFactory.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/log/NoneLogFactory.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/log/NoneLogFactory.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/log/package.html b/openjpa-lib/src/main/java/org/apache/openjpa/lib/log/package.html similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/log/package.html rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/log/package.html diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/meta/CFMetaDataParser.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/CFMetaDataParser.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/meta/CFMetaDataParser.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/CFMetaDataParser.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/meta/CFMetaDataSerializer.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/CFMetaDataSerializer.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/meta/CFMetaDataSerializer.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/CFMetaDataSerializer.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/meta/ClassAnnotationMetaDataFilter.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/ClassAnnotationMetaDataFilter.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/meta/ClassAnnotationMetaDataFilter.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/ClassAnnotationMetaDataFilter.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/meta/ClassArgParser.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/ClassArgParser.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/meta/ClassArgParser.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/ClassArgParser.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/meta/ClassMetaDataIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/ClassMetaDataIterator.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/meta/ClassMetaDataIterator.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/ClassMetaDataIterator.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/meta/ClasspathMetaDataIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/ClasspathMetaDataIterator.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/meta/ClasspathMetaDataIterator.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/ClasspathMetaDataIterator.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/meta/FileMetaDataIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/FileMetaDataIterator.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/meta/FileMetaDataIterator.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/FileMetaDataIterator.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/meta/MetaDataFilter.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/MetaDataFilter.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/meta/MetaDataFilter.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/MetaDataFilter.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/meta/MetaDataIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/MetaDataIterator.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/meta/MetaDataIterator.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/MetaDataIterator.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/meta/MetaDataIteratorChain.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/MetaDataIteratorChain.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/meta/MetaDataIteratorChain.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/MetaDataIteratorChain.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/meta/MetaDataParser.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/MetaDataParser.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/meta/MetaDataParser.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/MetaDataParser.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/meta/MetaDataSerializer.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/MetaDataSerializer.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/meta/MetaDataSerializer.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/MetaDataSerializer.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/meta/ResourceMetaDataIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/ResourceMetaDataIterator.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/meta/ResourceMetaDataIterator.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/ResourceMetaDataIterator.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/meta/SourceTracker.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/SourceTracker.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/meta/SourceTracker.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/SourceTracker.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/meta/SourceTrackers.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/SourceTrackers.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/meta/SourceTrackers.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/SourceTrackers.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/meta/SuffixMetaDataFilter.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/SuffixMetaDataFilter.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/meta/SuffixMetaDataFilter.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/SuffixMetaDataFilter.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/meta/URLMetaDataIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/URLMetaDataIterator.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/meta/URLMetaDataIterator.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/URLMetaDataIterator.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/meta/XMLMetaDataParser.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/XMLMetaDataParser.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/meta/XMLMetaDataParser.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/XMLMetaDataParser.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/meta/XMLMetaDataSerializer.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/XMLMetaDataSerializer.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/meta/XMLMetaDataSerializer.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/XMLMetaDataSerializer.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/meta/ZipFileMetaDataIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/ZipFileMetaDataIterator.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/meta/ZipFileMetaDataIterator.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/ZipFileMetaDataIterator.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/meta/ZipStreamMetaDataIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/ZipStreamMetaDataIterator.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/meta/ZipStreamMetaDataIterator.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/ZipStreamMetaDataIterator.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/meta/package.html b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/package.html similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/meta/package.html rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/package.html diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/rop/AbstractListIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/AbstractListIterator.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/rop/AbstractListIterator.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/AbstractListIterator.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/rop/AbstractNonSequentialResultList.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/AbstractNonSequentialResultList.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/rop/AbstractNonSequentialResultList.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/AbstractNonSequentialResultList.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/rop/AbstractResultList.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/AbstractResultList.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/rop/AbstractResultList.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/AbstractResultList.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/rop/AbstractSequentialResultList.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/AbstractSequentialResultList.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/rop/AbstractSequentialResultList.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/AbstractSequentialResultList.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/rop/EagerResultList.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/EagerResultList.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/rop/EagerResultList.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/EagerResultList.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/rop/LazyForwardResultList.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/LazyForwardResultList.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/rop/LazyForwardResultList.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/LazyForwardResultList.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/rop/ListResultList.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/ListResultList.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/rop/ListResultList.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/ListResultList.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/rop/ListResultObjectProvider.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/ListResultObjectProvider.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/rop/ListResultObjectProvider.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/ListResultObjectProvider.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/rop/MergedResultObjectProvider.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/MergedResultObjectProvider.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/rop/MergedResultObjectProvider.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/MergedResultObjectProvider.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/rop/RandomAccessResultList.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/RandomAccessResultList.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/rop/RandomAccessResultList.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/RandomAccessResultList.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/rop/RangeResultObjectProvider.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/RangeResultObjectProvider.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/rop/RangeResultObjectProvider.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/RangeResultObjectProvider.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/rop/ResultList.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/ResultList.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/rop/ResultList.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/ResultList.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/rop/ResultListIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/ResultListIterator.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/rop/ResultListIterator.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/ResultListIterator.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/rop/ResultObjectProvider.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/ResultObjectProvider.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/rop/ResultObjectProvider.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/ResultObjectProvider.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/rop/ResultObjectProviderIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/ResultObjectProviderIterator.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/rop/ResultObjectProviderIterator.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/ResultObjectProviderIterator.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/rop/SimpleResultList.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/SimpleResultList.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/rop/SimpleResultList.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/SimpleResultList.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/rop/SoftRandomAccessResultList.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/SoftRandomAccessResultList.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/rop/SoftRandomAccessResultList.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/SoftRandomAccessResultList.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/rop/WindowResultList.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/WindowResultList.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/rop/WindowResultList.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/WindowResultList.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/rop/package.html b/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/package.html similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/rop/package.html rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/package.html diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/AbstractEventManager.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/AbstractEventManager.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/AbstractEventManager.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/AbstractEventManager.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/Base16Encoder.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Base16Encoder.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/Base16Encoder.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Base16Encoder.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/BytecodeWriter.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/BytecodeWriter.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/BytecodeWriter.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/BytecodeWriter.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/Closeable.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Closeable.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/Closeable.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Closeable.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/CodeFormat.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/CodeFormat.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/CodeFormat.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/CodeFormat.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/EventManager.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/EventManager.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/EventManager.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/EventManager.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/Files.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Files.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/Files.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Files.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/FormatPreservingProperties.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/FormatPreservingProperties.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/FormatPreservingProperties.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/FormatPreservingProperties.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/JavaVersions.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/JavaVersions.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/JavaVersions.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/JavaVersions.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/LRUMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/LRUMap.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/LRUMap.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/LRUMap.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/Localizer.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Localizer.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/Localizer.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Localizer.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/MultiClassLoader.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/MultiClassLoader.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/MultiClassLoader.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/MultiClassLoader.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/Options.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Options.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/Options.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Options.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/ParameterTemplate.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/ParameterTemplate.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/ParameterTemplate.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/ParameterTemplate.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/ParseException.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/ParseException.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/ParseException.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/ParseException.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/ReferenceHashMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/ReferenceHashMap.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/ReferenceHashMap.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/ReferenceHashMap.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/ReferenceHashSet.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/ReferenceHashSet.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/ReferenceHashSet.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/ReferenceHashSet.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/ReferenceMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/ReferenceMap.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/ReferenceMap.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/ReferenceMap.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/ResourceBundleProvider.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/ResourceBundleProvider.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/ResourceBundleProvider.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/ResourceBundleProvider.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/Services.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Services.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/Services.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Services.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/SimpleRegex.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/SimpleRegex.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/SimpleRegex.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/SimpleRegex.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/SimpleResourceBundleProvider.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/SimpleResourceBundleProvider.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/SimpleResourceBundleProvider.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/SimpleResourceBundleProvider.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/SizedMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/SizedMap.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/SizedMap.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/SizedMap.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/StreamResourceBundleProvider.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/StreamResourceBundleProvider.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/StreamResourceBundleProvider.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/StreamResourceBundleProvider.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/StringDistance.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/StringDistance.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/StringDistance.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/StringDistance.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/TemporaryClassLoader.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/TemporaryClassLoader.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/TemporaryClassLoader.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/TemporaryClassLoader.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/TypedProperties.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/TypedProperties.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/TypedProperties.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/TypedProperties.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/UUIDGenerator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/UUIDGenerator.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/UUIDGenerator.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/UUIDGenerator.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/ZipResourceBundleProvider.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/ZipResourceBundleProvider.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/ZipResourceBundleProvider.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/ZipResourceBundleProvider.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/AbstractCollection.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/AbstractCollection.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/AbstractCollection.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/AbstractCollection.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/AbstractConcurrentEventManager.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/AbstractConcurrentEventManager.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/AbstractConcurrentEventManager.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/AbstractConcurrentEventManager.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/AbstractQueue.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/AbstractQueue.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/AbstractQueue.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/AbstractQueue.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/AbstractSet.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/AbstractSet.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/AbstractSet.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/AbstractSet.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/Arrays.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/Arrays.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/Arrays.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/Arrays.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentHashMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentHashMap.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentHashMap.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentHashMap.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentHashSet.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentHashSet.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentHashSet.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentHashSet.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentLinkedQueue.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentLinkedQueue.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentLinkedQueue.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentLinkedQueue.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentMap.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentMap.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentMap.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentReferenceHashMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentReferenceHashMap.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentReferenceHashMap.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentReferenceHashMap.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentReferenceHashSet.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentReferenceHashSet.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentReferenceHashSet.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentReferenceHashSet.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/CondVar.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/CondVar.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/CondVar.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/CondVar.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/Condition.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/Condition.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/Condition.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/Condition.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/CopyOnWriteArrayList.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/CopyOnWriteArrayList.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/CopyOnWriteArrayList.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/CopyOnWriteArrayList.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/CopyOnWriteArraySet.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/CopyOnWriteArraySet.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/CopyOnWriteArraySet.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/CopyOnWriteArraySet.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/FIFOCondVar.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/FIFOCondVar.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/FIFOCondVar.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/FIFOCondVar.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/FIFOWaitQueue.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/FIFOWaitQueue.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/FIFOWaitQueue.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/FIFOWaitQueue.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/Lock.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/Lock.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/Lock.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/Lock.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/NanoTimer.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/NanoTimer.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/NanoTimer.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/NanoTimer.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/Queue.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/Queue.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/Queue.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/Queue.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ReentrantLock.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/ReentrantLock.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ReentrantLock.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/ReentrantLock.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/TimeUnit.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/TimeUnit.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/TimeUnit.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/TimeUnit.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/Utils.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/Utils.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/Utils.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/Utils.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/WaitQueue.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/WaitQueue.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/WaitQueue.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/WaitQueue.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/package.html b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/package.html similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/package.html rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/package.html diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/util/package.html b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/package.html similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/util/package.html rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/util/package.html diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/xml/Commentable.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/xml/Commentable.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/xml/Commentable.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/xml/Commentable.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/xml/DocTypeReader.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/xml/DocTypeReader.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/xml/DocTypeReader.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/xml/DocTypeReader.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/xml/Location.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/xml/Location.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/xml/Location.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/xml/Location.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/xml/ValidatingErrorHandler.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/xml/ValidatingErrorHandler.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/xml/ValidatingErrorHandler.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/xml/ValidatingErrorHandler.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/xml/XMLFactory.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/xml/XMLFactory.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/xml/XMLFactory.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/xml/XMLFactory.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/xml/XMLWriter.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/xml/XMLWriter.java similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/xml/XMLWriter.java rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/xml/XMLWriter.java diff --git a/openjpa-lib/main/java/org/apache/openjpa/lib/xml/package.html b/openjpa-lib/src/main/java/org/apache/openjpa/lib/xml/package.html similarity index 100% rename from openjpa-lib/main/java/org/apache/openjpa/lib/xml/package.html rename to openjpa-lib/src/main/java/org/apache/openjpa/lib/xml/package.html diff --git a/openjpa-lib/main/resources/org/apache/openjpa/lib/ant/localizer.properties b/openjpa-lib/src/main/resources/org/apache/openjpa/lib/ant/localizer.properties similarity index 100% rename from openjpa-lib/main/resources/org/apache/openjpa/lib/ant/localizer.properties rename to openjpa-lib/src/main/resources/org/apache/openjpa/lib/ant/localizer.properties diff --git a/openjpa-lib/main/resources/org/apache/openjpa/lib/conf/localizer.properties b/openjpa-lib/src/main/resources/org/apache/openjpa/lib/conf/localizer.properties similarity index 100% rename from openjpa-lib/main/resources/org/apache/openjpa/lib/conf/localizer.properties rename to openjpa-lib/src/main/resources/org/apache/openjpa/lib/conf/localizer.properties diff --git a/openjpa-lib/main/resources/org/apache/openjpa/lib/jdbc/localizer.properties b/openjpa-lib/src/main/resources/org/apache/openjpa/lib/jdbc/localizer.properties similarity index 100% rename from openjpa-lib/main/resources/org/apache/openjpa/lib/jdbc/localizer.properties rename to openjpa-lib/src/main/resources/org/apache/openjpa/lib/jdbc/localizer.properties diff --git a/openjpa-lib/main/resources/org/apache/openjpa/lib/log/localizer.properties b/openjpa-lib/src/main/resources/org/apache/openjpa/lib/log/localizer.properties similarity index 100% rename from openjpa-lib/main/resources/org/apache/openjpa/lib/log/localizer.properties rename to openjpa-lib/src/main/resources/org/apache/openjpa/lib/log/localizer.properties diff --git a/openjpa-lib/main/resources/org/apache/openjpa/lib/meta/localizer.properties b/openjpa-lib/src/main/resources/org/apache/openjpa/lib/meta/localizer.properties similarity index 100% rename from openjpa-lib/main/resources/org/apache/openjpa/lib/meta/localizer.properties rename to openjpa-lib/src/main/resources/org/apache/openjpa/lib/meta/localizer.properties diff --git a/openjpa-lib/main/resources/org/apache/openjpa/lib/rop/localizer.properties b/openjpa-lib/src/main/resources/org/apache/openjpa/lib/rop/localizer.properties similarity index 100% rename from openjpa-lib/main/resources/org/apache/openjpa/lib/rop/localizer.properties rename to openjpa-lib/src/main/resources/org/apache/openjpa/lib/rop/localizer.properties diff --git a/openjpa-lib/main/resources/org/apache/openjpa/lib/util/localizer.properties b/openjpa-lib/src/main/resources/org/apache/openjpa/lib/util/localizer.properties similarity index 100% rename from openjpa-lib/main/resources/org/apache/openjpa/lib/util/localizer.properties rename to openjpa-lib/src/main/resources/org/apache/openjpa/lib/util/localizer.properties diff --git a/openjpa-lib/main/resources/org/apache/openjpa/lib/xml/localizer.properties b/openjpa-lib/src/main/resources/org/apache/openjpa/lib/xml/localizer.properties similarity index 100% rename from openjpa-lib/main/resources/org/apache/openjpa/lib/xml/localizer.properties rename to openjpa-lib/src/main/resources/org/apache/openjpa/lib/xml/localizer.properties diff --git a/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/Duration.java b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/Duration.java new file mode 100755 index 000000000..4691af307 --- /dev/null +++ b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/Duration.java @@ -0,0 +1,162 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util; + +import java.lang.Exception; +import java.lang.System; + + +/* + Millisecond (only) accuracy timer. + Java 1.4 supposedly has sun.misc.Perf. + Java 1.5 has System.nanoTime (JSR 166) +*/ +public class Duration implements Cloneable { + private String _name; + private boolean _started; + private boolean _running; + private long _startTime; // millis + private long _stopTime; // millis + + // NYI clock time of day at start + public Duration(String name) { + _name = name; + _started = false; + _running = false; + } + + public String getName() { + return _name; + } + + public synchronized void start() { + if (_started) { + throw new RuntimeException("Duration was already started."); + } + + _startTime = System.currentTimeMillis(); + _started = true; + _running = true; + } + + public synchronized void stop() { + if (!_started) { + throw new RuntimeException("Duration was never started."); + } + + if (!_running) { + throw new RuntimeException("Duration was already stopped."); + } + + _stopTime = System.currentTimeMillis(); + _running = false; + } + + protected Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + /* + Returns a new Duration object from a currently running timer + as a snapshot of this object. + The returned timer is stopped, while this object continue on. + */ + public synchronized Duration getCurrentDuration() { + if (!_started) { + throw new RuntimeException("Duration was never started."); + } + + if (!_running) { + throw new RuntimeException("Duration is not running."); + } + + long now = System.currentTimeMillis(); + Duration currentDuration; + + try { + currentDuration = (Duration) this.clone(); + } catch (Exception e) { + currentDuration = new Duration(""); + } + + currentDuration._stopTime = now; + currentDuration._running = false; + + return currentDuration; + } + + /* Obtain the duration that this timer has run (in seconds) */ + public synchronized double getDurationAsSeconds() { + if (!_started) { + throw new RuntimeException("Duration was never started."); + } + + if (_running) { + // snapshot + Duration snapshot = getCurrentDuration(); + + return (1000.0 * (snapshot._stopTime - snapshot._startTime)); + } + + // Return a double value. Someday this class may make use of + // higher precision timing services (e.g. java 1.5) + return ((_stopTime - _startTime) / (double) 1000.0); + } + + public synchronized boolean isRunning() { + return _running; + } + + public synchronized boolean wasStarted() { + return _started; + } + + public String toString() { + double time = 0.0; + StringBuffer buf = new StringBuffer(256); + + if (wasStarted()) { + if (isRunning()) { + Duration snapshot = getCurrentDuration(); + time = snapshot.getDurationAsSeconds(); + } else { + time = getDurationAsSeconds(); + } + + buf.append("Duration for '" + _name + "' is " + time + " (s)."); + } else { + buf.append("Duration for '" + _name + + "' has not yet been started."); + } + + return buf.toString(); + } + + /* Example usage: + public static void main (String[] args) + throws Exception + { + Duration test = new Duration ("hello, count to 1 million"); + System.out.println (test); + test.start (); + for (int i = 0; i < 1000000000; i++) + { + } + test.stop (); + System.out.println (test); + } + */ +} diff --git a/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestAbstractEventManager.java b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestAbstractEventManager.java new file mode 100755 index 000000000..b9e921f10 --- /dev/null +++ b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestAbstractEventManager.java @@ -0,0 +1,95 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util; + +import junit.framework.*; + +import junit.textui.*; + +import java.util.*; + + +/** + *

Tests the {@link AbstractEventManager}.

+ * + * @author Abe White + */ +public class TestAbstractEventManager extends TestCase { + private EventManager _em = new EventManager(); + + public TestAbstractEventManager(String test) { + super(test); + } + + public void testReentrantAdd() { + Listener l1 = new Listener(Listener.ADD); + Listener l2 = new Listener(Listener.NONE); + _em.addListener(l1); + _em.addListener(l2); + _em.fireEvent(new Object()); + assertTrue(l1.fired); + assertTrue(l2.fired); + assertEquals(3, _em.getListeners().size()); + } + + public void testReentrantRemove() { + Listener l1 = new Listener(Listener.REMOVE); + Listener l2 = new Listener(Listener.NONE); + _em.addListener(l1); + _em.addListener(l2); + _em.fireEvent(new Object()); + assertTrue(l1.fired); + assertTrue(l2.fired); + assertEquals(1, _em.getListeners().size()); + assertFalse(_em.getListeners().contains(l1)); + } + + public static Test suite() { + return new TestSuite(TestAbstractEventManager.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } + + private static class EventManager extends AbstractEventManager { + protected void fireEvent(Object event, Object listener) { + ((Listener) listener).fire(); + } + } + + private class Listener { + public static final int NONE = 0; + public static final int ADD = 1; + public static final int REMOVE = 2; + public boolean fired; + private final int _action; + + public Listener(int action) { + _action = action; + } + + public void fire() { + fired = true; + + if (_action == ADD) { + _em.addListener(new Listener(NONE)); + } else if (_action == REMOVE) { + assertTrue(_em.removeListener(this)); + } + } + } +} diff --git a/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestLocalizer.java b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestLocalizer.java new file mode 100755 index 000000000..bdcf227e3 --- /dev/null +++ b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestLocalizer.java @@ -0,0 +1,125 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util; + +import junit.framework.*; + +import junit.textui.*; + +import org.apache.openjpa.lib.util.testlocalizer.*; + +import java.util.*; + + +/** + *

Tests the Localizer.

+ * + * @author Abe White + */ +public class TestLocalizer extends TestCase { + private Localizer _locals = null; + + public TestLocalizer(String test) { + super(test); + } + + public void setUp() { + _locals = Localizer.forPackage(LocalizerTestHelper.class); + } + + /** + * Test getting a string for a class. + */ + public void testForClass() { + assertEquals("value1", _locals.get("test.local1")); + } + + /** + * Test getting a string for a non-default locale. + */ + public void testForLocale() { + Localizer locl = Localizer.forPackage(LocalizerTestHelper.class, + Locale.GERMANY); + assertEquals("value1_de", locl.get("test.local1")); + } + + /** + * Tests that if a locale is missing the system falls back to + * the default. + */ + public void testFallbackLocale() { + Localizer locl = Localizer.forPackage(LocalizerTestHelper.class, + Locale.FRANCE); + assertEquals("value1", locl.get("test.local1")); + } + + /** + * Tests that a null class accesses the localizer.properties file + * for the top-level (no package). + */ + public void testTopLevel() { + Localizer system = Localizer.forPackage(null); + assertEquals("systemvalue1", system.get("test.systemlocal")); + } + + /** + * Test that the message formatting works correctly. + */ + public void testMessageFormat() { + assertEquals("value2 x sep y", + _locals.get("test.local2", new String[] { "x", "y" })); + + // test that it treates single objects as single-element arrays + assertEquals("value2 x sep {1}", _locals.get("test.local2", "x")); + } + + /** + * Test that a {@link MissingResourceException} is thrown for missing + * resource bundles. + */ + public void testMissingBundle() { + Localizer missing = Localizer.forPackage(String.class); + assertEquals("foo.bar", missing.get("foo.bar")); + + try { + missing.getFatal("foo.bar"); + fail("No exception for fatal get on missing bundle."); + } catch (MissingResourceException mre) { + } + } + + /** + * Test that a {@link MissingResourceException} is thrown for missing + * keys. + */ + public void testMissingKey() { + assertEquals("foo.bar", _locals.get("foo.bar")); + + try { + _locals.getFatal("foo.bar"); + fail("No exception for fatal get on missing key."); + } catch (MissingResourceException mre) { + } + } + + public static Test suite() { + return new TestSuite(TestLocalizer.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } +} diff --git a/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestMultiClassLoader.java b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestMultiClassLoader.java new file mode 100755 index 000000000..b378b95f7 --- /dev/null +++ b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestMultiClassLoader.java @@ -0,0 +1,163 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util; + +import junit.framework.*; + +import junit.textui.*; + +import java.net.*; + +import java.util.*; + + +/** + *

Tests the {@link MultiClassLoader}.

+ * + * @author Abe White + */ +public class TestMultiClassLoader extends TestCase { + private ClassLoader SYSTEM_LOADER = MultiClassLoader.class.getClassLoader(); + private MultiClassLoader _loader = new MultiClassLoader(); + + public TestMultiClassLoader(String test) { + super(test); + } + + public void setUp() { + _loader.addClassLoader(MultiClassLoader.THREAD_LOADER); + _loader.addClassLoader(SYSTEM_LOADER); + } + + /** + * Tests basic add/remove functionality. + */ + public void testBasic() { + assertEquals(2, _loader.size()); + assertTrue(!_loader.isEmpty()); + assertTrue(_loader.getClassLoaders()[0] == SYSTEM_LOADER); + assertEquals(Thread.currentThread().getContextClassLoader(), + _loader.getClassLoaders()[1]); + assertTrue(_loader.getClassLoader(0) == SYSTEM_LOADER); + assertTrue(!_loader.addClassLoader(_loader.THREAD_LOADER)); + + FooLoader foo = new FooLoader(); + assertTrue(_loader.addClassLoader(foo)); + assertTrue(!_loader.addClassLoader(foo)); + assertEquals(3, _loader.size()); + assertEquals(foo, _loader.getClassLoaders()[2]); + + assertTrue(_loader.removeClassLoader(_loader.THREAD_LOADER)); + assertTrue(!_loader.removeClassLoader(_loader.THREAD_LOADER)); + assertEquals(2, _loader.size()); + assertTrue(_loader.getClassLoaders()[0] == SYSTEM_LOADER); + assertEquals(foo, _loader.getClassLoaders()[1]); + + assertTrue(_loader.removeClassLoader(foo)); + assertTrue(_loader.removeClassLoader(SYSTEM_LOADER)); + assertTrue(_loader.isEmpty()); + + MultiClassLoader loader2 = new MultiClassLoader(); + loader2.addClassLoader(MultiClassLoader.THREAD_LOADER); + loader2.addClassLoader(SYSTEM_LOADER); + assertTrue(_loader.addClassLoaders(loader2)); + assertTrue(!_loader.addClassLoaders(loader2)); + + FooLoader foo2 = new FooLoader(); + loader2.addClassLoader(foo); + loader2.addClassLoader(foo2); + assertTrue(_loader.addClassLoaders(1, loader2)); + + ClassLoader[] loaders = _loader.getClassLoaders(); + assertTrue(loaders[0] == SYSTEM_LOADER); + assertEquals(Thread.currentThread().getContextClassLoader(), loaders[3]); + assertEquals(foo, loaders[1]); + assertEquals(foo2, loaders[2]); + } + + /** + * Test finding classes. + */ + + /* + public void testClassForName () + throws Exception + { + assertEquals (MultiClassLoader.class, + Class.forName (MultiClassLoader.class.getName (), true, _loader)); + assertTrue (_loader.removeClassLoader (SYSTEM_LOADER)); + assertTrue (_loader.removeClassLoader (MultiClassLoader.THREAD_LOADER)); + try + { + // have to switch classes here b/c other is now cached + assertEquals (TestMultiClassLoader.class, Class.forName + (TestMultiClassLoader.class.getName (), true, _loader)); + fail ("System class laoder still working."); + } + catch (ClassNotFoundException cnfe) + { + } + try + { + Class.forName ("foo", true, _loader); + fail ("Somehow found 'foo'???"); + } + catch (ClassNotFoundException cnfe) + { + } + _loader.addClassLoader (new FooLoader ()); + assertEquals (Integer.class, Class.forName ("foo", true, _loader)); + } + */ + + /** + * Test finding resources. + */ + public void testGetResource() { + assertNull(_loader.getResource("foo")); + _loader.addClassLoader(new FooLoader()); + assertNotNull(_loader.getResource("foo")); + } + + public static Test suite() { + return new TestSuite(TestMultiClassLoader.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } + + private static final class FooLoader extends ClassLoader { + protected Class findClass(String name) throws ClassNotFoundException { + if ("foo".equals(name)) { + return Integer.class; + } + + throw new ClassNotFoundException(name); + } + + protected URL findResource(String name) { + try { + if ("foo".equals(name)) { + return new URL("file:///dev/null"); + } + } catch (Exception e) { + } + + return null; + } + } +} diff --git a/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestOptions.java b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestOptions.java new file mode 100755 index 000000000..d436dcfe5 --- /dev/null +++ b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestOptions.java @@ -0,0 +1,218 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util; + +import junit.framework.*; + +import junit.textui.*; + +import java.util.*; + + +/** + *

Tests the {@link Options} type.

+ * + * @author Abe White + */ +public class TestOptions extends TestCase { + private Options _opts = null; + private String[] _args = new String[] { + "-int", "10", "-boolean", "-string", "STR,STR2", "-range1", "10,100", + "-range2", "10", "-fieldVal", "20", "-FieldVal2", "30", + "-inner.nullInner.range1", "10,100", "arg1", "arg2", "arg3" + }; + + public TestOptions(String test) { + super(test); + } + + public void setUp() { + Properties defs = new Properties(); + defs.setProperty("default", "value"); + _opts = new Options(defs); + _args = _opts.setFromCmdLine(_args); + } + + /** + * Test command-line parsing. + */ + public void testCmdLineParsing() { + assertEquals(3, _args.length); + assertEquals("arg1", _args[0]); + assertEquals("arg2", _args[1]); + assertEquals("arg3", _args[2]); + + assertEquals("10", _opts.getProperty("int")); + assertEquals("true", _opts.getProperty("boolean")); + assertEquals("STR,STR2", _opts.getProperty("string")); + assertEquals("20", _opts.getProperty("fieldVal")); + assertEquals("30", _opts.getProperty("FieldVal2")); + assertEquals("10,100", _opts.getProperty("range1")); + assertEquals("10", _opts.getProperty("range2")); + assertEquals("10,100", _opts.getProperty("inner.nullInner.range1")); + assertEquals("value", _opts.getProperty("default")); + + _args = _opts.setFromCmdLine(new String[] { "-default", "newValue" }); + assertEquals(0, _args.length); + assertEquals("newValue", _opts.getProperty("default")); + } + + /** + * Tests the setting of option values into bean objects. + */ + public void testSetObject() { + Inner inner = new Inner(); + _opts.setInto(inner); + + assertEquals(10, inner.getInt()); + assertTrue(inner.getBoolean()); + assertEquals("STR,STR2", inner.getString()); + assertEquals(20, inner.fieldVal); + assertEquals(30, inner.fieldVal2); + assertEquals(10, inner.getRange1()[0]); + assertEquals(100, inner.getRange1()[1]); + assertEquals(10, inner.getRange2()[0]); + assertEquals(0, inner.getRange2()[1]); + assertEquals("value", inner.getDefault()); + + assertEquals(10, inner.getInner().getNullInner().getRange1()[0]); + assertEquals(100, inner.getInner().getNullInner().getRange1()[1]); + + inner = new Inner(); + + Options opts = new Options(); + opts.setProperty("inner", Inner2.class.getName()); + opts.setInto(inner); + assertEquals(Inner2.class, inner.getInner().getClass()); + + inner = new Inner(); + opts = new Options(); + opts.setProperty("mixed", "STR,1"); + opts.setInto(inner); + assertEquals(1, inner.getInt()); + assertEquals("STR", inner.getString()); + } + + public static Test suite() { + return new TestSuite(TestOptions.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } + + /** + * Used internally for testing; must be public so Options can + * construct it. + */ + public static class Inner { + public int fieldVal = 0; + public int fieldVal2 = 0; + private int _int = 0; + private boolean _boolean = false; + private String _string = null; + private String _default = null; + private Inner _inner = null; + private Inner _nullInner = null; + private int[] _range1 = new int[2]; + private int[] _range2 = new int[2]; + + public Inner() { + } + + public int getInt() { + return _int; + } + + public void setInt(int i) { + _int = i; + } + + public boolean getBoolean() { + return _boolean; + } + + public void setBoolean(boolean b) { + _boolean = b; + } + + public String getString() { + return _string; + } + + public void setString(String s) { + _string = s; + } + + public String getDefault() { + return _default; + } + + public void setDefault(String s) { + _default = s; + } + + public int[] getRange1() { + return _range1; + } + + public void setRange1(int min, int max) { + _range1[0] = min; + _range1[1] = max; + } + + public int[] getRange2() { + return _range2; + } + + public void setRange2(int min, int max) { + _range2[0] = min; + _range2[1] = max; + } + + public void setMixed(String s, int i) { + _int = i; + _string = s; + } + + public Inner getInner() { + if (_inner == null) { + _inner = new Inner(); + } + + return _inner; + } + + public void setInner(Inner in) { + _inner = in; + } + + public Inner getNullInner() { + return _nullInner; + } + + public void setNullInner(Inner in) { + _nullInner = in; + } + } + + /** + * Used internally for testing; must be public so Options can + * construct it. + */ + public static class Inner2 extends Inner { + } +} diff --git a/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestParameterTemplate.java b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestParameterTemplate.java new file mode 100755 index 000000000..e432bebe1 --- /dev/null +++ b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestParameterTemplate.java @@ -0,0 +1,62 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util; + +import junit.framework.*; + +import junit.textui.*; + + +/** + *

Tests the {@link ParameterTemplate} utility class.

+ * + * @author Abe White + */ +public class TestParameterTemplate extends TestCase { + private ParameterTemplate templ = new ParameterTemplate(); + + public TestParameterTemplate(String test) { + super(test); + } + + public void testParameters() { + templ.append("{foo$${foo}bar{${bar}}biz baz$"); + templ.append("{booz}booz${java.io.tmpdir}{ack}"); + + templ.setParameter("foo", "X"); + templ.setParameter("bar", "Y"); + templ.setParameter("booz", "Z"); + + String tmpdir = System.getProperty("java.io.tmpdir"); + assertEquals("{foo$Xbar{Y}biz bazZbooz" + tmpdir + "{ack}", + templ.toString()); + + templ.clearParameters(); + templ.setParameter("foo", "AA"); + templ.setParameter("bar", "BB"); + templ.setParameter("booz", "CC"); + assertEquals("{foo$AAbar{BB}biz bazCCbooz" + tmpdir + "{ack}", + templ.toString()); + } + + public static Test suite() { + return new TestSuite(TestParameterTemplate.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } +} diff --git a/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestPropertiesParser.java b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestPropertiesParser.java new file mode 100755 index 000000000..3d293f1f1 --- /dev/null +++ b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestPropertiesParser.java @@ -0,0 +1,406 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util; + +import junit.framework.*; + +import org.apache.openjpa.lib.util.FormatPreservingProperties.*; + +import java.io.*; + +import java.util.*; + + +// things to test: +// - delimiters in keys +// - escape chars, including \:, \= in files (as generated by Properties) +// - unicode +// - non-String keys / vals +// - list() method behavior +public class TestPropertiesParser extends TestCase { + public void testSimpleProperties() throws IOException { + StringBuffer buf = new StringBuffer(); + buf.append("key: value\n"); + buf.append("key2: value2"); // no EOL -- this is intentional + + Properties p = toProperties(buf.toString()); + assertProperties(new String[][] { + { "key", "value" }, + { "key2", "value2" } + }, p); + } + + public void testComments() throws IOException { + StringBuffer buf = new StringBuffer(); + buf.append("# this is a comment\n"); + buf.append(" # another one, with leading whitespace \n"); + buf.append(" # and more with interesting whitespace \n"); + buf.append("! and with a ! delimiter\n"); + buf.append("! and with escape \t chars\n"); + buf.append("#and a comment with no whitespace\n"); + + Properties p = toProperties(buf.toString()); + assertEquals(0, p.size()); + } + + public void testMixedContent() throws IOException { + StringBuffer buf = new StringBuffer(); + buf.append("# this is a comment\n"); + buf.append(" # another one, with leading whitespace \n"); + buf.append("foo: bar#baz\n"); + buf.append("! and with a ! delimiter\n"); + buf.append("! and with escape \t chars\n"); + + Properties p = toProperties(buf.toString()); + assertProperties(new String[][] { + { "foo", "bar#baz" } + }, p); + } + + public void testMultiLineInput() throws IOException { + String s = "foo: bar\\\n" + "more line goes here"; + Properties p = toProperties(s); + assertProperties(new String[][] { + { "foo", "barmore line goes here" } + }, p); + } + + public void testEmptyLines() throws IOException { + Properties p = toProperties("\nfoo: bar\n\nbaz: val"); + assertProperties(new String[][] { + { "foo", "bar" }, + { "baz", "val" } + }, p); + } + + public void testAddProperties() throws IOException { + // intentionally left out the trailing end line + String s = "foo: bar\nbaz: val"; + Properties p = toProperties(s); + assertProperties(new String[][] { + { "foo", "bar" }, + { "baz", "val" } + }, p); + + p.put("new-key", "val1"); + p.put("new-key-2", "val2"); + p.put("another-new-key", "val3"); + assertRoundTrip(s + "\nnew-key: val1\nnew-key-2: val2\n" + + "another-new-key: val3\n", p); + } + + public void testAddAndMutateProperties() throws IOException { + // intentionally left out the trailing end line + Properties p = toProperties("foo: bar\nbaz: val"); + assertProperties(new String[][] { + { "foo", "bar" }, + { "baz", "val" } + }, p); + + p.put("new-key", "new value"); + p.put("foo", "barbar"); + assertRoundTrip("foo: barbar\nbaz: val\nnew-key: new value\n", p); + } + + public void testEscapedEquals() throws IOException { + Properties p = toProperties("foo=bar\\=WARN,baz\\=TRACE"); + assertProperties(new String[][] { + { "foo", "bar=WARN,baz=TRACE" } + }, p); + } + + public void testLineTypes() throws IOException { + StringBuffer buf = new StringBuffer(); + buf.append(" !comment\n \t \nname = no\n " + + "#morec\tomm\\\nents\n\n dog=no\\cat \nburps " + + ":\ntest=\ndate today\n\n\nlong\\\n value=tryin \\\n " + + "gto\n4:vier\nvier :4"); + + Properties p = toProperties(buf.toString()); + assertProperties(new String[][] { + { "name", "no" }, + { "ents", "" }, + { "dog", "nocat " }, + { "burps", "" }, + { "test", "" }, + { "date", "today" }, + { "longvalue", "tryin gto" }, + { "4", "vier" }, + { "vier", "4" }, + }, p); + } + + public void testSpecialChars() throws Throwable { + testSpecialChars(false, true); + testSpecialChars(true, true); + testSpecialChars(false, false); + testSpecialChars(true, false); + } + + /** + * Test that special characters work. + * + * @param formattingProps if true, test against the + * FormatPreservingProperties, otherwise test + * against a normal Properties instance (for + * validation of the test case). + * @param value whether to test the key or the value + */ + public void testSpecialChars(boolean formattingProps, boolean value) + throws Throwable { + List valueList = new ArrayList(Arrays.asList( + new String[] { + "xxyy", "xx\\yy", "xx\nyy", "xx\\nyy", "xx\tyy", + "xx\\tyy", "xx\ryy", "xx\\ryy", "xx\fyy", "xx\\fyy", + "xx\r\n\\\t\r\t\nyy", "xx\\r\n\\\t\\r\t\\nyy", + "xx\r\n\\\\\\\\\\\\\\\\\\\\\\\\\\\t\r\t\nyy", + "C:\\Program Files\\Some Application\\OpenJPA\\My File.dat", + })); + + // also store every individual character + for (char c = 'a'; c < 'Z'; c++) { + valueList.add(new String(new char[] { c })); + valueList.add(new String(new char[] { c, '\\', c })); + valueList.add(new String(new char[] { '\\', c })); + } + + String[] values = (String[]) valueList.toArray(new String[0]); + + final String dummy = "XXX"; + + for (int i = 0; i < values.length; i++) { + // test special characters in either keys or values + String val = value ? values[i] : dummy; + String key = value ? dummy : values[i]; + + Properties p = formattingProps ? new FormatPreservingProperties() + : new Properties(); + + if (p instanceof FormatPreservingProperties) { + // set these properties so we behave the same way as + // java.util.Properties + ((FormatPreservingProperties) p).setDefaultEntryDelimiter('='); + ((FormatPreservingProperties) p).setAddWhitespaceAfterDelimiter(false); + } + + p.setProperty(key, val); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + p.store(out, null); + + Properties copy = new Properties(); + copy.setProperty(key, val); + + ByteArrayOutputStream copyOut = new ByteArrayOutputStream(); + copy.store(copyOut, null); + + p = formattingProps ? new FormatPreservingProperties() + : new Properties(); + + InputStream in = new BufferedInputStream(new ByteArrayInputStream( + out.toByteArray())); + + try { + // make sure that the 2 properties serialized are the same + String copyOutString = stripComments(copyOut.toByteArray()); + String outString = stripComments(out.toByteArray()); + assertEquals(copyOutString, outString); + + p.load(in); + + assertNotNull("Property \"" + key + "\" was null", + p.getProperty(key)); + assertEquals(val.trim(), p.getProperty(key).trim()); + } catch (Throwable ioe) { + if (!formattingProps) { + throw ioe; + } + + // bug (1211, ioe, + // "Cannot store backslash in FormatPreservingProperties"); + throw ioe; + } + } + } + + static Character randomChar() { + char[] TEST_CHAR_ARRAY = new char[] { + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '1', '2', '3', '4', '5', '6', '7', '8', '9' + }; + + return new Character(TEST_CHAR_ARRAY[(int) (Math.random() * TEST_CHAR_ARRAY.length)]); + } + + static String randomString(int len) { + StringBuffer buf = new StringBuffer(); + + for (int i = 0; i < ((int) (Math.random() * len) + 1); i++) + buf.append(randomChar()); + + return buf.toString(); + } + + public void testEquivalentStore() throws IOException { + Properties p1 = new Properties(); + FormatPreservingProperties p2 = new FormatPreservingProperties(); + + ((FormatPreservingProperties) p2).setDefaultEntryDelimiter('='); + ((FormatPreservingProperties) p2).setAddWhitespaceAfterDelimiter(false); + + String[] values = new String[] { + "x", "x\ny", "x\\ny", "x\ty", "x\\ty", "x\fy", "x\\fy", "x\ry", + "x\\ry", "C:\\Foo Bar\\Baz", randomString(5).replace('a', '\\'), + randomString(500).replace('a', '\\'), + randomString(5000).replace('a', '\\'), + }; + + for (int i = 0; i < values.length; i++) { + p1.clear(); + p2.clear(); + + p1.setProperty("xxx", values[i]); + p2.setProperty("xxx", values[i]); + + ByteArrayOutputStream out1 = new ByteArrayOutputStream(); + ByteArrayOutputStream out2 = new ByteArrayOutputStream(); + + p1.store(out1, null); + p2.store(out2, null); + + String s1 = new String(out1.toByteArray()); + String s2 = new String(out2.toByteArray()); + + assertTrue("Expected <" + s1 + "> but was <" + s2 + ">", + s1.indexOf(s2) != -1); + } + } + + static String stripComments(byte[] bytes) throws IOException { + BufferedReader reader = new BufferedReader(new InputStreamReader( + new ByteArrayInputStream(bytes))); + StringBuffer sbuf = new StringBuffer(); + String line; + + while ((line = reader.readLine()) != null) { + // skip comments + if (line.trim().startsWith("#")) { + continue; + } + + sbuf.append(line); + sbuf.append('\n'); + } + + return sbuf.toString(); + } + + public void testDuplicateProperties() throws IOException { + FormatPreservingProperties p = new FormatPreservingProperties(); + + try { + toProperties("foo=bar\nfoo=baz", p); + fail("expected duplicate keys to cause exception"); + } catch (DuplicateKeyException e) { + // expected + } + + // now test the expected behavior when duplicates are allowed. + p = new FormatPreservingProperties(); + p.setAllowDuplicates(true); + toProperties("foo=bar\nfoo=baz", p); + assertProperties(new String[][] { + { "foo", "baz" } + }, p); + } + + public void testMultipleLoads() throws IOException { + String props = "foo=bar\nbaz=quux"; + String props2 = "a=b\nc=d"; + Properties vanilla = new Properties(); + vanilla.load(new BufferedInputStream(new StringBufferInputStream(props))); + vanilla.load(new BufferedInputStream( + new StringBufferInputStream(props2))); + + Properties p = new FormatPreservingProperties(); + p.load(new BufferedInputStream(new StringBufferInputStream(props))); + p.load(new BufferedInputStream(new StringBufferInputStream(props2))); + assertPropertiesSame(vanilla, p); + } + + protected FormatPreservingProperties toProperties(String s) + throws IOException { + return toProperties(s, new FormatPreservingProperties()); + } + + protected FormatPreservingProperties toProperties(String s, + FormatPreservingProperties p) throws IOException { + Properties vanilla = new Properties(); + vanilla.load(new StringBufferInputStream(s)); + + p.load(new StringBufferInputStream(s)); + assertRoundTrip(s, p); + + assertPropertiesSame(vanilla, p); + + return p; + } + + private void assertRoundTrip(String s, Properties p) + throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + p.store(out, null); + assertEquals(s, out.toString()); + + // also check serializable + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + new ObjectOutputStream(bout).writeObject(p); + + try { + FormatPreservingProperties deserialized = (FormatPreservingProperties) new ObjectInputStream(new ByteArrayInputStream( + bout.toByteArray())).readObject(); + assertEquals(p, deserialized); + + out = new ByteArrayOutputStream(); + deserialized.store(out, null); + assertEquals(s, out.toString()); + } catch (ClassNotFoundException cnfe) { + fail(cnfe + ""); + } + } + + public static void assertEquals(String expected, String actual) { + if (expected == actual) { + return; + } + + if ((expected == null) || !expected.equals(actual)) { + fail("Expected <" + expected + "> but was <" + actual + ">"); + } + } + + private void assertPropertiesSame(Properties vanilla, Properties p) { + assertEquals(vanilla, p); + } + + protected void assertProperties(String[][] strings, Properties p) { + for (int i = 0; i < strings.length; i++) + assertEquals(strings[i][1], p.get(strings[i][0])); + + assertEquals(strings.length, p.size()); + } +} diff --git a/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestReferenceSet.java b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestReferenceSet.java new file mode 100755 index 000000000..1094668eb --- /dev/null +++ b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestReferenceSet.java @@ -0,0 +1,135 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util; + +import junit.framework.*; + +import junit.textui.*; + +import java.util.*; + + +/** + *

Tests the {@link ReferenceHashSet}.

+ * + * @author Abe White + */ +public class TestReferenceSet extends TestCase { + private ReferenceHashSet _coll = new ReferenceHashSet(ReferenceHashSet.WEAK); + private Object _heldValue = new Integer(2); + + public TestReferenceSet(String test) { + super(test); + } + + public void setUp() { + _coll.add(_heldValue); + _coll.add(new Integer(1)); + } + + /** + * Tests basic add/contains/remove functionality. + */ + public void testBasics() { + Collection coll = new ReferenceHashSet(ReferenceHashSet.WEAK); + assertEquals(0, coll.size()); + assertTrue(!coll.contains("foo")); + + assertTrue(coll.add("foo")); + assertEquals(1, coll.size()); + assertTrue(coll.contains("foo")); + assertEquals("foo", coll.iterator().next()); + + assertTrue(!coll.remove("bar")); + assertEquals(1, coll.size()); + assertTrue(coll.remove("foo")); + assertEquals(0, coll.size()); + assertTrue(coll.isEmpty()); + } + + /** + * Test that values with strong references are not gc'd. + */ + public void testHeldReference() { + if (JavaVersions.VERSION >= 5) { + return; + } + + System.gc(); + System.gc(); + assertTrue(_coll.contains(_heldValue)); + } + + /** + * Test that weak references are gc'd. + */ + public void testWeakReference() { + if (JavaVersions.VERSION >= 5) { + return; + } + + System.gc(); + System.gc(); + assertTrue(!_coll.contains(new Integer(1))); + assertEquals(1, _coll.size()); + + int size = 0; + + for (Iterator itr = _coll.iterator(); itr.hasNext(); size++) + assertEquals(_heldValue, itr.next()); + + assertEquals(1, size); + + // run a mutator method to ensure expired elements are removed + _coll.add("foo"); + assertEquals(2, _coll.size()); + assertTrue(_coll.contains("foo")); + assertTrue(_coll.contains(_heldValue)); + assertTrue(!_coll.contains(new Integer(1))); + } + + /** + * Test that values that have been replaced aren't expired. + */ + public void testChangeValue() { + if (JavaVersions.VERSION >= 5) { + return; + } + + Object held = new Integer(1); + assertTrue(_coll.remove(held)); + assertTrue(_coll.add(held)); + System.gc(); + System.gc(); + + // run a mutator to clear expired references + _coll.add("foo"); + assertTrue(_coll.contains(held)); + } + + /** + * Used to test inherited functionality. + */ + private static final class Node { + public int hashCode() { + return 1; + } + + public boolean equals(Object other) { + return true; + } + } +} diff --git a/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestSimpleRegex.java b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestSimpleRegex.java new file mode 100755 index 000000000..91be3eac0 --- /dev/null +++ b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestSimpleRegex.java @@ -0,0 +1,70 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util; + +import junit.framework.*; + +import junit.textui.*; + + +/** + *

Tests simple regex for use in in-memory query execution.

+ * + * @author Greg Campbell + */ +public class TestSimpleRegex extends TestCase { + private boolean matchExpr(String target, String expr, boolean caseInsens) { + SimpleRegex re = new SimpleRegex(expr, caseInsens); + + return re.matches(target); + } + + public void testWildcards() { + assertTrue(matchExpr("Hello", "Hello", false)); + assertFalse(matchExpr("Hello", "Bye", false)); + assertFalse(matchExpr("Hello", "ByeBye", false)); + assertFalse(matchExpr("Hello", "Hellooo", false)); + assertFalse(matchExpr("Hello", "HHello", false)); + assertTrue(matchExpr("Hello", "H.llo", false)); + assertTrue(matchExpr("Hello", "Hell.*", false)); + assertTrue(matchExpr("Yo Hello", ".*ello", false)); + assertTrue(matchExpr("Hello", ".*ello", false)); + assertTrue(matchExpr("Hello", ".*ell.*", false)); + assertTrue(matchExpr("Hellow", ".*ell.*", false)); + assertTrue(matchExpr("Hello", "Hel.*lo", false)); + assertTrue(matchExpr("HelYolo", "Hel.*lo", false)); + assertTrue(matchExpr("Hello", "H.*lo", false)); + assertFalse(matchExpr("Hellowe", "H.*lo", false)); + assertTrue(matchExpr("Hello", "h.*lo", true)); + assertFalse(matchExpr("Hello", "h.*lo", false)); + assertTrue(matchExpr("The quick brown fox jumped over the lazy dog", + "The .*brown.*dog", false)); + assertTrue(matchExpr("The quick brown fox jumped over the lazy dog", + "The .*br.wn.*d.g", false)); + assertTrue(matchExpr("the quick BRown fox jumped over the lazy dog", + "The .*br.wn.*d.g", true)); + assertFalse(matchExpr("The quick brown fox jumped over the lazy dog", + "The .*brown.*dogg", false)); + assertFalse(matchExpr("The quick brown fox jumped over the lazy dog", + "TThe .*brown.*dogg", false)); + + assertFalse(matchExpr("Yo Hellow", ".*ello", false)); + } + + public static void main(String[] args) { + TestRunner.run(TestSimpleRegex.class); + } +} diff --git a/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestTemporaryClassLoader.java b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestTemporaryClassLoader.java new file mode 100755 index 000000000..6c5762bd5 --- /dev/null +++ b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestTemporaryClassLoader.java @@ -0,0 +1,41 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util; + +import junit.framework.*; + +import java.util.*; + + +public class TestTemporaryClassLoader extends TestCase { + public void testTemporaryClassLoader() throws Exception { + Set s = new HashSet(); + + for (int i = 0; i < 2; i++) { + ClassLoader loader = new TemporaryClassLoader(getClass() + .getClassLoader()); + Class cls = Class.forName("org.apache.openjpa.lib.util.TempClass", + true, loader); + s.add(cls); + assertEquals(loader, cls.getClassLoader()); + assertEquals(i + 1, s.size()); + } + } +} + + +class TempClass { +} diff --git a/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestTypedProperties.java b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestTypedProperties.java new file mode 100755 index 000000000..7cc87988b --- /dev/null +++ b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestTypedProperties.java @@ -0,0 +1,110 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util; + +import junit.framework.*; + +import junit.textui.*; + + +/** + *

Tests the {@link TypedProperties} type.

+ * + * @author Abe White + */ +public class TestTypedProperties extends TestCase { + private TypedProperties _props = null; + private TypedProperties _defs = null; + + public TestTypedProperties(String test) { + super(test); + } + + public void setUp() { + _props = new TypedProperties(); + _props.setProperty("bool", "true"); + _props.setProperty("int", "1"); + _props.setProperty("long", "2"); + _props.setProperty("float", "1.1"); + _props.setProperty("double", "2.2"); + + _defs = new TypedProperties(_props); + } + + /** + * Tests basic typed gets. + */ + public void testTypes() { + assertTrue(_props.getBooleanProperty("bool")); + assertEquals(1, _props.getIntProperty("int")); + assertEquals(2L, _props.getLongProperty("long")); + assertEquals(1.1F, _props.getFloatProperty("float"), 0.01F); + assertEquals(2.2D, _props.getDoubleProperty("double"), 0.01D); + assertEquals("2.2", _props.getProperty("double")); + } + + /** + * Tests the defaults returned for missing keys. + */ + public void testNoDefaults() { + assertTrue(!_props.getBooleanProperty("bool2")); + assertEquals(0, _props.getIntProperty("int2")); + assertEquals(0L, _props.getLongProperty("long2")); + assertEquals(0F, _props.getFloatProperty("float2"), 0F); + assertEquals(0D, _props.getDoubleProperty("double2"), 0D); + assertEquals(null, _props.getProperty("double2")); + } + + /** + * Tests the defaults returned by keys found in the default + * backing properties instance. + */ + public void testDefaults() { + assertTrue(_defs.getBooleanProperty("bool")); + assertEquals(1, _defs.getIntProperty("int")); + assertEquals(2L, _defs.getLongProperty("long")); + assertEquals(1.1F, _defs.getFloatProperty("float"), 0.01F); + assertEquals(2.2D, _defs.getDoubleProperty("double"), 0.01D); + assertEquals("2.2", _defs.getProperty("double")); + } + + /** + * Tests that given defaults works. + */ + public void testGivenDefaults() { + assertTrue(_props.getBooleanProperty("bool2", true)); + assertEquals(1, _props.getIntProperty("int2", 1)); + assertEquals(2L, _props.getLongProperty("long2", 2L)); + assertEquals(1.1F, _props.getFloatProperty("float2", 1.1F), 0.01F); + assertEquals(2.2D, _props.getDoubleProperty("double2", 2.2D), 0.01D); + assertEquals("2.2", _props.getProperty("double2", "2.2")); + + assertTrue(_defs.getBooleanProperty("bool", false)); + assertEquals(1, _defs.getIntProperty("int", 2)); + assertEquals(2L, _defs.getLongProperty("long", 3L)); + assertEquals(1.1F, _defs.getFloatProperty("float", 2.2F), 0.01F); + assertEquals(2.2D, _defs.getDoubleProperty("double", 3.3D), 0.01D); + assertEquals("2.2", _defs.getProperty("double", "3.3")); + } + + public static Test suite() { + return new TestSuite(TestTypedProperties.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } +} diff --git a/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestUUIDGenerator.java b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestUUIDGenerator.java new file mode 100755 index 000000000..86250eecd --- /dev/null +++ b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestUUIDGenerator.java @@ -0,0 +1,42 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util; + +import junit.framework.*; + +import java.util.*; + + +/** + *

Test UUID generation.

+ * + * @author Abe White + */ +public class TestUUIDGenerator extends TestCase { + public void testUniqueString() { + Set seen = new HashSet(); + + for (int i = 0; i < 1000; i++) + assertTrue(seen.add(UUIDGenerator.nextString())); + } + + public void testUniqueHex() { + Set seen = new HashSet(); + + for (int i = 0; i < 1000; i++) + assertTrue(seen.add(UUIDGenerator.nextHex())); + } +} diff --git a/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/concurrent/TestConcurrentMap.java b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/concurrent/TestConcurrentMap.java new file mode 100755 index 000000000..a4cd1a1b6 --- /dev/null +++ b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/concurrent/TestConcurrentMap.java @@ -0,0 +1,155 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.concurrent; + +import junit.framework.*; + +import junit.textui.*; + +import org.apache.openjpa.lib.util.*; + +import java.util.*; + + +/** + *

Tests the methods of {@link ConcurrentMap}.

+ * + * @author Abe White + */ +public class TestConcurrentMap extends TestCase { + private static final int ENTRIES = 333; + private static final int SLEEP = 3; + private ConcurrentMap[] _maps = new ConcurrentMap[] { + new ConcurrentHashMap(), + new ConcurrentReferenceHashMap(ReferenceMap.HARD, ReferenceMap.HARD), + }; + + public void setUp() { + for (int i = 0; i < ENTRIES; i++) { + for (int j = 0; j < _maps.length; j++) { + int key = (j * ENTRIES) + i; + _maps[j].put(new Integer(key), "v" + key); + } + } + } + + public void testRemoveRandom() { + Set keys = new TreeSet(); + + for (int i = 0; i < ENTRIES; i++) + for (int j = 0; j < _maps.length; j++) + assertTrue(removeRandom(_maps[j], keys)); + + postRemoveTest(keys); + } + + private static boolean removeRandom(ConcurrentMap map, Set keys) { + Map.Entry rem = map.removeRandom(); + + return (rem != null) && rem.getValue().equals("v" + rem.getKey()) && + keys.add(rem.getKey()); + } + + private void postRemoveTest(Set keys) { + for (int i = 0; i < _maps.length; i++) { + assertTrue(_maps[i].isEmpty()); + assertTrue(!_maps[i].containsKey(new Integer((ENTRIES * i) + i))); + } + + assertEquals(keys.toString(), ENTRIES * _maps.length, keys.size()); + } + + public synchronized void testRemoveRandomThreaded() + throws InterruptedException { + Set keys = Collections.synchronizedSet(new TreeSet()); + RemoveRandomRunnable[] runs = new RemoveRandomRunnable[ENTRIES * _maps.length]; + + for (int i = 0; i < ENTRIES; i++) + for (int j = 0; j < _maps.length; j++) + runs[(j * ENTRIES) + i] = new RemoveRandomRunnable(_maps[j], + keys); + + for (int i = 0; i < runs.length; i++) + new Thread(runs[i]).start(); + + Thread.currentThread().sleep(SLEEP * ENTRIES * _maps.length); + + for (int i = 0; i < runs.length; i++) { + assertTrue(String.valueOf(i), !runs[i].error); + + if (runs[i].interrupted) { + throw new InterruptedException(String.valueOf(i)); + } + } + + postRemoveTest(keys); + } + + public void testIterate() { + iterationTest(false); + } + + private List iterationTest(boolean random) { + Set keys = new TreeSet(); + List ordered = new ArrayList(200); + + for (int i = 0; i < _maps.length; i++) { + Iterator itr = (random) ? _maps[i].randomEntryIterator() + : _maps[i].entrySet().iterator(); + + while (itr.hasNext()) { + Map.Entry entry = (Map.Entry) itr.next(); + assertEquals("v" + entry.getKey(), entry.getValue()); + assertTrue(keys + ":: " + _maps[i].getClass() + "::" + + entry.getKey() + "::" + entry.getValue(), + keys.add(entry.getKey())); + ordered.add(entry.getKey()); + } + } + + assertEquals(keys.toString(), ENTRIES * _maps.length, keys.size()); + + return ordered; + } + + public void testRandomIterate() { + List l1 = iterationTest(true); + List l2 = iterationTest(true); + assertTrue(!l1.equals(l2)); + } + + private static class RemoveRandomRunnable implements Runnable { + public boolean error = false; + public boolean interrupted = false; + private final ConcurrentMap _map; + private final Set _keys; + + public RemoveRandomRunnable(ConcurrentMap map, Set keys) { + _map = map; + _keys = keys; + } + + public synchronized void run() { + try { + Thread.currentThread().sleep((long) (Math.random() * SLEEP)); + } catch (InterruptedException ie) { + interrupted = true; + } + + error = !removeRandom(_map, _keys); + } + } +} diff --git a/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/testlocalizer/LocalizerTestHelper.java b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/testlocalizer/LocalizerTestHelper.java new file mode 100755 index 000000000..7033caaf8 --- /dev/null +++ b/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/testlocalizer/LocalizerTestHelper.java @@ -0,0 +1,28 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.testlocalizer; + + +/** + *

Helper class used so that the localizer.properties file in this package + * can be accessed. The properties cannot be in the same package (serp.util) + * as the main tester class because other utilities in the source line might + * eventually want to use a localizer.properties file for that package.

+ * + * @author Abe White + */ +public class LocalizerTestHelper { +} diff --git a/openjpa-lib/src/test/resources/localizer.properties b/openjpa-lib/src/test/resources/localizer.properties new file mode 100755 index 000000000..678863203 --- /dev/null +++ b/openjpa-lib/src/test/resources/localizer.properties @@ -0,0 +1 @@ +test.systemlocal systemvalue1 diff --git a/openjpa-lib/src/test/resources/org/apache/openjpa/lib/conf/test/localizer.properties b/openjpa-lib/src/test/resources/org/apache/openjpa/lib/conf/test/localizer.properties new file mode 100755 index 000000000..25dea07de --- /dev/null +++ b/openjpa-lib/src/test/resources/org/apache/openjpa/lib/conf/test/localizer.properties @@ -0,0 +1,12 @@ +testKey-name: name +testKey-desc: desc +testKey-type: General +sysKey-name: name +sysKey-desc: desc +sysKey-type: General +pluginKey-name: name +pluginKey-desc: desc +pluginKey-type: General +objectKey-name: name +objectKey-desc: desc +objectKey-type: General diff --git a/openjpa-lib/src/test/resources/org/apache/openjpa/lib/util/testlocalizer/localizer.properties b/openjpa-lib/src/test/resources/org/apache/openjpa/lib/util/testlocalizer/localizer.properties new file mode 100755 index 000000000..930196f0f --- /dev/null +++ b/openjpa-lib/src/test/resources/org/apache/openjpa/lib/util/testlocalizer/localizer.properties @@ -0,0 +1,2 @@ +test.local1 value1 +test.local2 value2 {0} sep {1} diff --git a/openjpa-lib/src/test/resources/org/apache/openjpa/lib/util/testlocalizer/localizer_de_DE.properties b/openjpa-lib/src/test/resources/org/apache/openjpa/lib/util/testlocalizer/localizer_de_DE.properties new file mode 100755 index 000000000..93b786788 --- /dev/null +++ b/openjpa-lib/src/test/resources/org/apache/openjpa/lib/util/testlocalizer/localizer_de_DE.properties @@ -0,0 +1,2 @@ +test.local1 value1_de +test.local2 value2_de {0} sep {1} diff --git a/pom.xml b/pom.xml new file mode 100755 index 000000000..12069c696 --- /dev/null +++ b/pom.xml @@ -0,0 +1,179 @@ + + 4.0.0 + org.apache.openjpa + openjpa + pom + + OpenJPA + OpenJPA + 0.0.1 + http://incubator.apache.org/projects/openjpa + + jira + http://issues.apache.org/jira/browse/OPENJPA + + 2006 + + + OpenJPA Developer List + open-jpa-dev-subscribe@incubator.apache.org + open-jpa-dev-unsubscribe@incubator.apache.org + open-jpa-dev@incubator.apache.org + http://mail-archives.apache.org/mod_mbox/incubator-open-jpa-dev/ + + + OpenJPA Commits List + open-jpa-commits-subscribe@incubator.apache.org + open-jpa-commits-unsubscribe@incubator.apache.org + open-jpa-commits@incubator.apache.org + http://mail-archives.apache.org/mod_mbox/incubator-open-jpa-commits/ + + + + + Patrick Linskey + plinskey + BEA Systems, Inc. + plinskey@bea.com + + + Abe White + awhite + BEA Systems, Inc. + awhite@bea.com + + + Steve Kim + stkim + BEA Systems, Inc. + stkim@bea.com + + + Marc Prud'hommeaux + mprudhom + BEA Systems, Inc. + mprudhom@bea.com + + + + + Apache Software License 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + Apache Software Foundation + http://www.apache.org + + + scm:svn:http://svn.apache.org/repos/asf/incubator/openjpa/${pom.artifactId} + scm:svn:https://svn.apache.org/repos/asf/incubator/openjpa/${pom.artifactId} + https://svn.apache.org/repos/asf/incubator/openjpa/${pom.artifactId} + + + + openjpa-lib + serp + + + + + + central + Maven Repository Switchboard + http://www.ibiblio.org/maven2 + + + + + swami + Swami repository + http://m2.ngbw.org + + + + + java-net + dev.java.net repository + https://maven-repository.dev.java.net/nonav/repository + legacy + + + + + + + + junit + junit + 3.8.1 + compile + + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.5 + 1.5 + + + + + + + + + + + + maven-surefire-plugin + + + org.codehaus.mojo + taglist-maven-plugin + + + org.codehaus.mojo + jxr-maven-plugin + + true + + + + org.apache.maven.plugins + maven-javadoc-plugin + + true + true + + http://java.sun.com/j2se/1.5.0/docs/api + http://java.sun.com/javaee/5/docs/api + http://jakarta.apache.org/commons/collections/api-release + + + + + + + + bea-internal + Internal BEA OpenJPA test site + file:///${user.home}/web/devel/openjpa/dist + + + bea-internal + Internal BEA OpenJPA test sit + file:///${user.home}/web/devel/openjpa/site + + + + diff --git a/serp/pom.xml b/serp/pom.xml new file mode 100755 index 000000000..5bb716319 --- /dev/null +++ b/serp/pom.xml @@ -0,0 +1,41 @@ + + 4.0.0 + org.apache.openjpa + serp + jar + + Serp + Serp + http://incubator.apache.org/projects/openjpa + + + org.apache.openjpa + openjpa + 0.0.1 + + + + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.3 + 1.3 + + + + + + + + + + diff --git a/serp/src/main/java/serp/bytecode/ArrayInstruction.java b/serp/src/main/java/serp/bytecode/ArrayInstruction.java new file mode 100755 index 000000000..613678710 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/ArrayInstruction.java @@ -0,0 +1,37 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import java.util.*; + + +/** + *

Any array load or store instruction. This class has + * no functionality beyond the {@link TypedInstruction} but is provided + * so that users can easily identify array instructions in code if + * need be.

+ * + * @author Abe White + */ +public abstract class ArrayInstruction extends TypedInstruction { + ArrayInstruction(Code owner) { + super(owner); + } + + ArrayInstruction(Code owner, int opcode) { + super(owner, opcode); + } +} diff --git a/serp/src/main/java/serp/bytecode/ArrayLoadInstruction.java b/serp/src/main/java/serp/bytecode/ArrayLoadInstruction.java new file mode 100755 index 000000000..ab83f7b96 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/ArrayLoadInstruction.java @@ -0,0 +1,131 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + + +/** + *

Loads a value from an array onto the stack.

+ * + * @author Abe White + */ +public class ArrayLoadInstruction extends ArrayInstruction { + private static final Class[][] _mappings = new Class[][] { + { boolean.class, int.class }, + { void.class, int.class }, + }; + + ArrayLoadInstruction(Code owner) { + super(owner); + } + + ArrayLoadInstruction(Code owner, int opcode) { + super(owner, opcode); + } + + public int getLogicalStackChange() { + switch (getOpcode()) { + case Constants.NOP: + return 0; + + default: + return -1; + } + } + + public int getStackChange() { + switch (getOpcode()) { + case Constants.DALOAD: + case Constants.LALOAD: + case Constants.NOP: + return 0; + + default: + return -1; + } + } + + public String getTypeName() { + switch (getOpcode()) { + case Constants.IALOAD: + return int.class.getName(); + + case Constants.LALOAD: + return long.class.getName(); + + case Constants.FALOAD: + return float.class.getName(); + + case Constants.DALOAD: + return double.class.getName(); + + case Constants.AALOAD: + return Object.class.getName(); + + case Constants.BALOAD: + return byte.class.getName(); + + case Constants.CALOAD: + return char.class.getName(); + + case Constants.SALOAD: + return short.class.getName(); + + default: + return null; + } + } + + public TypedInstruction setType(String type) { + type = mapType(type, _mappings, true); + + if (type == null) { + return (TypedInstruction) setOpcode(Constants.NOP); + } + + switch (type.charAt(0)) { + case 'i': + return (TypedInstruction) setOpcode(Constants.IALOAD); + + case 'l': + return (TypedInstruction) setOpcode(Constants.LALOAD); + + case 'f': + return (TypedInstruction) setOpcode(Constants.FALOAD); + + case 'd': + return (TypedInstruction) setOpcode(Constants.DALOAD); + + case 'b': + return (TypedInstruction) setOpcode(Constants.BALOAD); + + case 'c': + return (TypedInstruction) setOpcode(Constants.CALOAD); + + case 's': + return (TypedInstruction) setOpcode(Constants.SALOAD); + + default: + return (TypedInstruction) setOpcode(Constants.AALOAD); + } + } + + public void acceptVisit(BCVisitor visit) { + visit.enterArrayLoadInstruction(this); + visit.exitArrayLoadInstruction(this); + } +} diff --git a/serp/src/main/java/serp/bytecode/ArrayState.java b/serp/src/main/java/serp/bytecode/ArrayState.java new file mode 100755 index 000000000..135224d39 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/ArrayState.java @@ -0,0 +1,94 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import java.util.*; + + +/** + *

State implementing the behavior of an array class.

+ * + * @author Abe White + */ +class ArrayState extends State { + private String _name = null; + private String _componentName = null; + + public ArrayState(String name, String componentName) { + _name = name; + _componentName = componentName; + } + + public int getMagic() { + return Constants.VALID_MAGIC; + } + + public int getMajorVersion() { + return Constants.MAJOR_VERSION; + } + + public int getMinorVersion() { + return Constants.MINOR_VERSION; + } + + public int getAccessFlags() { + return Constants.ACCESS_PUBLIC | Constants.ACCESS_FINAL; + } + + public int getIndex() { + return 0; + } + + public int getSuperclassIndex() { + return 0; + } + + public Collection getInterfacesHolder() { + return Collections.EMPTY_LIST; + } + + public Collection getFieldsHolder() { + return Collections.EMPTY_LIST; + } + + public Collection getMethodsHolder() { + return Collections.EMPTY_LIST; + } + + public Collection getAttributesHolder() { + return Collections.EMPTY_LIST; + } + + public String getName() { + return _name; + } + + public String getSuperclassName() { + return Object.class.getName(); + } + + public String getComponentName() { + return _componentName; + } + + public boolean isPrimitive() { + return false; + } + + public boolean isArray() { + return true; + } +} diff --git a/serp/src/main/java/serp/bytecode/ArrayStoreInstruction.java b/serp/src/main/java/serp/bytecode/ArrayStoreInstruction.java new file mode 100755 index 000000000..79eea5bd1 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/ArrayStoreInstruction.java @@ -0,0 +1,133 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + + +/** + *

Store a value from the stack into an array.

+ * + * @author Abe White + */ +public class ArrayStoreInstruction extends ArrayInstruction { + private static final Class[][] _mappings = new Class[][] { + { boolean.class, int.class }, + { void.class, int.class }, + }; + + ArrayStoreInstruction(Code owner) { + super(owner); + } + + ArrayStoreInstruction(Code owner, int opcode) { + super(owner, opcode); + } + + public int getLogicalStackChange() { + switch (getOpcode()) { + case Constants.NOP: + return 0; + + default: + return -3; + } + } + + public int getStackChange() { + switch (getOpcode()) { + case Constants.DASTORE: + case Constants.LASTORE: + return -4; + + case Constants.NOP: + return 0; + + default: + return -3; + } + } + + public String getTypeName() { + switch (getOpcode()) { + case Constants.IASTORE: + return int.class.getName(); + + case Constants.LASTORE: + return long.class.getName(); + + case Constants.FASTORE: + return float.class.getName(); + + case Constants.DASTORE: + return double.class.getName(); + + case Constants.AASTORE: + return Object.class.getName(); + + case Constants.BASTORE: + return byte.class.getName(); + + case Constants.CASTORE: + return char.class.getName(); + + case Constants.SASTORE: + return short.class.getName(); + + default: + return null; + } + } + + public TypedInstruction setType(String type) { + type = mapType(type, _mappings, true); + + if (type == null) { + return (TypedInstruction) setOpcode(Constants.NOP); + } + + switch (type.charAt(0)) { + case 'i': + return (TypedInstruction) setOpcode(Constants.IASTORE); + + case 'l': + return (TypedInstruction) setOpcode(Constants.LASTORE); + + case 'f': + return (TypedInstruction) setOpcode(Constants.FASTORE); + + case 'd': + return (TypedInstruction) setOpcode(Constants.DASTORE); + + case 'b': + return (TypedInstruction) setOpcode(Constants.BASTORE); + + case 'c': + return (TypedInstruction) setOpcode(Constants.CASTORE); + + case 's': + return (TypedInstruction) setOpcode(Constants.SASTORE); + + default: + return (TypedInstruction) setOpcode(Constants.AASTORE); + } + } + + public void acceptVisit(BCVisitor visit) { + visit.enterArrayStoreInstruction(this); + visit.exitArrayStoreInstruction(this); + } +} diff --git a/serp/src/main/java/serp/bytecode/Attribute.java b/serp/src/main/java/serp/bytecode/Attribute.java new file mode 100755 index 000000000..1b03800f2 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/Attribute.java @@ -0,0 +1,148 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import serp.bytecode.visitor.*; + +import serp.util.*; + +import java.io.*; + +import java.lang.reflect.*; + +import java.util.*; + + +/** + *

In bytecode attributes are used to represent anything that is not + * part of the class structure. This includes the source file name, code of + * methods, the line number table, etc. All attributes contain at a minimum + * an immutable name that also determines the attribute's type.

+ * + * @author Abe White + */ +public abstract class Attribute extends Attributes implements VisitAcceptor { + private int _nameIndex = 0; + private Attributes _owner = null; + + Attribute(int nameIndex, Attributes owner) { + _owner = owner; + _nameIndex = nameIndex; + } + + /** + * Create an attribute of the appropriate type based on the + * the attribute name. + */ + static Attribute create(String name, Attributes owner) { + int nameIndex = owner.getPool().findUTF8Entry(name, true); + + try { + Class type = Class.forName("serp.bytecode." + name); + Constructor cons = type.getDeclaredConstructor(new Class[] { + int.class, Attributes.class + }); + + return (Attribute) cons.newInstance(new Object[] { + Numbers.valueOf(nameIndex), owner + }); + } catch (Throwable t) { + return new UnknownAttribute(nameIndex, owner); + } + } + + /** + * Return the {@link Attributes} that owns this attribute. The entity + * might be a {@link BCClass}, {@link BCField}, {@link BCMethod}, or other + * attribute. + */ + public Attributes getOwner() { + return _owner; + } + + /** + * Return the index in the {@link ConstantPool} of the {@link UTF8Entry} + * holding the name of this attribute. + */ + public int getNameIndex() { + return _nameIndex; + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return ((UTF8Entry) getPool().getEntry(_nameIndex)).getValue(); + } + + public Project getProject() { + return _owner.getProject(); + } + + public ConstantPool getPool() { + return _owner.getPool(); + } + + public ClassLoader getClassLoader() { + return _owner.getClassLoader(); + } + + public boolean isValid() { + return _owner != null; + } + + Collection getAttributesHolder() { + return Collections.EMPTY_LIST; + } + + /** + * Invalidate this attribute. + */ + void invalidate() { + _owner = null; + } + + /** + * Return the length of the bytecode representation of this attribute + * in bytes, excluding the name index. + */ + int getLength() { + return 0; + } + + /** + * Copy the information from the given attribute to this one. Does + * nothing by default. + */ + void read(Attribute other) { + } + + /** + * Read the attribute bytecode from the given stream, up to length + * bytes, excluding the name index. Does nothing by default. + */ + void read(DataInput in, int length) throws IOException { + } + + /** + * Write the attribute bytecode to the given stream, up to length bytes, + * excluding the name index. Does nothing by default. + */ + void write(DataOutput out, int length) throws IOException { + } +} diff --git a/serp/src/main/java/serp/bytecode/Attributes.java b/serp/src/main/java/serp/bytecode/Attributes.java new file mode 100755 index 000000000..2bff18f43 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/Attributes.java @@ -0,0 +1,217 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import serp.bytecode.visitor.*; + +import java.io.*; + +import java.util.*; + + +/** + *

Abstract superclass for all bytecode entities that hold attributes.

+ * + * @author Abe White + */ +public abstract class Attributes implements BCEntity { + /** + * Return all the attributes owned by this entity. + * + * @return all owned attributes, or empty array if none + */ + public Attribute[] getAttributes() { + Collection attrs = getAttributesHolder(); + + return (Attribute[]) attrs.toArray(new Attribute[attrs.size()]); + } + + /** + * Return the attribute with the given name. If multiple attributes + * share the name, which is returned is undefined. + */ + public Attribute getAttribute(String name) { + Collection attrs = getAttributesHolder(); + Attribute attr; + + for (Iterator itr = attrs.iterator(); itr.hasNext();) { + attr = (Attribute) itr.next(); + + if (attr.getName().equals(name)) { + return attr; + } + } + + return null; + } + + /** + * Return all attributes with the given name. + * + * @return the matching attributes, or empty array if none + */ + public Attribute[] getAttributes(String name) { + List matches = new LinkedList(); + + Collection attrs = getAttributesHolder(); + Attribute attr; + + for (Iterator itr = attrs.iterator(); itr.hasNext();) { + attr = (Attribute) itr.next(); + + if (attr.getName().equals(name)) { + matches.add(attr); + } + } + + return (Attribute[]) matches.toArray(new Attribute[matches.size()]); + } + + /** + * Set the attributes for this entity; this method is useful for importing + * all attributes from another entity. Set to null or empty array if none. + */ + public void setAttributes(Attribute[] attrs) { + clearAttributes(); + + if (attrs != null) { + for (int i = 0; i < attrs.length; i++) + addAttribute(attrs[i]); + } + } + + /** + * Import an attribute from another entity, or make a copy of one + * on this entity. + */ + public Attribute addAttribute(Attribute attr) { + Attribute newAttr = addAttribute(attr.getName()); + newAttr.read(attr); + + return newAttr; + } + + /** + * Add an attribute of the given type. + */ + public Attribute addAttribute(String name) { + Attribute attr = Attribute.create(name, this); + getAttributesHolder().add(attr); + + return attr; + } + + /** + * Clear all attributes from this entity. + */ + public void clearAttributes() { + Collection attrs = getAttributesHolder(); + Attribute attr; + + for (Iterator itr = attrs.iterator(); itr.hasNext();) { + attr = (Attribute) itr.next(); + itr.remove(); + attr.invalidate(); + } + } + + /** + * Remove all attributes with the given name from this entity. + * + * @return true if an attribute was removed, false otherwise + */ + public boolean removeAttribute(String name) { + return removeAttribute(getAttribute(name)); + } + + /** + * Remove the given attribute. After being removed, the attribute + * is invalid, and the result of any operations on it are undefined. + * + * @return true if the attribute was removed, false otherwise + */ + public boolean removeAttribute(Attribute attribute) { + if ((attribute == null) || !getAttributesHolder().remove(attribute)) { + return false; + } + + attribute.invalidate(); + + return true; + } + + /** + * Convenience method to be called by BCEntities when being visited + * by a {@link BCVisitor}; this method will allow the visitor to visit all + * attributes of this entity. + */ + void visitAttributes(BCVisitor visit) { + Attribute attr; + + for (Iterator itr = getAttributesHolder().iterator(); itr.hasNext();) { + attr = (Attribute) itr.next(); + visit.enterAttribute(attr); + attr.acceptVisit(visit); + visit.exitAttribute(attr); + } + } + + /** + * Build the attribute list from the given stream. + * Relies on the ability of attributes to read themselves, and + * requires access to the constant pool, which must already by read. + */ + void readAttributes(DataInput in) throws IOException { + Collection attrs = getAttributesHolder(); + attrs.clear(); + + Attribute attribute; + String name; + + for (int i = in.readUnsignedShort(); i > 0; i--) { + name = ((UTF8Entry) getPool().getEntry(in.readUnsignedShort())).getValue(); + attribute = addAttribute(name); + attribute.read(in, in.readInt()); + } + } + + /** + * Writes all the owned attributes to the given stream. + * Relies on the ability of attributes to write themselves. + */ + void writeAttributes(DataOutput out) throws IOException { + Collection attrs = getAttributesHolder(); + out.writeShort(attrs.size()); + + Attribute attribute; + int length; + + for (Iterator itr = attrs.iterator(); itr.hasNext();) { + attribute = (Attribute) itr.next(); + out.writeShort(attribute.getNameIndex()); + length = attribute.getLength(); + out.writeInt(length); + attribute.write(out, length); + } + } + + /** + * Return the collection used to hold the attributes of this entity. + */ + abstract Collection getAttributesHolder(); +} diff --git a/serp/src/main/java/serp/bytecode/BCClass.java b/serp/src/main/java/serp/bytecode/BCClass.java new file mode 100755 index 000000000..d9e5e6caf --- /dev/null +++ b/serp/src/main/java/serp/bytecode/BCClass.java @@ -0,0 +1,1878 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import serp.bytecode.visitor.*; + +import serp.util.*; + +import java.io.*; + +import java.net.*; + +import java.util.*; + + +/** + *

The BCClass represents a class object in the bytecode framework, in many + * ways mirroring the {@link Class} class of Java reflection. The represented + * class might be a primitive, array, existing object type, or some new user- + * defined type. As with most entities in the bytecode framework, the BCClass + * contains methods to manipulate the low-level state of the class (constant + * pool indexes, etc), but these can and should be ignored in + * favor of the available high-level methods.

+ * + *

A BCClass instance is loaded from a {@link Project} and remains + * attached to that project for its lifetime. If a BCClass is removed from + * its project, the result of any further operations on the class are + * undefined.

+ * + *

Note that if a BCClass represents a primitive or array type, all of the + * available mutator methods and any methods that access the constant pool + * will throw {@link UnsupportedOperationException}s.

+ * + * @author Abe White + */ +public class BCClass extends Attributes implements VisitAcceptor { + private Project _project = null; + private State _state = null; + private ClassLoader _loader = null; + + /** + * Hide constructor. For use by the owning project only. + */ + BCClass(Project project) { + _project = project; + } + + /** + * Set the class state. For use by the owning project only. + */ + void setState(State state) { + _state = state; + } + + /** + * Invalidate this class. + */ + void invalidate() { + _project = null; + _state = State.INVALID; + } + + ////////////////// + // I/O operations + ////////////////// + + /** + * Initialize from the class definition in the given file. For use by + * the owning project only. + */ + void read(File classFile, ClassLoader loader) throws IOException { + InputStream in = new FileInputStream(classFile); + + try { + read(in, loader); + } finally { + in.close(); + } + } + + /** + * Initialize from the class definition in the given stream. For use by + * the owning project only. + */ + void read(InputStream instream, ClassLoader loader) + throws IOException { + DataInput in = new DataInputStream(instream); + + // header information + _state.setMagic(in.readInt()); + _state.setMinorVersion(in.readUnsignedShort()); + _state.setMajorVersion(in.readUnsignedShort()); + + // constant pool + _state.getPool().read(in); + + // access flags + _state.setAccessFlags(in.readUnsignedShort()); + + // class, super class, interfaces + _state.setIndex(in.readUnsignedShort()); + _state.setSuperclassIndex(in.readUnsignedShort()); + + Collection interfaces = _state.getInterfacesHolder(); + interfaces.clear(); + + int interfaceCount = in.readUnsignedShort(); + + for (int i = 0; i < interfaceCount; i++) + interfaces.add(Numbers.valueOf(in.readUnsignedShort())); + + // fields + Collection fields = _state.getFieldsHolder(); + fields.clear(); + + int fieldCount = in.readUnsignedShort(); + BCField field; + + for (int i = 0; i < fieldCount; i++) { + field = new BCField(this); + fields.add(field); + field.read(in); + } + + // methods + Collection methods = _state.getMethodsHolder(); + methods.clear(); + + int methodCount = in.readUnsignedShort(); + BCMethod method; + + for (int i = 0; i < methodCount; i++) { + method = new BCMethod(this); + methods.add(method); + method.read(in); + } + + readAttributes(in); + _loader = loader; + } + + /** + * Initialize from the bytecode of the definition of the given class. + * For use by the owning project only. + */ + void read(Class type) throws IOException { + // find out the length of the package name + int dotIndex = type.getName().lastIndexOf('.') + 1; + + // strip the package off of the class name + String className = type.getName().substring(dotIndex); + + // attempt to get the class file for the class as a stream + InputStream in = type.getResourceAsStream(className + ".class"); + + try { + read(in, type.getClassLoader()); + } finally { + in.close(); + } + } + + /** + * Initialize from the given parsed bytecode. + * For use by the owning project only. + */ + void read(BCClass orig) { + try { + ByteArrayInputStream in = new ByteArrayInputStream(orig.toByteArray()); + read(in, orig.getClassLoader()); + in.close(); + } catch (IOException ioe) { + throw new RuntimeException(ioe.toString()); + } + } + + /** + * Write the class bytecode to the .class file in the proper directory of + * the CLASSPATH. The file must exist already, so this method only works + * on existing classes. + */ + public void write() throws IOException { + String name = getName(); + int dotIndex = name.lastIndexOf('.') + 1; + name = name.substring(dotIndex); + + Class type = getType(); + + // attempt to get the class file for the class as a stream; + // we need to use the url decoder in case the target directory + // has spaces in it + OutputStream out = new FileOutputStream(URLDecoder.decode( + type.getResource(name + ".class").getFile())); + + try { + write(out); + } finally { + out.close(); + } + } + + /** + * Write the class bytecode to the specified file. + */ + public void write(File classFile) throws IOException { + OutputStream out = new FileOutputStream(classFile); + + try { + write(out); + } finally { + out.close(); + } + } + + /** + * Write the class bytecode to the specified stream. + */ + public void write(OutputStream outstream) throws IOException { + DataOutput out = new DataOutputStream(outstream); + + // header information + out.writeInt(_state.getMagic()); + out.writeShort(_state.getMinorVersion()); + out.writeShort(_state.getMajorVersion()); + + // constant pool + _state.getPool().write(out); + + // access flags + out.writeShort(_state.getAccessFlags()); + + // class, super class + out.writeShort(_state.getIndex()); + out.writeShort(_state.getSuperclassIndex()); + + // interfaces + Collection interfaces = _state.getInterfacesHolder(); + out.writeShort(interfaces.size()); + + for (Iterator itr = interfaces.iterator(); itr.hasNext();) + out.writeShort(((Number) itr.next()).intValue()); + + // fields + Collection fields = _state.getFieldsHolder(); + out.writeShort(fields.size()); + + for (Iterator itr = fields.iterator(); itr.hasNext();) + ((BCField) itr.next()).write(out); + + // methods + Collection methods = _state.getMethodsHolder(); + out.writeShort(methods.size()); + + for (Iterator itr = methods.iterator(); itr.hasNext();) + ((BCMethod) itr.next()).write(out); + + // attributes + writeAttributes(out); + } + + /** + * Return the bytecode of this class as a byte array, possibly for use + * in a custom {@link ClassLoader}. + */ + public byte[] toByteArray() { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try { + write(out); + out.flush(); + + return out.toByteArray(); + } catch (IOException ioe) { + throw new RuntimeException(ioe.toString()); + } finally { + try { + out.close(); + } catch (IOException ioe) { + } + } + } + + ///////////////////// + // Access operations + ///////////////////// + + /** + * Return the magic number for this class; if this is a valid type, this + * should be equal to {@link Constants#VALID_MAGIC} (the default value). + */ + public int getMagic() { + return _state.getMagic(); + } + + /** + * Set the magic number for this class; if this is a valid type, this + * should be equal to {@link Constants#VALID_MAGIC} (the default value). + */ + public void setMagic(int magic) { + _state.setMagic(magic); + } + + /** + * Return the major version of the bytecode spec used for this class. + * JVMs are only required to operate with versions that they understand; + * leaving the default value of {@link Constants#MAJOR_VERSION} is safe. + */ + public int getMajorVersion() { + return _state.getMajorVersion(); + } + + /** + * Set the major version of the bytecode spec used for this class. + * JVMs are only required to operate with versions that they understand; + * leaving the default value of {@link Constants#MAJOR_VERSION} is safe. + */ + public void setMajorVersion(int majorVersion) { + _state.setMajorVersion(majorVersion); + } + + /** + * Get the minor version of the bytecode spec used for this class. + * JVMs are only required to operate with versions that they understand; + * leaving the default value of {@link Constants#MINOR_VERSION} is safe. + */ + public int getMinorVersion() { + return _state.getMinorVersion(); + } + + /** + * Set the minor version of the bytecode spec used for this class. + * JVMs are only required to operate with versions that they understand; + * leaving the default value of {@link Constants#MINOR_VERSION} is safe. + */ + public void setMinorVersion(int minorVersion) { + _state.setMinorVersion(minorVersion); + } + + /** + * Return the access flags for this class as a bit array of + * ACCESS_XXX constants from {@link Constants}. This can be used to + * transfer access flags between classes without getting/setting each + * possible flag. + */ + public int getAccessFlags() { + return _state.getAccessFlags(); + } + + /** + * Set the access flags for this class as a bit array of + * ACCESS_XXX constants from {@link Constants}. This can be used to + * transfer access flags between classes without getting/setting each + * possible flag. + */ + public void setAccessFlags(int access) { + _state.setAccessFlags(access); + } + + /** + * Manipulate the class access flags. + */ + public boolean isPublic() { + return (getAccessFlags() & Constants.ACCESS_PUBLIC) > 0; + } + + /** + * Manipulate the class access flags. + */ + public void makePublic() { + setAccessFlags(getAccessFlags() | Constants.ACCESS_PUBLIC); + } + + /** + * Manipulate the class access flags. + */ + public boolean isPackage() { + return !isPublic(); + } + + /** + * Manipulate the class access flags. + */ + public void makePackage() { + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_PUBLIC); + } + + /** + * Manipulate the class access flags. + */ + public boolean isFinal() { + return (getAccessFlags() & Constants.ACCESS_FINAL) > 0; + } + + /** + * Manipulate the class access flags. + */ + public void setFinal(boolean on) { + if (on) { + setAccessFlags(getAccessFlags() | Constants.ACCESS_FINAL); + } else { + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_FINAL); + } + } + + /** + * Manipulate the class access flags. + */ + public boolean isInterface() { + return (getAccessFlags() & Constants.ACCESS_INTERFACE) > 0; + } + + /** + * Manipulate the class access flags. + */ + public void setInterface(boolean on) { + if (on) { + setAccessFlags(getAccessFlags() | Constants.ACCESS_INTERFACE); + setAbstract(true); + } else { + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_INTERFACE); + } + } + + /** + * Manipulate the class access flags. + */ + public boolean isAbstract() { + return (getAccessFlags() & Constants.ACCESS_ABSTRACT) > 0; + } + + /** + * Manipulate the class access flags. + */ + public void setAbstract(boolean on) { + if (on) { + setAccessFlags(getAccessFlags() | Constants.ACCESS_ABSTRACT); + } else { + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_ABSTRACT); + } + } + + /** + * Return true if this class is a primitive type. + */ + public boolean isPrimitive() { + return _state.isPrimitive(); + } + + /** + * Return true if this class is an array type. + */ + public boolean isArray() { + return _state.isArray(); + } + + ///////////////////////// + // Class name operations + ///////////////////////// + + /** + * Return the {@link ConstantPool} index of the + * {@link ClassEntry} for this class. Returns 0 if the class does not + * have a constant pool (such as a primitive or array). + */ + public int getIndex() { + return _state.getIndex(); + } + + /** + * Set the {@link ConstantPool} index of the {@link ClassEntry} for this + * class. Unlike most other low-level methods, the index + * will be checked against the pool immediately; + * classes must have a valid name at all times. + */ + public void setIndex(int index) { + String oldName = getName(); + String newName = ((ClassEntry) getPool().getEntry(index)).getNameEntry() + .getValue(); + + beforeRename(oldName, newName); + _state.setIndex(index); + } + + /** + * Return the name of this class, including package name. The name will + * be in a form suitable for a {@link Class#forName} call. + */ + public String getName() { + return _state.getName(); + } + + /** + * Return the name of the class only, without package. + */ + public String getClassName() { + String name = _project.getNameCache().getExternalForm(getName(), true); + + return name.substring(name.lastIndexOf('.') + 1); + } + + /** + * Return the package name only, without class, or null if none. + */ + public String getPackageName() { + String name = _project.getNameCache().getExternalForm(getName(), true); + int index = name.lastIndexOf('.'); + + if (index == -1) { + return null; + } + + return name.substring(0, index); + } + + /** + * Set the name of this class, including package name. + */ + public void setName(String name) { + name = _project.getNameCache().getExternalForm(name, false); + + String oldName = getName(); + + // get a reference to the class entry for this class + int index = getIndex(); + + if (index == 0) { + index = getPool().findClassEntry(name, true); + } + + ClassEntry entry = (ClassEntry) getPool().getEntry(index); + + // make sure the rename is ok with the project + beforeRename(oldName, name); + + // reset the name index of the class entry to the new name + int nameIndex = getPool() + .findUTF8Entry(_project.getNameCache() + .getInternalForm(name, false), + true); + entry.setNameIndex(nameIndex); + + // we might have just added a new entry; set the index + _state.setIndex(index); + } + + /** + * Return the {@link Class} object for this class, if it is loadable. + */ + public Class getType() { + return Strings.toClass(getName(), getClassLoader()); + } + + /** + * Return the component type name of this class, or null if not an array. + * The name will be in a form suitable for a {@link Class#forName} call. + */ + public String getComponentName() { + return _state.getComponentName(); + } + + /** + * Return the component type of this class, or null if not an array. + */ + public Class getComponentType() { + String componentName = getComponentName(); + + if (componentName == null) { + return null; + } + + return Strings.toClass(componentName, getClassLoader()); + } + + /** + * Return the component type of this class, or null if not an array. + */ + public BCClass getComponentBC() { + String componentName = getComponentName(); + + if (componentName == null) { + return null; + } + + return getProject().loadClass(componentName, getClassLoader()); + } + + ///////////////////////// + // Superclass operations + ///////////////////////// + + /** + * Return the {@link ConstantPool} index of the + * {@link ClassEntry} for the superclass of this class. Returns -1 if + * the class does not have a constant pool (such as a primitive or array). + */ + public int getSuperclassIndex() { + return _state.getSuperclassIndex(); + } + + /** + * Set the {@link ConstantPool} index of the + * {@link ClassEntry} for the superclass of this class. + */ + public void setSuperclassIndex(int index) { + _state.setSuperclassIndex(index); + } + + /** + * Return the name of the superclass for this class, including package + * name. The name will be in a form suitable for a + * {@link Class#forName} call, or null for types without superclasses. + */ + public String getSuperclassName() { + return _state.getSuperclassName(); + } + + /** + * Return the {@link Class} object for the superclass of this class, if it + * is loadable. Returns null for types without superclasses. + */ + public Class getSuperclassType() { + String name = getSuperclassName(); + + if (name == null) { + return null; + } + + return Strings.toClass(name, getClassLoader()); + } + + /** + * Return the bytecode of the superclass of this class, or + * null for types without superclasses. + */ + public BCClass getSuperclassBC() { + String name = getSuperclassName(); + + if (name == null) { + return null; + } + + return getProject().loadClass(name, getClassLoader()); + } + + /** + * Set the superclass of this class. + */ + public void setSuperclass(String name) { + if (name == null) { + setSuperclassIndex(0); + } else { + setSuperclassIndex(getPool() + .findClassEntry(_project.getNameCache() + .getInternalForm(name, + false), true)); + } + } + + /** + * Set the superclass of this class. + */ + public void setSuperclass(Class type) { + if (type == null) { + setSuperclass((String) null); + } else { + setSuperclass(type.getName()); + } + } + + /** + * Set the superclass of this class. + */ + public void setSuperclass(BCClass type) { + if (type == null) { + setSuperclass((String) null); + } else { + setSuperclass(type.getName()); + } + } + + //////////////////////// + // Interface operations + //////////////////////// + + /** + * Return the list of {@link ConstantPool} indexes of the + * {@link ClassEntry}s describing all the interfaces this class declares + * that it implements/extends. + * + * @return the implmented interfaces, or an empty array if none + */ + public int[] getDeclaredInterfaceIndexes() { + Collection interfaces = _state.getInterfacesHolder(); + int[] indexes = new int[interfaces.size()]; + + Iterator itr = interfaces.iterator(); + + for (int i = 0, max = interfaces.size(); i < max; i++) + indexes[i] = ((Number) itr.next()).intValue(); + + return indexes; + } + + /** + * Set the list of {@link ConstantPool} indexes of the + * {@link ClassEntry}s describing all the interfaces this class declares + * it implements/extends; set to null or an empty array if none. + */ + public void setDeclaredInterfaceIndexes(int[] interfaceIndexes) { + Collection stateIndexes = _state.getInterfacesHolder(); + stateIndexes.clear(); + + for (int i = 0; i < interfaceIndexes.length; i++) + stateIndexes.add(Numbers.valueOf(interfaceIndexes[i])); + } + + /** + * Return the names of the interfaces declared for this class, including + * package names, or an empty array if none. The names will be in a form + * suitable for a {@link Class#forName} call. + */ + public String[] getDeclaredInterfaceNames() { + int[] indexes = getDeclaredInterfaceIndexes(); + String[] names = new String[indexes.length]; + + ClassEntry entry; + + for (int i = 0; i < indexes.length; i++) { + entry = (ClassEntry) getPool().getEntry(indexes[i]); + names[i] = _project.getNameCache() + .getExternalForm(entry.getNameEntry().getValue(), + false); + } + + return names; + } + + /** + * Return the {@link Class} objects for the declared interfaces of this + * class, or an empty array if none. + */ + public Class[] getDeclaredInterfaceTypes() { + String[] names = getDeclaredInterfaceNames(); + Class[] types = new Class[names.length]; + + for (int i = 0; i < names.length; i++) + types[i] = Strings.toClass(names[i], getClassLoader()); + + return types; + } + + /** + * Return the bytecode for the declared interfaces of this class, or an + * empty array if none. + */ + public BCClass[] getDeclaredInterfaceBCs() { + String[] names = getDeclaredInterfaceNames(); + BCClass[] types = new BCClass[names.length]; + + for (int i = 0; i < names.length; i++) + types[i] = getProject().loadClass(names[i], getClassLoader()); + + return types; + } + + /** + * Set the interfaces declared implemented/extended by this class; set to + * null or an empty array if none. + */ + public void setDeclaredInterfaces(String[] interfaces) { + clearDeclaredInterfaces(); + + if (interfaces != null) { + for (int i = 0; i < interfaces.length; i++) + declareInterface(interfaces[i]); + } + } + + /** + * Set the interfaces declared implemented/extended by this class; set to + * null or an empty array if none. + */ + public void setDeclaredInterfaces(Class[] interfaces) { + String[] names = null; + + if (interfaces != null) { + names = new String[interfaces.length]; + + for (int i = 0; i < interfaces.length; i++) + names[i] = interfaces[i].getName(); + } + + setDeclaredInterfaces(names); + } + + /** + * Set the interfaces declared implemented/extended by this class; set to + * null or an empty array if none. + */ + public void setDeclaredInterfaces(BCClass[] interfaces) { + String[] names = null; + + if (interfaces != null) { + names = new String[interfaces.length]; + + for (int i = 0; i < interfaces.length; i++) + names[i] = interfaces[i].getName(); + } + + setDeclaredInterfaces(names); + } + + /** + * Return the names of all unique interfaces implemented by this class, + * including those of all superclasses. The names will be returned in a + * form suitable for a {@link Class#forName} call. + * This method does not recurse into interfaces-of-interfaces. + */ + public String[] getInterfaceNames() { + Collection allNames = new LinkedList(); + String[] names; + + for (BCClass type = this; type != null; + type = type.getSuperclassBC()) { + names = type.getDeclaredInterfaceNames(); + + for (int i = 0; i < names.length; i++) + allNames.add(names[i]); + } + + return (String[]) allNames.toArray(new String[allNames.size()]); + } + + /** + * Return the {@link Class} objects of all unique interfaces implemented + * by this class, including those of all superclasses. + * This method does not recurse into interfaces-of-interfaces. + */ + public Class[] getInterfaceTypes() { + Collection allTypes = new LinkedList(); + Class[] types; + + for (BCClass type = this; type != null; + type = type.getSuperclassBC()) { + types = type.getDeclaredInterfaceTypes(); + + for (int i = 0; i < types.length; i++) + allTypes.add(types[i]); + } + + return (Class[]) allTypes.toArray(new Class[allTypes.size()]); + } + + /** + * Return the bytecode of all unique interfaces implemented by this class, + * including those of all superclasses. + * This method does not recurse into interfaces-of-interfaces. + */ + public BCClass[] getInterfaceBCs() { + Collection allTypes = new LinkedList(); + BCClass[] types; + + for (BCClass type = this; type != null; + type = type.getSuperclassBC()) { + types = type.getDeclaredInterfaceBCs(); + + for (int i = 0; i < types.length; i++) + allTypes.add(types[i]); + } + + return (BCClass[]) allTypes.toArray(new BCClass[allTypes.size()]); + } + + /** + * Clear this class of all interface declarations. + */ + public void clearDeclaredInterfaces() { + _state.getInterfacesHolder().clear(); + } + + /** + * Remove an interface declared by this class. + * + * @return true if the class had the interface, false otherwise + */ + public boolean removeDeclaredInterface(String name) { + String[] names = getDeclaredInterfaceNames(); + Iterator itr = _state.getInterfacesHolder().iterator(); + + for (int i = 0; i < names.length; i++) { + itr.next(); + + if (names[i].equals(name)) { + itr.remove(); + + return true; + } + } + + return false; + } + + /** + * Remove an interface declared by this class. + * + * @return true if the class had the interface, false otherwise + */ + public boolean removeDeclaredInterface(Class type) { + if (type == null) { + return false; + } + + return removeDeclaredInterface(type.getName()); + } + + /** + * Remove an interface declared by this class. + * + * @return true if the class had the interface, false otherwise + */ + public boolean removeDeclaredInterface(BCClass type) { + if (type == null) { + return false; + } + + return removeDeclaredInterface(type.getName()); + } + + /** + * Add an interface to those declared by this class. + */ + public void declareInterface(String name) { + int index = getPool() + .findClassEntry(_project.getNameCache() + .getInternalForm(name, false), + true); + _state.getInterfacesHolder().add(Numbers.valueOf(index)); + } + + /** + * Add an interface to those declared by this class. + */ + public void declareInterface(Class type) { + declareInterface(type.getName()); + } + + /** + * Add an interface to those declared by this class. + */ + public void declareInterface(BCClass type) { + declareInterface(type.getName()); + } + + /** + * Return true if this class or any of its superclasses implement/extend + * the given interface/class. + * This method does not recurse into interfaces-of-interfaces. + */ + public boolean isInstanceOf(String name) { + name = _project.getNameCache().getExternalForm(name, false); + + String[] interfaces = getInterfaceNames(); + + for (int i = 0; i < interfaces.length; i++) + if (interfaces[i].equals(name)) { + return true; + } + + for (BCClass type = this; type != null; + type = type.getSuperclassBC()) + if (type.getName().equals(name)) { + return true; + } + + return false; + } + + /** + * Return true if this class or any of its superclasses implement/extend + * the given interface/class. + * This method does not recurse into interfaces-of-interfaces. + */ + public boolean isInstanceOf(Class type) { + if (type == null) { + return false; + } + + return isInstanceOf(type.getName()); + } + + /** + * Return true if this class or any of its superclasses implement/extend + * the given interface/class. + * This method does not recurse into interfaces-of-interfaces. + */ + public boolean isInstanceOf(BCClass type) { + if (type == null) { + return false; + } + + return isInstanceOf(type.getName()); + } + + ////////////////////// + // Field operations + ////////////////////// + + /** + * Return all the declared fields of this class, or an empty array if none. + */ + public BCField[] getDeclaredFields() { + Collection fields = _state.getFieldsHolder(); + + return (BCField[]) fields.toArray(new BCField[fields.size()]); + } + + /** + * Return the declared field with the given name, or null if none. + */ + public BCField getDeclaredField(String name) { + BCField[] fields = getDeclaredFields(); + + for (int i = 0; i < fields.length; i++) + if (fields[i].getName().equals(name)) { + return fields[i]; + } + + return null; + } + + /** + * Return all the fields of this class, including those of all + * superclasses, or an empty array if none. + */ + public BCField[] getFields() { + Collection allFields = new LinkedList(); + BCField[] fields; + + for (BCClass type = this; type != null; + type = type.getSuperclassBC()) { + fields = type.getDeclaredFields(); + + for (int i = 0; i < fields.length; i++) + allFields.add(fields[i]); + } + + return (BCField[]) allFields.toArray(new BCField[allFields.size()]); + } + + /** + * Return all fields with the given name, including those of all + * superclasses, or an empty array if none. + */ + public BCField[] getFields(String name) { + List matches = new LinkedList(); + BCField[] fields = getFields(); + + for (int i = 0; i < fields.length; i++) + if (fields[i].getName().equals(name)) { + matches.add(fields[i]); + } + + return (BCField[]) matches.toArray(new BCField[matches.size()]); + } + + /** + * Set the fields for this class; this method is useful for importing all + * fields from another class. Set to null or empty array if none. + */ + public void setDeclaredFields(BCField[] fields) { + clearDeclaredFields(); + + if (fields != null) { + for (int i = 0; i < fields.length; i++) + declareField(fields[i]); + } + } + + /** + * Import the information from given field as a new field in this class. + * + * @return the added field + */ + public BCField declareField(BCField field) { + BCField newField = declareField(field.getName(), field.getTypeName()); + newField.setAccessFlags(field.getAccessFlags()); + newField.setAttributes(field.getAttributes()); + + return newField; + } + + /** + * Add a field to this class. + * + * @return the added field + */ + public BCField declareField(String name, String type) { + BCField field = new BCField(this); + _state.getFieldsHolder().add(field); + field.initialize(name, + _project.getNameCache().getInternalForm(type, true)); + + return field; + } + + /** + * Add a field to this class. + * + * @return the added field + */ + public BCField declareField(String name, Class type) { + String typeName = (type == null) ? null : type.getName(); + + return declareField(name, typeName); + } + + /** + * Add a field to this class. + * + * @return the added field + */ + public BCField declareField(String name, BCClass type) { + String typeName = (type == null) ? null : type.getName(); + + return declareField(name, typeName); + } + + /** + * Clear all fields from this class. + */ + public void clearDeclaredFields() { + Collection fields = _state.getFieldsHolder(); + BCField field; + + for (Iterator itr = fields.iterator(); itr.hasNext();) { + field = (BCField) itr.next(); + itr.remove(); + field.invalidate(); + } + } + + /** + * Remove a field from this class. After this method, the removed field + * will be invalid, and the result of any operations on it is undefined. + * + * @return true if this class contained the field, false otherwise + */ + public boolean removeDeclaredField(String name) { + Collection fields = _state.getFieldsHolder(); + BCField field; + + for (Iterator itr = fields.iterator(); itr.hasNext();) { + field = (BCField) itr.next(); + + if (field.getName().equals(name)) { + itr.remove(); + field.invalidate(); + + return true; + } + } + + return false; + } + + /** + * Remove a field from this class. After this method, the removed field + * will be invalid, and the result of any operations on it is undefined. + * + * @return true if this class contained the field, false otherwise + */ + public boolean removeDeclaredField(BCField field) { + if (field == null) { + return false; + } + + return removeDeclaredField(field.getName()); + } + + ////////////////////// + // Method operations + ////////////////////// + + /** + * Return all methods declared by this class. Constructors and static + * initializers are included. + */ + public BCMethod[] getDeclaredMethods() { + Collection methods = _state.getMethodsHolder(); + + return (BCMethod[]) methods.toArray(new BCMethod[methods.size()]); + } + + /** + * Return the declared method with the given name, or null if none. + * If multiple methods are declared with the given name, which is returned + * is undefined. + * Note that in bytecode, constructors are named <init> + * and static initializers are named <clinit>. + */ + public BCMethod getDeclaredMethod(String name) { + BCMethod[] methods = getDeclaredMethods(); + + for (int i = 0; i < methods.length; i++) + if (methods[i].getName().equals(name)) { + return methods[i]; + } + + return null; + } + + /** + * Return all the declared methods with the given name, or an empty array + * if none. + * Note that in bytecode, constructors are named <init> + * and static initializers are named <clinit>. + */ + public BCMethod[] getDeclaredMethods(String name) { + Collection matches = new LinkedList(); + BCMethod[] methods = getDeclaredMethods(); + + for (int i = 0; i < methods.length; i++) + if (methods[i].getName().equals(name)) { + matches.add(methods[i]); + } + + return (BCMethod[]) matches.toArray(new BCMethod[matches.size()]); + } + + /** + * Return the declared method with the given name and parameter types, + * or null if none. + * Note that in bytecode, constructors are named <init> + * and static initializers are named <clinit>. + */ + public BCMethod getDeclaredMethod(String name, String[] paramTypes) { + if (paramTypes == null) { + paramTypes = new String[0]; + } + + String[] curParams; + boolean match; + BCMethod[] methods = getDeclaredMethods(); + + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals(name)) { + curParams = methods[i].getParamNames(); + + if (curParams.length != paramTypes.length) { + continue; + } + + match = true; + + for (int j = 0; j < paramTypes.length; j++) { + if (!curParams[j].equals(_project.getNameCache() + .getExternalForm(paramTypes[j], + false))) { + match = false; + + break; + } + } + + if (match) { + return methods[i]; + } + } + } + + return null; + } + + /** + * Return the declared method with the given name and parameter types, + * or null if none. + * Note that in bytecode, constructors are named <init> + * and static initializers are named <clinit>. + */ + public BCMethod getDeclaredMethod(String name, Class[] paramTypes) { + if (paramTypes == null) { + return getDeclaredMethod(name, (String[]) null); + } + + String[] paramNames = new String[paramTypes.length]; + + for (int i = 0; i < paramTypes.length; i++) + paramNames[i] = paramTypes[i].getName(); + + return getDeclaredMethod(name, paramNames); + } + + /** + * Return the declared method with the given name and parameter types, + * or null if none. + * Note that in bytecode, constructors are named <init> + * and static initializers are named <clinit>. + */ + public BCMethod getDeclaredMethod(String name, BCClass[] paramTypes) { + if (paramTypes == null) { + return getDeclaredMethod(name, (String[]) null); + } + + String[] paramNames = new String[paramTypes.length]; + + for (int i = 0; i < paramTypes.length; i++) + paramNames[i] = paramTypes[i].getName(); + + return getDeclaredMethod(name, paramNames); + } + + /** + * Return the methods of this class, including those of all superclasses, + * or an empty array if none. + * The base version of methods that are overridden will be included, as + * will all constructors and static initializers. + * The methods will be ordered from those in the most-specific type up to + * those in {@link Object}. + */ + public BCMethod[] getMethods() { + Collection allMethods = new LinkedList(); + BCMethod[] methods; + + for (BCClass type = this; type != null; + type = type.getSuperclassBC()) { + methods = type.getDeclaredMethods(); + + for (int i = 0; i < methods.length; i++) + allMethods.add(methods[i]); + } + + return (BCMethod[]) allMethods.toArray(new BCMethod[allMethods.size()]); + } + + /** + * Return the methods with the given name, including those of all + * superclasses, or an empty array if none. + * Note that in bytecode, constructors are named <init> + * and static initializers are named <clinit>. + */ + public BCMethod[] getMethods(String name) { + Collection matches = new LinkedList(); + BCMethod[] methods = getMethods(); + + for (int i = 0; i < methods.length; i++) + if (methods[i].getName().equals(name)) { + matches.add(methods[i]); + } + + return (BCMethod[]) matches.toArray(new BCMethod[matches.size()]); + } + + /** + * Return the methods with the given name and parameter types, including + * those of all superclasses, or an empty array if none. + * Note that in bytecode, constructors are named <init> + * and static initializers are named <clinit>. + */ + public BCMethod[] getMethods(String name, String[] paramTypes) { + if (paramTypes == null) { + paramTypes = new String[0]; + } + + String[] curParams; + boolean match; + BCMethod[] methods = getMethods(); + Collection matches = new LinkedList(); + + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals(name)) { + curParams = methods[i].getParamNames(); + + if (curParams.length != paramTypes.length) { + continue; + } + + match = true; + + for (int j = 0; j < paramTypes.length; j++) { + if (!curParams[j].equals(_project.getNameCache() + .getExternalForm(paramTypes[j], + false))) { + match = false; + + break; + } + } + + if (match) { + matches.add(methods[i]); + } + } + } + + return (BCMethod[]) matches.toArray(new BCMethod[matches.size()]); + } + + /** + * Return the methods with the given name and parameter types, including + * those of all superclasses, or an empty array if none. + * Note that in bytecode, constructors are named <init> + * and static initializers are named <clinit>. + */ + public BCMethod[] getMethods(String name, Class[] paramTypes) { + if (paramTypes == null) { + return getMethods(name, (String[]) null); + } + + String[] paramNames = new String[paramTypes.length]; + + for (int i = 0; i < paramTypes.length; i++) + paramNames[i] = paramTypes[i].getName(); + + return getMethods(name, paramNames); + } + + /** + * Return the methods with the given name and parameter types, including + * those of all superclasses, or an empty array if none. + * Note that in bytecode, constructors are named <init> + * and static initializers are named <clinit>. + */ + public BCMethod[] getMethods(String name, BCClass[] paramTypes) { + if (paramTypes == null) { + return getMethods(name, (String[]) null); + } + + String[] paramNames = new String[paramTypes.length]; + + for (int i = 0; i < paramTypes.length; i++) + paramNames[i] = paramTypes[i].getName(); + + return getMethods(name, paramNames); + } + + /** + * Set the methods for this class; this method is useful for importing all + * methods from another class. Set to null or empty array if none. + */ + public void setDeclaredMethods(BCMethod[] methods) { + clearDeclaredMethods(); + + if (methods != null) { + for (int i = 0; i < methods.length; i++) + declareMethod(methods[i]); + } + } + + /** + * Import the information in the given method as a new method of this + * class. + * + * @return the added method + */ + public BCMethod declareMethod(BCMethod method) { + BCMethod newMethod = declareMethod(method.getName(), + method.getReturnName(), method.getParamNames()); + newMethod.setAccessFlags(method.getAccessFlags()); + newMethod.setAttributes(method.getAttributes()); + + return newMethod; + } + + /** + * Add a method to this class. + * Note that in bytecode, constructors are named <init> + * and static initializers are named <clinit>. + * + * @return the added method + */ + public BCMethod declareMethod(String name, String returnType, + String[] paramTypes) { + BCMethod method = new BCMethod(this); + _state.getMethodsHolder().add(method); + method.initialize(name, + _project.getNameCache().getDescriptor(returnType, paramTypes)); + + return method; + } + + /** + * Add a method to this class. + * Note that in bytecode, constructors are named <init> + * and static initializers are named <clinit>. + * + * @return the added method + */ + public BCMethod declareMethod(String name, Class returnType, + Class[] paramTypes) { + String[] paramNames = null; + + if (paramTypes != null) { + paramNames = new String[paramTypes.length]; + + for (int i = 0; i < paramTypes.length; i++) + paramNames[i] = paramTypes[i].getName(); + } + + String returnName = (returnType == null) ? null : returnType.getName(); + + return declareMethod(name, returnName, paramNames); + } + + /** + * Add a method to this class. + * Note that in bytecode, constructors are named <init> + * and static initializers are named <clinit>. + * + * @return the added method + */ + public BCMethod declareMethod(String name, BCClass returnType, + BCClass[] paramTypes) { + String[] paramNames = null; + + if (paramTypes != null) { + paramNames = new String[paramTypes.length]; + + for (int i = 0; i < paramTypes.length; i++) + paramNames[i] = paramTypes[i].getName(); + } + + String returnName = (returnType == null) ? null : returnType.getName(); + + return declareMethod(name, returnName, paramNames); + } + + /** + * Clear all declared methods from this class. + */ + public void clearDeclaredMethods() { + Collection methods = _state.getMethodsHolder(); + BCMethod method; + + for (Iterator itr = methods.iterator(); itr.hasNext();) { + method = (BCMethod) itr.next(); + itr.remove(); + method.invalidate(); + } + } + + /** + * Remove a method from this class. After this method, the removed method + * will be invalid, and the result of any operations on it is undefined. + * If multiple methods match the given name, which is removed is undefined. + * Note that in bytecode, constructors are named <init> + * and static initializers are named <clinit>. + * + * @return true if this class contained the method, false otherwise + */ + public boolean removeDeclaredMethod(String name) { + Collection methods = _state.getMethodsHolder(); + BCMethod method; + + for (Iterator itr = methods.iterator(); itr.hasNext();) { + method = (BCMethod) itr.next(); + + if (method.getName().equals(name)) { + itr.remove(); + method.invalidate(); + + return true; + } + } + + return false; + } + + /** + * Removes a method from this class. After this method, the removed method + * will be invalid, and the result of any operations on it is undefined. + * + * @return true if this class contained the method, false otherwise + */ + public boolean removeDeclaredMethod(BCMethod method) { + if (method == null) { + return false; + } + + return removeDeclaredMethod(method.getName(), method.getParamNames()); + } + + /** + * Removes a method from this class. After this method, the removed method + * will be invalid, and the result of any operations on it is undefined. + * Note that in bytecode, constructors are named <init> + * and static initializers are named <clinit>. + * + * @return true if this class contained the method, false otherwise + */ + public boolean removeDeclaredMethod(String name, String[] paramTypes) { + if (paramTypes == null) { + paramTypes = new String[0]; + } + + String[] curParams; + boolean match; + Collection methods = _state.getMethodsHolder(); + BCMethod method; + + for (Iterator itr = methods.iterator(); itr.hasNext();) { + method = (BCMethod) itr.next(); + + if (method.getName().equals(name)) { + curParams = method.getParamNames(); + + if (curParams.length != paramTypes.length) { + continue; + } + + match = true; + + for (int j = 0; j < paramTypes.length; j++) { + if (!curParams[j].equals(_project.getNameCache() + .getExternalForm(paramTypes[j], + false))) { + match = false; + + break; + } + } + + if (match) { + itr.remove(); + method.invalidate(); + + return true; + } + } + } + + return false; + } + + /** + * Removes a method from this class. After this method, the removed method + * will be invalid, and the result of any operations on it is undefined. + * Note that in bytecode, constructors are named <init> + * and static initializers are named <clinit>. + * + * @return true if this class contained the method, false otherwise + */ + public boolean removeDeclaredMethod(String name, Class[] paramTypes) { + if (paramTypes == null) { + return removeDeclaredMethod(name, (String[]) null); + } + + String[] paramNames = new String[paramTypes.length]; + + for (int i = 0; i < paramTypes.length; i++) + paramNames[i] = paramTypes[i].getName(); + + return removeDeclaredMethod(name, paramNames); + } + + /** + * Removes a method from this class. After this method, the removed method + * will be invalid, and the result of any operations on it is undefined. + * Note that in bytecode, constructors are named <init> + * and static initializers are named <clinit>. + * + * @return true if this class contained the method, false otherwise + */ + public boolean removeDeclaredMethod(String name, BCClass[] paramTypes) { + if (paramTypes == null) { + return removeDeclaredMethod(name, (String[]) null); + } + + String[] paramNames = new String[paramTypes.length]; + + for (int i = 0; i < paramTypes.length; i++) + paramNames[i] = paramTypes[i].getName(); + + return removeDeclaredMethod(name, paramNames); + } + + /////////////////////// + // Convenience methods + /////////////////////// + + /** + * Convenience method to add a default constructor to this class. + * If a default constructor already exists, this method will return it + * without modification. + * This method can only be called if the superclass has been set. + * + * @return the default constructor + */ + public BCMethod addDefaultConstructor() { + BCMethod method = getDeclaredMethod("", (String[]) null); + + if (method != null) { + return method; + } + + method = declareMethod("", void.class, null); + + Code code = method.getCode(true); + code.setMaxStack(1); + code.setMaxLocals(1); + + code.xload().setThis(); + code.invokespecial() + .setMethod(getSuperclassName(), "", "void", null); + code.vreturn(); + + return method; + } + + /** + * Return source file information for the class. + * Acts internally through the {@link Attributes} interface. + * + * @param add if true, a new source file attribute will be added + * if not already present + * @return the source file information, or null if none and the + * add param is set to false + */ + public SourceFile getSourceFile(boolean add) { + SourceFile source = (SourceFile) getAttribute(Constants.ATTR_SOURCE); + + if (!add || (source != null)) { + return source; + } + + return (SourceFile) addAttribute(Constants.ATTR_SOURCE); + } + + /** + * Remove the source file attribute for the class. + * Acts internally through the {@link Attributes} interface. + * + * @return true if there was a file to remove + */ + public boolean removeSourceFile() { + return removeAttribute(Constants.ATTR_SOURCE); + } + + /** + * Return inner classes information for the class. + * Acts internally through the {@link Attributes} interface. + * + * @param add if true, a new inner classes attribute will be added + * if not already present + * @return the inner classes information, or null if none and the + * add param is set to false + */ + public InnerClasses getInnerClasses(boolean add) { + InnerClasses inner = (InnerClasses) getAttribute(Constants.ATTR_INNERCLASS); + + if (!add || (inner != null)) { + return inner; + } + + return (InnerClasses) addAttribute(Constants.ATTR_INNERCLASS); + } + + /** + * Remove the inner classes attribute for the class. + * Acts internally through the {@link Attributes} interface. + * + * @return true if there was an attribute to remove + */ + public boolean removeInnerClasses() { + return removeAttribute(Constants.ATTR_INNERCLASS); + } + + /** + * Convenience method to return deprecation information for the class. + * Acts internally through the {@link Attributes} interface. + */ + public boolean isDeprecated() { + return getAttribute(Constants.ATTR_DEPRECATED) != null; + } + + /** + * Convenience method to set whether this class should be considered + * deprecated. + * Acts internally through the {@link Attributes} interface. + */ + public void setDeprecated(boolean on) { + if (!on) { + removeAttribute(Constants.ATTR_DEPRECATED); + } else if (!isDeprecated()) { + addAttribute(Constants.ATTR_DEPRECATED); + } + } + + /////////////////////////////////// + // Implementation of VisitAcceptor + /////////////////////////////////// + public void acceptVisit(BCVisitor visit) { + visit.enterBCClass(this); + + ConstantPool pool = null; + + try { + pool = _state.getPool(); + } catch (UnsupportedOperationException uoe) { + } + + if (pool != null) { + pool.acceptVisit(visit); + } + + BCField[] fields = getDeclaredFields(); + + for (int i = 0; i < fields.length; i++) { + visit.enterBCMember(fields[i]); + fields[i].acceptVisit(visit); + visit.exitBCMember(fields[i]); + } + + BCMethod[] methods = getDeclaredMethods(); + + for (int i = 0; i < methods.length; i++) { + visit.enterBCMember(methods[i]); + methods[i].acceptVisit(visit); + visit.exitBCMember(methods[i]); + } + + visitAttributes(visit); + visit.exitBCClass(this); + } + + //////////////////////////////// + // Implementation of Attributes + //////////////////////////////// + public Project getProject() { + return _project; + } + + public ConstantPool getPool() { + return _state.getPool(); + } + + public ClassLoader getClassLoader() { + if (_loader != null) { + return _loader; + } + + return Thread.currentThread().getContextClassLoader(); + } + + public boolean isValid() { + return _project != null; + } + + Collection getAttributesHolder() { + return _state.getAttributesHolder(); + } + + /** + * Attempts to change the class name with the owning project. The project + * can reject the change if a class with the given new name already + * exists; therefore this method should be called before the change is + * recorded in the class. + */ + private void beforeRename(String oldName, String newName) { + if ((_project != null) && (oldName != null)) { + _project.renameClass(oldName, newName, this); + } + } +} diff --git a/serp/src/main/java/serp/bytecode/BCClassLoader.java b/serp/src/main/java/serp/bytecode/BCClassLoader.java new file mode 100755 index 000000000..6e3b8edd4 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/BCClassLoader.java @@ -0,0 +1,83 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + + +/** + *

Class loader that will attempt to find requested classes in a given + * {@link Project}.

+ * + * @author Abe White + */ +public class BCClassLoader extends ClassLoader { + private Project _project = null; + + /** + * Constructor. Supply the project to use when looking for classes. + */ + public BCClassLoader(Project project) { + _project = project; + } + + /** + * Constructor. Supply the project to use when looking for classes. + * + * @param parent the parent classoader + */ + public BCClassLoader(Project project, ClassLoader loader) { + super(loader); + _project = project; + } + + /** + * Return this class loader's project. + */ + public Project getProject() { + return _project; + } + + protected Class findClass(String name) throws ClassNotFoundException { + byte[] bytes; + + try { + BCClass type; + + if (!_project.containsClass(name)) { + type = createClass(name); + } else { + type = _project.loadClass(name); + } + + if (type == null) { + throw new ClassNotFoundException(name); + } + + bytes = type.toByteArray(); + } catch (RuntimeException re) { + throw new ClassNotFoundException(re.toString()); + } + + return defineClass(name, bytes, 0, bytes.length); + } + + /** + * Override this method if unfound classes should be created on-the-fly. + * Returns null by default. + */ + protected BCClass createClass(String name) { + return null; + } +} diff --git a/serp/src/main/java/serp/bytecode/BCEntity.java b/serp/src/main/java/serp/bytecode/BCEntity.java new file mode 100755 index 000000000..5d5ac85bd --- /dev/null +++ b/serp/src/main/java/serp/bytecode/BCEntity.java @@ -0,0 +1,49 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + + +/** + *

Interface implemented by all bytecode entities. Entities must be able + * to access the project, constant pool, and class loader of the current + * class.

+ * + * @author Abe White + */ +public interface BCEntity { + /** + * Return the project of the current class. + */ + public Project getProject(); + + /** + * Return the constant pool of the current class. + */ + public ConstantPool getPool(); + + /** + * Return the class loader to use when loading related classes. + */ + public ClassLoader getClassLoader(); + + /** + * Return false if this entity has been removed from its parent; in this + * case the results of any operations on the entity are undefined. + */ + public boolean isValid(); +} diff --git a/serp/src/main/java/serp/bytecode/BCField.java b/serp/src/main/java/serp/bytecode/BCField.java new file mode 100755 index 000000000..aad057a1d --- /dev/null +++ b/serp/src/main/java/serp/bytecode/BCField.java @@ -0,0 +1,166 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import serp.bytecode.visitor.*; + +import serp.util.*; + + +/** + *

A field of a class.

+ * + * @author Abe White + */ +public class BCField extends BCMember implements VisitAcceptor { + BCField(BCClass owner) { + super(owner); + } + + /** + * Manipulate the field access flags. + */ + public boolean isVolatile() { + return (getAccessFlags() & Constants.ACCESS_VOLATILE) > 0; + } + + /** + * Manipulate the field access flags. + */ + public void setVolatile(boolean on) { + if (on) { + setAccessFlags(getAccessFlags() | Constants.ACCESS_VOLATILE); + } else { + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_VOLATILE); + } + } + + /** + * Manipulate the field access flags. + */ + public boolean isTransient() { + return (getAccessFlags() & Constants.ACCESS_TRANSIENT) > 0; + } + + /** + * Manipulate the field access flags. + */ + public void setTransient(boolean on) { + if (on) { + setAccessFlags(getAccessFlags() | Constants.ACCESS_TRANSIENT); + } else { + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_TRANSIENT); + } + } + + /** + * Return the name of the type of this field. The name will be given in + * a form suitable for a {@link Class#forName} call. + * + * @see BCMember#getDescriptor + */ + public String getTypeName() { + return getProject().getNameCache() + .getExternalForm(getDescriptor(), false); + } + + /** + * Return the {@link Class} object for the type of this field. + */ + public Class getType() { + return Strings.toClass(getTypeName(), getClassLoader()); + } + + /** + * Return the bytecode for the type of this field. + */ + public BCClass getTypeBC() { + return getProject().loadClass(getTypeName(), getClassLoader()); + } + + /** + * Set the name of the type of this field. + * + * @see BCMember#setDescriptor + */ + public void setType(String type) { + setDescriptor(type); + } + + /** + * Set the type of this field. + * + * @see BCMember#setDescriptor + */ + public void setType(Class type) { + setType(type.getName()); + } + + /** + * Set the type of this field. + * + * @see BCMember#setDescriptor + */ + public void setType(BCClass type) { + setType(type.getName()); + } + + /** + * Return the constant value information for the field. + * Acts internally through the {@link Attributes} interface. + * + * @param add if true, a new constant value attribute will be added + * if not already present + * @return the constant value information, or null if none and the + * add param is set to false + */ + public ConstantValue getConstantValue(boolean add) { + ConstantValue constant = (ConstantValue) getAttribute(Constants.ATTR_CONST); + + if (!add || (constant != null)) { + return constant; + } + + if (constant == null) { + constant = (ConstantValue) addAttribute(Constants.ATTR_CONST); + } + + return constant; + } + + /** + * Remove the constant value attribute for the field. + * Acts internally through the {@link Attributes} interface. + * + * @return true if there was a value to remove + */ + public boolean removeConstantValue() { + return removeAttribute(Constants.ATTR_CONST); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterBCField(this); + visitAttributes(visit); + visit.exitBCField(this); + } + + void initialize(String name, String descriptor) { + super.initialize(name, descriptor); + makePrivate(); + } +} diff --git a/serp/src/main/java/serp/bytecode/BCMember.java b/serp/src/main/java/serp/bytecode/BCMember.java new file mode 100755 index 000000000..92c80aac3 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/BCMember.java @@ -0,0 +1,399 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import java.io.*; + +import java.util.*; + + +/** + *

A member field or method of a class.

+ * + * @author Abe White + */ +public abstract class BCMember extends Attributes { + private BCClass _owner = null; + private int _access = Constants.ACCESS_PRIVATE; + private int _nameIndex = 0; + private int _descriptorIndex = 0; + private Collection _attrs = new LinkedList(); + + BCMember(BCClass owner) { + _owner = owner; + } + + /** + * Return the {@link BCClass} that declares this member. + */ + public BCClass getDeclarer() { + return _owner; + } + + ///////////////////// + // Access operations + ///////////////////// + + /** + * Return the access flags for this member as a bit array of + * ACCESS_XXX constants from {@link Constants}. This can be used to + * transfer access flags between members without getting/setting each + * possible access flag. Defaults to {@link Constants#ACCESS_PRIVATE} + */ + public int getAccessFlags() { + return _access; + } + + /** + * Set the access flags for this member as a bit array of + * ACCESS_XXX constants from {@link Constants}. This can be used to + * transfer access flags between members without getting/setting each + * possible access flag. Defaults to {@link Constants#ACCESS_PRIVATE} + */ + public void setAccessFlags(int access) { + _access = access; + } + + /** + * Manipulate the member access flags. + */ + public boolean isPublic() { + return (getAccessFlags() & Constants.ACCESS_PUBLIC) > 0; + } + + /** + * Manipulate the member access flags. + */ + public void makePublic() { + setAccessFlags(getAccessFlags() | Constants.ACCESS_PUBLIC); + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_PRIVATE); + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_PROTECTED); + } + + /** + * Manipulate the member access flags. + */ + public boolean isProtected() { + return (getAccessFlags() & Constants.ACCESS_PROTECTED) > 0; + } + + /** + * Manipulate the member access flags. + */ + public void makeProtected() { + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_PUBLIC); + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_PRIVATE); + setAccessFlags(getAccessFlags() | Constants.ACCESS_PROTECTED); + } + + /** + * Manipulate the member access flags. + */ + public boolean isPrivate() { + return (getAccessFlags() & Constants.ACCESS_PRIVATE) > 0; + } + + /** + * Manipulate the member access flags. + */ + public void makePrivate() { + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_PUBLIC); + setAccessFlags(getAccessFlags() | Constants.ACCESS_PRIVATE); + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_PROTECTED); + } + + /** + * Manipulate the member access flags. + */ + public boolean isPackage() { + boolean hasAccess = false; + hasAccess = hasAccess || + ((getAccessFlags() & Constants.ACCESS_PRIVATE) > 0); + hasAccess = hasAccess || + ((getAccessFlags() & Constants.ACCESS_PROTECTED) > 0); + hasAccess = hasAccess || + ((getAccessFlags() & Constants.ACCESS_PUBLIC) > 0); + + return !hasAccess; + } + + /** + * Manipulate the member access flags. + */ + public void makePackage() { + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_PUBLIC); + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_PRIVATE); + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_PROTECTED); + } + + /** + * Manipulate the member access flags. + */ + public boolean isFinal() { + return (getAccessFlags() & Constants.ACCESS_FINAL) > 0; + } + + /** + * Manipulate the member access flags. + */ + public void setFinal(boolean on) { + if (on) { + setAccessFlags(getAccessFlags() | Constants.ACCESS_FINAL); + } else { + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_FINAL); + } + } + + /** + * Manipulate the member access flags. + */ + public boolean isStatic() { + return (getAccessFlags() & Constants.ACCESS_STATIC) > 0; + } + + /** + * Manipulate the member access flags. + */ + public void setStatic(boolean on) { + if (on) { + setAccessFlags(getAccessFlags() | Constants.ACCESS_STATIC); + } else { + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_STATIC); + } + } + + ///////////////////////// + // Descriptor operations + ///////////////////////// + + /** + * Return the index in the class {@link ConstantPool} of the + * {@link UTF8Entry} holding the name of this member. + */ + public int getNameIndex() { + return _nameIndex; + } + + /** + * Set the index in the class {@link ConstantPool} of the + * {@link UTF8Entry} holding the name of this member. + */ + public void setNameIndex(int index) { + String origName = getName(); + _nameIndex = index; + + // change all the references in the owning class + setEntry(origName, getDescriptor()); + } + + /** + * Return the index in the class {@link ConstantPool} of the + * {@link UTF8Entry} holding the descriptor of this member. + */ + public int getDescriptorIndex() { + return _descriptorIndex; + } + + /** + * Set the index in the class {@link ConstantPool} of the + * {@link UTF8Entry} holding the descriptor of this member. + */ + public void setDescriptorIndex(int index) { + String origDesc = getDescriptor(); + _descriptorIndex = index; + + // change all the references in the owning class + setEntry(getName(), origDesc); + } + + /** + * Return the name of this member. + */ + public String getName() { + return ((UTF8Entry) getPool().getEntry(_nameIndex)).getValue(); + } + + /** + * Set the name of this member. + */ + public void setName(String name) { + String origName = getName(); + + // reset the name + _nameIndex = getPool().findUTF8Entry(name, true); + + // change all references in the owning class + setEntry(origName, getDescriptor()); + } + + /** + * Return the descriptor of this member, in internal form. + */ + public String getDescriptor() { + return ((UTF8Entry) getPool().getEntry(_descriptorIndex)).getValue(); + } + + /** + * Set the descriptor of this member. + */ + public void setDescriptor(String desc) { + String origDesc = getDescriptor(); + + // reset the desc + desc = getProject().getNameCache().getInternalForm(desc, true); + _descriptorIndex = getPool().findUTF8Entry(desc, true); + + // change all the references in the owning class + setEntry(getName(), origDesc); + } + + /** + * Resets the {@link ComplexEntry} of the owning class corresponding to + * this member. Changes in the member will therefore propogate to all + * code in the class. + */ + private void setEntry(String origName, String origDesc) { + // find the entry matching this member, if any + String owner = getProject().getNameCache() + .getInternalForm(_owner.getName(), false); + ConstantPool pool = getPool(); + + int index; + + if (this instanceof BCField) { + index = pool.findFieldEntry(origName, origDesc, owner, false); + } else if (!_owner.isInterface()) { + index = pool.findMethodEntry(origName, origDesc, owner, false); + } else { + index = pool.findInterfaceMethodEntry(origName, origDesc, owner, + false); + } + + // change the entry to match the new info; this is dones so + // that refs to the member in code will still be valid after the + // change, without changing any other constants that happened to match + // the old name and/or descriptor + if (index != 0) { + ComplexEntry complex = (ComplexEntry) pool.getEntry(index); + int ntIndex = pool.findNameAndTypeEntry(getName(), getDescriptor(), + true); + complex.setNameAndTypeIndex(ntIndex); + } + } + + /////////////////////// + // Convenience methods + /////////////////////// + + /** + * Convenience method to return deprecation information for the member. + * Acts internally through the {@link Attributes} interface. + */ + public boolean isDeprecated() { + return getAttribute(Constants.ATTR_DEPRECATED) != null; + } + + /** + * Convenience method to set whether this member should be considered + * deprecated. + * Acts internally through the {@link Attributes} interface. + */ + public void setDeprecated(boolean on) { + if (!on) { + removeAttribute(Constants.ATTR_DEPRECATED); + } else if (!isDeprecated()) { + addAttribute(Constants.ATTR_DEPRECATED); + } + } + + /** + * Convenience method to return synthetic information for the member. + * Acts internally through the {@link Attributes} interface. + */ + public boolean isSynthetic() { + return getAttribute(Constants.ATTR_SYNTHETIC) != null; + } + + /** + * Convenience method to set whether this member should be considered + * synthetic. + * Acts internally through the {@link Attributes} interface. + */ + public void setSynthetic(boolean on) { + if (!on) { + removeAttribute(Constants.ATTR_SYNTHETIC); + } else if (!isSynthetic()) { + addAttribute(Constants.ATTR_SYNTHETIC); + } + } + + //////////////////////////////// + // Implementation of Attributes + //////////////////////////////// + public Project getProject() { + return _owner.getProject(); + } + + public ConstantPool getPool() { + return _owner.getPool(); + } + + public ClassLoader getClassLoader() { + return _owner.getClassLoader(); + } + + public boolean isValid() { + return _owner != null; + } + + Collection getAttributesHolder() { + return _attrs; + } + + /** + * Either this method or {@link #read} must be called prior to use + * of this class. The given descriptor must be in internal form. + */ + void initialize(String name, String descriptor) { + _nameIndex = getPool().findUTF8Entry(name, true); + _descriptorIndex = getPool().findUTF8Entry(descriptor, true); + } + + /** + * Used when this member is deleted from its class. + */ + void invalidate() { + _owner = null; + } + + void read(DataInput in) throws IOException { + _access = in.readUnsignedShort(); + _nameIndex = in.readUnsignedShort(); + _descriptorIndex = in.readUnsignedShort(); + + readAttributes(in); + } + + void write(DataOutput out) throws IOException { + out.writeShort(_access); + out.writeShort(_nameIndex); + out.writeShort(_descriptorIndex); + + writeAttributes(out); + } +} diff --git a/serp/src/main/java/serp/bytecode/BCMethod.java b/serp/src/main/java/serp/bytecode/BCMethod.java new file mode 100755 index 000000000..1803590d0 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/BCMethod.java @@ -0,0 +1,498 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + +import serp.util.*; + + +/** + *

A method of a class.

+ * + * @author Abe White + */ +public class BCMethod extends BCMember implements VisitAcceptor { + BCMethod(BCClass owner) { + super(owner); + } + + ///////////////////// + // Access operations + ///////////////////// + + /** + * Manipulate the method access flags. + */ + public boolean isSynchronized() { + return (getAccessFlags() & Constants.ACCESS_SYNCHRONIZED) > 0; + } + + /** + * Manipulate the method access flags. + */ + public void setSynchronized(boolean on) { + if (on) { + setAccessFlags(getAccessFlags() | Constants.ACCESS_SYNCHRONIZED); + } else { + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_SYNCHRONIZED); + } + } + + /** + * Manipulate the method access flags. + */ + public boolean isNative() { + return (getAccessFlags() & Constants.ACCESS_NATIVE) > 0; + } + + /** + * Manipulate the method access flags. + */ + public void setNative(boolean on) { + if (on) { + setAccessFlags(getAccessFlags() | Constants.ACCESS_NATIVE); + } else { + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_NATIVE); + } + } + + /** + * Manipulate the method access flags. + */ + public boolean isAbstract() { + return (getAccessFlags() & Constants.ACCESS_ABSTRACT) > 0; + } + + /** + * Manipulate the method access flags. + */ + public void setAbstract(boolean on) { + if (on) { + setAccessFlags(getAccessFlags() | Constants.ACCESS_ABSTRACT); + } else { + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_ABSTRACT); + } + } + + /** + * Manipulate the method access flags. + */ + public boolean isStrict() { + return (getAccessFlags() & Constants.ACCESS_STRICT) > 0; + } + + /** + * Manipulate the method access flags. + */ + public void setStrict(boolean on) { + if (on) { + setAccessFlags(getAccessFlags() | Constants.ACCESS_STRICT); + } else { + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_STRICT); + } + } + + ///////////////////// + // Return operations + ///////////////////// + + /** + * Return the name of the type returned by this method. The name + * will be given in a form suitable for a {@link Class#forName} call. + * + * @see BCMember#getDescriptor + */ + public String getReturnName() { + return getProject().getNameCache() + .getExternalForm(getProject().getNameCache() + .getDescriptorReturnName(getDescriptor()), + false); + } + + /** + * Return the {@link Class} object for the return type of this method. + * + * @see BCMember#getDescriptor + */ + public Class getReturnType() { + return Strings.toClass(getReturnName(), getClassLoader()); + } + + /** + * Return the bytecode for the return type of this method. + * + * @see BCMember#getDescriptor + */ + public BCClass getReturnBC() { + return getProject().loadClass(getReturnName(), getClassLoader()); + } + + /** + * Set the return type of this method. + */ + public void setReturn(String name) { + setDescriptor(getProject().getNameCache() + .getDescriptor(name, getParamNames())); + } + + /** + * Set the return type of this method. + */ + public void setReturn(Class type) { + setReturn(type.getName()); + } + + /** + * Set the return type of this method. + */ + public void setReturn(BCClass type) { + setReturn(type.getName()); + } + + //////////////////////// + // Parameter operations + //////////////////////// + + /** + * Return the names of all the parameter types for this method. The names + * will be returned in a form suitable for a {@link Class#forName} call. + * + * @see BCMember#getDescriptor + */ + public String[] getParamNames() { + // get the parameter types from the descriptor + String[] params = getProject().getNameCache() + .getDescriptorParamNames(getDescriptor()); + + // convert them to external form + for (int i = 0; i < params.length; i++) + params[i] = getProject().getNameCache() + .getExternalForm(params[i], false); + + return params; + } + + /** + * Return the {@link Class} objects for all the parameter types for this + * method. + * + * @see BCMember#getDescriptor + */ + public Class[] getParamTypes() { + String[] paramNames = getParamNames(); + Class[] params = new Class[paramNames.length]; + + for (int i = 0; i < paramNames.length; i++) + params[i] = Strings.toClass(paramNames[i], getClassLoader()); + + return params; + } + + /** + * Return the bytecode for all the parameter types for this + * method. + * + * @see BCMember#getDescriptor + */ + public BCClass[] getParamBCs() { + String[] paramNames = getParamNames(); + BCClass[] params = new BCClass[paramNames.length]; + + for (int i = 0; i < paramNames.length; i++) + params[i] = getProject().loadClass(paramNames[i], getClassLoader()); + + return params; + } + + /** + * Set the parameter types of this method. + * + * @see BCMember#setDescriptor + */ + public void setParams(String[] names) { + if (names == null) { + names = new String[0]; + } + + setDescriptor(getProject().getNameCache() + .getDescriptor(getReturnName(), names)); + } + + /** + * Set the parameter type of this method. + * + * @see BCMember#setDescriptor + */ + public void setParams(Class[] types) { + if (types == null) { + setParams((String[]) null); + } else { + String[] names = new String[types.length]; + + for (int i = 0; i < types.length; i++) + names[i] = types[i].getName(); + + setParams(names); + } + } + + /** + * Set the parameter type of this method. + * + * @see BCMember#setDescriptor + */ + public void setParams(BCClass[] types) { + if (types == null) { + setParams((String[]) null); + } else { + String[] names = new String[types.length]; + + for (int i = 0; i < types.length; i++) + names[i] = types[i].getName(); + + setParams(names); + } + } + + /** + * Add a parameter type to this method. + */ + public void addParam(String type) { + String[] origParams = getParamNames(); + String[] params = new String[origParams.length + 1]; + + for (int i = 0; i < origParams.length; i++) + params[i] = origParams[i]; + + params[origParams.length] = type; + setParams(params); + } + + /** + * Add a parameter type to this method. + */ + public void addParam(Class type) { + addParam(type.getName()); + } + + /** + * Add a parameter type to this method. + */ + public void addParam(BCClass type) { + addParam(type.getName()); + } + + /** + * Add a parameter type to this method. + * + * @see java.util.List#add(int,Object) + */ + public void addParam(int pos, String type) { + String[] origParams = getParamNames(); + + if ((pos < 0) || (pos >= origParams.length)) { + throw new IndexOutOfBoundsException("pos = " + pos); + } + + String[] params = new String[origParams.length + 1]; + + for (int i = 0, index = 0; i < params.length; i++) { + if (i == pos) { + params[i] = type; + } else { + params[i] = origParams[index++]; + } + } + + setParams(params); + } + + /** + * Add a parameter type to this method. + * + * @see java.util.List#add(int,Object) + */ + public void addParam(int pos, Class type) { + addParam(pos, type.getName()); + } + + /** + * Add a parameter type to this method. + * + * @see java.util.List#add(int,Object) + */ + public void addParam(int pos, BCClass type) { + addParam(pos, type.getName()); + } + + /** + * Change a parameter type of this method. + * + * @see java.util.List#set(int,Object) + */ + public void setParam(int pos, String type) { + String[] origParams = getParamNames(); + + if ((pos < 0) || (pos >= origParams.length)) { + throw new IndexOutOfBoundsException("pos = " + pos); + } + + String[] params = new String[origParams.length]; + + for (int i = 0; i < params.length; i++) { + if (i == pos) { + params[i] = type; + } else { + params[i] = origParams[i]; + } + } + + setParams(params); + } + + /** + * Change a parameter type of this method. + * + * @see java.util.List#set(int,Object) + */ + public void setParam(int pos, Class type) { + setParam(pos, type.getName()); + } + + /** + * Change a parameter type of this method. + * + * @see java.util.List#set(int,Object) + */ + public void setParam(int pos, BCClass type) { + setParam(pos, type.getName()); + } + + /** + * Clear all parameters from this method. + */ + public void clearParams() { + setParams((String[]) null); + } + + /** + * Remove a parameter from this method. + */ + public void removeParam(int pos) { + String[] origParams = getParamNames(); + + if ((pos < 0) || (pos >= origParams.length)) { + throw new IndexOutOfBoundsException("pos = " + pos); + } + + String[] params = new String[origParams.length - 1]; + + for (int i = 0, index = 0; i < origParams.length; i++) + if (i != pos) { + params[index++] = origParams[i]; + } + + setParams(params); + } + + /////////////////////// + // Convenience methods + /////////////////////// + + /** + * Return the checked exceptions information for the method. + * Acts internally through the {@link Attributes} interface. + * + * @param add if true, a new exceptions attribute will be added + * if not already present + * @return the exceptions information, or null if none and the + * add param is set to false + */ + public Exceptions getExceptions(boolean add) { + Exceptions exceptions = (Exceptions) getAttribute(Constants.ATTR_EXCEPTIONS); + + if (!add || (exceptions != null)) { + return exceptions; + } + + if (exceptions == null) { + exceptions = (Exceptions) addAttribute(Constants.ATTR_EXCEPTIONS); + } + + return exceptions; + } + + /** + * Remove the exceptions attribute for the method. + * Acts internally through the {@link Attributes} interface. + * + * @return true if there was a value to remove + */ + public boolean removeExceptions() { + return removeAttribute(Constants.ATTR_EXCEPTIONS); + } + + /** + * Return the code for the method. If the code already exists, its + * iterator will be reset to the first instruction. + * Acts internally through the {@link Attributes} interface. + * + * @param add if true, a new code attribute will be added + * if not already present + * @return the code for the metohd, or null if none and the + * add param is set to false + */ + public Code getCode(boolean add) { + Code code = (Code) getAttribute(Constants.ATTR_CODE); + + if (code != null) { + code.beforeFirst(); + + return code; + } + + if (!add) { + return null; + } + + return (Code) addAttribute(Constants.ATTR_CODE); + } + + /** + * Remove the code attribute from the method. + * Acts internally through the {@link Attributes} interface. + * + * @return true if there was a value to remove + */ + public boolean removeCode() { + return removeAttribute(Constants.ATTR_CODE); + } + + //////////////////////////////// + // VisitAcceptor implementation + //////////////////////////////// + public void acceptVisit(BCVisitor visit) { + visit.enterBCMethod(this); + visitAttributes(visit); + visit.exitBCMethod(this); + } + + void initialize(String name, String descriptor) { + super.initialize(name, descriptor); + makePublic(); + } +} diff --git a/serp/src/main/java/serp/bytecode/ClassConstantInstruction.java b/serp/src/main/java/serp/bytecode/ClassConstantInstruction.java new file mode 100755 index 000000000..88e956821 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/ClassConstantInstruction.java @@ -0,0 +1,223 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import java.util.*; + + +/** + *

Pseudo-instruction used to place {@link Class} objects onto the stack. + * This logical instruction may actually involve a large chunk of code, and + * may even add static synthetic fields and methods to the owning class. + * Therefore, once the type of class being loaded is set, it cannot + * be changed. Also, this instruction is invalid as the target of + * any jump instruction or exception handler.

+ * + * @author Abe White + */ +public class ClassConstantInstruction { + private static final Class[] _params = new Class[] { String.class }; + private static final Map _wrappers = new HashMap(); + + static { + _wrappers.put(byte.class.getName(), Byte.class); + _wrappers.put(boolean.class.getName(), Boolean.class); + _wrappers.put(char.class.getName(), Character.class); + _wrappers.put(double.class.getName(), Double.class); + _wrappers.put(float.class.getName(), Float.class); + _wrappers.put(int.class.getName(), Integer.class); + _wrappers.put(long.class.getName(), Long.class); + _wrappers.put(short.class.getName(), Short.class); + } + + private Instruction _ins = null; + private Code _code = null; + private BCClass _class = null; + private boolean _invalid = false; + + ClassConstantInstruction(BCClass bc, Code code, Instruction nop) { + _class = bc; + _code = code; + _ins = nop; + } + + /** + * Set the type of class being loaded. + * + * @return the first Instruction of the block added by setting + * the type + * @throws IllegalStateException if type has already been set + */ + public Instruction setClass(String name) { + name = _class.getProject().getNameCache().getExternalForm(name, false); + setClassName(name, getWrapperClass(name)); + + return _ins; + } + + /** + * Set the type of class being loaded. + * + * @return the first Instruction of the block added by setting + * the type + * @throws IllegalStateException if type has already been set + */ + public Instruction setClass(Class type) { + return setClass(type.getName()); + } + + /** + * Set the type of class being loaded. + * + * @return the first Instruction of the block added by setting + * the type + * @throws IllegalStateException if type has already been set + */ + public Instruction setClass(BCClass type) { + return setClass(type.getName()); + } + + /** + * Set the name of the class to load. + */ + private void setClassName(String name, Class wrapper) { + if (_invalid) { + throw new IllegalStateException(); + } + + // remember the position of the code iterator + Instruction before = (_code.hasNext()) ? _code.next() : null; + _code.before(_ins); + _code.next(); + + if (wrapper != null) { + _code.getstatic().setField(wrapper, "TYPE", Class.class); + } else { + setObject(name); + } + + // move to the old position + if (before != null) { + _code.before(before); + } else { + _code.afterLast(); + } + + _invalid = true; + } + + /** + * Adds fields and methods as necessary to load a class constant of + * an object type. + */ + private void setObject(String name) { + BCField field = addClassField(name); + BCMethod method = addClassLoadMethod(); + + // copied from the way jikes loads classes + _code.getstatic().setField(field); + + JumpInstruction ifnull = _code.ifnull(); + + _code.getstatic().setField(field); + + JumpInstruction go2 = _code.go2(); + + ifnull.setTarget(_code.constant().setValue(name)); + _code.invokestatic().setMethod(method); + _code.dup(); + _code.putstatic().setField(field); + + go2.setTarget(_code.nop()); + } + + /** + * Adds a static field to hold the loaded class constant. + */ + private BCField addClassField(String name) { + String fieldName = "class$L" + + name.replace('.', '$').replace('[', '$').replace(';', '$'); + + BCField field = _class.getDeclaredField(fieldName); + + if (field == null) { + field = _class.declareField(fieldName, Class.class); + field.makePackage(); + field.setStatic(true); + field.setSynthetic(true); + } + + return field; + } + + /** + * Adds the standard class$ method used inernally by classes + * to load class constants for object types. + */ + private BCMethod addClassLoadMethod() { + BCMethod method = _class.getDeclaredMethod("class$", _params); + + if (method != null) { + return method; + } + + // add the special synthetic method + method = _class.declareMethod("class$", Class.class, _params); + method.setStatic(true); + method.makePackage(); + method.setSynthetic(true); + + // copied directly from the output of the jikes compiler + Code code = method.getCode(true); + code.setMaxStack(3); + code.setMaxLocals(2); + + Instruction tryStart = code.aload().setLocal(0); + code.invokestatic() + .setMethod(Class.class, "forName", Class.class, _params); + + Instruction tryEnd = code.areturn(); + Instruction handlerStart = code.astore().setLocal(1); + code.anew().setType(NoClassDefFoundError.class); + code.dup(); + code.aload().setLocal(1); + code.invokevirtual() + .setMethod(Throwable.class, "getMessage", String.class, null); + code.invokespecial() + .setMethod(NoClassDefFoundError.class, "", void.class, _params); + code.athrow(); + + code.addExceptionHandler(tryStart, tryEnd, handlerStart, + ClassNotFoundException.class); + + return method; + } + + /** + * Return the wrapper type for the given primitive class, or null + * if the given name is not a primitive type. The given name should + * be in external form. + */ + private static Class getWrapperClass(String name) { + if (name == null) { + return null; + } + + return (Class) _wrappers.get(name); + } +} diff --git a/serp/src/main/java/serp/bytecode/ClassInstruction.java b/serp/src/main/java/serp/bytecode/ClassInstruction.java new file mode 100755 index 000000000..11e707bd0 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/ClassInstruction.java @@ -0,0 +1,135 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import serp.bytecode.visitor.*; + +import java.io.*; + + +/** + *

An instruction that takes as an argument a class to operate + * on. Examples include anewarray, checkcast, instance, anew, + * etc.

+ * + * @author Abe White + */ +public class ClassInstruction extends TypedInstruction { + private int _index = 0; + + ClassInstruction(Code owner, int opcode) { + super(owner, opcode); + } + + public int getLogicalStackChange() { + return getStackChange(); + } + + public int getStackChange() { + if (getOpcode() == Constants.NEW) { + return 1; + } + + return 0; + } + + int getLength() { + return super.getLength() + 2; + } + + /** + * Return the {@link ConstantPool} index of the + * {@link ClassEntry} describing the class for this instruction. + */ + public int getTypeIndex() { + return _index; + } + + /** + * Set the {@link ConstantPool} index of the + * {@link ClassEntry} describing the class for this instruction. + * + * @return this instruction, for method chaining + */ + public ClassInstruction setTypeIndex(int index) { + _index = index; + + return this; + } + + public String getTypeName() { + if (_index == 0) { + return null; + } + + ClassEntry entry = (ClassEntry) getPool().getEntry(_index); + + return getProject().getNameCache() + .getExternalForm(entry.getNameEntry().getValue(), false); + } + + public TypedInstruction setType(String type) { + if (type == null) { + setTypeIndex(0); + } else { + type = getProject().getNameCache().getInternalForm(type, false); + setTypeIndex(getPool().findClassEntry(type, true)); + } + + return this; + } + + /** + * ClassInstructions are equal if the type they reference is the same or + * unset and if their opcodes are equal. + */ + public boolean equalsInstruction(Instruction other) { + if (other == this) { + return true; + } + + if (!super.equalsInstruction(other)) { + return false; + } + + String type = getTypeName(); + String otherType = ((ClassInstruction) other).getTypeName(); + + return (type == null) || (otherType == null) || type.equals(otherType); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterClassInstruction(this); + visit.exitClassInstruction(this); + } + + void read(Instruction other) { + super.read(other); + setType(((ClassInstruction) other).getTypeName()); + } + + void read(DataInput in) throws IOException { + super.read(in); + setTypeIndex(in.readUnsignedShort()); + } + + void write(DataOutput out) throws IOException { + super.write(out); + out.writeShort(getTypeIndex()); + } +} diff --git a/serp/src/main/java/serp/bytecode/CmpInstruction.java b/serp/src/main/java/serp/bytecode/CmpInstruction.java new file mode 100755 index 000000000..23fb0dbd0 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/CmpInstruction.java @@ -0,0 +1,190 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + + +/** + *

An instruction comparing two stack values. Examples include + * lcmp, fcmpl, etc.

+ * + * @author Abe White + */ +public class CmpInstruction extends TypedInstruction { + private static Class[][] _mappings = new Class[][] { + { int.class, long.class }, + { byte.class, long.class }, + { char.class, long.class }, + { short.class, long.class }, + { boolean.class, long.class }, + { void.class, long.class }, + { Object.class, long.class }, + }; + + CmpInstruction(Code owner) { + super(owner); + } + + CmpInstruction(Code owner, int opcode) { + super(owner, opcode); + } + + public int getLogicalStackChange() { + switch (getOpcode()) { + case Constants.NOP: + return 0; + + default: + return -1; + } + } + + public int getStackChange() { + switch (getOpcode()) { + case Constants.LCMP: + case Constants.DCMPL: + case Constants.DCMPG: + return -3; + + case Constants.NOP: + return 0; + + default: + return -1; + } + } + + public String getTypeName() { + switch (getOpcode()) { + case Constants.LCMP: + return long.class.getName(); + + case Constants.FCMPL: + case Constants.FCMPG: + return float.class.getName(); + + case Constants.DCMPL: + case Constants.DCMPG: + return double.class.getName(); + + default: + return null; + } + } + + public TypedInstruction setType(String type) { + type = mapType(type, _mappings, true); + + if (type == null) { + return (TypedInstruction) setOpcode(Constants.NOP); + } + + int opcode = getOpcode(); + + switch (type.charAt(0)) { + case 'l': + return (TypedInstruction) setOpcode(Constants.LCMP); + + case 'f': + + if ((opcode == Constants.FCMPL) || (opcode == Constants.DCMPL)) { + return (TypedInstruction) setOpcode(Constants.FCMPL); + } + + return (TypedInstruction) setOpcode(Constants.FCMPG); + + case 'd': + + if ((opcode == Constants.FCMPL) || (opcode == Constants.DCMPL)) { + return (TypedInstruction) setOpcode(Constants.DCMPL); + } + + return (TypedInstruction) setOpcode(Constants.DCMPG); + + default: + throw new IllegalStateException(); + } + } + + /** + * Return the number that will be placed on the stack if this instruction + * is of type float or double and one of the operands is NaN. For + * FCMPG or DCMPG, this value will be 1; for FCMPL or DCMPL this value + * will be -1. For LCMP or if the type is unset, this value will be 0. + */ + public int getNaNValue() { + switch (getOpcode()) { + case Constants.FCMPL: + case Constants.DCMPL: + return -1; + + case Constants.FCMPG: + case Constants.DCMPG: + return 1; + + default: + return 0; + } + } + + /** + * Set the number that will be placed on the stack if this instruction + * is of type float or double and one of the operands is NaN. For + * FCMPG or DCMPG, this value should be 1; for FCMPL or DCMPL this value + * should be -1. For LCMP, this value should be 0. + * + * @return this instruction, for method chaining + */ + public CmpInstruction setNaNValue(int nan) { + switch (getOpcode()) { + case Constants.FCMPL: + case Constants.FCMPG: + + if (nan == 1) { + setOpcode(Constants.FCMPG); + } else if (nan == -1) { + setOpcode(Constants.FCMPL); + } else { + throw new IllegalArgumentException("Invalid nan for type"); + } + + case Constants.DCMPL: + case Constants.DCMPG: + + if (nan == 1) { + setOpcode(Constants.DCMPG); + } else if (nan == -1) { + setOpcode(Constants.DCMPL); + } else { + throw new IllegalArgumentException("Invalid nan for type"); + } + + default: + + if (nan != 0) { + throw new IllegalArgumentException("Invalid nan for type"); + } + } + + return this; + } + + public void acceptVisit(BCVisitor visit) { + visit.enterCmpInstruction(this); + visit.exitCmpInstruction(this); + } +} diff --git a/serp/src/main/java/serp/bytecode/Code.java b/serp/src/main/java/serp/bytecode/Code.java new file mode 100755 index 000000000..d07ae6df8 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/Code.java @@ -0,0 +1,2847 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import serp.bytecode.visitor.*; + +import java.io.*; + +import java.util.*; + + +/** + *

Representation of a code block of a class. + * The methods of this class mimic those of the same name in the + * {@link java.util.ListIterator} class. Note that the size and index + * information of the code block will change as opcodes are added.

+ * + *

Code blocks are usually obtained from a {@link BCMethod}, but can also + * be constructed via the default constructor. Blocks created this way can + * be used to provide template instructions to the various search/replace + * methods in this class.

+ * + *

The code class contains methods named after most JVM instructions, each + * of which adds the matching opcode to the code block at the + * current iterator position. It also contains generic versions of various + * JVM instructions whose opcodes are not set until their properties are set + * with additional information. Almost all instruction types are able to + * 'morph' their opcode on the fly as the arguments to the instruction change. + * Thus the developer can initially call, for example, the aload + * opcode, but later change the type to load to int and the + * opcode will automatically morph to the iload opcode.

+ * + * @author Abe White + */ +public class Code extends Attribute { + private final CodeEntry _head; + private final CodeEntry _tail; + private CodeIterator _ci; + private int _maxStack = 0; + private int _maxLocals = 0; + private int _size = 0; + private Collection _handlers = new LinkedList(); + private Collection _attrs = new LinkedList(); + + Code(int nameIndex, Attributes owner) { + super(nameIndex, owner); + + _head = new CodeEntry(); + _tail = new CodeEntry(); + _head.next = _tail; + _tail.prev = _head; + + _ci = new CodeIterator(_head, -1); + } + + /** + * The public constructor is for creating template code modules + * that produce {@link Instruction}s used in matching through + * the various search and replace + * methods. + */ + public Code() { + this(0, + new Project().loadClass("", null).declareMethod("", void.class, null)); + } + + /** + * The owning method. + */ + public BCMethod getMethod() { + return (BCMethod) getOwner(); + } + + Collection getAttributesHolder() { + return _attrs; + } + + //////////////////////////// + // Stack, Locals operations + //////////////////////////// + + /** + * Return the maximum stack depth set for this code block. + */ + public int getMaxStack() { + return _maxStack; + } + + /** + * Set the maximum stack depth for this code block. + */ + public void setMaxStack(int max) { + _maxStack = max; + } + + /** + * Return the maximum number of local variables (including params) + * set for this method. + */ + public int getMaxLocals() { + return _maxLocals; + } + + /** + * Set the maximum number of local variables (including params) in + * this method. + */ + public void setMaxLocals(int max) { + _maxLocals = max; + } + + /** + * Return the local variable index for the paramIndex'th parameter to + * the method. Local variable indexes differ from parameter indexes + * because: + * a) non-static methods use the 0th local variable for the 'this' ptr, and + * b) double and long values occupy two spots in the local + * variable array. + * Returns -1 if the given index is not valid. + */ + public int getLocalsIndex(int paramIndex) { + if (paramIndex < 0) { + return -1; + } + + int pos = 0; + + if (!getMethod().isStatic()) { + pos = 1; + } + + String[] params = getMethod().getParamNames(); + + for (int i = 0; i < paramIndex; i++, pos++) { + if (i == params.length) { + return -1; + } + + if (params[i].equals(long.class.getName()) || + params[i].equals(double.class.getName())) { + pos++; + } + } + + return pos; + } + + /** + * Return the parameter index for the given local index, or -1 if + * the given local does not reference a param. + * + * @see #getLocalsIndex + */ + public int getParamsIndex(int localIndex) { + int pos = 0; + + if (!getMethod().isStatic()) { + pos = 1; + } + + String[] params = getMethod().getParamNames(); + + for (int i = 0; i < params.length; i++, pos++) { + if (localIndex == pos) { + return i; + } + + if (params[i].equals(long.class.getName()) || + params[i].equals(double.class.getName())) { + pos++; + } + } + + return -1; + } + + /** + * Return the next available local variable index. + */ + public int getNextLocalsIndex() { + calculateMaxLocals(); + + return getMaxLocals(); + } + + /** + * Calculate and set the number of locals needed based on + * the instructions used and the parameters of the method this code + * block is a part of. + * + * @see #setMaxLocals + */ + public void calculateMaxLocals() { + // start off assuming the max number needed is the + // number for all the params + String[] params = getMethod().getParamNames(); + int max = 0; + + if ((params.length == 0) && !getMethod().isStatic()) { + max = 1; + } else if (params.length > 0) { + max = getLocalsIndex(params.length - 1) + 1; + + if (params[params.length - 1].equals(long.class.getName()) || + params[params.length - 1].equals(double.class.getName())) { + max++; + } + } + + // check to see if there are any store instructions that + // try to reference beyond that point + StoreInstruction store; + int current; + + for (CodeEntry entry = _head.next; entry != _tail; + entry = entry.next) { + current = 0; + + if (entry instanceof StoreInstruction) { + store = (StoreInstruction) entry; + current = store.getLocal() + 1; + + if (store.getType().equals(long.class) || + store.getType().equals(double.class)) { + current++; + } + + if (current > max) { + max = current; + } + } + } + + setMaxLocals(max); + } + + /** + * Calculate and set the maximum stack depth needed for + * the instructions used. + * + * @see #setMaxStack + */ + public void calculateMaxStack() { + int stack = 0; + int max = 0; + + ExceptionHandler[] handlers = getExceptionHandlers(); + Instruction ins; + + for (CodeEntry entry = _head.next; entry != _tail; + entry = entry.next) { + ins = (Instruction) entry; + stack += ins.getStackChange(); + + // if this is the start of a try, the exception will be placed + // on the stack + for (int j = 0; j < handlers.length; j++) + if (handlers[j].getTryStart() == ins) { + stack++; + } + + if (stack > max) { + max = stack; + } + } + + setMaxStack(max); + } + + /////////////////////////////// + // ExceptionHandler operations + /////////////////////////////// + + /** + * Return the exception handlers active in this code block, or an + * empty array if none. + */ + public ExceptionHandler[] getExceptionHandlers() { + return (ExceptionHandler[]) _handlers.toArray(new ExceptionHandler[_handlers.size()]); + } + + /** + * Return the exception handler that catches the given exception type; + * if multiple handlers catch the given type, which is returned is + * undefined. + */ + public ExceptionHandler getExceptionHandler(String catchType) { + catchType = getProject().getNameCache().getExternalForm(catchType, false); + + String type; + ExceptionHandler[] handlers = getExceptionHandlers(); + + for (int i = 0; i < handlers.length; i++) { + type = handlers[i].getCatchName(); + + if (((type == null) && (catchType == null)) || + ((type != null) && type.equals(catchType))) { + return handlers[i]; + } + } + + return null; + } + + /** + * Return the exception handler that catches the given exception type; + * if multiple handlers catch the given type, which is returned is + * undefined. + */ + public ExceptionHandler getExceptionHandler(Class catchType) { + if (catchType == null) { + return getExceptionHandler((String) null); + } + + return getExceptionHandler(catchType.getName()); + } + + /** + * Return the exception handler that catches the given exception type; + * if multiple handlers catch the given type, which is returned is + * undefined. + */ + public ExceptionHandler getExceptionHandler(BCClass catchType) { + if (catchType == null) { + return getExceptionHandler((String) null); + } + + return getExceptionHandler(catchType.getName()); + } + + /** + * Return all exception handlers that catch the given exception type, + * or an empty array if none. + */ + public ExceptionHandler[] getExceptionHandlers(String catchType) { + catchType = getProject().getNameCache().getExternalForm(catchType, false); + + List matches = new LinkedList(); + String type; + ExceptionHandler[] handlers = getExceptionHandlers(); + + for (int i = 0; i < handlers.length; i++) { + type = handlers[i].getCatchName(); + + if (((type == null) && (catchType == null)) || + ((type != null) && type.equals(catchType))) { + matches.add(handlers[i]); + } + } + + return (ExceptionHandler[]) matches.toArray(new ExceptionHandler[matches.size()]); + } + + /** + * Return all exception handlers that catch the given exception type, + * or an empty array if none. + */ + public ExceptionHandler[] getExceptionHandlers(Class catchType) { + if (catchType == null) { + return getExceptionHandlers((String) null); + } + + return getExceptionHandlers(catchType.getName()); + } + + /** + * Return all exception handlers that catch the given exception type, + * or an empty array if none. + */ + public ExceptionHandler[] getExceptionHandlers(BCClass catchType) { + if (catchType == null) { + return getExceptionHandlers((String) null); + } + + return getExceptionHandlers(catchType.getName()); + } + + /** + * Set the exception handlers for this code block. This method is useful + * for importing all handlers from another code block. Set to null or + * empty array if none. + */ + public void setExceptionHandlers(ExceptionHandler[] handlers) { + clearExceptionHandlers(); + + if (handlers != null) { + for (int i = 0; i < handlers.length; i++) + addExceptionHandler(handlers[i]); + } + } + + /** + * Import the given exception handler from another code block. + */ + public ExceptionHandler addExceptionHandler(ExceptionHandler handler) { + ExceptionHandler newHandler = addExceptionHandler(); + newHandler.read(handler); + + return newHandler; + } + + /** + * Add an exception handler to this code block. + */ + public ExceptionHandler addExceptionHandler() { + ExceptionHandler handler = new ExceptionHandler(this); + _handlers.add(handler); + + return handler; + } + + /** + * Add an exception handler to this code block. + * + * @param tryStart the first instruction of the try {} block + * @param tryEnd the last instruction of the try {} block + * @param handlerStart the first instruction of the catch {} block + * @param catchType the type of exception being caught + */ + public ExceptionHandler addExceptionHandler(Instruction tryStart, + Instruction tryEnd, Instruction handlerStart, String catchType) { + ExceptionHandler handler = addExceptionHandler(); + handler.setTryStart(tryStart); + handler.setTryEnd(tryEnd); + handler.setHandlerStart(handlerStart); + handler.setCatch(catchType); + + return handler; + } + + /** + * Add an exception handler to this code block. + * + * @param tryStart the first instruction of the try {} block + * @param tryEnd the last instruction of the try {} block + * @param handlerStart the first instruction of the catch {} block + * @param catchType the type of exception being caught + */ + public ExceptionHandler addExceptionHandler(Instruction tryStart, + Instruction tryEnd, Instruction handlerStart, Class catchType) { + String catchName = null; + + if (catchType != null) { + catchName = catchType.getName(); + } + + return addExceptionHandler(tryStart, tryEnd, handlerStart, catchName); + } + + /** + * Add an exception handler to this code block. + * + * @param tryStart the first instruction of the try {} block + * @param tryEnd the last instruction of the try {} block + * @param handlerStart the first instruction of the catch {} block + * @param catchType the type of exception being caught + */ + public ExceptionHandler addExceptionHandler(Instruction tryStart, + Instruction tryEnd, Instruction handlerStart, BCClass catchType) { + String catchName = null; + + if (catchType != null) { + catchName = catchType.getName(); + } + + return addExceptionHandler(tryStart, tryEnd, handlerStart, catchName); + } + + /** + * Clear all exception handlers. + */ + public void clearExceptionHandlers() { + ExceptionHandler handler; + + for (Iterator itr = _handlers.iterator(); itr.hasNext();) { + handler = (ExceptionHandler) itr.next(); + itr.remove(); + handler.invalidate(); + } + } + + /** + * Remove the exception handler that catches the given type. + */ + public boolean removeExceptionHandler(String catchType) { + return removeExceptionHandler(getExceptionHandler(catchType)); + } + + /** + * Remove the exception handler that catches the given type. + * + * @return true if the handler was removed, false otherwise + */ + public boolean removeExceptionHandler(Class catchType) { + if (catchType == null) { + return removeExceptionHandler((String) null); + } + + return removeExceptionHandler(catchType.getName()); + } + + /** + * Remove the exception handler that catches the given type. + * + * @return true if the handler was removed, false otherwise + */ + public boolean removeExceptionHandler(BCClass catchType) { + if (catchType == null) { + return removeExceptionHandler((String) null); + } + + return removeExceptionHandler(catchType.getName()); + } + + /** + * Remove an exception handler from this code block. The given handler + * must belong to this code block. + */ + public boolean removeExceptionHandler(ExceptionHandler handler) { + if ((handler == null) || !_handlers.remove(handler)) { + return false; + } + + handler.invalidate(); + + return true; + } + + ///////////////////////// + // Code block operations + ///////////////////////// + + /** + * Return the number of instructions in the method. + */ + public int size() { + return _size; + } + + /** + * Reset the position of the instruction iterator to the first opcode. + */ + public void beforeFirst() { + _ci = new CodeIterator(_head, -1); + } + + /** + * Set the position of the instruction iterator to after the last opcode. + */ + public void afterLast() { + if (_size == 0) { + _ci = new CodeIterator(_head, -1); + } else { + _ci = new CodeIterator(_tail.prev, _size - 1); + } + } + + /** + * Position the iterator just before the given instruction. The + * instruction must belong to this method. + */ + public void before(Instruction ins) { + if (ins.getCode() != this) { + throw new IllegalArgumentException("ins.code != this"); + } + + _ci = new CodeIterator(ins.prev, CodeIterator.UNSET); + } + + /** + * Position the iterator just after the given instruction. The + * instruction must belong to this method. + */ + public void after(Instruction ins) { + before(ins); + next(); + } + + /** + * Return true if a subsequent call to {@link #next} will return an + * instruction. + */ + public boolean hasNext() { + return _ci.hasNext(); + } + + /** + * Return true if a subsequent call to {@link #previous} will return an + * instruction. + */ + public boolean hasPrevious() { + return _ci.hasPrevious(); + } + + /** + * Return the next instruction. + */ + public Instruction next() { + return (Instruction) _ci.next(); + } + + /** + * Return the index of the next instruction, or {@link #size} if at end. + */ + public int nextIndex() { + return _ci.nextIndex(); + } + + /** + * Return the previous instruction. + */ + public Instruction previous() { + return (Instruction) _ci.previous(); + } + + /** + * Return the index of the previous instruction, or -1 if at beginning. + */ + public int previousIndex() { + return _ci.previousIndex(); + } + + /** + * Place the iterator before the given list index. + */ + public void before(int index) { + if ((index < 0) || (index >= _size)) { + throw new IndexOutOfBoundsException(String.valueOf(index)); + } + + CodeEntry entry = _head; + + for (int i = 0; i < index; entry = entry.next, i++) + ; + + _ci = new CodeIterator(entry, index - 1); + } + + /** + * Place the iterator after the given list index. + */ + public void after(int index) { + before(index); + next(); + } + + /** + * Find the next instruction from the current iterator position that + * matches the given one, according to the {@link Object#equals} methods of + * the instruction types. This allows for matching based on template + * instructions, as the equals methods of most instructions return + * true if the information for the given instruction has not been filled + * in. If a match is found, the iterator is placed after the matching + * Instruction. If no match is found, moves the iterator to + * {@link #afterLast}. + * + * @return true if match found + */ + public boolean searchForward(Instruction template) { + if (template == null) { + return false; + } + + while (hasNext()) + + if (template.equalsInstruction(next())) { + return true; + } + + return false; + } + + /** + * Find the closest previous instruction from the current iterator + * position that matches the given one, according to the + * {@link Object#equals} methods of the instruction types. This allows + * for matching based on template instructions, as the equals methods of + * most instructions return true if the information for the given + * instruction has not been filled in. If a match is found, the iterator + * is placed before the matching Instruction. If no match is found, + * moves the iterator to {@link #beforeFirst}. + * + * @return true if match found + */ + public boolean searchBackward(Instruction template) { + if (template == null) { + return false; + } + + while (hasPrevious()) + + if (template.equalsInstruction(previous())) { + return true; + } + + return false; + } + + /** + * Adds a copy of the given instruction. + * + * @return the newly added instruction + */ + public Instruction add(Instruction ins) { + Instruction newIns = createInstruction(ins.getOpcode()); + newIns.read(ins); + _ci.add(newIns); + + return newIns; + } + + /** + * Replaces the last iterated instruction with a copy of the given one. + * This method will also make sure that all jump points + * that referenced the old opcode are updated correctly. + * + * @return the newly added instruction + * + * @see ListIterator#set + */ + public Instruction set(Instruction ins) { + Instruction newIns = createInstruction(ins.getOpcode()); + newIns.read(ins); + _ci.set(newIns); + + return newIns; + } + + /** + * Replaces all the instructions in this code block that match the + * given template with the given instruction. After this method, + * the iterator will be {@link #afterLast}. + * + * @return the number of substitutions made + */ + public int replace(Instruction template, Instruction with) { + beforeFirst(); + + int count; + + for (count = 0; searchForward(template); count++) + set(with); + + return count; + } + + /** + * Equivalent to looping over each given template/replacement + * pair and calling {@link #replace(Instruction,Instruction)} for each. + */ + public int replace(Instruction[] templates, Instruction[] with) { + if ((templates == null) || (with == null)) { + return 0; + } + + int count = 0; + + for (int i = 0; i < templates.length; i++) { + if (with == null) { + count += replace(templates[i], null); + } else { + count += replace(templates[i], with[i]); + } + } + + return count; + } + + /** + * Remove the last iterated instruction. + * + * @see ListIterator#remove + */ + public void remove() { + _ci.remove(); + } + + ////////////////////////// + // Instruction operations + ////////////////////////// + + /** + * Load a class constant onto the stack. + * For primitive types, this translates into a + * getstatic for the TYPE field of the primitive's wrapper type. + * For non-primitives, things get much more complex. Suffice it to + * say that the operation involves adding synthetic static fields + * and even methods to the class. Note that this instruction requires + * up to 3 stack positions to execute. + */ + public ClassConstantInstruction classconstant() { + return new ClassConstantInstruction(getMethod().getDeclarer(), this, + nop()); + } + + /** + * Add the nop opcode. + */ + public Instruction nop() { + return addInstruction(Constants.NOP); + } + + /** + * Load some constant onto the stack. The {@link ConstantInstruction} + * type takes any constant and correctly translates it into the proper + * opcode, depending on the constant type and value. For example, + * if the constant value is set to 0L, the opcode will be set to + * lconst0. + */ + public ConstantInstruction constant() { + return (ConstantInstruction) addInstruction(new ConstantInstruction( + this)); + } + + /** + * Load a local variable onto the stack. This instruction will result + * in a nop until its type and local index are set. + */ + public LoadInstruction xload() { + return (LoadInstruction) addInstruction(new LoadInstruction(this)); + } + + /** + * Load an int local variable onto the stack. This instruction will + * result in a nop until its local index is set. + */ + public LoadInstruction iload() { + return (LoadInstruction) addInstruction(new LoadInstruction(this).setType( + int.class)); + } + + /** + * Load a long local variable onto the stack. This instruction will + * result in a nop until its local index is set. + */ + public LoadInstruction lload() { + return (LoadInstruction) addInstruction(new LoadInstruction(this).setType( + long.class)); + } + + /** + * Load a float local variable onto the stack. This instruction will + * result in a nop until its local index is set. + */ + public LoadInstruction fload() { + return (LoadInstruction) addInstruction(new LoadInstruction(this).setType( + float.class)); + } + + /** + * Load a double local variable onto the stack. This instruction will + * result in a nop until its local index is set. + */ + public LoadInstruction dload() { + return (LoadInstruction) addInstruction(new LoadInstruction(this).setType( + double.class)); + } + + /** + * Load an object local variable onto the stack. This instruction will + * result in a nop until its local index is set. + */ + public LoadInstruction aload() { + return (LoadInstruction) addInstruction(new LoadInstruction(this).setType( + Object.class)); + } + + /** + * Store a value from the stack into a local variable. This instruction + * will result in a nop until its type and local index are + * set. + */ + public StoreInstruction xstore() { + return (StoreInstruction) addInstruction(new StoreInstruction(this)); + } + + /** + * Store an int value from the stack into a local variable. This + * instruction will result in a nop until its local index is + * set. + */ + public StoreInstruction istore() { + return (StoreInstruction) addInstruction(new StoreInstruction(this).setType( + int.class)); + } + + /** + * Store a long value from the stack into a local variable. This + * instruction will result in a nop until its local index is + * set. + */ + public StoreInstruction lstore() { + return (StoreInstruction) addInstruction(new StoreInstruction(this).setType( + long.class)); + } + + /** + * Store a float value from the stack into a local variable. This + * instruction will result in a nop until its local index is + * set. + */ + public StoreInstruction fstore() { + return (StoreInstruction) addInstruction(new StoreInstruction(this).setType( + float.class)); + } + + /** + * Store a double value from the stack into a local variable. This + * instruction will result in a nop until its local index is + * set. + */ + public StoreInstruction dstore() { + return (StoreInstruction) addInstruction(new StoreInstruction(this).setType( + double.class)); + } + + /** + * Store an object value from the stack into a local variable. This + * instruction will result in a nop until its local index is + * set. + */ + public StoreInstruction astore() { + return (StoreInstruction) addInstruction(new StoreInstruction(this).setType( + Object.class)); + } + + /** + * Add the ret opcode, used in implementing + * finally clauses. + */ + public RetInstruction ret() { + return (RetInstruction) addInstruction(Constants.RET); + } + + /** + * Add the iinc opcode. + */ + public IIncInstruction iinc() { + return (IIncInstruction) addInstruction(Constants.IINC); + } + + /** + * Add the wide opcode. + */ + public WideInstruction wide() { + return (WideInstruction) addInstruction(Constants.WIDE); + } + + /** + * Load an array value onto the stack. This instruction will result + * in a nop until its type is set. + */ + public ArrayLoadInstruction xaload() { + return (ArrayLoadInstruction) addInstruction(new ArrayLoadInstruction( + this)); + } + + /** + * Load an int array value onto the stack; the iaload + * opcode. + */ + public ArrayLoadInstruction iaload() { + return (ArrayLoadInstruction) addInstruction(Constants.IALOAD); + } + + /** + * Load a long array value onto the stack; the laload + * opcode. + */ + public ArrayLoadInstruction laload() { + return (ArrayLoadInstruction) addInstruction(Constants.LALOAD); + } + + /** + * Load a float array value onto the stack; the faload + * opcode. + */ + public ArrayLoadInstruction faload() { + return (ArrayLoadInstruction) addInstruction(Constants.FALOAD); + } + + /** + * Load a double array value onto the stack; the daload + * opcode. + */ + public ArrayLoadInstruction daload() { + return (ArrayLoadInstruction) addInstruction(Constants.DALOAD); + } + + /** + * Load an object array value onto the stack; the aaload + * opcode. + */ + public ArrayLoadInstruction aaload() { + return (ArrayLoadInstruction) addInstruction(Constants.AALOAD); + } + + /** + * Load a byte array value onto the stack; the baload + * opcode. + */ + public ArrayLoadInstruction baload() { + return (ArrayLoadInstruction) addInstruction(Constants.BALOAD); + } + + /** + * Load a char array value onto the stack; the caload + * opcode. + */ + public ArrayLoadInstruction caload() { + return (ArrayLoadInstruction) addInstruction(Constants.CALOAD); + } + + /** + * Load a short array value onto the stack; the saload + * opcode. + */ + public ArrayLoadInstruction saload() { + return (ArrayLoadInstruction) addInstruction(Constants.SALOAD); + } + + /** + * Store a value from the stack into an array. This instruction + * will result in a nop until its type is set. + */ + public ArrayStoreInstruction xastore() { + return (ArrayStoreInstruction) addInstruction(new ArrayStoreInstruction( + this)); + } + + /** + * Store an int value from the stack into an array; the + * iastore opcode. + */ + public ArrayStoreInstruction iastore() { + return (ArrayStoreInstruction) addInstruction(Constants.IASTORE); + } + + /** + * Store a long value from the stack into an array; the + * lastore opcode. + */ + public ArrayStoreInstruction lastore() { + return (ArrayStoreInstruction) addInstruction(Constants.LASTORE); + } + + /** + * Store a float value from the stack into an array; the + * fastore opcode. + */ + public ArrayStoreInstruction fastore() { + return (ArrayStoreInstruction) addInstruction(Constants.FASTORE); + } + + /** + * Store a double value from the stack into an array; the + * dastore opcode. + */ + public ArrayStoreInstruction dastore() { + return (ArrayStoreInstruction) addInstruction(Constants.DASTORE); + } + + /** + * Store an object value from the stack into an array; the + * aastore opcode. + */ + public ArrayStoreInstruction aastore() { + return (ArrayStoreInstruction) addInstruction(Constants.AASTORE); + } + + /** + * Store a byte value from the stack into an array; the + * bastore opcode. + */ + public ArrayStoreInstruction bastore() { + return (ArrayStoreInstruction) addInstruction(Constants.BASTORE); + } + + /** + * Store a char value from the stack into an array; the + * castore opcode. + */ + public ArrayStoreInstruction castore() { + return (ArrayStoreInstruction) addInstruction(Constants.CASTORE); + } + + /** + * Store a short value from the stack into an array; the + * sastore opcode. + */ + public ArrayStoreInstruction sastore() { + return (ArrayStoreInstruction) addInstruction(Constants.SASTORE); + } + + /** + * The pop opcode. + */ + public StackInstruction pop() { + return (StackInstruction) addInstruction(Constants.POP); + } + + /** + * The pop2 opcode. + */ + public StackInstruction pop2() { + return (StackInstruction) addInstruction(Constants.POP2); + } + + /** + * The dup opcode. + */ + public StackInstruction dup() { + return (StackInstruction) addInstruction(Constants.DUP); + } + + /** + * The dupx1 opcode. + */ + public StackInstruction dupx1() { + return (StackInstruction) addInstruction(Constants.DUPX1); + } + + /** + * The dupx2 opcode. + */ + public StackInstruction dupx2() { + return (StackInstruction) addInstruction(Constants.DUPX2); + } + + /** + * The dup2 opcode. + */ + public StackInstruction dup2() { + return (StackInstruction) addInstruction(Constants.DUP2); + } + + /** + * The dup2x1 opcode. + */ + public StackInstruction dup2x1() { + return (StackInstruction) addInstruction(Constants.DUP2X1); + } + + /** + * The dup2x2 opcode. + */ + public StackInstruction dup2x2() { + return (StackInstruction) addInstruction(Constants.DUP2X2); + } + + /** + * The swap opcode. + */ + public StackInstruction swap() { + return (StackInstruction) addInstruction(Constants.SWAP); + } + + /** + * Perform some math operation on the stack items. This instruction will + * result in a nop until its operation and type are set. + */ + public MathInstruction math() { + return (MathInstruction) addInstruction(new MathInstruction(this)); + } + + /** + * Add the top two stack values. This instruction will result in + * a nop until its type is set. + */ + public MathInstruction xadd() { + MathInstruction mi = math(); + + return mi.setOperation(Constants.MATH_ADD); + } + + /** + * Add the top two stack int values; the iadd opcode. + */ + public MathInstruction iadd() { + return (MathInstruction) addInstruction(Constants.IADD); + } + + /** + * Add the top two stack long values; the ladd opcode. + */ + public MathInstruction ladd() { + return (MathInstruction) addInstruction(Constants.LADD); + } + + /** + * Add the top two stack float values; the fadd opcode. + */ + public MathInstruction fadd() { + return (MathInstruction) addInstruction(Constants.FADD); + } + + /** + * Add the top two stack double values; the dadd opcode. + */ + public MathInstruction dadd() { + return (MathInstruction) addInstruction(Constants.DADD); + } + + /** + * Subtract the top two stack values. This instruction will result in + * a nop until its type is set. + */ + public MathInstruction xsub() { + MathInstruction mi = math(); + + return mi.setOperation(Constants.MATH_SUB); + } + + /** + * Subtract the top two stack int values; the isub opcode. + */ + public MathInstruction isub() { + return (MathInstruction) addInstruction(Constants.ISUB); + } + + /** + * Subtract the top two stack long values; the lsub opcode. + */ + public MathInstruction lsub() { + return (MathInstruction) addInstruction(Constants.LSUB); + } + + /** + * Subtract the top two stack float values; the fsub opcode. + */ + public MathInstruction fsub() { + return (MathInstruction) addInstruction(Constants.FSUB); + } + + /** + * Subtract the top two stack double values; the dsub opcode. + */ + public MathInstruction dsub() { + return (MathInstruction) addInstruction(Constants.DSUB); + } + + /** + * Multiply the top two stack values. This instruction will result in + * a nop until its type is set. + */ + public MathInstruction xmul() { + MathInstruction mi = math(); + + return mi.setOperation(Constants.MATH_MUL); + } + + /** + * Multiply the top two stack int values; the imul opcode. + */ + public MathInstruction imul() { + return (MathInstruction) addInstruction(Constants.IMUL); + } + + /** + * Multiply the top two stack long values; the lmul opcode. + */ + public MathInstruction lmul() { + return (MathInstruction) addInstruction(Constants.LMUL); + } + + /** + * Multiply the top two stack float values; the fmul opcode. + */ + public MathInstruction fmul() { + return (MathInstruction) addInstruction(Constants.FMUL); + } + + /** + * Multiply the top two stack double values; the dmul opcode. + */ + public MathInstruction dmul() { + return (MathInstruction) addInstruction(Constants.DMUL); + } + + /** + * Divide the top two stack values. This instruction will result in + * a nop until its type is set. + */ + public MathInstruction xdiv() { + MathInstruction mi = math(); + + return mi.setOperation(Constants.MATH_DIV); + } + + /** + * Divide the top two stack int values; the idiv opcode. + */ + public MathInstruction idiv() { + return (MathInstruction) addInstruction(Constants.IDIV); + } + + /** + * Divide the top two stack long values; the ldiv opcode. + */ + public MathInstruction ldiv() { + return (MathInstruction) addInstruction(Constants.LDIV); + } + + /** + * Divide the top two stack float values; the fdiv opcode. + */ + public MathInstruction fdiv() { + return (MathInstruction) addInstruction(Constants.FDIV); + } + + /** + * Divide the top two stack double values; the ddiv opcode. + */ + public MathInstruction ddiv() { + return (MathInstruction) addInstruction(Constants.DDIV); + } + + /** + * Take the remainder of the top two stack values. This instruction will + * result in a nop until its type is set. + */ + public MathInstruction xrem() { + MathInstruction mi = math(); + + return mi.setOperation(Constants.MATH_REM); + } + + /** + * Take the remainder of the top two int stack values; the + * irem opcode. + */ + public MathInstruction irem() { + return (MathInstruction) addInstruction(Constants.IREM); + } + + /** + * Take the remainder of the top two long stack values; the + * lrem opcode. + */ + public MathInstruction lrem() { + return (MathInstruction) addInstruction(Constants.LREM); + } + + /** + * Take the remainder of the top two float stack values; the + * frem opcode. + */ + public MathInstruction frem() { + return (MathInstruction) addInstruction(Constants.FREM); + } + + /** + * Take the remainder of the top two double stack values; the + * drem opcode. + */ + public MathInstruction drem() { + return (MathInstruction) addInstruction(Constants.DREM); + } + + /** + * Negate the top stack value. This instruction will result in a + * nop until its type is set. + */ + public MathInstruction xneg() { + MathInstruction mi = math(); + + return mi.setOperation(Constants.MATH_NEG); + } + + /** + * Negate the top stack int value; the ineg opcode. + */ + public MathInstruction ineg() { + return (MathInstruction) addInstruction(Constants.INEG); + } + + /** + * Negate the top stack long value; the lneg opcode. + */ + public MathInstruction lneg() { + return (MathInstruction) addInstruction(Constants.LNEG); + } + + /** + * Negate the top stack float value; the fneg opcode. + */ + public MathInstruction fneg() { + return (MathInstruction) addInstruction(Constants.FNEG); + } + + /** + * Negate the top stack double value; the dneg opcode. + */ + public MathInstruction dneg() { + return (MathInstruction) addInstruction(Constants.DNEG); + } + + /** + * Shift the top stack values. This instruction will result in a + * nop until its type is set. + */ + public MathInstruction xshl() { + MathInstruction mi = math(); + + return mi.setOperation(Constants.MATH_SHL); + } + + /** + * Shift the top stack int values; the ishl opcode. + */ + public MathInstruction ishl() { + return (MathInstruction) addInstruction(Constants.ISHL); + } + + /** + * Shift the top stack long values; the lshl opcode. + */ + public MathInstruction lshl() { + return (MathInstruction) addInstruction(Constants.LSHL); + } + + /** + * Shift the top stack values. This instruction will result in a + * nop until its type is set. + */ + public MathInstruction xshr() { + MathInstruction mi = math(); + + return mi.setOperation(Constants.MATH_SHR); + } + + /** + * Shift the top stack int values; the ishr opcode. + */ + public MathInstruction ishr() { + return (MathInstruction) addInstruction(Constants.ISHR); + } + + /** + * Shift the top stack long values; the lshr opcode. + */ + public MathInstruction lshr() { + return (MathInstruction) addInstruction(Constants.LSHR); + } + + /** + * Shift the top stack values. This instruction will result in a + * nop until its type is set. + */ + public MathInstruction xushr() { + MathInstruction mi = math(); + + return mi.setOperation(Constants.MATH_USHR); + } + + /** + * Shift the top stack int values; the iushr opcode. + */ + public MathInstruction iushr() { + return (MathInstruction) addInstruction(Constants.IUSHR); + } + + /** + * Shift the top stack long values; the lushr opcode. + */ + public MathInstruction lushr() { + return (MathInstruction) addInstruction(Constants.LUSHR); + } + + /** + * Take the mathematical and of the top two stack values. This instruction + * results in a nop until its type is set. + */ + public MathInstruction xand() { + MathInstruction mi = math(); + + return mi.setOperation(Constants.MATH_AND); + } + + /** + * Take the mathematical and of the top two stack int values; the + * iand opcode. + */ + public MathInstruction iand() { + return (MathInstruction) addInstruction(Constants.IAND); + } + + /** + * Take the mathematical and of the top two stack long values; the + * land opcode. + */ + public MathInstruction land() { + return (MathInstruction) addInstruction(Constants.LAND); + } + + /** + * Take the mathematical or of the top two stack values. This instruction + * results in a nop until its type is set. + */ + public MathInstruction xor() { + MathInstruction mi = math(); + + return mi.setOperation(Constants.MATH_OR); + } + + /** + * Take the mathematical or of the top two stack int values; the + * ior opcode. + */ + public MathInstruction ior() { + return (MathInstruction) addInstruction(Constants.IOR); + } + + /** + * Take the mathematical or of the top two stack long values; the + * lor opcode. + */ + public MathInstruction lor() { + return (MathInstruction) addInstruction(Constants.LOR); + } + + /** + * Take the mathematical xor of the top two stack values. This instruction + * results in a nop until its type is set. + */ + public MathInstruction xxor() { + MathInstruction mi = math(); + + return mi.setOperation(Constants.MATH_XOR); + } + + /** + * Take the mathematical xor of the top two stack int values; the + * ixor opcode. + */ + public MathInstruction ixor() { + return (MathInstruction) addInstruction(Constants.IXOR); + } + + /** + * Take the mathematical xor of the top two stack long values; the + * lxor opcode. + */ + public MathInstruction lxor() { + return (MathInstruction) addInstruction(Constants.LXOR); + } + + /** + * Convert the top stack value to another type. This instruction + * will result in a nop until the types to convert + * between are set. + */ + public ConvertInstruction convert() { + return (ConvertInstruction) addInstruction(new ConvertInstruction(this)); + } + + /** + * Compare the top two stack values. This instruction will result in a + * nop until its type is set. + */ + public CmpInstruction xcmp() { + return (CmpInstruction) addInstruction(new CmpInstruction(this)); + } + + /** + * Compare the top two stack values; the lcmp opcode. + */ + public CmpInstruction lcmp() { + return (CmpInstruction) addInstruction(Constants.LCMP); + } + + /** + * Compare the top two stack values; the fcmpl opcode. + */ + public CmpInstruction fcmpl() { + return (CmpInstruction) addInstruction(Constants.FCMPL); + } + + /** + * Compare the top two stack values; the fcmpg opcode. + */ + public CmpInstruction fcmpg() { + return (CmpInstruction) addInstruction(Constants.FCMPG); + } + + /** + * Compare the top two stack values; the dcmpl opcode. + */ + public CmpInstruction dcmpl() { + return (CmpInstruction) addInstruction(Constants.DCMPL); + } + + /** + * Compare the top two stack values; the dcmpg opcode. + */ + public CmpInstruction dcmpg() { + return (CmpInstruction) addInstruction(Constants.DCMPG); + } + + /** + * The ifeq opcode. + */ + public IfInstruction ifeq() { + return (IfInstruction) addInstruction(Constants.IFEQ); + } + + /** + * The ifne opcode. + */ + public IfInstruction ifne() { + return (IfInstruction) addInstruction(Constants.IFNE); + } + + /** + * The iflt opcode. + */ + public IfInstruction iflt() { + return (IfInstruction) addInstruction(Constants.IFLT); + } + + /** + * The ifge opcode. + */ + public IfInstruction ifge() { + return (IfInstruction) addInstruction(Constants.IFGE); + } + + /** + * The ifgt opcode. + */ + public IfInstruction ifgt() { + return (IfInstruction) addInstruction(Constants.IFGT); + } + + /** + * The ifle opcode. + */ + public IfInstruction ifle() { + return (IfInstruction) addInstruction(Constants.IFLE); + } + + /** + * The ificmpeq opcode. + */ + public IfInstruction ificmpeq() { + return (IfInstruction) addInstruction(Constants.IFICMPEQ); + } + + /** + * The ificmpne opcode. + */ + public IfInstruction ificmpne() { + return (IfInstruction) addInstruction(Constants.IFICMPNE); + } + + /** + * The ificmplt opcode. + */ + public IfInstruction ificmplt() { + return (IfInstruction) addInstruction(Constants.IFICMPLT); + } + + /** + * The ificmpge opcode. + */ + public IfInstruction ificmpge() { + return (IfInstruction) addInstruction(Constants.IFICMPGE); + } + + /** + * The ificmpgt opcode. + */ + public IfInstruction ificmpgt() { + return (IfInstruction) addInstruction(Constants.IFICMPGT); + } + + /** + * The ificmple opcode. + */ + public IfInstruction ificmple() { + return (IfInstruction) addInstruction(Constants.IFICMPLE); + } + + /** + * The ifacmpeq opcode. + */ + public IfInstruction ifacmpeq() { + return (IfInstruction) addInstruction(Constants.IFACMPEQ); + } + + /** + * The ifacmpne opcode. + */ + public IfInstruction ifacmpne() { + return (IfInstruction) addInstruction(Constants.IFACMPNE); + } + + /** + * The ifnull opcode. + */ + public IfInstruction ifnull() { + return (IfInstruction) addInstruction(Constants.IFNULL); + } + + /** + * The ifnonnull opcode. + */ + public IfInstruction ifnonnull() { + return (IfInstruction) addInstruction(Constants.IFNONNULL); + } + + /** + * The go2 opcode. + */ + public JumpInstruction go2() { + return (JumpInstruction) addInstruction(Constants.GOTO); + } + + /** + * The jsr opcode used in implementing finally + * clauses. + */ + public JumpInstruction jsr() { + return (JumpInstruction) addInstruction(Constants.JSR); + } + + /** + * The tableswitch opcode. + */ + public TableSwitchInstruction tableswitch() { + return (TableSwitchInstruction) addInstruction(Constants.TABLESWITCH); + } + + /** + * The lookupswitch opcode. + */ + public LookupSwitchInstruction lookupswitch() { + return (LookupSwitchInstruction) addInstruction(Constants.LOOKUPSWITCH); + } + + /** + * Return from a method. This method will result in a + * nop until its type is set. + */ + public ReturnInstruction xreturn() { + return (ReturnInstruction) addInstruction(new ReturnInstruction(this)); + } + + /** + * Return void from a method; the return opcode. + */ + public ReturnInstruction vreturn() { + return (ReturnInstruction) addInstruction(Constants.RETURN); + } + + /** + * Return an int from a method; the ireturn opcode. + */ + public ReturnInstruction ireturn() { + return (ReturnInstruction) addInstruction(Constants.IRETURN); + } + + /** + * Return a long from a method; the lreturn opcode. + */ + public ReturnInstruction lreturn() { + return (ReturnInstruction) addInstruction(Constants.LRETURN); + } + + /** + * Return a float from a method; the freturn opcode. + */ + public ReturnInstruction freturn() { + return (ReturnInstruction) addInstruction(Constants.FRETURN); + } + + /** + * Return a double from a method; the dreturn opcode. + */ + public ReturnInstruction dreturn() { + return (ReturnInstruction) addInstruction(Constants.DRETURN); + } + + /** + * Return an object from a method; the areturn opcode. + */ + public ReturnInstruction areturn() { + return (ReturnInstruction) addInstruction(Constants.ARETURN); + } + + /** + * Load the value from a field onto the stack; the getfield + * opcode. + */ + public GetFieldInstruction getfield() { + return (GetFieldInstruction) addInstruction(Constants.GETFIELD); + } + + /** + * Load the value from a static field onto the stack; the + * getstatic opcode. + */ + public GetFieldInstruction getstatic() { + return (GetFieldInstruction) addInstruction(Constants.GETSTATIC); + } + + /** + * Place the value of a field onto the stack; the putfield + * opcode. + */ + public PutFieldInstruction putfield() { + return (PutFieldInstruction) addInstruction(Constants.PUTFIELD); + } + + /** + * Place the value of a static field onto the stack; the + * putstatic opcode. + */ + public PutFieldInstruction putstatic() { + return (PutFieldInstruction) addInstruction(Constants.PUTSTATIC); + } + + /** + * Invoke a virtual method; the invokevirtual opcode. + */ + public MethodInstruction invokevirtual() { + return (MethodInstruction) addInstruction(Constants.INVOKEVIRTUAL); + } + + /** + * Invoke a method non-virtually, as for constructors and superclass + * methods; the invokespecial opcode. + */ + public MethodInstruction invokespecial() { + return (MethodInstruction) addInstruction(Constants.INVOKESPECIAL); + } + + /** + * Invoke a method on an interface; the invokeinterface + * opcode. + */ + public MethodInstruction invokeinterface() { + return (MethodInstruction) addInstruction(Constants.INVOKEINTERFACE); + } + + /** + * Invoke a static method; the invokestatic opcode. + */ + public MethodInstruction invokestatic() { + return (MethodInstruction) addInstruction(Constants.INVOKESTATIC); + } + + /** + * Create a new instance of an object; the new opcode. + */ + public ClassInstruction anew() { + return (ClassInstruction) addInstruction(Constants.NEW); + } + + /** + * Create a new instance of an object array; the anew opcode. + */ + public ClassInstruction anewarray() { + return (ClassInstruction) addInstruction(Constants.ANEWARRAY); + } + + /** + * Cast an object on the stack to another type; the checkcast + * opcode. + */ + public ClassInstruction checkcast() { + return (ClassInstruction) addInstruction(Constants.CHECKCAST); + } + + /** + * Test if a stack object is an instance of a class; the + * instanceof opcode. + */ + public ClassInstruction isinstance() { + return (ClassInstruction) addInstruction(Constants.INSTANCEOF); + } + + /** + * Create a new multidimensional array; the multianewarray + * opcode. + */ + public MultiANewArrayInstruction multianewarray() { + return (MultiANewArrayInstruction) addInstruction(Constants.MULTIANEWARRAY); + } + + /** + * Create a new array of a primitive type; the newarray + * opcode. + */ + public NewArrayInstruction newarray() { + return (NewArrayInstruction) addInstruction(Constants.NEWARRAY); + } + + /** + * Get the length of an array on the stack; the arraylength + * opcode. + */ + public Instruction arraylength() { + return addInstruction(Constants.ARRAYLENGTH); + } + + /** + * Throw an exception; the athrow opcode. + */ + public Instruction athrow() { + return addInstruction(Constants.ATHROW); + } + + /** + * The monitorenter opcode. + */ + public MonitorEnterInstruction monitorenter() { + return (MonitorEnterInstruction) addInstruction(Constants.MONITORENTER); + } + + /** + * The monitorexit opcode. + */ + public MonitorExitInstruction monitorexit() { + return (MonitorExitInstruction) addInstruction(Constants.MONITOREXIT); + } + + ///////////////////////// + // Wholisitic operations + ///////////////////////// + + /** + * Return all the Instructions of this method. + */ + public Instruction[] getInstructions() { + Instruction[] arr = new Instruction[_size]; + int i = 0; + + for (CodeEntry entry = _head.next; entry != _tail; + entry = entry.next) + arr[i++] = (Instruction) entry; + + return arr; + } + + int getLength() { + // covers maxStack, maxLocals, codeLength, exceptionTableLength, + // attributeCount + int length = 12; + + // add code + try { + length += toByteArray().length; + } catch (IOException ioe) { + throw new RuntimeException(ioe.toString()); + } + + // add exception reps; each is 8 bytes + length += (8 * _handlers.size()); + + // add all attribute lengths + Attribute[] attrs = getAttributes(); + + for (int i = 0; i < attrs.length; i++) + length += (attrs[i].getLength() + 6); + + return length; + } + + public void acceptVisit(BCVisitor visit) { + visit.enterCode(this); + + Instruction ins; + + for (CodeEntry entry = _head.next; entry != _tail; + entry = entry.next) { + ins = (Instruction) entry; + visit.enterInstruction(ins); + ins.acceptVisit(visit); + visit.exitInstruction(ins); + } + + for (Iterator i = _handlers.iterator(); i.hasNext();) + ((ExceptionHandler) i.next()).acceptVisit(visit); + + visitAttributes(visit); + + visit.exitCode(this); + } + + ////////////////////////// + // Convenience operations + ////////////////////////// + + /** + * Return line number information for the code. + * Acts internally through the {@link Attributes} interface. + * + * @param add if true, a new line number table will be added + * if not already present + * @return the line number information, or null if none + * and the add param is set to false + */ + public LineNumberTable getLineNumberTable(boolean add) { + LineNumberTable attr = (LineNumberTable) getAttribute(Constants.ATTR_LINENUMBERS); + + if (!add || (attr != null)) { + return attr; + } + + return (LineNumberTable) addAttribute(Constants.ATTR_LINENUMBERS); + } + + /** + * Remove the line number table for the code. + * Acts internally through the {@link Attributes} interface. + * + * @return true if there was a table to remove + */ + public boolean removeLineNumberTable() { + return removeAttribute(Constants.ATTR_LINENUMBERS); + } + + /** + * Return local variable information for the code. + * Acts internally through the {@link Attributes} interface. + * + * @param add if true, a new local variable table will be + * added if not already present + * @return the local variable information, or null if none + * and the add param is set to false + */ + public LocalVariableTable getLocalVariableTable(boolean add) { + LocalVariableTable attr = (LocalVariableTable) getAttribute(Constants.ATTR_LOCALS); + + if (!add || (attr != null)) { + return attr; + } + + return (LocalVariableTable) addAttribute(Constants.ATTR_LOCALS); + } + + /** + * Remove the local variable table for the code. + * Acts internally through the {@link Attributes} interface. + * + * @return true if there was a table to remove + */ + public boolean removeLocalVariableTables() { + return removeAttribute(Constants.ATTR_LOCALS); + } + + /** + * Return local variable generics information for the code. + * Acts internally through the {@link Attributes} interface. + * + * @param add if true, a new local variable type table will be + * added if not already present + * @return the local variable type information, or null if none + * and the add param is set to false + */ + public LocalVariableTypeTable getLocalVariableTypeTable(boolean add) { + LocalVariableTypeTable attr = (LocalVariableTypeTable) getAttribute(Constants.ATTR_LOCAL_TYPES); + + if (!add || (attr != null)) { + return attr; + } + + return (LocalVariableTypeTable) addAttribute(Constants.ATTR_LOCAL_TYPES); + } + + /** + * Remove the local variable type table for the code. + * Acts internally through the {@link Attributes} interface. + * + * @return true if there was a table to remove + */ + public boolean removeLocalVariableTypeTables() { + return removeAttribute(Constants.ATTR_LOCAL_TYPES); + } + + ////////////////// + // I/O operations + ////////////////// + void read(Attribute attr) { + Code orig = (Code) attr; + + _maxStack = orig.getMaxStack(); + _maxLocals = orig.getMaxLocals(); + + // clear existing code + _head.next = _tail; + _tail.prev = _head; + _size = 0; + beforeFirst(); + _handlers.clear(); + + // copy all instructions; don't set constant instruction values until + // instruction ptrs have been updated in case the instruction width + // changes because of differences in the constant pool (LDC vs LDCW) + Instruction ins; + Instruction origIns; + + for (CodeEntry entry = orig._head.next; entry != orig._tail; + entry = entry.next) { + origIns = (Instruction) entry; + ins = addInstruction(origIns.getOpcode()); + + if (!(ins instanceof ConstantInstruction)) { + ins.read(origIns); + } + } + + // copy exception handlers + ExceptionHandler[] origHandlers = orig.getExceptionHandlers(); + ExceptionHandler handler; + + for (int i = 0; i < origHandlers.length; i++) { + handler = addExceptionHandler(); + handler.read(origHandlers[i]); + handler.updateTargets(); + } + + // reset all opcode ptrs to the new copied opcodes + updateInstructionPointers(); + setAttributes(orig.getAttributes()); + + // setup local variable markers + LocalVariableTable locals = getLocalVariableTable(false); + + if (locals != null) { + locals.updateTargets(); + } + + // setup local variable markers + LocalVariableTypeTable localTypes = getLocalVariableTypeTable(false); + + if (localTypes != null) { + localTypes.updateTargets(); + } + + // setup line number markers + LineNumberTable lines = getLineNumberTable(false); + + if (lines != null) { + lines.updateTargets(); + } + + // now copy constant instruction values + CodeEntry copy = _head.next; + + for (CodeEntry entry = orig._head.next; entry != orig._tail; + entry = entry.next, copy = copy.next) { + if (entry instanceof ConstantInstruction) { + ((ConstantInstruction) copy).read((Instruction) entry); + } + } + + beforeFirst(); + } + + void read(DataInput in, int length) throws IOException { + _maxStack = in.readUnsignedShort(); + _maxLocals = in.readUnsignedShort(); + + readCode(in, in.readInt()); + + _handlers.clear(); + + int exceptionCount = in.readUnsignedShort(); + ExceptionHandler excep; + + for (int i = 0; i < exceptionCount; i++) { + excep = addExceptionHandler(); + excep.read(in); + excep.updateTargets(); + } + + readAttributes(in); + + // setup local variable markers + LocalVariableTable locals = getLocalVariableTable(false); + + if (locals != null) { + locals.updateTargets(); + } + + // setup local variable markers + LocalVariableTypeTable localTypes = getLocalVariableTypeTable(false); + + if (localTypes != null) { + localTypes.updateTargets(); + } + + // setup line number markers + LineNumberTable lines = getLineNumberTable(false); + + if (lines != null) { + lines.updateTargets(); + } + } + + void write(DataOutput out, int length) throws IOException { + out.writeShort(_maxStack); + out.writeShort(_maxLocals); + + byte[] code = toByteArray(); + out.writeInt(code.length); + out.write(code); + + out.writeShort(_handlers.size()); + + for (Iterator itr = _handlers.iterator(); itr.hasNext();) + ((ExceptionHandler) itr.next()).write(out); + + writeAttributes(out); + } + + private void readCode(DataInput in, int len) throws IOException { + _head.next = _tail; + _tail.prev = _head; + _size = 0; + beforeFirst(); + + Instruction ins; + + for (int byteIndex = 0; byteIndex < len;) { + ins = addInstruction(in.readUnsignedByte()); + ins.read(in); + byteIndex += ins.getLength(); + } + + updateInstructionPointers(); + beforeFirst(); + } + + /** + * Ensures that all the opcode targets are set up correctly. + */ + private void updateInstructionPointers() { + for (CodeEntry entry = _head.next; entry != _tail; + entry = entry.next) + if (entry instanceof InstructionPtr) { + ((InstructionPtr) entry).updateTargets(); + } + } + + /** + * Returns the byteIndex of the given instruction. + */ + int getByteIndex(Instruction ins) { + int byteIndex = 0; + + for (CodeEntry entry = _head.next; entry != _tail; + entry = entry.next) { + if (entry == ins) { + return byteIndex; + } + + byteIndex += ((Instruction) entry).getLength(); + } + + throw new IllegalArgumentException("ins.owner != this"); + } + + /** + * Returns the instruction in this code block found at the given + * byte index. + */ + Instruction getInstruction(int byteIndex) { + if (byteIndex < 0) { + return null; + } + + int curIndex = 0; + + for (CodeEntry entry = _head.next; entry != _tail; + entry = entry.next) { + if (byteIndex == curIndex) { + return (Instruction) entry; + } + + curIndex += ((Instruction) entry).getLength(); + } + + throw new IllegalArgumentException(String.valueOf(byteIndex)); + } + + /** + * Returns the number of instructions that occur before 'ins' + * in this code block that 'ins' is a part of. + * + * @throws IllegalArgumentException if this code block is not the owner + * of ins + */ + private int indexOf(Instruction ins) { + int i = 0; + + for (CodeEntry entry = _head.next; entry != _tail; + entry = entry.next, i++) + if (entry == ins) { + return i; + } + + throw new IllegalArgumentException("ins.code != this"); + } + + private void writeCode(DataOutput out) throws IOException { + Instruction ins; + + for (CodeEntry entry = _head.next; entry != _tail; + entry = entry.next) { + ins = (Instruction) entry; + out.writeByte(ins.getOpcode()); + ins.write(out); + } + } + + private byte[] toByteArray() throws IOException { + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); + DataOutputStream stream = new DataOutputStream(byteStream); + + try { + writeCode(stream); + + return byteStream.toByteArray(); + } finally { + try { + stream.close(); + } catch (Exception e) { + } + } + } + + private void fromByteArray(byte[] code) throws IOException { + if (code == null) { + _head.next = _tail; + _tail.prev = _head; + _size = 0; + } else { + DataInputStream stream = new DataInputStream(new ByteArrayInputStream( + code)); + + try { + readCode(stream, code.length); + } finally { + try { + stream.close(); + } catch (Exception e) { + } + } + } + } + + private Instruction addInstruction(Instruction ins) { + _ci.add(ins); + + return ins; + } + + private Instruction addInstruction(int opcode) { + return addInstruction(createInstruction(opcode)); + } + + /** + * Creates an Instruction, with this code block as the owner. + * Note that the Instruction is not added to this Code block. + */ + private Instruction createInstruction(int opcode) { + switch (opcode) { + case Constants.NOP: + case Constants.ARRAYLENGTH: + case Constants.ATHROW: + return new Instruction(this, opcode); + + case Constants.ACONSTNULL: + case Constants.ICONSTM1: + case Constants.ICONST0: + case Constants.ICONST1: + case Constants.ICONST2: + case Constants.ICONST3: + case Constants.ICONST4: + case Constants.ICONST5: + case Constants.LCONST0: + case Constants.LCONST1: + case Constants.FCONST0: + case Constants.FCONST1: + case Constants.FCONST2: + case Constants.DCONST0: + case Constants.DCONST1: + case Constants.BIPUSH: + case Constants.SIPUSH: + case Constants.LDC: + case Constants.LDCW: + case Constants.LDC2W: + return new ConstantInstruction(this, opcode); + + case Constants.ILOAD: + case Constants.LLOAD: + case Constants.FLOAD: + case Constants.DLOAD: + case Constants.ALOAD: + case Constants.ILOAD0: + case Constants.ILOAD1: + case Constants.ILOAD2: + case Constants.ILOAD3: + case Constants.LLOAD0: + case Constants.LLOAD1: + case Constants.LLOAD2: + case Constants.LLOAD3: + case Constants.FLOAD0: + case Constants.FLOAD1: + case Constants.FLOAD2: + case Constants.FLOAD3: + case Constants.DLOAD0: + case Constants.DLOAD1: + case Constants.DLOAD2: + case Constants.DLOAD3: + case Constants.ALOAD0: + case Constants.ALOAD1: + case Constants.ALOAD2: + case Constants.ALOAD3: + return new LoadInstruction(this, opcode); + + case Constants.IALOAD: + case Constants.LALOAD: + case Constants.FALOAD: + case Constants.DALOAD: + case Constants.AALOAD: + case Constants.BALOAD: + case Constants.CALOAD: + case Constants.SALOAD: + return new ArrayLoadInstruction(this, opcode); + + case Constants.ISTORE: + case Constants.LSTORE: + case Constants.FSTORE: + case Constants.DSTORE: + case Constants.ASTORE: + case Constants.ISTORE0: + case Constants.ISTORE1: + case Constants.ISTORE2: + case Constants.ISTORE3: + case Constants.LSTORE0: + case Constants.LSTORE1: + case Constants.LSTORE2: + case Constants.LSTORE3: + case Constants.FSTORE0: + case Constants.FSTORE1: + case Constants.FSTORE2: + case Constants.FSTORE3: + case Constants.DSTORE0: + case Constants.DSTORE1: + case Constants.DSTORE2: + case Constants.DSTORE3: + case Constants.ASTORE0: + case Constants.ASTORE1: + case Constants.ASTORE2: + case Constants.ASTORE3: + return new StoreInstruction(this, opcode); + + case Constants.IASTORE: + case Constants.LASTORE: + case Constants.FASTORE: + case Constants.DASTORE: + case Constants.AASTORE: + case Constants.BASTORE: + case Constants.CASTORE: + case Constants.SASTORE: + return new ArrayStoreInstruction(this, opcode); + + case Constants.POP: + case Constants.POP2: + case Constants.DUP: + case Constants.DUPX1: + case Constants.DUPX2: + case Constants.DUP2: + case Constants.DUP2X1: + case Constants.DUP2X2: + case Constants.SWAP: + return new StackInstruction(this, opcode); + + case Constants.IADD: + case Constants.LADD: + case Constants.FADD: + case Constants.DADD: + case Constants.ISUB: + case Constants.LSUB: + case Constants.FSUB: + case Constants.DSUB: + case Constants.IMUL: + case Constants.LMUL: + case Constants.FMUL: + case Constants.DMUL: + case Constants.IDIV: + case Constants.LDIV: + case Constants.FDIV: + case Constants.DDIV: + case Constants.IREM: + case Constants.LREM: + case Constants.FREM: + case Constants.DREM: + case Constants.INEG: + case Constants.LNEG: + case Constants.FNEG: + case Constants.DNEG: + case Constants.ISHL: + case Constants.LSHL: + case Constants.ISHR: + case Constants.LSHR: + case Constants.IUSHR: + case Constants.LUSHR: + case Constants.IAND: + case Constants.LAND: + case Constants.IOR: + case Constants.LOR: + case Constants.IXOR: + case Constants.LXOR: + return new MathInstruction(this, opcode); + + case Constants.IINC: + return new IIncInstruction(this); + + case Constants.I2L: + case Constants.I2F: + case Constants.I2D: + case Constants.L2I: + case Constants.L2F: + case Constants.L2D: + case Constants.F2I: + case Constants.F2L: + case Constants.F2D: + case Constants.D2I: + case Constants.D2L: + case Constants.D2F: + case Constants.I2B: + case Constants.I2C: + case Constants.I2S: + return new ConvertInstruction(this, opcode); + + case Constants.LCMP: + case Constants.FCMPL: + case Constants.FCMPG: + case Constants.DCMPL: + case Constants.DCMPG: + return new CmpInstruction(this, opcode); + + case Constants.IFEQ: + case Constants.IFNE: + case Constants.IFLT: + case Constants.IFGE: + case Constants.IFGT: + case Constants.IFLE: + case Constants.IFICMPEQ: + case Constants.IFICMPNE: + case Constants.IFICMPLT: + case Constants.IFICMPGE: + case Constants.IFICMPGT: + case Constants.IFICMPLE: + case Constants.IFACMPEQ: + case Constants.IFACMPNE: + case Constants.IFNULL: + case Constants.IFNONNULL: + return new IfInstruction(this, opcode); + + case Constants.GOTO: + case Constants.JSR: + case Constants.GOTOW: + case Constants.JSRW: + return new JumpInstruction(this, opcode); + + case Constants.RET: + return new RetInstruction(this); + + case Constants.TABLESWITCH: + return new TableSwitchInstruction(this); + + case Constants.LOOKUPSWITCH: + return new LookupSwitchInstruction(this); + + case Constants.IRETURN: + case Constants.LRETURN: + case Constants.FRETURN: + case Constants.DRETURN: + case Constants.ARETURN: + case Constants.RETURN: + return new ReturnInstruction(this, opcode); + + case Constants.GETSTATIC: + case Constants.GETFIELD: + return new GetFieldInstruction(this, opcode); + + case Constants.PUTSTATIC: + case Constants.PUTFIELD: + return new PutFieldInstruction(this, opcode); + + case Constants.INVOKEVIRTUAL: + case Constants.INVOKESPECIAL: + case Constants.INVOKESTATIC: + case Constants.INVOKEINTERFACE: + return new MethodInstruction(this, opcode); + + case Constants.NEW: + case Constants.ANEWARRAY: + case Constants.CHECKCAST: + case Constants.INSTANCEOF: + return new ClassInstruction(this, opcode); + + case Constants.NEWARRAY: + return new NewArrayInstruction(this); + + case Constants.MONITORENTER: + return new MonitorEnterInstruction(this); + + case Constants.MONITOREXIT: + return new MonitorExitInstruction(this); + + case Constants.WIDE: + return new WideInstruction(this); + + case Constants.MULTIANEWARRAY: + return new MultiANewArrayInstruction(this); + + default: + throw new IllegalArgumentException("Illegal opcode: " + opcode); + } + } + + /** + * Returns another listIterator view of the Instructions in this + * code block. Useful for performing read-only searches through + * Instructions without effecting the pointer location of the main + * code block. + */ + public ListIterator listIterator() { + return new CodeIterator(_head, -1); + } + + /** + * Helper class to handle invalidation of instructions on removal + * and notification of modification on addition. + */ + private class CodeIterator implements ListIterator { + public static final int UNSET = -99; + private CodeEntry _bn = null; // "before next" entry + private Instruction _last = null; // last entry returned + private int _index = UNSET; // index of _bn + + public CodeIterator(CodeEntry entry, int index) { + _bn = entry; + _index = index; + } + + public boolean hasNext() { + return _bn.next != _tail; + } + + public boolean hasPrevious() { + return _bn != _head; + } + + public Object next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + + _bn = _bn.next; + _last = (Instruction) _bn; + + if (_index != UNSET) { + _index++; + } + + return _last; + } + + public int nextIndex() { + return initIndex() + 1; + } + + public Object previous() { + if (!hasPrevious()) { + throw new NoSuchElementException(); + } + + _last = (Instruction) _bn; + _bn = _bn.prev; + + if (_index != UNSET) { + _index--; + } + + return _last; + } + + public int previousIndex() { + return initIndex(); + } + + private int initIndex() { + if (_index == UNSET) { + if (_bn == _head) { + _index = -1; + } else { + _index = indexOf((Instruction) _bn); + } + } + + return _index; + } + + public void add(Object obj) { + if (obj == null) { + throw new NullPointerException("obj = null"); + } + + Instruction ins = (Instruction) obj; + + if (_size == 0) { + _head.next = ins; + _tail.prev = ins; + ins.prev = _head; + ins.next = _tail; + _index = 0; + } else { + CodeEntry next = _bn.next; + _bn.next = ins; + next.prev = ins; + ins.prev = _bn; + ins.next = next; + + if (_index != UNSET) { + _index++; + } + } + + _bn = ins; + _last = ins; + _size++; + } + + public void set(Object obj) { + if (obj == null) { + throw new NullPointerException("obj = null"); + } + + if (_last == null) { + throw new IllegalStateException(); + } + + Instruction ins = (Instruction) obj; + ins.prev = _last.prev; + ins.next = _last.next; + ins.prev.next = ins; + ins.next.prev = ins; + + replaceTarget(_last, ins); + _last.invalidate(); + + if (_bn == _last) { + _bn = ins; + } + + _last = ins; + } + + public void remove() { + if (_last == null) { + throw new IllegalStateException(); + } + + if (_bn == _last) { + _bn = _last.prev; + } + + _index--; + + _last.prev.next = _last.next; + _last.next.prev = _last.prev; + _size--; + + Instruction orig = _last; + Instruction replace = null; + + if (orig.next != _tail) { + replace = (Instruction) orig.next; + } else { + replace = nop(); + } + + replaceTarget(orig, replace); + + orig.invalidate(); + _last = null; + } + + private void replaceTarget(Instruction orig, Instruction replace) { + for (CodeEntry entry = _head.next; entry != _tail; + entry = entry.next) + if (entry instanceof InstructionPtr) { + ((InstructionPtr) entry).replaceTarget(orig, replace); + } + + // update the ExceptionHandler pointers + ExceptionHandler[] handlers = getExceptionHandlers(); + + for (int i = 0; i < handlers.length; i++) + handlers[i].replaceTarget(orig, replace); + + // update LineNumber pointers + LineNumberTable lineNumbers = getLineNumberTable(false); + + if (lineNumbers != null) { + lineNumbers.replaceTarget(orig, replace); + } + + // update LocalVariable pointers + LocalVariableTable variables = getLocalVariableTable(false); + + if (variables != null) { + variables.replaceTarget(orig, replace); + } + + // update LocalVariableType pointers + LocalVariableTypeTable types = getLocalVariableTypeTable(false); + + if (types != null) { + types.replaceTarget(orig, replace); + } + } + } +} diff --git a/serp/src/main/java/serp/bytecode/CodeEntry.java b/serp/src/main/java/serp/bytecode/CodeEntry.java new file mode 100755 index 000000000..ef18b0a7c --- /dev/null +++ b/serp/src/main/java/serp/bytecode/CodeEntry.java @@ -0,0 +1,27 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + + +/** + *

An entry in a code block.

+ * + * @author Abe White + */ +class CodeEntry { + CodeEntry next = null; + CodeEntry prev = null; +} diff --git a/serp/src/main/java/serp/bytecode/ConstantInstruction.java b/serp/src/main/java/serp/bytecode/ConstantInstruction.java new file mode 100755 index 000000000..28290ac99 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/ConstantInstruction.java @@ -0,0 +1,531 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import serp.bytecode.visitor.*; + +import serp.util.*; + +import java.io.*; + + +/** + *

An instruction that that loads a constant onto the stack. + * The opcode represented by this instruction may change depending on the + * type and value of the constant set. For example, if the constant value + * is initially set to 5, the opcode will be iconst5; if later + * incremented to 6, the opcode will be changed to bipush(6).

+ * + * @author Abe White + */ +public class ConstantInstruction extends TypedInstruction { + private int _arg = -1; + + ConstantInstruction(Code owner) { + super(owner); + } + + ConstantInstruction(Code owner, int opcode) { + super(owner, opcode); + } + + int getLength() { + switch (getOpcode()) { + case Constants.BIPUSH: + case Constants.LDC: + return super.getLength() + 1; + + case Constants.SIPUSH: + case Constants.LDCW: + case Constants.LDC2W: + return super.getLength() + 2; + + default: + return super.getLength(); + } + } + + public int getStackChange() { + String type = getTypeName(); + + if (double.class.getName().equals(type) || + long.class.getName().equals(type)) { + return 2; + } + + return 1; + } + + public int getLogicalStackChange() { + return 1; + } + + public String getTypeName() { + int opcode = getOpcode(); + + switch (opcode) { + case Constants.NOP: + return null; + + case Constants.ACONSTNULL: + return Object.class.getName(); + + case Constants.ICONSTM1: + case Constants.ICONST0: + case Constants.ICONST1: + case Constants.ICONST2: + case Constants.ICONST3: + case Constants.ICONST4: + case Constants.ICONST5: + case Constants.BIPUSH: + case Constants.SIPUSH: + return int.class.getName(); + + case Constants.LCONST0: + case Constants.LCONST1: + return long.class.getName(); + + case Constants.FCONST0: + case Constants.FCONST1: + case Constants.FCONST2: + return float.class.getName(); + + case Constants.DCONST0: + case Constants.DCONST1: + return double.class.getName(); + } + + Entry entry = getPool().getEntry(_arg); + + switch (entry.getType()) { + case Entry.UTF8: + case Entry.STRING: + return String.class.getName(); + + case Entry.INT: + return int.class.getName(); + + case Entry.FLOAT: + return float.class.getName(); + + case Entry.LONG: + return long.class.getName(); + + case Entry.DOUBLE: + return double.class.getName(); + + case Entry.CLASS: + return Class.class.getName(); + + default: + return null; + } + } + + public TypedInstruction setType(String type) { + throw new UnsupportedOperationException("Use setValue"); + } + + /** + * Return the value of the constant as its wrapper type, or null if + * not set. Returns class values as the class name. + */ + public Object getValue() { + int opcode = getOpcode(); + + switch (opcode) { + case Constants.NOP: + case Constants.ACONSTNULL: + return null; + + case Constants.ICONSTM1: + case Constants.ICONST0: + case Constants.ICONST1: + case Constants.ICONST2: + case Constants.ICONST3: + case Constants.ICONST4: + case Constants.ICONST5: + return Numbers.valueOf(opcode - Constants.ICONST0); + + case Constants.LCONST0: + case Constants.LCONST1: + return Numbers.valueOf((long) (opcode - Constants.LCONST0)); + + case Constants.FCONST0: + case Constants.FCONST1: + case Constants.FCONST2: + return new Float(opcode - Constants.FCONST0); + + case Constants.DCONST0: + case Constants.DCONST1: + return new Double(opcode - Constants.DCONST0); + + case Constants.BIPUSH: + case Constants.SIPUSH: + return Numbers.valueOf(_arg); + + default: + + Entry entry = getPool().getEntry(_arg); + Object val = ((ConstantEntry) entry).getConstant(); + + if (entry.getType() == Entry.CLASS) { + return getProject().getNameCache() + .getExternalForm((String) val, false); + } + + return val; + } + } + + /** + * Set the constant to the given value. The value should be + * an instance of String, Integer, Long, Double, Float, Class, BCClass, or + * null depending on the constant type. If the given value is not + * supported directly, it will be converted accordingly. + * + * @return this instruction, for method chaining + */ + public ConstantInstruction setValue(Object value) { + if (value instanceof Boolean) { + value = Numbers.valueOf((((Boolean) value).booleanValue()) ? 1 : 0); + } else if (value instanceof Character) { + value = Numbers.valueOf((int) ((Character) value).charValue()); + } else if (value instanceof Byte) { + value = Numbers.valueOf(((Byte) value).intValue()); + } else if (value instanceof Short) { + value = Numbers.valueOf(((Short) value).intValue()); + } else if ((value != null) && !(value instanceof Number) && + !(value instanceof String) && !(value instanceof Class) && + !(value instanceof BCClass)) { + throw new IllegalArgumentException("value = " + value); + } + + calculateOpcode(value, false); + + return this; + } + + /** + * Return the string value of this constant, or null if not set. + */ + public String getStringValue() { + return (String) getValue(); + } + + /** + * Return the int value of this constant, or 0 if not set. + */ + public int getIntValue() { + Object value = getValue(); + + return (value == null) ? 0 : ((Number) value).intValue(); + } + + /** + * Return the long value of this constant, or 0 if not set. + */ + public long getLongValue() { + Object value = getValue(); + + return (value == null) ? 0L : ((Number) value).longValue(); + } + + /** + * Return the float value of this constant, or 0 if not set. + */ + public float getFloatValue() { + Object value = getValue(); + + return (value == null) ? 0F : ((Number) value).floatValue(); + } + + /** + * Return the double value of this constant, or 0 if not set. + */ + public double getDoubleValue() { + Object value = getValue(); + + return (value == null) ? 0D : ((Number) value).doubleValue(); + } + + /** + * Return the class value of this constant, or null if not set. + */ + public String getClassNameValue() { + return (String) getValue(); + } + + /** + * Set this constant to null. + * + * @return this instruction, for method chaining + */ + public ConstantInstruction setNull() { + calculateOpcode(null, false); + + return this; + } + + /** + * Set the value of this constant. + * + * @return this instruction, for method chaining + */ + public ConstantInstruction setValue(String value) { + calculateOpcode(value, false); + + return this; + } + + /** + * Set the value of this constant. + * + * @return this instruction, for method chaining + */ + public ConstantInstruction setValue(Class value) { + calculateOpcode(value, false); + + return this; + } + + /** + * Set the value of this constant. + * + * @return this instruction, for method chaining + */ + public ConstantInstruction setValue(BCClass value) { + calculateOpcode(value, false); + + return this; + } + + /** + * Set the value of this constant. + * + * @return this instruction, for method chaining + */ + public ConstantInstruction setValue(int value) { + calculateOpcode(Numbers.valueOf(value), false); + + return this; + } + + /** + * Set the value of this constant. + * + * @return this instruction, for method chaining + */ + public ConstantInstruction setValue(long value) { + calculateOpcode(Numbers.valueOf(value), false); + + return this; + } + + /** + * Set the value of this constant. + * + * @return this instruction, for method chaining + */ + public ConstantInstruction setValue(float value) { + calculateOpcode(new Float(value), false); + + return this; + } + + /** + * Set the value of this constant. + * + * @return this instruction, for method chaining + */ + public ConstantInstruction setValue(double value) { + calculateOpcode(new Double(value), false); + + return this; + } + + /** + * Set the value of this constant; note that this type is converted + * to int. + * + * @return this instruction, for method chaining + */ + public ConstantInstruction setValue(boolean value) { + return setValue((value) ? 1 : 0); + } + + /** + * Set the value of this constant; note that this type is converted + * to int. + * + * @return this instruction, for method chaining + */ + public ConstantInstruction setValue(short value) { + return setValue((int) value); + } + + /** + * Set the value of this constant; note that this type is converted + * to int. + * + * @return this instruction, for method chaining + */ + public ConstantInstruction setValue(char value) { + return setValue((int) value); + } + + /** + * ConstantInstructions are equal if the const they reference is the same, + * or if the const of either is unset. + */ + public boolean equalsInstruction(Instruction other) { + if (this == other) { + return true; + } + + if (!(other instanceof ConstantInstruction)) { + return false; + } + + Object value = getValue(); + Object otherValue = ((ConstantInstruction) other).getValue(); + + return (value == null) || (otherValue == null) || + value.equals(otherValue); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterConstantInstruction(this); + visit.exitConstantInstruction(this); + } + + void read(Instruction orig) { + super.read(orig); + + ConstantInstruction ci = (ConstantInstruction) orig; + calculateOpcode(ci.getValue(), ci.getOpcode() == Constants.LDCW); + } + + void read(DataInput in) throws IOException { + super.read(in); + + switch (getOpcode()) { + case Constants.BIPUSH: + case Constants.LDC: + _arg = in.readUnsignedByte(); + + break; + + case Constants.SIPUSH: + case Constants.LDCW: + case Constants.LDC2W: + _arg = in.readUnsignedShort(); + } + } + + void write(DataOutput out) throws IOException { + super.write(out); + + switch (getOpcode()) { + case Constants.BIPUSH: + case Constants.LDC: + out.writeByte(_arg); + + break; + + case Constants.SIPUSH: + case Constants.LDCW: + case Constants.LDC2W: + out.writeShort(_arg); + + break; + } + } + + private void calculateOpcode(Object value, boolean wide) { + _arg = -1; + + if (value == null) { + setOpcode(Constants.ACONSTNULL); + } else if (value instanceof Float) { + float floatVal = ((Float) value).floatValue(); + + if ((floatVal == 0) || (floatVal == 1) || (floatVal == 2)) { + setOpcode(Constants.FCONST0 + (int) floatVal); + } else { + _arg = getPool().findFloatEntry((float) floatVal, true); + setOpcode(((_arg > 255) || wide) ? Constants.LDCW : Constants.LDC); + } + } else if (value instanceof Long) { + long longVal = ((Long) value).longValue(); + + if ((longVal == 0) || (longVal == 1)) { + setOpcode(Constants.LCONST0 + (int) longVal); + } else { + _arg = getPool().findLongEntry(longVal, true); + setOpcode(Constants.LDC2W); + } + } else if (value instanceof Double) { + double doubleVal = ((Double) value).doubleValue(); + + if ((doubleVal == 0) || (doubleVal == 1)) { + setOpcode(Constants.DCONST0 + (int) doubleVal); + } else { + _arg = getPool().findDoubleEntry(doubleVal, true); + setOpcode(Constants.LDC2W); + } + } else if (value instanceof Integer) { + int intVal = ((Integer) value).intValue(); + + if ((intVal >= -1) && (intVal <= 5)) { + setOpcode(Constants.ICONST0 + intVal); + } else if ((intVal >= -(2 << 6)) && (intVal < (2 << 6))) { + setOpcode(Constants.BIPUSH); + _arg = intVal; + } else if ((intVal >= -(2 << 14)) && (intVal < (2 << 14))) { + setOpcode(Constants.SIPUSH); + _arg = intVal; + } else { + _arg = getPool().findIntEntry(intVal, true); + setOpcode(((_arg > 255) || wide) ? Constants.LDCW : Constants.LDC); + } + } else if (value instanceof String) { + _arg = getPool().findStringEntry((String) value, true); + setOpcode(((_arg > 255) || wide) ? Constants.LDCW : Constants.LDC); + } else if (value instanceof Class) { + String name = getProject().getNameCache() + .getInternalForm(((Class) value).getName(), false); + _arg = getPool().findClassEntry(name, true); + setOpcode(Constants.LDCW); + } else if (value instanceof BCClass) { + BCClass bc = (BCClass) value; + ClassEntry entry = (ClassEntry) bc.getPool().getEntry(bc.getIndex()); + + if (bc.getPool() == getPool()) { + _arg = getPool().indexOf(entry); + } else { + _arg = getPool() + .findClassEntry((String) entry.getConstant(), true); + } + + setOpcode(Constants.LDCW); + } else { + throw new IllegalArgumentException(String.valueOf(value)); + } + } +} diff --git a/serp/src/main/java/serp/bytecode/ConstantValue.java b/serp/src/main/java/serp/bytecode/ConstantValue.java new file mode 100755 index 000000000..e1283bac5 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/ConstantValue.java @@ -0,0 +1,263 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import serp.bytecode.visitor.*; + +import java.io.*; + + +/** + *

A constant value for a member field.

+ * + * @author Abe White + */ +public class ConstantValue extends Attribute { + int _valueIndex = 0; + + ConstantValue(int nameIndex, Attributes owner) { + super(nameIndex, owner); + } + + int getLength() { + return 2; + } + + /** + * Return the owning field. + */ + public BCField getField() { + return (BCField) getOwner(); + } + + /** + * Return the {@link ConstantPool} index of the {@link ConstantEntry} + * holding the value of this constant. Defaults to 0. + */ + public int getValueIndex() { + return _valueIndex; + } + + /** + * Set the {@link ConstantPool} of the {@link ConstantEntry} + * holding the value of this constant. + */ + public void setValueIndex(int valueIndex) { + _valueIndex = valueIndex; + } + + /** + * Return the type of constant this attribute represents, or null if + * not set. + */ + public String getTypeName() { + Class type = getType(); + + if (type == null) { + return null; + } + + return type.getName(); + } + + /** + * Return the type of constant this attribute represents (String.class, + * int.class, etc), or null if not set. + */ + public Class getType() { + Object value = getValue(); + + if (value == null) { + return null; + } + + Class type = value.getClass(); + + if (type == Integer.class) { + return int.class; + } + + if (type == Float.class) { + return float.class; + } + + if (type == Double.class) { + return double.class; + } + + if (type == Long.class) { + return long.class; + } + + return String.class; + } + + /** + * Return the bytecode for the type of constant this attribute + * represents. + */ + public BCClass getTypeBC() { + return getProject().loadClass(getType()); + } + + /** + * Return the value of this constant as an Object of the appropriate + * type (String, Integer, Double, etc), or null if not set. + */ + public Object getValue() { + if (_valueIndex <= 0) { + return null; + } + + return ((ConstantEntry) getPool().getEntry(_valueIndex)).getConstant(); + } + + /** + * Set the value of this constant using the appropriate wrapper Object + * type (String, Integer, Double, etc). Types that are not directly + * supported will be converted accordingly if possible. + */ + public void setValue(Object value) { + Class type = value.getClass(); + + if (type == Boolean.class) { + setIntValue((((Boolean) value).booleanValue()) ? 1 : 0); + } else if (type == Character.class) { + setIntValue((int) ((Character) value).charValue()); + } else if ((type == Byte.class) || (type == Integer.class) || + (type == Short.class)) { + setIntValue(((Number) value).intValue()); + } else if (type == Float.class) { + setFloatValue(((Number) value).floatValue()); + } else if (type == Double.class) { + setDoubleValue(((Number) value).doubleValue()); + } else if (type == Long.class) { + setLongValue(((Number) value).longValue()); + } else { + setStringValue(value.toString()); + } + } + + /** + * Get the value of this int constant, or 0 if not set. + */ + public int getIntValue() { + if (getValueIndex() <= 0) { + return 0; + } + + return ((IntEntry) getPool().getEntry(getValueIndex())).getValue(); + } + + /** + * Set the value of this int constant. + */ + public void setIntValue(int value) { + setValueIndex(getPool().findIntEntry(value, true)); + } + + /** + * Get the value of this float constant. + */ + public float getFloatValue() { + if (getValueIndex() <= 0) { + return 0F; + } + + return ((FloatEntry) getPool().getEntry(getValueIndex())).getValue(); + } + + /** + * Set the value of this float constant. + */ + public void setFloatValue(float value) { + setValueIndex(getPool().findFloatEntry(value, true)); + } + + /** + * Get the value of this double constant. + */ + public double getDoubleValue() { + if (getValueIndex() <= 0) { + return 0D; + } + + return ((DoubleEntry) getPool().getEntry(getValueIndex())).getValue(); + } + + /** + * Set the value of this double constant. + */ + public void setDoubleValue(double value) { + setValueIndex(getPool().findDoubleEntry(value, true)); + } + + /** + * Get the value of this long constant. + */ + public long getLongValue() { + if (getValueIndex() <= 0) { + return 0L; + } + + return ((LongEntry) getPool().getEntry(getValueIndex())).getValue(); + } + + /** + * Set the value of this long constant. + */ + public void setLongValue(long value) { + setValueIndex(getPool().findLongEntry(value, true)); + } + + /** + * Get the value of this string constant. + */ + public String getStringValue() { + if (getValueIndex() <= 0) { + return null; + } + + return ((StringEntry) getPool().getEntry(getValueIndex())).getStringEntry() + .getValue(); + } + + /** + * Set the value of this string constant. + */ + public void setStringValue(String value) { + setValueIndex(getPool().findStringEntry(value, true)); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterConstantValue(this); + visit.exitConstantValue(this); + } + + void read(Attribute other) { + setValue(((ConstantValue) other).getValue()); + } + + void read(DataInput in, int length) throws IOException { + setValueIndex(in.readUnsignedShort()); + } + + void write(DataOutput out, int length) throws IOException { + out.writeShort(getValueIndex()); + } +} diff --git a/serp/src/main/java/serp/bytecode/Constants.java b/serp/src/main/java/serp/bytecode/Constants.java new file mode 100755 index 000000000..d78a191ab --- /dev/null +++ b/serp/src/main/java/serp/bytecode/Constants.java @@ -0,0 +1,324 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + + +/** + *

Interface to track constants used in bytecode. Entities can access these + * constants using the static Constants. field prefix, + * or implement this interface themselves to conveniently import the + * constants into their own namespace.

+ * + * @author Abe White + */ +public interface Constants { + // class magic number + public static final int VALID_MAGIC = 0xcafebabe; + + // standard major, minor versions + public static final int MAJOR_VERSION = 45; + public static final int MINOR_VERSION = 3; + + // access constants for classes, fields, methods + public static final int ACCESS_PUBLIC = 0x0001; + public static final int ACCESS_PRIVATE = 0x0002; + public static final int ACCESS_PROTECTED = 0x0004; + public static final int ACCESS_STATIC = 0x0008; + public static final int ACCESS_FINAL = 0x0010; + public static final int ACCESS_SUPER = 0x0020; + public static final int ACCESS_SYNCHRONIZED = 0x0020; + public static final int ACCESS_VOLATILE = 0x0040; + public static final int ACCESS_TRANSIENT = 0x0080; + public static final int ACCESS_NATIVE = 0x0100; + public static final int ACCESS_INTERFACE = 0x0200; + public static final int ACCESS_ABSTRACT = 0x0400; + public static final int ACCESS_STRICT = 0x0800; + + // attribute types the compiler must support + public static final String ATTR_CODE = "Code"; + public static final String ATTR_CONST = "ConstantValue"; + public static final String ATTR_DEPRECATED = "Deprecated"; + public static final String ATTR_EXCEPTIONS = "Exceptions"; + public static final String ATTR_INNERCLASS = "InnerClasses"; + public static final String ATTR_LINENUMBERS = "LineNumberTable"; + public static final String ATTR_LOCALS = "LocalVariableTable"; + public static final String ATTR_LOCAL_TYPES = "LocalVariableTypeTable"; + public static final String ATTR_SOURCE = "SourceFile"; + public static final String ATTR_SYNTHETIC = "Synthetic"; + public static final String ATTR_UNKNOWN = "Unknown"; + + // opcodes + public static final int NOP = 0; + public static final int ACONSTNULL = 1; + public static final int ICONSTM1 = 2; + public static final int ICONST0 = 3; + public static final int ICONST1 = 4; + public static final int ICONST2 = 5; + public static final int ICONST3 = 6; + public static final int ICONST4 = 7; + public static final int ICONST5 = 8; + public static final int LCONST0 = 9; + public static final int LCONST1 = 10; + public static final int FCONST0 = 11; + public static final int FCONST1 = 12; + public static final int FCONST2 = 13; + public static final int DCONST0 = 14; + public static final int DCONST1 = 15; + public static final int BIPUSH = 16; + public static final int SIPUSH = 17; + public static final int LDC = 18; + public static final int LDCW = 19; + public static final int LDC2W = 20; + public static final int ILOAD = 21; + public static final int LLOAD = 22; + public static final int FLOAD = 23; + public static final int DLOAD = 24; + public static final int ALOAD = 25; + public static final int ILOAD0 = 26; + public static final int ILOAD1 = 27; + public static final int ILOAD2 = 28; + public static final int ILOAD3 = 29; + public static final int LLOAD0 = 30; + public static final int LLOAD1 = 31; + public static final int LLOAD2 = 32; + public static final int LLOAD3 = 33; + public static final int FLOAD0 = 34; + public static final int FLOAD1 = 35; + public static final int FLOAD2 = 36; + public static final int FLOAD3 = 37; + public static final int DLOAD0 = 38; + public static final int DLOAD1 = 39; + public static final int DLOAD2 = 40; + public static final int DLOAD3 = 41; + public static final int ALOAD0 = 42; + public static final int ALOAD1 = 43; + public static final int ALOAD2 = 44; + public static final int ALOAD3 = 45; + public static final int IALOAD = 46; + public static final int LALOAD = 47; + public static final int FALOAD = 48; + public static final int DALOAD = 49; + public static final int AALOAD = 50; + public static final int BALOAD = 51; + public static final int CALOAD = 52; + public static final int SALOAD = 53; + public static final int ISTORE = 54; + public static final int LSTORE = 55; + public static final int FSTORE = 56; + public static final int DSTORE = 57; + public static final int ASTORE = 58; + public static final int ISTORE0 = 59; + public static final int ISTORE1 = 60; + public static final int ISTORE2 = 61; + public static final int ISTORE3 = 62; + public static final int LSTORE0 = 63; + public static final int LSTORE1 = 64; + public static final int LSTORE2 = 65; + public static final int LSTORE3 = 66; + public static final int FSTORE0 = 67; + public static final int FSTORE1 = 68; + public static final int FSTORE2 = 69; + public static final int FSTORE3 = 70; + public static final int DSTORE0 = 71; + public static final int DSTORE1 = 72; + public static final int DSTORE2 = 73; + public static final int DSTORE3 = 74; + public static final int ASTORE0 = 75; + public static final int ASTORE1 = 76; + public static final int ASTORE2 = 77; + public static final int ASTORE3 = 78; + public static final int IASTORE = 79; + public static final int LASTORE = 80; + public static final int FASTORE = 81; + public static final int DASTORE = 82; + public static final int AASTORE = 83; + public static final int BASTORE = 84; + public static final int CASTORE = 85; + public static final int SASTORE = 86; + public static final int POP = 87; + public static final int POP2 = 88; + public static final int DUP = 89; + public static final int DUPX1 = 90; + public static final int DUPX2 = 91; + public static final int DUP2 = 92; + public static final int DUP2X1 = 93; + public static final int DUP2X2 = 94; + public static final int SWAP = 95; + public static final int IADD = 96; + public static final int LADD = 97; + public static final int FADD = 98; + public static final int DADD = 99; + public static final int ISUB = 100; + public static final int LSUB = 101; + public static final int FSUB = 102; + public static final int DSUB = 103; + public static final int IMUL = 104; + public static final int LMUL = 105; + public static final int FMUL = 106; + public static final int DMUL = 107; + public static final int IDIV = 108; + public static final int LDIV = 109; + public static final int FDIV = 110; + public static final int DDIV = 111; + public static final int IREM = 112; + public static final int LREM = 113; + public static final int FREM = 114; + public static final int DREM = 115; + public static final int INEG = 116; + public static final int LNEG = 117; + public static final int FNEG = 118; + public static final int DNEG = 119; + public static final int ISHL = 120; + public static final int LSHL = 121; + public static final int ISHR = 122; + public static final int LSHR = 123; + public static final int IUSHR = 124; + public static final int LUSHR = 125; + public static final int IAND = 126; + public static final int LAND = 127; + public static final int IOR = 128; + public static final int LOR = 129; + public static final int IXOR = 130; + public static final int LXOR = 131; + public static final int IINC = 132; + public static final int I2L = 133; + public static final int I2F = 134; + public static final int I2D = 135; + public static final int L2I = 136; + public static final int L2F = 137; + public static final int L2D = 138; + public static final int F2I = 139; + public static final int F2L = 140; + public static final int F2D = 141; + public static final int D2I = 142; + public static final int D2L = 143; + public static final int D2F = 144; + public static final int I2B = 145; + public static final int I2C = 146; + public static final int I2S = 147; + public static final int LCMP = 148; + public static final int FCMPL = 149; + public static final int FCMPG = 150; + public static final int DCMPL = 151; + public static final int DCMPG = 152; + public static final int IFEQ = 153; + public static final int IFNE = 154; + public static final int IFLT = 155; + public static final int IFGE = 156; + public static final int IFGT = 157; + public static final int IFLE = 158; + public static final int IFICMPEQ = 159; + public static final int IFICMPNE = 160; + public static final int IFICMPLT = 161; + public static final int IFICMPGE = 162; + public static final int IFICMPGT = 163; + public static final int IFICMPLE = 164; + public static final int IFACMPEQ = 165; + public static final int IFACMPNE = 166; + public static final int GOTO = 167; + public static final int JSR = 168; + public static final int RET = 169; + public static final int TABLESWITCH = 170; + public static final int LOOKUPSWITCH = 171; + public static final int IRETURN = 172; + public static final int LRETURN = 173; + public static final int FRETURN = 174; + public static final int DRETURN = 175; + public static final int ARETURN = 176; + public static final int RETURN = 177; + public static final int GETSTATIC = 178; + public static final int PUTSTATIC = 179; + public static final int GETFIELD = 180; + public static final int PUTFIELD = 181; + public static final int INVOKEVIRTUAL = 182; + public static final int INVOKESPECIAL = 183; + public static final int INVOKESTATIC = 184; + public static final int INVOKEINTERFACE = 185; + public static final int NEW = 187; + public static final int NEWARRAY = 188; + public static final int ANEWARRAY = 189; + public static final int ARRAYLENGTH = 190; + public static final int ATHROW = 191; + public static final int CHECKCAST = 192; + public static final int INSTANCEOF = 193; + public static final int MONITORENTER = 194; + public static final int MONITOREXIT = 195; + public static final int WIDE = 196; + public static final int MULTIANEWARRAY = 197; + public static final int IFNULL = 198; + public static final int IFNONNULL = 199; + public static final int GOTOW = 200; + public static final int JSRW = 201; + + // array types + public static final int ARRAY_BOOLEAN = 4; + public static final int ARRAY_CHAR = 5; + public static final int ARRAY_FLOAT = 6; + public static final int ARRAY_DOUBLE = 7; + public static final int ARRAY_BYTE = 8; + public static final int ARRAY_SHORT = 9; + public static final int ARRAY_INT = 10; + public static final int ARRAY_LONG = 11; + + // math operations + public static final int MATH_ADD = IADD; + public static final int MATH_SUB = ISUB; + public static final int MATH_MUL = IMUL; + public static final int MATH_DIV = IDIV; + public static final int MATH_REM = IREM; + public static final int MATH_NEG = INEG; + public static final int MATH_SHL = ISHL; + public static final int MATH_SHR = ISHR; + public static final int MATH_USHR = IUSHR; + public static final int MATH_AND = IAND; + public static final int MATH_OR = IOR; + public static final int MATH_XOR = IXOR; + + // human-readable opcode names + public static final String[] OPCODE_NAMES = new String[] { + "nop", "aconstnull", "iconstm1", "iconst0", "iconst1", "iconst2", + "iconst3", "iconst4", "iconst5", "lconst0", "lconst1", "fconst0", + "fconst1", "fconst2", "dconst0", "dconst1", "bipush", "sipush", + "ldc", "ldcw", "ldc2w", "iload", "lload", "fload", "dload", "aload", + "iload0", "iload1", "iload2", "iload3", "lload0", "lload1", "lload2", + "lload3", "fload0", "fload1", "fload2", "fload3", "dload0", "dload1", + "dload2", "dload3", "aload0", "aload1", "aload2", "aload3", "iaload", + "laload", "faload", "daload", "aaload", "baload", "caload", "saload", + "istore", "lstore", "fstore", "dstore", "astore", "istore0", + "istore1", "istore2", "istore3", "lstore0", "lstore1", "lstore2", + "lstore3", "fstore0", "fstore1", "fstore2", "fstore3", "dstore0", + "dstore1", "dstore2", "dstore3", "astore0", "astore1", "astore2", + "astore3", "iastore", "lastore", "fastore", "dastore", "aastore", + "bastore", "castore", "sastore", "pop", "pop2", "dup", "dupx1", + "dupx2", "dup2", "dup2x1", "dup2x2", "swap", "iadd", "ladd", "fadd", + "dadd", "isub", "lsub", "fsub", "dsub", "imul", "lmul", "fmul", + "dmul", "idiv", "ldiv", "fdiv", "ddiv", "irem", "lrem", "frem", + "drem", "ineg", "lneg", "fneg", "dneg", "ishl", "lshl", "ishr", + "lshr", "iushr", "lushr", "iand", "land", "ior", "lor", "ixor", + "lxor", "iinc", "i2l", "i2f", "i2d", "l2i", "l2f", "l2d", "f2i", + "f2l", "f2d", "d2i", "d2l", "d2f", "i2b", "i2c", "i2s", "lcmp", + "fcmpl", "fcmpg", "dcmpl", "dcmpg", "ifeq", "ifne", "iflt", "ifge", + "ifgt", "ifle", "ificmpeq", "ificmpne", "ificmplt", "ificmpge", + "ificmpgt", "ificmple", "ifacmpeq", "ifacmpne", "goto", "jsr", "ret", + "tableswitch", "lookupswitch", "ireturn", "lreturn", "freturn", + "dreturn", "areturn", "return", "getstatic", "putstatic", "getfield", + "putfield", "invokevirtual", "invokespecial", "invokestatic", + "invokeinterface", "??", "new", "newarray", "anewarray", + "arraylength", "athrow", "checkcast", "instanceof", "monitorenter", + "monitorexit", "wide", "multianewarray", "ifnull", "ifnonnull", + "gotow", "jsrw", + }; +} diff --git a/serp/src/main/java/serp/bytecode/ConvertInstruction.java b/serp/src/main/java/serp/bytecode/ConvertInstruction.java new file mode 100755 index 000000000..e4e137400 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/ConvertInstruction.java @@ -0,0 +1,440 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + +import serp.util.*; + +import java.util.*; + + +/** + *

A conversion opcode such as i2l, f2i, etc. + * Changing the types of the instruction will automatically + * update the underlying opcode. Converting from one type to the same + * type will result in a nop.

+ * + * @author Abe White + */ +public class ConvertInstruction extends TypedInstruction { + private static final Class[][] _mappings = new Class[][] { + { boolean.class, int.class }, + { void.class, int.class }, + { Object.class, int.class }, + }; + private static final Class[][] _fromMappings = new Class[][] { + { boolean.class, int.class }, + { void.class, int.class }, + { Object.class, int.class }, + { byte.class, int.class }, + { char.class, int.class }, + { short.class, int.class }, + }; + String _toType = null; + String _fromType = null; + + ConvertInstruction(Code owner) { + super(owner); + } + + ConvertInstruction(Code owner, int opcode) { + super(owner, opcode); + } + + public int getLogicalStackChange() { + return 0; + } + + public int getStackChange() { + switch (getOpcode()) { + case Constants.I2L: + case Constants.I2D: + case Constants.F2L: + case Constants.F2D: + return 1; + + case Constants.L2I: + case Constants.L2F: + case Constants.D2I: + case Constants.D2F: + return -1; + + default: + return 0; + } + } + + public String getTypeName() { + switch (getOpcode()) { + case Constants.L2I: + case Constants.F2I: + case Constants.D2I: + return int.class.getName(); + + case Constants.I2L: + case Constants.F2L: + case Constants.D2L: + return long.class.getName(); + + case Constants.I2F: + case Constants.L2F: + case Constants.D2F: + return float.class.getName(); + + case Constants.I2D: + case Constants.L2D: + case Constants.F2D: + return double.class.getName(); + + case Constants.I2B: + return byte.class.getName(); + + case Constants.I2C: + return char.class.getName(); + + case Constants.I2S: + return short.class.getName(); + + default: + return _toType; + } + } + + public TypedInstruction setType(String type) { + String toType = mapType(type, _mappings, true); + String fromType = getFromTypeName(); + + // if no valid opcode, remember current types in case they reset one + // to create a valid opcode + if ((toType == null) || (fromType == null) || toType.equals(fromType)) { + _toType = toType; + _fromType = fromType; + + return (TypedInstruction) setOpcode(Constants.NOP); + } + + // ok, valid conversion possible, forget saved types + _toType = null; + _fromType = null; + + char to = toType.charAt(0); + char from = fromType.charAt(0); + + switch (to) { + case 'i': + + switch (from) { + case 'l': + return (TypedInstruction) setOpcode(Constants.L2I); + + case 'f': + return (TypedInstruction) setOpcode(Constants.F2I); + + case 'd': + return (TypedInstruction) setOpcode(Constants.D2I); + } + + case 'l': + + switch (from) { + case 'i': + return (TypedInstruction) setOpcode(Constants.I2L); + + case 'f': + return (TypedInstruction) setOpcode(Constants.F2L); + + case 'd': + return (TypedInstruction) setOpcode(Constants.D2L); + } + + case 'f': + + switch (from) { + case 'i': + return (TypedInstruction) setOpcode(Constants.I2F); + + case 'l': + return (TypedInstruction) setOpcode(Constants.L2F); + + case 'd': + return (TypedInstruction) setOpcode(Constants.D2F); + } + + case 'd': + + switch (from) { + case 'i': + return (TypedInstruction) setOpcode(Constants.I2D); + + case 'l': + return (TypedInstruction) setOpcode(Constants.L2D); + + case 'f': + return (TypedInstruction) setOpcode(Constants.F2D); + } + + case 'b': + + if (from == 'i') { + return (TypedInstruction) setOpcode(Constants.I2B); + } + + case 'C': + + if (from == 'i') { + return (TypedInstruction) setOpcode(Constants.I2C); + } + + case 'S': + + if (from == 'i') { + return (TypedInstruction) setOpcode(Constants.I2S); + } + + default: + throw new IllegalStateException(); + } + } + + /** + * Return the name of the type being converted from. + * If neither type has been set, this method will return null. + */ + public String getFromTypeName() { + switch (getOpcode()) { + case Constants.I2L: + case Constants.I2F: + case Constants.I2D: + case Constants.I2B: + case Constants.I2S: + case Constants.I2C: + return int.class.getName(); + + case Constants.L2I: + case Constants.L2F: + case Constants.L2D: + return long.class.getName(); + + case Constants.F2I: + case Constants.F2L: + case Constants.F2D: + return float.class.getName(); + + case Constants.D2I: + case Constants.D2L: + case Constants.D2F: + return double.class.getName(); + + default: + return _fromType; + } + } + + /** + * Return the {@link Class} of the type being converted from. + * If neither type has been set, this method will return null. + */ + public Class getFromType() { + String type = getFromTypeName(); + + if (type == null) { + return null; + } + + return Strings.toClass(type, getClassLoader()); + } + + /** + * Return the bytecode of the type being converted from. + * If neither type has been set, this method will return null. + */ + public BCClass getFromTypeBC() { + String type = getFromTypeName(); + + if (type == null) { + return null; + } + + return getProject().loadClass(type, getClassLoader()); + } + + /** + * Set the type being converted from. Types that have no direct + * support will be converted accordingly. + * + * @return this instruction, for method chaining + */ + public ConvertInstruction setFromType(String type) { + String fromType = mapType(type, _fromMappings, true); + String toType = getTypeName(); + + // if no valid opcode, remember current types in case they reset one + // to create a valid opcode + if ((toType == null) || (fromType == null) || toType.equals(fromType)) { + _toType = toType; + _fromType = fromType; + + return (ConvertInstruction) setOpcode(Constants.NOP); + } + + // ok, valid conversion possible, forget saved types + _toType = null; + _fromType = null; + + char to = toType.charAt(0); + char from = fromType.charAt(0); + + switch (from) { + case 'i': + + switch (to) { + case 'l': + return (ConvertInstruction) setOpcode(Constants.I2L); + + case 'f': + return (ConvertInstruction) setOpcode(Constants.I2F); + + case 'd': + return (ConvertInstruction) setOpcode(Constants.I2D); + + case 'b': + return (ConvertInstruction) setOpcode(Constants.I2B); + + case 'c': + return (ConvertInstruction) setOpcode(Constants.I2C); + + case 's': + return (ConvertInstruction) setOpcode(Constants.I2S); + } + + case 'l': + + switch (to) { + case 'i': + return (ConvertInstruction) setOpcode(Constants.L2I); + + case 'f': + return (ConvertInstruction) setOpcode(Constants.L2F); + + case 'd': + return (ConvertInstruction) setOpcode(Constants.L2D); + } + + case 'f': + + switch (to) { + case 'i': + return (ConvertInstruction) setOpcode(Constants.F2I); + + case 'l': + return (ConvertInstruction) setOpcode(Constants.F2L); + + case 'd': + return (ConvertInstruction) setOpcode(Constants.F2D); + } + + case 'd': + + switch (to) { + case 'i': + return (ConvertInstruction) setOpcode(Constants.D2I); + + case 'l': + return (ConvertInstruction) setOpcode(Constants.D2L); + + case 'f': + return (ConvertInstruction) setOpcode(Constants.D2F); + } + + default: + throw new IllegalStateException(); + } + } + + /** + * Set the type being converted from. Types that have no direct + * support will be converted accordingly. + * + * @return this instruction, for method chaining + */ + public ConvertInstruction setFromType(Class type) { + if (type == null) { + return setFromType((String) null); + } + + return setFromType(type.getName()); + } + + /** + * Set the type being converted from. Types that have no direct + * support will be converted accordingly. + * + * @return this instruction, for method chaining + */ + public ConvertInstruction setFromType(BCClass type) { + if (type == null) { + return setFromType((String) null); + } + + return setFromType(type.getName()); + } + + /** + * ConvertInstructions are equal if the types they convert between are + * either equal or unset. + */ + public boolean equalsInstruction(Instruction other) { + if (other == this) { + return true; + } + + if (!(other instanceof ConvertInstruction)) { + return false; + } + + ConvertInstruction ins = (ConvertInstruction) other; + + if ((getOpcode() != Constants.NOP) && (getOpcode() == ins.getOpcode())) { + return true; + } + + String type = getTypeName(); + String otherType = ins.getTypeName(); + + if (!((type == null) || (otherType == null) || type.equals(otherType))) { + return false; + } + + type = getFromTypeName(); + otherType = ins.getFromTypeName(); + + return (type == null) || (otherType == null) || type.equals(otherType); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterConvertInstruction(this); + visit.exitConvertInstruction(this); + } + + void read(Instruction orig) { + super.read(orig); + + ConvertInstruction ins = (ConvertInstruction) orig; + _toType = ins._toType; + _fromType = ins._fromType; + } +} diff --git a/serp/src/main/java/serp/bytecode/Deprecated.java b/serp/src/main/java/serp/bytecode/Deprecated.java new file mode 100755 index 000000000..77c5b45c6 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/Deprecated.java @@ -0,0 +1,35 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + + +/** + *

Attribute signifying that a method or class is deprecated.

+ * + * @author Abe White + */ +public class Deprecated extends Attribute { + Deprecated(int nameIndex, Attributes owner) { + super(nameIndex, owner); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterDeprecated(this); + visit.exitDeprecated(this); + } +} diff --git a/serp/src/main/java/serp/bytecode/ExceptionHandler.java b/serp/src/main/java/serp/bytecode/ExceptionHandler.java new file mode 100755 index 000000000..2cc5cbd41 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/ExceptionHandler.java @@ -0,0 +1,310 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import serp.bytecode.visitor.*; + +import serp.util.*; + +import java.io.*; + +import java.util.*; + + +/** + *

Represents a try {} catch() {} statement in bytecode.

+ * + * @author Abe White + */ +public class ExceptionHandler implements InstructionPtr, BCEntity, + VisitAcceptor { + private int _catchIndex = 0; + private Code _owner = null; + private InstructionPtrStrategy _tryStart = new InstructionPtrStrategy(this); + private InstructionPtrStrategy _tryEnd = new InstructionPtrStrategy(this); + private InstructionPtrStrategy _tryHandler = new InstructionPtrStrategy(this); + + ExceptionHandler(Code owner) { + _owner = owner; + } + + /** + * Return the owning code block. + */ + public Code getCode() { + return _owner; + } + + /////////////////// + // Body operations + /////////////////// + + /** + * Return the instruction marking the beginning of the try {} block. + */ + public Instruction getTryStart() { + return _tryStart.getTargetInstruction(); + } + + /** + * Set the {@link Instruction} marking the beginning of the try block. + * The instruction must already be a part of the method. + */ + public void setTryStart(Instruction instruction) { + _tryStart.setTargetInstruction(instruction); + } + + /** + * Return the instruction at the end of the try {} block. + */ + public Instruction getTryEnd() { + return _tryEnd.getTargetInstruction(); + } + + /** + * Set the Instruction at the end of the try block. The + * Instruction must already be a part of the method. + */ + public void setTryEnd(Instruction instruction) { + _tryEnd.setTargetInstruction(instruction); + } + + ////////////////////// + // Handler operations + ////////////////////// + + /** + * Return the instruction marking the beginning of the catch {} block. + */ + public Instruction getHandlerStart() { + return _tryHandler.getTargetInstruction(); + } + + /** + * Set the {@link Instruction} marking the beginning of the catch block. + * The instruction must already be a part of the method. + * WARNING: if this instruction is deleted, the results are undefined. + */ + public void setHandlerStart(Instruction instruction) { + _tryHandler.setTargetInstruction(instruction); + } + + //////////////////// + // Catch operations + //////////////////// + + /** + * Return the index into the class {@link ConstantPool} of the + * {@link ClassEntry} describing the exception type this handler catches. + */ + public int getCatchIndex() { + return _catchIndex; + } + + /** + * Set the index into the class {@link ConstantPool} of the + * {@link ClassEntry} describing the exception type this handler catches. + */ + public void setCatchIndex(int catchTypeIndex) { + _catchIndex = catchTypeIndex; + } + + /** + * Return the name of the exception type; returns null for catch-all + * clauses used to implement finally blocks. The name will be returned + * in a forum suitable for a {@link Class#forName} call. + */ + public String getCatchName() { + if (_catchIndex == 0) { + return null; + } + + ClassEntry entry = (ClassEntry) getPool().getEntry(_catchIndex); + + return getProject().getNameCache() + .getExternalForm(entry.getNameEntry().getValue(), false); + } + + /** + * Return the {@link Class} of the exception type; returns null for + * catch-all clauses used to implement finally blocks. + */ + public Class getCatchType() { + String name = getCatchName(); + + if (name == null) { + return null; + } + + return Strings.toClass(name, getClassLoader()); + } + + /** + * Return the bytecode of the exception type; returns null for + * catch-all clauses used to implement finally blocks. + */ + public BCClass getCatchBC() { + String name = getCatchName(); + + if (name == null) { + return null; + } + + return getProject().loadClass(name, getClassLoader()); + } + + /** + * Set the class of the exception type, or null for catch-all clauses used + * with finally blocks. + */ + public void setCatch(String name) { + if (name == null) { + _catchIndex = 0; + } else { + _catchIndex = getPool() + .findClassEntry(getProject().getNameCache() + .getInternalForm(name, false), + true); + } + } + + /** + * Set the class of the exception type, or null for catch-all clauses used + * for finally blocks. + */ + public void setCatch(Class type) { + if (type == null) { + setCatch((String) null); + } else { + setCatch(type.getName()); + } + } + + /** + * Set the class of the exception type, or null for catch-all clauses used + * for finally blocks. + */ + public void setCatch(BCClass type) { + if (type == null) { + setCatch((String) null); + } else { + setCatch(type.getName()); + } + } + + ///////////////////////////////// + // InstructionPtr implementation + ///////////////////////////////// + public void updateTargets() { + _tryStart.updateTargets(); + _tryEnd.updateTargets(); + _tryHandler.updateTargets(); + } + + public void replaceTarget(Instruction oldTarget, Instruction newTarget) { + _tryStart.replaceTarget(oldTarget, newTarget); + _tryEnd.replaceTarget(oldTarget, newTarget); + _tryHandler.replaceTarget(oldTarget, newTarget); + } + + /////////////////////////// + // BCEntity implementation + /////////////////////////// + public Project getProject() { + return _owner.getProject(); + } + + public ConstantPool getPool() { + return _owner.getPool(); + } + + public ClassLoader getClassLoader() { + return _owner.getClassLoader(); + } + + public boolean isValid() { + return _owner != null; + } + + //////////////////////////////// + // VisitAcceptor implementation + //////////////////////////////// + public void acceptVisit(BCVisitor visit) { + visit.enterExceptionHandler(this); + visit.exitExceptionHandler(this); + } + + ////////////////// + // I/O operations + ////////////////// + void read(ExceptionHandler orig) { + _tryStart.setByteIndex(orig._tryStart.getByteIndex()); + _tryEnd.setByteIndex(orig._tryEnd.getByteIndex()); + _tryHandler.setByteIndex(orig._tryHandler.getByteIndex()); + + // done at a high level so that if the name isn't in our constant pool, + // it will be added + setCatch(orig.getCatchName()); + } + + void read(DataInput in) throws IOException { + setTryStart(in.readUnsignedShort()); + setTryEnd(in.readUnsignedShort()); + setHandlerStart(in.readUnsignedShort()); + setCatchIndex(in.readUnsignedShort()); + } + + void write(DataOutput out) throws IOException { + out.writeShort(getTryStartPc()); + out.writeShort(getTryEndPc()); + out.writeShort(getHandlerStartPc()); + out.writeShort(getCatchIndex()); + } + + public void setTryStart(int start) { + _tryStart.setByteIndex(start); + } + + public int getTryStartPc() { + return _tryStart.getByteIndex(); + } + + public void setTryEnd(int end) { + setTryEnd((Instruction) _owner.getInstruction(end).prev); + } + + /** + * Return the program counter end position for this exception handler. + * This represents an index into the code byte array. + */ + public int getTryEndPc() { + return _tryEnd.getByteIndex() + getTryEnd().getLength(); + } + + public void setHandlerStart(int handler) { + _tryHandler.setByteIndex(handler); + } + + public int getHandlerStartPc() { + return _tryHandler.getByteIndex(); + } + + void invalidate() { + _owner = null; + } +} diff --git a/serp/src/main/java/serp/bytecode/Exceptions.java b/serp/src/main/java/serp/bytecode/Exceptions.java new file mode 100755 index 000000000..8ab929b88 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/Exceptions.java @@ -0,0 +1,330 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import serp.bytecode.visitor.*; + +import serp.util.*; + +import java.io.*; + +import java.util.*; + + +/** + *

Attribute declaring the checked exceptions a method can throw.

+ * + * @author Abe White + */ +public class Exceptions extends Attribute { + private List _indexes = new LinkedList(); + + Exceptions(int nameIndex, Attributes owner) { + super(nameIndex, owner); + } + + int getLength() { + return 2 + (2 * _indexes.size()); + } + + /** + * Return the owning method. + */ + public BCMethod getMethod() { + return (BCMethod) getOwner(); + } + + /** + * Return the indexes in the class {@link ConstantPool} of the + * {@link ClassEntry}s for the exception types thrown by this method, or + * an empty array if none. + */ + public int[] getExceptionIndexes() { + int[] indexes = new int[_indexes.size()]; + Iterator itr = _indexes.iterator(); + + for (int i = 0; i < indexes.length; i++) + indexes[i] = ((Integer) itr.next()).intValue(); + + return indexes; + } + + /** + * Set the indexes in the class {@link ConstantPool} of the + * {@link ClassEntry}s for the exception types thrown by this method. Use + * null or an empty array for none. + */ + public void setExceptionIndexes(int[] exceptionIndexes) { + _indexes.clear(); + + if (exceptionIndexes != null) { + for (int i = 0; i < exceptionIndexes.length; i++) + _indexes.add(Numbers.valueOf(exceptionIndexes[i])); + } + } + + /** + * Return the names of the exception types for this method, or an empty + * array if none. The names will be in a form suitable for a + * {@link Class#forName} call. + */ + public String[] getExceptionNames() { + String[] names = new String[_indexes.size()]; + Iterator itr = _indexes.iterator(); + int index; + ClassEntry entry; + + for (int i = 0; i < names.length; i++) { + index = ((Number) itr.next()).intValue(); + entry = (ClassEntry) getPool().getEntry(index); + names[i] = getProject().getNameCache() + .getExternalForm(entry.getNameEntry().getValue(), + false); + } + + return names; + } + + /** + * Return the {@link Class} objects for the exception types for this + * method, or an empty array if none. + */ + public Class[] getExceptionTypes() { + String[] names = getExceptionNames(); + Class[] types = new Class[names.length]; + + for (int i = 0; i < names.length; i++) + types[i] = Strings.toClass(names[i], getClassLoader()); + + return types; + } + + /** + * Return bytecode for the exception types of this + * method, or an empty array if none. + */ + public BCClass[] getExceptionBCs() { + String[] names = getExceptionNames(); + BCClass[] types = new BCClass[names.length]; + + for (int i = 0; i < names.length; i++) + types[i] = getProject().loadClass(names[i], getClassLoader()); + + return types; + } + + /** + * Set the checked exceptions thrown by this method. Use null or an + * empty array for none. + */ + public void setExceptions(String[] exceptions) { + if (exceptions != null) { + for (int i = 0; i < exceptions.length; i++) + if (exceptions[i] == null) { + throw new NullPointerException("exceptions[" + i + + "] = null"); + } + } + + clear(); + + if (exceptions != null) { + for (int i = 0; i < exceptions.length; i++) + addException(exceptions[i]); + } + } + + /** + * Set the checked exceptions thrown by this method. Use null or an + * empty array for none. + */ + public void setExceptions(Class[] exceptions) { + String[] names = null; + + if (exceptions != null) { + names = new String[exceptions.length]; + + for (int i = 0; i < exceptions.length; i++) + names[i] = exceptions[i].getName(); + } + + setExceptions(names); + } + + /** + * Set the checked exceptions thrown by this method. Use null or an + * empty array for none. + */ + public void setExceptions(BCClass[] exceptions) { + String[] names = null; + + if (exceptions != null) { + names = new String[exceptions.length]; + + for (int i = 0; i < exceptions.length; i++) + names[i] = exceptions[i].getName(); + } + + setExceptions(names); + } + + /** + * Clear this method of all exception declarations. + */ + public void clear() { + _indexes.clear(); + } + + /** + * Remove an exception type thrown by this method. + * + * @return true if the method had the exception type, false otherwise + */ + public boolean removeException(String type) { + String internalForm = getProject().getNameCache() + .getInternalForm(type, false); + ClassEntry entry; + + for (Iterator itr = _indexes.iterator(); itr.hasNext();) { + entry = (ClassEntry) getPool() + .getEntry(((Integer) itr.next()).intValue()); + + if (entry.getNameEntry().getValue().equals(internalForm)) { + itr.remove(); + + return true; + } + } + + return false; + } + + /** + * Remove an exception thrown by this method. + * + * @return true if the method had the exception type, false otherwise + */ + public boolean removeException(Class type) { + if (type == null) { + return false; + } + + return removeException(type.getName()); + } + + /** + * Remove an exception thrown by this method. + * + * @return true if the method had the exception type, false otherwise + */ + public boolean removeException(BCClass type) { + if (type == null) { + return false; + } + + return removeException(type.getName()); + } + + /** + * Add an exception type to those thrown by this method. + */ + public void addException(String type) { + int index = getPool() + .findClassEntry(getProject().getNameCache() + .getInternalForm(type, false), true); + _indexes.add(Numbers.valueOf(index)); + } + + /** + * Add an exception to those thrown by this method. + */ + public void addException(Class type) { + addException(type.getName()); + } + + /** + * Add an exception to those thrown by this method. + */ + public void addException(BCClass type) { + addException(type.getName()); + } + + /** + * Return true if the method declares that it throws the given + * exception type. + */ + public boolean throwsException(String type) { + String[] exceptions = getExceptionNames(); + + for (int i = 0; i < exceptions.length; i++) + if (exceptions[i].equals(type)) { + return true; + } + + return false; + } + + /** + * Return true if the method declares that it throws the given + * exception type. + */ + public boolean throwsException(Class type) { + if (type == null) { + return false; + } + + return throwsException(type.getName()); + } + + /** + * Return true if the method declares that it throws the given + * exception type. + */ + public boolean throwsException(BCClass type) { + if (type == null) { + return false; + } + + return throwsException(type.getName()); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterExceptions(this); + visit.exitExceptions(this); + } + + void read(Attribute other) { + setExceptions(((Exceptions) other).getExceptionNames()); + } + + void read(DataInput in, int length) throws IOException { + _indexes.clear(); + + int exceptionCount = in.readUnsignedShort(); + + for (int i = 0; i < exceptionCount; i++) + _indexes.add(Numbers.valueOf((int) in.readUnsignedShort())); + } + + void write(DataOutput out, int length) throws IOException { + out.writeShort(_indexes.size()); + + for (Iterator itr = _indexes.iterator(); itr.hasNext();) + out.writeShort(((Number) itr.next()).shortValue()); + } +} diff --git a/serp/src/main/java/serp/bytecode/FieldInstruction.java b/serp/src/main/java/serp/bytecode/FieldInstruction.java new file mode 100755 index 000000000..ca93a91f9 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/FieldInstruction.java @@ -0,0 +1,499 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import serp.bytecode.visitor.*; + +import serp.util.*; + +import java.io.*; + +import java.lang.reflect.*; + + +/** + *

Instruction that takes as an argument a field to operate + * on. Examples include getfield, getstatic, setfield, + * setstatic.

+ * + * @author Abe White + */ +public abstract class FieldInstruction extends Instruction { + private int _index = 0; + + FieldInstruction(Code owner, int opcode) { + super(owner, opcode); + } + + int getLength() { + return super.getLength() + 2; + } + + //////////////////// + // Field operations + //////////////////// + + /** + * Return the index in the class {@link ConstantPool} of the + * {@link ComplexEntry} describing the field to operate on. + */ + public int getFieldIndex() { + return _index; + } + + /** + * Set the index in the class {@link ConstantPool} of the + * {@link ComplexEntry} describing the field to operate on. + * + * @return this instruction, for method chaining + */ + public FieldInstruction setFieldIndex(int index) { + _index = index; + + return this; + } + + /** + * Return the field this instruction operates on, or null if not set. + */ + public BCField getField() { + String dec = getFieldDeclarerName(); + + if (dec == null) { + return null; + } + + BCClass bc = getProject().loadClass(dec, getClassLoader()); + BCField[] fields = bc.getFields(getFieldName()); + + if (fields.length == 0) { + return null; + } + + return fields[0]; + } + + /** + * Set the field this instruction operates on. + * + * @return this instruction, for method chaining + */ + public FieldInstruction setField(BCField field) { + if (field == null) { + return setFieldIndex(0); + } + + return setField(field.getDeclarer().getName(), field.getName(), + field.getTypeName()); + } + + /** + * Set the field this instruction operates on. + * + * @return this instruction, for method chaining + */ + public FieldInstruction setField(Field field) { + if (field == null) { + return setFieldIndex(0); + } + + return setField(field.getDeclaringClass(), field.getName(), + field.getType()); + } + + /** + * Set the field this instruction operates on. + * + * @param dec the full class name of the field's declaring class + * @param name the field name + * @param type the full class name of the field type + * @return this instruction, for method chaining + */ + public FieldInstruction setField(String dec, String name, String type) { + if ((dec == null) && (name == null) && (type == null)) { + return setFieldIndex(0); + } + + if (dec == null) { + dec = ""; + } + + if (name == null) { + name = ""; + } + + if (type == null) { + type = ""; + } + + dec = getProject().getNameCache().getInternalForm(dec, false); + type = getProject().getNameCache().getInternalForm(type, true); + + return setFieldIndex(getPool().findFieldEntry(dec, name, type, true)); + } + + /** + * Set the field this instruction operates on, for fields that are + * declared by the current class. + * + * @param name the field name + * @param type the full class name of the field type + * @return this instruction, for method chaining + */ + public FieldInstruction setField(String name, String type) { + BCClass owner = getCode().getMethod().getDeclarer(); + + return setField(owner.getName(), name, type); + } + + /** + * Set the field this instruction operates on. + * + * @param dec the field's declaring class + * @param name the field name + * @param type the class of the field type + * @return this instruction, for method chaining + */ + public FieldInstruction setField(Class dec, String name, Class type) { + String decName = (dec == null) ? null : dec.getName(); + String typeName = (type == null) ? null : type.getName(); + + return setField(decName, name, typeName); + } + + /** + * Set the field this instruction operates on, for fields that are + * declared by the current class. + * + * @param name the field name + * @param type the class of the field type + * @return this instruction, for method chaining + */ + public FieldInstruction setField(String name, Class type) { + BCClass owner = getCode().getMethod().getDeclarer(); + String typeName = (type == null) ? null : type.getName(); + + return setField(owner.getName(), name, typeName); + } + + /** + * Set the field this instruction operates on. + * + * @param dec the field's declaring class + * @param name the field name + * @param type the class of the field type + * @return this instruction, for method chaining + */ + public FieldInstruction setField(BCClass dec, String name, BCClass type) { + String decName = (dec == null) ? null : dec.getName(); + String typeName = (type == null) ? null : type.getName(); + + return setField(decName, name, typeName); + } + + /** + * Set the field this instruction operates on, for fields that are + * declared by the current class. + * + * @param name the field name + * @param type the class of the field type + * @return this instruction, for method chaining + */ + public FieldInstruction setField(String name, BCClass type) { + BCClass owner = getCode().getMethod().getDeclarer(); + String typeName = (type == null) ? null : type.getName(); + + return setField(owner.getName(), name, typeName); + } + + //////////////////////////////// + // Name, Type, Owner operations + //////////////////////////////// + + /** + * Return the name of the field this instruction operates on, or null + * if not set. + */ + public String getFieldName() { + int index = getFieldIndex(); + + if (index == 0) { + return null; + } + + ComplexEntry entry = (ComplexEntry) getPool().getEntry(index); + String name = entry.getNameAndTypeEntry().getNameEntry().getValue(); + + if (name.length() == 0) { + return null; + } + + return name; + } + + /** + * Set the name of the field this instruction operates on. + * + * @return this instruction, for method chaining + */ + public FieldInstruction setFieldName(String name) { + return setField(getFieldDeclarerName(), name, getFieldTypeName()); + } + + /** + * Return the type of the field this instruction operates on, or null + * if not set. + */ + public String getFieldTypeName() { + int index = getFieldIndex(); + + if (index == 0) { + return null; + } + + ComplexEntry entry = (ComplexEntry) getPool().getEntry(index); + String name = getProject().getNameCache() + .getExternalForm(entry.getNameAndTypeEntry() + .getDescriptorEntry().getValue(), + false); + + if (name.length() == 0) { + return null; + } + + return name; + } + + /** + * Return the type of the field this instruction operates on, or null + * if not set. + */ + public Class getFieldType() { + String type = getFieldTypeName(); + + if (type == null) { + return null; + } + + return Strings.toClass(type, getClassLoader()); + } + + /** + * Return the type of the field this instruction operates on, or null + * if not set. + */ + public BCClass getFieldTypeBC() { + String type = getFieldTypeName(); + + if (type == null) { + return null; + } + + return getProject().loadClass(type, getClassLoader()); + } + + /** + * Set the type of the field this instruction operates on. + * + * @return this instruction, for method chaining + */ + public FieldInstruction setFieldType(String type) { + return setField(getFieldDeclarerName(), getFieldName(), type); + } + + /** + * Set the type of the field this instruction operates on. + * + * @return this instruction, for method chaining + */ + public FieldInstruction setFieldType(Class type) { + String name = null; + + if (type != null) { + name = type.getName(); + } + + return setFieldType(name); + } + + /** + * Set the type of the field this instruction operates on. + * + * @return this instruction, for method chaining + */ + public FieldInstruction setFieldType(BCClass type) { + String name = null; + + if (type != null) { + name = type.getName(); + } + + return setFieldType(name); + } + + /** + * Return the declaring class of the field this instruction operates on, + * or null if not set. + */ + public String getFieldDeclarerName() { + int index = getFieldIndex(); + + if (index == 0) { + return null; + } + + ComplexEntry entry = (ComplexEntry) getPool().getEntry(index); + String name = getProject().getNameCache() + .getExternalForm(entry.getClassEntry().getNameEntry() + .getValue(), false); + + if (name.length() == 0) { + return null; + } + + return name; + } + + /** + * Return the declaring class of the field this instruction operates on, + * or null if not set. + */ + public Class getFieldDeclarerType() { + String type = getFieldDeclarerName(); + + if (type == null) { + return null; + } + + return Strings.toClass(type, getClassLoader()); + } + + /** + * Return the declaring class of the field this instruction operates on, + * or null if not set. + */ + public BCClass getFieldDeclarerBC() { + String type = getFieldDeclarerName(); + + if (type == null) { + return null; + } + + return getProject().loadClass(type, getClassLoader()); + } + + /** + * Set the declaring class of the field this instruction operates on. + * + * @return this instruction, for method chaining + */ + public FieldInstruction setFieldDeclarer(String type) { + return setField(type, getFieldName(), getFieldTypeName()); + } + + /** + * Set the declaring class of the field this instruction operates on. + * + * @return this instruction, for method chaining + */ + public FieldInstruction setFieldDeclarer(Class type) { + String name = null; + + if (type != null) { + name = type.getName(); + } + + return setFieldDeclarer(name); + } + + /** + * Set the declaring class of the field this instruction operates on. + * + * @return this instruction, for method chaining + */ + public FieldInstruction setFieldDeclarer(BCClass type) { + String name = null; + + if (type != null) { + name = type.getName(); + } + + return setFieldDeclarer(name); + } + + /** + * FieldInstructions are equal if the field they reference is the same, + * or if the field of either is unset. + */ + public boolean equalsInstruction(Instruction other) { + if (other == this) { + return true; + } + + if (!(other instanceof FieldInstruction)) { + return false; + } + + if (!super.equalsInstruction(other)) { + return false; + } + + FieldInstruction ins = (FieldInstruction) other; + + String s1 = getFieldName(); + String s2 = ins.getFieldName(); + + if (!((s1 == null) || (s2 == null) || s1.equals(s2))) { + return false; + } + + s1 = getFieldTypeName(); + s2 = ins.getFieldTypeName(); + + if (!((s1 == null) || (s2 == null) || s1.equals(s2))) { + return false; + } + + s1 = getFieldDeclarerName(); + s2 = ins.getFieldDeclarerName(); + + if (!((s1 == null) || (s2 == null) || s1.equals(s2))) { + return false; + } + + return true; + } + + void read(Instruction orig) { + super.read(orig); + + FieldInstruction ins = (FieldInstruction) orig; + setField(ins.getFieldDeclarerName(), ins.getFieldName(), + ins.getFieldTypeName()); + } + + void read(DataInput in) throws IOException { + super.read(in); + setFieldIndex(in.readUnsignedShort()); + } + + void write(DataOutput out) throws IOException { + super.write(out); + out.writeShort(getFieldIndex()); + } +} diff --git a/serp/src/main/java/serp/bytecode/GetFieldInstruction.java b/serp/src/main/java/serp/bytecode/GetFieldInstruction.java new file mode 100755 index 000000000..af27da453 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/GetFieldInstruction.java @@ -0,0 +1,64 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + + +/** + *

Loads a value from a field onto the stack.

+ * + * @author Abe White + */ +public class GetFieldInstruction extends FieldInstruction { + GetFieldInstruction(Code owner, int opcode) { + super(owner, opcode); + } + + public int getLogicalStackChange() { + if (getOpcode() == Constants.GETSTATIC) { + return 1; + } + + return 0; + } + + public int getStackChange() { + String type = getFieldTypeName(); + + if (type == null) { + return 0; + } + + int stack = 0; + + if (long.class.getName().equals(type) || + double.class.getName().equals(type)) { + stack++; + } + + if (getOpcode() == Constants.GETSTATIC) { + stack++; + } + + return stack; + } + + public void acceptVisit(BCVisitor visit) { + visit.enterGetFieldInstruction(this); + visit.exitGetFieldInstruction(this); + } +} diff --git a/serp/src/main/java/serp/bytecode/IIncInstruction.java b/serp/src/main/java/serp/bytecode/IIncInstruction.java new file mode 100755 index 000000000..54ae769eb --- /dev/null +++ b/serp/src/main/java/serp/bytecode/IIncInstruction.java @@ -0,0 +1,97 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + +import java.io.*; + + +/** + *

The iinc instruction.

+ * + * @author Abe White + */ +public class IIncInstruction extends LocalVariableInstruction { + private int _inc = 0; + + IIncInstruction(Code owner) { + super(owner, Constants.IINC); + } + + int getLength() { + return super.getLength() + 2; + } + + /** + * Return the increment for this IINC instruction. + */ + public int getIncrement() { + return _inc; + } + + /** + * Set the increment on this IINC instruction. + * + * @return this Instruction, for method chaining + */ + public IIncInstruction setIncrement(int val) { + _inc = val; + + return this; + } + + public boolean equalsInstruction(Instruction other) { + if (this == other) { + return true; + } + + if (!(other instanceof IIncInstruction)) { + return false; + } + + if (!super.equalsInstruction(other)) { + return false; + } + + IIncInstruction ins = (IIncInstruction) other; + + return ((getIncrement() == 0) || (ins.getIncrement() == 0) || + (getIncrement() == ins.getIncrement())); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterIIncInstruction(this); + visit.exitIIncInstruction(this); + } + + void read(Instruction other) { + super.read(other); + setIncrement(((IIncInstruction) other).getIncrement()); + } + + void read(DataInput in) throws IOException { + super.read(in); + setLocal(in.readUnsignedByte()); + setIncrement(in.readByte()); + } + + void write(DataOutput out) throws IOException { + super.write(out); + out.writeByte(getLocal()); + out.writeByte(getIncrement()); + } +} diff --git a/serp/src/main/java/serp/bytecode/IfInstruction.java b/serp/src/main/java/serp/bytecode/IfInstruction.java new file mode 100755 index 000000000..053dfa847 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/IfInstruction.java @@ -0,0 +1,81 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import serp.bytecode.visitor.*; + + +/** + *

An if instruction such as ifnull, ifeq, etc.

+ * + * @author Abe White + */ +public class IfInstruction extends JumpInstruction { + IfInstruction(Code owner, int opcode) { + super(owner, opcode); + } + + public int getLogicalStackChange() { + return getStackChange(); + } + + public int getStackChange() { + switch (getOpcode()) { + case Constants.IFACMPEQ: + case Constants.IFACMPNE: + case Constants.IFICMPEQ: + case Constants.IFICMPNE: + case Constants.IFICMPLT: + case Constants.IFICMPGT: + case Constants.IFICMPLE: + case Constants.IFICMPGE: + return -2; + + case Constants.IFEQ: + case Constants.IFNE: + case Constants.IFLT: + case Constants.IFGT: + case Constants.IFLE: + case Constants.IFGE: + case Constants.IFNULL: + case Constants.IFNONNULL: + return -1; + + default: + return super.getStackChange(); + } + } + + public String getTypeName() { + switch (getOpcode()) { + case Constants.IFACMPEQ: + case Constants.IFACMPNE: + case Constants.IFNULL: + case Constants.IFNONNULL: + return "java.lang.Object"; + + default: + return "I"; + } + } + + public void acceptVisit(BCVisitor visit) { + visit.enterIfInstruction(this); + visit.exitIfInstruction(this); + } +} diff --git a/serp/src/main/java/serp/bytecode/InnerClass.java b/serp/src/main/java/serp/bytecode/InnerClass.java new file mode 100755 index 000000000..0e7f58f62 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/InnerClass.java @@ -0,0 +1,466 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import serp.bytecode.visitor.*; + +import serp.util.*; + +import java.io.*; + + +/** + *

Any referenced class that is not a package member is represented by + * this structure. This includes member classes and interfaces.

+ * + * @author Abe White + */ +public class InnerClass implements BCEntity, VisitAcceptor { + private int _index = 0; + private int _nameIndex = 0; + private int _ownerIndex = 0; + private int _access = Constants.ACCESS_PRIVATE; + private InnerClasses _owner = null; + + InnerClass(InnerClasses owner) { + _owner = owner; + } + + /** + * Inner classes are stored in an {@link InnerClasses} attribute. + */ + public InnerClasses getOwner() { + return _owner; + } + + void invalidate() { + _owner = null; + } + + ///////////////////// + // Access operations + ///////////////////// + + /** + * Return the access flags of the inner class. + */ + public int getAccessFlags() { + return _access; + } + + /** + * Set the access flags of the inner class. + */ + public void setAccessFlags(int accessFlags) { + _access = accessFlags; + } + + /** + * Manipulate the inner class access flags. + */ + public boolean isPublic() { + return (getAccessFlags() & Constants.ACCESS_PUBLIC) > 0; + } + + /** + * Manipulate the inner class access flags. + */ + public void makePublic() { + setAccessFlags(getAccessFlags() | Constants.ACCESS_PUBLIC); + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_PRIVATE); + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_PROTECTED); + } + + /** + * Manipulate the inner class access flags. + */ + public boolean isProtected() { + return (getAccessFlags() & Constants.ACCESS_PROTECTED) > 0; + } + + /** + * Manipulate the inner class access flags. + */ + public void makeProtected() { + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_PUBLIC); + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_PRIVATE); + setAccessFlags(getAccessFlags() | Constants.ACCESS_PROTECTED); + } + + /** + * Manipulate the inner class access flags. + */ + public boolean isPrivate() { + return (getAccessFlags() & Constants.ACCESS_PRIVATE) > 0; + } + + /** + * Manipulate the inner class access flags. + */ + public void makePrivate() { + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_PUBLIC); + setAccessFlags(getAccessFlags() | Constants.ACCESS_PRIVATE); + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_PROTECTED); + } + + /** + * Manipulate the inner class access flags. + */ + public boolean isFinal() { + return (getAccessFlags() & Constants.ACCESS_FINAL) > 0; + } + + /** + * Manipulate the inner class access flags. + */ + public void setFinal(boolean on) { + if (on) { + setAccessFlags(getAccessFlags() | Constants.ACCESS_FINAL); + } else { + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_FINAL); + } + } + + /** + * Manipulate the inner class access flags. + */ + public boolean isStatic() { + return (getAccessFlags() & Constants.ACCESS_STATIC) > 0; + } + + /** + * Manipulate the inner class access flags. + */ + public void setStatic(boolean on) { + if (on) { + setAccessFlags(getAccessFlags() | Constants.ACCESS_STATIC); + } else { + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_STATIC); + } + } + + /** + * Manipulate the class access flags. + */ + public boolean isInterface() { + return (getAccessFlags() & Constants.ACCESS_INTERFACE) > 0; + } + + /** + * Manipulate the class access flags. + */ + public void setInterface(boolean on) { + if (on) { + setAccessFlags(getAccessFlags() | Constants.ACCESS_INTERFACE); + setAbstract(true); + } else { + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_INTERFACE); + } + } + + /** + * Manipulate the class access flags. + */ + public boolean isAbstract() { + return (getAccessFlags() & Constants.ACCESS_ABSTRACT) > 0; + } + + /** + * Manipulate the class access flags. + */ + public void setAbstract(boolean on) { + if (on) { + setAccessFlags(getAccessFlags() | Constants.ACCESS_INTERFACE); + } else { + setAccessFlags(getAccessFlags() & ~Constants.ACCESS_INTERFACE); + } + } + + //////////////////////////////// + // Name, type, owner operations + //////////////////////////////// + + /** + * Return the {@link ConstantPool} index of the {@link UTF8Entry} that + * describes the simple name this class is referred to in source, or + * 0 for anonymous classes. + */ + public int getNameIndex() { + return _nameIndex; + } + + /** + * Set the {@link ConstantPool} index of the {@link UTF8Entry} that + * describes the simple name this class is referred to in source, or + * 0 for anonymous classes. + */ + public void setNameIndex(int nameIndex) { + _nameIndex = nameIndex; + } + + /** + * Return the simple name of this inner class, or null if anonymous. + */ + public String getName() { + if (getNameIndex() == 0) { + return null; + } + + return ((UTF8Entry) getPool().getEntry(getNameIndex())).getValue(); + } + + /** + * Set the simple name of this inner class. + */ + public void setName(String name) { + if (name == null) { + setNameIndex(0); + } else { + setNameIndex(getPool().findUTF8Entry(name, true)); + } + } + + /** + * Return the {@link ConstantPool} index of the {@link ClassEntry} that + * describes this class, or 0 if none. + */ + public int getTypeIndex() { + return _index; + } + + /** + * Set the {@link ConstantPool} index of the {@link ClassEntry} that + * describes this class. + */ + public void setTypeIndex(int index) { + _index = index; + } + + /** + * Return the full name of the inner class, or null if unset. + */ + public String getTypeName() { + if (getTypeIndex() == 0) { + return null; + } + + ClassEntry entry = (ClassEntry) getPool() + .getEntry(getTypeIndex()); + + return getProject().getNameCache() + .getExternalForm(entry.getNameEntry().getValue(), false); + } + + /** + * Return the type of the inner class. + * If the type has not been set, this method will return null. + */ + public Class getType() { + String type = getTypeName(); + + if (type == null) { + return null; + } + + return Strings.toClass(type, getClassLoader()); + } + + /** + * Return the type for this instruction. + * If the type has not been set, this method will return null. + */ + public BCClass getTypeBC() { + String type = getTypeName(); + + if (type == null) { + return null; + } + + return getProject().loadClass(type, getClassLoader()); + } + + /** + * Set the type of this inner class. + */ + public void setType(String type) { + if (type == null) { + setTypeIndex(0); + } else { + type = getProject().getNameCache().getInternalForm(type, false); + setTypeIndex(getPool().findClassEntry(type, true)); + } + } + + /** + * Set the type of this inner class. + */ + public void setType(Class type) { + if (type == null) { + setType((String) null); + } else { + setType(type.getName()); + } + } + + /** + * Set the type of this inner class. + */ + public void setType(BCClass type) { + if (type == null) { + setType((String) null); + } else { + setType(type.getName()); + } + } + + /** + * Return the {@link ConstantPool} index of the {@link ClassEntry} that + * describes the declaring class, or 0 if this class is not a member class. + */ + public int getDeclarerIndex() { + return _ownerIndex; + } + + /** + * Set the {@link ConstantPool} index of the {@link ClassEntry} that + * describes the declaring class, or 0 if this class is not a member class. + */ + public void setDeclarerIndex(int ownerIndex) { + _ownerIndex = ownerIndex; + } + + /** + * Return the full name of the declaring class, or null if unset/not a + * member. + */ + public String getDeclarerName() { + if (getDeclarerIndex() == 0) { + return null; + } + + ClassEntry entry = (ClassEntry) getPool().getEntry(getDeclarerIndex()); + + return getProject().getNameCache() + .getExternalForm(entry.getNameEntry().getValue(), false); + } + + /** + * Return the type of the declaring class. + * If the type has not been set or the class is not a member, this method + * will return null. + */ + public Class getDeclarerType() { + String type = getDeclarerName(); + + if (type == null) { + return null; + } + + return Strings.toClass(type, getClassLoader()); + } + + /** + * Return the type for this instruction. + * If the type has not been set or the class is not a member, this method + * will return null. + */ + public BCClass getDeclarerBC() { + String type = getDeclarerName(); + + if (type == null) { + return null; + } + + return getProject().loadClass(type, getClassLoader()); + } + + /** + * Set the type of this declaring class. + */ + public void setDeclarer(String type) { + if (type == null) { + setDeclarerIndex(0); + } else { + type = getProject().getNameCache().getInternalForm(type, false); + setDeclarerIndex(getPool().findClassEntry(type, true)); + } + } + + /** + * Set the type of this declaring class. + */ + public void setDeclarer(Class type) { + if (type == null) { + setDeclarer((String) null); + } else { + setDeclarer(type.getName()); + } + } + + /** + * Set the type of this declaring class. + */ + public void setDeclarer(BCClass type) { + if (type == null) { + setDeclarer((String) null); + } else { + setDeclarer(type.getName()); + } + } + + /////////////////////////// + // BCEntity implementation + /////////////////////////// + public Project getProject() { + return _owner.getProject(); + } + + public ConstantPool getPool() { + return _owner.getPool(); + } + + public ClassLoader getClassLoader() { + return _owner.getClassLoader(); + } + + public boolean isValid() { + return _owner != null; + } + + public void acceptVisit(BCVisitor visit) { + visit.enterInnerClass(this); + visit.exitInnerClass(this); + } + + ////////////////// + // I/O operations + ////////////////// + void read(DataInput in) throws IOException { + setTypeIndex(in.readUnsignedShort()); + setDeclarerIndex(in.readUnsignedShort()); + setNameIndex(in.readUnsignedShort()); + setAccessFlags(in.readUnsignedShort()); + } + + void write(DataOutput out) throws IOException { + out.writeShort(getTypeIndex()); + out.writeShort(getDeclarerIndex()); + out.writeShort(getNameIndex()); + out.writeShort(getAccessFlags()); + } +} diff --git a/serp/src/main/java/serp/bytecode/InnerClasses.java b/serp/src/main/java/serp/bytecode/InnerClasses.java new file mode 100755 index 000000000..a9652b680 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/InnerClasses.java @@ -0,0 +1,246 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + +import java.io.*; + +import java.util.*; + + +/** + *

Attribute describing all referenced classes that are not package + * members. This includes all member interfaces and classes.

+ * + * @author Abe White + */ +public class InnerClasses extends Attribute { + private List _innerClasses = new LinkedList(); + + InnerClasses(int nameIndex, Attributes owner) { + super(nameIndex, owner); + } + + /** + * Return all referenced inner classes, or empty array if none. + */ + public InnerClass[] getInnerClasses() { + return (InnerClass[]) _innerClasses.toArray(new InnerClass[_innerClasses.size()]); + } + + /** + * Return the inner class with the given name. If multiple inner classes + * share the name, which is returned is undefined. Use null to retrieve + * anonymous classes. + */ + public InnerClass getInnerClass(String name) { + InnerClass[] inners = getInnerClasses(); + String inner; + + for (int i = 0; i < inners.length; i++) { + inner = inners[i].getName(); + + if (((inner == null) && (name == null)) || + ((inner != null) && inner.equals(name))) { + return inners[i]; + } + } + + return null; + } + + /** + * Return all inner classes with the given name, or empty array if none. + * Use null to retrieve anonymous classes. + */ + public InnerClass[] getInnerClasses(String name) { + List matches = new LinkedList(); + InnerClass[] inners = getInnerClasses(); + String inner; + + for (int i = 0; i < inners.length; i++) { + inner = inners[i].getName(); + + if (((inner == null) && (name == null)) || + ((inner != null) && inner.equals(name))) { + matches.add(inners[i]); + } + } + + return (InnerClass[]) matches.toArray(new InnerClass[matches.size()]); + } + + /** + * Set the inner class references for this class. This method is + * useful when importing inner class references from another class. + */ + public void setInnerClasses(InnerClass[] inners) { + clear(); + + if (inners != null) { + for (int i = 0; i < inners.length; i++) + addInnerClass(inners[i]); + } + } + + /** + * Import an inner class from another entity, or make a copy of one + * on this entity. + * + * @return the newly added inner class + */ + public InnerClass addInnerClass(InnerClass inner) { + InnerClass newInner = addInnerClass(inner.getName(), + inner.getTypeName(), inner.getDeclarerName()); + newInner.setAccessFlags(inner.getAccessFlags()); + + return newInner; + } + + /** + * Add an inner class. + */ + public InnerClass addInnerClass() { + InnerClass inner = new InnerClass(this); + _innerClasses.add(inner); + + return inner; + } + + /** + * Add an inner class. + * + * @param name the simple name of the class, or null if anonymous + * @param type the full class name of the inner class + * @param owner the declaring class, or null if not a member class + */ + public InnerClass addInnerClass(String name, String type, String owner) { + InnerClass inner = addInnerClass(); + inner.setName(name); + inner.setType(type); + inner.setDeclarer(owner); + + return inner; + } + + /** + * Add an inner class. + * + * @param name the simple name of the class, or null if anonymous + * @param type the class of the inner class + * @param owner the declaring class, or null if not a member class + */ + public InnerClass addInnerClass(String name, Class type, Class owner) { + String typeName = (type == null) ? null : type.getName(); + String ownerName = (owner == null) ? null : owner.getName(); + + return addInnerClass(name, typeName, ownerName); + } + + /** + * Add an inner class. + * + * @param name the simple name of the class, or null if anonymous + * @param type the class of the inner class + * @param owner the declaring class, or null if not a member class + */ + public InnerClass addInnerClass(String name, BCClass type, BCClass owner) { + String typeName = (type == null) ? null : type.getName(); + String ownerName = (owner == null) ? null : owner.getName(); + + return addInnerClass(name, typeName, ownerName); + } + + /** + * Clear all inner classes from this entity. + */ + public void clear() { + InnerClass inner; + + for (Iterator itr = _innerClasses.iterator(); itr.hasNext();) { + inner = (InnerClass) itr.next(); + itr.remove(); + inner.invalidate(); + } + } + + /** + * Remove the inner class with the given name. Use null for anonymous + * classes. + * + * @return true if an inner class was removed, false otherwise + */ + public boolean removeInnerClass(String name) { + return removeInnerClass(getInnerClass(name)); + } + + /** + * Remove the given inner class. After being removed, the given inner + * class is invalid, and the result of any operations on it are undefined. + * + * @return true if the inner class was removed, false otherwise + */ + public boolean removeInnerClass(InnerClass innerClass) { + if ((innerClass == null) || !_innerClasses.remove(innerClass)) { + return false; + } + + innerClass.invalidate(); + + return true; + } + + public void acceptVisit(BCVisitor visit) { + visit.enterInnerClasses(this); + + InnerClass[] inners = getInnerClasses(); + + for (int i = 0; i < inners.length; i++) + inners[i].acceptVisit(visit); + + visit.exitInnerClasses(this); + } + + int getLength() { + return 2 + (8 * _innerClasses.size()); + } + + void read(Attribute other) { + setInnerClasses(((InnerClasses) other).getInnerClasses()); + } + + void read(DataInput in, int length) throws IOException { + clear(); + + int numInnerClasses = in.readUnsignedShort(); + + InnerClass innerClass; + + for (int i = 0; i < numInnerClasses; i++) { + innerClass = addInnerClass(); + innerClass.read(in); + } + } + + void write(DataOutput out, int length) throws IOException { + InnerClass[] inners = getInnerClasses(); + out.writeShort(inners.length); + + for (int i = 0; i < inners.length; i++) + inners[i].write(out); + } +} diff --git a/serp/src/main/java/serp/bytecode/Instruction.java b/serp/src/main/java/serp/bytecode/Instruction.java new file mode 100755 index 000000000..899eef649 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/Instruction.java @@ -0,0 +1,191 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import serp.bytecode.visitor.*; + +import java.io.*; + +import java.util.*; + + +/** + *

An opcode in a method of a class.

+ * + * @author Abe White + */ +public class Instruction extends CodeEntry implements BCEntity, VisitAcceptor { + private Code _owner = null; + private int _opcode = Constants.NOP; + + Instruction(Code owner) { + _owner = owner; + } + + Instruction(Code owner, int opcode) { + _owner = owner; + _opcode = opcode; + } + + /** + * Return the code block that owns this instruction. + */ + public Code getCode() { + return _owner; + } + + /** + * Return the name of this instruction. + */ + public String getName() { + return Constants.OPCODE_NAMES[_opcode]; + } + + /** + * Return the opcode this instruction represents. + */ + public int getOpcode() { + return _opcode; + } + + /** + * Set the opcode this instruction represents. For internal use only. + * + * @return this instruction, for method chaining + */ + Instruction setOpcode(int opcode) { + _opcode = opcode; + + return this; + } + + /** + * Return the index in the method code byte block at which this opcode + * starts. Note that this information may be out of date if the code + * block has been modified since last read/written. + */ + public int getByteIndex() { + if (_owner != null) { + return _owner.getByteIndex(this); + } + + return 0; + } + + /** + * Return the line number of this instruction, or null if none. This + * method is subject to the validity constraints of {@link #getByteIndex}. + * + * @see LineNumberTable#getLineNumber(Instruction) + */ + public LineNumber getLineNumber() { + LineNumberTable table = _owner.getLineNumberTable(false); + + if (table == null) { + return null; + } + + return table.getLineNumber(this); + } + + /** + * Return the length in bytes of this opcode, including all arguments. + * For many opcodes this method relies on an up-to-date byte index. + */ + int getLength() { + return 1; + } + + /** + * Return the logical number of stack positions changed by this + * instruction. In other words, ignore weirdness with longs and doubles + * taking two stack positions. + */ + public int getLogicalStackChange() { + return getStackChange(); + } + + /** + * Return the number of stack positions this instruction pushes + * or pops during its execution. + * + * @return 0 if the stack is not affected by this instruction, a + * positive number if it pushes onto the stack, and a negative + * number if it pops from the stack + */ + public int getStackChange() { + return 0; + } + + /** + * Instructions are equal if their opcodes are the same. Subclasses + * should override this method to perform a template comparison: + * instructions should compare equal to other instructions of the same + * type where the data is either the same or the data is unset. + */ + public boolean equalsInstruction(Instruction other) { + if (other == this) { + return true; + } + + return other.getOpcode() == getOpcode(); + } + + public Project getProject() { + return _owner.getProject(); + } + + public ConstantPool getPool() { + return _owner.getPool(); + } + + public ClassLoader getClassLoader() { + return _owner.getClassLoader(); + } + + public boolean isValid() { + return _owner != null; + } + + public void acceptVisit(BCVisitor visit) { + } + + void invalidate() { + _owner = null; + } + + /** + * Copy the given instruction data. + */ + void read(Instruction orig) { + } + + /** + * Read the arguments for this opcode from the given stream. + * This method should be overridden by opcodes that take arguments. + */ + void read(DataInput in) throws IOException { + } + + /** + * Write the arguments for this opcode to the given stream. + * This method should be overridden by opcodes that take arguments. + */ + void write(DataOutput out) throws IOException { + } +} diff --git a/serp/src/main/java/serp/bytecode/InstructionPtr.java b/serp/src/main/java/serp/bytecode/InstructionPtr.java new file mode 100755 index 000000000..4a64cbc98 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/InstructionPtr.java @@ -0,0 +1,48 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import java.util.*; + + +/** + *

An entity that maintains ptrs to instructions in a code block.

+ * + * @author Abe White + */ +public interface InstructionPtr { + /** + * Use the byte indexes read from the class file to calculate and + * set references to the target instruction(s) for this ptr. + * This method will be called after the byte code + * has been read in for the first time and before it is written after + * modification. + */ + public void updateTargets(); + + /** + * Replace the given old, likely invalid, target with a new target. The + * new target Instruction is guaranteed to be in the same code + * block as this InstructionPtr. + */ + public void replaceTarget(Instruction oldTarget, Instruction newTarget); + + /** + * Returns the Code block that owns the Instruction(s) this + * InstructionPtr points to. + */ + public Code getCode(); +} diff --git a/serp/src/main/java/serp/bytecode/InstructionPtrStrategy.java b/serp/src/main/java/serp/bytecode/InstructionPtrStrategy.java new file mode 100755 index 000000000..84fbb0a93 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/InstructionPtrStrategy.java @@ -0,0 +1,120 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + + +/** + *

InstructionPtrStrategy handles the different strategies for finding the + * Instructions that InstructionPtrs point to. These strategies include, + * from least desirable to most desirable, using byte indexes, + * and storing a reference to the target Instruction proper.

+ * + * @author Eric Lindauer + * @date 2002.7.26 + */ +class InstructionPtrStrategy implements InstructionPtr { + // the Instruction doing the targetting + private InstructionPtr _pointer; + + // two different ways to find the target from the pointer. + // _target is used first, then _byteIndex + private Instruction _target = null; + private int _byteIndex = -1; + + public InstructionPtrStrategy(InstructionPtr pointer) { + _pointer = pointer; + } + + public InstructionPtrStrategy(InstructionPtr pointer, Instruction target) { + this(pointer); + setTargetInstruction(target); + } + + /** + * Sets the byteIndex where the target Instruction can be found. + * This target will now be using byte indices as its target finding + * strategy, which is the least robust option. Changing the Code block + * or importing it into another Method may result in an invalid target. + */ + public void setByteIndex(int index) { + if ((index < 0) && (index != -1)) { + throw new IllegalArgumentException(String.valueOf(index)); + } + + _byteIndex = index; + _target = null; + } + + /** + * Changes the target Instruction. The target is in the best state + * possible and should maintain this information even in the face + * of Code imports and Code changes. + */ + public void setTargetInstruction(Instruction ins) { + if (ins.getCode() != getCode()) { + throw new IllegalArgumentException("Instruction pointers and " + + "targets must be part of the same code block."); + } + + _target = ins; + _byteIndex = -1; + } + + /** + * Returns the Instruction this Target is targetting. This request + * does not change the targetting strategy for this Target. + */ + public Instruction getTargetInstruction() { + if (_target != null) { + return _target; + } + + return getCode().getInstruction(_byteIndex); + } + + /** + * Returns the byteIndex at which the target instruction can be found. + * This call does not change the Target strategy. + */ + public int getByteIndex() { + if (_target == null) { + return _byteIndex; + } + + return _target.getByteIndex(); + } + + /** + * Same as getInstruction, but this method alters the Target strategy + * to use the returned Instruction. This method alters the Target + * strategy (and Instruction) iff it was previously using byte indexes. + */ + public void updateTargets() { + if (_target == null) { + _target = getCode().getInstruction(_byteIndex); + } + } + + public void replaceTarget(Instruction oldTarget, Instruction newTarget) { + if (getTargetInstruction() == oldTarget) { + setTargetInstruction(newTarget); + } + } + + public Code getCode() { + return _pointer.getCode(); + } +} diff --git a/serp/src/main/java/serp/bytecode/JumpInstruction.java b/serp/src/main/java/serp/bytecode/JumpInstruction.java new file mode 100755 index 000000000..58161375b --- /dev/null +++ b/serp/src/main/java/serp/bytecode/JumpInstruction.java @@ -0,0 +1,186 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + +import java.io.*; + +import java.util.*; + + +/** + *

An instruction that specifies a position in the code block to jump to. + * Examples include go2, jsr, etc.

+ * + * @author Abe White + */ +public class JumpInstruction extends Instruction implements InstructionPtr { + private InstructionPtrStrategy _target = new InstructionPtrStrategy(this); + + JumpInstruction(Code owner, int opcode) { + super(owner, opcode); + } + + public int getLogicalStackChange() { + return getStackChange(); + } + + public int getStackChange() { + if (getOpcode() == Constants.JSR) { + return 1; + } + + return 0; + } + + int getLength() { + switch (getOpcode()) { + case Constants.GOTOW: + case Constants.JSRW: + return super.getLength() + 4; + + default: + return super.getLength() + 2; + } + } + + /** + * Get the current target instruction to jump to, if it has been set. + */ + public Instruction getTarget() { + return _target.getTargetInstruction(); + } + + /** + * Set the instruction to jump to; the instruction must already be + * added to the code block. + * + * @return this instruction, for method chaining + */ + public JumpInstruction setTarget(Instruction instruction) { + _target.setTargetInstruction(instruction); + + return this; + } + + /** + * JumpInstructions are equal if they represent the same operation and + * the instruction they jump to is the + * same, or if the jump Instruction of either is unset. + */ + public boolean equalsInstruction(Instruction other) { + if (this == other) { + return true; + } + + if (!super.equalsInstruction(other)) { + return false; + } + + Instruction target = ((JumpInstruction) other).getTarget(); + + return ((target == null) || (getTarget() == null) || + (target == getTarget())); + } + + public void updateTargets() { + _target.updateTargets(); + } + + public void replaceTarget(Instruction oldTarget, Instruction newTarget) { + _target.replaceTarget(oldTarget, newTarget); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterJumpInstruction(this); + visit.exitJumpInstruction(this); + } + + void read(Instruction orig) { + super.read(orig); + _target.setByteIndex(((JumpInstruction) orig)._target.getByteIndex()); + } + + void read(DataInput in) throws IOException { + super.read(in); + + switch (getOpcode()) { + case Constants.GOTOW: + case Constants.JSRW: + setOffset(in.readInt()); + + break; + + default: + setOffset(in.readShort()); + } + } + + void write(DataOutput out) throws IOException { + super.write(out); + + switch (getOpcode()) { + case Constants.GOTOW: + case Constants.JSRW: + out.writeInt(getOffset()); + + break; + + default: + out.writeShort(getOffset()); + } + } + + void calculateOpcode() { + int offset; + + switch (getOpcode()) { + case Constants.GOTO: + case Constants.GOTOW: + offset = getOffset(); + + if (offset < (2 << 16)) { + setOpcode(Constants.GOTO); + } else { + setOpcode(Constants.GOTOW); + } + + break; + + case Constants.JSR: + case Constants.JSRW: + offset = getOffset(); + + if (offset < (2 << 16)) { + setOpcode(Constants.JSR); + } else { + setOpcode(Constants.JSRW); + } + + break; + } + } + + public void setOffset(int offset) { + _target.setByteIndex(getByteIndex() + offset); + calculateOpcode(); + } + + public int getOffset() { + return _target.getByteIndex() - getByteIndex(); + } +} diff --git a/serp/src/main/java/serp/bytecode/LineNumber.java b/serp/src/main/java/serp/bytecode/LineNumber.java new file mode 100755 index 000000000..134071563 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/LineNumber.java @@ -0,0 +1,162 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import serp.bytecode.visitor.*; + +import java.io.*; + +import java.util.*; + + +/** + *

A line number corresponds to a sequence of opcodes that map logically + * to a line of source code.

+ * + * @author Abe White + */ +public class LineNumber implements Comparable, InstructionPtr, BCEntity, + VisitAcceptor { + private int _line = 0; + private LineNumberTable _owner = null; + InstructionPtrStrategy _target = new InstructionPtrStrategy(this); + + LineNumber(LineNumberTable owner) { + _owner = owner; + } + + LineNumber(LineNumberTable owner, int startPc) { + this(owner); + setStartPc(startPc); + } + + /** + * Line numbers are stored in a {@link LineNumberTable}. + */ + public LineNumberTable getTable() { + return _owner; + } + + void invalidate() { + _owner = null; + } + + /** + * Return source line number. + */ + public int getLine() { + return _line; + } + + /** + * Set the source line number. + */ + public void setLine(int lineNumber) { + _line = lineNumber; + } + + /** + * Return the instruction marking the beginning of this line. + */ + public Instruction getStart() { + return _target.getTargetInstruction(); + } + + /** + * Return the index into the code byte array at which this line starts. + */ + public int getStartPc() { + return _target.getByteIndex(); + } + + /** + * Set the index into the code byte array at which this line starts. + */ + public void setStartPc(int startPc) { + _target.setByteIndex(startPc); + } + + /** + * Set the {@link Instruction} marking the beginning this line. + * The instruction must already be a part of the method. + */ + public void setStart(Instruction instruction) { + _target.setTargetInstruction(instruction); + } + + public void updateTargets() { + _target.updateTargets(); + } + + public void replaceTarget(Instruction oldTarget, Instruction newTarget) { + _target.replaceTarget(oldTarget, newTarget); + } + + public Project getProject() { + return _owner.getProject(); + } + + public ConstantPool getPool() { + return _owner.getPool(); + } + + public ClassLoader getClassLoader() { + return _owner.getClassLoader(); + } + + public boolean isValid() { + return _owner != null; + } + + public void acceptVisit(BCVisitor visit) { + visit.enterLineNumber(this); + visit.exitLineNumber(this); + } + + public int compareTo(Object other) { + if (!(other instanceof LineNumber)) { + return -1; + } + + LineNumber ln = (LineNumber) other; + + if (getStartPc() == ln.getStartPc()) { + return 0; + } + + if (getStartPc() < ln.getStartPc()) { + return -1; + } + + return 1; + } + + void read(DataInput in) throws IOException { + setStartPc(in.readUnsignedShort()); + setLine(in.readUnsignedShort()); + } + + void write(DataOutput out) throws IOException { + out.writeShort(getStartPc()); + out.writeShort(getLine()); + } + + public Code getCode() { + return _owner.getCode(); + } +} diff --git a/serp/src/main/java/serp/bytecode/LineNumberTable.java b/serp/src/main/java/serp/bytecode/LineNumberTable.java new file mode 100755 index 000000000..01decb563 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/LineNumberTable.java @@ -0,0 +1,227 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + +import java.io.*; + +import java.util.*; + + +/** + *

Code blocks compiled from source have line number tables mapping + * opcodes to source lines. This table automatically maintains line + * numbers in ascending order by their start program counter position + * at all times.

+ * + * @author Abe White + */ +public class LineNumberTable extends Attribute implements InstructionPtr { + private List _lineNumbers = new ArrayList(); + + LineNumberTable(int nameIndex, Attributes owner) { + super(nameIndex, owner); + } + + /** + * Return the line numbers held in this table. + */ + public LineNumber[] getLineNumbers() { + Collections.sort(_lineNumbers); + + return (LineNumber[]) _lineNumbers.toArray(new LineNumber[_lineNumbers.size()]); + } + + /** + * Return the line number for the given program counter, or null if none. + */ + public LineNumber getLineNumber(int pc) { + for (int i = _lineNumbers.size() - 1; i >= 0; i--) + if (((LineNumber) _lineNumbers.get(i))._target.getByteIndex() <= pc) { + return (LineNumber) _lineNumbers.get(i); + } + + return null; + } + + /** + * Return the line number for the given instruction, or null if none. + */ + public LineNumber getLineNumber(Instruction ins) { + if (ins == null) { + return null; + } + + return getLineNumber(ins.getByteIndex()); + } + + /** + * Set the line numbers for the table. This method is useful when + * importing line numbers from another method. + */ + public void setLineNumbers(LineNumber[] lines) { + clear(); + + if (lines != null) { + for (int i = 0; i < lines.length; i++) + addLineNumber(lines[i]); + } + } + + /** + * Import a line number from another method. + * + * @return the newly added line number + */ + public LineNumber addLineNumber(LineNumber ln) { + LineNumber line = addLineNumber(); + line.setStartPc(ln.getStartPc()); + line.setLine(ln.getLine()); + + return line; + } + + /** + * Add a new line number to this table. + */ + public LineNumber addLineNumber() { + LineNumber ln = new LineNumber(this); + _lineNumbers.add(ln); + + return ln; + } + + /** + * Add a new line number to this table. + */ + public LineNumber addLineNumber(int startPc, int line) { + LineNumber ln = addLineNumber(); + ln.setStartPc(startPc); + ln.setLine(line); + + return ln; + } + + /** + * Add a new line number to this table. + */ + public LineNumber addLineNumber(Instruction start, int line) { + LineNumber ln = addLineNumber(); + ln.setStart(start); + ln.setLine(line); + + return ln; + } + + /** + * Clear the line numbers. + */ + public void clear() { + for (int i = 0; i < _lineNumbers.size(); i++) + ((LineNumber) _lineNumbers.get(i)).invalidate(); + + _lineNumbers.clear(); + } + + /** + * Remove the given line. + * + * @return true if the line was removed, false otherwise + */ + public boolean removeLineNumber(LineNumber ln) { + if ((ln == null) || !_lineNumbers.remove(ln)) { + return false; + } + + ln.invalidate(); + + return true; + } + + /** + * Remove the line number for the given program counter. + * + * @return true if the line was removed, false otherwise + */ + public boolean removeLineNumber(int pc) { + return removeLineNumber(getLineNumber(pc)); + } + + /** + * Remove the line number for the given instruction. + * + * @return true if the line was removed, false otherwise + */ + public boolean removeLineNumber(Instruction ins) { + return removeLineNumber(getLineNumber(ins)); + } + + public void updateTargets() { + for (int i = 0; i < _lineNumbers.size(); i++) + ((LineNumber) _lineNumbers.get(i)).updateTargets(); + } + + public void replaceTarget(Instruction oldTarget, Instruction newTarget) { + for (int i = 0; i < _lineNumbers.size(); i++) + ((LineNumber) _lineNumbers.get(i)).replaceTarget(oldTarget, + newTarget); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterLineNumberTable(this); + + LineNumber[] lines = getLineNumbers(); + + for (int i = 0; i < lines.length; i++) + lines[i].acceptVisit(visit); + + visit.exitLineNumberTable(this); + } + + int getLength() { + return 2 + (4 * _lineNumbers.size()); + } + + void read(Attribute other) { + setLineNumbers(((LineNumberTable) other).getLineNumbers()); + } + + void read(DataInput in, int length) throws IOException { + clear(); + + int numLines = in.readUnsignedShort(); + + LineNumber lineNumber; + + for (int i = 0; i < numLines; i++) { + lineNumber = addLineNumber(); + lineNumber.read(in); + } + } + + void write(DataOutput out, int length) throws IOException { + LineNumber[] lines = getLineNumbers(); + out.writeShort(lines.length); + + for (int i = 0; i < lines.length; i++) + lines[i].write(out); + } + + public Code getCode() { + return (Code) getOwner(); + } +} diff --git a/serp/src/main/java/serp/bytecode/LoadInstruction.java b/serp/src/main/java/serp/bytecode/LoadInstruction.java new file mode 100755 index 000000000..a3b3d05c9 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/LoadInstruction.java @@ -0,0 +1,299 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + +import java.io.*; + + +/** + *

Loads a value from the locals table to the stack.

+ * + * @author Abe White + */ +public class LoadInstruction extends LocalVariableInstruction { + private static final Class[][] _mappings = new Class[][] { + { byte.class, int.class }, + { boolean.class, int.class }, + { char.class, int.class }, + { short.class, int.class }, + { void.class, int.class }, + }; + String _type = null; + + LoadInstruction(Code owner) { + super(owner); + } + + LoadInstruction(Code owner, int opcode) { + super(owner, opcode); + } + + int getLength() { + switch (getOpcode()) { + case Constants.ILOAD: + case Constants.LLOAD: + case Constants.FLOAD: + case Constants.DLOAD: + case Constants.ALOAD: + return super.getLength() + 1; + + default: + return super.getLength(); + } + } + + public int getStackChange() { + switch (getOpcode()) { + case Constants.LLOAD: + case Constants.LLOAD0: + case Constants.LLOAD1: + case Constants.LLOAD2: + case Constants.LLOAD3: + case Constants.DLOAD: + case Constants.DLOAD0: + case Constants.DLOAD1: + case Constants.DLOAD2: + case Constants.DLOAD3: + return 2; + + case Constants.NOP: + return 0; + + default: + return 1; + } + } + + public int getLogicalStackChange() { + switch (getOpcode()) { + case Constants.NOP: + return 0; + + default: + return 1; + } + } + + public String getTypeName() { + switch (getOpcode()) { + case Constants.ILOAD: + case Constants.ILOAD0: + case Constants.ILOAD1: + case Constants.ILOAD2: + case Constants.ILOAD3: + return int.class.getName(); + + case Constants.LLOAD: + case Constants.LLOAD0: + case Constants.LLOAD1: + case Constants.LLOAD2: + case Constants.LLOAD3: + return long.class.getName(); + + case Constants.FLOAD: + case Constants.FLOAD0: + case Constants.FLOAD1: + case Constants.FLOAD2: + case Constants.FLOAD3: + return float.class.getName(); + + case Constants.DLOAD: + case Constants.DLOAD0: + case Constants.DLOAD1: + case Constants.DLOAD2: + case Constants.DLOAD3: + return double.class.getName(); + + case Constants.ALOAD: + case Constants.ALOAD0: + case Constants.ALOAD1: + case Constants.ALOAD2: + case Constants.ALOAD3: + return Object.class.getName(); + + default: + return _type; + } + } + + public TypedInstruction setType(String type) { + type = mapType(type, _mappings, true); + + int local = getLocal(); + + // if an invalid type or local, revert to nop + if ((type == null) || (local < 0)) { + _type = type; + + return (TypedInstruction) setOpcode(Constants.NOP); + } + + // valid opcode, unset saved type + _type = null; + + switch (type.charAt(0)) { + case 'i': + return (TypedInstruction) setOpcode((local > 3) ? Constants.ILOAD + : (Constants.ILOAD0 + + local)); + + case 'l': + return (TypedInstruction) setOpcode((local > 3) ? Constants.LLOAD + : (Constants.LLOAD0 + + local)); + + case 'f': + return (TypedInstruction) setOpcode((local > 3) ? Constants.FLOAD + : (Constants.FLOAD0 + + local)); + + case 'd': + return (TypedInstruction) setOpcode((local > 3) ? Constants.DLOAD + : (Constants.DLOAD0 + + local)); + + default: + return (TypedInstruction) setOpcode((local > 3) ? Constants.ALOAD + : (Constants.ALOAD0 + + local)); + } + } + + /** + * Equivalent to setLocal (0).setType (Object.class); the + * this ptr is always passed in local variable 0. + * + * @return this instruction, for method chaining + */ + public LoadInstruction setThis() { + return (LoadInstruction) setLocal(0).setType(Object.class); + } + + /** + * Equivalent to getLocal () == 0 && getType () == + * Object.class; the this ptr + * is always passed in local variable 0. + */ + public boolean isThis() { + return (getLocal() == 0) && (getType() == Object.class); + } + + /** + * LoadInstructions are equal if the type they reference the same + * type and locals index or if either is unset. + */ + public boolean equalsInstruction(Instruction other) { + if (other == this) { + return true; + } + + if (!super.equalsInstruction(other)) { + return false; + } + + String type = getTypeName(); + String otherType = ((LoadInstruction) other).getTypeName(); + + return (type == null) || (otherType == null) || type.equals(otherType); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterLoadInstruction(this); + visit.exitLoadInstruction(this); + } + + void read(Instruction orig) { + super.read(orig); + + LoadInstruction ins = (LoadInstruction) orig; + _type = ins._type; + } + + void read(DataInput in) throws IOException { + super.read(in); + + switch (getOpcode()) { + case Constants.ILOAD: + case Constants.LLOAD: + case Constants.FLOAD: + case Constants.DLOAD: + case Constants.ALOAD: + setLocal(in.readUnsignedByte()); + + break; + } + } + + void write(DataOutput out) throws IOException { + super.write(out); + + switch (getOpcode()) { + case Constants.ILOAD: + case Constants.LLOAD: + case Constants.FLOAD: + case Constants.DLOAD: + case Constants.ALOAD: + out.writeByte(getLocal()); + } + } + + void calculateOpcode() { + // taken care of when setting type + setType(getTypeName()); + } + + void calculateLocal() { + switch (getOpcode()) { + case Constants.ILOAD0: + case Constants.LLOAD0: + case Constants.FLOAD0: + case Constants.DLOAD0: + case Constants.ALOAD0: + setLocal(0); + + break; + + case Constants.ILOAD1: + case Constants.LLOAD1: + case Constants.FLOAD1: + case Constants.DLOAD1: + case Constants.ALOAD1: + setLocal(1); + + break; + + case Constants.ILOAD2: + case Constants.LLOAD2: + case Constants.FLOAD2: + case Constants.DLOAD2: + case Constants.ALOAD2: + setLocal(2); + + break; + + case Constants.ILOAD3: + case Constants.LLOAD3: + case Constants.FLOAD3: + case Constants.DLOAD3: + case Constants.ALOAD3: + setLocal(3); + + break; + } + } +} diff --git a/serp/src/main/java/serp/bytecode/Local.java b/serp/src/main/java/serp/bytecode/Local.java new file mode 100755 index 000000000..3f875afa1 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/Local.java @@ -0,0 +1,272 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import serp.bytecode.visitor.*; + +import serp.util.*; + +import java.io.*; + +import java.util.*; + + +/** + *

A local variable or local variable type.

+ * + * @author Abe White + */ +public abstract class Local implements BCEntity, InstructionPtr { + private LocalTable _owner = null; + private InstructionPtrStrategy _target = new InstructionPtrStrategy(this); + private int _length = 0; + private int _nameIndex = 0; + private int _descriptorIndex = 0; + private int _index = 0; + + Local(LocalTable owner) { + _owner = owner; + } + + /** + * The owning table. + */ + public LocalTable getTable() { + return _owner; + } + + void invalidate() { + _owner = null; + } + + ////////////////////////// + // Local index operations + ////////////////////////// + + /** + * Get the local variable index of the current frame for this local. + */ + public int getLocal() { + return _index; + } + + /** + * Set the local variable index of the current frame for this local. + */ + public void setLocal(int index) { + _index = index; + } + + /** + * Return the parameter that this local corresponds to, or -1 if none. + */ + public int getParam() { + return getCode().getParamsIndex(getLocal()); + } + + /** + * Set the method parameter that this local corresponds to. + */ + public void setParam(int param) { + setLocal(_owner.getCode().getLocalsIndex(param)); + } + + /** + * Return the index into the code byte array at which this local starts. + */ + public int getStartPc() { + return _target.getByteIndex(); + } + + //////////////////////////// + // Start, Length operations + //////////////////////////// + + /** + * Return the instruction marking the beginning of this local. + */ + public Instruction getStart() { + return _target.getTargetInstruction(); + } + + /** + * Set the index into the code byte array at which this local starts. + */ + public void setStartPc(int startPc) { + _target.setByteIndex(startPc); + } + + /** + * Set the {@link Instruction} marking the beginning this local. + * The instruction must already be a part of the method. + * WARNING: if this instruction is deleted, the results are undefined. + */ + public void setStart(Instruction instruction) { + _target.setTargetInstruction(instruction); + } + + public void updateTargets() { + _target.updateTargets(); + } + + public void replaceTarget(Instruction oldTarget, Instruction newTarget) { + _target.replaceTarget(oldTarget, newTarget); + } + + /** + * Get the number of bytes for which this local has a value in + * the code byte array. + */ + public int getLength() { + return _length; + } + + /** + * Set the number of bytes for which this local has a value in + * the code byte array. + */ + public void setLength(int length) { + _length = length; + } + + ///////////////////////// + // Name, Type operations + ///////////////////////// + + /** + * Return the {@link ConstantPool} index of the {@link UTF8Entry} that + * describes the name of this local. Defaults to 0. + */ + public int getNameIndex() { + return _nameIndex; + } + + /** + * Set the {@link ConstantPool} index of the {@link UTF8Entry} that + * describes the name of this local. + */ + public void setNameIndex(int nameIndex) { + _nameIndex = nameIndex; + } + + /** + * Return the name of this local, or null if unset. + */ + public String getName() { + if (getNameIndex() == 0) { + return null; + } + + return ((UTF8Entry) getPool().getEntry(getNameIndex())).getValue(); + } + + /** + * Set the name of this inner local. + */ + public void setName(String name) { + if (name == null) { + setNameIndex(0); + } else { + setNameIndex(getPool().findUTF8Entry(name, true)); + } + } + + /** + * Return the {@link ConstantPool} index of the {@link UTF8Entry} that + * describes this local. Defaults to 0. + */ + public int getTypeIndex() { + return _descriptorIndex; + } + + /** + * Set the {@link ConstantPool} index of the {@link UTF8Entry} that + * describes this local. + */ + public void setTypeIndex(int index) { + _descriptorIndex = index; + } + + /** + * Return the full name of the local's type, or null if unset. + */ + public String getTypeName() { + if (getTypeIndex() == 0) { + return null; + } + + UTF8Entry entry = (UTF8Entry) getPool().getEntry(getTypeIndex()); + + return getProject().getNameCache() + .getExternalForm(entry.getValue(), false); + } + + /** + * Set the type of this local. + */ + public void setType(String type) { + if (type == null) { + setTypeIndex(0); + } else { + type = getProject().getNameCache().getInternalForm(type, true); + setTypeIndex(getPool().findUTF8Entry(type, true)); + } + } + + /////////////////////////// + // BCEntity implementation + /////////////////////////// + public Project getProject() { + return _owner.getProject(); + } + + public ConstantPool getPool() { + return _owner.getPool(); + } + + public ClassLoader getClassLoader() { + return _owner.getClassLoader(); + } + + public boolean isValid() { + return _owner != null; + } + + ////////////////// + // I/O operations + ////////////////// + void read(DataInput in) throws IOException { + setStartPc(in.readUnsignedShort()); + setLength(in.readUnsignedShort()); + setNameIndex(in.readUnsignedShort()); + setTypeIndex(in.readUnsignedShort()); + setLocal(in.readUnsignedShort()); + } + + void write(DataOutput out) throws IOException { + out.writeShort(getStartPc()); + out.writeShort(getLength()); + out.writeShort(getNameIndex()); + out.writeShort(getTypeIndex()); + out.writeShort(getLocal()); + } + + public Code getCode() { + return _owner.getCode(); + } +} diff --git a/serp/src/main/java/serp/bytecode/LocalTable.java b/serp/src/main/java/serp/bytecode/LocalTable.java new file mode 100755 index 000000000..71be28609 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/LocalTable.java @@ -0,0 +1,238 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + +import java.io.*; + +import java.util.*; + + +/** + *

Code blocks compiled from source have local tables mapping + * locals used in opcodes to their names and descriptions.

+ * + * @author Abe White + */ +public abstract class LocalTable extends Attribute implements InstructionPtr { + private List _locals = new ArrayList(); + + LocalTable(int nameIndex, Attributes owner) { + super(nameIndex, owner); + } + + /** + * Return all the locals of this method. + */ + public Local[] getLocals() { + return (Local[]) _locals.toArray(newLocalArray(_locals.size())); + } + + /** + * Return the local with the given locals index, or null if none. + */ + public Local getLocal(int local) { + for (int i = 0; i < _locals.size(); i++) + if (((Local) _locals.get(i)).getLocal() == local) { + return (Local) _locals.get(i); + } + + return null; + } + + /** + * Return the local with the given name, or null if none. If multiple + * locals have the given name, which is returned is undefined. + */ + public Local getLocal(String name) { + String loc; + + for (int i = 0; i < _locals.size(); i++) { + loc = ((Local) _locals.get(i)).getName(); + + if (((loc == null) && (name == null)) || + ((loc != null) && loc.equals(name))) { + return (Local) _locals.get(i); + } + } + + return null; + } + + /** + * Return all locals with the given name, or empty array if none. + */ + public Local[] getLocals(String name) { + List matches = new LinkedList(); + String loc; + + for (int i = 0; i < _locals.size(); i++) { + loc = ((Local) _locals.get(i)).getName(); + + if (((loc == null) && (name == null)) || + ((loc != null) && loc.equals(name))) { + matches.add(_locals.get(i)); + } + } + + return (Local[]) matches.toArray(newLocalArray(matches.size())); + } + + /** + * Set the locals of this table. This method is useful when + * importing locals from another method. + */ + public void setLocals(Local[] locals) { + clear(); + + if (locals != null) { + for (int i = 0; i < locals.length; i++) + addLocal(locals[i]); + } + } + + /** + * Import a local from another method/class. Note that + * the program counter and length from the given local is copied + * directly, and thus will be incorrect unless this method is the same + * as the one the local is copied from, or the pc and length are reset. + */ + public Local addLocal(Local local) { + Local newLocal = addLocal(local.getName(), local.getTypeName()); + newLocal.setStartPc(local.getStartPc()); + newLocal.setLength(local.getLength()); + + return newLocal; + } + + /** + * Add a local to this table. + */ + public Local addLocal() { + Local local = newLocal(); + _locals.add(local); + + return local; + } + + /** + * Create a new element of this table. + */ + protected abstract Local newLocal(); + + /** + * Create a new array. + */ + protected abstract Local[] newLocalArray(int size); + + /** + * Add a local to this table. + */ + public Local addLocal(String name, String type) { + Local local = addLocal(); + local.setName(name); + local.setType(type); + + return local; + } + + /** + * Clear all locals from this table. + */ + public void clear() { + for (int i = 0; i < _locals.size(); i++) + ((Local) _locals.get(i)).invalidate(); + + _locals.clear(); + } + + /** + * Removes the local with the given locals index from the table. + * + * @return true if a local was removed, false otherwise + */ + public boolean removeLocal(int local) { + return removeLocal(getLocal(local)); + } + + /** + * Removes the local with the given name from this method. + * + * @return true if a local was removed, false otherwise + */ + public boolean removeLocal(String name) { + return removeLocal(getLocal(name)); + } + + /** + * Removes a local from this method. After this method, the local + * will be invalid, and the result of any operations on it is undefined. + * + * @return true if a local was removed, false otherwise + */ + public boolean removeLocal(Local local) { + if ((local == null) || !_locals.remove(local)) { + return false; + } + + local.invalidate(); + + return true; + } + + public void updateTargets() { + for (int i = 0; i < _locals.size(); i++) + ((Local) _locals.get(i)).updateTargets(); + } + + public void replaceTarget(Instruction oldTarget, Instruction newTarget) { + for (int i = 0; i < _locals.size(); i++) + ((Local) _locals.get(i)).replaceTarget(oldTarget, newTarget); + } + + public Code getCode() { + return (Code) getOwner(); + } + + int getLength() { + return 2 + (10 * _locals.size()); + } + + void read(Attribute other) { + setLocals(((LocalTable) other).getLocals()); + } + + void read(DataInput in, int length) throws IOException { + clear(); + + int numLocals = in.readUnsignedShort(); + + Local Local; + + for (int i = 0; i < numLocals; i++) { + Local = addLocal(); + Local.read(in); + } + } + + void write(DataOutput out, int length) throws IOException { + out.writeShort(_locals.size()); + + for (int i = 0; i < _locals.size(); i++) + ((Local) _locals.get(i)).write(out); + } +} diff --git a/serp/src/main/java/serp/bytecode/LocalVariable.java b/serp/src/main/java/serp/bytecode/LocalVariable.java new file mode 100755 index 000000000..fffc6bb58 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/LocalVariable.java @@ -0,0 +1,95 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + +import serp.util.*; + + +/** + *

A local variable contains the name, description, index and scope + * of a local used in opcodes.

+ * + * @author Abe White + */ +public class LocalVariable extends Local { + LocalVariable(LocalVariableTable owner) { + super(owner); + } + + /** + * The owning table. + */ + public LocalVariableTable getLocalVariableTable() { + return (LocalVariableTable) getTable(); + } + + /** + * Return the type of this local. + * If the type has not been set, this method will return null. + */ + public Class getType() { + String type = getTypeName(); + + if (type == null) { + return null; + } + + return Strings.toClass(type, getClassLoader()); + } + + /** + * Return the type of this local. + * If the type has not been set, this method will return null. + */ + public BCClass getTypeBC() { + String type = getTypeName(); + + if (type == null) { + return null; + } + + return getProject().loadClass(type, getClassLoader()); + } + + /** + * Set the type of this local. + */ + public void setType(Class type) { + if (type == null) { + setType((String) null); + } else { + setType(type.getName()); + } + } + + /** + * Set the type of this local. + */ + public void setType(BCClass type) { + if (type == null) { + setType((String) null); + } else { + setType(type.getName()); + } + } + + public void acceptVisit(BCVisitor visit) { + visit.enterLocalVariable(this); + visit.exitLocalVariable(this); + } +} diff --git a/serp/src/main/java/serp/bytecode/LocalVariableInstruction.java b/serp/src/main/java/serp/bytecode/LocalVariableInstruction.java new file mode 100755 index 000000000..4ab6be9c4 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/LocalVariableInstruction.java @@ -0,0 +1,177 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + + +/** + *

An instruction that has an argument of an index into the + * local variable table of the current frame. This includes most of the + * load and store instructions.

+ * + *

The local variable table size is fixed by the maxLocals + * property of the code block. Long and double types take up 2 local variable + * indexes.

+ * + *

Parameter values to methods are loaded into the local variable table + * prior to the execution of the first instruction. The 0 index of the + * table is set to the instance of the class the method is being invoked + * on.

+ * + * @author Abe White + */ +public abstract class LocalVariableInstruction extends TypedInstruction { + private int _index = -1; + + LocalVariableInstruction(Code owner) { + super(owner); + } + + LocalVariableInstruction(Code owner, int opcode) { + super(owner, opcode); + calculateLocal(); + } + + public String getTypeName() { + return null; + } + + public TypedInstruction setType(String type) { + throw new UnsupportedOperationException(); + } + + /** + * Return the index of the local variable that this instruction + * operates on. + */ + public int getLocal() { + return _index; + } + + /** + * Set the index of the local variable that this instruction + * operates on. + * + * @return this instruction, for method chaining + */ + public LocalVariableInstruction setLocal(int index) { + _index = index; + calculateOpcode(); + + return this; + } + + /** + * Return the parameter that this instruction operates on, or -1 if none. + */ + public int getParam() { + return getCode().getParamsIndex(getLocal()); + } + + /** + * Set the method parameter that this instruction operates on. This + * will set both the local index and the type of the instruction based + * on the current method parameters. + */ + public LocalVariableInstruction setParam(int param) { + int local = getCode().getLocalsIndex(param); + + if (local != -1) { + BCMethod method = getCode().getMethod(); + setType(method.getParamNames()[param]); + } + + return setLocal(local); + } + + /** + * Return the local variable object this instruction + * operates on, or null if none. + * + * @see LocalVariableTable#getLocalVariable(int) + */ + public LocalVariable getLocalVariable() { + LocalVariableTable table = getCode().getLocalVariableTable(false); + + if (table == null) { + return null; + } + + return table.getLocalVariable(getLocal()); + } + + /** + * Set the local variable object this instruction + * operates on. This method will set both the type and local index + * of this instruction from the given local variable. + * + * @return this instruction, for method chaining + */ + public LocalVariableInstruction setLocalVariable(LocalVariable local) { + if (local == null) { + return setLocal(-1); + } else { + String type = local.getTypeName(); + + if (type != null) { + setType(type); + } + + return setLocal(local.getLocal()); + } + } + + /** + * Two local variable instructions are equal if the local index they + * reference is equal or if either index is 0/unset. + */ + public boolean equalsInstruction(Instruction other) { + if (this == other) { + return true; + } + + if (!getClass().equals(other.getClass())) { + return false; + } + + LocalVariableInstruction ins = (LocalVariableInstruction) other; + int index = getLocal(); + int insIndex = ins.getLocal(); + + return (index == -1) || (insIndex == -1) || (index == insIndex); + } + + void read(Instruction orig) { + super.read(orig); + setLocal(((LocalVariableInstruction) orig).getLocal()); + } + + /** + * Subclasses with variable opcodes can use this method to be + * notified that information possibly affecting the opcode has been + * changed. + */ + void calculateOpcode() { + } + + /** + * Subclasses can use this method to calculate + * the locals index based on their opcode. + */ + void calculateLocal() { + } +} diff --git a/serp/src/main/java/serp/bytecode/LocalVariableTable.java b/serp/src/main/java/serp/bytecode/LocalVariableTable.java new file mode 100755 index 000000000..9238d9bab --- /dev/null +++ b/serp/src/main/java/serp/bytecode/LocalVariableTable.java @@ -0,0 +1,121 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + + +/** + *

Code blocks compiled from source have local variable tables mapping + * locals used in opcodes to their names and descriptions.

+ * + * @author Abe White + */ +public class LocalVariableTable extends LocalTable { + LocalVariableTable(int nameIndex, Attributes owner) { + super(nameIndex, owner); + } + + /** + * Return all the locals of this method. + */ + public LocalVariable[] getLocalVariables() { + return (LocalVariable[]) getLocals(); + } + + /** + * Return the local with the given locals index, or null if none. + */ + public LocalVariable getLocalVariable(int local) { + return (LocalVariable) getLocal(local); + } + + /** + * Return the local with the given name, or null if none. If multiple + * locals have the given name, which is returned is undefined. + */ + public LocalVariable getLocalVariable(String name) { + return (LocalVariable) getLocal(name); + } + + /** + * Return all locals with the given name, or empty array if none. + */ + public LocalVariable[] getLocalVariables(String name) { + return (LocalVariable[]) getLocals(name); + } + + /** + * Import a local from another method/class. Note that + * the program counter and length from the given local is copied + * directly, and thus will be incorrect unless this method is the same + * as the one the local is copied from, or the pc and length are reset. + */ + public LocalVariable addLocalVariable(LocalVariable local) { + return (LocalVariable) addLocal(local); + } + + /** + * Add a local to this table. + */ + public LocalVariable addLocalVariable() { + return (LocalVariable) addLocal(); + } + + /** + * Add a local to this table. + */ + public LocalVariable addLocalVariable(String name, String type) { + return (LocalVariable) addLocal(name, type); + } + + /** + * Add a local to this table. + */ + public LocalVariable addLocalVariable(String name, Class type) { + String typeName = (type == null) ? null : type.getName(); + + return addLocalVariable(name, typeName); + } + + /** + * Add a local to this table. + */ + public LocalVariable addLocalVariable(String name, BCClass type) { + String typeName = (type == null) ? null : type.getName(); + + return addLocalVariable(name, typeName); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterLocalVariableTable(this); + + LocalVariable[] locals = (LocalVariable[]) getLocals(); + + for (int i = 0; i < locals.length; i++) + locals[i].acceptVisit(visit); + + visit.exitLocalVariableTable(this); + } + + protected Local newLocal() { + return new LocalVariable(this); + } + + protected Local[] newLocalArray(int size) { + return new LocalVariable[size]; + } +} diff --git a/serp/src/main/java/serp/bytecode/LocalVariableType.java b/serp/src/main/java/serp/bytecode/LocalVariableType.java new file mode 100755 index 000000000..c18a66ac7 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/LocalVariableType.java @@ -0,0 +1,43 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + + +/** + *

A local variable type contains the name, signature, index and scope + * of a generics-using local used in opcodes.

+ * + * @author Abe White + */ +public class LocalVariableType extends Local { + LocalVariableType(LocalVariableTypeTable owner) { + super(owner); + } + + /** + * The owning table. + */ + public LocalVariableTypeTable getLocalVariableTypeTable() { + return (LocalVariableTypeTable) getTable(); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterLocalVariableType(this); + visit.exitLocalVariableType(this); + } +} diff --git a/serp/src/main/java/serp/bytecode/LocalVariableTypeTable.java b/serp/src/main/java/serp/bytecode/LocalVariableTypeTable.java new file mode 100755 index 000000000..de44a4b9c --- /dev/null +++ b/serp/src/main/java/serp/bytecode/LocalVariableTypeTable.java @@ -0,0 +1,103 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + + +/** + *

Code blocks compiled from source have local variable type tables mapping + * generics-using locals used in opcodes to their names and signatures.

+ * + * @author Abe White + */ +public class LocalVariableTypeTable extends LocalTable { + LocalVariableTypeTable(int nameIndex, Attributes owner) { + super(nameIndex, owner); + } + + /** + * Return all the locals of this method. + */ + public LocalVariableType[] getLocalVariableTypes() { + return (LocalVariableType[]) getLocals(); + } + + /** + * Return the local with the given locals index, or null if none. + */ + public LocalVariableType getLocalVariableType(int local) { + return (LocalVariableType) getLocal(local); + } + + /** + * Return the local with the given name, or null if none. If multiple + * locals have the given name, which is returned is undefined. + */ + public LocalVariableType getLocalVariableType(String name) { + return (LocalVariableType) getLocal(name); + } + + /** + * Return all locals with the given name, or empty array if none. + */ + public LocalVariableType[] getLocalVariableTypes(String name) { + return (LocalVariableType[]) getLocals(name); + } + + /** + * Import a local from another method/class. Note that + * the program counter and length from the given local is copied + * directly, and thus will be incorrect unless this method is the same + * as the one the local is copied from, or the pc and length are reset. + */ + public LocalVariableType addLocalVariableType(LocalVariableType local) { + return (LocalVariableType) addLocal(local); + } + + /** + * Add a local to this table. + */ + public LocalVariableType addLocalVariableType() { + return (LocalVariableType) addLocal(); + } + + /** + * Add a local to this table. + */ + public LocalVariableType addLocalVariableType(String name, String type) { + return (LocalVariableType) addLocal(name, type); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterLocalVariableTypeTable(this); + + LocalVariableType[] locals = (LocalVariableType[]) getLocals(); + + for (int i = 0; i < locals.length; i++) + locals[i].acceptVisit(visit); + + visit.exitLocalVariableTypeTable(this); + } + + protected Local newLocal() { + return new LocalVariableType(this); + } + + protected Local[] newLocalArray(int size) { + return new LocalVariableType[size]; + } +} diff --git a/serp/src/main/java/serp/bytecode/LookupSwitchInstruction.java b/serp/src/main/java/serp/bytecode/LookupSwitchInstruction.java new file mode 100755 index 000000000..51ab34a94 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/LookupSwitchInstruction.java @@ -0,0 +1,255 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + +import serp.util.*; + +import java.io.*; + +import java.util.*; + + +/** + *

The lookupswitch instruction.

+ * + * @author Abe White + */ +public class LookupSwitchInstruction extends JumpInstruction { + // case info + private List _matches = new LinkedList(); + private List _cases = new LinkedList(); + + LookupSwitchInstruction(Code owner) { + super(owner, Constants.LOOKUPSWITCH); + } + + int getLength() { + // don't call super.getLength(), cause JumpInstruction will return + // value assuming this is an 'if' or 'goto' instruction + int length = 1; + + // make the first byte of the 'default' a multiple of 4 from the + // start of the method + int byteIndex = getByteIndex() + 1; + + for (; (byteIndex % 4) != 0; byteIndex++, length++) + ; + + // default, npairs + length += 8; + + // pairs + length += (8 * _matches.size()); + + return length; + } + + public int getLogicalStackChange() { + return getStackChange(); + } + + public int getStackChange() { + return -1; + } + + /** + * Synonymous with {@link #getTarget}. + */ + public Instruction getDefaultTarget() { + return getTarget(); + } + + /** + * Synonymous with {@link #setTarget}. + */ + public LookupSwitchInstruction setDefaultTarget(Instruction ins) { + return (LookupSwitchInstruction) setTarget(ins); + } + + /** + * Synonymous with {@link #getOffset}. + */ + public int getDefaultOffset() { + return getOffset(); + } + + /** + * Synonymous with {@link #setOffset}. + */ + public LookupSwitchInstruction setDefaultOffset(int offset) { + setOffset(offset); + + return this; + } + + /** + * Set the match-jumppt pairs for this switch. + * + * @return this instruction, for method chaining + */ + public LookupSwitchInstruction setCases(int[] matches, Instruction[] targets) { + _matches.clear(); + _cases.clear(); + + for (int i = 0; i < matches.length; i++) + _matches.add(Numbers.valueOf(matches[i])); + + for (int i = 0; i < targets.length; i++) { + InstructionPtrStrategy next = new InstructionPtrStrategy(this); + next.setTargetInstruction(targets[i]); + _cases.add(next); + } + + return this; + } + + public int[] getOffsets() { + int bi = getByteIndex(); + int[] offsets = new int[_cases.size()]; + + for (int i = 0; i < offsets.length; i++) + offsets[i] = ((InstructionPtrStrategy) _cases.get(i)).getByteIndex() - + bi; + + return offsets; + } + + /** + * Return the values of the case statements for this switch. + */ + public int[] getMatches() { + int[] matches = new int[_matches.size()]; + Iterator itr = _matches.iterator(); + + for (int i = 0; i < matches.length; i++) + matches[i] = ((Integer) itr.next()).intValue(); + + return matches; + } + + /** + * Return the targets of the case statements for this switch. + */ + public Instruction[] getTargets() { + Instruction[] result = new Instruction[_cases.size()]; + + for (int i = 0; i < result.length; i++) + result[i] = ((InstructionPtrStrategy) _cases.get(i)).getTargetInstruction(); + + return result; + } + + /** + * Add a case to this switch. + * + * @return this instruction, for method chaining + */ + public LookupSwitchInstruction addCase(int match, Instruction target) { + _matches.add(Numbers.valueOf(match)); + _cases.add(new InstructionPtrStrategy(this, target)); + + return this; + } + + private Instruction findJumpPoint(int jumpByteIndex, List inss) { + Instruction ins; + + for (Iterator itr = inss.iterator(); itr.hasNext();) { + ins = (Instruction) itr.next(); + + if (ins.getByteIndex() == jumpByteIndex) { + return ins; + } + } + + return null; + } + + public void updateTargets() { + super.updateTargets(); + + for (Iterator itr = _cases.iterator(); itr.hasNext();) + ((InstructionPtrStrategy) itr.next()).updateTargets(); + } + + public void replaceTarget(Instruction oldTarget, Instruction newTarget) { + super.replaceTarget(oldTarget, newTarget); + + for (Iterator itr = _cases.iterator(); itr.hasNext();) + ((InstructionPtrStrategy) itr.next()).replaceTarget(oldTarget, + newTarget); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterLookupSwitchInstruction(this); + visit.exitLookupSwitchInstruction(this); + } + + void read(Instruction orig) { + super.read(orig); + + LookupSwitchInstruction ins = (LookupSwitchInstruction) orig; + _matches = new LinkedList(ins._matches); + _cases.clear(); + + for (Iterator itr = ins._cases.iterator(); itr.hasNext();) { + InstructionPtrStrategy origPtr = (InstructionPtrStrategy) itr.next(); + InstructionPtrStrategy newPtr = new InstructionPtrStrategy(this); + newPtr.setByteIndex(origPtr.getByteIndex()); + _cases.add(newPtr); + } + } + + void read(DataInput in) throws IOException { + // don't call super + int bi = getByteIndex(); + + for (int byteIndex = bi + 1; (byteIndex % 4) != 0; byteIndex++) + in.readByte(); + + setOffset(in.readInt()); + + _matches.clear(); + _cases.clear(); + + for (int i = 0, pairCount = in.readInt(); i < pairCount; i++) { + _matches.add(Numbers.valueOf(in.readInt())); + + InstructionPtrStrategy next = new InstructionPtrStrategy(this); + next.setByteIndex(bi + in.readInt()); + _cases.add(next); + } + } + + void write(DataOutput out) throws IOException { + // don't call super + int bi = getByteIndex(); + + for (int byteIndex = bi + 1; (byteIndex % 4) != 0; byteIndex++) + out.writeByte(0); + + out.writeInt(getOffset()); + out.writeInt(_matches.size()); + + for (int i = 0; i < _matches.size(); i++) { + out.writeInt(((Integer) _matches.get(i)).intValue()); + out.writeInt(((InstructionPtrStrategy) _cases.get(i)).getByteIndex() - + bi); + } + } +} diff --git a/serp/src/main/java/serp/bytecode/MathInstruction.java b/serp/src/main/java/serp/bytecode/MathInstruction.java new file mode 100755 index 000000000..b3a541b60 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/MathInstruction.java @@ -0,0 +1,294 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + +import java.util.*; + + +/** + *

One of the math operations defined in the {@link Constants} interface. + * Changing the type or operation of the instruction will automatically + * update the underlying opcode.

+ * + * @author Abe White + */ +public class MathInstruction extends TypedInstruction { + private static final Class[][] _mappings = new Class[][] { + { byte.class, int.class }, + { boolean.class, int.class }, + { char.class, int.class }, + { short.class, int.class }, + { void.class, int.class }, + { Object.class, int.class }, + }; + private int _op = -1; + private String _type = null; + + MathInstruction(Code owner) { + super(owner); + } + + MathInstruction(Code owner, int opcode) { + super(owner, opcode); + _op = getOperation(); + } + + public int getStackChange() { + int op = getOperation(); + + if ((op == Constants.MATH_NEG) || (getOpcode() == Constants.NOP)) { + return 0; + } + + String type = getTypeName(); + + if (long.class.getName().equals(type) || + double.class.getName().equals(type)) { + switch (getOpcode()) { + case (Constants.LSHL): + case (Constants.LSHR): + case (Constants.LUSHR): + return -1; + + default: + return -2; + } + } + + return -1; + } + + public int getLogicalStackChange() { + int op = getOperation(); + + if ((op == Constants.MATH_NEG) || (getOpcode() == Constants.NOP)) { + return 0; + } + + return -1; + } + + public String getTypeName() { + switch (getOpcode()) { + case Constants.IADD: + case Constants.ISUB: + case Constants.IMUL: + case Constants.IDIV: + case Constants.IREM: + case Constants.INEG: + case Constants.ISHL: + case Constants.ISHR: + case Constants.IUSHR: + case Constants.IAND: + case Constants.IOR: + case Constants.IXOR: + return int.class.getName(); + + case Constants.LADD: + case Constants.LSUB: + case Constants.LMUL: + case Constants.LDIV: + case Constants.LREM: + case Constants.LNEG: + case Constants.LSHL: + case Constants.LSHR: + case Constants.LUSHR: + case Constants.LAND: + case Constants.LOR: + case Constants.LXOR: + return long.class.getName(); + + case Constants.FADD: + case Constants.FSUB: + case Constants.FMUL: + case Constants.FDIV: + case Constants.FREM: + case Constants.FNEG: + return float.class.getName(); + + case Constants.DADD: + case Constants.DSUB: + case Constants.DMUL: + case Constants.DDIV: + case Constants.DREM: + case Constants.DNEG: + return double.class.getName(); + + default: + return _type; + } + } + + public TypedInstruction setType(String type) { + type = mapType(type, _mappings, true); + + // if an invalid type or op, revert to nop + if ((type == null) || (_op < 0)) { + _type = type; + + return (TypedInstruction) setOpcode(Constants.NOP); + } + + // valid opcode, unset saved type + _type = null; + + switch (type.charAt(0)) { + case 'i': + return (TypedInstruction) setOpcode(_op); + + case 'l': + return (TypedInstruction) setOpcode(_op + 1); + + case 'f': + return (TypedInstruction) setOpcode(_op + 2); + + case 'd': + return (TypedInstruction) setOpcode(_op + 3); + + default: + throw new IllegalStateException(); + } + } + + /** + * Set the math operation to be performed. This should be one of the + * math constant defined in {@link Constants}. + * + * @return this instruction, for method chaining + */ + public MathInstruction setOperation(int operation) { + _op = operation; + + // this calculates the opcode + setType(getTypeName()); + + return this; + } + + /** + * Return the operation for this math instruction; will be one of the + * math constant defined in {@link Constants}, or -1 if + * unset. + */ + public int getOperation() { + switch (getOpcode()) { + case Constants.IADD: + case Constants.LADD: + case Constants.FADD: + case Constants.DADD: + return Constants.MATH_ADD; + + case Constants.ISUB: + case Constants.LSUB: + case Constants.FSUB: + case Constants.DSUB: + return Constants.MATH_SUB; + + case Constants.IMUL: + case Constants.LMUL: + case Constants.FMUL: + case Constants.DMUL: + return Constants.MATH_MUL; + + case Constants.IDIV: + case Constants.LDIV: + case Constants.FDIV: + case Constants.DDIV: + return Constants.MATH_DIV; + + case Constants.IREM: + case Constants.LREM: + case Constants.FREM: + case Constants.DREM: + return Constants.MATH_REM; + + case Constants.INEG: + case Constants.LNEG: + case Constants.FNEG: + case Constants.DNEG: + return Constants.MATH_NEG; + + case Constants.ISHL: + case Constants.LSHL: + return Constants.MATH_SHL; + + case Constants.ISHR: + case Constants.LSHR: + return Constants.MATH_SHR; + + case Constants.IUSHR: + case Constants.LUSHR: + return Constants.MATH_USHR; + + case Constants.IAND: + case Constants.LAND: + return Constants.MATH_AND; + + case Constants.IOR: + case Constants.LOR: + return Constants.MATH_OR; + + case Constants.IXOR: + case Constants.LXOR: + return Constants.MATH_XOR; + + default: + return _op; + } + } + + /** + * MathInstructions are equal if they have the same operation and type, + * or the operation and type of either is unset. + */ + public boolean equalsInstruction(Instruction other) { + if (this == other) { + return true; + } + + if (!(other instanceof MathInstruction)) { + return false; + } + + MathInstruction ins = (MathInstruction) other; + + int op = getOperation(); + int otherOp = ins.getOperation(); + boolean opEq = (op == -1) || (otherOp == -1) || (op == otherOp); + + String type = getTypeName(); + String otherType = ins.getTypeName(); + boolean typeEq = (type == null) || (otherType == null) || + type.equals(otherType); + + return opEq && typeEq; + } + + public void acceptVisit(BCVisitor visit) { + visit.enterMathInstruction(this); + visit.exitMathInstruction(this); + } + + void read(Instruction orig) { + super.read(orig); + + MathInstruction ins = (MathInstruction) orig; + _type = ins._type; + _op = ins._op; + } +} diff --git a/serp/src/main/java/serp/bytecode/MethodInstruction.java b/serp/src/main/java/serp/bytecode/MethodInstruction.java new file mode 100755 index 000000000..86eab36e4 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/MethodInstruction.java @@ -0,0 +1,777 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import serp.bytecode.visitor.*; + +import serp.util.*; + +import java.io.*; + +import java.lang.reflect.*; + + +/** + *

An instruction that invokes a method.

+ * + * @author Abe White + */ +public class MethodInstruction extends Instruction { + private int _index = 0; + + MethodInstruction(Code owner, int opcode) { + super(owner, opcode); + } + + int getLength() { + if (getOpcode() == Constants.INVOKEINTERFACE) { + return super.getLength() + 4; + } + + return super.getLength() + 2; + } + + public int getLogicalStackChange() { + String ret = getMethodReturnName(); + + if (ret == null) { + return 0; + } + + int stack = 0; + + // subtract a stack pos for the this ptr + if (getOpcode() != Constants.INVOKESTATIC) { + stack--; + } + + // and for each arg + String[] params = getMethodParamNames(); + + for (int i = 0; i < params.length; i++) + stack--; + + // add for the return value, if any + if (!void.class.getName().equals(ret)) { + stack++; + } + + return stack; + } + + public int getStackChange() { + String ret = getMethodReturnName(); + + if (ret == null) { + return 0; + } + + int stack = 0; + + // subtract a stack pos for the this ptr + if (getOpcode() != Constants.INVOKESTATIC) { + stack--; + } + + // and for each arg (2 for longs, doubles) + String[] params = getMethodParamNames(); + + for (int i = 0; i < params.length; i++, stack--) + if (long.class.getName().equals(params[i]) || + double.class.getName().equals(params[i])) { + stack--; + } + + // add for the return value, if any + if (!void.class.getName().equals(ret)) { + stack++; + } + + if (long.class.getName().equals(ret) || + double.class.getName().equals(ret)) { + stack++; + } + + return stack; + } + + ///////////////////// + // Method operations + ///////////////////// + + /** + * Return the index in the class {@link ConstantPool} of the + * {@link ComplexEntry} describing the method to operate on. + */ + public int getMethodIndex() { + return _index; + } + + /** + * Set the index in the class {@link ConstantPool} of the + * {@link ComplexEntry} describing the method to operate on. + * + * @return this instruction, for method chaining + */ + public MethodInstruction setMethodIndex(int index) { + _index = index; + + return this; + } + + /** + * Return the method this instruction operates on, or null if not set. + */ + public BCMethod getMethod() { + String dec = getMethodDeclarerName(); + + if (dec == null) { + return null; + } + + BCClass bc = getProject().loadClass(dec, getClassLoader()); + BCMethod[] meths = bc.getMethods(getMethodName(), getMethodParamNames()); + + if (meths.length == 0) { + return null; + } + + return meths[0]; + } + + /** + * Set the method this instruction operates on. + * + * @return this instruction, for method chaining + */ + public MethodInstruction setMethod(BCMethod method) { + if (method == null) { + return setMethodIndex(0); + } + + return setMethod(method.getDeclarer().getName(), method.getName(), + method.getReturnName(), method.getParamNames()); + } + + /** + * Set the method this instruction operates on. + * + * @return this instruction, for method chaining + */ + public MethodInstruction setMethod(Method method) { + if (method == null) { + return setMethodIndex(0); + } + + return setMethod(method.getDeclaringClass(), method.getName(), + method.getReturnType(), method.getParameterTypes()); + } + + /** + * Set the method this instruction operates on. + * + * @return this instruction, for method chaining + */ + public MethodInstruction setMethod(Constructor method) { + if (method == null) { + return setMethodIndex(0); + } + + setOpcode(Constants.INVOKESPECIAL); + + return setMethod(method.getDeclaringClass(), "", void.class, + method.getParameterTypes()); + } + + /** + * Set the method this instruction operates on. + * + * @param dec the full class name of the method's declaring class + * @param name the method name + * @param returnType the full class name of the method return type + * @param param the full class names of the method param types + * @return this instruction, for method chaining + */ + public MethodInstruction setMethod(String dec, String name, + String returnType, String[] params) { + if ((name == null) && (returnType == null) && (dec == null) && + ((params == null) || (params.length == 0))) { + return setMethodIndex(0); + } + + if (dec == null) { + dec = ""; + } + + if (name == null) { + name = ""; + } + + if (returnType == null) { + returnType = ""; + } + + if (params == null) { + params = new String[0]; + } + + NameCache cache = getProject().getNameCache(); + returnType = cache.getInternalForm(returnType, true); + dec = cache.getInternalForm(dec, false); + + for (int i = 0; i < params.length; i++) + params[i] = cache.getInternalForm(params[i], true); + + String desc = cache.getDescriptor(returnType, params); + + if (getOpcode() == Constants.INVOKEINTERFACE) { + return setMethodIndex(getPool() + .findInterfaceMethodEntry(dec, name, + desc, true)); + } + + return setMethodIndex(getPool().findMethodEntry(dec, name, desc, true)); + } + + /** + * Set the method this instruction operates on, for methods that are + * declared by the current class. + * + * @param name the method name + * @param returnType the full class name of the method return type + * @param param the full class names of the method param types + * @return this instruction, for method chaining + */ + public MethodInstruction setMethod(String name, String returnType, + String[] params) { + BCClass owner = getCode().getMethod().getDeclarer(); + + return setMethod(owner.getName(), name, returnType, params); + } + + /** + * Set the method this instruction operates on. + * + * @param dec the method's declaring class + * @param name the method name + * @param returnType the class of the method return type + * @param param the class of the method param types + * @return this instruction, for method chaining + */ + public MethodInstruction setMethod(Class dec, String name, + Class returnType, Class[] params) { + String decName = (dec == null) ? null : dec.getName(); + String returnName = (returnType == null) ? null : returnType.getName(); + String[] paramNames = null; + + if (params != null) { + paramNames = new String[params.length]; + + for (int i = 0; i < params.length; i++) + paramNames[i] = params[i].getName(); + } + + return setMethod(decName, name, returnName, paramNames); + } + + /** + * Set the method this instruction operates on, for methods that are + * declared by the current class. + * + * @param name the method name + * @param returnType the class of the method return type + * @param param the class of the method param types + * @return this instruction, for method chaining + */ + public MethodInstruction setMethod(String name, Class returnType, + Class[] params) { + BCClass owner = getCode().getMethod().getDeclarer(); + String returnName = (returnType == null) ? null : returnType.getName(); + String[] paramNames = null; + + if (params != null) { + paramNames = new String[params.length]; + + for (int i = 0; i < params.length; i++) + paramNames[i] = params[i].getName(); + } + + return setMethod(owner.getName(), name, returnName, paramNames); + } + + /** + * Set the method this instruction operates on. + * + * @param dec the method's declaring class + * @param name the method name + * @param returnType the class of the method return type + * @param param the class of the method param types + * @return this instruction, for method chaining + */ + public MethodInstruction setMethod(BCClass dec, String name, + BCClass returnType, BCClass[] params) { + String decName = (dec == null) ? null : dec.getName(); + String returnName = (returnType == null) ? null : returnType.getName(); + String[] paramNames = null; + + if (params != null) { + paramNames = new String[params.length]; + + for (int i = 0; i < params.length; i++) + paramNames[i] = params[i].getName(); + } + + return setMethod(decName, name, returnName, paramNames); + } + + /** + * Set the method this instruction operates on, for methods that are + * declared by the current class. + * + * @param name the method name + * @param returnType the class of the method return type + * @param param the class of the method param types + * @return this instruction, for method chaining + */ + public MethodInstruction setMethod(String name, BCClass returnType, + BCClass[] params) { + BCClass owner = getCode().getMethod().getDeclarer(); + String returnName = (returnType == null) ? null : returnType.getName(); + String[] paramNames = null; + + if (params != null) { + paramNames = new String[params.length]; + + for (int i = 0; i < params.length; i++) + paramNames[i] = params[i].getName(); + } + + return setMethod(owner.getName(), name, returnName, paramNames); + } + + ///////////////////////////////////////// + // Name, Return, Param, Owner operations + ///////////////////////////////////////// + + /** + * Return the name of the method this instruction operates on, or null + * if not set. + */ + public String getMethodName() { + int index = getMethodIndex(); + + if (index == 0) { + return null; + } + + ComplexEntry entry = (ComplexEntry) getPool().getEntry(index); + String name = entry.getNameAndTypeEntry().getNameEntry().getValue(); + + if (name.length() == 0) { + return null; + } + + return name; + } + + /** + * Set the name of the method this instruction operates on. + * + * @return this instruction, for method chaining + */ + public MethodInstruction setMethodName(String name) { + return setMethod(getMethodDeclarerName(), name, getMethodReturnName(), + getMethodParamNames()); + } + + /** + * Return the return type of the method this instruction operates on, + * or null if not set. + */ + public String getMethodReturnName() { + int index = getMethodIndex(); + + if (index == 0) { + return null; + } + + ComplexEntry entry = (ComplexEntry) getPool().getEntry(index); + String desc = entry.getNameAndTypeEntry().getDescriptorEntry().getValue(); + NameCache cache = getProject().getNameCache(); + String name = cache.getExternalForm(cache.getDescriptorReturnName(desc), + false); + + if (name.length() == 0) { + return null; + } + + return name; + } + + /** + * Return the return type of the method this instruction operates on, + * or null if not set. + */ + public Class getMethodReturnType() { + String type = getMethodReturnName(); + + if (type == null) { + return null; + } + + return Strings.toClass(type, getClassLoader()); + } + + /** + * Return the return type of the method this instruction operates on, + * or null if not set. + */ + public BCClass getMethodReturnBC() { + String type = getMethodReturnName(); + + if (type == null) { + return null; + } + + return getProject().loadClass(type, getClassLoader()); + } + + /** + * Set the return type of the method this instruction operates on. + * + * @return this instruction, for method chaining + */ + public MethodInstruction setMethodReturn(String type) { + return setMethod(getMethodDeclarerName(), getMethodName(), type, + getMethodParamNames()); + } + + /** + * Set the return type of the method this instruction operates on. + * + * @return this instruction, for method chaining + */ + public MethodInstruction setMethodReturn(Class type) { + String name = null; + + if (type != null) { + name = type.getName(); + } + + return setMethodReturn(name); + } + + /** + * Set the return type of the method this instruction operates on. + * + * @return this instruction, for method chaining + */ + public MethodInstruction setMethodReturn(BCClass type) { + String name = null; + + if (type != null) { + name = type.getName(); + } + + return setMethodReturn(name); + } + + /** + * Return the param types of the method this instruction operates on, + * or empty array if none. + */ + public String[] getMethodParamNames() { + int index = getMethodIndex(); + + if (index == 0) { + return null; + } + + ComplexEntry entry = (ComplexEntry) getPool().getEntry(index); + String desc = entry.getNameAndTypeEntry().getDescriptorEntry().getValue(); + NameCache cache = getProject().getNameCache(); + String[] names = cache.getDescriptorParamNames(desc); + + for (int i = 0; i < names.length; i++) + names[i] = cache.getExternalForm(names[i], false); + + return names; + } + + /** + * Return the param types of the method this instruction operates on, + * or empty array if none. + */ + public Class[] getMethodParamTypes() { + String[] paramNames = getMethodParamNames(); + Class[] params = new Class[paramNames.length]; + + for (int i = 0; i < paramNames.length; i++) + params[i] = Strings.toClass(paramNames[i], getClassLoader()); + + return params; + } + + /** + * Return the param types of the method this instruction operates on, + * or empty array if none. + */ + public BCClass[] getMethodParamBCs() { + String[] paramNames = getMethodParamNames(); + BCClass[] params = new BCClass[paramNames.length]; + + for (int i = 0; i < paramNames.length; i++) + params[i] = getProject().loadClass(paramNames[i], getClassLoader()); + + return params; + } + + /** + * Set the param types of the method this instruction operates on. + * + * @return this instruction, for method chaining + */ + public MethodInstruction setMethodParams(String[] types) { + return setMethod(getMethodDeclarerName(), getMethodName(), + getMethodReturnName(), types); + } + + /** + * Set the param types of the method this instruction operates on. + * + * @return this instruction, for method chaining + */ + public void setMethodParams(Class[] types) { + if (types == null) { + setMethodParams((String[]) null); + } else { + String[] names = new String[types.length]; + + for (int i = 0; i < types.length; i++) + names[i] = types[i].getName(); + + setMethodParams(names); + } + } + + /** + * Set the param types of the method this instruction operates on. + * + * @return this instruction, for method chaining + */ + public void setMethodParams(BCClass[] types) { + if (types == null) { + setMethodParams((String[]) null); + } else { + String[] names = new String[types.length]; + + for (int i = 0; i < types.length; i++) + names[i] = types[i].getName(); + + setMethodParams(names); + } + } + + /** + * Return the declaring type of the method this instruction operates on, + * or null if not set. + */ + public String getMethodDeclarerName() { + int index = getMethodIndex(); + + if (index == 0) { + return null; + } + + ComplexEntry entry = (ComplexEntry) getPool().getEntry(index); + String name = getProject().getNameCache() + .getExternalForm(entry.getClassEntry().getNameEntry() + .getValue(), false); + + if (name.length() == 0) { + return null; + } + + return name; + } + + /** + * Return the declaring type of the method this instruction operates on, + * or null if not set. + */ + public Class getMethodDeclarerType() { + String type = getMethodDeclarerName(); + + if (type == null) { + return null; + } + + return Strings.toClass(type, getClassLoader()); + } + + /** + * Return the declaring type of the method this instruction operates on, + * or null if not set. + */ + public BCClass getMethodDeclarerBC() { + String type = getMethodDeclarerName(); + + if (type == null) { + return null; + } + + return getProject().loadClass(type, getClassLoader()); + } + + /** + * Set the declaring type of the method this instruction operates on. + * + * @return this instruction, for method chaining + */ + public MethodInstruction setMethodDeclarer(String type) { + return setMethod(type, getMethodName(), getMethodReturnName(), + getMethodParamNames()); + } + + /** + * Set the declaring type of the method this instruction operates on. + * + * @return this instruction, for method chaining + */ + public MethodInstruction setMethodDeclarer(Class type) { + String name = null; + + if (type != null) { + name = type.getName(); + } + + return setMethodDeclarer(name); + } + + /** + * Set the declaring type of the method this instruction operates on. + * + * @return this instruction, for method chaining + */ + public MethodInstruction setMethodDeclarer(BCClass type) { + String name = null; + + if (type != null) { + name = type.getName(); + } + + return setMethodDeclarer(name); + } + + /** + * MethodInstructions are equal if the method they reference is the same, + * or if the method of either is unset. + */ + public boolean equalsInstruction(Instruction other) { + if (other == this) { + return true; + } + + if (!(other instanceof MethodInstruction)) { + return false; + } + + if (!super.equalsInstruction(other)) { + return false; + } + + MethodInstruction ins = (MethodInstruction) other; + + String s1 = getMethodName(); + String s2 = ins.getMethodName(); + + if (!((s1 == null) || (s2 == null) || s1.equals(s2))) { + return false; + } + + s1 = getMethodReturnName(); + s2 = ins.getMethodReturnName(); + + if (!((s1 == null) || (s2 == null) || s1.equals(s2))) { + return false; + } + + s1 = getMethodDeclarerName(); + s2 = ins.getMethodDeclarerName(); + + if (!((s1 == null) || (s2 == null) || s1.equals(s2))) { + return false; + } + + String[] p1 = getMethodParamNames(); + String[] p2 = ins.getMethodParamNames(); + + if (!((p1.length == 0) || (p2.length == 0) || (p1.length == p2.length))) { + return false; + } + + for (int i = 0; i < p1.length; i++) + if (!((p1[i] == null) || (p2[i] == null) || p1[i].equals(p2[i]))) { + return false; + } + + return true; + } + + public void acceptVisit(BCVisitor visit) { + visit.enterMethodInstruction(this); + visit.exitMethodInstruction(this); + } + + void read(Instruction orig) { + super.read(orig); + + MethodInstruction ins = (MethodInstruction) orig; + setMethod(ins.getMethodDeclarerName(), ins.getMethodName(), + ins.getMethodReturnName(), ins.getMethodParamNames()); + } + + void read(DataInput in) throws IOException { + super.read(in); + setMethodIndex(in.readUnsignedShort()); + + if (getOpcode() == Constants.INVOKEINTERFACE) { + in.readByte(); + in.readByte(); + } + } + + void write(DataOutput out) throws IOException { + super.write(out); + out.writeShort(getMethodIndex()); + + if (getOpcode() == Constants.INVOKEINTERFACE) { + String[] args = getMethodParamNames(); + int count = 1; + + for (int i = 0; i < args.length; i++, count++) + if (long.class.getName().equals(args[i]) || + double.class.getName().equals(args[i])) { + count++; + } + + out.writeByte(count); + out.writeByte(0); + } + } +} diff --git a/serp/src/main/java/serp/bytecode/MonitorEnterInstruction.java b/serp/src/main/java/serp/bytecode/MonitorEnterInstruction.java new file mode 100755 index 000000000..8fa122657 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/MonitorEnterInstruction.java @@ -0,0 +1,37 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + +import java.io.*; + + +/** + *

The monitorenter instruction.

+ * + * @author Abe White + */ +public class MonitorEnterInstruction extends MonitorInstruction { + MonitorEnterInstruction(Code owner) { + super(owner, Constants.MONITORENTER); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterMonitorEnterInstruction(this); + visit.exitMonitorEnterInstruction(this); + } +} diff --git a/serp/src/main/java/serp/bytecode/MonitorExitInstruction.java b/serp/src/main/java/serp/bytecode/MonitorExitInstruction.java new file mode 100755 index 000000000..aa2072754 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/MonitorExitInstruction.java @@ -0,0 +1,35 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + + +/** + *

The monitorexit instruction.

+ * + * @author Abe White + */ +public class MonitorExitInstruction extends MonitorInstruction { + MonitorExitInstruction(Code owner) { + super(owner, Constants.MONITOREXIT); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterMonitorExitInstruction(this); + visit.exitMonitorExitInstruction(this); + } +} diff --git a/serp/src/main/java/serp/bytecode/MonitorInstruction.java b/serp/src/main/java/serp/bytecode/MonitorInstruction.java new file mode 100755 index 000000000..356981220 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/MonitorInstruction.java @@ -0,0 +1,36 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + + +/** + *

A synchronization instruction.

+ * + * @author Abe White + */ +public abstract class MonitorInstruction extends Instruction { + MonitorInstruction(Code owner, int opcode) { + super(owner, opcode); + } + + public int getLogicalStackChange() { + return getStackChange(); + } + + public int getStackChange() { + return -1; + } +} diff --git a/serp/src/main/java/serp/bytecode/MultiANewArrayInstruction.java b/serp/src/main/java/serp/bytecode/MultiANewArrayInstruction.java new file mode 100755 index 000000000..01a17d1d8 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/MultiANewArrayInstruction.java @@ -0,0 +1,110 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + +import java.io.*; + + +/** + *

The multianewarray instruction, which creates a new + * multi-dimensional array.

+ * + * @author Abe White + */ +public class MultiANewArrayInstruction extends ClassInstruction { + private int _dims = -1; + + MultiANewArrayInstruction(Code owner) { + super(owner, Constants.MULTIANEWARRAY); + } + + int getLength() { + return super.getLength() + 1; + } + + public int getLogicalStackChange() { + return getStackChange(); + } + + public int getStackChange() { + return -(getDimensions()) + 1; + } + + /** + * Return the dimensions of the array, or -1 if not set. + */ + public int getDimensions() { + return _dims; + } + + /** + * Set the dimensions of the array. + * + * @return this instruction, for method chaining + */ + public MultiANewArrayInstruction setDimensions(int dims) { + _dims = dims; + + return this; + } + + /** + * Two MultiANewArray instructions are equal if they have the same + * type and dimensions, or if the type and dimensions of either + * is unset. + */ + public boolean equalsInstruction(Instruction other) { + if (other == this) { + return true; + } + + if (!(other instanceof MultiANewArrayInstruction)) { + return false; + } + + if (!super.equalsInstruction(other)) { + return false; + } + + MultiANewArrayInstruction ins = (MultiANewArrayInstruction) other; + int dims = getDimensions(); + int otherDims = ins.getDimensions(); + + return (dims == -1) || (otherDims == -1) || (dims == otherDims); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterMultiANewArrayInstruction(this); + visit.exitMultiANewArrayInstruction(this); + } + + void read(Instruction orig) { + super.read(orig); + setDimensions(((MultiANewArrayInstruction) orig).getDimensions()); + } + + void read(DataInput in) throws IOException { + super.read(in); + setDimensions(in.readUnsignedByte()); + } + + void write(DataOutput out) throws IOException { + super.write(out); + out.writeByte(getDimensions()); + } +} diff --git a/serp/src/main/java/serp/bytecode/NameCache.java b/serp/src/main/java/serp/bytecode/NameCache.java new file mode 100755 index 000000000..3b02a47ed --- /dev/null +++ b/serp/src/main/java/serp/bytecode/NameCache.java @@ -0,0 +1,296 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import java.util.*; + + +/** + *

Caching and conversion of names in both internal and external form.

+ * + * @author Abe White + */ +public class NameCache { + static final Object[][] _codes = new Object[][] { + { byte.class, "B" }, + { char.class, "C" }, + { double.class, "D" }, + { float.class, "F" }, + { int.class, "I" }, + { long.class, "J" }, + { short.class, "S" }, + { boolean.class, "Z" }, + { void.class, "V" }, + }; + + // caches of internal and external forms of strings + private final Map _internal = new HashMap(); + private final Map _internalDescriptor = new HashMap(); + private final Map _external = new HashMap(); + private final Map _externalHuman = new HashMap(); + + /** + * Converts the given class name to its internal form. + * + * @param className the name to convert + * @param descriptor true if the name is to be used for a descriptor + * section -- the difference seems to be that for + * descriptors, non-primitives are prefixed with 'L' + * and ended with ';' + */ + public String getInternalForm(String className, boolean descriptor) { + if ((className == null) || (className.length() == 0)) { + return className; + } + + Map cache = (descriptor) ? _internalDescriptor : _internal; + String cached = (String) cache.get(className); + + if (cached != null) { + return cached; + } + + String ret = getInternalFormInternal(className, descriptor); + cache.put(className, ret); + + return ret; + } + + /** + * @see #getInternalForm + */ + private String getInternalFormInternal(String cls, boolean descriptor) { + // handle array types, whether already in internal form or not + StringBuffer prefix = new StringBuffer(); + + while (true) { + if (cls.endsWith("[]")) { + prefix.append("["); + cls = cls.substring(0, cls.length() - 2); + } else if (cls.startsWith("[")) { + prefix.append("["); + cls = cls.substring(1); + } else { + break; + } + } + + // handle primitive array types + for (int i = 0; i < _codes.length; i++) + if (cls.equals(_codes[i][1].toString()) || + cls.equals(_codes[i][0].toString())) { + return prefix.append(_codes[i][1]).toString(); + } + + // if in descriptor form, strip leading 'L' and trailing ';' + if (cls.startsWith("L") && cls.endsWith(";")) { + cls = cls.substring(1, cls.length() - 1); + } + + // non-primitive; make sure we don't prefix method descriptors with 'L' + cls = cls.replace('.', '/'); + + if ((descriptor || (prefix.length() > 0)) && (cls.charAt(0) != '(')) { + return prefix.append("L").append(cls).append(";").toString(); + } + + return prefix.append(cls).toString(); + } + + /** + * Given the internal name of the class, return the 'normal' java name. + * + * @param internalName the internal name being used + * @param humanReadable if the returned name should be in human-readable + * form, rather than a form suitable for a + * {@link Class#forName} call -- the difference + * lies in the handling of arrays + */ + public String getExternalForm(String internalName, boolean humanReadable) { + if ((internalName == null) || (internalName.length() == 0)) { + return internalName; + } + + Map cache = (humanReadable) ? _externalHuman : _external; + String cached = (String) cache.get(internalName); + + if (cached != null) { + return cached; + } + + String ret = getExternalFormInternal(internalName, humanReadable); + cache.put(internalName, ret); + + return ret; + } + + /** + * @see #getExternalForm + */ + private String getExternalFormInternal(String intern, boolean humanReadable) { + if (!humanReadable) { + // check against primitives + for (int i = 0; i < _codes.length; i++) { + if (intern.equals(_codes[i][1].toString())) { + return _codes[i][0].toString(); + } + + if (intern.equals(_codes[i][0].toString())) { + return intern; + } + } + + intern = getInternalForm(intern, false); + + return intern.replace('/', '.'); + } + + // handle arrays + StringBuffer postfix = new StringBuffer(2); + + while (intern.startsWith("[")) { + intern = intern.substring(1); + postfix.append("[]"); + } + + // strip off leading 'L' and trailing ';' + if (intern.endsWith(";")) { + intern = intern.substring(1, intern.length() - 1); + } + + // check primitives + for (int i = 0; i < _codes.length; i++) + if (intern.equals(_codes[i][1].toString())) { + return _codes[i][0].toString() + postfix; + } + + return intern.replace('/', '.') + postfix; + } + + /** + * Construct a method descriptor from the given return and parameter + * types, which will be converted to internal form. + */ + public String getDescriptor(String returnType, String[] paramTypes) { + StringBuffer buf = new StringBuffer(); + buf.append("("); + + if (paramTypes != null) { + for (int i = 0; i < paramTypes.length; i++) { + if (paramTypes[i] == null) { + throw new NullPointerException("paramTypes[" + i + + "] = null"); + } + + buf.append(getInternalForm(paramTypes[i], true)); + } + } + + buf.append(")"); + + if (returnType == null) { + throw new NullPointerException("returnType = null"); + } + + buf.append(getInternalForm(returnType, true)); + + return buf.toString(); + } + + /** + * Return the return type, in internal form, for the given method + * descriptor string. + */ + public String getDescriptorReturnName(String descriptor) { + int index = descriptor.indexOf(')'); + + if (index == -1) { + return ""; + } + + return descriptor.substring(descriptor.indexOf(')') + 1); + } + + /** + * Return the parameter types, in internal form, for the given method + * descriptor string. + */ + public String[] getDescriptorParamNames(String descriptor) { + if ((descriptor == null) || (descriptor.length() == 0)) { + return new String[0]; + } + + int index = descriptor.indexOf(')'); + + if (index == -1) { + return new String[0]; + } + + // get rid of the parens and the return type + descriptor = descriptor.substring(1, index); + + // break the param string into individual params + List tokens = new LinkedList(); + + while (descriptor.length() > 0) { + index = 0; + + // skip the '[' up to the first letter code + while (!Character.isLetter(descriptor.charAt(index))) + index++; + + // non-primitives always start with 'L' and end with ';' + if (descriptor.charAt(index) == 'L') { + index = descriptor.indexOf(';'); + } + + tokens.add(descriptor.substring(0, index + 1)); + descriptor = descriptor.substring(index + 1); + } + + return (String[]) tokens.toArray(new String[tokens.size()]); + } + + /** + * Return the component type name for the given array type, or null + * if the given string does not represent an array type name. The name + * given should be in proper {@link Class#forName} form. + */ + public String getComponentName(String name) { + if ((name == null) || !name.startsWith("[")) { + return null; + } + + name = name.substring(1); + + if (!name.startsWith("[") && name.endsWith(";")) { + name = name.substring(1, name.length() - 1); + } + + // will convert primitive type codes to names + return getExternalForm(name, false); + } + + /** + * Clear the cache. + */ + public void clear() { + _internal.clear(); + _internalDescriptor.clear(); + _external.clear(); + _externalHuman.clear(); + } +} diff --git a/serp/src/main/java/serp/bytecode/NewArrayInstruction.java b/serp/src/main/java/serp/bytecode/NewArrayInstruction.java new file mode 100755 index 000000000..ac28ca419 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/NewArrayInstruction.java @@ -0,0 +1,173 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + +import java.io.*; + +import java.util.*; + + +/** + *

The newarray instruction, which is used to create new + * arrays of primitive types.

+ * + * @author Abe White + */ +public class NewArrayInstruction extends TypedInstruction { + private static final Class[][] _mappings = new Class[][] { + { void.class, int.class }, + { Object.class, int.class }, + }; + private int _code = -1; + + NewArrayInstruction(Code owner) { + super(owner, Constants.NEWARRAY); + } + + int getLength() { + return super.getLength() + 1; + } + + public String getTypeName() { + switch (getTypeCode()) { + case Constants.ARRAY_BOOLEAN: + return boolean.class.getName(); + + case Constants.ARRAY_CHAR: + return char.class.getName(); + + case Constants.ARRAY_FLOAT: + return float.class.getName(); + + case Constants.ARRAY_DOUBLE: + return double.class.getName(); + + case Constants.ARRAY_BYTE: + return byte.class.getName(); + + case Constants.ARRAY_SHORT: + return short.class.getName(); + + case Constants.ARRAY_INT: + return int.class.getName(); + + case Constants.ARRAY_LONG: + return long.class.getName(); + + default: + return null; + } + } + + public TypedInstruction setType(String type) { + type = mapType(type, _mappings, true); + + if (type == null) { + return setTypeCode(-1); + } + + switch (type.charAt(0)) { + case 'b': + + if (boolean.class.getName().equals(type)) { + return setTypeCode(Constants.ARRAY_BOOLEAN); + } + + return setTypeCode(Constants.ARRAY_BYTE); + + case 'c': + return setTypeCode(Constants.ARRAY_CHAR); + + case 'f': + return setTypeCode(Constants.ARRAY_FLOAT); + + case 'd': + return setTypeCode(Constants.ARRAY_DOUBLE); + + case 's': + return setTypeCode(Constants.ARRAY_SHORT); + + case 'i': + return setTypeCode(Constants.ARRAY_INT); + + case 'l': + return setTypeCode(Constants.ARRAY_LONG); + + default: + throw new IllegalStateException(); + } + } + + /** + * Return the array code used in the lowlevel bytecode, or -1 if unset. + */ + public int getTypeCode() { + return _code; + } + + /** + * Set the array code used in the lowlevel bytecode. + * + * @return this instruction, for method chaining + */ + public NewArrayInstruction setTypeCode(int code) { + _code = code; + + return this; + } + + /** + * NewArray instructions are equal if the array type is the same, + * of if the array type of either is unset. + */ + public boolean equalsInstruction(Instruction other) { + if (this == other) { + return true; + } + + if (!(other instanceof NewArrayInstruction)) { + return false; + } + + NewArrayInstruction ins = (NewArrayInstruction) other; + int code = getTypeCode(); + int otherCode = ins.getTypeCode(); + + return (code == -1) || (otherCode == -1) || (code == otherCode); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterNewArrayInstruction(this); + visit.exitNewArrayInstruction(this); + } + + void read(Instruction orig) { + super.read(orig); + setTypeCode(((NewArrayInstruction) orig).getTypeCode()); + } + + void read(DataInput in) throws IOException { + super.read(in); + setTypeCode(in.readUnsignedByte()); + } + + void write(DataOutput out) throws IOException { + super.write(out); + out.writeByte(getTypeCode()); + } +} diff --git a/serp/src/main/java/serp/bytecode/ObjectState.java b/serp/src/main/java/serp/bytecode/ObjectState.java new file mode 100755 index 000000000..9ae3d8bee --- /dev/null +++ b/serp/src/main/java/serp/bytecode/ObjectState.java @@ -0,0 +1,143 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import java.util.*; + + +/** + *

State implementing the behavior of an object type.

+ * + * @author Abe White + */ +class ObjectState extends State { + private final ConstantPool _pool = new ConstantPool(); + private final NameCache _names; + private int _index = 0; + private int _superclassIndex = 0; + private int _magic = Constants.VALID_MAGIC; + private int _major = Constants.MAJOR_VERSION; + private int _minor = Constants.MINOR_VERSION; + private int _access = Constants.ACCESS_PUBLIC | Constants.ACCESS_SUPER; + private final Collection _interfaces = new HashSet(); + private final Collection _fields = new LinkedList(); + private final Collection _methods = new LinkedList(); + private final Collection _attributes = new LinkedList(); + + public ObjectState(NameCache names) { + _names = names; + } + + public int getMagic() { + return _magic; + } + + public void setMagic(int magic) { + _magic = magic; + } + + public int getMajorVersion() { + return _major; + } + + public void setMajorVersion(int major) { + _major = major; + } + + public int getMinorVersion() { + return _minor; + } + + public void setMinorVersion(int minor) { + _minor = minor; + } + + public int getAccessFlags() { + return _access; + } + + public void setAccessFlags(int access) { + _access = access; + } + + public int getIndex() { + return _index; + } + + public void setIndex(int index) { + _index = index; + } + + public int getSuperclassIndex() { + return _superclassIndex; + } + + public void setSuperclassIndex(int index) { + _superclassIndex = index; + } + + public Collection getInterfacesHolder() { + return _interfaces; + } + + public Collection getFieldsHolder() { + return _fields; + } + + public Collection getMethodsHolder() { + return _methods; + } + + public Collection getAttributesHolder() { + return _attributes; + } + + public ConstantPool getPool() { + return _pool; + } + + public String getName() { + if (_index == 0) { + return null; + } + + return _names.getExternalForm(((ClassEntry) _pool.getEntry(_index)).getNameEntry() + .getValue(), false); + } + + public String getSuperclassName() { + if (_superclassIndex == 0) { + return null; + } + + return _names.getExternalForm(((ClassEntry) _pool.getEntry( + _superclassIndex)).getNameEntry().getValue(), false); + } + + public String getComponentName() { + return null; + } + + public boolean isPrimitive() { + return false; + } + + public boolean isArray() { + return false; + } +} diff --git a/serp/src/main/java/serp/bytecode/PrimitiveState.java b/serp/src/main/java/serp/bytecode/PrimitiveState.java new file mode 100755 index 000000000..1d87bf010 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/PrimitiveState.java @@ -0,0 +1,96 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import java.util.*; + + +/** + *

State implementing the behavior of a primitive class.

+ * + * @author Abe White + */ +class PrimitiveState extends State { + private final Class _type; + private final NameCache _names; + + public PrimitiveState(Class type, NameCache names) { + _type = type; + _names = names; + } + + public int getMagic() { + return Constants.VALID_MAGIC; + } + + public int getMajorVersion() { + return Constants.MAJOR_VERSION; + } + + public int getMinorVersion() { + return Constants.MINOR_VERSION; + } + + public int getAccessFlags() { + return Constants.ACCESS_PUBLIC | Constants.ACCESS_FINAL; + } + + public int getIndex() { + return 0; + } + + public int getSuperclassIndex() { + return 0; + } + + public Collection getInterfacesHolder() { + return Collections.EMPTY_LIST; + } + + public Collection getFieldsHolder() { + return Collections.EMPTY_LIST; + } + + public Collection getMethodsHolder() { + return Collections.EMPTY_LIST; + } + + public Collection getAttributesHolder() { + return Collections.EMPTY_LIST; + } + + public String getName() { + return _names.getExternalForm(_type.getName(), false); + } + + public String getSuperclassName() { + return null; + } + + public String getComponentName() { + return null; + } + + public boolean isPrimitive() { + return true; + } + + public boolean isArray() { + return false; + } +} diff --git a/serp/src/main/java/serp/bytecode/Project.java b/serp/src/main/java/serp/bytecode/Project.java new file mode 100755 index 000000000..c66b978c8 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/Project.java @@ -0,0 +1,451 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import serp.bytecode.visitor.*; + +import serp.util.*; + +import java.io.*; + +import java.util.*; + + +/** + *

The Project represents a working set of classes. It caches parsed + * bytecode and is responsible for bytecode class creation. Currently + * changes made in one class are not reflected in other + * classes, though this will be an option in the future.

+ * + *

Bytecode that has been parsed is held in a cache so that retrieving + * a class with the same name multiple times always returns the same + * {@link BCClass} instance.

+ * + *

A future goal is to eventually have facilities for traversing jars + * or directory structures to find classes that meet a given criteria (such + * as implementing a given interface, etc) and to perform operations on entire + * projects, similar to aspect-oriented programming.

+ * + * @author Abe White + */ +public class Project implements VisitAcceptor { + private final String _name; + private final HashMap _cache = new HashMap(); + private final NameCache _names = new NameCache(); + + /** + * Default constructor. + */ + public Project() { + this(null); + } + + /** + * Construct a named project. + */ + public Project(String name) { + _name = name; + } + + /** + * Return the project name, or null if unset. + */ + public String getName() { + return _name; + } + + /** + * Return the name cache, which includes utilities for converting names + * from internal to external form and vice versa. + */ + public NameCache getNameCache() { + return _names; + } + + /** + * Load a class with the given name. + * + * @see #loadClass(String,ClassLoader) + */ + public BCClass loadClass(String name) { + return loadClass(name, null); + } + + /** + * Load the bytecode for the class with the given name. + * If a {@link BCClass} with the given name already exists in this project, + * it will be returned. Otherwise, a new {@link BCClass} will be created + * with the given name and returned. If the name represents an existing + * type, the returned instance will contain the parsed bytecode for + * that type. If the name is of a primitive or array type, the returned + * instance will act accordingly. + * + * @param name the name of the class, including package + * @param loader the class loader to use to search for an existing + * class with the given name; if null defaults to the + * context loader of the current thread + * @throws RuntimeException on parse error + */ + public BCClass loadClass(String name, ClassLoader loader) { + // convert to proper Class.forName() form + name = _names.getExternalForm(name, false); + + BCClass cached = checkCache(name); + + if (cached != null) { + return cached; + } + + // check for existing type + if (loader == null) { + loader = Thread.currentThread().getContextClassLoader(); + } + + try { + return loadClass(Strings.toClass(name, loader)); + } catch (Exception e) { + } + + String componentName = _names.getComponentName(name); + BCClass ret = new BCClass(this); + + if (componentName != null) { + ret.setState(new ArrayState(name, componentName)); + } else { + ret.setState(new ObjectState(_names)); + ret.setName(name); + ret.setSuperclass(Object.class); + } + + cache(name, ret); + + return ret; + } + + /** + * Load the bytecode for the given class. + * If a {@link BCClass} with the name of the given class already exists in + * this project, it will be returned. Otherwise, the bytecode of the given + * class will be parsed and returned as a new {@link BCClass}. If the + * given class is an array or primitive type, the returned instance will + * act accordingly. + * + * @param type the class to parse + * @throws RuntimeException on parse error + */ + public BCClass loadClass(Class type) { + BCClass cached = checkCache(type.getName()); + + if (cached != null) { + return cached; + } + + BCClass ret = new BCClass(this); + + if (type.isPrimitive()) { + ret.setState(new PrimitiveState(type, _names)); + } else if (type.isArray()) { + ret.setState(new ArrayState(type.getName(), + _names.getExternalForm(type.getComponentType().getName(), + false))); + } else { + ret.setState(new ObjectState(_names)); + + try { + ret.read(type); + } catch (IOException ioe) { + throw new RuntimeException(ioe.toString()); + } + } + + cache(type.getName(), ret); + + return ret; + } + + /** + * Load the bytecode from the given class file. + * If this project already contains the class in the given file, it will + * be returned. Otherwise a new {@link BCClass} will be created from the + * given bytecode. + * + * @throws RuntimeException on parse error + */ + public BCClass loadClass(File classFile) { + return loadClass(classFile, null); + } + + /** + * Load the bytecode from the given class file. + * If this project already contains the class in the given file, it will + * be returned. Otherwise a new {@link BCClass} will be created from the + * given bytecode. + * + * @throws RuntimeException on parse error + */ + public BCClass loadClass(File classFile, ClassLoader loader) { + // parse the bytecode from the file + BCClass ret = new BCClass(this); + ret.setState(new ObjectState(_names)); + + try { + ret.read(classFile, loader); + } catch (IOException ioe) { + throw new RuntimeException(ioe.toString()); + } + + String name = ret.getName(); + BCClass cached = checkCache(name); + + if (cached != null) { + return cached; + } + + cache(name, ret); + + return ret; + } + + /** + * Load the bytecode from the given stream. + * If this project already contains the class in the given stream, + * it will be returned. Otherwise a new {@link BCClass} will be created + * from the given bytecode. + * + * @throws RuntimeException on parse error + */ + public BCClass loadClass(InputStream in) { + return loadClass(in, null); + } + + /** + * Load the bytecode from the given stream. + * If this project already contains the class in the given stream, + * it will be returned. Otherwise a new {@link BCClass} will be created + * from the given bytecode. + * + * @throws RuntimeException on parse error + */ + public BCClass loadClass(InputStream in, ClassLoader loader) { + BCClass ret = new BCClass(this); + ret.setState(new ObjectState(_names)); + + try { + ret.read(in, loader); + } catch (IOException ioe) { + throw new RuntimeException(ioe.toString()); + } + + String name = ret.getName(); + BCClass cached = checkCache(name); + + if (cached != null) { + return cached; + } + + cache(name, ret); + + return ret; + } + + /** + * Import the given bytecode from another project. If a {@link BCClass} + * with the same name already exists in this project, it will be returned. + * Otherwise, a new {@link BCClass} will be created from the + * information in the given class. + */ + public BCClass loadClass(BCClass bc) { + String name = bc.getName(); + BCClass cached = checkCache(name); + + if (cached != null) { + return cached; + } + + BCClass ret = new BCClass(this); + + if (bc.isPrimitive()) { + ret.setState(new PrimitiveState(bc.getType(), _names)); + } else if (bc.isArray()) { + ret.setState(new ArrayState(bc.getName(), bc.getComponentName())); + } else { + ret.setState(new ObjectState(_names)); + ret.read(bc); + } + + cache(name, ret); + + return ret; + } + + /** + * Clears all classes from this project. + */ + public void clear() { + Collection values = _cache.values(); + BCClass bc; + + for (Iterator itr = values.iterator(); itr.hasNext();) { + bc = (BCClass) itr.next(); + itr.remove(); + bc.invalidate(); + } + + _names.clear(); + } + + /** + * Remove a class from this project. After removal, the result of any + * further operations on the class is undefined. + * + * @return true if the class belonged to this project, false + * otherwise + */ + public boolean removeClass(String type) { + return removeClass(checkCache(type)); + } + + /** + * Remove a class from this project. After removal, the result of any + * further operations on the class is undefined. + * + * @return true if the class belonged to this project, false + * otherwise + */ + public boolean removeClass(Class type) { + if (type == null) { + return false; + } + + return removeClass(checkCache(type.getName())); + } + + /** + * Remove a class from this project. After removal, the result of any + * further operations on the class is undefined. + * + * @return true if the class belonged to this project, false + * otherwise + */ + public boolean removeClass(BCClass type) { + if (type == null) { + return false; + } + + if (!removeFromCache(type.getName(), type)) { + return false; + } + + type.invalidate(); + + return true; + } + + /** + * Return all loaded classes in the project. + */ + public BCClass[] getClasses() { + Collection values = _cache.values(); + + return (BCClass[]) values.toArray(new BCClass[values.size()]); + } + + /** + * Return true if the project already contains the given class. + */ + public boolean containsClass(String type) { + return _cache.containsKey(type); + } + + /** + * Return true if the project already contains the given class. + */ + public boolean containsClass(Class type) { + return (type == null) ? false : containsClass(type.getName()); + } + + /** + * Return true if the project already contains the given class. + */ + public boolean containsClass(BCClass type) { + return (type == null) ? false : containsClass(type.getName()); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterProject(this); + + BCClass[] classes = getClasses(); + + for (int i = 0; i < classes.length; i++) + classes[i].acceptVisit(visit); + + visit.exitProject(this); + } + + /** + * Renames the given class within this project. Used internally by + * {@link BCClass} instances when their name is modified. + * + * @throws IllegalStateException if a class with the new name already + * exists + */ + void renameClass(String oldName, String newName, BCClass bc) { + if (oldName.equals(newName)) { + return; + } + + BCClass cached = (BCClass) checkCache(newName); + + if (cached != null) { + throw new IllegalStateException("A class with name " + newName + + " already exists in this project"); + } + + removeFromCache(oldName, bc); + cache(newName, bc); + } + + /** + * Check the cache for a loaded type. + */ + private BCClass checkCache(String name) { + return (BCClass) _cache.get(name); + } + + /** + * Cache a class. + */ + private void cache(String name, BCClass bc) { + _cache.put(name, bc); + } + + /** + * Remove a cached class. + */ + private boolean removeFromCache(String name, BCClass bc) { + BCClass rem = (BCClass) checkCache(name); + + if (rem != bc) { + return false; + } + + _cache.remove(name); + + return true; + } +} diff --git a/serp/src/main/java/serp/bytecode/PutFieldInstruction.java b/serp/src/main/java/serp/bytecode/PutFieldInstruction.java new file mode 100755 index 000000000..8251a85dd --- /dev/null +++ b/serp/src/main/java/serp/bytecode/PutFieldInstruction.java @@ -0,0 +1,68 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + + +/** + *

Stores a value from the stack into a field.

+ * + * @author Abe White + */ +public class PutFieldInstruction extends FieldInstruction { + PutFieldInstruction(Code owner, int opcode) { + super(owner, opcode); + } + + public int getLogicalStackChange() { + if (getFieldTypeName() == null) { + return 0; + } + + if (getOpcode() == Constants.PUTSTATIC) { + return -1; + } + + return -2; + } + + public int getStackChange() { + String type = getFieldTypeName(); + + if (type == null) { + return 0; + } + + int stack = -2; + + if (long.class.getName().equals(type) || + double.class.getName().equals(type)) { + stack++; + } + + if (getOpcode() == Constants.PUTSTATIC) { + stack++; + } + + return stack; + } + + public void acceptVisit(BCVisitor visit) { + visit.enterPutFieldInstruction(this); + visit.exitPutFieldInstruction(this); + } +} diff --git a/serp/src/main/java/serp/bytecode/RetInstruction.java b/serp/src/main/java/serp/bytecode/RetInstruction.java new file mode 100755 index 000000000..156c4b039 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/RetInstruction.java @@ -0,0 +1,64 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + +import java.io.*; + + +/** + *

The ret instruction is used in the implementation of + * finally.

+ * + * @author Abe White + */ +public class RetInstruction extends LocalVariableInstruction { + RetInstruction(Code owner) { + super(owner, Constants.RET); + } + + int getLength() { + return super.getLength() + 1; + } + + public boolean equalsInstruction(Instruction other) { + if (this == other) { + return true; + } + + if (!(other instanceof RetInstruction)) { + return false; + } + + return super.equalsInstruction(other); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterRetInstruction(this); + visit.exitRetInstruction(this); + } + + void read(DataInput in) throws IOException { + super.read(in); + setLocal(in.readUnsignedByte()); + } + + void write(DataOutput out) throws IOException { + super.write(out); + out.writeByte(getLocal()); + } +} diff --git a/serp/src/main/java/serp/bytecode/ReturnInstruction.java b/serp/src/main/java/serp/bytecode/ReturnInstruction.java new file mode 100755 index 000000000..d3eb42645 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/ReturnInstruction.java @@ -0,0 +1,128 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + +import java.io.*; + +import java.util.*; + + +/** + *

Returns a value (or void) from a method.

+ * + * @author Abe White + */ +public class ReturnInstruction extends TypedInstruction { + private static final Class[][] _mappings = new Class[][] { + { byte.class, int.class }, + { char.class, int.class }, + { short.class, int.class }, + { boolean.class, int.class }, + }; + + ReturnInstruction(Code owner) { + super(owner); + } + + ReturnInstruction(Code owner, int opcode) { + super(owner, opcode); + } + + public String getTypeName() { + switch (getOpcode()) { + case Constants.IRETURN: + return int.class.getName(); + + case Constants.LRETURN: + return long.class.getName(); + + case Constants.FRETURN: + return float.class.getName(); + + case Constants.DRETURN: + return double.class.getName(); + + case Constants.ARETURN: + return Object.class.getName(); + + case Constants.RETURN: + return void.class.getName(); + + default: + return null; + } + } + + public TypedInstruction setType(String type) { + type = mapType(type, _mappings, true); + + if (type == null) { + return (TypedInstruction) setOpcode(Constants.NOP); + } + + switch (type.charAt(0)) { + case 'i': + return (TypedInstruction) setOpcode(Constants.IRETURN); + + case 'l': + return (TypedInstruction) setOpcode(Constants.LRETURN); + + case 'f': + return (TypedInstruction) setOpcode(Constants.FRETURN); + + case 'd': + return (TypedInstruction) setOpcode(Constants.DRETURN); + + case 'v': + return (TypedInstruction) setOpcode(Constants.RETURN); + + default: + return (TypedInstruction) setOpcode(Constants.ARETURN); + } + } + + public int getLogicalStackChange() { + switch (getOpcode()) { + case Constants.NOP: + return 0; + + default: + return -1; + } + } + + public int getStackChange() { + switch (getOpcode()) { + case Constants.RETURN: + case Constants.NOP: + return 0; + + case Constants.LRETURN: + case Constants.DRETURN: + return -2; + + default: + return -1; + } + } + + public void acceptVisit(BCVisitor visit) { + visit.enterReturnInstruction(this); + visit.exitReturnInstruction(this); + } +} diff --git a/serp/src/main/java/serp/bytecode/SourceFile.java b/serp/src/main/java/serp/bytecode/SourceFile.java new file mode 100755 index 000000000..ce8e22c2f --- /dev/null +++ b/serp/src/main/java/serp/bytecode/SourceFile.java @@ -0,0 +1,135 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import serp.bytecode.visitor.*; + +import java.io.*; + + +/** + *

Attribute naming the source file for this class.

+ * + * @author Abe White + */ +public class SourceFile extends Attribute { + int _sourceFileIndex = 0; + + SourceFile(int nameIndex, Attributes owner) { + super(nameIndex, owner); + } + + int getLength() { + return 2; + } + + /** + * Return the index into the class {@link ConstantPool} of the + * {@link UTF8Entry} naming the source file for this class, or 0 if + * not set. + */ + public int getFileIndex() { + return _sourceFileIndex; + } + + /** + * Set the index into the class {@link ConstantPool} of the + * {@link UTF8Entry} naming the source file for this class. + */ + public void setFileIndex(int sourceFileIndex) { + if (sourceFileIndex < 0) { + sourceFileIndex = 0; + } + + _sourceFileIndex = sourceFileIndex; + } + + /** + * Return the name of the source file, or null if not set. + */ + public String getFileName() { + if (_sourceFileIndex == 0) { + return null; + } + + return ((UTF8Entry) getPool().getEntry(_sourceFileIndex)).getValue(); + } + + /** + * Return the file object for the source file, or null if not set. + * + * @param dir the directory of the file, or null + */ + public File getFile(File dir) { + String name = getFileName(); + + if (name == null) { + return null; + } + + return new File(dir, name); + } + + /** + * Set the name of the source file. The name should be the file name + * only; it should not include the path to the file. + */ + public void setFile(String name) { + if (name == null) { + setFileIndex(0); + } else { + setFileIndex(getPool().findUTF8Entry(name, true)); + } + } + + /** + * Set the source file. Note that only the file name is recorded; + * the path to the file is discarded. + */ + public void setFile(File file) { + if (file == null) { + setFile((String) null); + } else { + setFile(file.getName()); + } + } + + /** + * Set the file name from the current class name plus the .java extension. + */ + public void setFromClassName() { + setFile(((BCClass) getOwner()).getClassName() + ".java"); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterSourceFile(this); + visit.exitSourceFile(this); + } + + void read(Attribute other) { + setFile(((SourceFile) other).getFileName()); + } + + void read(DataInput in, int length) throws IOException { + setFileIndex(in.readUnsignedShort()); + } + + void write(DataOutput out, int length) throws IOException { + out.writeShort(getFileIndex()); + } +} diff --git a/serp/src/main/java/serp/bytecode/StackInstruction.java b/serp/src/main/java/serp/bytecode/StackInstruction.java new file mode 100755 index 000000000..0bf25a6da --- /dev/null +++ b/serp/src/main/java/serp/bytecode/StackInstruction.java @@ -0,0 +1,177 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import serp.bytecode.visitor.*; + + +/** + *

Represents an instruction that manipulates the stack of the current + * frame. Using the {@link #setType} methods is a hint about the type being + * manipulated that might cause this instruction to use the wide version + * of the opcode it represents (if manipulating a long or double). This + * saves the developer from having to decide at compile time whether to + * use pop or pop2, etc.

+ * + * @author Abe White + */ +public class StackInstruction extends TypedInstruction { + StackInstruction(Code owner, int opcode) { + super(owner, opcode); + } + + public int getLogicalStackChange() { + return getStackChange(); + } + + public int getStackChange() { + switch (getOpcode()) { + case Constants.POP: + return -1; + + case Constants.POP2: + return -2; + + case Constants.DUP: + case Constants.DUPX1: + case Constants.DUPX2: + return 1; + + case Constants.DUP2: + case Constants.DUP2X1: + case Constants.DUP2X2: + return 2; + + default: + return 0; + } + } + + /** + * This method will always return null; use {@link #isWide} to determine + * if this is pop2, dup2, etc. + */ + public String getTypeName() { + return null; + } + + public TypedInstruction setType(String type) { + type = getProject().getNameCache().getExternalForm(type, false); + + return setWide(long.class.getName().equals(type) || + double.class.getName().equals(type)); + } + + /** + * Return whether to use the wide form of the current opcode for + * operations on longs or doubles. + */ + public boolean isWide() { + switch (getOpcode()) { + case Constants.POP2: + case Constants.DUP2: + case Constants.DUP2X1: + case Constants.DUP2X2: + return true; + + default: + return false; + } + } + + /** + * Set whether to use the wide form of the current opcode for operations + * on longs or doubles. + * + * @return this instruction, for method chaining + */ + public StackInstruction setWide(boolean wide) { + switch (getOpcode()) { + case Constants.POP: + + if (wide) { + setOpcode(Constants.POP2); + } + + break; + + case Constants.POP2: + + if (!wide) { + setOpcode(Constants.POP); + } + + break; + + case Constants.DUP: + + if (wide) { + setOpcode(Constants.DUP2); + } + + break; + + case Constants.DUP2: + + if (!wide) { + setOpcode(Constants.DUP); + } + + break; + + case Constants.DUPX1: + + if (wide) { + setOpcode(Constants.DUP2X1); + } + + break; + + case Constants.DUP2X1: + + if (!wide) { + setOpcode(Constants.DUPX1); + } + + break; + + case Constants.DUPX2: + + if (wide) { + setOpcode(Constants.DUP2X2); + } + + break; + + case Constants.DUP2X2: + + if (!wide) { + setOpcode(Constants.DUPX2); + } + + break; + } + + return this; + } + + public void acceptVisit(BCVisitor visit) { + visit.enterStackInstruction(this); + visit.exitStackInstruction(this); + } +} diff --git a/serp/src/main/java/serp/bytecode/State.java b/serp/src/main/java/serp/bytecode/State.java new file mode 100755 index 000000000..e4c282f56 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/State.java @@ -0,0 +1,208 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import java.util.*; + + +/** + *

The State type is extended by various concrete types to change + * the behavior of a {@link BCClass}. All methods in this base + * implementation throw an {@link UnsupportedOperationException}

+ * + * @author Abe White + */ +class State { + /** + * A singleton instance of this type that can be used to make a + * class invalid. + */ + public static final State INVALID = new State(); + + /** + * Return the magic number of the bytecode class. + */ + public int getMagic() { + throw new UnsupportedOperationException(); + } + + /** + * Set the magic number of the bytecode class. + */ + public void setMagic(int magic) { + throw new UnsupportedOperationException(); + } + + /** + * Return the major number of the bytecode class. + */ + public int getMajorVersion() { + throw new UnsupportedOperationException(); + } + + /** + * Set the major version of the bytecode class. + */ + public void setMajorVersion(int major) { + throw new UnsupportedOperationException(); + } + + /** + * Return the minor number of the bytecode class. + */ + public int getMinorVersion() { + throw new UnsupportedOperationException(); + } + + /** + * Set the minor version of the bytecode class. + */ + public void setMinorVersion(int minor) { + throw new UnsupportedOperationException(); + } + + /** + * Return the access flags of the bytecode class. + */ + public int getAccessFlags() { + throw new UnsupportedOperationException(); + } + + /** + * Set the access flags of the bytecode class. + */ + public void setAccessFlags(int access) { + throw new UnsupportedOperationException(); + } + + /** + * Return the {@link ConstantPool} index of the {@link ClassEntry} + * for this class, or 0 if none. + */ + public int getIndex() { + throw new UnsupportedOperationException(); + } + + /** + * Set the {@link ConstantPool} index of the {@link ClassEntry} + * for this class. + */ + public void setIndex(int index) { + throw new UnsupportedOperationException(); + } + + /** + * Return the {@link ConstantPool} index of the {@link ClassEntry} + * for the superclass of this class, or 0 if none. + */ + public int getSuperclassIndex() { + throw new UnsupportedOperationException(); + } + + /** + * Set the {@link ConstantPool} index of the {@link ClassEntry} + * for the superclass of this class. Throws + * {@link UnsupportedOperationException} by default. + */ + public void setSuperclassIndex(int index) { + throw new UnsupportedOperationException(); + } + + /** + * Return the {@link ConstantPool} indexes of the {@link ClassEntry}s + * for the indexes of this class, or empty collection if none. If the + * state does not support changing the interfaces, the returned + * collection should be immutable. + */ + public Collection getInterfacesHolder() { + throw new UnsupportedOperationException(); + } + + /** + * Return the {@link BCField}s of this class, or empty collection if none. + * If the state does not support changing the fields, the returned + * collection should be immutable. + */ + public Collection getFieldsHolder() { + throw new UnsupportedOperationException(); + } + + /** + * Return the {@link BCMethod}s of this class, or empty collection if none. + * If the state does not support changing the methods, the returned + * collection should be immutable. + */ + public Collection getMethodsHolder() { + throw new UnsupportedOperationException(); + } + + /** + * Return the {@link Attribute}s of this class, or empty collection if + * none. If the state does not support changing the attributes, the + * returned collection should be immutable. + */ + public Collection getAttributesHolder() { + throw new UnsupportedOperationException(); + } + + /** + * Return the constant pool of the class. + */ + public ConstantPool getPool() { + throw new UnsupportedOperationException(); + } + + /** + * Return the name of the class. The name should be in a form suitable + * for a {@link Class#forName} call. + */ + public String getName() { + throw new UnsupportedOperationException(); + } + + /** + * Return the name of the superclass. The name should be in a form + * suitable for a {@link Class#forName} call, or null if none. + */ + public String getSuperclassName() { + throw new UnsupportedOperationException(); + } + + /** + * Return the name of the component type of this array, or null if not + * an array. The name should be in a form suitable for a + * {@link Class#forName} call. + */ + public String getComponentName() { + throw new UnsupportedOperationException(); + } + + /** + * Return true if this class is a primitive. + */ + public boolean isPrimitive() { + throw new UnsupportedOperationException(); + } + + /** + * Return true if this class is an array. + */ + public boolean isArray() { + throw new UnsupportedOperationException(); + } +} diff --git a/serp/src/main/java/serp/bytecode/StoreInstruction.java b/serp/src/main/java/serp/bytecode/StoreInstruction.java new file mode 100755 index 000000000..bbc747ee1 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/StoreInstruction.java @@ -0,0 +1,281 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + +import java.io.*; + + +/** + *

An instruction to store a value from a local variable onto + * the stack.

+ * + * @author Abe White + */ +public class StoreInstruction extends LocalVariableInstruction { + private static final Class[][] _mappings = new Class[][] { + { byte.class, int.class }, + { boolean.class, int.class }, + { char.class, int.class }, + { short.class, int.class }, + { void.class, int.class }, + }; + String _type = null; + + StoreInstruction(Code owner) { + super(owner); + } + + StoreInstruction(Code owner, int opcode) { + super(owner, opcode); + } + + int getLength() { + switch (getOpcode()) { + case Constants.ISTORE: + case Constants.LSTORE: + case Constants.FSTORE: + case Constants.DSTORE: + case Constants.ASTORE: + return super.getLength() + 1; + + default: + return super.getLength(); + } + } + + public int getLogicalStackChange() { + switch (getOpcode()) { + case Constants.NOP: + return 0; + + default: + return -1; + } + } + + public int getStackChange() { + switch (getOpcode()) { + case Constants.LSTORE: + case Constants.LSTORE0: + case Constants.LSTORE1: + case Constants.LSTORE2: + case Constants.LSTORE3: + case Constants.DSTORE: + case Constants.DSTORE0: + case Constants.DSTORE1: + case Constants.DSTORE2: + case Constants.DSTORE3: + return -2; + + case Constants.NOP: + return 0; + + default: + return -1; + } + } + + public String getTypeName() { + switch (getOpcode()) { + case Constants.ISTORE: + case Constants.ISTORE0: + case Constants.ISTORE1: + case Constants.ISTORE2: + case Constants.ISTORE3: + return int.class.getName(); + + case Constants.LSTORE: + case Constants.LSTORE0: + case Constants.LSTORE1: + case Constants.LSTORE2: + case Constants.LSTORE3: + return long.class.getName(); + + case Constants.FSTORE: + case Constants.FSTORE0: + case Constants.FSTORE1: + case Constants.FSTORE2: + case Constants.FSTORE3: + return float.class.getName(); + + case Constants.DSTORE: + case Constants.DSTORE0: + case Constants.DSTORE1: + case Constants.DSTORE2: + case Constants.DSTORE3: + return double.class.getName(); + + case Constants.ASTORE: + case Constants.ASTORE0: + case Constants.ASTORE1: + case Constants.ASTORE2: + case Constants.ASTORE3: + return Object.class.getName(); + + default: + return _type; + } + } + + public TypedInstruction setType(String type) { + type = mapType(type, _mappings, true); + + int local = getLocal(); + + // if an invalid type or local, revert to nop + if ((type == null) || (local < 0)) { + _type = type; + + return (TypedInstruction) setOpcode(Constants.NOP); + } + + // valid opcode, unset saved type + _type = null; + + switch (type.charAt(0)) { + case 'i': + return (TypedInstruction) setOpcode((local > 3) ? Constants.ISTORE + : (Constants.ISTORE0 + + local)); + + case 'l': + return (TypedInstruction) setOpcode((local > 3) ? Constants.LSTORE + : (Constants.LSTORE0 + + local)); + + case 'f': + return (TypedInstruction) setOpcode((local > 3) ? Constants.FSTORE + : (Constants.FSTORE0 + + local)); + + case 'd': + return (TypedInstruction) setOpcode((local > 3) ? Constants.DSTORE + : (Constants.DSTORE0 + + local)); + + default: + return (TypedInstruction) setOpcode((local > 3) ? Constants.ASTORE + : (Constants.ASTORE0 + + local)); + } + } + + /** + * StoreInstructions are equal if the type they reference the same + * type and locals index or if either is unset. + */ + public boolean equalsInstruction(Instruction other) { + if (other == this) { + return true; + } + + if (!super.equalsInstruction(other)) { + return false; + } + + String type = getTypeName(); + String otherType = ((StoreInstruction) other).getTypeName(); + + return (type == null) || (otherType == null) || type.equals(otherType); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterStoreInstruction(this); + visit.exitStoreInstruction(this); + } + + void read(Instruction orig) { + super.read(orig); + + StoreInstruction ins = (StoreInstruction) orig; + _type = ins._type; + } + + void read(DataInput in) throws IOException { + super.read(in); + + switch (getOpcode()) { + case Constants.ISTORE: + case Constants.LSTORE: + case Constants.FSTORE: + case Constants.DSTORE: + case Constants.ASTORE: + setLocal(in.readUnsignedByte()); + + break; + } + } + + void write(DataOutput out) throws IOException { + super.write(out); + + switch (getOpcode()) { + case Constants.ISTORE: + case Constants.LSTORE: + case Constants.FSTORE: + case Constants.DSTORE: + case Constants.ASTORE: + out.writeByte(getLocal()); + } + } + + void calculateOpcode() { + // taken care of when setting type + setType(getTypeName()); + } + + void calculateLocal() { + switch (getOpcode()) { + case Constants.ISTORE0: + case Constants.LSTORE0: + case Constants.FSTORE0: + case Constants.DSTORE0: + case Constants.ASTORE0: + setLocal(0); + + break; + + case Constants.ISTORE1: + case Constants.LSTORE1: + case Constants.FSTORE1: + case Constants.DSTORE1: + case Constants.ASTORE1: + setLocal(1); + + break; + + case Constants.ISTORE2: + case Constants.LSTORE2: + case Constants.FSTORE2: + case Constants.DSTORE2: + case Constants.ASTORE2: + setLocal(2); + + break; + + case Constants.ISTORE3: + case Constants.LSTORE3: + case Constants.FSTORE3: + case Constants.DSTORE3: + case Constants.ASTORE3: + setLocal(3); + + break; + } + } +} diff --git a/serp/src/main/java/serp/bytecode/SwitchInstruction.java b/serp/src/main/java/serp/bytecode/SwitchInstruction.java new file mode 100755 index 000000000..53573ab77 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/SwitchInstruction.java @@ -0,0 +1,247 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import java.io.DataInput; +import java.io.IOException; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + + +/** + * Contains functionality common to the different switch types + * (TableSwitch and LookupSwitch). + * + * @author Eric Lindauer + */ +public abstract class SwitchInstruction extends JumpInstruction { + private List _cases = new LinkedList(); + + public SwitchInstruction(Code owner, int opcode) { + super(owner, opcode); + } + + /** + * Returns the current byte offsets for the different + * switch cases in this Instruction. + */ + public int[] getOffsets() { + int bi = getByteIndex(); + int[] offsets = new int[_cases.size()]; + + for (int i = 0; i < offsets.length; i++) + offsets[i] = ((InstructionPtrStrategy) _cases.get(i)).getByteIndex() - + bi; + + return offsets; + } + + /** + * Sets the offsets for the instructions representing the different + * switch statement cases. WARNING: these offsets will not be changed + * in the event that the code is modified following this call. It is + * typically a good idea to follow this call with a call to updateTargets + * as soon as the instructions at the given offsets are valid, at which + * point the Instructions themselves will be used as the targets and the + * offsets will be updated as expected. + */ + public void setOffsets(int[] offsets) { + int bi = getByteIndex(); + _cases.clear(); + + for (int i = 0; i < offsets.length; i++) { + InstructionPtrStrategy next = new InstructionPtrStrategy(this); + next.setByteIndex(offsets[i] + bi); + _cases.add(next); + } + } + + public int countTargets() { + return _cases.size(); + } + + int getLength() { + // don't call super.getLength(), cause JumpInstruction will return + // value assuming this is an 'if' or 'goto' instruction + int length = 1; + + // make the first byte of the 'default' a multiple of 4 from the + // start of the method + int byteIndex = getByteIndex() + 1; + + for (; (byteIndex % 4) != 0; byteIndex++, length++) + ; + + return length; + } + + /** + * Synonymous with {@link #getTarget}. + */ + public Instruction getDefaultTarget() { + return getTarget(); + } + + /** + * Synonymous with {@link #getOffset}. + */ + public int getDefaultOffset() { + return getOffset(); + } + + /** + * Synonymous with {@link #setOffset}. + */ + public SwitchInstruction setDefaultOffset(int offset) { + setOffset(offset); + + return this; + } + + /** + * Synonymous with {@link #setTarget}. + */ + public SwitchInstruction setDefaultTarget(Instruction ins) { + return (SwitchInstruction) setTarget(ins); + } + + /** + * Return the targets for this switch, or empty array if not set. + */ + public Instruction[] getTargets() { + Instruction[] result = new Instruction[_cases.size()]; + + for (int i = 0; i < _cases.size(); i++) + result[i] = ((InstructionPtrStrategy) _cases.get(i)).getTargetInstruction(); + + return result; + } + + /** + * Set the jump points for this switch. + * + * @return this instruction, for method chaining + */ + public SwitchInstruction setTargets(Instruction[] targets) { + _cases.clear(); + + if (targets != null) { + for (int i = 0; i < targets.length; i++) + addTarget(targets[i]); + } + + return this; + } + + /** + * Add a target to this switch. + * + * @return this instruction, for method chaining + */ + public SwitchInstruction addTarget(Instruction target) { + _cases.add(new InstructionPtrStrategy(this, target)); + + return this; + } + + public int getLogicalStackChange() { + return getStackChange(); + } + + public int getStackChange() { + return -1; + } + + public void updateTargets() { + super.updateTargets(); + + for (Iterator itr = _cases.iterator(); itr.hasNext();) + ((InstructionPtrStrategy) itr.next()).updateTargets(); + } + + public void replaceTarget(Instruction oldTarget, Instruction newTarget) { + super.replaceTarget(oldTarget, newTarget); + + for (Iterator itr = _cases.iterator(); itr.hasNext();) + ((InstructionPtrStrategy) itr.next()).replaceTarget(oldTarget, + newTarget); + } + + void read(Instruction orig) { + super.read(orig); + + SwitchInstruction ins = (SwitchInstruction) orig; + _cases.clear(); + + for (Iterator itr = ins._cases.iterator(); itr.hasNext();) { + InstructionPtrStrategy incoming = (InstructionPtrStrategy) itr.next(); + InstructionPtrStrategy next = new InstructionPtrStrategy(this); + next.setByteIndex(incoming.getByteIndex()); + _cases.add(next); + } + } + + void clearTargets() { + _cases.clear(); + } + + void readTarget(DataInput in) throws IOException { + InstructionPtrStrategy next = new InstructionPtrStrategy(this); + next.setByteIndex(getByteIndex() + in.readInt()); + _cases.add(next); + } + + /** + * Set the match-jumppt pairs for this switch. + * + * @return this instruction, for method chaining + */ + public SwitchInstruction setCases(int[] matches, Instruction[] targets) { + setMatches(matches); + setTargets(targets); + + return this; + } + + public SwitchInstruction setMatches(int[] matches) { + clearMatches(); + + for (int i = 0; i < matches.length; i++) + addMatch(matches[i]); + + return this; + } + + /** + * Add a case to this switch. + * + * @return this instruction, for method chaining + */ + public SwitchInstruction addCase(int match, Instruction target) { + addMatch(match); + addTarget(target); + + return this; + } + + public abstract SwitchInstruction addMatch(int match); + + public abstract int[] getMatches(); + + abstract void clearMatches(); +} diff --git a/serp/src/main/java/serp/bytecode/Synthetic.java b/serp/src/main/java/serp/bytecode/Synthetic.java new file mode 100755 index 000000000..c17947bed --- /dev/null +++ b/serp/src/main/java/serp/bytecode/Synthetic.java @@ -0,0 +1,36 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + + +/** + *

Attribute marking a member as synthetic, or not present in the class + * source code.

+ * + * @author Abe White + */ +public class Synthetic extends Attribute { + Synthetic(int nameIndex, Attributes owner) { + super(nameIndex, owner); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterSynthetic(this); + visit.exitSynthetic(this); + } +} diff --git a/serp/src/main/java/serp/bytecode/TableSwitchInstruction.java b/serp/src/main/java/serp/bytecode/TableSwitchInstruction.java new file mode 100755 index 000000000..8a7cfa8b2 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/TableSwitchInstruction.java @@ -0,0 +1,272 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + +import java.io.*; + +import java.util.*; + + +/** + *

The tableswitch instruction.

+ * + * @author Abe White + */ +public class TableSwitchInstruction extends JumpInstruction { + // case info + private int _low = 0; + private int _high = 0; + private List _cases = new LinkedList(); + + TableSwitchInstruction(Code owner) { + super(owner, Constants.TABLESWITCH); + } + + /** + * Returns the current byte offsets for the different + * switch cases in this Instruction. + */ + public int[] getOffsets() { + int bi = getByteIndex(); + int[] offsets = new int[_cases.size()]; + + for (int i = 0; i < _cases.size(); i++) + offsets[i] = ((InstructionPtrStrategy) _cases.get(i)).getByteIndex() - + bi; + + return offsets; + } + + /** + * Sets the offsets for the instructions representing the different + * switch statement cases. WARNING: these offsets will not be changed + * in the event that the code is modified following this call. It is + * typically a good idea to follow this call with a call to updateTargets + * as soon as the instructions at the given offsets are valid, at which + * point the Instructions themselves will be used as the targets and the + * offsets will be updated as expected. + */ + public void setOffsets(int[] offsets) { + int bi = getByteIndex(); + _cases.clear(); + + for (int i = 0; i < offsets.length; i++) { + InstructionPtrStrategy next = new InstructionPtrStrategy(this); + next.setByteIndex(offsets[i] + bi); + _cases.add(next); + } + } + + int getLength() { + // don't call super + int length = 1; + + // make the first byte of the 'default' a multiple of 4 from the + // start of the method + int byteIndex = getByteIndex() + 1; + + for (; (byteIndex % 4) != 0; byteIndex++, length++) + ; + + // default, low, high + length += 12; + + // offsets + length += (4 * _cases.size()); + + return length; + } + + /** + * Synonymous with {@link #getTarget}. + */ + public Instruction getDefaultTarget() { + return getTarget(); + } + + /** + * Synonymous with {@link #setTarget}. + */ + public TableSwitchInstruction setDefaultTarget(Instruction ins) { + return (TableSwitchInstruction) setTarget(ins); + } + + /** + * Synonymous with {@link #getOffset}. + */ + public int getDefaultOffset() { + return getOffset(); + } + + /** + * Synonymous with {@link #setOffset}. + */ + public TableSwitchInstruction setDefaultOffset(int offset) { + setOffset(offset); + + return this; + } + + public int getLow() { + return _low; + } + + public TableSwitchInstruction setLow(int low) { + _low = low; + + return this; + } + + public int getHigh() { + return _high; + } + + public TableSwitchInstruction setHigh(int high) { + _high = high; + + return this; + } + + /** + * Return the targets for this switch, or empty array if not set. + */ + public Instruction[] getTargets() { + Instruction[] result = new Instruction[_cases.size()]; + + for (int i = 0; i < _cases.size(); i++) + result[i] = ((InstructionPtrStrategy) _cases.get(i)).getTargetInstruction(); + + return result; + } + + /** + * Set the jump points for this switch. + * + * @return this instruction, for method chaining + */ + public TableSwitchInstruction setTargets(Instruction[] targets) { + _cases.clear(); + + if (targets != null) { + for (int i = 0; i < targets.length; i++) + addTarget(targets[i]); + } + + return this; + } + + /** + * Add a target to this switch. + * + * @return this instruction, for method chaining + */ + public TableSwitchInstruction addTarget(Instruction target) { + _cases.add(new InstructionPtrStrategy(this, target)); + + return this; + } + + public int getStackChange() { + return -1; + } + + private Instruction findTarget(int jumpByteIndex, List inss) { + Instruction ins; + + for (Iterator itr = inss.iterator(); itr.hasNext();) { + ins = (Instruction) itr.next(); + + if (ins.getByteIndex() == jumpByteIndex) { + return ins; + } + } + + return null; + } + + public void updateTargets() { + super.updateTargets(); + + for (Iterator itr = _cases.iterator(); itr.hasNext();) + ((InstructionPtrStrategy) itr.next()).updateTargets(); + } + + public void replaceTarget(Instruction oldTarget, Instruction newTarget) { + super.replaceTarget(oldTarget, newTarget); + + for (Iterator itr = _cases.iterator(); itr.hasNext();) + ((InstructionPtrStrategy) itr.next()).replaceTarget(oldTarget, + newTarget); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterTableSwitchInstruction(this); + visit.exitTableSwitchInstruction(this); + } + + void read(Instruction orig) { + super.read(orig); + + TableSwitchInstruction ins = (TableSwitchInstruction) orig; + setLow(ins.getLow()); + setHigh(ins.getHigh()); + + for (Iterator itr = ins._cases.iterator(); itr.hasNext();) { + InstructionPtrStrategy incoming = (InstructionPtrStrategy) itr.next(); + InstructionPtrStrategy next = new InstructionPtrStrategy(this); + next.setByteIndex(incoming.getByteIndex()); + _cases.add(next); + } + } + + void read(DataInput in) throws IOException { + // don't call super + int bi = getByteIndex(); + + for (int byteIndex = bi + 1; (byteIndex % 4) != 0; byteIndex++) + in.readByte(); + + setOffset(in.readInt()); + setLow(in.readInt()); + setHigh(in.readInt()); + + _cases.clear(); + + for (int i = 0; i < (_high - _low + 1); i++) { + InstructionPtrStrategy next = new InstructionPtrStrategy(this); + next.setByteIndex(bi + in.readInt()); + _cases.add(next); + } + } + + void write(DataOutput out) throws IOException { + // don't call super + int bi = getByteIndex(); + + for (int byteIndex = bi + 1; (byteIndex % 4) != 0; byteIndex++) + out.writeByte(0); + + out.writeInt(getOffset()); + out.writeInt(getLow()); + out.writeInt(getHigh()); + + for (Iterator itr = _cases.iterator(); itr.hasNext();) + out.writeInt(((InstructionPtrStrategy) itr.next()).getByteIndex() - + bi); + } +} diff --git a/serp/src/main/java/serp/bytecode/TypedInstruction.java b/serp/src/main/java/serp/bytecode/TypedInstruction.java new file mode 100755 index 000000000..3bba8ac60 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/TypedInstruction.java @@ -0,0 +1,157 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.lowlevel.*; + +import serp.util.*; + +import java.util.*; + + +/** + *

Any typed instruction.

+ * + * @author Abe White + */ +public abstract class TypedInstruction extends Instruction { + private static final Set _opcodeTypes = new HashSet(); + + static { + _opcodeTypes.add(int.class.getName()); + _opcodeTypes.add(long.class.getName()); + _opcodeTypes.add(float.class.getName()); + _opcodeTypes.add(double.class.getName()); + _opcodeTypes.add(Object.class.getName()); + _opcodeTypes.add(byte.class.getName()); + _opcodeTypes.add(char.class.getName()); + _opcodeTypes.add(short.class.getName()); + _opcodeTypes.add(boolean.class.getName()); + _opcodeTypes.add(void.class.getName()); + } + + TypedInstruction(Code owner) { + super(owner); + } + + TypedInstruction(Code owner, int opcode) { + super(owner, opcode); + } + + /** + * Return the type for the given name. Takes into account + * the given mappings and the demote flag. + * + * @param mappings mappings of one type to another; for example, + * array instruction treat booleans as ints, so + * to reflect that there should be an index x of the + * array such that mappings[x][0] = boolean.class and + * mappings[x][1] = int.class; may be null if + * no special mappings are needed + * @param demote if true, all object types will be demoted to + * Object.class + */ + String mapType(String type, Class[][] mappings, boolean demote) { + if (type == null) { + return null; + } + + type = getProject().getNameCache().getExternalForm(type, false); + + if (!_opcodeTypes.contains(type) && demote) { + type = Object.class.getName(); + } + + if (mappings != null) { + for (int i = 0; i < mappings.length; i++) + if (mappings[i][0].getName().equals(type)) { + type = mappings[i][1].getName(); + } + } + + return type; + } + + /** + * Return the type name for this instruction. + * If the type has not been set, this method will return null. + */ + public abstract String getTypeName(); + + /** + * Return the type for this instruction. + * If the type has not been set, this method will return null. + */ + public Class getType() { + String type = getTypeName(); + + if (type == null) { + return null; + } + + return Strings.toClass(type, getClassLoader()); + } + + /** + * Return the type for this instruction. + * If the type has not been set, this method will return null. + */ + public BCClass getTypeBC() { + String type = getTypeName(); + + if (type == null) { + return null; + } + + return getProject().loadClass(type, getClassLoader()); + } + + /** + * Set the type of this instruction. Types that have no direct + * support will be converted accordingly. + * + * @return this instruction, for method chaining + */ + public abstract TypedInstruction setType(String type); + + /** + * Set the type of this instruction. Types that have no direct + * support will be converted accordingly. + * + * @return this instruction, for method chaining + */ + public TypedInstruction setType(Class type) { + if (type == null) { + return setType((String) null); + } + + return setType(type.getName()); + } + + /** + * Set the type of this instruction. Types that have no direct + * support will be converted accordingly. + * + * @return this instruction, for method chaining + */ + public TypedInstruction setType(BCClass type) { + if (type == null) { + return setType((String) null); + } + + return setType(type.getName()); + } +} diff --git a/serp/src/main/java/serp/bytecode/UnknownAttribute.java b/serp/src/main/java/serp/bytecode/UnknownAttribute.java new file mode 100755 index 000000000..8f5513499 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/UnknownAttribute.java @@ -0,0 +1,75 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + +import java.io.*; + + +/** + *

An unrecognized attribute; class files are allowed to contain + * attributes that are not recognized, and the JVM must ignore them.

+ * + * @author Abe White + */ +public class UnknownAttribute extends Attribute { + private byte[] _value = new byte[0]; + + UnknownAttribute(int nameIndex, Attributes owner) { + super(nameIndex, owner); + } + + int getLength() { + return _value.length; + } + + /** + * The value is of unknown content, so it is stored as a byte array. + */ + public byte[] getValue() { + return _value; + } + + /** + * The value is of unknown content, so it is stored as a byte array. + */ + public void setValue(byte[] value) { + if (value == null) { + value = new byte[0]; + } + + _value = value; + } + + public void acceptVisit(BCVisitor visit) { + visit.enterUnknownAttribute(this); + visit.exitUnknownAttribute(this); + } + + void read(Attribute other) { + setValue(((UnknownAttribute) other).getValue()); + } + + void read(DataInput in, int length) throws IOException { + _value = new byte[length]; + in.readFully(_value); + } + + void write(DataOutput out, int length) throws IOException { + out.write(_value); + } +} diff --git a/serp/src/main/java/serp/bytecode/WideInstruction.java b/serp/src/main/java/serp/bytecode/WideInstruction.java new file mode 100755 index 000000000..9c467e333 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/WideInstruction.java @@ -0,0 +1,427 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import serp.bytecode.visitor.*; + +import java.io.*; + + +/** + *

The wide instruction, which is used to allow other + * instructions to index values beyond what they can normally index baed + * on the length of their arguments.

+ * + * @author Abe White + */ +public class WideInstruction extends LocalVariableInstruction { + private static final Class[][] _mappings = new Class[][] { + { byte.class, int.class }, + { boolean.class, int.class }, + { char.class, int.class }, + { short.class, int.class }, + { void.class, int.class }, + }; + private int _ins = Constants.NOP; + private int _inc = -1; + + WideInstruction(Code owner) { + super(owner, Constants.WIDE); + } + + int getLength() { + // opcode, ins, index + int length = super.getLength() + 1 + 2; + + // increment + if (getInstruction() == Constants.IINC) { + length += 2; + } + + return length; + } + + public int getStackChange() { + switch (getInstruction()) { + case Constants.ILOAD: + case Constants.FLOAD: + case Constants.ALOAD: + return 1; + + case Constants.LLOAD: + case Constants.DLOAD: + return 2; + + case Constants.ISTORE: + case Constants.FSTORE: + case Constants.ASTORE: + return -1; + + case Constants.LSTORE: + case Constants.DSTORE: + return -2; + + default: + return 0; + } + } + + public int getLogicalStackChange() { + switch (getInstruction()) { + case Constants.ILOAD: + case Constants.FLOAD: + case Constants.ALOAD: + case Constants.LLOAD: + case Constants.DLOAD: + return 1; + + case Constants.ISTORE: + case Constants.FSTORE: + case Constants.ASTORE: + case Constants.LSTORE: + case Constants.DSTORE: + return -1; + + default: + return 0; + } + } + + public String getTypeName() { + switch (getInstruction()) { + case Constants.ILOAD: + case Constants.ISTORE: + return int.class.getName(); + + case Constants.LLOAD: + case Constants.LSTORE: + return long.class.getName(); + + case Constants.FLOAD: + case Constants.FSTORE: + return float.class.getName(); + + case Constants.DLOAD: + case Constants.DSTORE: + return double.class.getName(); + + case Constants.ALOAD: + case Constants.ASTORE: + return Object.class.getName(); + + default: + return null; + } + } + + public TypedInstruction setType(String type) { + type = mapType(type, _mappings, true); + + switch (getInstruction()) { + case Constants.ILOAD: + case Constants.LLOAD: + case Constants.FLOAD: + case Constants.DLOAD: + case Constants.ALOAD: + + if (type == null) { + throw new IllegalStateException(); + } + + switch (type.charAt(0)) { + case 'i': + return (TypedInstruction) setInstruction(Constants.ILOAD); + + case 'l': + return (TypedInstruction) setInstruction(Constants.LLOAD); + + case 'f': + return (TypedInstruction) setInstruction(Constants.FLOAD); + + case 'd': + return (TypedInstruction) setInstruction(Constants.DLOAD); + + default: + return (TypedInstruction) setInstruction(Constants.ALOAD); + } + + case Constants.ISTORE: + case Constants.LSTORE: + case Constants.FSTORE: + case Constants.DSTORE: + case Constants.ASTORE: + + if (type == null) { + throw new IllegalStateException(); + } + + switch (type.charAt(0)) { + case 'i': + return (TypedInstruction) setInstruction(Constants.ISTORE); + + case 'l': + return (TypedInstruction) setInstruction(Constants.LSTORE); + + case 'f': + return (TypedInstruction) setInstruction(Constants.FSTORE); + + case 'd': + return (TypedInstruction) setInstruction(Constants.DSTORE); + + default: + return (TypedInstruction) setInstruction(Constants.ASTORE); + } + + default: + + if (type != null) { + throw new IllegalStateException("Augmented instruction not " + + "typed"); + } + + return this; + } + } + + /** + * Return the opcode of the instruction to modify; this will return one + * of the constants defined in {@link Constants}. + */ + public int getInstruction() { + return _ins; + } + + /** + * Set the type of instruction this wide instruction modifies. + */ + public WideInstruction setInstruction(Instruction ins) { + if (ins == null) { + return setInstruction(Constants.NOP); + } + + setInstruction(ins.getOpcode()); + + if (ins instanceof IIncInstruction) { + setIncrement(((IIncInstruction) ins).getIncrement()); + } + + return this; + } + + /** + * Set the type of instruction this wide instruction modifies. + */ + public WideInstruction setInstruction(int opcode) { + _ins = opcode; + + return this; + } + + /** + * Set the type of instruction this wide instruction modifies. + * + * @return this instruction, for method chaining + */ + public WideInstruction iinc() { + return setInstruction(Constants.IINC); + } + + /** + * Set the type of instruction this wide instruction modifies. + * + * @return this instruction, for method chaining + */ + public WideInstruction ret() { + return setInstruction(Constants.RET); + } + + /** + * Set the type of instruction this wide instruction modifies. + * + * @return this instruction, for method chaining + */ + public WideInstruction iload() { + return setInstruction(Constants.ILOAD); + } + + /** + * Set the type of instruction this wide instruction modifies. + * + * @return this instruction, for method chaining + */ + public WideInstruction fload() { + return setInstruction(Constants.FLOAD); + } + + /** + * Set the type of instruction this wide instruction modifies. + * + * @return this instruction, for method chaining + */ + public WideInstruction aload() { + return setInstruction(Constants.ALOAD); + } + + /** + * Set the type of instruction this wide instruction modifies. + * + * @return this instruction, for method chaining + */ + public WideInstruction lload() { + return setInstruction(Constants.LLOAD); + } + + /** + * Set the type of instruction this wide instruction modifies. + * + * @return this instruction, for method chaining + */ + public WideInstruction dload() { + return setInstruction(Constants.DLOAD); + } + + /** + * Set the type of instruction this wide instruction modifies. + * + * @return this instruction, for method chaining + */ + public WideInstruction istore() { + return setInstruction(Constants.ISTORE); + } + + /** + * Set the type of instruction this wide instruction modifies. + * + * @return this instruction, for method chaining + */ + public WideInstruction fstore() { + return setInstruction(Constants.FSTORE); + } + + /** + * Set the type of instruction this wide instruction modifies. + * + * @return this instruction, for method chaining + */ + public WideInstruction astore() { + return setInstruction(Constants.ASTORE); + } + + /** + * Set the type of instruction this wide instruction modifies. + * + * @return this instruction, for method chaining + */ + public WideInstruction lstore() { + return setInstruction(Constants.LSTORE); + } + + /** + * Set the type of instruction this wide instruction modifies. + * + * @return this instruction, for method chaining + */ + public WideInstruction dstore() { + return setInstruction(Constants.DSTORE); + } + + /** + * Return the increment for this instruction if it augments IINC, or -1 + * if unset. + */ + public int getIncrement() { + return _inc; + } + + /** + * Set the increment on this instruction if it augments IINC. + * + * @return this Instruction, for method chaining + */ + public WideInstruction setIncrement(int val) { + _inc = val; + + return this; + } + + /** + * WideInstructions are equal if the instruction they augment is the same + * or unset. + */ + public boolean equalsInstruction(Instruction other) { + if (other == this) { + return true; + } + + if (!super.equalsInstruction(other)) { + return false; + } + + if (!(other instanceof WideInstruction)) { + return false; + } + + WideInstruction ins = (WideInstruction) other; + + int code = getInstruction(); + int otherCode = ins.getInstruction(); + + if (code != otherCode) { + return false; + } + + if (code == Constants.IINC) { + int inc = getIncrement(); + int otherInc = ins.getIncrement(); + + return (inc == -1) || (otherInc == -1) || (inc == otherInc); + } + + return true; + } + + public void acceptVisit(BCVisitor visit) { + visit.enterWideInstruction(this); + visit.exitWideInstruction(this); + } + + void read(Instruction orig) { + super.read(orig); + setInstruction(((WideInstruction) orig).getInstruction()); + } + + void read(DataInput in) throws IOException { + super.read(in); + + setInstruction(in.readUnsignedByte()); + setLocal(in.readUnsignedShort()); + + if (getInstruction() == Constants.IINC) { + setIncrement(in.readUnsignedShort()); + } + } + + void write(DataOutput out) throws IOException { + super.write(out); + + out.writeByte(getInstruction()); + out.writeShort(getLocal()); + + if (getInstruction() == Constants.IINC) { + out.writeShort(getIncrement()); + } + } +} diff --git a/serp/src/main/java/serp/bytecode/lowlevel/ClassEntry.java b/serp/src/main/java/serp/bytecode/lowlevel/ClassEntry.java new file mode 100755 index 000000000..d32da4f75 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/lowlevel/ClassEntry.java @@ -0,0 +1,101 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode.lowlevel; + +import serp.bytecode.visitor.*; + +import java.io.*; + + +/** + *

A constant pool entry describing a class. + * Class entries are used to refer to the current class, the superclass, + * implemented interfaces, etc. Each class entry contains the constant pool + * index of the {@link UTF8Entry} that stores the class name, which is + * represented in internal form.

+ * + * @author Abe White + */ +public class ClassEntry extends Entry implements ConstantEntry { + private int _nameIndex = 0; + + /** + * Default constructor. + */ + public ClassEntry() { + } + + /** + * Constructor. + * + * @param nameIndex the constant pool index of the {@link UTF8Entry} + * containing the class name + */ + public ClassEntry(int nameIndex) { + _nameIndex = nameIndex; + } + + /** + * Return the constant pool index of the {@link UTF8Entry} + * containing the class name. Defaults to 0. + */ + public int getNameIndex() { + return _nameIndex; + } + + /** + * Set the constant pool index of the {@link UTF8Entry} + * containing the class name. + */ + public void setNameIndex(int nameIndex) { + Object key = beforeModify(); + _nameIndex = nameIndex; + afterModify(key); + } + + /** + * Return the referenced {@link UTF8Entry}. This method can only + * be run for entries that have been added to a constant pool. + */ + public UTF8Entry getNameEntry() { + return (UTF8Entry) getPool().getEntry(_nameIndex); + } + + public int getType() { + return Entry.CLASS; + } + + public Object getConstant() { + return getNameEntry().getValue(); + } + + public void setConstant(Object value) { + getNameEntry().setConstant(value); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterClassEntry(this); + visit.exitClassEntry(this); + } + + void readData(DataInput in) throws IOException { + _nameIndex = in.readUnsignedShort(); + } + + void writeData(DataOutput out) throws IOException { + out.writeShort(_nameIndex); + } +} diff --git a/serp/src/main/java/serp/bytecode/lowlevel/ComplexEntry.java b/serp/src/main/java/serp/bytecode/lowlevel/ComplexEntry.java new file mode 100755 index 000000000..dec9db192 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/lowlevel/ComplexEntry.java @@ -0,0 +1,115 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode.lowlevel; + +import java.io.*; + + +/** + *

Base class for field, method, and interface method constant pool + * entries. All complex entries reference the {@link ClassEntry} of the + * class that owns the entity and a {@link NameAndTypeEntry} describing + * the entity.

+ * + * @author Abe White + */ +public abstract class ComplexEntry extends Entry { + private int _classIndex = 0; + private int _nameAndTypeIndex = 0; + + /** + * Default constructor. + */ + public ComplexEntry() { + } + + /** + * Constructor. + * + * @param classIndex the constant pool index of the + * {@link ClassEntry} describing the owner + * of this entity + * @param nameAndTypeIndex the constant pool index of the + * {@link NameAndTypeEntry} describing this + * entity + */ + public ComplexEntry(int classIndex, int nameAndTypeIndex) { + _classIndex = classIndex; + _nameAndTypeIndex = nameAndTypeIndex; + } + + /** + * Return the constant pool index of the {@link ClassEntry} describing + * the owning class of this entity. Defaults to 0. + */ + public int getClassIndex() { + return _classIndex; + } + + /** + * Set the constant pool index of the {@link ClassEntry} describing + * the owning class of this entity. + */ + public void setClassIndex(int classIndex) { + Object key = beforeModify(); + _classIndex = classIndex; + afterModify(key); + } + + /** + * Return the referenced {@link ClassEntry}. This method can only + * be run for entries that have been added to a constant pool. + */ + public ClassEntry getClassEntry() { + return (ClassEntry) getPool().getEntry(_classIndex); + } + + /** + * Return the constant pool index of the {@link NameAndTypeEntry} + * describing this entity. + */ + public int getNameAndTypeIndex() { + return _nameAndTypeIndex; + } + + /** + * Set the constant pool index of the {@link NameAndTypeEntry} + * describing this entity. + */ + public void setNameAndTypeIndex(int nameAndTypeIndex) { + Object key = beforeModify(); + _nameAndTypeIndex = nameAndTypeIndex; + afterModify(key); + } + + /** + * Return the referenced {@link NameAndTypeEntry}. This method can only + * be run for entries that have been added to a constant pool. + */ + public NameAndTypeEntry getNameAndTypeEntry() { + return (NameAndTypeEntry) getPool().getEntry(_nameAndTypeIndex); + } + + void readData(DataInput in) throws IOException { + _classIndex = in.readUnsignedShort(); + _nameAndTypeIndex = in.readUnsignedShort(); + } + + void writeData(DataOutput out) throws IOException { + out.writeShort(_classIndex); + out.writeShort(_nameAndTypeIndex); + } +} diff --git a/serp/src/main/java/serp/bytecode/lowlevel/ConstantEntry.java b/serp/src/main/java/serp/bytecode/lowlevel/ConstantEntry.java new file mode 100755 index 000000000..9172e739e --- /dev/null +++ b/serp/src/main/java/serp/bytecode/lowlevel/ConstantEntry.java @@ -0,0 +1,35 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode.lowlevel; + + +/** + *

Interface implemented by entries representing constant values. Allows + * generic access the constant value regardless of type.

+ * + * @author Abe White + */ +public interface ConstantEntry { + /** + * Return the value of the constant held by this entry.

+ */ + public Object getConstant(); + + /** + * Set the value of the constant held by this entry.

+ */ + public void setConstant(Object value); +} diff --git a/serp/src/main/java/serp/bytecode/lowlevel/ConstantPool.java b/serp/src/main/java/serp/bytecode/lowlevel/ConstantPool.java new file mode 100755 index 000000000..bd3efd254 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/lowlevel/ConstantPool.java @@ -0,0 +1,725 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode.lowlevel; + +import serp.bytecode.visitor.*; + +import serp.util.*; + +import java.io.*; + +import java.util.*; + + +/** + *

A bytecode constant pool, containing entries for all strings, + * constants, classes, etc referenced in the class structure and method + * opcodes. In keeping with the low-level bytecode representation, all pool + * indexes are 1-based and {@link LongEntry}s and {@link DoubleEntry}s each + * occupy two indexes in the pool.

+ * + * @author Abe White + */ +public class ConstantPool implements VisitAcceptor { + private List _entries = new ArrayList(50); + private Map _lookup = new HashMap(50); + + /** + * Default constructor. + */ + public ConstantPool() { + } + + /** + * Return all the entries in the pool. + */ + public Entry[] getEntries() { + List entries = new ArrayList(_entries.size()); + Entry entry; + + for (Iterator itr = _entries.iterator(); itr.hasNext();) { + entry = (Entry) itr.next(); + + if (entry != null) { + entries.add(entry); + } + } + + return (Entry[]) entries.toArray(new Entry[entries.size()]); + } + + /** + * Retrieve the entry at the specified 1-based index. + * + * @throws IndexOutOfBoundsException if index is invalid, + * including the case that it points to the second slot of a + * long or double entry + */ + public Entry getEntry(int index) { + Entry entry = (Entry) _entries.get(index - 1); + + if (entry == null) { + throw new IndexOutOfBoundsException("index = " + index); + } + + return entry; + } + + /** + * Return the index of the given entry, or 0 if it is not in the pool. + */ + public int indexOf(Entry entry) { + if ((entry == null) || (entry.getPool() != this)) { + return 0; + } + + return entry.getIndex(); + } + + /** + * Add an entry to the pool. + * + * @return the index at which the entry was added + */ + public int addEntry(Entry entry) { + if (entry.getPool() != this) { + addEntry(getKey(entry), entry); + } + + return entry.getIndex(); + } + + /** + * Add an entry to the pool using the given key. + */ + private int addEntry(Object key, Entry entry) { + entry.setPool(this); + _entries.add(entry); + entry.setIndex(_entries.size()); + + _lookup.put(key, entry); + + if (entry.isWide()) { + _entries.add(null); + } + + return entry.getIndex(); + } + + /** + * Remove the given entry from the pool. + * + * @return false if the entry is not in the pool, true otherwise + */ + public boolean removeEntry(Entry entry) { + if ((entry == null) || (entry.getPool() != this)) { + return false; + } + + int index = entry.getIndex() - 1; + entry.setPool(null); + entry.setIndex(0); + + _entries.remove(index); + + if (entry.isWide()) { + _entries.remove(index); + } + + _lookup.remove(getKey(entry)); + + // rehash all the entries after the removed one with their new index + Object key; + + for (int i = index; i < _entries.size(); i++) { + entry = (Entry) _entries.get(i); + + if (entry != null) { + key = getKey(entry); + _lookup.remove(key); + entry.setIndex(i + 1); + _lookup.put(key, entry); + } + } + + return true; + } + + /** + * Clear all entries from the pool. + */ + public void clear() { + Entry entry; + + for (Iterator itr = _entries.iterator(); itr.hasNext();) { + entry = (Entry) itr.next(); + + if (entry != null) { + entry.setPool(null); + entry.setIndex(0); + } + } + + _entries.clear(); + _lookup.clear(); + } + + /** + * Return the number of places occupied in the pool, including the fact + * that long and double entries occupy two places. + */ + public int size() { + return _entries.size(); + } + + /** + * Return the index of the {@link UTF8Entry} with the given value, or + * 0 if it does not exist. + * + * @param add if true, the entry will be added if it does not + * already exist, and the new entry's index returned + */ + public int findUTF8Entry(String value, boolean add) { + if (value == null) { + if (add) { + throw new NullPointerException("value = null"); + } + + return 0; + } + + int index = find(value); + + if (!add || (index > 0)) { + return index; + } + + return addEntry(value, new UTF8Entry(value)); + } + + /** + * Return the constant pool index of the {@link DoubleEntry} for the given + * value, or 0 if it does not exist. + * + * @param value the value to find + * @param add if true, the entry will be added if it does not + * already exist, and the new entry's index returned + */ + public int findDoubleEntry(double value, boolean add) { + Double key = new Double(value); + int index = find(key); + + if (!add || (index > 0)) { + return index; + } + + return addEntry(key, new DoubleEntry(value)); + } + + /** + * Return the constant pool index of the {@link FloatEntry} for the given + * value, or 0 if it does not exist. + * + * @param value the value to find + * @param add if true, the entry will be added if it does not + * already exist, and the new entry's index returned + */ + public int findFloatEntry(float value, boolean add) { + Float key = new Float(value); + int index = find(key); + + if (!add || (index > 0)) { + return index; + } + + return addEntry(key, new FloatEntry(value)); + } + + /** + * Return the constant pool index of the {@link IntEntry} for the given + * value, or 0 if it does not exist. + * + * @param value the value to find + * @param add if true, the entry will be added if it does not + * already exist, and the new entry's index returned + */ + public int findIntEntry(int value, boolean add) { + Integer key = Numbers.valueOf(value); + int index = find(key); + + if (!add || (index > 0)) { + return index; + } + + return addEntry(key, new IntEntry(value)); + } + + /** + * Return the constant pool index of the {@link LongEntry} for the given + * value, or 0 if it does not exist. + * + * @param value the value to find + * @param add if true, the entry will be added if it does not + * already exist, and the new entry's index returned + */ + public int findLongEntry(long value, boolean add) { + Long key = Numbers.valueOf(value); + int index = find(key); + + if (!add || (index > 0)) { + return index; + } + + return addEntry(key, new LongEntry(value)); + } + + /** + * Return the constant pool index of the {@link StringEntry} for the given + * string value, or 0 if it does not exist. + * + * @param value the value to find + * @param add if true, the entry will be added if it does not + * already exist, and the new entry's index returned + */ + public int findStringEntry(String value, boolean add) { + int valueIndex = findUTF8Entry(value, add); + + if (valueIndex == 0) { + return 0; + } + + StringKey key = new StringKey(valueIndex); + int index = find(key); + + if (!add || (index > 0)) { + return index; + } + + return addEntry(key, new StringEntry(valueIndex)); + } + + /** + * Return the constant pool index of the {@link ClassEntry} for the given + * class name, or 0 if it does not exist. + * + * @param name the class name in internal form + * @param add if true, the entry will be added if it does not + * already exist, and the new entry's index returned + */ + public int findClassEntry(String name, boolean add) { + int nameIndex = findUTF8Entry(name, add); + + if (nameIndex == 0) { + return 0; + } + + ClassKey key = new ClassKey(nameIndex); + int index = find(key); + + if (!add || (index > 0)) { + return index; + } + + return addEntry(key, new ClassEntry(nameIndex)); + } + + /** + * Return the constant pool index of the {@link NameAndTypeEntry} for the + * given name and descriptor, or 0 if it does not exist. + * + * @param name the name of the entity + * @param desc the descriptor of the entity in internal form + * @param add if true, the entry will be added if it does not + * already exist, and the new entry's index returned + */ + public int findNameAndTypeEntry(String name, String desc, boolean add) { + int nameIndex = findUTF8Entry(name, add); + + if (nameIndex == 0) { + return 0; + } + + int descIndex = findUTF8Entry(desc, add); + + if (descIndex == 0) { + return 0; + } + + NameAndTypeKey key = new NameAndTypeKey(nameIndex, descIndex); + int index = find(key); + + if (!add || (index > 0)) { + return index; + } + + return addEntry(key, new NameAndTypeEntry(nameIndex, descIndex)); + } + + /** + * Return the constant pool index of the {@link FieldEntry} for the + * given name, descriptor, and owner class name. + * + * @param owner the name of the field's owning class in internal form + * @param name the name of the field + * @param desc the descriptor of the field in internal form + * @param add if true, the entry will be added if it does not + * already exist, and the new entry's index returned + */ + public int findFieldEntry(String owner, String name, String desc, + boolean add) { + return findComplexEntry(owner, name, desc, Entry.FIELD, add); + } + + /** + * Return the constant pool index of the {@link MethodEntry} for the + * given name, descriptor, and owner class name. + * + * @param owner the name of the method's owning class in internal form + * @param name the name of the method + * @param desc the descriptor of the method in internal form + * @param add if true, the entry will be added if it does not + * already exist, and the new entry's index returned + */ + public int findMethodEntry(String owner, String name, String desc, + boolean add) { + return findComplexEntry(owner, name, desc, Entry.METHOD, add); + } + + /** + * Return the constant pool index of the {@link InterfaceMethodEntry} for + * the given name, descriptor, and owner class name. + * + * @param owner the name of the method's owning class in internal form + * @param name the name of the method + * @param desc the descriptor of the method in internal form + * @param add if true, the entry will be added if it does not + * already exist, and the new entry's index returned + */ + public int findInterfaceMethodEntry(String owner, String name, String desc, + boolean add) { + return findComplexEntry(owner, name, desc, Entry.INTERFACEMETHOD, add); + } + + /** + * Return the constant pool index of the {@link ComplexEntry} for the + * given name, descriptor, and owner class name. + * + * @param owner the name of the owning class in internal form + * @param name the name of the entity + * @param desc the descriptor of the entity in internal form + * @param type the type of entry: field, method, interface method + * @param add if true, the entry will be added if it does not + * already exist, and the new entry's index returned + */ + private int findComplexEntry(String owner, String name, String desc, + int type, boolean add) { + int classIndex = findClassEntry(owner, add); + + if (classIndex == 0) { + return 0; + } + + int descIndex = findNameAndTypeEntry(name, desc, add); + + if (descIndex == 0) { + return 0; + } + + Object key = null; + + switch (type) { + case Entry.FIELD: + key = new FieldKey(classIndex, descIndex); + + break; + + case Entry.METHOD: + key = new MethodKey(classIndex, descIndex); + + break; + + case Entry.INTERFACEMETHOD: + key = new InterfaceMethodKey(classIndex, descIndex); + + break; + } + + int index = find(key); + + if (!add || (index > 0)) { + return index; + } + + Entry entry = null; + + switch (type) { + case Entry.FIELD: + entry = new FieldEntry(classIndex, descIndex); + + break; + + case Entry.METHOD: + entry = new MethodEntry(classIndex, descIndex); + + break; + + case Entry.INTERFACEMETHOD: + entry = new InterfaceMethodEntry(classIndex, descIndex); + + break; + } + + return addEntry(key, entry); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterConstantPool(this); + + Entry entry; + + for (Iterator itr = _entries.iterator(); itr.hasNext();) { + entry = (Entry) itr.next(); + + if (entry == null) { + continue; + } + + visit.enterEntry(entry); + entry.acceptVisit(visit); + visit.exitEntry(entry); + } + + visit.exitConstantPool(this); + } + + /** + * Fill the constant pool from the given bytecode stream. + */ + public void read(DataInput in) throws IOException { + clear(); + + int entryCount = in.readUnsignedShort(); + Entry entry; + + for (int i = 1; i < entryCount; i++) { + entry = Entry.read(in); + addEntry(entry); + + if (entry.isWide()) { + i++; + } + } + } + + /** + * Write the constant pool to the given bytecode stream. + */ + public void write(DataOutput out) throws IOException { + out.writeShort(_entries.size() + 1); + + Entry entry; + + for (Iterator itr = _entries.iterator(); itr.hasNext();) { + entry = (Entry) itr.next(); + + if (entry != null) { + Entry.write(entry, out); + } + } + } + + /** + * Called by constant pool entries when they are mutated. + */ + void modifyEntry(Object origKey, Entry entry) { + _lookup.remove(origKey); + _lookup.put(getKey(entry), entry); + } + + /** + * Returns the constant pool index of the entry with the given key. + */ + private int find(Object key) { + Entry entry = (Entry) _lookup.get(key); + + if (entry == null) { + return 0; + } + + return entry.getIndex(); + } + + /** + * Return the hash key used for the specified entry. + */ + static Object getKey(Entry entry) { + switch (entry.getType()) { + case Entry.CLASS: + return new ClassKey(((ClassEntry) entry).getNameIndex()); + + case Entry.FIELD: + + FieldEntry fe = (FieldEntry) entry; + + return new FieldKey(fe.getClassIndex(), fe.getNameAndTypeIndex()); + + case Entry.METHOD: + + MethodEntry me = (MethodEntry) entry; + + return new MethodKey(me.getClassIndex(), me.getNameAndTypeIndex()); + + case Entry.INTERFACEMETHOD: + + InterfaceMethodEntry ime = (InterfaceMethodEntry) entry; + + return new InterfaceMethodKey(ime.getClassIndex(), + ime.getNameAndTypeIndex()); + + case Entry.STRING: + return new StringKey(((StringEntry) entry).getStringIndex()); + + case Entry.INT: + case Entry.FLOAT: + case Entry.LONG: + case Entry.DOUBLE: + case Entry.UTF8: + return ((ConstantEntry) entry).getConstant(); + + case Entry.NAMEANDTYPE: + + NameAndTypeEntry nte = (NameAndTypeEntry) entry; + + return new NameAndTypeKey(nte.getNameIndex(), + nte.getDescriptorIndex()); + + default: + return null; + } + } + + /** + * Base class key for entries with one ptr to another entry. + */ + private static abstract class PtrKey { + private final int _index; + + public PtrKey(int index) { + _index = index; + } + + public int hashCode() { + return _index; + } + + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (other.getClass() != getClass()) { + return false; + } + + return ((PtrKey) other)._index == _index; + } + } + + /** + * Key for string entries. + */ + private static class StringKey extends PtrKey { + public StringKey(int index) { + super(index); + } + } + + /** + * Key for class entries. + */ + private static class ClassKey extends PtrKey { + public ClassKey(int index) { + super(index); + } + } + + /** + * Base class key for entries with two ptr to other entries. + */ + private static abstract class DoublePtrKey { + private final int _index1; + private final int _index2; + + public DoublePtrKey(int index1, int index2) { + _index1 = index1; + _index2 = index2; + } + + public int hashCode() { + return _index1 ^ _index2; + } + + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (other.getClass() != getClass()) { + return false; + } + + DoublePtrKey key = (DoublePtrKey) other; + + return (key._index1 == _index1) && (key._index2 == _index2); + } + } + + /** + * Key for name and type entries. + */ + private static class NameAndTypeKey extends DoublePtrKey { + public NameAndTypeKey(int index1, int index2) { + super(index1, index2); + } + } + + /** + * Key for field entries. + */ + private static class FieldKey extends DoublePtrKey { + public FieldKey(int index1, int index2) { + super(index1, index2); + } + } + + /** + * Key for method entries. + */ + private static class MethodKey extends DoublePtrKey { + public MethodKey(int index1, int index2) { + super(index1, index2); + } + } + + /** + * Key for interface method entries. + */ + private static class InterfaceMethodKey extends DoublePtrKey { + public InterfaceMethodKey(int index1, int index2) { + super(index1, index2); + } + } +} diff --git a/serp/src/main/java/serp/bytecode/lowlevel/ConstantPoolTable.java b/serp/src/main/java/serp/bytecode/lowlevel/ConstantPoolTable.java new file mode 100755 index 000000000..cf38888bb --- /dev/null +++ b/serp/src/main/java/serp/bytecode/lowlevel/ConstantPoolTable.java @@ -0,0 +1,207 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode.lowlevel; + +import java.io.*; + + +/** + *

Efficient representation of the constant pool as a table. This class + * can be used to parse out bits of information from bytecode without + * instantiating a full {@link serp.bytecode.BCClass}.

+ * + * @author Abe White + */ +public class ConstantPoolTable { + private byte[] _bytecode = null; + private int[] _table = null; + private int _idx = 0; + + /** + * Constructor; supply class bytecode. + */ + public ConstantPoolTable(byte[] b) { + _bytecode = b; + _table = new int[readUnsignedShort(b, 8)]; + _idx = parse(b, _table); + } + + /** + * Constructor; supply input stream to bytecode. + */ + public ConstantPoolTable(InputStream in) throws IOException { + this(toByteArray(in)); + } + + /** + * Allows static computation of the byte index after the constant + * pool without caching constant pool information. + */ + public static int getEndIndex(byte[] b) { + return parse(b, null); + } + + /** + * Parse class bytecode, returning end index of pool. + */ + private static int parse(byte[] b, int[] table) { + // each entry is the index in the byte array of the data for a const + // pool entry + int entries = (table == null) ? readUnsignedShort(b, 8) : table.length; + int idx = 10; + + for (int i = 1; i < entries; i++) { + if (table != null) { + table[i] = idx + 1; // skip entry type + } + + switch (b[idx]) { + case 1: // utf8 + idx += (3 + readUnsignedShort(b, idx + 1)); + + break; + + case 3: // integer + case 4: // float + case 9: // field + case 10: // method + case 11: // interface method + case 12: // name + idx += 5; + + break; + + case 5: // long + case 6: // double + idx += 9; + i++; // wide entry + + break; + + default: + idx += 3; + } + } + + return idx; + } + + /** + * Read a byte value at the given offset into the given bytecode. + */ + public static int readByte(byte[] b, int idx) { + return b[idx] & 0xFF; + } + + /** + * Read an unsigned short value at the given offset into the given + * bytecode. + */ + public static int readUnsignedShort(byte[] b, int idx) { + return (readByte(b, idx) << 8) | readByte(b, idx + 1); + } + + /** + * Read an int value at the given offset into the given bytecode. + */ + public static int readInt(byte[] b, int idx) { + return (readByte(b, idx) << 24) | (readByte(b, idx + 1) << 16) | + (readByte(b, idx + 2) << 8) | readByte(b, idx + 3); + } + + /** + * Read a long value at the given offset into the given bytecode. + */ + public static long readLong(byte[] b, int idx) { + return (readInt(b, idx) << 32) | readInt(b, idx + 4); + } + + /** + * Read a UTF-8 string value at the given offset into the given + * bytecode. + */ + public static String readString(byte[] b, int idx) { + int len = readUnsignedShort(b, idx); + + try { + return new String(b, idx + 2, len, "UTF-8"); + } catch (UnsupportedEncodingException uee) { + throw new ClassFormatError(uee.toString()); + } + } + + /** + * Read the contents of the given stream. + */ + private static byte[] toByteArray(InputStream in) throws IOException { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + byte[] buf = new byte[1024]; + + for (int r; (r = in.read(buf)) != -1; bout.write(buf, 0, r)) + ; + + return bout.toByteArray(); + } + + /** + * Return the index into the bytecode of the end of the constant pool. + */ + public int getEndIndex() { + return _idx; + } + + /** + * Return the given table entry. + */ + public int get(int idx) { + return _table[idx]; + } + + /** + * Read a byte value at the given offset. + */ + public int readByte(int idx) { + return readByte(_bytecode, idx); + } + + /** + * Read an unsigned short value at the given offset. + */ + public int readUnsignedShort(int idx) { + return readUnsignedShort(_bytecode, idx); + } + + /** + * Read an int value at the given offset. + */ + public int readInt(int idx) { + return readInt(_bytecode, idx); + } + + /** + * Read a long value at the given offset. + */ + public long readLong(int idx) { + return readLong(_bytecode, idx); + } + + /** + * Read a UTF-8 string value at the given offset. + */ + public String readString(int idx) { + return readString(_bytecode, idx); + } +} diff --git a/serp/src/main/java/serp/bytecode/lowlevel/DoubleEntry.java b/serp/src/main/java/serp/bytecode/lowlevel/DoubleEntry.java new file mode 100755 index 000000000..6646eca71 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/lowlevel/DoubleEntry.java @@ -0,0 +1,90 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode.lowlevel; + +import serp.bytecode.visitor.*; + +import java.io.*; + + +/** + *

A constant double value in the constant pool.

+ * + * @author Abe White + */ +public class DoubleEntry extends Entry implements ConstantEntry { + private double _value = 0.0; + + /** + * Default constructor. + */ + public DoubleEntry() { + } + + /** + * Constructor. + * + * @param value the constant double value of this entry + */ + public DoubleEntry(double value) { + _value = value; + } + + public boolean isWide() { + return true; + } + + public int getType() { + return Entry.DOUBLE; + } + + /** + * Return the value of the constant. + */ + public double getValue() { + return _value; + } + + /** + * Set the value of the constant. + */ + public void setValue(double value) { + Object key = beforeModify(); + _value = value; + afterModify(key); + } + + public Object getConstant() { + return new Double(getValue()); + } + + public void setConstant(Object value) { + setValue(((Number) value).doubleValue()); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterDoubleEntry(this); + visit.exitDoubleEntry(this); + } + + void readData(DataInput in) throws IOException { + _value = in.readDouble(); + } + + void writeData(DataOutput out) throws IOException { + out.writeDouble(_value); + } +} diff --git a/serp/src/main/java/serp/bytecode/lowlevel/Entry.java b/serp/src/main/java/serp/bytecode/lowlevel/Entry.java new file mode 100755 index 000000000..e5cd5b1ee --- /dev/null +++ b/serp/src/main/java/serp/bytecode/lowlevel/Entry.java @@ -0,0 +1,188 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode.lowlevel; + +import serp.bytecode.visitor.*; + +import java.io.*; + +import java.util.*; + + +/** + *

Base type for all constant pool entries. Entries should generally be + * considered immutable; modifying an entry directly can have dire + * consequences, and often renders the resulting class file invalid.

+ * + *

Entries cannot be shared among constant pools.

+ * + * @author Abe White + */ +public abstract class Entry implements VisitAcceptor { + public static final int UTF8 = 1; + public static final int INT = 3; + public static final int FLOAT = 4; + public static final int LONG = 5; + public static final int DOUBLE = 6; + public static final int CLASS = 7; + public static final int STRING = 8; + public static final int FIELD = 9; + public static final int METHOD = 10; + public static final int INTERFACEMETHOD = 11; + public static final int NAMEANDTYPE = 12; + private ConstantPool _pool = null; + private int _index = 0; + + /** + * Read a single entry from the given bytecode stream and returns it. + */ + public static Entry read(DataInput in) throws IOException { + Entry entry = create(in.readUnsignedByte()); + entry.readData(in); + + return entry; + } + + /** + * Write the given entry to the given bytecode stream. + */ + public static void write(Entry entry, DataOutput out) + throws IOException { + out.writeByte(entry.getType()); + entry.writeData(out); + } + + /** + * Create an entry based on its type code. + */ + public static Entry create(int type) { + switch (type) { + case CLASS: + return new ClassEntry(); + + case FIELD: + return new FieldEntry(); + + case METHOD: + return new MethodEntry(); + + case INTERFACEMETHOD: + return new InterfaceMethodEntry(); + + case STRING: + return new StringEntry(); + + case INT: + return new IntEntry(); + + case FLOAT: + return new FloatEntry(); + + case LONG: + return new LongEntry(); + + case DOUBLE: + return new DoubleEntry(); + + case NAMEANDTYPE: + return new NameAndTypeEntry(); + + case UTF8: + return new UTF8Entry(); + + default: + throw new IllegalArgumentException("type = " + type); + } + } + + /** + * Return the type code for this entry type. + */ + public abstract int getType(); + + /** + * Return true if this is a wide entry -- i.e. if it takes up two + * places in the constant pool. Returns false by default. + */ + public boolean isWide() { + return false; + } + + /** + * Returns the constant pool containing this entry, or null if none. + */ + public ConstantPool getPool() { + return _pool; + } + + /** + * Returns the index of the entry in the owning constant pool, or 0. + */ + public int getIndex() { + return _index; + } + + /** + * This method is called after reading the entry type from bytecode. + * It should read all the data for this entry from the given stream. + */ + abstract void readData(DataInput in) throws IOException; + + /** + * This method is called after writing the entry type to bytecode. + * It should write all data for this entry to the given stream. + */ + abstract void writeData(DataOutput out) throws IOException; + + /** + * Subclasses must call this method before their state is mutated. + */ + Object beforeModify() { + if (_pool == null) { + return null; + } + + return _pool.getKey(this); + } + + /** + * Subclasses must call this method when their state is mutated. + */ + void afterModify(Object key) { + if (_pool != null) { + _pool.modifyEntry(key, this); + } + } + + /** + * Sets the owning pool of the entry. + */ + void setPool(ConstantPool pool) { + // attempting to overwrite current pool? + if ((_pool != null) && (pool != null) && (_pool != pool)) { + throw new IllegalStateException("Entry already belongs to a pool"); + } + + _pool = pool; + } + + /** + * Set the index of this entry within the pool. + */ + void setIndex(int index) { + _index = index; + } +} diff --git a/serp/src/main/java/serp/bytecode/lowlevel/FieldEntry.java b/serp/src/main/java/serp/bytecode/lowlevel/FieldEntry.java new file mode 100755 index 000000000..48e6a2b76 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/lowlevel/FieldEntry.java @@ -0,0 +1,52 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode.lowlevel; + +import serp.bytecode.visitor.*; + +import java.io.*; + + +/** + *

A reference to a class field.

+ * + * @author Abe White + */ +public class FieldEntry extends ComplexEntry { + /** + * Default constructor. + */ + public FieldEntry() { + } + + /** + * Constructor. + * + * @see ComplexEntry#ComplexEntry(int,int) + */ + public FieldEntry(int classIndex, int nameAndTypeIndex) { + super(classIndex, nameAndTypeIndex); + } + + public int getType() { + return Entry.FIELD; + } + + public void acceptVisit(BCVisitor visit) { + visit.enterFieldEntry(this); + visit.exitFieldEntry(this); + } +} diff --git a/serp/src/main/java/serp/bytecode/lowlevel/FloatEntry.java b/serp/src/main/java/serp/bytecode/lowlevel/FloatEntry.java new file mode 100755 index 000000000..0907cd28b --- /dev/null +++ b/serp/src/main/java/serp/bytecode/lowlevel/FloatEntry.java @@ -0,0 +1,86 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode.lowlevel; + +import serp.bytecode.visitor.*; + +import java.io.*; + + +/** + *

A constant float value in the constant pool.

+ * + * @author Abe White + */ +public class FloatEntry extends Entry implements ConstantEntry { + private float _value = 0.0F; + + /** + * Default constructor. + */ + public FloatEntry() { + } + + /** + * Constructor. + * + * @param value the constant float value of this entry + */ + public FloatEntry(float value) { + _value = value; + } + + public int getType() { + return Entry.FLOAT; + } + + /** + * Return the value of this constant. + */ + public float getValue() { + return _value; + } + + /** + * Set the value of this constant. + */ + public void setValue(float value) { + Object key = beforeModify(); + _value = value; + afterModify(key); + } + + public Object getConstant() { + return new Float(getValue()); + } + + public void setConstant(Object value) { + setValue(((Number) value).floatValue()); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterFloatEntry(this); + visit.exitFloatEntry(this); + } + + void readData(DataInput in) throws IOException { + _value = in.readFloat(); + } + + void writeData(DataOutput out) throws IOException { + out.writeFloat(_value); + } +} diff --git a/serp/src/main/java/serp/bytecode/lowlevel/IntEntry.java b/serp/src/main/java/serp/bytecode/lowlevel/IntEntry.java new file mode 100755 index 000000000..c9d5237e1 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/lowlevel/IntEntry.java @@ -0,0 +1,88 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode.lowlevel; + +import serp.bytecode.visitor.*; + +import serp.util.*; + +import java.io.*; + + +/** + *

A constant int value in the constant pool.

+ * + * @author Abe White + */ +public class IntEntry extends Entry implements ConstantEntry { + private int _value = -1; + + /** + * Default constructor. + */ + public IntEntry() { + } + + /** + * Constructor. + * + * @param value the constant int value of this entry + */ + public IntEntry(int value) { + _value = value; + } + + public int getType() { + return Entry.INT; + } + + /** + * Return the value of this constant. + */ + public int getValue() { + return _value; + } + + /** + * Set the value of this constant. + */ + public void setValue(int value) { + Object key = beforeModify(); + _value = value; + afterModify(key); + } + + public Object getConstant() { + return Numbers.valueOf(getValue()); + } + + public void setConstant(Object value) { + setValue(((Number) value).intValue()); + } + + protected void readData(DataInput in) throws IOException { + _value = in.readInt(); + } + + protected void writeData(DataOutput out) throws IOException { + out.writeInt(_value); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterIntEntry(this); + visit.exitIntEntry(this); + } +} diff --git a/serp/src/main/java/serp/bytecode/lowlevel/InterfaceMethodEntry.java b/serp/src/main/java/serp/bytecode/lowlevel/InterfaceMethodEntry.java new file mode 100755 index 000000000..b3ea1d458 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/lowlevel/InterfaceMethodEntry.java @@ -0,0 +1,52 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode.lowlevel; + +import serp.bytecode.visitor.*; + +import java.io.*; + + +/** + *

A reference to an interface method.

+ * + * @author Abe White + */ +public class InterfaceMethodEntry extends ComplexEntry { + /** + * Default constructor. + */ + public InterfaceMethodEntry() { + } + + /** + * Constructor. + * + * @see ComplexEntry#ComplexEntry(int,int) + */ + public InterfaceMethodEntry(int classIndex, int nameAndTypeIndex) { + super(classIndex, nameAndTypeIndex); + } + + public int getType() { + return Entry.INTERFACEMETHOD; + } + + public void acceptVisit(BCVisitor visit) { + visit.enterInterfaceMethodEntry(this); + visit.exitInterfaceMethodEntry(this); + } +} diff --git a/serp/src/main/java/serp/bytecode/lowlevel/LongEntry.java b/serp/src/main/java/serp/bytecode/lowlevel/LongEntry.java new file mode 100755 index 000000000..c1cd1319d --- /dev/null +++ b/serp/src/main/java/serp/bytecode/lowlevel/LongEntry.java @@ -0,0 +1,92 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode.lowlevel; + +import serp.bytecode.visitor.*; + +import serp.util.*; + +import java.io.*; + + +/** + *

A long constant in the constant pool.

+ * + * @author Abe White + */ +public class LongEntry extends Entry implements ConstantEntry { + private long _value = 0L; + + /** + * Default constructor. + */ + public LongEntry() { + } + + /** + * Constructor. + * + * @param value the constant long value of this entry + */ + public LongEntry(long value) { + _value = value; + } + + public boolean isWide() { + return true; + } + + public int getType() { + return Entry.LONG; + } + + /** + * Return the value of the constant. + */ + public long getValue() { + return _value; + } + + /** + * Set the value of the constant. + */ + public void setValue(long value) { + Object key = beforeModify(); + _value = value; + afterModify(key); + } + + public Object getConstant() { + return Numbers.valueOf(getValue()); + } + + public void setConstant(Object value) { + setValue(((Number) value).longValue()); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterLongEntry(this); + visit.exitLongEntry(this); + } + + void readData(DataInput in) throws IOException { + _value = in.readLong(); + } + + void writeData(DataOutput out) throws IOException { + out.writeLong(_value); + } +} diff --git a/serp/src/main/java/serp/bytecode/lowlevel/MethodEntry.java b/serp/src/main/java/serp/bytecode/lowlevel/MethodEntry.java new file mode 100755 index 000000000..582813a3d --- /dev/null +++ b/serp/src/main/java/serp/bytecode/lowlevel/MethodEntry.java @@ -0,0 +1,52 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode.lowlevel; + +import serp.bytecode.visitor.*; + +import java.io.*; + + +/** + *

A reference to a class method.

+ * + * @author Abe White + */ +public class MethodEntry extends ComplexEntry { + /** + * Default constructor. + */ + public MethodEntry() { + } + + /** + * Constructor. + * + * @see ComplexEntry#ComplexEntry(int,int) + */ + public MethodEntry(int classIndex, int nameAndTypeIndex) { + super(classIndex, nameAndTypeIndex); + } + + public int getType() { + return Entry.METHOD; + } + + public void acceptVisit(BCVisitor visit) { + visit.enterMethodEntry(this); + visit.exitMethodEntry(this); + } +} diff --git a/serp/src/main/java/serp/bytecode/lowlevel/NameAndTypeEntry.java b/serp/src/main/java/serp/bytecode/lowlevel/NameAndTypeEntry.java new file mode 100755 index 000000000..efe8260fd --- /dev/null +++ b/serp/src/main/java/serp/bytecode/lowlevel/NameAndTypeEntry.java @@ -0,0 +1,124 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode.lowlevel; + +import serp.bytecode.visitor.*; + +import java.io.*; + + +/** + *

Entry containing indexes referencing a name and a descriptor. Used + * to describe fields and methods of other classes referenced by opcodes.

+ * + * @author Abe White + */ +public class NameAndTypeEntry extends Entry { + private int _nameIndex = 0; + private int _descriptorIndex = 0; + + /** + * Default constructor. + */ + public NameAndTypeEntry() { + } + + /** + * Constructor. + * + * @param nameIndex the constant pool index of the + * {@link UTF8Entry} containing the name of + * this entity + * @param descriptorIndex the constant pool index of the + * {@link UTF8Entry} containing the descriptor + * for this entity + */ + public NameAndTypeEntry(int nameIndex, int descriptorIndex) { + _nameIndex = nameIndex; + _descriptorIndex = descriptorIndex; + } + + public int getType() { + return Entry.NAMEANDTYPE; + } + + /** + * Return the constant pool index of the {@link UTF8Entry} + * containing the name of this entity. + */ + public int getNameIndex() { + return _nameIndex; + } + + /** + * Set the constant pool index of the {@link UTF8Entry} + * containing the name of this entity. + */ + public void setNameIndex(int nameIndex) { + Object key = beforeModify(); + _nameIndex = nameIndex; + afterModify(key); + } + + /** + * Return the name's referenced {@link UTF8Entry}. This method can only + * be run for entries that have been added to a constant pool. + */ + public UTF8Entry getNameEntry() { + return (UTF8Entry) getPool().getEntry(_nameIndex); + } + + /** + * Return the constant pool index of the {@link UTF8Entry} + * containing the descriptor for this entity. + */ + public int getDescriptorIndex() { + return _descriptorIndex; + } + + /** + * Set the constant pool index of a {@link UTF8Entry} + * containing the descriptor for this entity. + */ + public void setDescriptorIndex(int descriptorIndex) { + Object key = beforeModify(); + _descriptorIndex = descriptorIndex; + afterModify(key); + } + + /** + * Return the descriptor's referenced {@link UTF8Entry}. This method + * can only be run for entries that have been added to a constant pool. + */ + public UTF8Entry getDescriptorEntry() { + return (UTF8Entry) getPool().getEntry(_descriptorIndex); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterNameAndTypeEntry(this); + visit.exitNameAndTypeEntry(this); + } + + void readData(DataInput in) throws IOException { + _nameIndex = in.readUnsignedShort(); + _descriptorIndex = in.readUnsignedShort(); + } + + void writeData(DataOutput out) throws IOException { + out.writeShort(_nameIndex); + out.writeShort(_descriptorIndex); + } +} diff --git a/serp/src/main/java/serp/bytecode/lowlevel/StringEntry.java b/serp/src/main/java/serp/bytecode/lowlevel/StringEntry.java new file mode 100755 index 000000000..533d854f0 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/lowlevel/StringEntry.java @@ -0,0 +1,98 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode.lowlevel; + +import serp.bytecode.visitor.*; + +import java.io.*; + + +/** + *

A String constant in the constant pool. String constants + * hold a reference to a {@link UTF8Entry} that stores the actual value.

+ * + * @author Abe White + */ +public class StringEntry extends Entry implements ConstantEntry { + private int _stringIndex = -1; + + /** + * Default constructor. + */ + public StringEntry() { + } + + /** + * Constructor. + * + * @param stringIndex the constant pool index of the {@link UTF8Entry} + * containing the value of this string + */ + public StringEntry(int stringIndex) { + _stringIndex = stringIndex; + } + + public int getType() { + return Entry.STRING; + } + + /** + * Return the constant pool index of the {@link UTF8Entry} + * storing the value of this string. + */ + public int getStringIndex() { + return _stringIndex; + } + + /** + * Set the constant pool index of the {@link UTF8Entry} + * storing the value of this string. + */ + public void setStringIndex(int stringIndex) { + Object key = beforeModify(); + _stringIndex = stringIndex; + afterModify(key); + } + + /** + * Return the referenced {@link UTF8Entry}. This method can only + * be run for entries that have been added to a constant pool. + */ + public UTF8Entry getStringEntry() { + return (UTF8Entry) getPool().getEntry(_stringIndex); + } + + public Object getConstant() { + return getStringEntry().getValue(); + } + + public void setConstant(Object value) { + getStringEntry().setConstant(value); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterStringEntry(this); + visit.exitStringEntry(this); + } + + void readData(DataInput in) throws IOException { + _stringIndex = in.readUnsignedShort(); + } + + void writeData(DataOutput out) throws IOException { + out.writeShort(_stringIndex); + } +} diff --git a/serp/src/main/java/serp/bytecode/lowlevel/UTF8Entry.java b/serp/src/main/java/serp/bytecode/lowlevel/UTF8Entry.java new file mode 100755 index 000000000..b6ef9f1c2 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/lowlevel/UTF8Entry.java @@ -0,0 +1,90 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode.lowlevel; + +import serp.bytecode.visitor.*; + +import java.io.*; + + +/** + *

A unicode string value in the constant pool.

+ * + * @author Abe White + */ +public class UTF8Entry extends Entry implements ConstantEntry { + private String _value = ""; + + /** + * Default constructor. + */ + public UTF8Entry() { + } + + /** + * Constructor. + * + * @param value the constant string value of this entry + */ + public UTF8Entry(String value) { + _value = value; + } + + public int getType() { + return Entry.UTF8; + } + + /** + * Return the value of the entry. + */ + public String getValue() { + return _value; + } + + /** + * Set the value of the entry. + */ + public void setValue(String value) { + if (value == null) { + throw new NullPointerException("value = null"); + } + + Object key = beforeModify(); + _value = value; + afterModify(key); + } + + public Object getConstant() { + return getValue(); + } + + public void setConstant(Object value) { + setValue((String) value); + } + + public void acceptVisit(BCVisitor visit) { + visit.enterUTF8Entry(this); + visit.exitUTF8Entry(this); + } + + void readData(DataInput in) throws IOException { + _value = in.readUTF(); + } + + void writeData(DataOutput out) throws IOException { + out.writeUTF(_value); + } +} diff --git a/serp/src/main/java/serp/bytecode/lowlevel/package.html b/serp/src/main/java/serp/bytecode/lowlevel/package.html new file mode 100755 index 000000000..9b61b6911 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/lowlevel/package.html @@ -0,0 +1,11 @@ + + +

Lowlevel Bytecode Manipuation

+

+ This package contains facilities for lowlevel bytecode manipulation + through the class constant pool. It is generally not necessary to use + this package directly, as all necessary functionality is made available + at a high level via the serp.bytecode package. +

+ + diff --git a/serp/src/main/java/serp/bytecode/package.html b/serp/src/main/java/serp/bytecode/package.html new file mode 100755 index 000000000..12ce4d8a6 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/package.html @@ -0,0 +1,139 @@ + + +

Bytecode Manipuation

+

+ This package contains a framework for Java bytecode manipulation. +

+

+ Bytecode manipulation is a powerful tool in the arsenal of the Java + developer. It can be used for tasks from compiling alternative + programming languages to run in a JVM, to creating new classes on the + fly at runtime, to instrumenting classes for performance analysis, to + debugging, to altering or enhancing the capabilities of existing + compiled classes. Traditionally, however, this power has come at a + price: modifying bytecode has required an in-depth knowledge of the + class file structure and has necessitated very low-level programming + techniques. These costs have proven too much for most developers, and + bytecode manipulation has been largely ignored by the mainstream. +

+

+ The goal of the serp bytecode framework is to tap the full power of + bytecode modification while lowering its associated costs. + The framework provides a set of high-level APIs for manipulating all + aspects of bytecode, from large-scale structures like class member + fields to the individual instructions that comprise the code of + methods. While in order to perform any advanced manipulation, some + understanding of the + + class file format and especially of the + + JVM instruction set is necessary, the framework makes it as easy + as possible to enter the world of bytecode development. +

+

+ There are several other excellent bytecode frameworks available. Serp + excels, however, in the following areas: +

    +
  • + Ease of use. Serp provides very high-level APIs for + all normal bytecode modification functionality. Additionally, + the framework contains a large set of convenience methods to + make code that uses it as clean as possible. From overloading + its methods to prevent you from having to make type + conversions, to making shortcuts for operations like adding + default constructors, serp tries to take the pain out of + bytecode development. +
  • +
  • + Power. Serp does not hide any of the power of + bytecode manipulation behind a limited set of high-level + functions. In addition to its available high-level APIs, which + themselves cover the functionality all but the most advanced + users will ever need, serp gives you direct access to the + low-level details of the class file and constant pool. You + can even switch back and + forth between low-level and high-level operations; + serp maintains complete consistency of the class structure + at all times. A change to a method descriptor in the constant + pool, for example, will immediately change the return values + of all the high-level APIs that describe that method. +
  • +
  • + Constant pool management. In the class file format, + all constant values are stored in a constant pool of shared + entries to minimize the size of class structures. Serp gives + you access to the constant pool directly, but most of you + will never use it; serp's high-level APIs completely + abstract management of the constant pool. + Any time a new constant is needed, serp will automatically add + it to the pool while ensuring that no duplicates ever exist. + Serp also does its best to manipulate the pool so that the + effects of changing a constant are as expected: i.e. changing + one instruction to use the string "bar" instead of "foo" + will not affect other instructions that use the string "foo", + but changing the name of a class field will instantly change + all instructions that reference that field to use the new name. +
  • +
  • + Instruction morphing. Dealing with the individual + instructions that make up method code is the most difficult + part of bytecode manipulation. To facilitate this process, + most serp instruction representations have the ability to + change their underlying low-level opcodes on the fly as the + you modify the parameters of the instruction. For + example, accessing the constant integer value 0 requires the + opcode iconst0, while accessing the string + constant "foo" requires a different opcode, ldc, + followed by the constant pool index of "foo". In serp, however, + there is only one instruction, constant. This + instruction has setValue methods which use the + given value to automatically determine the correct opcodes and + arguments -- iconst0 for a value of 0 and + ldc plus the proper constant pool index for the + value of "foo". +
  • +
+

+

+ Serp is not ideally suited to all applications. Here are a few + disadvantages of serp: +

    +
  • + Speed. Serp is not built for speed. Though there + are plans for performing incremental parsing, serp currently + fully parses class files when a class is loaded, which is a + slow process. + Also, serp's insistence on full-time consistency between the + low and high level class structures slows down both access and + mutator methods. These factors are less of a concern, though, + when creating new classes at runtime (rather than modifying + existing code), or when using serp as part of the compilation + process. Serp excels in both of these scenarios. +
  • +
  • + Memory. Serp's high-level structures for representing + class bytecode are very memory-hungry. +
  • +
  • + Multi-threaded modifications. The serp toolkit is + not threadsafe. Multiple threads cannot safely make + modifications to the same classes the same time. +
  • +
  • + Project-level modifications. Changes made in one + class in a serp project are not yet automatically propogated + to other classes. However, there are plans to implement this, + as well as plans to allow operations to modify bytecode based + on specified patterns, similar to aspect-oriented programming. +
  • +
+

+

+ The first class that you should study in this package is the + {@link serp.bytecode.Project} type. From there, move onto the + {@link serp.bytecode.BCClass}, and trace its APIs into + {@link serp.bytecode.BCField}s, {@link serp.bytecode.BCMethod}s, + and finally into actual {@link serp.bytecode.Code}. +

+ + diff --git a/serp/src/main/java/serp/bytecode/visitor/BCVisitor.java b/serp/src/main/java/serp/bytecode/visitor/BCVisitor.java new file mode 100755 index 000000000..00c2799af --- /dev/null +++ b/serp/src/main/java/serp/bytecode/visitor/BCVisitor.java @@ -0,0 +1,415 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode.visitor; + +import serp.bytecode.*; +import serp.bytecode.Deprecated; + +import serp.bytecode.lowlevel.*; + + +/** + *

Base class for visitors on a bytecode entity. The public {@link #visit} + * method will traverse the object graph of the given entity, calling the + * enter* and exit* methods as it visits each + * object. The traversal is done depth-first. Subclasses should override + * only the methods for visiting the entities they are interested in. + * Whenever there is a general method (i.e. enter/exitEntry) as + * well as a more specific one (i.e. enter/exitStringEntry), the + * more general method will be called first, followed by a call on the correct + * specific method. Most subclasses will override either the general or + * specific cases, but not both.

+ * + * @author Abe White + */ +public class BCVisitor { + /** + * Visit the given entity. + */ + public void visit(VisitAcceptor obj) { + if (obj == null) { + return; + } + + obj.acceptVisit(this); + } + + public void enterProject(Project obj) { + } + + public void exitProject(Project obj) { + } + + public void enterBCClass(BCClass obj) { + } + + public void exitBCClass(BCClass obj) { + } + + public void enterBCMember(BCMember obj) { + } + + public void exitBCMember(BCMember obj) { + } + + public void enterBCField(BCField obj) { + } + + public void exitBCField(BCField obj) { + } + + public void enterBCMethod(BCMethod obj) { + } + + public void exitBCMethod(BCMethod obj) { + } + + public void enterAttribute(Attribute obj) { + } + + public void exitAttribute(Attribute obj) { + } + + public void enterConstantValue(ConstantValue obj) { + } + + public void exitConstantValue(ConstantValue obj) { + } + + public void enterDeprecated(Deprecated obj) { + } + + public void exitDeprecated(Deprecated obj) { + } + + public void enterExceptions(Exceptions obj) { + } + + public void exitExceptions(Exceptions obj) { + } + + public void enterInnerClasses(InnerClasses obj) { + } + + public void exitInnerClasses(InnerClasses obj) { + } + + public void enterLineNumberTable(LineNumberTable obj) { + } + + public void exitLineNumberTable(LineNumberTable obj) { + } + + public void enterLocalVariableTable(LocalVariableTable obj) { + } + + public void exitLocalVariableTable(LocalVariableTable obj) { + } + + public void enterLocalVariableTypeTable(LocalVariableTypeTable obj) { + } + + public void exitLocalVariableTypeTable(LocalVariableTypeTable obj) { + } + + public void enterSourceFile(SourceFile obj) { + } + + public void exitSourceFile(SourceFile obj) { + } + + public void enterSynthetic(Synthetic obj) { + } + + public void exitSynthetic(Synthetic obj) { + } + + public void enterUnknownAttribute(UnknownAttribute obj) { + } + + public void exitUnknownAttribute(UnknownAttribute obj) { + } + + public void enterCode(Code obj) { + } + + public void exitCode(Code obj) { + } + + public void enterExceptionHandler(ExceptionHandler obj) { + } + + public void exitExceptionHandler(ExceptionHandler obj) { + } + + public void enterInnerClass(InnerClass obj) { + } + + public void exitInnerClass(InnerClass obj) { + } + + public void enterLineNumber(LineNumber obj) { + } + + public void exitLineNumber(LineNumber obj) { + } + + public void enterLocalVariable(LocalVariable obj) { + } + + public void exitLocalVariable(LocalVariable obj) { + } + + public void enterLocalVariableType(LocalVariableType obj) { + } + + public void exitLocalVariableType(LocalVariableType obj) { + } + + public void enterInstruction(Instruction obj) { + } + + public void exitInstruction(Instruction obj) { + } + + public void enterArrayLoadInstruction(ArrayLoadInstruction obj) { + } + + public void exitArrayLoadInstruction(ArrayLoadInstruction obj) { + } + + public void enterArrayStoreInstruction(ArrayStoreInstruction obj) { + } + + public void exitArrayStoreInstruction(ArrayStoreInstruction obj) { + } + + public void enterClassInstruction(ClassInstruction obj) { + } + + public void exitClassInstruction(ClassInstruction obj) { + } + + public void enterConstantInstruction(ConstantInstruction obj) { + } + + public void exitConstantInstruction(ConstantInstruction obj) { + } + + public void enterConvertInstruction(ConvertInstruction obj) { + } + + public void exitConvertInstruction(ConvertInstruction obj) { + } + + public void enterGetFieldInstruction(GetFieldInstruction obj) { + } + + public void exitGetFieldInstruction(GetFieldInstruction obj) { + } + + public void enterIIncInstruction(IIncInstruction obj) { + } + + public void exitIIncInstruction(IIncInstruction obj) { + } + + public void enterJumpInstruction(JumpInstruction obj) { + } + + public void exitJumpInstruction(JumpInstruction obj) { + } + + public void enterIfInstruction(IfInstruction obj) { + } + + public void exitIfInstruction(IfInstruction obj) { + } + + public void enterLoadInstruction(LoadInstruction obj) { + } + + public void exitLoadInstruction(LoadInstruction obj) { + } + + public void enterLookupSwitchInstruction(LookupSwitchInstruction obj) { + } + + public void exitLookupSwitchInstruction(LookupSwitchInstruction obj) { + } + + public void enterMathInstruction(MathInstruction obj) { + } + + public void exitMathInstruction(MathInstruction obj) { + } + + public void enterMethodInstruction(MethodInstruction obj) { + } + + public void exitMethodInstruction(MethodInstruction obj) { + } + + public void enterMultiANewArrayInstruction(MultiANewArrayInstruction obj) { + } + + public void exitMultiANewArrayInstruction(MultiANewArrayInstruction obj) { + } + + public void enterNewArrayInstruction(NewArrayInstruction obj) { + } + + public void exitNewArrayInstruction(NewArrayInstruction obj) { + } + + public void enterPutFieldInstruction(PutFieldInstruction obj) { + } + + public void exitPutFieldInstruction(PutFieldInstruction obj) { + } + + public void enterRetInstruction(RetInstruction obj) { + } + + public void exitRetInstruction(RetInstruction obj) { + } + + public void enterReturnInstruction(ReturnInstruction obj) { + } + + public void exitReturnInstruction(ReturnInstruction obj) { + } + + public void enterStackInstruction(StackInstruction obj) { + } + + public void exitStackInstruction(StackInstruction obj) { + } + + public void enterStoreInstruction(StoreInstruction obj) { + } + + public void exitStoreInstruction(StoreInstruction obj) { + } + + public void enterTableSwitchInstruction(TableSwitchInstruction obj) { + } + + public void exitTableSwitchInstruction(TableSwitchInstruction obj) { + } + + public void enterWideInstruction(WideInstruction obj) { + } + + public void exitWideInstruction(WideInstruction obj) { + } + + public void enterMonitorEnterInstruction(MonitorEnterInstruction obj) { + } + + public void exitMonitorEnterInstruction(MonitorEnterInstruction obj) { + } + + public void enterMonitorExitInstruction(MonitorExitInstruction obj) { + } + + public void exitMonitorExitInstruction(MonitorExitInstruction obj) { + } + + public void enterCmpInstruction(CmpInstruction obj) { + } + + public void exitCmpInstruction(CmpInstruction obj) { + } + + public void enterConstantPool(ConstantPool obj) { + } + + public void exitConstantPool(ConstantPool obj) { + } + + public void enterEntry(Entry obj) { + } + + public void exitEntry(Entry obj) { + } + + public void enterClassEntry(ClassEntry obj) { + } + + public void exitClassEntry(ClassEntry obj) { + } + + public void enterDoubleEntry(DoubleEntry obj) { + } + + public void exitDoubleEntry(DoubleEntry obj) { + } + + public void enterFieldEntry(FieldEntry obj) { + } + + public void exitFieldEntry(FieldEntry obj) { + } + + public void enterFloatEntry(FloatEntry obj) { + } + + public void exitFloatEntry(FloatEntry obj) { + } + + public void enterIntEntry(IntEntry obj) { + } + + public void exitIntEntry(IntEntry obj) { + } + + public void enterInterfaceMethodEntry(InterfaceMethodEntry obj) { + } + + public void exitInterfaceMethodEntry(InterfaceMethodEntry obj) { + } + + public void enterLongEntry(LongEntry obj) { + } + + public void exitLongEntry(LongEntry obj) { + } + + public void enterMethodEntry(MethodEntry obj) { + } + + public void exitMethodEntry(MethodEntry obj) { + } + + public void enterNameAndTypeEntry(NameAndTypeEntry obj) { + } + + public void exitNameAndTypeEntry(NameAndTypeEntry obj) { + } + + public void enterStringEntry(StringEntry obj) { + } + + public void exitStringEntry(StringEntry obj) { + } + + public void enterUTF8Entry(UTF8Entry obj) { + } + + public void exitUTF8Entry(UTF8Entry obj) { + } +} diff --git a/serp/src/main/java/serp/bytecode/visitor/PrettyPrintVisitor.java b/serp/src/main/java/serp/bytecode/visitor/PrettyPrintVisitor.java new file mode 100755 index 000000000..cff04aff4 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/visitor/PrettyPrintVisitor.java @@ -0,0 +1,459 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode.visitor; + +import serp.bytecode.*; + +import serp.bytecode.lowlevel.*; + +import java.io.*; + + +/** + *

Visitor type that outputs a detailed, formatted document of the + * visited entity; similar to the javap -c command but more + * detailed.

+ * + * @author Abe White + */ +public class PrettyPrintVisitor extends BCVisitor { + private PrintWriter _out = null; + private String _prefix = ""; + private int _entryCount = 0; + + /** + * Constructor; all pritning will go to stdout. + */ + public PrettyPrintVisitor() { + _out = new PrintWriter(System.out); + } + + /** + * Constructor. + * + * @param out the stream to print to + */ + public PrettyPrintVisitor(PrintWriter out) { + _out = out; + } + + /** + * Invoke with the class or file names to pretty print; the + * functionality is similar to the javap -c command, but more + * detailed. + */ + public static void main(String[] args) + throws ClassNotFoundException, IOException { + if (args.length == 0) { + System.err.println("Usage: java " + + PrettyPrintVisitor.class.getName() + + " +"); + System.exit(1); + } + + PrettyPrintVisitor ppv = new PrettyPrintVisitor(); + Project project = new Project(); + BCClass type; + + for (int i = 0; i < args.length; i++) { + if (args[i].endsWith(".class")) { + type = project.loadClass(new File(args[i])); + } else { + type = project.loadClass(Class.forName(args[i], false, + PrettyPrintVisitor.class.getClassLoader())); + } + + ppv.visit(type); + } + } + + public void visit(VisitAcceptor entity) { + super.visit(entity); + _out.flush(); + } + + public void enterProject(Project obj) { + openBlock("Project"); + + println("name=" + obj.getName()); + } + + public void exitProject(Project obj) { + closeBlock(); + } + + public void enterBCClass(BCClass obj) { + openBlock("Class"); + + println("magic=" + obj.getMagic()); + println("minor=" + obj.getMinorVersion()); + println("major=" + obj.getMajorVersion()); + println("access=" + obj.getAccessFlags()); + println("name=" + obj.getIndex() + " <" + obj.getName() + ">"); + println("super=" + obj.getSuperclassIndex() + " <" + + obj.getSuperclassName() + ">"); + + int[] indexes = obj.getDeclaredInterfaceIndexes(); + String[] names = obj.getDeclaredInterfaceNames(); + + for (int i = 0; i < indexes.length; i++) + println("interface=" + indexes[i] + " <" + names[i] + ">"); + } + + public void exitBCClass(BCClass obj) { + closeBlock(); + } + + public void enterBCField(BCField obj) { + openBlock("Field"); + println("access=" + obj.getAccessFlags()); + println("name=" + obj.getNameIndex() + " <" + obj.getName() + ">"); + println("type=" + obj.getDescriptorIndex() + " <" + obj.getTypeName() + + ">"); + } + + public void exitBCField(BCField obj) { + closeBlock(); + } + + public void enterBCMethod(BCMethod obj) { + openBlock("Method"); + println("access=" + obj.getAccessFlags()); + println("name=" + obj.getNameIndex() + " <" + obj.getName() + ">"); + println("descriptor=" + obj.getDescriptorIndex()); + println("return=" + obj.getReturnName()); + + String[] params = obj.getParamNames(); + + for (int i = 0; i < params.length; i++) + println("param=" + params[i]); + } + + public void exitBCMethod(BCMethod obj) { + closeBlock(); + } + + public void enterAttribute(Attribute obj) { + openBlock(obj.getName()); + } + + public void exitAttribute(Attribute obj) { + closeBlock(); + } + + public void enterConstantValue(ConstantValue obj) { + println("value=" + obj.getValueIndex() + " <" + obj.getTypeName() + + "=" + obj.getValue() + ">"); + } + + public void enterExceptions(Exceptions obj) { + int[] indexes = obj.getExceptionIndexes(); + String[] names = obj.getExceptionNames(); + + for (int i = 0; i < indexes.length; i++) + println("exception=" + indexes[i] + " <" + names[i] + ">"); + } + + public void enterSourceFile(SourceFile obj) { + println("source=" + obj.getFileIndex() + " <" + obj.getFileName() + + ">"); + } + + public void enterCode(Code obj) { + println("maxStack=" + obj.getMaxStack()); + println("maxLocals=" + obj.getMaxLocals()); + println(""); + } + + public void enterExceptionHandler(ExceptionHandler obj) { + openBlock("ExceptionHandler"); + println("startPc=" + obj.getTryStartPc()); + println("endPc=" + obj.getTryEndPc()); + println("handlerPc=" + obj.getHandlerStartPc()); + println("catch=" + obj.getCatchIndex() + " <" + obj.getCatchName() + + ">"); + } + + public void exitExceptionHandler(ExceptionHandler obj) { + closeBlock(); + } + + public void enterInnerClass(InnerClass obj) { + openBlock("InnerClass"); + println("access=" + obj.getAccessFlags()); + println("name=" + obj.getNameIndex() + " <" + obj.getName() + ">"); + println("type=" + obj.getTypeIndex() + "<" + obj.getTypeName() + ">"); + println("declarer=" + obj.getDeclarerIndex() + "<" + + obj.getDeclarerName() + ">"); + } + + public void exitInnerClass(InnerClass obj) { + closeBlock(); + } + + public void enterLineNumber(LineNumber obj) { + openBlock("LineNumber"); + println("startPc=" + obj.getStartPc()); + println("line=" + obj.getLine()); + } + + public void exitLineNumber(LineNumber obj) { + closeBlock(); + } + + public void enterLocalVariable(LocalVariable obj) { + openBlock("LocalVariable"); + println("startPc=" + obj.getStartPc()); + println("length=" + obj.getLength()); + println("local=" + obj.getLocal()); + println("name=" + obj.getNameIndex() + " <" + obj.getName() + ">"); + println("type=" + obj.getTypeIndex() + " <" + obj.getTypeName() + ">"); + } + + public void exitLocalVariable(LocalVariable obj) { + closeBlock(); + } + + public void enterLocalVariableType(LocalVariableType obj) { + openBlock("LocalVariableType"); + println("startPc=" + obj.getStartPc()); + println("length=" + obj.getLength()); + println("local=" + obj.getLocal()); + println("name=" + obj.getNameIndex() + " <" + obj.getName() + ">"); + println("signature=" + obj.getTypeIndex() + " <" + obj.getTypeName() + + ">"); + } + + public void exitLocalVariableType(LocalVariableType obj) { + closeBlock(); + } + + public void enterInstruction(Instruction obj) { + _out.print(_prefix + obj.getByteIndex() + " " + obj.getName() + " "); + } + + public void exitInstruction(Instruction obj) { + _out.println(); + } + + public void enterClassInstruction(ClassInstruction obj) { + _out.print(obj.getTypeIndex() + " <" + obj.getTypeName() + ">"); + } + + public void enterConstantInstruction(ConstantInstruction obj) { + _out.print("<" + obj.getValue() + ">"); + } + + public void enterGetFieldInstruction(GetFieldInstruction obj) { + _out.print(obj.getFieldIndex() + " <" + obj.getFieldTypeName() + " " + + obj.getFieldDeclarerName() + "." + obj.getFieldName() + ">"); + } + + public void enterIIncInstruction(IIncInstruction obj) { + _out.print(obj.getLocal() + " "); + + if (obj.getIncrement() < 0) { + _out.print("-"); + } + + _out.print(obj.getIncrement()); + } + + public void enterJumpInstruction(JumpInstruction obj) { + _out.print(obj.getOffset()); + } + + public void enterIfInstruction(IfInstruction obj) { + _out.print(obj.getOffset()); + } + + public void enterLoadInstruction(LoadInstruction obj) { + _out.print("<" + obj.getLocal() + ">"); + } + + public void enterLookupSwitchInstruction(LookupSwitchInstruction obj) { + _out.println(); + _prefix += " "; + + int[] offsets = obj.getOffsets(); + int[] matches = obj.getMatches(); + + for (int i = 0; i < offsets.length; i++) + println("case " + matches[i] + "=" + offsets[i]); + + _out.print(_prefix + "default=" + obj.getDefaultOffset()); + + _prefix = _prefix.substring(2); + } + + public void enterMethodInstruction(MethodInstruction obj) { + _out.print(obj.getMethodIndex() + " <" + obj.getMethodReturnName() + + " " + obj.getMethodDeclarerName() + "." + obj.getMethodName() + + "("); + + String[] params = obj.getMethodParamNames(); + int dotIndex; + + for (int i = 0; i < params.length; i++) { + dotIndex = params[i].lastIndexOf('.'); + + if (dotIndex != -1) { + params[i] = params[i].substring(dotIndex + 1); + } + + _out.print(params[i]); + + if (i != (params.length - 1)) { + _out.print(", "); + } + } + + _out.print(")>"); + } + + public void enterMultiANewArrayInstruction(MultiANewArrayInstruction obj) { + _out.print(obj.getTypeIndex() + " " + obj.getDimensions() + " <" + + obj.getTypeName()); + + String post = ""; + + for (int i = 0; i < obj.getDimensions(); i++) + post += "[]"; + + _out.print(post + ">"); + } + + public void enterNewArrayInstruction(NewArrayInstruction obj) { + _out.print(obj.getTypeCode() + " <" + obj.getTypeName() + "[]>"); + } + + public void enterPutFieldInstruction(PutFieldInstruction obj) { + _out.print(obj.getFieldIndex() + " <" + obj.getFieldTypeName() + " " + + obj.getFieldDeclarerName() + "." + obj.getFieldName() + ">"); + } + + public void enterRetInstruction(RetInstruction obj) { + _out.print(obj.getLocal()); + } + + public void enterStoreInstruction(StoreInstruction obj) { + _out.print("<" + obj.getLocal() + ">"); + } + + public void enterTableSwitchInstruction(TableSwitchInstruction obj) { + _out.println(); + _prefix += " "; + + println("low=" + obj.getLow()); + println("high=" + obj.getHigh()); + + int[] offsets = obj.getOffsets(); + + for (int i = 0; i < offsets.length; i++) + println("case=" + offsets[i]); + + _out.print(_prefix + "default=" + obj.getDefaultOffset()); + + _prefix = _prefix.substring(2); + } + + public void enterWideInstruction(WideInstruction obj) { + int ins = obj.getInstruction(); + _out.print(ins + " <" + Constants.OPCODE_NAMES[ins] + ">"); + } + + public void enterConstantPool(ConstantPool obj) { + _entryCount = 0; + openBlock("ConstantPool"); + } + + public void exitConstantPool(ConstantPool obj) { + closeBlock(); + } + + public void enterEntry(Entry obj) { + String name = obj.getClass().getName(); + openBlock(++_entryCount + ": " + + name.substring(name.lastIndexOf('.') + 1)); + } + + public void exitEntry(Entry obj) { + closeBlock(); + } + + public void enterClassEntry(ClassEntry obj) { + println("name=" + obj.getNameIndex()); + } + + public void enterDoubleEntry(DoubleEntry obj) { + println("value=" + obj.getValue()); + } + + public void enterFieldEntry(FieldEntry obj) { + println("class=" + obj.getClassIndex()); + println("nameAndType=" + obj.getNameAndTypeIndex()); + } + + public void enterFloatEntry(FloatEntry obj) { + println("value=" + obj.getValue()); + } + + public void enterIntEntry(IntEntry obj) { + println("value=" + obj.getValue()); + } + + public void enterInterfaceMethodEntry(InterfaceMethodEntry obj) { + println("class=" + obj.getClassIndex()); + println("nameAndType=" + obj.getNameAndTypeIndex()); + } + + public void enterLongEntry(LongEntry obj) { + println("value=" + obj.getValue()); + } + + public void enterMethodEntry(MethodEntry obj) { + println("class=" + obj.getClassIndex()); + println("nameAndType=" + obj.getNameAndTypeIndex()); + } + + public void enterNameAndTypeEntry(NameAndTypeEntry obj) { + println("name=" + obj.getNameIndex()); + println("descriptor=" + obj.getDescriptorIndex()); + } + + public void enterStringEntry(StringEntry obj) { + println("index=" + obj.getStringIndex()); + } + + public void enterUTF8Entry(UTF8Entry obj) { + println("value=" + obj.getValue()); + } + + private void println(String ln) { + _out.print(_prefix); + _out.println(ln); + } + + private void openBlock(String name) { + println(name + " {"); + _prefix += " "; + } + + private void closeBlock() { + _prefix = _prefix.substring(2); + println("}"); + } +} diff --git a/serp/src/main/java/serp/bytecode/visitor/VisitAcceptor.java b/serp/src/main/java/serp/bytecode/visitor/VisitAcceptor.java new file mode 100755 index 000000000..509e7143b --- /dev/null +++ b/serp/src/main/java/serp/bytecode/visitor/VisitAcceptor.java @@ -0,0 +1,34 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode.visitor; + + +/** + *

Interface denoting an entity that can accept a {@link BCVisitor} and + * provide its internal state to it. All entities in the bytecode framework + * implement this interface.

+ * + * @author Abe White + */ +public interface VisitAcceptor { + /** + * Accept a visit from a {@link BCVisitor}, calling the appropriate methods + * to notify the visitor that it has entered this entity, and + * to provide it with the proper callbacks for each sub-entity owned + * by this one. + */ + public void acceptVisit(BCVisitor visitor); +} diff --git a/serp/src/main/java/serp/bytecode/visitor/package.html b/serp/src/main/java/serp/bytecode/visitor/package.html new file mode 100755 index 000000000..8321df185 --- /dev/null +++ b/serp/src/main/java/serp/bytecode/visitor/package.html @@ -0,0 +1,10 @@ + + +

Bytecode Visitor

+

+ This package implements the visitor pattern on bytecode entities + and provides a useful concrete visitor that pretty-prints a detailed + document describing any bytecode entity. +

+ + diff --git a/serp/src/main/java/serp/util/Numbers.java b/serp/src/main/java/serp/util/Numbers.java new file mode 100755 index 000000000..fde9ac358 --- /dev/null +++ b/serp/src/main/java/serp/util/Numbers.java @@ -0,0 +1,69 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.util; + + +/** + *

Number utilities.

+ * + * @author Abe White + */ +public class Numbers { + private static final Integer INT_NEGONE = new Integer(-1); + private static final Long LONG_NEGONE = new Long(-1); + private static final Integer[] INTEGERS = new Integer[50]; + private static final Long[] LONGS = new Long[50]; + + static { + for (int i = 0; i < INTEGERS.length; i++) + INTEGERS[i] = new Integer(i); + + for (int i = 0; i < LONGS.length; i++) + LONGS[i] = new Long(i); + } + + /** + * Return the wrapper for the given number, taking advantage of cached + * common values. + */ + public static Integer valueOf(int n) { + if (n == -1) { + return INT_NEGONE; + } + + if ((n >= 0) && (n < INTEGERS.length)) { + return INTEGERS[n]; + } + + return new Integer(n); + } + + /** + * Return the wrapper for the given number, taking advantage of cached + * common values. + */ + public static Long valueOf(long n) { + if (n == -1) { + return LONG_NEGONE; + } + + if ((n >= 0) && (n < LONGS.length)) { + return LONGS[(int) n]; + } + + return new Long(n); + } +} diff --git a/serp/src/main/java/serp/util/Strings.java b/serp/src/main/java/serp/util/Strings.java new file mode 100755 index 000000000..648008baa --- /dev/null +++ b/serp/src/main/java/serp/util/Strings.java @@ -0,0 +1,417 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.util; + +import java.math.*; + +import java.util.*; + + +/** + *

String utiltity methods.

+ * + * @author Abe White + */ +public class Strings { + private static final Object[][] _codes = new Object[][] { + { byte.class, "byte", "B" }, + { char.class, "char", "C" }, + { double.class, "double", "D" }, + { float.class, "float", "F" }, + { int.class, "int", "I" }, + { long.class, "long", "J" }, + { short.class, "short", "S" }, + { boolean.class, "boolean", "Z" }, + { void.class, "void", "V" } + }; + + /** + * Replace all instances of from in str + * with to. + * + * @param str the candidate string to replace + * @param from the token to replace + * @param to the new token + * @return the string with all the replacements made + */ + public static String replace(String str, String from, String to) { + String[] split = split(str, from, Integer.MAX_VALUE); + + return join(split, to); + } + + /** + * Splits the given string on the given token. Follows the semantics + * of the Java 1.4 {@link String#split(String,int)} method, but does + * not treat the given token as a regular expression. + */ + public static String[] split(String str, String token, int max) { + if ((str == null) || (str.length() == 0)) { + return new String[0]; + } + + if ((token == null) || (token.length() == 0)) { + throw new IllegalArgumentException("token: [" + token + "]"); + } + + // split on token + LinkedList ret = new LinkedList(); + int start = 0; + + for (int split = 0; split != -1;) { + split = str.indexOf(token, start); + + if ((split == -1) && (start >= str.length())) { + ret.add(""); + } else if (split == -1) { + ret.add(str.substring(start)); + } else { + ret.add(str.substring(start, split)); + start = split + token.length(); + } + } + + // now take max into account; this isn't the most efficient way + // of doing things since we split the maximum number of times + // regardless of the given parameters, but it makes things easy + if (max == 0) { + // discard any trailing empty splits + while (ret.getLast().equals("")) + ret.removeLast(); + } else if ((max > 0) && (ret.size() > max)) { + // move all splits over max into the last split + StringBuffer buf = new StringBuffer(ret.removeLast().toString()); + + while (ret.size() >= max) { + buf.insert(0, token); + buf.insert(0, ret.removeLast()); + } + + ret.add(buf.toString()); + } + + return (String[]) ret.toArray(new String[ret.size()]); + } + + /** + * Joins the given strings, placing the given token between them. + */ + public static String join(Object[] strings, String token) { + if (strings == null) { + return null; + } + + StringBuffer buf = new StringBuffer(20 * strings.length); + + for (int i = 0; i < strings.length; i++) { + if (i > 0) { + buf.append(token); + } + + if (strings[i] != null) { + buf.append(strings[i]); + } + } + + return buf.toString(); + } + + /** + * Return the class for the given string, correctly handling + * primitive types. If the given class loader is null, the context + * loader of the current thread will be used. + * + * @throws RuntimeException on load error + */ + public static Class toClass(String str, ClassLoader loader) { + return toClass(str, false, loader); + } + + /** + * Return the class for the given string, correctly handling + * primitive types. If the given class loader is null, the context + * loader of the current thread will be used. + * + * @throws RuntimeException on load error + */ + public static Class toClass(String str, boolean resolve, ClassLoader loader) { + if (str == null) { + throw new NullPointerException("str == null"); + } + + // array handling + int dims = 0; + + while (str.endsWith("[]")) { + dims++; + str = str.substring(0, str.length() - 2); + } + + // check against primitive types + boolean primitive = false; + + if (str.indexOf('.') == -1) { + for (int i = 0; !primitive && (i < _codes.length); i++) { + if (_codes[i][1].equals(str)) { + if (dims == 0) { + return (Class) _codes[i][0]; + } + + str = (String) _codes[i][2]; + primitive = true; + } + } + } + + if (dims > 0) { + int size = str.length() + dims; + + if (!primitive) { + size += 2; + } + + StringBuffer buf = new StringBuffer(size); + + for (int i = 0; i < dims; i++) + buf.append('['); + + if (!primitive) { + buf.append('L'); + } + + buf.append(str); + + if (!primitive) { + buf.append(';'); + } + + str = buf.toString(); + } + + if (loader == null) { + loader = Thread.currentThread().getContextClassLoader(); + } + + try { + return Class.forName(str, resolve, loader); + } catch (Throwable t) { + throw new IllegalArgumentException(t.toString()); + } + } + + /** + * Return only the class name, without package. + */ + public static String getClassName(Class cls) { + return (cls == null) ? null : getClassName(cls.getName()); + } + + /** + * Return only the class name. + */ + public static String getClassName(String fullName) { + if (fullName == null) { + return null; + } + + // special case for arrays + int dims = 0; + + for (int i = 0; i < fullName.length(); i++) { + if (fullName.charAt(i) != '[') { + dims = i; + + break; + } + } + + if (dims > 0) { + fullName = fullName.substring(dims); + } + + // check for primitives + for (int i = 0; i < _codes.length; i++) { + if (_codes[i][2].equals(fullName)) { + fullName = (String) _codes[i][1]; + + break; + } + } + + fullName = fullName.substring(fullName.lastIndexOf('.') + 1); + + for (int i = 0; i < dims; i++) + fullName = fullName + "[]"; + + return fullName; + } + + /** + * Return only the package, or empty string if none. + */ + public static String getPackageName(Class cls) { + return (cls == null) ? null : getPackageName(cls.getName()); + } + + /** + * Return only the package, or empty string if none. + */ + public static String getPackageName(String fullName) { + if (fullName == null) { + return null; + } + + int dotIdx = fullName.lastIndexOf('.'); + + return (dotIdx == -1) ? "" : fullName.substring(0, dotIdx); + } + + /** + * Return val as the type specified by + * type. If type is a primitive, the + * primitive wrapper type is created and returned, and + * nulls are converted to the Java default for the + * primitive type. + * + * @param val The string value to parse + * @param type The type to parse. This must be a primitive or a + * primitive wrapper, or one of {@link BigDecimal}, + * {@link BigInteger}, {@link String}, {@link Date}. + * @throws IllegalArgumentException if type is not a + * supported type, or if val cannot be + * converted into an instance of type type. + */ + public static Object parse(String val, Class type) { + if (!canParse(type)) { + throw new IllegalArgumentException("invalid type: " + + type.getName()); + } + + // deal with null value + if (val == null) { + if (!type.isPrimitive()) { + return null; + } + + if (type == boolean.class) { + return Boolean.FALSE; + } + + if (type == byte.class) { + return new Byte((byte) 0); + } + + if (type == char.class) { + return new Character((char) 0); + } + + if (type == double.class) { + return new Double(0); + } + + if (type == float.class) { + return new Float(0); + } + + if (type == int.class) { + return Numbers.valueOf(0); + } + + if (type == long.class) { + return Numbers.valueOf(0L); + } + + if (type == short.class) { + return new Short((short) 0); + } + + throw new IllegalStateException("invalid type: " + type); + } + + // deal with non-null value + if ((type == boolean.class) || (type == Boolean.class)) { + return Boolean.valueOf(val); + } + + if ((type == byte.class) || (type == Byte.class)) { + return Byte.valueOf(val); + } + + if ((type == char.class) || (type == Character.class)) { + if (val.length() == 0) { + return new Character((char) 0); + } + + if (val.length() == 1) { + return new Character(val.charAt(0)); + } + + throw new IllegalArgumentException("'" + val + "' is longer than " + + "one character."); + } + + if ((type == double.class) || (type == Double.class)) { + return Double.valueOf(val); + } + + if ((type == float.class) || (type == Float.class)) { + return Float.valueOf(val); + } + + if ((type == int.class) || (type == Integer.class)) { + return Integer.valueOf(val); + } + + if ((type == long.class) || (type == Long.class)) { + return Long.valueOf(val); + } + + if ((type == short.class) || (type == Short.class)) { + return Short.valueOf(val); + } + + if (type == String.class) { + return val; + } + + if (type == Date.class) { + return new Date(val); + } + + if (type == BigInteger.class) { + return new BigInteger(val); + } + + if (type == BigDecimal.class) { + return new BigDecimal(val); + } + + throw new IllegalArgumentException("Invalid type: " + type); + } + + /** + * Whether the given type is parsable via {@link #parse}. + */ + public static boolean canParse(Class type) { + return type.isPrimitive() || (type == Boolean.class) || + (type == Byte.class) || (type == Character.class) || + (type == Short.class) || (type == Integer.class) || + (type == Long.class) || (type == Float.class) || + (type == Double.class) || (type == String.class) || + (type == Date.class) || (type == BigInteger.class) || + (type == BigDecimal.class); + } +} diff --git a/serp/src/main/java/serp/util/package.html b/serp/src/main/java/serp/util/package.html new file mode 100755 index 000000000..b894612ee --- /dev/null +++ b/serp/src/main/java/serp/util/package.html @@ -0,0 +1,8 @@ + + +

Utilties

+

+ Utilities used by bytecode libraries. +

+ + diff --git a/serp/src/test/java/serp/bytecode/AbstractStateTest.java b/serp/src/test/java/serp/bytecode/AbstractStateTest.java new file mode 100755 index 000000000..05f11aa0a --- /dev/null +++ b/serp/src/test/java/serp/bytecode/AbstractStateTest.java @@ -0,0 +1,207 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import junit.framework.*; + +import junit.textui.*; + + +/** + *

Base class for testing the handling of the {@link PrimitiveState} and + * {@link ArrayState}. Subclasses should set the {@link #_bc} member in + * their {@link TestCase#setUp} method.

+ * + * @author Abe White + */ +public abstract class AbstractStateTest extends TestCase { + protected Project _project = new Project(); + protected BCClass _bc = null; + + public AbstractStateTest(String test) { + super(test); + } + + /** + * Test the name and type operations. + */ + public abstract void testType(); + + /** + * Test operations on the superclass. + */ + public abstract void testSuperclass(); + + /** + * Test operations on the component type. + */ + public abstract void testComponent(); + + /** + * Test the basics -- magic number, etc. + */ + public void testBasics() { + assertEquals(Constants.VALID_MAGIC, _bc.getMagic()); + + try { + _bc.setMagic(1); + fail("Allowed set magic"); + } catch (UnsupportedOperationException uoe) { + } + + assertEquals(Constants.MAJOR_VERSION, _bc.getMajorVersion()); + + try { + _bc.setMajorVersion(1); + fail("Allowed set major version"); + } catch (UnsupportedOperationException uoe) { + } + + assertEquals(Constants.MINOR_VERSION, _bc.getMinorVersion()); + + try { + _bc.setMinorVersion(1); + fail("Allowed set minor version"); + } catch (UnsupportedOperationException uoe) { + } + + assertEquals(Constants.ACCESS_PUBLIC | Constants.ACCESS_FINAL, + _bc.getAccessFlags()); + + try { + _bc.setAccessFlags(1); + fail("Allowed set access flags"); + } catch (UnsupportedOperationException uoe) { + } + + try { + _bc.getPool(); + fail("Allowed access constant pool"); + } catch (UnsupportedOperationException uoe) { + } + } + + /** + * Test operations on interfaces. + */ + public void testInterfaces() { + assertEquals(0, _bc.getDeclaredInterfaceNames().length); + assertEquals(0, _bc.getInterfaceNames().length); + + try { + _bc.declareInterface("foo"); + fail("Allowed declare interface"); + } catch (UnsupportedOperationException uoe) { + } + + _bc.clearDeclaredInterfaces(); + assertTrue(!_bc.removeDeclaredInterface((String) null)); + assertTrue(!_bc.removeDeclaredInterface("foo")); + + assertTrue(_bc.isInstanceOf(_bc.getName())); + assertTrue(!_bc.isInstanceOf("foo")); + } + + /** + * Test operations on fields. + */ + public void testFields() { + assertEquals(0, _bc.getDeclaredFields().length); + assertEquals(0, _bc.getFields().length); + + try { + _bc.declareField("foo", int.class); + fail("Allowed declare field"); + } catch (UnsupportedOperationException uoe) { + } + + _bc.clearDeclaredFields(); + assertTrue(!_bc.removeDeclaredField((String) null)); + assertTrue(!_bc.removeDeclaredField("foo")); + } + + /** + * Test operations on methods. + */ + public void testMethods() { + assertEquals(0, _bc.getDeclaredMethods().length); + + try { + _bc.declareMethod("foo", int.class, null); + fail("Allowed declare method"); + } catch (UnsupportedOperationException uoe) { + } + + _bc.clearDeclaredMethods(); + assertTrue(!_bc.removeDeclaredMethod((String) null)); + assertTrue(!_bc.removeDeclaredMethod("foo")); + + try { + _bc.addDefaultConstructor(); + fail("Allowed add default constructor"); + } catch (UnsupportedOperationException uoe) { + } + } + + /** + * Test operations on attributes. + */ + public void testAttributes() { + assertNull(_bc.getSourceFile(false)); + + try { + _bc.getSourceFile(true); + fail("Allowed add source file"); + } catch (UnsupportedOperationException uoe) { + } + + assertNull(_bc.getInnerClasses(false)); + + try { + _bc.getInnerClasses(true); + fail("Allowed add inner classes"); + } catch (UnsupportedOperationException uoe) { + } + + assertTrue(!_bc.isDeprecated()); + + try { + _bc.setDeprecated(true); + fail("Allowed set deprecated"); + } catch (UnsupportedOperationException uoe) { + } + + assertEquals(0, _bc.getAttributes().length); + _bc.clearAttributes(); + assertTrue(!_bc.removeAttribute(Constants.ATTR_SYNTHETIC)); + + try { + _bc.addAttribute(Constants.ATTR_SYNTHETIC); + fail("Allowed add attribute"); + } catch (UnsupportedOperationException uoe) { + } + } + + /** + * Tests that these types cannot be written. + */ + public void testWrite() { + try { + _bc.toByteArray(); + } catch (UnsupportedOperationException uoe) { + } + } +} diff --git a/serp/src/test/java/serp/bytecode/TestArray.java b/serp/src/test/java/serp/bytecode/TestArray.java new file mode 100755 index 000000000..176048c65 --- /dev/null +++ b/serp/src/test/java/serp/bytecode/TestArray.java @@ -0,0 +1,87 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import junit.framework.*; + +import junit.textui.*; + + +/** + *

Tests the handling of array {@link BCClass}es.

+ * + * @author Abe White + */ +public class TestArray extends AbstractStateTest { + private BCClass _bc2 = null; + + public TestArray(String test) { + super(test); + } + + public void setUp() { + _bc = _project.loadClass(String[].class); + _bc2 = _project.loadClass(int[][].class); + } + + public void testType() { + assertEquals(String[].class.getName(), _bc.getName()); + assertEquals("java.lang", _bc.getPackageName()); + assertEquals("String[]", _bc.getClassName()); + assertEquals(String[].class, _bc.getType()); + + try { + _bc.setName("Foo[]"); + fail("Allowed set name"); + } catch (UnsupportedOperationException uoe) { + } + + assertTrue(!_bc.isPrimitive()); + assertTrue(_bc.isArray()); + + assertEquals(int[][].class.getName(), _bc2.getName()); + assertNull(_bc2.getPackageName()); + assertEquals("int[][]", _bc2.getClassName()); + assertEquals(int[][].class, _bc2.getType()); + } + + public void testSuperclass() { + assertEquals(Object.class.getName(), _bc.getSuperclassName()); + + try { + _bc.setSuperclass("Foo"); + fail("Allowed set superclass"); + } catch (UnsupportedOperationException uoe) { + } + } + + public void testComponent() { + assertEquals(String.class.getName(), _bc.getComponentName()); + assertEquals(String.class, _bc.getComponentType()); + assertEquals(String.class, _bc.getComponentBC().getType()); + assertEquals(int[].class.getName(), _bc2.getComponentName()); + assertEquals(int[].class, _bc2.getComponentType()); + assertEquals(int[].class, _bc2.getComponentBC().getType()); + } + + public static Test suite() { + return new TestSuite(TestArray.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } +} diff --git a/serp/src/test/java/serp/bytecode/TestArrayLoadInstruction.java b/serp/src/test/java/serp/bytecode/TestArrayLoadInstruction.java new file mode 100755 index 000000000..12d6312bd --- /dev/null +++ b/serp/src/test/java/serp/bytecode/TestArrayLoadInstruction.java @@ -0,0 +1,96 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import junit.framework.*; + +import junit.textui.*; + + +/** + *

Tests the {@link ArrayLoadInstruction} type.

+ * + * @author Abe White + */ +public class TestArrayLoadInstruction extends TestCase { + private Code _code = new Code(); + + public TestArrayLoadInstruction(String test) { + super(test); + } + + /** + * Test that the instruction initializes correctly when generated. + */ + public void testIniitalize() { + assertEquals(Constants.NOP, _code.xaload().getOpcode()); + assertEquals(Constants.IALOAD, _code.iaload().getOpcode()); + assertEquals(Constants.LALOAD, _code.laload().getOpcode()); + assertEquals(Constants.FALOAD, _code.faload().getOpcode()); + assertEquals(Constants.DALOAD, _code.daload().getOpcode()); + assertEquals(Constants.AALOAD, _code.aaload().getOpcode()); + assertEquals(Constants.BALOAD, _code.baload().getOpcode()); + assertEquals(Constants.CALOAD, _code.caload().getOpcode()); + assertEquals(Constants.SALOAD, _code.saload().getOpcode()); + } + + /** + * Test the the instruction returns its type correctly. + */ + public void testGetType() { + assertNull(_code.xaload().getType()); + assertEquals(int.class, _code.iaload().getType()); + assertEquals(long.class, _code.laload().getType()); + assertEquals(float.class, _code.faload().getType()); + assertEquals(double.class, _code.daload().getType()); + assertEquals(Object.class, _code.aaload().getType()); + assertEquals(byte.class, _code.baload().getType()); + assertEquals(char.class, _code.caload().getType()); + assertEquals(short.class, _code.saload().getType()); + } + + /** + * Test that the opcode morphs correctly with type changes. + */ + public void testOpcodeMorph() { + ArrayLoadInstruction ins = _code.xaload(); + assertEquals(Constants.NOP, ins.getOpcode()); + assertEquals(Constants.NOP, ins.setType((String) null).getOpcode()); + assertEquals(Constants.NOP, ins.setType((BCClass) null).getOpcode()); + assertEquals(Constants.NOP, ins.setType((Class) null).getOpcode()); + + assertEquals(Constants.IALOAD, ins.setType(int.class).getOpcode()); + assertEquals(Constants.NOP, ins.setType((String) null).getOpcode()); + assertEquals(Constants.LALOAD, ins.setType(long.class).getOpcode()); + assertEquals(Constants.FALOAD, ins.setType(float.class).getOpcode()); + assertEquals(Constants.DALOAD, ins.setType(double.class).getOpcode()); + assertEquals(Constants.AALOAD, ins.setType(Object.class).getOpcode()); + assertEquals(Constants.BALOAD, ins.setType(byte.class).getOpcode()); + assertEquals(Constants.CALOAD, ins.setType(char.class).getOpcode()); + assertEquals(Constants.SALOAD, ins.setType(short.class).getOpcode()); + assertEquals(Constants.IALOAD, ins.setType(void.class).getOpcode()); + assertEquals(Constants.AALOAD, ins.setType(String.class).getOpcode()); + assertEquals(Constants.IALOAD, ins.setType(boolean.class).getOpcode()); + } + + public static Test suite() { + return new TestSuite(TestArrayLoadInstruction.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } +} diff --git a/serp/src/test/java/serp/bytecode/TestArrayStoreInstruction.java b/serp/src/test/java/serp/bytecode/TestArrayStoreInstruction.java new file mode 100755 index 000000000..9016d9c3c --- /dev/null +++ b/serp/src/test/java/serp/bytecode/TestArrayStoreInstruction.java @@ -0,0 +1,96 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import junit.framework.*; + +import junit.textui.*; + + +/** + *

Tests the {@link ArrayStoreInstruction} type.

+ * + * @author Abe White + */ +public class TestArrayStoreInstruction extends TestCase { + private Code _code = new Code(); + + public TestArrayStoreInstruction(String test) { + super(test); + } + + /** + * Test that the instruction initializes correctly when generated. + */ + public void testIniitalize() { + assertEquals(Constants.NOP, _code.xastore().getOpcode()); + assertEquals(Constants.IASTORE, _code.iastore().getOpcode()); + assertEquals(Constants.LASTORE, _code.lastore().getOpcode()); + assertEquals(Constants.FASTORE, _code.fastore().getOpcode()); + assertEquals(Constants.DASTORE, _code.dastore().getOpcode()); + assertEquals(Constants.AASTORE, _code.aastore().getOpcode()); + assertEquals(Constants.BASTORE, _code.bastore().getOpcode()); + assertEquals(Constants.CASTORE, _code.castore().getOpcode()); + assertEquals(Constants.SASTORE, _code.sastore().getOpcode()); + } + + /** + * Test the the instruction returns its type correctly. + */ + public void testGetType() { + assertNull(_code.xastore().getType()); + assertEquals(int.class, _code.iastore().getType()); + assertEquals(long.class, _code.lastore().getType()); + assertEquals(float.class, _code.fastore().getType()); + assertEquals(double.class, _code.dastore().getType()); + assertEquals(Object.class, _code.aastore().getType()); + assertEquals(byte.class, _code.bastore().getType()); + assertEquals(char.class, _code.castore().getType()); + assertEquals(short.class, _code.sastore().getType()); + } + + /** + * Test that the opcode morphs correctly with type changes. + */ + public void testOpcodeMorph() { + ArrayStoreInstruction ins = _code.xastore(); + assertEquals(Constants.NOP, ins.getOpcode()); + assertEquals(Constants.NOP, ins.setType((String) null).getOpcode()); + assertEquals(Constants.NOP, ins.setType((BCClass) null).getOpcode()); + assertEquals(Constants.NOP, ins.setType((Class) null).getOpcode()); + + assertEquals(Constants.IASTORE, ins.setType(int.class).getOpcode()); + assertEquals(Constants.NOP, ins.setType((String) null).getOpcode()); + assertEquals(Constants.LASTORE, ins.setType(long.class).getOpcode()); + assertEquals(Constants.FASTORE, ins.setType(float.class).getOpcode()); + assertEquals(Constants.DASTORE, ins.setType(double.class).getOpcode()); + assertEquals(Constants.AASTORE, ins.setType(Object.class).getOpcode()); + assertEquals(Constants.BASTORE, ins.setType(byte.class).getOpcode()); + assertEquals(Constants.CASTORE, ins.setType(char.class).getOpcode()); + assertEquals(Constants.SASTORE, ins.setType(short.class).getOpcode()); + assertEquals(Constants.IASTORE, ins.setType(void.class).getOpcode()); + assertEquals(Constants.AASTORE, ins.setType(String.class).getOpcode()); + assertEquals(Constants.IASTORE, ins.setType(boolean.class).getOpcode()); + } + + public static Test suite() { + return new TestSuite(TestArrayStoreInstruction.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } +} diff --git a/serp/src/test/java/serp/bytecode/TestAttributes.java b/serp/src/test/java/serp/bytecode/TestAttributes.java new file mode 100755 index 000000000..f05508339 --- /dev/null +++ b/serp/src/test/java/serp/bytecode/TestAttributes.java @@ -0,0 +1,155 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import junit.framework.*; + +import junit.textui.*; + + +/** + *

Tests the {@link Attributes} type.

+ * + * @author Abe White + */ +public class TestAttributes extends TestCase { + private Project _project = new Project(); + private Attributes _attrs = _project.loadClass("serp.Attrs"); + private Attributes _attrs2 = _project.loadClass("serp.Attrs2"); + + public TestAttributes(String test) { + super(test); + } + + /** + * Test getting attributes. + */ + public void testGetAttributes() { + assertEquals(0, _attrs.getAttributes().length); + assertNull(_attrs.getAttribute(Constants.ATTR_SYNTHETIC)); + + Attribute attr1 = _attrs.addAttribute(Constants.ATTR_DEPRECATED); + Attribute attr2 = _attrs.addAttribute(Constants.ATTR_SYNTHETIC); + + assertEquals(2, _attrs.getAttributes().length); + assertNull(_attrs.getAttribute(Constants.ATTR_CODE)); + assertTrue(attr1 == _attrs.getAttribute(Constants.ATTR_DEPRECATED)); + assertTrue(attr2 == _attrs.getAttribute(Constants.ATTR_SYNTHETIC)); + assertEquals(0, _attrs.getAttributes(Constants.ATTR_CODE).length); + assertEquals(1, _attrs.getAttributes(Constants.ATTR_DEPRECATED).length); + assertEquals(1, _attrs.getAttributes(Constants.ATTR_SYNTHETIC).length); + assertTrue(attr1 == _attrs.getAttributes(Constants.ATTR_DEPRECATED)[0]); + assertTrue(attr2 == _attrs.getAttributes(Constants.ATTR_SYNTHETIC)[0]); + + Attribute attr3 = _attrs.addAttribute(Constants.ATTR_DEPRECATED); + assertEquals(3, _attrs.getAttributes().length); + assertEquals(2, _attrs.getAttributes(Constants.ATTR_DEPRECATED).length); + } + + /** + * Test setting attributes. + */ + public void testSetAttributes() { + Attribute attr1 = _attrs.addAttribute(Constants.ATTR_DEPRECATED); + Attribute attr2 = _attrs.addAttribute(Constants.ATTR_SYNTHETIC); + + _attrs2.setAttributes(_attrs.getAttributes()); + assertEquals(2, _attrs2.getAttributes().length); + assertEquals(Constants.ATTR_DEPRECATED, + _attrs2.getAttribute(Constants.ATTR_DEPRECATED).getName()); + assertEquals(Constants.ATTR_SYNTHETIC, + _attrs2.getAttribute(Constants.ATTR_SYNTHETIC).getName()); + assertTrue(attr1 != _attrs2.getAttribute(Constants.ATTR_DEPRECATED)); + assertTrue(attr2 != _attrs2.getAttribute(Constants.ATTR_SYNTHETIC)); + + Attribute attr3 = _attrs.addAttribute(Constants.ATTR_SOURCE); + _attrs2.setAttributes(new Attribute[] { attr3 }); + assertEquals(1, _attrs2.getAttributes().length); + assertEquals(Constants.ATTR_SOURCE, _attrs2.getAttributes()[0].getName()); + } + + /** + * Test adding attributs. + */ + public void testAddAttributes() { + SourceFile attr1 = (SourceFile) _attrs.addAttribute(Constants.ATTR_SOURCE); + assertEquals(attr1.getName(), Constants.ATTR_SOURCE); + assertTrue(attr1 != _attrs.addAttribute(Constants.ATTR_SOURCE)); + assertEquals(2, _attrs.getAttributes(Constants.ATTR_SOURCE).length); + attr1.setFile("foo"); + + SourceFile attr2 = (SourceFile) _attrs2.addAttribute(attr1); + assertTrue(attr1 != attr2); + assertEquals("foo", attr2.getFileName()); + } + + /** + * Test clearing attributes. + */ + public void testClear() { + _attrs.clearAttributes(); + + Attribute attr1 = _attrs.addAttribute(Constants.ATTR_SYNTHETIC); + Attribute attr2 = _attrs.addAttribute(Constants.ATTR_DEPRECATED); + + assertTrue(attr1.isValid()); + assertTrue(attr2.isValid()); + + assertEquals(2, _attrs.getAttributes().length); + _attrs.clearAttributes(); + assertEquals(0, _attrs.getAttributes().length); + + // cleared classes should be invalid + assertTrue(!attr1.isValid()); + assertTrue(!attr2.isValid()); + } + + /** + * Test removing a class. + */ + public void testRemoveAttribute() { + assertTrue(!_attrs.removeAttribute((String) null)); + assertTrue(!_attrs.removeAttribute((Attribute) null)); + assertTrue(!_attrs.removeAttribute(Constants.ATTR_SYNTHETIC)); + assertTrue(!_attrs.removeAttribute(_attrs2.addAttribute( + Constants.ATTR_SYNTHETIC))); + + Attribute attr1 = _attrs.addAttribute(Constants.ATTR_SYNTHETIC); + Attribute attr2 = _attrs.addAttribute(Constants.ATTR_DEPRECATED); + + assertTrue(attr1.isValid()); + assertTrue(attr2.isValid()); + assertEquals(2, _attrs.getAttributes().length); + + assertTrue(_attrs.removeAttribute(attr1.getName())); + assertEquals(1, _attrs.getAttributes().length); + + assertTrue(!_attrs.removeAttribute(_attrs2.addAttribute(attr2.getName()))); + assertTrue(_attrs.removeAttribute(attr2)); + assertEquals(0, _attrs.getAttributes().length); + + assertTrue(!attr1.isValid()); + assertTrue(!attr2.isValid()); + } + + public static Test suite() { + return new TestSuite(TestAttributes.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } +} diff --git a/serp/src/test/java/serp/bytecode/TestBCClass.java b/serp/src/test/java/serp/bytecode/TestBCClass.java new file mode 100755 index 000000000..229ab2a39 --- /dev/null +++ b/serp/src/test/java/serp/bytecode/TestBCClass.java @@ -0,0 +1,269 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import junit.framework.*; + +import junit.textui.*; + +import java.io.*; + + +/** + *

Tests the {@link BCClass} type.

+ * + * UNFINISHED + * + * @author Abe White + */ +public class TestBCClass extends TestCase { + private Project _project = new Project(); + private BCClass _bc = _project.loadClass(Integer.class); + + public TestBCClass(String test) { + super(test); + } + + /** + * Test accessing the class project. + */ + public void testProject() { + assertTrue(_project == _bc.getProject()); + assertTrue(_bc.isValid()); + assertTrue(_project.removeClass(_bc)); + assertTrue(!_bc.isValid()); + } + + /** + * Test read/write. + */ + public void testReadWrite() throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + InputStream in = Integer.class.getResourceAsStream("Integer.class"); + int ch; + + while ((ch = in.read()) != -1) + out.write(ch); + + byte[] origBytes = out.toByteArray(); + byte[] bytes = _bc.toByteArray(); + assertEquals(origBytes.length, bytes.length); + + for (int i = 0; i < origBytes.length; i++) + assertEquals(origBytes[i], bytes[i]); + + // also test copying one class to another + BCClass bc2 = new Project().loadClass(_bc); + bytes = bc2.toByteArray(); + assertEquals(origBytes.length, bytes.length); + + for (int i = 0; i < origBytes.length; i++) + assertEquals(origBytes[i], bytes[i]); + } + + /** + * Test basics -- magic number, major version, minor version. + */ + public void testBasics() { + assertEquals(Constants.VALID_MAGIC, _bc.getMagic()); + _bc.setMagic(1); + assertEquals(1, _bc.getMagic()); + + assertTrue(Constants.MAJOR_VERSION <= _bc.getMajorVersion()); + _bc.setMajorVersion(1); + assertEquals(1, _bc.getMajorVersion()); + + _bc.setMinorVersion(1); + assertEquals(1, _bc.getMinorVersion()); + + assertTrue(!_bc.isPrimitive()); + assertTrue(!_bc.isArray()); + assertNull(_bc.getComponentName()); + assertNull(_bc.getComponentType()); + assertNull(_bc.getComponentBC()); + } + + /** + * Test access flags. + */ + public void testAccessFlags() { + assertEquals(Constants.ACCESS_PUBLIC | Constants.ACCESS_SUPER | + Constants.ACCESS_FINAL, _bc.getAccessFlags()); + assertTrue(_bc.isPublic()); + assertTrue(!_bc.isPackage()); + assertTrue(_bc.isFinal()); + assertTrue(!_bc.isInterface()); + assertTrue(!_bc.isAbstract()); + + _bc.setAccessFlags(Constants.ACCESS_ABSTRACT | + Constants.ACCESS_INTERFACE); + assertTrue(!_bc.isPublic()); + assertTrue(_bc.isPackage()); + assertTrue(!_bc.isFinal()); + assertTrue(_bc.isInterface()); + assertTrue(_bc.isAbstract()); + + _bc.setAccessFlags(Constants.ACCESS_PUBLIC | Constants.ACCESS_SUPER | + Constants.ACCESS_FINAL); + + _bc.makePackage(); + assertTrue(!_bc.isPublic()); + assertTrue(_bc.isPackage()); + _bc.makePublic(); + assertTrue(_bc.isPublic()); + assertTrue(!_bc.isPackage()); + + _bc.setFinal(false); + assertTrue(!_bc.isFinal()); + _bc.setFinal(true); + assertTrue(_bc.isFinal()); + + _bc.setAbstract(true); + assertTrue(_bc.isAbstract()); + _bc.setAbstract(false); + assertTrue(!_bc.isAbstract()); + + _bc.setInterface(true); + assertTrue(_bc.isInterface()); + assertTrue(_bc.isAbstract()); + _bc.setInterface(false); + assertTrue(!_bc.isInterface()); + } + + /** + * Test class type operations. + */ + public void testType() { + assertEquals(Integer.class.getName(), _bc.getName()); + assertEquals("java.lang", _bc.getPackageName()); + assertEquals("Integer", _bc.getClassName()); + assertEquals(Integer.class, _bc.getType()); + + _bc.setName("serp.Foo"); + assertEquals("serp.Foo", _bc.getName()); + } + + /** + * Test superclass operations. + */ + public void testSuperclass() { + assertEquals(Number.class.getName(), _bc.getSuperclassName()); + assertEquals(Number.class, _bc.getSuperclassType()); + assertEquals(Number.class.getName(), _bc.getSuperclassBC().getName()); + assertEquals(null, + _bc.getSuperclassBC().getSuperclassBC().getSuperclassBC()); + + _bc.setSuperclass(String.class); + assertEquals(String.class.getName(), _bc.getSuperclassName()); + + _bc.setSuperclass((BCClass) null); + _bc.setSuperclass((Class) null); + _bc.setSuperclass((String) null); + assertNull(_bc.getSuperclassName()); + assertNull(_bc.getSuperclassType()); + assertNull(_bc.getSuperclassBC()); + + assertEquals(0, _bc.getSuperclassIndex()); + } + + /** + * Test operations on interfaces. + */ + public void testInterfaces() { + Object[] interfaces = _bc.getInterfaceNames(); + assertEquals(2, interfaces.length); + assertEquals(Comparable.class.getName(), interfaces[0]); + assertEquals(Serializable.class.getName(), interfaces[1]); + + interfaces = _bc.getInterfaceTypes(); + assertEquals(2, interfaces.length); + assertEquals(Comparable.class, interfaces[0]); + assertEquals(Serializable.class, interfaces[1]); + + interfaces = _bc.getInterfaceBCs(); + assertEquals(2, interfaces.length); + assertEquals(Comparable.class, ((BCClass) interfaces[0]).getType()); + assertEquals(Serializable.class, ((BCClass) interfaces[1]).getType()); + + interfaces = _bc.getDeclaredInterfaceNames(); + assertEquals(1, interfaces.length); + assertEquals(Comparable.class.getName(), interfaces[0]); + + interfaces = _bc.getDeclaredInterfaceTypes(); + assertEquals(1, interfaces.length); + assertEquals(Comparable.class, interfaces[0]); + + interfaces = _bc.getDeclaredInterfaceBCs(); + assertEquals(1, interfaces.length); + assertEquals(Comparable.class, ((BCClass) interfaces[0]).getType()); + + assertTrue(_bc.isInstanceOf(Comparable.class.getName())); + assertTrue(_bc.isInstanceOf(Comparable.class)); + assertTrue(_bc.isInstanceOf(_project.loadClass(Comparable.class))); + assertTrue(_bc.isInstanceOf(Serializable.class)); + assertTrue(!_bc.isInstanceOf(Cloneable.class.getName())); + assertTrue(!_bc.isInstanceOf(Cloneable.class)); + assertTrue(!_bc.isInstanceOf(_project.loadClass(Cloneable.class))); + + _bc.clearDeclaredInterfaces(); + interfaces = _bc.getInterfaceNames(); + assertEquals(1, interfaces.length); + assertEquals(Serializable.class.getName(), interfaces[0]); + + interfaces = _bc.getDeclaredInterfaceNames(); + assertEquals(0, interfaces.length); + + _bc.declareInterface(Comparable.class.getName()); + assertTrue(_bc.isInstanceOf(Comparable.class.getName())); + interfaces = _bc.getDeclaredInterfaceNames(); + assertEquals(1, interfaces.length); + assertEquals(Comparable.class.getName(), interfaces[0]); + + assertTrue(!_bc.removeDeclaredInterface(Serializable.class)); + assertTrue(_bc.removeDeclaredInterface(Comparable.class.getName())); + interfaces = _bc.getDeclaredInterfaceNames(); + assertEquals(0, interfaces.length); + + _bc.declareInterface(Comparable.class); + assertTrue(_bc.isInstanceOf(Comparable.class)); + interfaces = _bc.getDeclaredInterfaceTypes(); + assertEquals(1, interfaces.length); + assertEquals(Comparable.class, interfaces[0]); + + assertTrue(_bc.removeDeclaredInterface(Comparable.class)); + interfaces = _bc.getDeclaredInterfaceNames(); + assertEquals(0, interfaces.length); + + _bc.declareInterface(_project.loadClass(Comparable.class)); + assertTrue(_bc.isInstanceOf(_project.loadClass(Comparable.class))); + interfaces = _bc.getDeclaredInterfaceBCs(); + assertEquals(1, interfaces.length); + assertEquals(Comparable.class, ((BCClass) interfaces[0]).getType()); + + assertTrue(_bc.removeDeclaredInterface(_project.loadClass( + Comparable.class))); + interfaces = _bc.getDeclaredInterfaceNames(); + assertEquals(0, interfaces.length); + } + + public static Test suite() { + return new TestSuite(TestBCClass.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } +} diff --git a/serp/src/test/java/serp/bytecode/TestCode.java b/serp/src/test/java/serp/bytecode/TestCode.java new file mode 100755 index 000000000..9c54b00d4 --- /dev/null +++ b/serp/src/test/java/serp/bytecode/TestCode.java @@ -0,0 +1,142 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import junit.framework.*; + +import junit.textui.*; + + +/** + *

Tests the {@link Code} class.

+ * + * @author Eric Lindauer + */ +public class TestCode extends TestCase { + public TestCode(String test) { + super(test); + } + + /** + * Test that removing Instructions from a Code block + * removes the correct Instructions. + */ + public void testRemove() { + Code code = new Code(); + JumpInstruction go2 = code.go2(); + Instruction first = code.nop(); + Instruction second = code.nop(); + Instruction third = code.nop(); + Instruction fourth = code.nop(); + go2.setTarget(second); + + // remove 'second' after a next() call + code.beforeFirst(); + code.next(); + code.next(); + code.next(); + code.remove(); + assertEquals(third, code.next()); + assertEquals(third, go2.getTarget()); + code.beforeFirst(); + assertEquals(go2, code.next()); + assertEquals(first, code.next()); + assertEquals(third, code.next()); + assertEquals(fourth, code.next()); + + // remove 'third' after a previous() call + code.beforeFirst(); + code.next(); + code.next(); + code.next(); + code.next(); + code.previous(); + code.previous(); + code.remove(); + assertEquals(fourth, go2.getTarget()); + assertEquals(fourth, code.next()); + + assertTrue(!code.hasNext()); + assertEquals(fourth, code.previous()); + code.remove(); + code.afterLast(); + assertEquals(code.previous(), go2.getTarget()); + assertEquals(first, code.previous()); + } + + /** + * Test that instruction indexes work correctly. + */ + public void testIndexes() { + Code code = new Code(); + assertEquals(0, code.nextIndex()); + assertEquals(-1, code.previousIndex()); + + Instruction first = code.nop(); + assertEquals(1, code.nextIndex()); + assertEquals(0, code.previousIndex()); + + Instruction second = code.nop(); + assertEquals(2, code.nextIndex()); + assertEquals(1, code.previousIndex()); + code.previous(); + assertEquals(1, code.nextIndex()); + assertEquals(0, code.previousIndex()); + code.next(); + assertEquals(2, code.nextIndex()); + assertEquals(1, code.previousIndex()); + + Instruction third = code.nop(); + assertEquals(3, code.nextIndex()); + assertEquals(2, code.previousIndex()); + + code.afterLast(); + assertEquals(3, code.nextIndex()); + assertEquals(2, code.previousIndex()); + + code.beforeFirst(); + assertEquals(0, code.nextIndex()); + assertEquals(-1, code.previousIndex()); + + code.before(first); + assertEquals(0, code.nextIndex()); + assertEquals(-1, code.previousIndex()); + code.before(second); + assertEquals(1, code.nextIndex()); + assertEquals(0, code.previousIndex()); + code.before(third); + assertEquals(2, code.nextIndex()); + assertEquals(1, code.previousIndex()); + + code.after(first); + assertEquals(1, code.nextIndex()); + assertEquals(0, code.previousIndex()); + code.after(second); + assertEquals(2, code.nextIndex()); + assertEquals(1, code.previousIndex()); + code.after(third); + assertEquals(3, code.nextIndex()); + assertEquals(2, code.previousIndex()); + } + + public static Test suite() { + return new TestSuite(TestCode.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } +} diff --git a/serp/src/test/java/serp/bytecode/TestConstantInstruction.java b/serp/src/test/java/serp/bytecode/TestConstantInstruction.java new file mode 100755 index 000000000..9ab0ea917 --- /dev/null +++ b/serp/src/test/java/serp/bytecode/TestConstantInstruction.java @@ -0,0 +1,125 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import junit.framework.*; + +import junit.textui.*; + + +/** + *

Tests the {@link ConstantInstruction} type.

+ * + * @author Abe White + */ +public class TestConstantInstruction extends TestCase { + private ConstantInstruction _const = new Code().constant(); + + public TestConstantInstruction(String test) { + super(test); + } + + /** + * Test that the type instruction returns its type correctly. + */ + public void testGetType() { + assertNull(_const.getType()); + assertEquals(int.class, _const.setValue(0).getType()); + assertEquals(int.class, _const.setValue(2 << 3).getType()); + assertEquals(int.class, _const.setValue(2 << 7).getType()); + assertEquals(int.class, _const.setValue(2 << 15).getType()); + assertEquals(long.class, _const.setValue(0L).getType()); + assertEquals(long.class, _const.setValue(1000L).getType()); + assertEquals(float.class, _const.setValue(0F).getType()); + assertEquals(float.class, _const.setValue(1000F).getType()); + assertEquals(double.class, _const.setValue(0D).getType()); + assertEquals(double.class, _const.setValue(1000D).getType()); + assertEquals(Object.class, _const.setValue((Object) null).getType()); + assertEquals(String.class, _const.setValue("foo").getType()); + assertEquals(int.class, _const.setValue(true).getType()); + assertEquals(int.class, _const.setValue((short) 0).getType()); + assertEquals(int.class, _const.setValue('a').getType()); + assertEquals(Class.class, _const.setValue(String.class).getType()); + } + + /** + * Test that the value is stored correctly. + */ + public void testGetValue() { + assertNull(_const.getValue()); + assertEquals(0, _const.setValue(0).getIntValue()); + assertEquals(-1, _const.setValue(-1).getIntValue()); + assertEquals(2 << 3, _const.setValue(2 << 3).getIntValue()); + assertEquals(2 << 7, _const.setValue(2 << 7).getIntValue()); + assertEquals(2 << 15, _const.setValue(2 << 15).getIntValue()); + assertEquals(0L, _const.setValue(0L).getLongValue()); + assertEquals(1000L, _const.setValue(1000L).getLongValue()); + assertEquals(0F, _const.setValue(0F).getFloatValue(), .001); + assertEquals(1000F, _const.setValue(1000F).getFloatValue(), .001); + assertEquals(0D, _const.setValue(0D).getDoubleValue(), .001); + assertEquals(1000D, _const.setValue(1000D).getDoubleValue(), .001); + assertNull(_const.setValue((Object) null).getValue()); + assertEquals("foo", _const.setValue("foo").getStringValue()); + assertEquals(1, _const.setValue(true).getIntValue()); + assertEquals(0, _const.setValue((short) 0).getIntValue()); + assertEquals((int) 'a', _const.setValue('a').getIntValue()); + assertEquals(String.class.getName(), + _const.setValue(String.class).getClassNameValue()); + } + + /** + * Test the the opcode is morphed correctly when the value is set. + */ + public void testOpcodeMorph() { + assertEquals(Constants.NOP, _const.getOpcode()); + assertEquals(Constants.ICONSTM1, _const.setValue(-1).getOpcode()); + assertEquals(Constants.ICONST0, _const.setValue(0).getOpcode()); + assertEquals(Constants.ICONST1, _const.setValue(1).getOpcode()); + assertEquals(Constants.ICONST2, _const.setValue(2).getOpcode()); + assertEquals(Constants.ICONST3, _const.setValue(3).getOpcode()); + assertEquals(Constants.ICONST4, _const.setValue(4).getOpcode()); + assertEquals(Constants.ICONST5, _const.setValue(5).getOpcode()); + assertEquals(Constants.BIPUSH, _const.setValue(2 << 3).getOpcode()); + assertEquals(Constants.SIPUSH, _const.setValue(2 << 7).getOpcode()); + assertEquals(Constants.LDC, _const.setValue(2 << 15).getOpcode()); + assertEquals(Constants.LCONST0, _const.setValue(0L).getOpcode()); + assertEquals(Constants.LCONST1, _const.setValue(1L).getOpcode()); + assertEquals(Constants.LDC2W, _const.setValue(1000L).getOpcode()); + assertEquals(Constants.FCONST2, _const.setValue(2F).getOpcode()); + assertEquals(Constants.FCONST1, _const.setValue(1F).getOpcode()); + assertEquals(Constants.FCONST0, _const.setValue(0F).getOpcode()); + assertEquals(Constants.LDC, _const.setValue(1000F).getOpcode()); + assertEquals(Constants.DCONST0, _const.setValue(0D).getOpcode()); + assertEquals(Constants.DCONST1, _const.setValue(1D).getOpcode()); + assertEquals(Constants.LDC2W, _const.setValue(2D).getOpcode()); + assertEquals(Constants.LDC2W, _const.setValue(1000D).getOpcode()); + assertEquals(Constants.LDC, _const.setValue("foo").getOpcode()); + assertEquals(Constants.ICONST1, _const.setValue(true).getOpcode()); + assertEquals(Constants.BIPUSH, _const.setValue('a').getOpcode()); + assertEquals(Constants.ICONST0, _const.setValue((short) 0).getOpcode()); + assertEquals(Constants.ACONSTNULL, + _const.setValue((Object) null).getOpcode()); + assertEquals(Constants.LDCW, _const.setValue(String.class).getOpcode()); + } + + public static Test suite() { + return new TestSuite(TestConstantInstruction.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } +} diff --git a/serp/src/test/java/serp/bytecode/TestConvertInstruction.java b/serp/src/test/java/serp/bytecode/TestConvertInstruction.java new file mode 100755 index 000000000..1434eaf66 --- /dev/null +++ b/serp/src/test/java/serp/bytecode/TestConvertInstruction.java @@ -0,0 +1,99 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import junit.framework.*; + +import junit.textui.*; + + +/** + *

Tests the {@link ConvertInstruction} type.

+ * + * @author Abe White + */ +public class TestConvertInstruction extends TestCase { + private Code _code = new Code(); + + public TestConvertInstruction(String test) { + super(test); + } + + /** + * Test that the opcode is morphed correctly when the types are set. + */ + public void testOpcodeMorph() { + ConvertInstruction ins = _code.convert(); + assertEquals(Constants.NOP, ins.getOpcode()); + + ins.setFromType(int.class); + assertEquals(Constants.NOP, ins.getOpcode()); + assertEquals(int.class, ins.getFromType()); + assertNull(ins.getType()); + + ins.setType(int.class); + assertEquals(Constants.NOP, ins.getOpcode()); + assertEquals(int.class, ins.getFromType()); + assertEquals(int.class, ins.getType()); + + ins.setType(long.class); + assertEquals(Constants.I2L, ins.getOpcode()); + assertEquals(int.class, ins.getFromType()); + assertEquals(long.class, ins.getType()); + + ins.setType(float.class); + assertEquals(Constants.I2F, ins.getOpcode()); + assertEquals(int.class, ins.getFromType()); + assertEquals(float.class, ins.getType()); + + ins.setType(double.class); + assertEquals(Constants.I2D, ins.getOpcode()); + assertEquals(int.class, ins.getFromType()); + assertEquals(double.class, ins.getType()); + + ins.setFromType(long.class); + assertEquals(Constants.L2D, ins.getOpcode()); + assertEquals(long.class, ins.getFromType()); + assertEquals(double.class, ins.getType()); + + ins.setType(long.class); + assertEquals(Constants.NOP, ins.getOpcode()); + assertEquals(long.class, ins.getFromType()); + assertEquals(long.class, ins.getType()); + + ins.setType(int.class); + assertEquals(Constants.L2I, ins.getOpcode()); + assertEquals(long.class, ins.getFromType()); + assertEquals(int.class, ins.getType()); + + ins.setType(String.class); + assertEquals(Constants.L2I, ins.getOpcode()); + + ins.setType((Class) null); + assertEquals(Constants.NOP, ins.getOpcode()); + + ins.setType(float.class); + assertEquals(Constants.L2F, ins.getOpcode()); + } + + public static Test suite() { + return new TestSuite(TestConvertInstruction.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } +} diff --git a/serp/src/test/java/serp/bytecode/TestLoadInstruction.java b/serp/src/test/java/serp/bytecode/TestLoadInstruction.java new file mode 100755 index 000000000..44ac535a7 --- /dev/null +++ b/serp/src/test/java/serp/bytecode/TestLoadInstruction.java @@ -0,0 +1,179 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import junit.framework.*; + +import junit.textui.*; + + +/** + *

Tests the {@link LoadInstruction} type.

+ * + * @author Abe White + */ +public class TestLoadInstruction extends TestCase { + private Code _code = new Code(); + + public TestLoadInstruction(String test) { + super(test); + } + + /** + * Test that the instruction intitializes correctly when generated. + */ + public void testInitialize() { + assertEquals(Constants.NOP, _code.xload().getOpcode()); + assertNull(_code.xload().getType()); + assertEquals(Constants.NOP, _code.iload().getOpcode()); + assertEquals(int.class, _code.iload().getType()); + assertEquals(Constants.NOP, _code.lload().getOpcode()); + assertEquals(long.class, _code.lload().getType()); + assertEquals(Constants.NOP, _code.fload().getOpcode()); + assertEquals(float.class, _code.fload().getType()); + assertEquals(Constants.NOP, _code.dload().getOpcode()); + assertEquals(double.class, _code.dload().getType()); + assertEquals(Constants.NOP, _code.aload().getOpcode()); + assertEquals(Object.class, _code.aload().getType()); + } + + /** + * Test that the instruction returns its type correctly. + */ + public void testGetType() { + LoadInstruction ins = _code.xload(); + assertNull(ins.getType()); + assertEquals(-1, ins.getLocal()); + + ins = _code.iload(); + assertEquals(int.class, ins.getType()); + assertEquals(int.class, ins.setLocal(1).getType()); + assertEquals(int.class, ins.setLocal(2).getType()); + assertEquals(int.class, ins.setLocal(3).getType()); + assertEquals(int.class, ins.setLocal(100).getType()); + + ins = _code.lload(); + assertEquals(long.class, ins.getType()); + assertEquals(long.class, ins.setLocal(1).getType()); + assertEquals(long.class, ins.setLocal(2).getType()); + assertEquals(long.class, ins.setLocal(3).getType()); + assertEquals(long.class, ins.setLocal(100).getType()); + + ins = _code.fload(); + assertEquals(float.class, ins.getType()); + assertEquals(float.class, ins.setLocal(1).getType()); + assertEquals(float.class, ins.setLocal(2).getType()); + assertEquals(float.class, ins.setLocal(3).getType()); + assertEquals(float.class, ins.setLocal(100).getType()); + + ins = _code.dload(); + assertEquals(double.class, ins.getType()); + assertEquals(double.class, ins.setLocal(1).getType()); + assertEquals(double.class, ins.setLocal(2).getType()); + assertEquals(double.class, ins.setLocal(3).getType()); + assertEquals(double.class, ins.setLocal(100).getType()); + + ins = _code.aload(); + assertEquals(Object.class, ins.getType()); + assertEquals(Object.class, ins.setLocal(1).getType()); + assertEquals(Object.class, ins.setLocal(2).getType()); + assertEquals(Object.class, ins.setLocal(3).getType()); + assertEquals(Object.class, ins.setLocal(100).getType()); + } + + /** + * Test that the opcode is morphed correctly when the type and local + * of the instruction are changed. + */ + public void testOpcodeMorph() { + LoadInstruction ins = _code.xload(); + + assertEquals(Constants.NOP, ins.getOpcode()); + assertEquals(Constants.NOP, ins.setType(int.class).getOpcode()); + assertEquals(Constants.ILOAD, ins.setLocal(10).getOpcode()); + assertEquals(Constants.ILOAD, ins.setType(boolean.class).getOpcode()); + assertEquals(Constants.ILOAD, ins.setType(byte.class).getOpcode()); + assertEquals(Constants.ILOAD, ins.setType(char.class).getOpcode()); + assertEquals(Constants.ILOAD, ins.setType(short.class).getOpcode()); + assertEquals(Constants.ILOAD0, ins.setLocal(0).getOpcode()); + assertEquals(0, ins.getLocal()); + assertEquals(Constants.ILOAD1, ins.setLocal(1).getOpcode()); + assertEquals(1, ins.getLocal()); + assertEquals(Constants.ILOAD2, ins.setLocal(2).getOpcode()); + assertEquals(2, ins.getLocal()); + assertEquals(Constants.ILOAD3, ins.setLocal(3).getOpcode()); + assertEquals(3, ins.getLocal()); + assertEquals(Constants.ILOAD, ins.setLocal(4).getOpcode()); + assertEquals(4, ins.getLocal()); + + assertEquals(Constants.LLOAD, ins.setType(long.class).getOpcode()); + assertEquals(Constants.LLOAD0, ins.setLocal(0).getOpcode()); + assertEquals(0, ins.getLocal()); + assertEquals(Constants.LLOAD1, ins.setLocal(1).getOpcode()); + assertEquals(1, ins.getLocal()); + assertEquals(Constants.LLOAD2, ins.setLocal(2).getOpcode()); + assertEquals(2, ins.getLocal()); + assertEquals(Constants.LLOAD3, ins.setLocal(3).getOpcode()); + assertEquals(3, ins.getLocal()); + assertEquals(Constants.LLOAD, ins.setLocal(4).getOpcode()); + assertEquals(4, ins.getLocal()); + + assertEquals(Constants.FLOAD, ins.setType(float.class).getOpcode()); + assertEquals(Constants.FLOAD0, ins.setLocal(0).getOpcode()); + assertEquals(0, ins.getLocal()); + assertEquals(Constants.FLOAD1, ins.setLocal(1).getOpcode()); + assertEquals(1, ins.getLocal()); + assertEquals(Constants.FLOAD2, ins.setLocal(2).getOpcode()); + assertEquals(2, ins.getLocal()); + assertEquals(Constants.FLOAD3, ins.setLocal(3).getOpcode()); + assertEquals(3, ins.getLocal()); + assertEquals(Constants.FLOAD, ins.setLocal(4).getOpcode()); + assertEquals(4, ins.getLocal()); + + assertEquals(Constants.DLOAD, ins.setType(double.class).getOpcode()); + assertEquals(Constants.DLOAD0, ins.setLocal(0).getOpcode()); + assertEquals(0, ins.getLocal()); + assertEquals(Constants.DLOAD1, ins.setLocal(1).getOpcode()); + assertEquals(1, ins.getLocal()); + assertEquals(Constants.DLOAD2, ins.setLocal(2).getOpcode()); + assertEquals(2, ins.getLocal()); + assertEquals(Constants.DLOAD3, ins.setLocal(3).getOpcode()); + assertEquals(3, ins.getLocal()); + assertEquals(Constants.DLOAD, ins.setLocal(4).getOpcode()); + assertEquals(4, ins.getLocal()); + + assertEquals(Constants.ALOAD, ins.setType(Object.class).getOpcode()); + assertEquals(Constants.ALOAD, ins.setType(String.class).getOpcode()); + assertEquals(Constants.ALOAD0, ins.setLocal(0).getOpcode()); + assertEquals(0, ins.getLocal()); + assertEquals(Constants.ALOAD1, ins.setLocal(1).getOpcode()); + assertEquals(1, ins.getLocal()); + assertEquals(Constants.ALOAD2, ins.setLocal(2).getOpcode()); + assertEquals(2, ins.getLocal()); + assertEquals(Constants.ALOAD3, ins.setLocal(3).getOpcode()); + assertEquals(3, ins.getLocal()); + assertEquals(Constants.ALOAD, ins.setLocal(4).getOpcode()); + assertEquals(4, ins.getLocal()); + } + + public static Test suite() { + return new TestSuite(TestLoadInstruction.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } +} diff --git a/serp/src/test/java/serp/bytecode/TestMathInstruction.java b/serp/src/test/java/serp/bytecode/TestMathInstruction.java new file mode 100755 index 000000000..59b61b9b7 --- /dev/null +++ b/serp/src/test/java/serp/bytecode/TestMathInstruction.java @@ -0,0 +1,347 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import junit.framework.*; + +import junit.textui.*; + + +/** + *

Tests the {@link MathInstruction} type.

+ * + * @author Abe White + */ +public class TestMathInstruction extends TestCase { + private Code _code = new Code(); + + public TestMathInstruction(String test) { + super(test); + } + + /** + * Test that the instruction intitializes correctly when generated. + */ + public void testInitialize() { + assertEquals(Constants.NOP, _code.math().getOpcode()); + assertEquals(Constants.NOP, _code.xadd().getOpcode()); + assertEquals(Constants.IADD, _code.iadd().getOpcode()); + assertEquals(Constants.LADD, _code.ladd().getOpcode()); + assertEquals(Constants.FADD, _code.fadd().getOpcode()); + assertEquals(Constants.DADD, _code.dadd().getOpcode()); + assertEquals(Constants.NOP, _code.xsub().getOpcode()); + assertEquals(Constants.ISUB, _code.isub().getOpcode()); + assertEquals(Constants.LSUB, _code.lsub().getOpcode()); + assertEquals(Constants.FSUB, _code.fsub().getOpcode()); + assertEquals(Constants.DSUB, _code.dsub().getOpcode()); + assertEquals(Constants.NOP, _code.xmul().getOpcode()); + assertEquals(Constants.IMUL, _code.imul().getOpcode()); + assertEquals(Constants.LMUL, _code.lmul().getOpcode()); + assertEquals(Constants.FMUL, _code.fmul().getOpcode()); + assertEquals(Constants.DMUL, _code.dmul().getOpcode()); + assertEquals(Constants.NOP, _code.xdiv().getOpcode()); + assertEquals(Constants.IDIV, _code.idiv().getOpcode()); + assertEquals(Constants.LDIV, _code.ldiv().getOpcode()); + assertEquals(Constants.FDIV, _code.fdiv().getOpcode()); + assertEquals(Constants.DDIV, _code.ddiv().getOpcode()); + assertEquals(Constants.NOP, _code.xrem().getOpcode()); + assertEquals(Constants.IREM, _code.irem().getOpcode()); + assertEquals(Constants.LREM, _code.lrem().getOpcode()); + assertEquals(Constants.FREM, _code.frem().getOpcode()); + assertEquals(Constants.DREM, _code.drem().getOpcode()); + assertEquals(Constants.NOP, _code.xneg().getOpcode()); + assertEquals(Constants.INEG, _code.ineg().getOpcode()); + assertEquals(Constants.LNEG, _code.lneg().getOpcode()); + assertEquals(Constants.FNEG, _code.fneg().getOpcode()); + assertEquals(Constants.DNEG, _code.dneg().getOpcode()); + assertEquals(Constants.NOP, _code.xshl().getOpcode()); + assertEquals(Constants.ISHL, _code.ishl().getOpcode()); + assertEquals(Constants.LSHL, _code.lshl().getOpcode()); + assertEquals(Constants.NOP, _code.xshr().getOpcode()); + assertEquals(Constants.ISHR, _code.ishr().getOpcode()); + assertEquals(Constants.LSHR, _code.lshr().getOpcode()); + assertEquals(Constants.NOP, _code.xushr().getOpcode()); + assertEquals(Constants.IUSHR, _code.iushr().getOpcode()); + assertEquals(Constants.LUSHR, _code.lushr().getOpcode()); + assertEquals(Constants.NOP, _code.xand().getOpcode()); + assertEquals(Constants.IAND, _code.iand().getOpcode()); + assertEquals(Constants.LAND, _code.land().getOpcode()); + assertEquals(Constants.NOP, _code.xor().getOpcode()); + assertEquals(Constants.IOR, _code.ior().getOpcode()); + assertEquals(Constants.LOR, _code.lor().getOpcode()); + assertEquals(Constants.NOP, _code.xxor().getOpcode()); + assertEquals(Constants.IXOR, _code.ixor().getOpcode()); + assertEquals(Constants.LXOR, _code.lxor().getOpcode()); + } + + /** + * Test that the instruction returns its type correctly. + */ + public void testGetType() { + MathInstruction ins = _code.math(); + assertNull(ins.getType()); + assertEquals(-1, ins.getOperation()); + + ins = _code.xadd(); + assertNull(ins.getType()); + assertEquals(Constants.MATH_ADD, ins.getOperation()); + ins = _code.iadd(); + assertEquals(int.class, ins.getType()); + assertEquals(Constants.MATH_ADD, ins.getOperation()); + ins = _code.ladd(); + assertEquals(long.class, ins.getType()); + assertEquals(Constants.MATH_ADD, ins.getOperation()); + ins = _code.fadd(); + assertEquals(float.class, ins.getType()); + assertEquals(Constants.MATH_ADD, ins.getOperation()); + ins = _code.dadd(); + assertEquals(double.class, ins.getType()); + assertEquals(Constants.MATH_ADD, ins.getOperation()); + + ins = _code.xsub(); + assertNull(ins.getType()); + assertEquals(Constants.MATH_SUB, ins.getOperation()); + ins = _code.isub(); + assertEquals(int.class, ins.getType()); + assertEquals(Constants.MATH_SUB, ins.getOperation()); + ins = _code.lsub(); + assertEquals(long.class, ins.getType()); + assertEquals(Constants.MATH_SUB, ins.getOperation()); + ins = _code.fsub(); + assertEquals(float.class, ins.getType()); + assertEquals(Constants.MATH_SUB, ins.getOperation()); + ins = _code.dsub(); + assertEquals(double.class, ins.getType()); + assertEquals(Constants.MATH_SUB, ins.getOperation()); + + ins = _code.xmul(); + assertNull(ins.getType()); + assertEquals(Constants.MATH_MUL, ins.getOperation()); + ins = _code.imul(); + assertEquals(int.class, ins.getType()); + assertEquals(Constants.MATH_MUL, ins.getOperation()); + ins = _code.lmul(); + assertEquals(long.class, ins.getType()); + assertEquals(Constants.MATH_MUL, ins.getOperation()); + ins = _code.fmul(); + assertEquals(float.class, ins.getType()); + assertEquals(Constants.MATH_MUL, ins.getOperation()); + ins = _code.dmul(); + assertEquals(double.class, ins.getType()); + assertEquals(Constants.MATH_MUL, ins.getOperation()); + + ins = _code.xdiv(); + assertNull(ins.getType()); + assertEquals(Constants.MATH_DIV, ins.getOperation()); + ins = _code.idiv(); + assertEquals(int.class, ins.getType()); + assertEquals(Constants.MATH_DIV, ins.getOperation()); + ins = _code.ldiv(); + assertEquals(long.class, ins.getType()); + assertEquals(Constants.MATH_DIV, ins.getOperation()); + ins = _code.fdiv(); + assertEquals(float.class, ins.getType()); + assertEquals(Constants.MATH_DIV, ins.getOperation()); + ins = _code.ddiv(); + assertEquals(double.class, ins.getType()); + assertEquals(Constants.MATH_DIV, ins.getOperation()); + + ins = _code.xrem(); + assertNull(ins.getType()); + assertEquals(Constants.MATH_REM, ins.getOperation()); + ins = _code.irem(); + assertEquals(int.class, ins.getType()); + assertEquals(Constants.MATH_REM, ins.getOperation()); + ins = _code.lrem(); + assertEquals(long.class, ins.getType()); + assertEquals(Constants.MATH_REM, ins.getOperation()); + ins = _code.frem(); + assertEquals(float.class, ins.getType()); + assertEquals(Constants.MATH_REM, ins.getOperation()); + ins = _code.drem(); + assertEquals(double.class, ins.getType()); + assertEquals(Constants.MATH_REM, ins.getOperation()); + + ins = _code.xneg(); + assertNull(ins.getType()); + assertEquals(Constants.MATH_NEG, ins.getOperation()); + ins = _code.ineg(); + assertEquals(int.class, ins.getType()); + assertEquals(Constants.MATH_NEG, ins.getOperation()); + ins = _code.lneg(); + assertEquals(long.class, ins.getType()); + assertEquals(Constants.MATH_NEG, ins.getOperation()); + ins = _code.fneg(); + assertEquals(float.class, ins.getType()); + assertEquals(Constants.MATH_NEG, ins.getOperation()); + ins = _code.dneg(); + assertEquals(double.class, ins.getType()); + assertEquals(Constants.MATH_NEG, ins.getOperation()); + + ins = _code.xshl(); + assertNull(ins.getType()); + assertEquals(Constants.MATH_SHL, ins.getOperation()); + ins = _code.ishl(); + assertEquals(int.class, ins.getType()); + assertEquals(Constants.MATH_SHL, ins.getOperation()); + ins = _code.lshl(); + assertEquals(long.class, ins.getType()); + assertEquals(Constants.MATH_SHL, ins.getOperation()); + + ins = _code.xshr(); + assertNull(ins.getType()); + assertEquals(Constants.MATH_SHR, ins.getOperation()); + ins = _code.ishr(); + assertEquals(int.class, ins.getType()); + assertEquals(Constants.MATH_SHR, ins.getOperation()); + ins = _code.lshr(); + assertEquals(long.class, ins.getType()); + assertEquals(Constants.MATH_SHR, ins.getOperation()); + + ins = _code.xushr(); + assertNull(ins.getType()); + assertEquals(Constants.MATH_USHR, ins.getOperation()); + ins = _code.iushr(); + assertEquals(int.class, ins.getType()); + assertEquals(Constants.MATH_USHR, ins.getOperation()); + ins = _code.lushr(); + assertEquals(long.class, ins.getType()); + assertEquals(Constants.MATH_USHR, ins.getOperation()); + + ins = _code.xand(); + assertNull(ins.getType()); + assertEquals(Constants.MATH_AND, ins.getOperation()); + ins = _code.iand(); + assertEquals(int.class, ins.getType()); + assertEquals(Constants.MATH_AND, ins.getOperation()); + ins = _code.land(); + assertEquals(long.class, ins.getType()); + assertEquals(Constants.MATH_AND, ins.getOperation()); + + ins = _code.xor(); + assertNull(ins.getType()); + assertEquals(Constants.MATH_OR, ins.getOperation()); + ins = _code.ior(); + assertEquals(int.class, ins.getType()); + assertEquals(Constants.MATH_OR, ins.getOperation()); + ins = _code.lor(); + assertEquals(long.class, ins.getType()); + assertEquals(Constants.MATH_OR, ins.getOperation()); + + ins = _code.xxor(); + assertNull(ins.getType()); + assertEquals(Constants.MATH_XOR, ins.getOperation()); + ins = _code.ixor(); + assertEquals(int.class, ins.getType()); + assertEquals(Constants.MATH_XOR, ins.getOperation()); + ins = _code.lxor(); + assertEquals(long.class, ins.getType()); + assertEquals(Constants.MATH_XOR, ins.getOperation()); + } + + /** + * Test that the opcode is morphed correctly when the type and operation + * of the instruction are changed. + */ + public void testOpcodeMorph() { + MathInstruction math = _code.math(); + + math.setOperation(Constants.MATH_ADD); + assertEquals(Constants.NOP, math.setType((String) null).getOpcode()); + assertEquals(Constants.NOP, math.setType((Class) null).getOpcode()); + assertEquals(Constants.NOP, math.setType((BCClass) null).getOpcode()); + assertEquals(Constants.IADD, math.setType(int.class).getOpcode()); + assertEquals(Constants.LADD, math.setType(long.class).getOpcode()); + assertEquals(Constants.FADD, math.setType(float.class).getOpcode()); + assertEquals(Constants.DADD, math.setType(double.class).getOpcode()); + assertEquals(Constants.IADD, math.setType(boolean.class).getOpcode()); + assertEquals(Constants.IADD, math.setType(short.class).getOpcode()); + assertEquals(Constants.IADD, math.setType(char.class).getOpcode()); + + math.setOperation(Constants.MATH_SUB); + assertEquals(Constants.NOP, math.setType((Class) null).getOpcode()); + assertEquals(Constants.ISUB, math.setType(int.class).getOpcode()); + assertEquals(Constants.LSUB, math.setType(long.class).getOpcode()); + assertEquals(Constants.FSUB, math.setType(float.class).getOpcode()); + assertEquals(Constants.DSUB, math.setType(double.class).getOpcode()); + + math.setOperation(Constants.MATH_MUL); + assertEquals(Constants.NOP, math.setType((Class) null).getOpcode()); + assertEquals(Constants.IMUL, math.setType(int.class).getOpcode()); + assertEquals(Constants.LMUL, math.setType(long.class).getOpcode()); + assertEquals(Constants.FMUL, math.setType(float.class).getOpcode()); + assertEquals(Constants.DMUL, math.setType(double.class).getOpcode()); + + math.setOperation(Constants.MATH_DIV); + assertEquals(Constants.NOP, math.setType((Class) null).getOpcode()); + assertEquals(Constants.IDIV, math.setType(int.class).getOpcode()); + assertEquals(Constants.LDIV, math.setType(long.class).getOpcode()); + assertEquals(Constants.FDIV, math.setType(float.class).getOpcode()); + assertEquals(Constants.DDIV, math.setType(double.class).getOpcode()); + + math.setOperation(Constants.MATH_REM); + assertEquals(Constants.NOP, math.setType((Class) null).getOpcode()); + assertEquals(Constants.IREM, math.setType(int.class).getOpcode()); + assertEquals(Constants.LREM, math.setType(long.class).getOpcode()); + assertEquals(Constants.FREM, math.setType(float.class).getOpcode()); + assertEquals(Constants.DREM, math.setType(double.class).getOpcode()); + + math.setOperation(Constants.MATH_NEG); + assertEquals(Constants.NOP, math.setType((Class) null).getOpcode()); + assertEquals(Constants.INEG, math.setType(int.class).getOpcode()); + assertEquals(Constants.LNEG, math.setType(long.class).getOpcode()); + assertEquals(Constants.FNEG, math.setType(float.class).getOpcode()); + assertEquals(Constants.DNEG, math.setType(double.class).getOpcode()); + + math.setOperation(Constants.MATH_SHL); + assertEquals(Constants.NOP, math.setType((Class) null).getOpcode()); + assertEquals(Constants.ISHL, math.setType(int.class).getOpcode()); + assertEquals(Constants.LSHL, math.setType(long.class).getOpcode()); + + math.setOperation(Constants.MATH_SHR); + assertEquals(Constants.NOP, math.setType((Class) null).getOpcode()); + assertEquals(Constants.ISHR, math.setType(int.class).getOpcode()); + assertEquals(Constants.LSHR, math.setType(long.class).getOpcode()); + + math.setOperation(Constants.MATH_USHR); + assertEquals(Constants.NOP, math.setType((Class) null).getOpcode()); + assertEquals(Constants.IUSHR, math.setType(int.class).getOpcode()); + assertEquals(Constants.LUSHR, math.setType(long.class).getOpcode()); + + math.setOperation(Constants.MATH_AND); + assertEquals(Constants.NOP, math.setType((Class) null).getOpcode()); + assertEquals(Constants.IAND, math.setType(int.class).getOpcode()); + assertEquals(Constants.LAND, math.setType(long.class).getOpcode()); + + math.setOperation(Constants.MATH_OR); + assertEquals(Constants.NOP, math.setType((Class) null).getOpcode()); + assertEquals(Constants.IOR, math.setType(int.class).getOpcode()); + assertEquals(Constants.LOR, math.setType(long.class).getOpcode()); + + math.setOperation(Constants.MATH_XOR); + assertEquals(Constants.NOP, math.setType((Class) null).getOpcode()); + assertEquals(Constants.IXOR, math.setType(int.class).getOpcode()); + assertEquals(Constants.LXOR, math.setType(long.class).getOpcode()); + } + + public static Test suite() { + return new TestSuite(TestMathInstruction.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } +} diff --git a/serp/src/test/java/serp/bytecode/TestNameCache.java b/serp/src/test/java/serp/bytecode/TestNameCache.java new file mode 100755 index 000000000..f4830f75c --- /dev/null +++ b/serp/src/test/java/serp/bytecode/TestNameCache.java @@ -0,0 +1,217 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import junit.framework.*; + +import junit.textui.*; + + +/** + * Tests the {@link NameCache} utility type. + * + * @author Abe White + */ +public class TestNameCache extends TestCase { + private NameCache _cache = null; + + public TestNameCache(String test) { + super(test); + } + + public void setUp() { + _cache = new Project().getNameCache(); + } + + /** + * Tests that class names are correctly converted to internal form. + */ + public void testInternalForm() { + assertEquals("I", _cache.getInternalForm("int", true)); + assertEquals("I", _cache.getInternalForm("int", false)); + assertEquals("I", _cache.getInternalForm("I", true)); + assertEquals("I", _cache.getInternalForm("I", false)); + + assertEquals("B", _cache.getInternalForm("byte", true)); + assertEquals("C", _cache.getInternalForm("char", true)); + assertEquals("D", _cache.getInternalForm("double", true)); + assertEquals("F", _cache.getInternalForm("float", true)); + assertEquals("J", _cache.getInternalForm("long", true)); + assertEquals("S", _cache.getInternalForm("short", true)); + assertEquals("Z", _cache.getInternalForm("boolean", true)); + assertEquals("V", _cache.getInternalForm("void", true)); + + assertEquals("Ljava/lang/Object;", + _cache.getInternalForm(Object.class.getName(), true)); + assertEquals("java/lang/Object", + _cache.getInternalForm(Object.class.getName(), false)); + assertEquals("Ljava/lang/Object;", + _cache.getInternalForm("Ljava/lang/Object;", true)); + assertEquals("java/lang/Object", + _cache.getInternalForm("Ljava/lang/Object;", false)); + } + + /** + * Tests that array class names are correctly converted to internal form. + */ + public void testArrayInternalForm() { + assertEquals("[B", _cache.getInternalForm(byte[].class.getName(), false)); + assertEquals("[B", _cache.getInternalForm(byte[].class.getName(), true)); + assertEquals("[B", _cache.getInternalForm("byte[]", false)); + assertEquals("[B", _cache.getInternalForm("byte[]", true)); + + assertEquals("[[Ljava/lang/Object;", + _cache.getInternalForm(Object[][].class.getName(), false)); + assertEquals("[[Ljava/lang/Object;", + _cache.getInternalForm(Object[][].class.getName(), true)); + assertEquals("[[Ljava/lang/Object;", + _cache.getInternalForm("java.lang.Object[][]", false)); + assertEquals("[[Ljava/lang/Object;", + _cache.getInternalForm("java.lang.Object[][]", true)); + } + + /** + * Tests that class names are correctly converted to external form. + */ + public void testExternalForm() { + assertEquals("byte", _cache.getExternalForm("B", true)); + assertEquals("byte", _cache.getExternalForm("byte", true)); + assertEquals("byte", _cache.getExternalForm("B", false)); + assertEquals("byte", _cache.getExternalForm("byte", false)); + + assertEquals("byte", _cache.getExternalForm("byte", false)); + assertEquals("byte", _cache.getExternalForm("B", true)); + assertEquals("char", _cache.getExternalForm("char", false)); + assertEquals("char", _cache.getExternalForm("C", true)); + assertEquals("double", _cache.getExternalForm("double", false)); + assertEquals("double", _cache.getExternalForm("D", true)); + assertEquals("float", _cache.getExternalForm("float", false)); + assertEquals("float", _cache.getExternalForm("F", true)); + assertEquals("int", _cache.getExternalForm("int", false)); + assertEquals("int", _cache.getExternalForm("I", true)); + assertEquals("long", _cache.getExternalForm("long", false)); + assertEquals("long", _cache.getExternalForm("J", true)); + assertEquals("short", _cache.getExternalForm("short", false)); + assertEquals("short", _cache.getExternalForm("S", true)); + assertEquals("boolean", _cache.getExternalForm("boolean", false)); + assertEquals("boolean", _cache.getExternalForm("Z", true)); + assertEquals("void", _cache.getExternalForm("void", false)); + assertEquals("void", _cache.getExternalForm("V", true)); + + assertEquals("[B", _cache.getExternalForm("byte[]", false)); + assertEquals("[C", _cache.getExternalForm("char[]", false)); + assertEquals("[D", _cache.getExternalForm("double[]", false)); + assertEquals("[F", _cache.getExternalForm("float[]", false)); + assertEquals("[I", _cache.getExternalForm("int[]", false)); + assertEquals("[J", _cache.getExternalForm("long[]", false)); + assertEquals("[S", _cache.getExternalForm("short[]", false)); + assertEquals("[Z", _cache.getExternalForm("boolean[]", false)); + + assertEquals("java.lang.Object", + _cache.getExternalForm("java.lang.Object", true)); + assertEquals("java.lang.Object", + _cache.getExternalForm("java/lang/Object", true)); + assertEquals("java.lang.Object", + _cache.getExternalForm("java/lang/Object", false)); + assertEquals("java.lang.Object", + _cache.getExternalForm("Ljava/lang/Object;", false)); + } + + /** + * Tests that array class names are correctly converted to external form. + */ + public void testArrayExternalForm() { + assertEquals("byte[]", _cache.getExternalForm("byte[]", true)); + assertEquals("byte[]", + _cache.getExternalForm(byte[].class.getName(), true)); + assertEquals("[B", _cache.getExternalForm("byte[]", false)); + assertEquals("[B", _cache.getExternalForm(byte[].class.getName(), false)); + + assertEquals("java.lang.Object[][]", + _cache.getExternalForm("java.lang.Object[][]", true)); + assertEquals("java.lang.Object[][]", + _cache.getExternalForm(Object[][].class.getName(), true)); + assertEquals("[[Ljava.lang.Object;", + _cache.getExternalForm("java.lang.Object[][]", false)); + assertEquals("[[Ljava.lang.Object;", + _cache.getExternalForm(Object[][].class.getName(), false)); + } + + /** + * Tests that method descriptors are correctly formed. + */ + public void testDescriptors() { + assertEquals("()V", _cache.getDescriptor("V", new String[0])); + assertEquals("()V", _cache.getDescriptor("void", null)); + assertEquals("()Ljava/lang/Object;", + _cache.getDescriptor("java.lang.Object", null)); + assertEquals("(Z)V", + _cache.getDescriptor("void", new String[] { "boolean" })); + assertEquals("([ZLjava/lang/Object;I)[I", + _cache.getDescriptor("int[]", + new String[] { "[Z", "Ljava/lang/Object;", "int" })); + } + + /** + * Test that return types are extracted from method descriptors. + */ + public void testDescriptorReturnName() { + assertEquals("", _cache.getDescriptorReturnName("foo")); + assertEquals("V", _cache.getDescriptorReturnName("()V")); + assertEquals("[Ljava/lang/Object;", + _cache.getDescriptorReturnName( + "(IZLjava/lang/String;)[Ljava/lang/Object;")); + } + + /** + * Test that param types are extracted from method descriptors. + */ + public void testDescriptorParamNames() { + assertEquals(0, _cache.getDescriptorParamNames("foo").length); + + String[] params = _cache.getDescriptorParamNames( + "([ZLjava/lang/Object;I)[I"); + assertEquals(3, params.length); + assertEquals("[Z", params[0]); + assertEquals("Ljava/lang/Object;", params[1]); + assertEquals("I", params[2]); + } + + /** + * Test {@link NameCache#getComponentTypeName}. + */ + public void testComponentTypes() { + assertNull(_cache.getComponentName(null)); + assertNull(_cache.getComponentName(int.class.getName())); + assertNull(_cache.getComponentName(String.class.getName())); + assertEquals(int.class.getName(), + _cache.getComponentName(int[].class.getName())); + assertEquals(int[][].class.getName(), + _cache.getComponentName(int[][][].class.getName())); + assertEquals(String.class.getName(), + _cache.getComponentName(String[].class.getName())); + assertEquals(String[][].class.getName(), + _cache.getComponentName(String[][][].class.getName())); + } + + public static Test suite() { + return new TestSuite(TestNameCache.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } +} diff --git a/serp/src/test/java/serp/bytecode/TestPrimitive.java b/serp/src/test/java/serp/bytecode/TestPrimitive.java new file mode 100755 index 000000000..45631b5d0 --- /dev/null +++ b/serp/src/test/java/serp/bytecode/TestPrimitive.java @@ -0,0 +1,74 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import junit.framework.*; + +import junit.textui.*; + + +/** + *

Tests the handling of primitive {@link BCClass}es.

+ * + * @author Abe White + */ +public class TestPrimitive extends AbstractStateTest { + public TestPrimitive(String test) { + super(test); + } + + public void setUp() { + _bc = _project.loadClass(int.class); + } + + public void testType() { + assertEquals("int", _bc.getName()); + assertNull(_bc.getPackageName()); + assertEquals("int", _bc.getClassName()); + assertEquals(int.class, _bc.getType()); + + try { + _bc.setName("long"); + fail("Allowed set name"); + } catch (UnsupportedOperationException uoe) { + } + + assertTrue(_bc.isPrimitive()); + assertTrue(!_bc.isArray()); + } + + public void testSuperclass() { + assertNull(_bc.getSuperclassName()); + + try { + _bc.setSuperclass("long"); + fail("Allowed set superclass"); + } catch (UnsupportedOperationException uoe) { + } + } + + public void testComponent() { + assertNull(_bc.getComponentName()); + } + + public static Test suite() { + return new TestSuite(TestPrimitive.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } +} diff --git a/serp/src/test/java/serp/bytecode/TestProject.java b/serp/src/test/java/serp/bytecode/TestProject.java new file mode 100755 index 000000000..6218379fb --- /dev/null +++ b/serp/src/test/java/serp/bytecode/TestProject.java @@ -0,0 +1,333 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import junit.framework.*; + +import junit.textui.*; + +import java.io.*; + + +/** + *

Tests the {@link Project} type.

+ * + * @author Abe White + */ +public class TestProject extends TestCase { + private Project _project = new Project(); + + public TestProject(String test) { + super(test); + } + + /** + * Test the project name. + */ + public void testName() { + assertNull(_project.getName()); + assertNull(new Project(null).getName()); + assertEquals("foo", new Project("foo").getName()); + } + + /** + * Test loading classes by name. + */ + public void testLoadByName() { + BCClass bc; + BCClass bc2; + String[] names; + String[] names2; + + // test primitive types + names = new String[] { + "boolean", "byte", "char", "double", "float", "int", "long", + "short", "void" + }; + names2 = new String[] { "Z", "B", "C", "D", "F", "I", "J", "S", "V" }; + + for (int i = 0; i < names.length; i++) { + bc = _project.loadClass(names[i]); + bc2 = _project.loadClass(names2[i]); + assertTrue(names[i], bc == bc2); + assertTrue(names[i], bc.isPrimitive()); + assertEquals(names[i], bc.getName()); + } + + // test primitive array types + names = new String[] { + "boolean[]", "byte[]", "char[]", "double[]", "float[]", "int[]", + "long[]", "short[]" + }; + names2 = new String[] { "[Z", "[B", "[C", "[D", "[F", "[I", "[J", "[S" }; + + for (int i = 0; i < names.length; i++) { + bc = _project.loadClass(names[i]); + bc2 = _project.loadClass(names2[i]); + assertTrue(names[i], bc == bc2); + assertTrue(names[i], bc.isArray()); + assertEquals(names2[i], bc.getName()); + } + + // test new object type + bc = _project.loadClass("serp.Foo"); + bc2 = _project.loadClass("serp.Foo"); + assertTrue(bc == bc2); + assertTrue(!bc.isPrimitive()); + assertTrue(!bc.isArray()); + assertEquals("serp.Foo", bc.getName()); + + // test new object array type + bc = _project.loadClass("serp.Foo[]"); + bc2 = _project.loadClass("[Lserp.Foo;"); + assertTrue(bc == bc2); + assertTrue(bc.isArray()); + assertEquals("[Lserp.Foo;", bc.getName()); + + // test existing object type + bc = _project.loadClass(String.class.getName()); + bc2 = _project.loadClass(String.class); + assertTrue(bc == bc2); + assertTrue(!bc.isPrimitive()); + assertTrue(!bc.isArray()); + assertEquals(String.class.getName(), bc.getName()); + assertEquals("length", bc.getDeclaredMethod("length").getName()); + + // test new object array type + bc = _project.loadClass(String.class.getName() + "[]"); + bc2 = _project.loadClass(String[].class); + assertTrue(bc == bc2); + assertTrue(bc.isArray()); + assertEquals(String[].class.getName(), bc.getName()); + } + + /** + * Test loading classes by type. + */ + public void testLoadByType() { + BCClass bc; + BCClass bc2; + Class[] types; + String[] names; + + // test primitive types + types = new Class[] { + boolean.class, byte.class, char.class, double.class, float.class, + int.class, long.class, short.class, void.class + }; + names = new String[] { "Z", "B", "C", "D", "F", "I", "J", "S", "V" }; + + for (int i = 0; i < names.length; i++) { + bc = _project.loadClass(types[i]); + bc2 = _project.loadClass(names[i]); + assertTrue(types[i].getName(), bc == bc2); + assertTrue(types[i].getName(), bc.isPrimitive()); + assertEquals(types[i].getName(), bc.getName()); + } + + // test primitive array types + types = new Class[] { + boolean[].class, byte[].class, char[].class, double[].class, + float[].class, int[].class, long[].class, short[].class, + }; + names = new String[] { "[Z", "[B", "[C", "[D", "[F", "[I", "[J", "[S" }; + + for (int i = 0; i < names.length; i++) { + bc = _project.loadClass(types[i]); + bc2 = _project.loadClass(names[i]); + assertTrue(types[i].getName(), bc == bc2); + assertTrue(types[i].getName(), bc.isArray()); + assertEquals(types[i].getName(), bc.getName()); + } + + // test existing object type + bc = _project.loadClass(String.class); + bc2 = _project.loadClass(String.class.getName()); + assertTrue(bc == bc2); + assertTrue(!bc.isPrimitive()); + assertTrue(!bc.isArray()); + assertEquals(String.class.getName(), bc.getName()); + assertEquals("length", bc.getDeclaredMethod("length").getName()); + + // test new object array type + bc = _project.loadClass(String[].class); + bc2 = _project.loadClass(String.class.getName() + "[]"); + assertTrue(bc == bc2); + assertTrue(bc.isArray()); + assertEquals(String[].class.getName(), bc.getName()); + } + + /** + * Test loading classes by file. + */ + public void testLoadByFile() { + File file = new File(getClass().getResource("TestProject.class") + .getFile()); + + BCClass bc = _project.loadClass(file); + BCClass bc2 = _project.loadClass(file); + assertTrue(bc == bc2); + assertTrue(!bc.isPrimitive()); + assertTrue(!bc.isArray()); + assertEquals(getClass().getName(), bc.getName()); + assertEquals("main", bc.getDeclaredMethod("main").getName()); + } + + /** + * Test loading classes by stream. + */ + public void testLoadByStream() { + InputStream in = getClass().getResourceAsStream("TestProject.class"); + InputStream in2 = getClass().getResourceAsStream("TestProject.class"); + + BCClass bc = _project.loadClass(in); + BCClass bc2 = _project.loadClass(in2); + assertTrue(bc == bc2); + assertTrue(!bc.isPrimitive()); + assertTrue(!bc.isArray()); + assertEquals(getClass().getName(), bc.getName()); + assertEquals("main", bc.getDeclaredMethod("main").getName()); + } + + /** + * Test retrieving all loaded classes. + */ + public void testGetClasses() { + BCClass[] bcs = _project.getClasses(); + assertEquals(0, bcs.length); + + BCClass[] added = new BCClass[3]; + added[0] = _project.loadClass("int"); + added[1] = _project.loadClass("serp.Foo"); + added[2] = _project.loadClass(String[].class); + + bcs = _project.getClasses(); + assertEquals(3, bcs.length); + + int matches; + + for (int i = 0; i < added.length; i++) { + matches = 0; + + for (int j = 0; j < bcs.length; j++) + if (added[i] == bcs[j]) { + matches++; + } + + assertEquals(1, matches); + } + } + + /** + * Test renaming classes within the project. + */ + public void testRename() { + BCClass str = _project.loadClass(String.class); + BCClass foo = _project.loadClass("serp.Foo"); + + str.setName("java.lang.String2"); + assertEquals("java.lang.String2", str.getName()); + foo.setName("serp.Foo2"); + assertEquals("serp.Foo2", foo.getName()); + + try { + str.setName("serp.Foo2"); + fail("Set to existing name"); + } catch (IllegalStateException ise) { + } + + assertEquals("java.lang.String2", str.getName()); + + try { + foo.setName("java.lang.String2"); + fail("Set to existing name"); + } catch (IllegalStateException ise) { + } + + assertEquals("serp.Foo2", foo.getName()); + + str.setName("serp.Foo"); + assertEquals("serp.Foo", str.getName()); + + foo.setName("java.lang.String"); + assertEquals("java.lang.String", foo.getName()); + + assertTrue(foo == _project.loadClass(String.class)); + assertTrue(str == _project.loadClass("serp.Foo")); + } + + /** + * Test clearing classes. + */ + public void testClear() { + _project.clear(); + + BCClass bc1 = _project.loadClass("int"); + BCClass bc2 = _project.loadClass("serp.Foo"); + BCClass bc3 = _project.loadClass(String[].class); + + assertTrue(bc1.isValid()); + assertTrue(bc2.isValid()); + assertTrue(bc3.isValid()); + + assertEquals(3, _project.getClasses().length); + _project.clear(); + assertEquals(0, _project.getClasses().length); + + // cleared classes should be invalid + assertTrue(!bc1.isValid()); + assertTrue(!bc2.isValid()); + assertTrue(!bc3.isValid()); + } + + /** + * Test removing a class. + */ + public void testRemove() { + assertTrue(!_project.removeClass((String) null)); + assertTrue(!_project.removeClass((Class) null)); + assertTrue(!_project.removeClass((BCClass) null)); + + BCClass bc1 = _project.loadClass("int"); + BCClass bc2 = _project.loadClass("serp.Foo"); + BCClass bc3 = _project.loadClass(String[].class); + + assertTrue(bc1.isValid()); + assertTrue(bc2.isValid()); + assertTrue(bc3.isValid()); + + assertTrue(!_project.removeClass(new Project().loadClass("int"))); + assertTrue(_project.removeClass(bc1)); + assertTrue(!bc1.isValid()); + assertEquals(2, _project.getClasses().length); + + assertTrue(_project.removeClass("serp.Foo")); + assertTrue(!bc1.isValid()); + assertEquals(1, _project.getClasses().length); + + assertTrue(_project.removeClass(String[].class)); + assertTrue(!bc1.isValid()); + assertEquals(0, _project.getClasses().length); + } + + public static Test suite() { + return new TestSuite(TestProject.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } +} diff --git a/serp/src/test/java/serp/bytecode/TestStoreInstruction.java b/serp/src/test/java/serp/bytecode/TestStoreInstruction.java new file mode 100755 index 000000000..291cedade --- /dev/null +++ b/serp/src/test/java/serp/bytecode/TestStoreInstruction.java @@ -0,0 +1,179 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode; + +import junit.framework.*; + +import junit.textui.*; + + +/** + *

Tests the {@link StoreInstruction} type.

+ * + * @author Abe White + */ +public class TestStoreInstruction extends TestCase { + private Code _code = new Code(); + + public TestStoreInstruction(String test) { + super(test); + } + + /** + * Test that the instruction intitializes correctly when generated. + */ + public void testInitialize() { + assertEquals(Constants.NOP, _code.xstore().getOpcode()); + assertNull(_code.xstore().getType()); + assertEquals(Constants.NOP, _code.istore().getOpcode()); + assertEquals(int.class, _code.istore().getType()); + assertEquals(Constants.NOP, _code.lstore().getOpcode()); + assertEquals(long.class, _code.lstore().getType()); + assertEquals(Constants.NOP, _code.fstore().getOpcode()); + assertEquals(float.class, _code.fstore().getType()); + assertEquals(Constants.NOP, _code.dstore().getOpcode()); + assertEquals(double.class, _code.dstore().getType()); + assertEquals(Constants.NOP, _code.astore().getOpcode()); + assertEquals(Object.class, _code.astore().getType()); + } + + /** + * Test that the instruction returns its type correctly. + */ + public void testGetType() { + StoreInstruction ins = _code.xstore(); + assertNull(ins.getType()); + assertEquals(-1, ins.getLocal()); + + ins = _code.istore(); + assertEquals(int.class, ins.getType()); + assertEquals(int.class, ins.setLocal(1).getType()); + assertEquals(int.class, ins.setLocal(2).getType()); + assertEquals(int.class, ins.setLocal(3).getType()); + assertEquals(int.class, ins.setLocal(100).getType()); + + ins = _code.lstore(); + assertEquals(long.class, ins.getType()); + assertEquals(long.class, ins.setLocal(1).getType()); + assertEquals(long.class, ins.setLocal(2).getType()); + assertEquals(long.class, ins.setLocal(3).getType()); + assertEquals(long.class, ins.setLocal(100).getType()); + + ins = _code.fstore(); + assertEquals(float.class, ins.getType()); + assertEquals(float.class, ins.setLocal(1).getType()); + assertEquals(float.class, ins.setLocal(2).getType()); + assertEquals(float.class, ins.setLocal(3).getType()); + assertEquals(float.class, ins.setLocal(100).getType()); + + ins = _code.dstore(); + assertEquals(double.class, ins.getType()); + assertEquals(double.class, ins.setLocal(1).getType()); + assertEquals(double.class, ins.setLocal(2).getType()); + assertEquals(double.class, ins.setLocal(3).getType()); + assertEquals(double.class, ins.setLocal(100).getType()); + + ins = _code.astore(); + assertEquals(Object.class, ins.getType()); + assertEquals(Object.class, ins.setLocal(1).getType()); + assertEquals(Object.class, ins.setLocal(2).getType()); + assertEquals(Object.class, ins.setLocal(3).getType()); + assertEquals(Object.class, ins.setLocal(100).getType()); + } + + /** + * Test that the opcode is morphed correctly when the type and local + * of the instruction are changed. + */ + public void testOpcodeMorph() { + StoreInstruction ins = _code.xstore(); + + assertEquals(Constants.NOP, ins.getOpcode()); + assertEquals(Constants.NOP, ins.setType(int.class).getOpcode()); + assertEquals(Constants.ISTORE, ins.setLocal(10).getOpcode()); + assertEquals(Constants.ISTORE, ins.setType(boolean.class).getOpcode()); + assertEquals(Constants.ISTORE, ins.setType(byte.class).getOpcode()); + assertEquals(Constants.ISTORE, ins.setType(char.class).getOpcode()); + assertEquals(Constants.ISTORE, ins.setType(short.class).getOpcode()); + assertEquals(Constants.ISTORE0, ins.setLocal(0).getOpcode()); + assertEquals(0, ins.getLocal()); + assertEquals(Constants.ISTORE1, ins.setLocal(1).getOpcode()); + assertEquals(1, ins.getLocal()); + assertEquals(Constants.ISTORE2, ins.setLocal(2).getOpcode()); + assertEquals(2, ins.getLocal()); + assertEquals(Constants.ISTORE3, ins.setLocal(3).getOpcode()); + assertEquals(3, ins.getLocal()); + assertEquals(Constants.ISTORE, ins.setLocal(4).getOpcode()); + assertEquals(4, ins.getLocal()); + + assertEquals(Constants.LSTORE, ins.setType(long.class).getOpcode()); + assertEquals(Constants.LSTORE0, ins.setLocal(0).getOpcode()); + assertEquals(0, ins.getLocal()); + assertEquals(Constants.LSTORE1, ins.setLocal(1).getOpcode()); + assertEquals(1, ins.getLocal()); + assertEquals(Constants.LSTORE2, ins.setLocal(2).getOpcode()); + assertEquals(2, ins.getLocal()); + assertEquals(Constants.LSTORE3, ins.setLocal(3).getOpcode()); + assertEquals(3, ins.getLocal()); + assertEquals(Constants.LSTORE, ins.setLocal(4).getOpcode()); + assertEquals(4, ins.getLocal()); + + assertEquals(Constants.FSTORE, ins.setType(float.class).getOpcode()); + assertEquals(Constants.FSTORE0, ins.setLocal(0).getOpcode()); + assertEquals(0, ins.getLocal()); + assertEquals(Constants.FSTORE1, ins.setLocal(1).getOpcode()); + assertEquals(1, ins.getLocal()); + assertEquals(Constants.FSTORE2, ins.setLocal(2).getOpcode()); + assertEquals(2, ins.getLocal()); + assertEquals(Constants.FSTORE3, ins.setLocal(3).getOpcode()); + assertEquals(3, ins.getLocal()); + assertEquals(Constants.FSTORE, ins.setLocal(4).getOpcode()); + assertEquals(4, ins.getLocal()); + + assertEquals(Constants.DSTORE, ins.setType(double.class).getOpcode()); + assertEquals(Constants.DSTORE0, ins.setLocal(0).getOpcode()); + assertEquals(0, ins.getLocal()); + assertEquals(Constants.DSTORE1, ins.setLocal(1).getOpcode()); + assertEquals(1, ins.getLocal()); + assertEquals(Constants.DSTORE2, ins.setLocal(2).getOpcode()); + assertEquals(2, ins.getLocal()); + assertEquals(Constants.DSTORE3, ins.setLocal(3).getOpcode()); + assertEquals(3, ins.getLocal()); + assertEquals(Constants.DSTORE, ins.setLocal(4).getOpcode()); + assertEquals(4, ins.getLocal()); + + assertEquals(Constants.ASTORE, ins.setType(Object.class).getOpcode()); + assertEquals(Constants.ASTORE, ins.setType(String.class).getOpcode()); + assertEquals(Constants.ASTORE0, ins.setLocal(0).getOpcode()); + assertEquals(0, ins.getLocal()); + assertEquals(Constants.ASTORE1, ins.setLocal(1).getOpcode()); + assertEquals(1, ins.getLocal()); + assertEquals(Constants.ASTORE2, ins.setLocal(2).getOpcode()); + assertEquals(2, ins.getLocal()); + assertEquals(Constants.ASTORE3, ins.setLocal(3).getOpcode()); + assertEquals(3, ins.getLocal()); + assertEquals(Constants.ASTORE, ins.setLocal(4).getOpcode()); + assertEquals(4, ins.getLocal()); + } + + public static Test suite() { + return new TestSuite(TestStoreInstruction.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } +} diff --git a/serp/src/test/java/serp/bytecode/lowlevel/TestConstantPool.java b/serp/src/test/java/serp/bytecode/lowlevel/TestConstantPool.java new file mode 100755 index 000000000..23e53cf96 --- /dev/null +++ b/serp/src/test/java/serp/bytecode/lowlevel/TestConstantPool.java @@ -0,0 +1,360 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.bytecode.lowlevel; + +import junit.framework.*; + +import junit.textui.*; + + +/** + *

Tests the {@link ConstantPool} type.

+ * + * @author Abe White + */ +public class TestConstantPool extends TestCase { + private ConstantPool _pool = new ConstantPool(); + private IntEntry _intEntry = new IntEntry(1); + private LongEntry _longEntry = new LongEntry(2L); + private UTF8Entry _utf8Entry = new UTF8Entry("4"); + + public TestConstantPool(String test) { + super(test); + } + + /** + * Tests adding entries. + */ + public void testAdd() { + assertEquals(0, _pool.size()); + assertEquals(1, _pool.addEntry(_intEntry)); + assertEquals(1, _pool.size()); + assertEquals(2, _pool.addEntry(_longEntry)); + assertEquals(3, _pool.size()); + assertEquals(4, _pool.addEntry(_utf8Entry)); + assertEquals(4, _pool.size()); + + assertEquals(_intEntry, _pool.getEntry(1)); + assertEquals(_longEntry, _pool.getEntry(2)); + assertEquals(_utf8Entry, _pool.getEntry(4)); + assertEquals(1, _pool.findIntEntry(1, false)); + assertEquals(2, _pool.findLongEntry(2L, false)); + assertEquals(4, _pool.findUTF8Entry("4", false)); + + // add an entry multiple times + assertEquals(4, _pool.addEntry(_utf8Entry)); + assertEquals(5, _pool.addEntry(new UTF8Entry("4"))); + + // cannot add to another pool + ConstantPool pool = new ConstantPool(); + + try { + pool.addEntry(_intEntry); + fail("Entry already in another pool"); + } catch (IllegalStateException ise) { + } + + assertEquals(0, pool.size()); + + _pool.removeEntry(_intEntry); + assertEquals(0, _pool.indexOf(_intEntry)); + + pool.addEntry(_intEntry); + assertEquals(1, pool.size()); + assertEquals(_intEntry, pool.getEntry(1)); + } + + /** + * Tests removing entries. + */ + public void testRemove() { + _pool.addEntry(_intEntry); + _pool.addEntry(_longEntry); + _pool.addEntry(_utf8Entry); + + assertEquals(4, _pool.size()); + assertTrue(_pool.removeEntry(_longEntry)); + assertEquals(2, _pool.size()); + assertEquals(_intEntry, _pool.getEntry(1)); + assertEquals(_utf8Entry, _pool.getEntry(2)); + assertEquals(1, _pool.findIntEntry(1, false)); + assertEquals(2, _pool.findUTF8Entry("4", false)); + assertEquals(0, _pool.findLongEntry(2L, false)); + + assertTrue(!_pool.removeEntry(_longEntry)); + + assertTrue(_pool.removeEntry(_intEntry)); + assertEquals(1, _pool.size()); + assertEquals(_utf8Entry, _pool.getEntry(1)); + assertEquals(0, _pool.findIntEntry(1, false)); + assertEquals(1, _pool.findUTF8Entry("4", false)); + + assertTrue(_pool.removeEntry(_utf8Entry)); + assertEquals(0, _pool.size()); + + try { + _pool.getEntry(1); + fail("Invalid index"); + } catch (IndexOutOfBoundsException ioobe) { + } + + assertEquals(0, _pool.findUTF8Entry("4", false)); + } + + /** + * Tests mutating entries. + */ + public void testMutate() { + _intEntry.setValue(2); + _pool.addEntry(_intEntry); + _pool.addEntry(_longEntry); + _pool.addEntry(_utf8Entry); + + assertEquals(1, _pool.findIntEntry(2, false)); + assertEquals(2, _pool.findLongEntry(2L, false)); + assertEquals(4, _pool.findUTF8Entry("4", false)); + + _intEntry.setValue(1); + _longEntry.setValue(3L); + _utf8Entry.setValue("foo"); + + assertEquals(1, _pool.findIntEntry(1, false)); + assertEquals(2, _pool.findLongEntry(3L, false)); + assertEquals(4, _pool.findUTF8Entry("foo", false)); + } + + /** + * Tests finding the index of entries. + */ + public void testIndexOf() { + _pool.addEntry(_intEntry); + _pool.addEntry(_longEntry); + _pool.addEntry(_utf8Entry); + + assertEquals(1, _pool.indexOf(_intEntry)); + assertEquals(2, _pool.indexOf(_longEntry)); + assertEquals(4, _pool.indexOf(_utf8Entry)); + + _pool.removeEntry(_longEntry); + assertEquals(1, _pool.indexOf(_intEntry)); + assertEquals(0, _pool.indexOf(_longEntry)); + assertEquals(2, _pool.indexOf(_utf8Entry)); + } + + /** + * Tests getting all entries. + */ + public void testGetEntries() { + _pool.addEntry(_intEntry); + _pool.addEntry(_longEntry); + _pool.addEntry(_utf8Entry); + + Entry[] entries = _pool.getEntries(); + assertEquals(3, entries.length); + assertEquals(_intEntry, entries[0]); + assertEquals(_longEntry, entries[1]); + assertEquals(_utf8Entry, entries[2]); + + _pool.clear(); + assertEquals(0, _pool.getEntries().length); + } + + /** + * Tests getting entries by index. + */ + public void testGetEntry() { + _pool.addEntry(_intEntry); + _pool.addEntry(_longEntry); + _pool.addEntry(_utf8Entry); + + assertEquals(_intEntry, _pool.getEntry(1)); + assertEquals(_longEntry, _pool.getEntry(2)); + assertEquals(_utf8Entry, _pool.getEntry(4)); + + try { + _pool.getEntry(0); + fail("0 index"); + } catch (IndexOutOfBoundsException ioobe) { + } + + try { + _pool.getEntry(_pool.size() + 1); + fail("size() + 1 index"); + } catch (IndexOutOfBoundsException ioobe) { + } + + try { + _pool.getEntry(3); + fail("Wide index"); + } catch (IndexOutOfBoundsException ioobe) { + } + } + + /** + * Test clearing the pool. + */ + public void testClear() { + // make sure clearing empty pool OK + _pool.clear(); + assertEquals(0, _pool.size()); + + _pool.addEntry(_intEntry); + _pool.addEntry(_longEntry); + _pool.addEntry(_utf8Entry); + + _pool.clear(); + assertEquals(0, _pool.size()); + assertEquals(0, _pool.findIntEntry(1, false)); + assertEquals(0, _pool.findUTF8Entry("4", false)); + } + + /** + * Test finding entry indexes. + */ + public void testFind() { + int double1 = _pool.addEntry(new DoubleEntry(1D)); + int double2 = _pool.addEntry(new DoubleEntry(2D)); + int float1 = _pool.addEntry(new FloatEntry(1F)); + int float2 = _pool.addEntry(new FloatEntry(2F)); + int int1 = _pool.addEntry(new IntEntry(1)); + int int2 = _pool.addEntry(new IntEntry(2)); + int long1 = _pool.addEntry(new LongEntry(1L)); + int long2 = _pool.addEntry(new LongEntry(2L)); + int utf1 = _pool.addEntry(new UTF8Entry("1")); + int utf2 = _pool.addEntry(new UTF8Entry("2")); + int string1 = _pool.addEntry(new StringEntry(utf1)); + int string2 = _pool.addEntry(new StringEntry(utf2)); + int class1 = _pool.addEntry(new ClassEntry(utf1)); + int class2 = _pool.addEntry(new ClassEntry(utf2)); + int name1 = _pool.addEntry(new NameAndTypeEntry(utf1, utf2)); + int name2 = _pool.addEntry(new NameAndTypeEntry(utf2, utf1)); + int field1 = _pool.addEntry(new FieldEntry(class1, name1)); + int field2 = _pool.addEntry(new FieldEntry(class2, name2)); + int method1 = _pool.addEntry(new MethodEntry(class1, name1)); + int method2 = _pool.addEntry(new MethodEntry(class2, name2)); + int imethod1 = _pool.addEntry(new InterfaceMethodEntry(class1, name1)); + int imethod2 = _pool.addEntry(new InterfaceMethodEntry(class2, name2)); + + assertEquals(0, _pool.findDoubleEntry(0D, false)); + assertEquals(double1, _pool.findDoubleEntry(1D, false)); + assertEquals(double2, _pool.findDoubleEntry(2D, false)); + assertEquals(double1, _pool.findDoubleEntry(1D, true)); + assertEquals(double2, _pool.findDoubleEntry(2D, true)); + assertEquals(_pool.size() + 1, _pool.findDoubleEntry(0D, true)); + + assertEquals(0, _pool.findFloatEntry(0F, false)); + assertEquals(float1, _pool.findFloatEntry(1F, false)); + assertEquals(float2, _pool.findFloatEntry(2F, false)); + assertEquals(float1, _pool.findFloatEntry(1F, true)); + assertEquals(float2, _pool.findFloatEntry(2F, true)); + assertEquals(_pool.size() + 1, _pool.findFloatEntry(0F, true)); + + assertEquals(0, _pool.findIntEntry(0, false)); + assertEquals(int1, _pool.findIntEntry(1, false)); + assertEquals(int2, _pool.findIntEntry(2, false)); + assertEquals(int1, _pool.findIntEntry(1, true)); + assertEquals(int2, _pool.findIntEntry(2, true)); + assertEquals(_pool.size() + 1, _pool.findIntEntry(0, true)); + + assertEquals(0, _pool.findLongEntry(0L, false)); + assertEquals(long1, _pool.findLongEntry(1L, false)); + assertEquals(long2, _pool.findLongEntry(2L, false)); + assertEquals(long1, _pool.findLongEntry(1L, true)); + assertEquals(long2, _pool.findLongEntry(2L, true)); + assertEquals(_pool.size() + 1, _pool.findLongEntry(0L, true)); + + assertEquals(0, _pool.findUTF8Entry("0", false)); + assertEquals(utf1, _pool.findUTF8Entry("1", false)); + assertEquals(utf2, _pool.findUTF8Entry("2", false)); + assertEquals(utf1, _pool.findUTF8Entry("1", true)); + assertEquals(utf2, _pool.findUTF8Entry("2", true)); + assertEquals(_pool.size() + 1, _pool.findUTF8Entry("0", true)); + + assertEquals(0, _pool.findStringEntry("0", false)); + assertEquals(string1, _pool.findStringEntry("1", false)); + assertEquals(string2, _pool.findStringEntry("2", false)); + assertEquals(string1, _pool.findStringEntry("1", true)); + assertEquals(string2, _pool.findStringEntry("2", true)); + assertEquals(_pool.size() + 1, _pool.findStringEntry("0", true)); + assertEquals(_pool.size() + 2, _pool.findStringEntry("aaa", true)); + + assertEquals(0, _pool.findClassEntry("0", false)); + assertEquals(class1, _pool.findClassEntry("1", false)); + assertEquals(class2, _pool.findClassEntry("2", false)); + assertEquals(class1, _pool.findClassEntry("1", true)); + assertEquals(class2, _pool.findClassEntry("2", true)); + assertEquals(_pool.size() + 1, _pool.findClassEntry("0", true)); + assertEquals(_pool.size() + 2, _pool.findStringEntry("bbb", true)); + + assertEquals(0, _pool.findNameAndTypeEntry("0", "1", false)); + assertEquals(name1, _pool.findNameAndTypeEntry("1", "2", false)); + assertEquals(name2, _pool.findNameAndTypeEntry("2", "1", false)); + assertEquals(name1, _pool.findNameAndTypeEntry("1", "2", true)); + assertEquals(name2, _pool.findNameAndTypeEntry("2", "1", true)); + assertEquals(_pool.size() + 1, + _pool.findNameAndTypeEntry("0", "1", true)); + assertEquals(_pool.size() + 2, + _pool.findNameAndTypeEntry("2", "3", true)); + assertEquals(_pool.size() + 3, + _pool.findNameAndTypeEntry("ccc", "ddd", true)); + + assertEquals(0, _pool.findFieldEntry("0", "1", "2", false)); + assertEquals(field1, _pool.findFieldEntry("1", "1", "2", false)); + assertEquals(field2, _pool.findFieldEntry("2", "2", "1", false)); + assertEquals(field1, _pool.findFieldEntry("1", "1", "2", true)); + assertEquals(field2, _pool.findFieldEntry("2", "2", "1", true)); + assertEquals(_pool.size() + 1, _pool.findFieldEntry("1", "2", "1", true)); + assertEquals(_pool.size() + 3, _pool.findFieldEntry("1", "3", "4", true)); + assertEquals(_pool.size() + 6, + _pool.findFieldEntry("eee", "fff", "ggg", true)); + + assertEquals(0, _pool.findMethodEntry("0", "1", "2", false)); + assertEquals(method1, _pool.findMethodEntry("1", "1", "2", false)); + assertEquals(method2, _pool.findMethodEntry("2", "2", "1", false)); + assertEquals(method1, _pool.findMethodEntry("1", "1", "2", true)); + assertEquals(method2, _pool.findMethodEntry("2", "2", "1", true)); + assertEquals(_pool.size() + 1, + _pool.findMethodEntry("1", "2", "1", true)); + assertEquals(_pool.size() + 3, + _pool.findMethodEntry("1", "3", "5", true)); + assertEquals(_pool.size() + 6, + _pool.findMethodEntry("hhh", "iii", "jjj", true)); + + assertEquals(0, _pool.findInterfaceMethodEntry("0", "1", "2", false)); + assertEquals(imethod1, + _pool.findInterfaceMethodEntry("1", "1", "2", false)); + assertEquals(imethod2, + _pool.findInterfaceMethodEntry("2", "2", "1", false)); + assertEquals(imethod1, + _pool.findInterfaceMethodEntry("1", "1", "2", true)); + assertEquals(imethod2, + _pool.findInterfaceMethodEntry("2", "2", "1", true)); + assertEquals(_pool.size() + 1, + _pool.findInterfaceMethodEntry("1", "2", "1", true)); + assertEquals(_pool.size() + 3, + _pool.findInterfaceMethodEntry("1", "3", "6", true)); + assertEquals(_pool.size() + 6, + _pool.findInterfaceMethodEntry("kkk", "lll", "mmm", true)); + } + + public static Test suite() { + return new TestSuite(TestConstantPool.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } +} diff --git a/serp/src/test/java/serp/util/TestStrings.java b/serp/src/test/java/serp/util/TestStrings.java new file mode 100755 index 000000000..80505bdf7 --- /dev/null +++ b/serp/src/test/java/serp/util/TestStrings.java @@ -0,0 +1,128 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package serp.util; + +import junit.framework.*; + +import junit.textui.*; + + +/** + *

Tests the {@link Strings} type.

+ * + * @author Abe White + */ +public class TestStrings extends TestCase { + public TestStrings(String test) { + super(test); + } + + /** + * Test {@link Strings#split}. + */ + public void testSplit() { + String str = "boo:and:foo"; + + assertEquals(new String[] { "boo", "and:foo" }, + Strings.split(str, ":", 2)); + assertEquals(new String[] { "boo:and:foo" }, Strings.split(str, ":", 1)); + assertEquals(new String[] { "boo", "and", "foo" }, + Strings.split(str, ":", 0)); + assertEquals(new String[] { "boo", "and", "foo" }, + Strings.split(str, ":", -2)); + assertEquals(new String[] { "b", "", ":and:f", "", "" }, + Strings.split(str, "o", 5)); + assertEquals(new String[] { "b", "", ":and:f", "o" }, + Strings.split(str, "o", 4)); + assertEquals(new String[] { "b", "", ":and:f", "", "" }, + Strings.split(str, "o", -2)); + assertEquals(new String[] { "b", "", ":and:f" }, + Strings.split(str, "o", 0)); + assertEquals(new String[] { "", "b", "", ":and:f" }, + Strings.split("o" + str, "o", 0)); + } + + /** + * Test {@link Strings#classForName}. + */ + public void testClassForName() { + // test primitives + assertEquals(boolean.class, Strings.toClass("boolean", null)); + assertEquals(byte.class, Strings.toClass("byte", null)); + assertEquals(char.class, Strings.toClass("char", null)); + assertEquals(double.class, Strings.toClass("double", null)); + assertEquals(float.class, Strings.toClass("float", null)); + assertEquals(int.class, Strings.toClass("int", null)); + assertEquals(long.class, Strings.toClass("long", null)); + assertEquals(short.class, Strings.toClass("short", null)); + assertEquals(void.class, Strings.toClass("void", null)); + + // test objects + assertEquals(String.class, Strings.toClass(String.class.getName(), null)); + + // test arrays + assertEquals(boolean[].class, Strings.toClass("[Z", null)); + assertEquals(byte[].class, Strings.toClass("[B", null)); + assertEquals(char[].class, Strings.toClass("[C", null)); + assertEquals(double[].class, Strings.toClass("[D", null)); + assertEquals(float[].class, Strings.toClass("[F", null)); + assertEquals(int[].class, Strings.toClass("[I", null)); + assertEquals(long[].class, Strings.toClass("[J", null)); + assertEquals(short[].class, Strings.toClass("[S", null)); + assertEquals(String[].class, + Strings.toClass(String[].class.getName(), null)); + assertEquals(boolean[][].class, Strings.toClass("[[Z", null)); + assertEquals(String[][].class, + Strings.toClass(String[][].class.getName(), null)); + + assertEquals(boolean[].class, Strings.toClass("boolean[]", null)); + assertEquals(byte[].class, Strings.toClass("byte[]", null)); + assertEquals(char[].class, Strings.toClass("char[]", null)); + assertEquals(double[].class, Strings.toClass("double[]", null)); + assertEquals(float[].class, Strings.toClass("float[]", null)); + assertEquals(int[].class, Strings.toClass("int[]", null)); + assertEquals(long[].class, Strings.toClass("long[]", null)); + assertEquals(short[].class, Strings.toClass("short[]", null)); + assertEquals(String[].class, Strings.toClass("java.lang.String[]", null)); + + try { + Strings.toClass("[V", null); + fail("Allowed invalid class name"); + } catch (RuntimeException re) { + } + + try { + Strings.toClass("java.lang.Foo", null); + fail("Allowed invalid class name"); + } catch (RuntimeException re) { + } + } + + private void assertEquals(String[] arr1, String[] arr2) { + assertEquals(arr1.length, arr2.length); + + for (int i = 0; i < arr1.length; i++) + assertEquals(arr1[i], arr2[i]); + } + + public static Test suite() { + return new TestSuite(TestStrings.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } +}