- *
- * @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.
+
+ 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.
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.*;
+
+
+/**
+ *